1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jscompartment.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,946 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "jscompartmentinlines.h" 1.11 + 1.12 +#include "mozilla/DebugOnly.h" 1.13 +#include "mozilla/MemoryReporting.h" 1.14 + 1.15 +#include "jscntxt.h" 1.16 +#include "jsfriendapi.h" 1.17 +#include "jsgc.h" 1.18 +#include "jsiter.h" 1.19 +#include "jsproxy.h" 1.20 +#include "jswatchpoint.h" 1.21 +#include "jswrapper.h" 1.22 + 1.23 +#include "gc/Marking.h" 1.24 +#ifdef JS_ION 1.25 +#include "jit/JitCompartment.h" 1.26 +#endif 1.27 +#include "js/RootingAPI.h" 1.28 +#include "vm/StopIterationObject.h" 1.29 +#include "vm/WrapperObject.h" 1.30 + 1.31 +#include "jsatominlines.h" 1.32 +#include "jsfuninlines.h" 1.33 +#include "jsgcinlines.h" 1.34 +#include "jsinferinlines.h" 1.35 +#include "jsobjinlines.h" 1.36 + 1.37 +using namespace js; 1.38 +using namespace js::gc; 1.39 + 1.40 +using mozilla::DebugOnly; 1.41 + 1.42 +JSCompartment::JSCompartment(Zone *zone, const JS::CompartmentOptions &options = JS::CompartmentOptions()) 1.43 + : options_(options), 1.44 + zone_(zone), 1.45 + runtime_(zone->runtimeFromMainThread()), 1.46 + principals(nullptr), 1.47 + isSystem(false), 1.48 + isSelfHosting(false), 1.49 + marked(true), 1.50 +#ifdef DEBUG 1.51 + firedOnNewGlobalObject(false), 1.52 +#endif 1.53 + global_(nullptr), 1.54 + enterCompartmentDepth(0), 1.55 + data(nullptr), 1.56 + objectMetadataCallback(nullptr), 1.57 + lastAnimationTime(0), 1.58 + regExps(runtime_), 1.59 + globalWriteBarriered(false), 1.60 + propertyTree(thisForCtor()), 1.61 + selfHostingScriptSource(nullptr), 1.62 + gcIncomingGrayPointers(nullptr), 1.63 + gcWeakMapList(nullptr), 1.64 + debugModeBits(runtime_->debugMode ? DebugFromC : 0), 1.65 + rngState(0), 1.66 + watchpointMap(nullptr), 1.67 + scriptCountsMap(nullptr), 1.68 + debugScriptMap(nullptr), 1.69 + debugScopes(nullptr), 1.70 + enumerators(nullptr), 1.71 + compartmentStats(nullptr) 1.72 +#ifdef JS_ION 1.73 + , jitCompartment_(nullptr) 1.74 +#endif 1.75 +{ 1.76 + runtime_->numCompartments++; 1.77 + JS_ASSERT_IF(options.mergeable(), options.invisibleToDebugger()); 1.78 +} 1.79 + 1.80 +JSCompartment::~JSCompartment() 1.81 +{ 1.82 +#ifdef JS_ION 1.83 + js_delete(jitCompartment_); 1.84 +#endif 1.85 + 1.86 + js_delete(watchpointMap); 1.87 + js_delete(scriptCountsMap); 1.88 + js_delete(debugScriptMap); 1.89 + js_delete(debugScopes); 1.90 + js_free(enumerators); 1.91 + 1.92 + runtime_->numCompartments--; 1.93 +} 1.94 + 1.95 +bool 1.96 +JSCompartment::init(JSContext *cx) 1.97 +{ 1.98 + /* 1.99 + * As a hack, we clear our timezone cache every time we create a new 1.100 + * compartment. This ensures that the cache is always relatively fresh, but 1.101 + * shouldn't interfere with benchmarks which create tons of date objects 1.102 + * (unless they also create tons of iframes, which seems unlikely). 1.103 + */ 1.104 + if (cx) 1.105 + cx->runtime()->dateTimeInfo.updateTimeZoneAdjustment(); 1.106 + 1.107 + activeAnalysis = false; 1.108 + 1.109 + if (!crossCompartmentWrappers.init(0)) 1.110 + return false; 1.111 + 1.112 + if (!regExps.init(cx)) 1.113 + return false; 1.114 + 1.115 + enumerators = NativeIterator::allocateSentinel(cx); 1.116 + if (!enumerators) 1.117 + return false; 1.118 + 1.119 + if (!savedStacks_.init()) 1.120 + return false; 1.121 + 1.122 + return debuggees.init(0); 1.123 +} 1.124 + 1.125 +#ifdef JS_ION 1.126 +jit::JitRuntime * 1.127 +JSRuntime::createJitRuntime(JSContext *cx) 1.128 +{ 1.129 + // The shared stubs are created in the atoms compartment, which may be 1.130 + // accessed by other threads with an exclusive context. 1.131 + AutoLockForExclusiveAccess atomsLock(cx); 1.132 + 1.133 + // The runtime will only be created on its owning thread, but reads of a 1.134 + // runtime's jitRuntime() can occur when another thread is requesting an 1.135 + // interrupt. 1.136 + AutoLockForInterrupt lock(this); 1.137 + 1.138 + JS_ASSERT(!jitRuntime_); 1.139 + 1.140 + jitRuntime_ = cx->new_<jit::JitRuntime>(); 1.141 + 1.142 + if (!jitRuntime_) 1.143 + return nullptr; 1.144 + 1.145 + if (!jitRuntime_->initialize(cx)) { 1.146 + js_delete(jitRuntime_); 1.147 + jitRuntime_ = nullptr; 1.148 + 1.149 + JSCompartment *comp = cx->runtime()->atomsCompartment(); 1.150 + if (comp->jitCompartment_) { 1.151 + js_delete(comp->jitCompartment_); 1.152 + comp->jitCompartment_ = nullptr; 1.153 + } 1.154 + 1.155 + return nullptr; 1.156 + } 1.157 + 1.158 + return jitRuntime_; 1.159 +} 1.160 + 1.161 +bool 1.162 +JSCompartment::ensureJitCompartmentExists(JSContext *cx) 1.163 +{ 1.164 + using namespace js::jit; 1.165 + if (jitCompartment_) 1.166 + return true; 1.167 + 1.168 + if (!zone()->getJitZone(cx)) 1.169 + return false; 1.170 + 1.171 + /* Set the compartment early, so linking works. */ 1.172 + jitCompartment_ = cx->new_<JitCompartment>(); 1.173 + 1.174 + if (!jitCompartment_) 1.175 + return false; 1.176 + 1.177 + if (!jitCompartment_->initialize(cx)) { 1.178 + js_delete(jitCompartment_); 1.179 + jitCompartment_ = nullptr; 1.180 + return false; 1.181 + } 1.182 + 1.183 + return true; 1.184 +} 1.185 +#endif 1.186 + 1.187 +#ifdef JSGC_GENERATIONAL 1.188 + 1.189 +/* 1.190 + * This class is used to add a post barrier on the crossCompartmentWrappers map, 1.191 + * as the key is calculated based on objects which may be moved by generational 1.192 + * GC. 1.193 + */ 1.194 +class WrapperMapRef : public BufferableRef 1.195 +{ 1.196 + WrapperMap *map; 1.197 + CrossCompartmentKey key; 1.198 + 1.199 + public: 1.200 + WrapperMapRef(WrapperMap *map, const CrossCompartmentKey &key) 1.201 + : map(map), key(key) {} 1.202 + 1.203 + void mark(JSTracer *trc) { 1.204 + CrossCompartmentKey prior = key; 1.205 + if (key.debugger) 1.206 + Mark(trc, &key.debugger, "CCW debugger"); 1.207 + if (key.kind != CrossCompartmentKey::StringWrapper) 1.208 + Mark(trc, reinterpret_cast<JSObject**>(&key.wrapped), "CCW wrapped object"); 1.209 + if (key.debugger == prior.debugger && key.wrapped == prior.wrapped) 1.210 + return; 1.211 + 1.212 + /* Look for the original entry, which might have been removed. */ 1.213 + WrapperMap::Ptr p = map->lookup(prior); 1.214 + if (!p) 1.215 + return; 1.216 + 1.217 + /* Rekey the entry. */ 1.218 + map->rekeyAs(prior, key, key); 1.219 + } 1.220 +}; 1.221 + 1.222 +#ifdef JS_GC_ZEAL 1.223 +void 1.224 +JSCompartment::checkWrapperMapAfterMovingGC() 1.225 +{ 1.226 + /* 1.227 + * Assert that the postbarriers have worked and that nothing is left in 1.228 + * wrapperMap that points into the nursery, and that the hash table entries 1.229 + * are discoverable. 1.230 + */ 1.231 + JS::shadow::Runtime *rt = JS::shadow::Runtime::asShadowRuntime(runtimeFromMainThread()); 1.232 + for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) { 1.233 + CrossCompartmentKey key = e.front().key(); 1.234 + JS_ASSERT(!IsInsideNursery(rt, key.debugger)); 1.235 + JS_ASSERT(!IsInsideNursery(rt, key.wrapped)); 1.236 + JS_ASSERT(!IsInsideNursery(rt, e.front().value().get().toGCThing())); 1.237 + 1.238 + WrapperMap::Ptr ptr = crossCompartmentWrappers.lookup(key); 1.239 + JS_ASSERT(ptr.found() && &*ptr == &e.front()); 1.240 + } 1.241 +} 1.242 +#endif 1.243 + 1.244 +#endif 1.245 + 1.246 +bool 1.247 +JSCompartment::putWrapper(JSContext *cx, const CrossCompartmentKey &wrapped, const js::Value &wrapper) 1.248 +{ 1.249 + JS_ASSERT(wrapped.wrapped); 1.250 + JS_ASSERT(!IsPoisonedPtr(wrapped.wrapped)); 1.251 + JS_ASSERT(!IsPoisonedPtr(wrapped.debugger)); 1.252 + JS_ASSERT(!IsPoisonedPtr(wrapper.toGCThing())); 1.253 + JS_ASSERT_IF(wrapped.kind == CrossCompartmentKey::StringWrapper, wrapper.isString()); 1.254 + JS_ASSERT_IF(wrapped.kind != CrossCompartmentKey::StringWrapper, wrapper.isObject()); 1.255 + bool success = crossCompartmentWrappers.put(wrapped, wrapper); 1.256 + 1.257 +#ifdef JSGC_GENERATIONAL 1.258 + /* There's no point allocating wrappers in the nursery since we will tenure them anyway. */ 1.259 + Nursery &nursery = cx->nursery(); 1.260 + JS_ASSERT(!nursery.isInside(wrapper.toGCThing())); 1.261 + 1.262 + if (success && (nursery.isInside(wrapped.wrapped) || nursery.isInside(wrapped.debugger))) { 1.263 + WrapperMapRef ref(&crossCompartmentWrappers, wrapped); 1.264 + cx->runtime()->gcStoreBuffer.putGeneric(ref); 1.265 + } 1.266 +#endif 1.267 + 1.268 + return success; 1.269 +} 1.270 + 1.271 +bool 1.272 +JSCompartment::wrap(JSContext *cx, JSString **strp) 1.273 +{ 1.274 + JS_ASSERT(!cx->runtime()->isAtomsCompartment(this)); 1.275 + JS_ASSERT(cx->compartment() == this); 1.276 + 1.277 + /* If the string is already in this compartment, we are done. */ 1.278 + JSString *str = *strp; 1.279 + if (str->zoneFromAnyThread() == zone()) 1.280 + return true; 1.281 + 1.282 + /* If the string is an atom, we don't have to copy. */ 1.283 + if (str->isAtom()) { 1.284 + JS_ASSERT(str->isPermanentAtom() || 1.285 + cx->runtime()->isAtomsZone(str->zone())); 1.286 + return true; 1.287 + } 1.288 + 1.289 + /* Check the cache. */ 1.290 + RootedValue key(cx, StringValue(str)); 1.291 + if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(key)) { 1.292 + *strp = p->value().get().toString(); 1.293 + return true; 1.294 + } 1.295 + 1.296 + /* 1.297 + * No dice. Make a copy, and cache it. Directly allocate the copy in the 1.298 + * destination compartment, rather than first flattening it (and possibly 1.299 + * allocating in source compartment), because we don't know whether the 1.300 + * flattening will pay off later. 1.301 + */ 1.302 + JSString *copy; 1.303 + if (str->hasPureChars()) { 1.304 + copy = js_NewStringCopyN<CanGC>(cx, str->pureChars(), str->length()); 1.305 + } else { 1.306 + ScopedJSFreePtr<jschar> copiedChars; 1.307 + if (!str->copyNonPureCharsZ(cx, copiedChars)) 1.308 + return false; 1.309 + copy = js_NewString<CanGC>(cx, copiedChars.forget(), str->length()); 1.310 + } 1.311 + 1.312 + if (!copy) 1.313 + return false; 1.314 + if (!putWrapper(cx, key, StringValue(copy))) 1.315 + return false; 1.316 + 1.317 + *strp = copy; 1.318 + return true; 1.319 +} 1.320 + 1.321 +bool 1.322 +JSCompartment::wrap(JSContext *cx, HeapPtrString *strp) 1.323 +{ 1.324 + RootedString str(cx, *strp); 1.325 + if (!wrap(cx, str.address())) 1.326 + return false; 1.327 + *strp = str; 1.328 + return true; 1.329 +} 1.330 + 1.331 +bool 1.332 +JSCompartment::wrap(JSContext *cx, MutableHandleObject obj, HandleObject existingArg) 1.333 +{ 1.334 + JS_ASSERT(!cx->runtime()->isAtomsCompartment(this)); 1.335 + JS_ASSERT(cx->compartment() == this); 1.336 + JS_ASSERT_IF(existingArg, existingArg->compartment() == cx->compartment()); 1.337 + JS_ASSERT_IF(existingArg, IsDeadProxyObject(existingArg)); 1.338 + 1.339 + if (!obj) 1.340 + return true; 1.341 + AutoDisableProxyCheck adpc(cx->runtime()); 1.342 + 1.343 + // Wrappers should really be parented to the wrapped parent of the wrapped 1.344 + // object, but in that case a wrapped global object would have a nullptr 1.345 + // parent without being a proper global object (JSCLASS_IS_GLOBAL). Instead, 1.346 + // we parent all wrappers to the global object in their home compartment. 1.347 + // This loses us some transparency, and is generally very cheesy. 1.348 + HandleObject global = cx->global(); 1.349 + RootedObject objGlobal(cx, &obj->global()); 1.350 + JS_ASSERT(global); 1.351 + JS_ASSERT(objGlobal); 1.352 + 1.353 + const JSWrapObjectCallbacks *cb = cx->runtime()->wrapObjectCallbacks; 1.354 + 1.355 + if (obj->compartment() == this) { 1.356 + obj.set(GetOuterObject(cx, obj)); 1.357 + return true; 1.358 + } 1.359 + 1.360 + // If we have a cross-compartment wrapper, make sure that the cx isn't 1.361 + // associated with the self-hosting global. We don't want to create 1.362 + // wrappers for objects in other runtimes, which may be the case for the 1.363 + // self-hosting global. 1.364 + JS_ASSERT(!cx->runtime()->isSelfHostingGlobal(global) && 1.365 + !cx->runtime()->isSelfHostingGlobal(objGlobal)); 1.366 + 1.367 + // Unwrap the object, but don't unwrap outer windows. 1.368 + unsigned flags = 0; 1.369 + obj.set(UncheckedUnwrap(obj, /* stopAtOuter = */ true, &flags)); 1.370 + 1.371 + if (obj->compartment() == this) { 1.372 + MOZ_ASSERT(obj == GetOuterObject(cx, obj)); 1.373 + return true; 1.374 + } 1.375 + 1.376 + // Translate StopIteration singleton. 1.377 + if (obj->is<StopIterationObject>()) { 1.378 + // StopIteration isn't a constructor, but it's stored in GlobalObject 1.379 + // as one, out of laziness. Hence the GetBuiltinConstructor call here. 1.380 + RootedObject stopIteration(cx); 1.381 + if (!GetBuiltinConstructor(cx, JSProto_StopIteration, &stopIteration)) 1.382 + return false; 1.383 + obj.set(stopIteration); 1.384 + return true; 1.385 + } 1.386 + 1.387 + // Invoke the prewrap callback. We're a bit worried about infinite 1.388 + // recursion here, so we do a check - see bug 809295. 1.389 + JS_CHECK_CHROME_RECURSION(cx, return false); 1.390 + if (cb->preWrap) { 1.391 + obj.set(cb->preWrap(cx, global, obj, flags)); 1.392 + if (!obj) 1.393 + return false; 1.394 + } 1.395 + MOZ_ASSERT(obj == GetOuterObject(cx, obj)); 1.396 + 1.397 + if (obj->compartment() == this) 1.398 + return true; 1.399 + 1.400 + 1.401 + // If we already have a wrapper for this value, use it. 1.402 + RootedValue key(cx, ObjectValue(*obj)); 1.403 + if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(key)) { 1.404 + obj.set(&p->value().get().toObject()); 1.405 + JS_ASSERT(obj->is<CrossCompartmentWrapperObject>()); 1.406 + JS_ASSERT(obj->getParent() == global); 1.407 + return true; 1.408 + } 1.409 + 1.410 + RootedObject proto(cx, TaggedProto::LazyProto); 1.411 + RootedObject existing(cx, existingArg); 1.412 + if (existing) { 1.413 + // Is it possible to reuse |existing|? 1.414 + if (!existing->getTaggedProto().isLazy() || 1.415 + // Note: don't use is<ObjectProxyObject>() here -- it also matches subclasses! 1.416 + existing->getClass() != &ProxyObject::uncallableClass_ || 1.417 + existing->getParent() != global || 1.418 + obj->isCallable()) 1.419 + { 1.420 + existing = nullptr; 1.421 + } 1.422 + } 1.423 + 1.424 + obj.set(cb->wrap(cx, existing, obj, proto, global, flags)); 1.425 + if (!obj) 1.426 + return false; 1.427 + 1.428 + // We maintain the invariant that the key in the cross-compartment wrapper 1.429 + // map is always directly wrapped by the value. 1.430 + JS_ASSERT(Wrapper::wrappedObject(obj) == &key.get().toObject()); 1.431 + 1.432 + return putWrapper(cx, key, ObjectValue(*obj)); 1.433 +} 1.434 + 1.435 +bool 1.436 +JSCompartment::wrapId(JSContext *cx, jsid *idp) 1.437 +{ 1.438 + MOZ_ASSERT(*idp != JSID_VOID, "JSID_VOID is an out-of-band sentinel value"); 1.439 + if (JSID_IS_INT(*idp)) 1.440 + return true; 1.441 + RootedValue value(cx, IdToValue(*idp)); 1.442 + if (!wrap(cx, &value)) 1.443 + return false; 1.444 + RootedId id(cx); 1.445 + if (!ValueToId<CanGC>(cx, value, &id)) 1.446 + return false; 1.447 + 1.448 + *idp = id; 1.449 + return true; 1.450 +} 1.451 + 1.452 +bool 1.453 +JSCompartment::wrap(JSContext *cx, PropertyOp *propp) 1.454 +{ 1.455 + RootedValue value(cx, CastAsObjectJsval(*propp)); 1.456 + if (!wrap(cx, &value)) 1.457 + return false; 1.458 + *propp = CastAsPropertyOp(value.toObjectOrNull()); 1.459 + return true; 1.460 +} 1.461 + 1.462 +bool 1.463 +JSCompartment::wrap(JSContext *cx, StrictPropertyOp *propp) 1.464 +{ 1.465 + RootedValue value(cx, CastAsObjectJsval(*propp)); 1.466 + if (!wrap(cx, &value)) 1.467 + return false; 1.468 + *propp = CastAsStrictPropertyOp(value.toObjectOrNull()); 1.469 + return true; 1.470 +} 1.471 + 1.472 +bool 1.473 +JSCompartment::wrap(JSContext *cx, MutableHandle<PropertyDescriptor> desc) 1.474 +{ 1.475 + if (!wrap(cx, desc.object())) 1.476 + return false; 1.477 + 1.478 + if (desc.hasGetterObject()) { 1.479 + if (!wrap(cx, &desc.getter())) 1.480 + return false; 1.481 + } 1.482 + if (desc.hasSetterObject()) { 1.483 + if (!wrap(cx, &desc.setter())) 1.484 + return false; 1.485 + } 1.486 + 1.487 + return wrap(cx, desc.value()); 1.488 +} 1.489 + 1.490 +bool 1.491 +JSCompartment::wrap(JSContext *cx, AutoIdVector &props) 1.492 +{ 1.493 + jsid *vector = props.begin(); 1.494 + int length = props.length(); 1.495 + for (size_t n = 0; n < size_t(length); ++n) { 1.496 + if (!wrapId(cx, &vector[n])) 1.497 + return false; 1.498 + } 1.499 + return true; 1.500 +} 1.501 + 1.502 +/* 1.503 + * This method marks pointers that cross compartment boundaries. It should be 1.504 + * called only for per-compartment GCs, since full GCs naturally follow pointers 1.505 + * across compartments. 1.506 + */ 1.507 +void 1.508 +JSCompartment::markCrossCompartmentWrappers(JSTracer *trc) 1.509 +{ 1.510 + JS_ASSERT(!zone()->isCollecting()); 1.511 + 1.512 + for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) { 1.513 + Value v = e.front().value(); 1.514 + if (e.front().key().kind == CrossCompartmentKey::ObjectWrapper) { 1.515 + ProxyObject *wrapper = &v.toObject().as<ProxyObject>(); 1.516 + 1.517 + /* 1.518 + * We have a cross-compartment wrapper. Its private pointer may 1.519 + * point into the compartment being collected, so we should mark it. 1.520 + */ 1.521 + Value referent = wrapper->private_(); 1.522 + MarkValueRoot(trc, &referent, "cross-compartment wrapper"); 1.523 + JS_ASSERT(referent == wrapper->private_()); 1.524 + } 1.525 + } 1.526 +} 1.527 + 1.528 +void 1.529 +JSCompartment::trace(JSTracer *trc) 1.530 +{ 1.531 + // At the moment, this is merely ceremonial, but any live-compartment-only tracing should go 1.532 + // here. 1.533 +} 1.534 + 1.535 +void 1.536 +JSCompartment::markRoots(JSTracer *trc) 1.537 +{ 1.538 + JS_ASSERT(!trc->runtime()->isHeapMinorCollecting()); 1.539 + 1.540 +#ifdef JS_ION 1.541 + if (jitCompartment_) 1.542 + jitCompartment_->mark(trc, this); 1.543 +#endif 1.544 + 1.545 + /* 1.546 + * If a compartment is on-stack, we mark its global so that 1.547 + * JSContext::global() remains valid. 1.548 + */ 1.549 + if (enterCompartmentDepth && global_) 1.550 + MarkObjectRoot(trc, global_.unsafeGet(), "on-stack compartment global"); 1.551 +} 1.552 + 1.553 +void 1.554 +JSCompartment::sweep(FreeOp *fop, bool releaseTypes) 1.555 +{ 1.556 + JS_ASSERT(!activeAnalysis); 1.557 + 1.558 + /* This function includes itself in PHASE_SWEEP_TABLES. */ 1.559 + sweepCrossCompartmentWrappers(); 1.560 + 1.561 + JSRuntime *rt = runtimeFromMainThread(); 1.562 + 1.563 + { 1.564 + gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP_TABLES); 1.565 + 1.566 + /* Remove dead references held weakly by the compartment. */ 1.567 + 1.568 + sweepBaseShapeTable(); 1.569 + sweepInitialShapeTable(); 1.570 + sweepNewTypeObjectTable(newTypeObjects); 1.571 + sweepNewTypeObjectTable(lazyTypeObjects); 1.572 + sweepCallsiteClones(); 1.573 + savedStacks_.sweep(rt); 1.574 + 1.575 + if (global_ && IsObjectAboutToBeFinalized(global_.unsafeGet())) 1.576 + global_ = nullptr; 1.577 + 1.578 + if (selfHostingScriptSource && 1.579 + IsObjectAboutToBeFinalized((JSObject **) selfHostingScriptSource.unsafeGet())) 1.580 + { 1.581 + selfHostingScriptSource = nullptr; 1.582 + } 1.583 + 1.584 +#ifdef JS_ION 1.585 + if (jitCompartment_) 1.586 + jitCompartment_->sweep(fop); 1.587 +#endif 1.588 + 1.589 + /* 1.590 + * JIT code increments activeUseCount for any RegExpShared used by jit 1.591 + * code for the lifetime of the JIT script. Thus, we must perform 1.592 + * sweeping after clearing jit code. 1.593 + */ 1.594 + regExps.sweep(rt); 1.595 + 1.596 + if (debugScopes) 1.597 + debugScopes->sweep(rt); 1.598 + 1.599 + /* Finalize unreachable (key,value) pairs in all weak maps. */ 1.600 + WeakMapBase::sweepCompartment(this); 1.601 + } 1.602 + 1.603 + NativeIterator *ni = enumerators->next(); 1.604 + while (ni != enumerators) { 1.605 + JSObject *iterObj = ni->iterObj(); 1.606 + NativeIterator *next = ni->next(); 1.607 + if (gc::IsObjectAboutToBeFinalized(&iterObj)) 1.608 + ni->unlink(); 1.609 + ni = next; 1.610 + } 1.611 +} 1.612 + 1.613 +/* 1.614 + * Remove dead wrappers from the table. We must sweep all compartments, since 1.615 + * string entries in the crossCompartmentWrappers table are not marked during 1.616 + * markCrossCompartmentWrappers. 1.617 + */ 1.618 +void 1.619 +JSCompartment::sweepCrossCompartmentWrappers() 1.620 +{ 1.621 + JSRuntime *rt = runtimeFromMainThread(); 1.622 + 1.623 + gcstats::AutoPhase ap1(rt->gcStats, gcstats::PHASE_SWEEP_TABLES); 1.624 + gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_SWEEP_TABLES_WRAPPER); 1.625 + 1.626 + /* Remove dead wrappers from the table. */ 1.627 + for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) { 1.628 + CrossCompartmentKey key = e.front().key(); 1.629 + bool keyDying = IsCellAboutToBeFinalized(&key.wrapped); 1.630 + bool valDying = IsValueAboutToBeFinalized(e.front().value().unsafeGet()); 1.631 + bool dbgDying = key.debugger && IsObjectAboutToBeFinalized(&key.debugger); 1.632 + if (keyDying || valDying || dbgDying) { 1.633 + JS_ASSERT(key.kind != CrossCompartmentKey::StringWrapper); 1.634 + e.removeFront(); 1.635 + } else if (key.wrapped != e.front().key().wrapped || 1.636 + key.debugger != e.front().key().debugger) 1.637 + { 1.638 + e.rekeyFront(key); 1.639 + } 1.640 + } 1.641 +} 1.642 + 1.643 +void 1.644 +JSCompartment::purge() 1.645 +{ 1.646 + dtoaCache.purge(); 1.647 +} 1.648 + 1.649 +void 1.650 +JSCompartment::clearTables() 1.651 +{ 1.652 + global_ = nullptr; 1.653 + 1.654 + regExps.clearTables(); 1.655 + 1.656 + // No scripts should have run in this compartment. This is used when 1.657 + // merging a compartment that has been used off thread into another 1.658 + // compartment and zone. 1.659 + JS_ASSERT(crossCompartmentWrappers.empty()); 1.660 + JS_ASSERT_IF(callsiteClones.initialized(), callsiteClones.empty()); 1.661 +#ifdef JS_ION 1.662 + JS_ASSERT(!jitCompartment_); 1.663 +#endif 1.664 + JS_ASSERT(!debugScopes); 1.665 + JS_ASSERT(!gcWeakMapList); 1.666 + JS_ASSERT(enumerators->next() == enumerators); 1.667 + 1.668 + types.clearTables(); 1.669 + if (baseShapes.initialized()) 1.670 + baseShapes.clear(); 1.671 + if (initialShapes.initialized()) 1.672 + initialShapes.clear(); 1.673 + if (newTypeObjects.initialized()) 1.674 + newTypeObjects.clear(); 1.675 + if (lazyTypeObjects.initialized()) 1.676 + lazyTypeObjects.clear(); 1.677 + if (savedStacks_.initialized()) 1.678 + savedStacks_.clear(); 1.679 +} 1.680 + 1.681 +void 1.682 +JSCompartment::setObjectMetadataCallback(js::ObjectMetadataCallback callback) 1.683 +{ 1.684 + // Clear any jitcode in the runtime, which behaves differently depending on 1.685 + // whether there is a creation callback. 1.686 + ReleaseAllJITCode(runtime_->defaultFreeOp()); 1.687 + 1.688 + objectMetadataCallback = callback; 1.689 +} 1.690 + 1.691 +bool 1.692 +JSCompartment::hasScriptsOnStack() 1.693 +{ 1.694 + for (ActivationIterator iter(runtimeFromMainThread()); !iter.done(); ++iter) { 1.695 + if (iter->compartment() == this) 1.696 + return true; 1.697 + } 1.698 + 1.699 + return false; 1.700 +} 1.701 + 1.702 +static bool 1.703 +AddInnerLazyFunctionsFromScript(JSScript *script, AutoObjectVector &lazyFunctions) 1.704 +{ 1.705 + if (!script->hasObjects()) 1.706 + return true; 1.707 + ObjectArray *objects = script->objects(); 1.708 + for (size_t i = script->innerObjectsStart(); i < objects->length; i++) { 1.709 + JSObject *obj = objects->vector[i]; 1.710 + if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpretedLazy()) { 1.711 + if (!lazyFunctions.append(obj)) 1.712 + return false; 1.713 + } 1.714 + } 1.715 + return true; 1.716 +} 1.717 + 1.718 +static bool 1.719 +CreateLazyScriptsForCompartment(JSContext *cx) 1.720 +{ 1.721 + AutoObjectVector lazyFunctions(cx); 1.722 + 1.723 + // Find all live lazy scripts in the compartment, and via them all root 1.724 + // lazy functions in the compartment: those which have not been compiled, 1.725 + // which have a source object, indicating that they have a parent, and 1.726 + // which do not have an uncompiled enclosing script. The last condition is 1.727 + // so that we don't compile lazy scripts whose enclosing scripts failed to 1.728 + // compile, indicating that the lazy script did not escape the script. 1.729 + for (gc::CellIter i(cx->zone(), gc::FINALIZE_LAZY_SCRIPT); !i.done(); i.next()) { 1.730 + LazyScript *lazy = i.get<LazyScript>(); 1.731 + JSFunction *fun = lazy->functionNonDelazifying(); 1.732 + if (fun->compartment() == cx->compartment() && 1.733 + lazy->sourceObject() && !lazy->maybeScript() && 1.734 + !lazy->hasUncompiledEnclosingScript()) 1.735 + { 1.736 + MOZ_ASSERT(fun->isInterpretedLazy()); 1.737 + MOZ_ASSERT(lazy == fun->lazyScriptOrNull()); 1.738 + if (!lazyFunctions.append(fun)) 1.739 + return false; 1.740 + } 1.741 + } 1.742 + 1.743 + // Create scripts for each lazy function, updating the list of functions to 1.744 + // process with any newly exposed inner functions in created scripts. 1.745 + // A function cannot be delazified until its outer script exists. 1.746 + for (size_t i = 0; i < lazyFunctions.length(); i++) { 1.747 + JSFunction *fun = &lazyFunctions[i]->as<JSFunction>(); 1.748 + 1.749 + // lazyFunctions may have been populated with multiple functions for 1.750 + // a lazy script. 1.751 + if (!fun->isInterpretedLazy()) 1.752 + continue; 1.753 + 1.754 + JSScript *script = fun->getOrCreateScript(cx); 1.755 + if (!script) 1.756 + return false; 1.757 + if (!AddInnerLazyFunctionsFromScript(script, lazyFunctions)) 1.758 + return false; 1.759 + } 1.760 + 1.761 + return true; 1.762 +} 1.763 + 1.764 +bool 1.765 +JSCompartment::ensureDelazifyScriptsForDebugMode(JSContext *cx) 1.766 +{ 1.767 + MOZ_ASSERT(cx->compartment() == this); 1.768 + if ((debugModeBits & DebugNeedDelazification) && !CreateLazyScriptsForCompartment(cx)) 1.769 + return false; 1.770 + debugModeBits &= ~DebugNeedDelazification; 1.771 + return true; 1.772 +} 1.773 + 1.774 +bool 1.775 +JSCompartment::setDebugModeFromC(JSContext *cx, bool b, AutoDebugModeInvalidation &invalidate) 1.776 +{ 1.777 + bool enabledBefore = debugMode(); 1.778 + bool enabledAfter = (debugModeBits & DebugModeFromMask & ~DebugFromC) || b; 1.779 + 1.780 + // Enabling debug mode from C (vs of from JS) can only be done when no 1.781 + // scripts from the target compartment are on the stack. 1.782 + // 1.783 + // We do allow disabling debug mode while scripts are on the stack. In 1.784 + // that case the debug-mode code for those scripts remains, so subsequently 1.785 + // hooks may be called erroneously, even though debug mode is supposedly 1.786 + // off, and we have to live with it. 1.787 + bool onStack = false; 1.788 + if (enabledBefore != enabledAfter) { 1.789 + onStack = hasScriptsOnStack(); 1.790 + if (b && onStack) { 1.791 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_IDLE); 1.792 + return false; 1.793 + } 1.794 + } 1.795 + 1.796 + debugModeBits = (debugModeBits & ~DebugFromC) | (b ? DebugFromC : 0); 1.797 + JS_ASSERT(debugMode() == enabledAfter); 1.798 + if (enabledBefore != enabledAfter) { 1.799 + // Pass in a nullptr cx to not bother recompiling for JSD1, since 1.800 + // we're still enforcing the idle-stack invariant here. 1.801 + if (!updateJITForDebugMode(nullptr, invalidate)) 1.802 + return false; 1.803 + if (!enabledAfter) 1.804 + DebugScopes::onCompartmentLeaveDebugMode(this); 1.805 + } 1.806 + return true; 1.807 +} 1.808 + 1.809 +bool 1.810 +JSCompartment::updateJITForDebugMode(JSContext *maybecx, AutoDebugModeInvalidation &invalidate) 1.811 +{ 1.812 +#ifdef JS_ION 1.813 + // The AutoDebugModeInvalidation argument makes sure we can't forget to 1.814 + // invalidate, but it is also important not to run any scripts in this 1.815 + // compartment until the invalidate is destroyed. That is the caller's 1.816 + // responsibility. 1.817 + if (!jit::UpdateForDebugMode(maybecx, this, invalidate)) 1.818 + return false; 1.819 +#endif 1.820 + return true; 1.821 +} 1.822 + 1.823 +bool 1.824 +JSCompartment::addDebuggee(JSContext *cx, js::GlobalObject *global) 1.825 +{ 1.826 + AutoDebugModeInvalidation invalidate(this); 1.827 + return addDebuggee(cx, global, invalidate); 1.828 +} 1.829 + 1.830 +bool 1.831 +JSCompartment::addDebuggee(JSContext *cx, 1.832 + GlobalObject *globalArg, 1.833 + AutoDebugModeInvalidation &invalidate) 1.834 +{ 1.835 + Rooted<GlobalObject*> global(cx, globalArg); 1.836 + 1.837 + bool wasEnabled = debugMode(); 1.838 + if (!debuggees.put(global)) { 1.839 + js_ReportOutOfMemory(cx); 1.840 + return false; 1.841 + } 1.842 + debugModeBits |= DebugFromJS; 1.843 + if (!wasEnabled && !updateJITForDebugMode(cx, invalidate)) 1.844 + return false; 1.845 + return true; 1.846 +} 1.847 + 1.848 +bool 1.849 +JSCompartment::removeDebuggee(JSContext *cx, 1.850 + js::GlobalObject *global, 1.851 + js::GlobalObjectSet::Enum *debuggeesEnum) 1.852 +{ 1.853 + AutoDebugModeInvalidation invalidate(this); 1.854 + return removeDebuggee(cx, global, invalidate, debuggeesEnum); 1.855 +} 1.856 + 1.857 +bool 1.858 +JSCompartment::removeDebuggee(JSContext *cx, 1.859 + js::GlobalObject *global, 1.860 + AutoDebugModeInvalidation &invalidate, 1.861 + js::GlobalObjectSet::Enum *debuggeesEnum) 1.862 +{ 1.863 + bool wasEnabled = debugMode(); 1.864 + removeDebuggeeUnderGC(cx->runtime()->defaultFreeOp(), global, invalidate, debuggeesEnum); 1.865 + if (wasEnabled && !debugMode() && !updateJITForDebugMode(cx, invalidate)) 1.866 + return false; 1.867 + return true; 1.868 +} 1.869 + 1.870 +void 1.871 +JSCompartment::removeDebuggeeUnderGC(FreeOp *fop, 1.872 + js::GlobalObject *global, 1.873 + js::GlobalObjectSet::Enum *debuggeesEnum) 1.874 +{ 1.875 + AutoDebugModeInvalidation invalidate(this); 1.876 + removeDebuggeeUnderGC(fop, global, invalidate, debuggeesEnum); 1.877 +} 1.878 + 1.879 +void 1.880 +JSCompartment::removeDebuggeeUnderGC(FreeOp *fop, 1.881 + js::GlobalObject *global, 1.882 + AutoDebugModeInvalidation &invalidate, 1.883 + js::GlobalObjectSet::Enum *debuggeesEnum) 1.884 +{ 1.885 + bool wasEnabled = debugMode(); 1.886 + JS_ASSERT(debuggees.has(global)); 1.887 + if (debuggeesEnum) 1.888 + debuggeesEnum->removeFront(); 1.889 + else 1.890 + debuggees.remove(global); 1.891 + 1.892 + if (debuggees.empty()) { 1.893 + debugModeBits &= ~DebugFromJS; 1.894 + if (wasEnabled && !debugMode()) 1.895 + DebugScopes::onCompartmentLeaveDebugMode(this); 1.896 + } 1.897 +} 1.898 + 1.899 +void 1.900 +JSCompartment::clearBreakpointsIn(FreeOp *fop, js::Debugger *dbg, HandleObject handler) 1.901 +{ 1.902 + for (gc::CellIter i(zone(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) { 1.903 + JSScript *script = i.get<JSScript>(); 1.904 + if (script->compartment() == this && script->hasAnyBreakpointsOrStepMode()) 1.905 + script->clearBreakpointsIn(fop, dbg, handler); 1.906 + } 1.907 +} 1.908 + 1.909 +void 1.910 +JSCompartment::clearTraps(FreeOp *fop) 1.911 +{ 1.912 + MinorGC(fop->runtime(), JS::gcreason::EVICT_NURSERY); 1.913 + for (gc::CellIter i(zone(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) { 1.914 + JSScript *script = i.get<JSScript>(); 1.915 + if (script->compartment() == this && script->hasAnyBreakpointsOrStepMode()) 1.916 + script->clearTraps(fop); 1.917 + } 1.918 +} 1.919 + 1.920 +void 1.921 +JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, 1.922 + size_t *tiAllocationSiteTables, 1.923 + size_t *tiArrayTypeTables, 1.924 + size_t *tiObjectTypeTables, 1.925 + size_t *compartmentObject, 1.926 + size_t *shapesCompartmentTables, 1.927 + size_t *crossCompartmentWrappersArg, 1.928 + size_t *regexpCompartment, 1.929 + size_t *debuggeesSet, 1.930 + size_t *savedStacksSet) 1.931 +{ 1.932 + *compartmentObject += mallocSizeOf(this); 1.933 + types.addSizeOfExcludingThis(mallocSizeOf, tiAllocationSiteTables, 1.934 + tiArrayTypeTables, tiObjectTypeTables); 1.935 + *shapesCompartmentTables += baseShapes.sizeOfExcludingThis(mallocSizeOf) 1.936 + + initialShapes.sizeOfExcludingThis(mallocSizeOf) 1.937 + + newTypeObjects.sizeOfExcludingThis(mallocSizeOf) 1.938 + + lazyTypeObjects.sizeOfExcludingThis(mallocSizeOf); 1.939 + *crossCompartmentWrappersArg += crossCompartmentWrappers.sizeOfExcludingThis(mallocSizeOf); 1.940 + *regexpCompartment += regExps.sizeOfExcludingThis(mallocSizeOf); 1.941 + *debuggeesSet += debuggees.sizeOfExcludingThis(mallocSizeOf); 1.942 + *savedStacksSet += savedStacks_.sizeOfExcludingThis(mallocSizeOf); 1.943 +} 1.944 + 1.945 +void 1.946 +JSCompartment::adoptWorkerAllocator(Allocator *workerAllocator) 1.947 +{ 1.948 + zone()->allocator.arenas.adoptArenas(runtimeFromMainThread(), &workerAllocator->arenas); 1.949 +}