LCOV - code coverage report
Current view: top level - js/src - jscompartment.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 287 704 40.8 %
Date: 2017-07-14 16:53:18 Functions: 34 87 39.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "jscompartmentinlines.h"
       8             : 
       9             : #include "mozilla/DebugOnly.h"
      10             : #include "mozilla/MemoryReporting.h"
      11             : 
      12             : #include <stddef.h>
      13             : 
      14             : #include "jscntxt.h"
      15             : #include "jsfriendapi.h"
      16             : #include "jsgc.h"
      17             : #include "jsiter.h"
      18             : #include "jswatchpoint.h"
      19             : #include "jswrapper.h"
      20             : 
      21             : #include "gc/Marking.h"
      22             : #include "gc/Policy.h"
      23             : #include "jit/JitCompartment.h"
      24             : #include "jit/JitOptions.h"
      25             : #include "js/Date.h"
      26             : #include "js/Proxy.h"
      27             : #include "js/RootingAPI.h"
      28             : #include "proxy/DeadObjectProxy.h"
      29             : #include "vm/Debugger.h"
      30             : #include "vm/StopIterationObject.h"
      31             : #include "vm/WrapperObject.h"
      32             : 
      33             : #include "jsatominlines.h"
      34             : #include "jsfuninlines.h"
      35             : #include "jsgcinlines.h"
      36             : #include "jsobjinlines.h"
      37             : #include "jsscriptinlines.h"
      38             : 
      39             : #include "vm/NativeObject-inl.h"
      40             : #include "vm/UnboxedObject-inl.h"
      41             : 
      42             : using namespace js;
      43             : using namespace js::gc;
      44             : using namespace js::jit;
      45             : 
      46             : using mozilla::DebugOnly;
      47             : using mozilla::PodArrayZero;
      48             : 
      49         315 : JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options = JS::CompartmentOptions())
      50         315 :   : creationOptions_(options.creationOptions()),
      51         315 :     behaviors_(options.behaviors()),
      52             :     zone_(zone),
      53         315 :     runtime_(zone->runtimeFromAnyThread()),
      54             :     principals_(nullptr),
      55             :     isSystem_(false),
      56             :     isAtomsCompartment_(false),
      57             :     isSelfHosting(false),
      58             :     marked(true),
      59             :     warnedAboutDateToLocaleFormat(false),
      60             :     warnedAboutExprClosure(false),
      61             :     warnedAboutForEach(false),
      62             :     warnedAboutStringGenericsMethods(0),
      63             : #ifdef DEBUG
      64             :     firedOnNewGlobalObject(false),
      65             : #endif
      66             :     global_(nullptr),
      67             :     enterCompartmentDepth(0),
      68             :     performanceMonitoring(runtime_),
      69             :     data(nullptr),
      70             :     allocationMetadataBuilder(nullptr),
      71             :     lastAnimationTime(0),
      72             :     regExps(zone),
      73             :     globalWriteBarriered(0),
      74             :     detachedTypedObjects(0),
      75         315 :     objectMetadataState(ImmediateMetadata()),
      76             :     selfHostingScriptSource(nullptr),
      77             :     objectMetadataTable(nullptr),
      78             :     innerViews(zone),
      79             :     lazyArrayBuffers(nullptr),
      80             :     wasm(zone),
      81             :     nonSyntacticLexicalEnvironments_(nullptr),
      82             :     gcIncomingGrayPointers(nullptr),
      83             :     debugModeBits(0),
      84             :     validAccessPtr(nullptr),
      85         315 :     randomKeyGenerator_(runtime_->forkRandomKeyGenerator()),
      86             :     watchpointMap(nullptr),
      87             :     scriptCountsMap(nullptr),
      88             :     scriptNameMap(nullptr),
      89             :     debugScriptMap(nullptr),
      90             :     debugEnvs(nullptr),
      91             :     enumerators(nullptr),
      92             :     lastCachedNativeIterator(nullptr),
      93             :     compartmentStats_(nullptr),
      94             :     scheduledForDestruction(false),
      95             :     maybeAlive(true),
      96             :     jitCompartment_(nullptr),
      97             :     mappedArgumentsTemplate_(nullptr),
      98             :     unmappedArgumentsTemplate_(nullptr),
      99        1890 :     lcovOutput()
     100             : {
     101         315 :     PodArrayZero(sawDeprecatedLanguageExtension);
     102         315 :     runtime_->numCompartments++;
     103         315 :     MOZ_ASSERT_IF(creationOptions_.mergeable(),
     104             :                   creationOptions_.invisibleToDebugger());
     105         315 : }
     106             : 
     107           0 : JSCompartment::~JSCompartment()
     108             : {
     109           0 :     reportTelemetry();
     110             : 
     111             :     // Write the code coverage information in a file.
     112           0 :     JSRuntime* rt = runtimeFromActiveCooperatingThread();
     113           0 :     if (rt->lcovOutput().isEnabled())
     114           0 :         rt->lcovOutput().writeLCovResult(lcovOutput);
     115             : 
     116           0 :     js_delete(jitCompartment_);
     117           0 :     js_delete(watchpointMap);
     118           0 :     js_delete(scriptCountsMap);
     119           0 :     js_delete(scriptNameMap);
     120           0 :     js_delete(debugScriptMap);
     121           0 :     js_delete(debugEnvs);
     122           0 :     js_delete(objectMetadataTable);
     123           0 :     js_delete(lazyArrayBuffers);
     124           0 :     js_delete(nonSyntacticLexicalEnvironments_);
     125           0 :     js_free(enumerators);
     126             : 
     127             : #ifdef DEBUG
     128             :     // Avoid assertion destroying the unboxed layouts list if the embedding
     129             :     // leaked GC things.
     130           0 :     if (!rt->gc.shutdownCollectedEverything())
     131           0 :         unboxedLayouts.clear();
     132             : #endif
     133             : 
     134           0 :     runtime_->numCompartments--;
     135           0 : }
     136             : 
     137             : bool
     138         315 : JSCompartment::init(JSContext* maybecx)
     139             : {
     140             :     /*
     141             :      * maybecx is null when called to create the atoms compartment from
     142             :      * JSRuntime::init().
     143             :      *
     144             :      * As a hack, we clear our timezone cache every time we create a new
     145             :      * compartment. This ensures that the cache is always relatively fresh, but
     146             :      * shouldn't interfere with benchmarks that create tons of date objects
     147             :      * (unless they also create tons of iframes, which seems unlikely).
     148             :      */
     149         315 :     JS::ResetTimeZone();
     150             : 
     151         315 :     if (!crossCompartmentWrappers.init(0)) {
     152           0 :         if (maybecx)
     153           0 :             ReportOutOfMemory(maybecx);
     154           0 :         return false;
     155             :     }
     156             : 
     157         315 :     enumerators = NativeIterator::allocateSentinel(maybecx);
     158         315 :     if (!enumerators)
     159           0 :         return false;
     160             : 
     161         315 :     if (!savedStacks_.init() || !varNames_.init() || !templateLiteralMap_.init()) {
     162           0 :         if (maybecx)
     163           0 :             ReportOutOfMemory(maybecx);
     164           0 :         return false;
     165             :     }
     166             : 
     167         315 :     return true;
     168             : }
     169             : 
     170             : jit::JitRuntime*
     171           4 : JSRuntime::createJitRuntime(JSContext* cx)
     172             : {
     173             :     // The shared stubs are created in the atoms compartment, which may be
     174             :     // accessed by other threads with an exclusive context.
     175           8 :     AutoLockForExclusiveAccess atomsLock(cx);
     176             : 
     177           4 :     MOZ_ASSERT(!jitRuntime_);
     178             : 
     179           4 :     if (!CanLikelyAllocateMoreExecutableMemory()) {
     180             :         // Report OOM instead of potentially hitting the MOZ_CRASH below.
     181           0 :         ReportOutOfMemory(cx);
     182           0 :         return nullptr;
     183             :     }
     184             : 
     185           4 :     jit::JitRuntime* jrt = cx->new_<jit::JitRuntime>(cx->runtime());
     186           4 :     if (!jrt)
     187           0 :         return nullptr;
     188             : 
     189             :     // Protect jitRuntime_ from being observed (by InterruptRunningJitCode)
     190             :     // while it is being initialized. Unfortunately, initialization depends on
     191             :     // jitRuntime_ being non-null, so we can't just wait to assign jitRuntime_.
     192           8 :     JitRuntime::AutoPreventBackedgePatching apbp(cx->runtime(), jrt);
     193           4 :     jitRuntime_ = jrt;
     194             : 
     195           8 :     AutoEnterOOMUnsafeRegion noOOM;
     196           4 :     if (!jitRuntime_->initialize(cx, atomsLock)) {
     197             :         // Handling OOM here is complicated: if we delete jitRuntime_ now, we
     198             :         // will destroy the ExecutableAllocator, even though there may still be
     199             :         // JitCode instances holding references to ExecutablePools.
     200           0 :         noOOM.crash("OOM in createJitRuntime");
     201             :     }
     202             : 
     203           4 :     return jitRuntime_;
     204             : }
     205             : 
     206             : bool
     207       14882 : JSCompartment::ensureJitCompartmentExists(JSContext* cx)
     208             : {
     209             :     using namespace js::jit;
     210       14882 :     if (jitCompartment_)
     211       14598 :         return true;
     212             : 
     213         284 :     if (!zone()->getJitZone(cx))
     214           0 :         return false;
     215             : 
     216             :     /* Set the compartment early, so linking works. */
     217         284 :     jitCompartment_ = cx->new_<JitCompartment>();
     218             : 
     219         284 :     if (!jitCompartment_)
     220           0 :         return false;
     221             : 
     222         284 :     if (!jitCompartment_->initialize(cx)) {
     223           0 :         js_delete(jitCompartment_);
     224           0 :         jitCompartment_ = nullptr;
     225           0 :         return false;
     226             :     }
     227             : 
     228         284 :     return true;
     229             : }
     230             : 
     231             : #ifdef JSGC_HASH_TABLE_CHECKS
     232             : 
     233             : void
     234           0 : js::DtoaCache::checkCacheAfterMovingGC()
     235             : {
     236           0 :     MOZ_ASSERT(!s || !IsForwarded(s));
     237           0 : }
     238             : 
     239             : namespace {
     240             : struct CheckGCThingAfterMovingGCFunctor {
     241           0 :     template <class T> void operator()(T* t) { CheckGCThingAfterMovingGC(*t); }
     242             : };
     243             : } // namespace (anonymous)
     244             : 
     245             : void
     246           0 : JSCompartment::checkWrapperMapAfterMovingGC()
     247             : {
     248             :     /*
     249             :      * Assert that the postbarriers have worked and that nothing is left in
     250             :      * wrapperMap that points into the nursery, and that the hash table entries
     251             :      * are discoverable.
     252             :      */
     253           0 :     for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
     254           0 :         e.front().mutableKey().applyToWrapped(CheckGCThingAfterMovingGCFunctor());
     255           0 :         e.front().mutableKey().applyToDebugger(CheckGCThingAfterMovingGCFunctor());
     256             : 
     257           0 :         WrapperMap::Ptr ptr = crossCompartmentWrappers.lookup(e.front().key());
     258           0 :         MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &e.front());
     259             :     }
     260           0 : }
     261             : 
     262             : #endif // JSGC_HASH_TABLE_CHECKS
     263             : 
     264             : bool
     265        9609 : JSCompartment::putWrapper(JSContext* cx, const CrossCompartmentKey& wrapped,
     266             :                           const js::Value& wrapper)
     267             : {
     268        9609 :     MOZ_ASSERT(wrapped.is<JSString*>() == wrapper.isString());
     269        9609 :     MOZ_ASSERT_IF(!wrapped.is<JSString*>(), wrapper.isObject());
     270             : 
     271        9609 :     if (!crossCompartmentWrappers.put(wrapped, wrapper)) {
     272           0 :         ReportOutOfMemory(cx);
     273           0 :         return false;
     274             :     }
     275             : 
     276        9609 :     return true;
     277             : }
     278             : 
     279             : static JSString*
     280         330 : CopyStringPure(JSContext* cx, JSString* str)
     281             : {
     282             :     /*
     283             :      * Directly allocate the copy in the destination compartment, rather than
     284             :      * first flattening it (and possibly allocating in source compartment),
     285             :      * because we don't know whether the flattening will pay off later.
     286             :      */
     287             : 
     288         330 :     size_t len = str->length();
     289             :     JSString* copy;
     290         330 :     if (str->isLinear()) {
     291             :         /* Only use AutoStableStringChars if the NoGC allocation fails. */
     292         326 :         if (str->hasLatin1Chars()) {
     293         488 :             JS::AutoCheckCannotGC nogc;
     294         244 :             copy = NewStringCopyN<NoGC>(cx, str->asLinear().latin1Chars(nogc), len);
     295             :         } else {
     296         164 :             JS::AutoCheckCannotGC nogc;
     297          82 :             copy = NewStringCopyNDontDeflate<NoGC>(cx, str->asLinear().twoByteChars(nogc), len);
     298             :         }
     299         326 :         if (copy)
     300         326 :             return copy;
     301             : 
     302           0 :         AutoStableStringChars chars(cx);
     303           0 :         if (!chars.init(cx, str))
     304           0 :             return nullptr;
     305             : 
     306           0 :         return chars.isLatin1()
     307           0 :                ? NewStringCopyN<CanGC>(cx, chars.latin1Range().begin().get(), len)
     308           0 :                : NewStringCopyNDontDeflate<CanGC>(cx, chars.twoByteRange().begin().get(), len);
     309             :     }
     310             : 
     311           4 :     if (str->hasLatin1Chars()) {
     312           8 :         ScopedJSFreePtr<Latin1Char> copiedChars;
     313           4 :         if (!str->asRope().copyLatin1CharsZ(cx, copiedChars))
     314           0 :             return nullptr;
     315             : 
     316           4 :         return NewString<CanGC>(cx, copiedChars.forget(), len);
     317             :     }
     318             : 
     319           0 :     ScopedJSFreePtr<char16_t> copiedChars;
     320           0 :     if (!str->asRope().copyTwoByteCharsZ(cx, copiedChars))
     321           0 :         return nullptr;
     322             : 
     323           0 :     return NewStringDontDeflate<CanGC>(cx, copiedChars.forget(), len);
     324             : }
     325             : 
     326             : bool
     327        5447 : JSCompartment::wrap(JSContext* cx, MutableHandleString strp)
     328             : {
     329        5447 :     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(this));
     330        5447 :     MOZ_ASSERT(cx->compartment() == this);
     331             : 
     332             :     /* If the string is already in this compartment, we are done. */
     333        5447 :     JSString* str = strp;
     334        5447 :     if (str->zoneFromAnyThread() == zone())
     335         603 :         return true;
     336             : 
     337             :     /*
     338             :      * If the string is an atom, we don't have to copy, but we do need to mark
     339             :      * the atom as being in use by the new zone.
     340             :      */
     341        4844 :     if (str->isAtom()) {
     342        4505 :         cx->markAtom(&str->asAtom());
     343        4505 :         return true;
     344             :     }
     345             : 
     346             :     /* Check the cache. */
     347         678 :     RootedValue key(cx, StringValue(str));
     348         339 :     if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(CrossCompartmentKey(key))) {
     349           9 :         strp.set(p->value().get().toString());
     350           9 :         return true;
     351             :     }
     352             : 
     353             :     /* No dice. Make a copy, and cache it. */
     354         330 :     JSString* copy = CopyStringPure(cx, str);
     355         330 :     if (!copy)
     356           0 :         return false;
     357         330 :     if (!putWrapper(cx, CrossCompartmentKey(key), StringValue(copy)))
     358           0 :         return false;
     359             : 
     360         330 :     strp.set(copy);
     361         330 :     return true;
     362             : }
     363             : 
     364             : bool
     365       44301 : JSCompartment::getNonWrapperObjectForCurrentCompartment(JSContext* cx, MutableHandleObject obj)
     366             : {
     367             :     // Ensure that we have entered a compartment.
     368       44301 :     MOZ_ASSERT(cx->global());
     369             : 
     370             :     // If we have a cross-compartment wrapper, make sure that the cx isn't
     371             :     // associated with the self-hosting global. We don't want to create
     372             :     // wrappers for objects in other runtimes, which may be the case for the
     373             :     // self-hosting global.
     374       44301 :     MOZ_ASSERT(!cx->runtime()->isSelfHostingGlobal(cx->global()));
     375       44301 :     MOZ_ASSERT(!cx->runtime()->isSelfHostingGlobal(&obj->global()));
     376             : 
     377             :     // The object is already in the right compartment. Normally same-
     378             :     // compartment returns the object itself, however, windows are always
     379             :     // wrapped by a proxy, so we have to check for that case here manually.
     380       44301 :     if (obj->compartment() == this) {
     381       13087 :         obj.set(ToWindowProxyIfWindow(obj));
     382       13087 :         return true;
     383             :     }
     384             : 
     385             :     // Note that if the object is same-compartment, but has been wrapped into a
     386             :     // different compartment, we need to unwrap it and return the bare same-
     387             :     // compartment object. Note again that windows are always wrapped by a
     388             :     // WindowProxy even when same-compartment so take care not to strip this
     389             :     // particular wrapper.
     390       62428 :     RootedObject objectPassedToWrap(cx, obj);
     391       31214 :     obj.set(UncheckedUnwrap(obj, /* stopAtWindowProxy = */ true));
     392       31214 :     if (obj->compartment() == this) {
     393        5131 :         MOZ_ASSERT(!IsWindow(obj));
     394        5131 :         return true;
     395             :     }
     396             : 
     397             :     // Translate StopIteration singleton.
     398       26083 :     if (obj->is<StopIterationObject>()) {
     399             :         // StopIteration isn't a constructor, but it's stored in GlobalObject
     400             :         // as one, out of laziness. Hence the GetBuiltinConstructor call here.
     401          10 :         RootedObject stopIteration(cx);
     402           5 :         if (!GetBuiltinConstructor(cx, JSProto_StopIteration, &stopIteration))
     403           0 :             return false;
     404           5 :         obj.set(stopIteration);
     405           5 :         return true;
     406             :     }
     407             : 
     408             :     // Invoke the prewrap callback. The prewrap callback is responsible for
     409             :     // doing similar reification as above, but can account for any additional
     410             :     // embedder requirements.
     411             :     //
     412             :     // We're a bit worried about infinite recursion here, so we do a check -
     413             :     // see bug 809295.
     414       26078 :     auto preWrap = cx->runtime()->wrapObjectCallbacks->preWrap;
     415       26078 :     if (!CheckSystemRecursionLimit(cx))
     416           0 :         return false;
     417       26078 :     if (preWrap) {
     418       26078 :         preWrap(cx, cx->global(), obj, objectPassedToWrap, obj);
     419       26078 :         if (!obj)
     420           0 :             return false;
     421             :     }
     422       26078 :     MOZ_ASSERT(!IsWindow(obj));
     423             : 
     424       26078 :     return true;
     425             : }
     426             : 
     427             : bool
     428       23354 : JSCompartment::getOrCreateWrapper(JSContext* cx, HandleObject existing, MutableHandleObject obj)
     429             : {
     430             :     // If we already have a wrapper for this value, use it.
     431       46708 :     RootedValue key(cx, ObjectValue(*obj));
     432       23354 :     if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(CrossCompartmentKey(key))) {
     433       14087 :         obj.set(&p->value().get().toObject());
     434       14087 :         MOZ_ASSERT(obj->is<CrossCompartmentWrapperObject>());
     435       14087 :         return true;
     436             :     }
     437             : 
     438             :     // Create a new wrapper for the object.
     439        9267 :     auto wrap = cx->runtime()->wrapObjectCallbacks->wrap;
     440       18534 :     RootedObject wrapper(cx, wrap(cx, existing, obj));
     441        9267 :     if (!wrapper)
     442           0 :         return false;
     443             : 
     444             :     // We maintain the invariant that the key in the cross-compartment wrapper
     445             :     // map is always directly wrapped by the value.
     446        9267 :     MOZ_ASSERT(Wrapper::wrappedObject(wrapper) == &key.get().toObject());
     447             : 
     448        9267 :     if (!putWrapper(cx, CrossCompartmentKey(key), ObjectValue(*wrapper))) {
     449             :         // Enforce the invariant that all cross-compartment wrapper object are
     450             :         // in the map by nuking the wrapper if we couldn't add it.
     451             :         // Unfortunately it's possible for the wrapper to still be marked if we
     452             :         // took this path, for example if the object metadata callback stashes a
     453             :         // reference to it.
     454           0 :         if (wrapper->is<CrossCompartmentWrapperObject>())
     455           0 :             NukeCrossCompartmentWrapper(cx, wrapper);
     456           0 :         return false;
     457             :     }
     458             : 
     459        9267 :     obj.set(wrapper);
     460        9267 :     return true;
     461             : }
     462             : 
     463             : bool
     464       46167 : JSCompartment::wrap(JSContext* cx, MutableHandleObject obj)
     465             : {
     466       46167 :     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(this));
     467       46167 :     MOZ_ASSERT(cx->compartment() == this);
     468             : 
     469       46167 :     if (!obj)
     470        1876 :         return true;
     471             : 
     472       88582 :     AutoDisableProxyCheck adpc;
     473             : 
     474             :     // Anything we're wrapping has already escaped into script, so must have
     475             :     // been unmarked-gray at some point in the past.
     476       44291 :     MOZ_ASSERT(JS::ObjectIsNotGray(obj));
     477             : 
     478             :     // The passed object may already be wrapped, or may fit a number of special
     479             :     // cases that we need to check for and manually correct.
     480       44291 :     if (!getNonWrapperObjectForCurrentCompartment(cx, obj))
     481           0 :         return false;
     482             : 
     483             :     // If the reification above did not result in a same-compartment object,
     484             :     // get or create a new wrapper object in this compartment for it.
     485       44291 :     if (obj->compartment() != this) {
     486       23344 :         if (!getOrCreateWrapper(cx, nullptr, obj))
     487           0 :             return false;
     488             :     }
     489             : 
     490             :     // Ensure that the wrapper is also exposed.
     491       44291 :     ExposeObjectToActiveJS(obj);
     492       44291 :     return true;
     493             : }
     494             : 
     495             : bool
     496          10 : JSCompartment::rewrap(JSContext* cx, MutableHandleObject obj, HandleObject existingArg)
     497             : {
     498          10 :     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(this));
     499          10 :     MOZ_ASSERT(cx->compartment() == this);
     500          10 :     MOZ_ASSERT(obj);
     501          10 :     MOZ_ASSERT(existingArg);
     502          10 :     MOZ_ASSERT(existingArg->compartment() == cx->compartment());
     503          10 :     MOZ_ASSERT(IsDeadProxyObject(existingArg));
     504             : 
     505          20 :     AutoDisableProxyCheck adpc;
     506             : 
     507             :     // It may not be possible to re-use existing; if so, clear it so that we
     508             :     // are forced to create a new wrapper. Note that this cannot call out to
     509             :     // |wrap| because of the different gray unmarking semantics.
     510          20 :     RootedObject existing(cx, existingArg);
     511          30 :     if (existing->hasStaticPrototype() ||
     512             :         // Note: Class asserted above, so all that's left to check is callability
     513          20 :         existing->isCallable() ||
     514          10 :         obj->isCallable())
     515             :     {
     516           0 :         existing.set(nullptr);
     517             :     }
     518             : 
     519             :     // The passed object may already be wrapped, or may fit a number of special
     520             :     // cases that we need to check for and manually correct.
     521          10 :     if (!getNonWrapperObjectForCurrentCompartment(cx, obj))
     522           0 :         return false;
     523             : 
     524             :     // If the reification above resulted in a same-compartment object, we do
     525             :     // not need to create or return an existing wrapper.
     526          10 :     if (obj->compartment() == this)
     527           0 :         return true;
     528             : 
     529          10 :     return getOrCreateWrapper(cx, existing, obj);
     530             : }
     531             : 
     532             : bool
     533        1920 : JSCompartment::wrap(JSContext* cx, MutableHandle<PropertyDescriptor> desc)
     534             : {
     535        1920 :     if (!wrap(cx, desc.object()))
     536           0 :         return false;
     537             : 
     538        1920 :     if (desc.hasGetterObject()) {
     539        1301 :         if (!wrap(cx, desc.getterObject()))
     540           0 :             return false;
     541             :     }
     542        1920 :     if (desc.hasSetterObject()) {
     543          53 :         if (!wrap(cx, desc.setterObject()))
     544           0 :             return false;
     545             :     }
     546             : 
     547        1920 :     return wrap(cx, desc.value());
     548             : }
     549             : 
     550             : bool
     551           0 : JSCompartment::wrap(JSContext* cx, MutableHandle<GCVector<Value>> vec)
     552             : {
     553           0 :     for (size_t i = 0; i < vec.length(); ++i) {
     554           0 :         if (!wrap(cx, vec[i]))
     555           0 :             return false;
     556             :     }
     557           0 :     return true;
     558             : }
     559             : 
     560             : LexicalEnvironmentObject*
     561         188 : JSCompartment::getOrCreateNonSyntacticLexicalEnvironment(JSContext* cx, HandleObject enclosing)
     562             : {
     563         188 :     if (!nonSyntacticLexicalEnvironments_) {
     564           3 :         nonSyntacticLexicalEnvironments_ = cx->new_<ObjectWeakMap>(cx);
     565           3 :         if (!nonSyntacticLexicalEnvironments_ || !nonSyntacticLexicalEnvironments_->init())
     566           0 :             return nullptr;
     567             :     }
     568             : 
     569             :     // The key is the unwrapped dynamic scope, as we may be creating different
     570             :     // WithEnvironmentObject wrappers each time.
     571         188 :     MOZ_ASSERT(!enclosing->as<WithEnvironmentObject>().isSyntactic());
     572         376 :     RootedObject key(cx, &enclosing->as<WithEnvironmentObject>().object());
     573         376 :     RootedObject lexicalEnv(cx, nonSyntacticLexicalEnvironments_->lookup(key));
     574             : 
     575         188 :     if (!lexicalEnv) {
     576          65 :         lexicalEnv = LexicalEnvironmentObject::createNonSyntactic(cx, enclosing);
     577          65 :         if (!lexicalEnv)
     578           0 :             return nullptr;
     579          65 :         if (!nonSyntacticLexicalEnvironments_->add(cx, key, lexicalEnv))
     580           0 :             return nullptr;
     581             :     }
     582             : 
     583         188 :     return &lexicalEnv->as<LexicalEnvironmentObject>();
     584             : }
     585             : 
     586             : LexicalEnvironmentObject*
     587         145 : JSCompartment::getNonSyntacticLexicalEnvironment(JSObject* enclosing) const
     588             : {
     589         145 :     if (!nonSyntacticLexicalEnvironments_)
     590         105 :         return nullptr;
     591          40 :     if (!enclosing->is<WithEnvironmentObject>())
     592           0 :         return nullptr;
     593          40 :     JSObject* key = &enclosing->as<WithEnvironmentObject>().object();
     594          40 :     JSObject* lexicalEnv = nonSyntacticLexicalEnvironments_->lookup(key);
     595          40 :     if (!lexicalEnv)
     596           0 :         return nullptr;
     597          40 :     return &lexicalEnv->as<LexicalEnvironmentObject>();
     598             : }
     599             : 
     600             : bool
     601        2944 : JSCompartment::addToVarNames(JSContext* cx, JS::Handle<JSAtom*> name)
     602             : {
     603        2944 :     MOZ_ASSERT(name);
     604        2944 :     MOZ_ASSERT(!isAtomsCompartment());
     605             : 
     606        2944 :     if (varNames_.put(name))
     607        2944 :         return true;
     608             : 
     609           0 :     ReportOutOfMemory(cx);
     610           0 :     return false;
     611             : }
     612             : 
     613             : /* static */ HashNumber
     614          12 : TemplateRegistryHashPolicy::hash(const Lookup& lookup)
     615             : {
     616          12 :     size_t length = GetAnyBoxedOrUnboxedInitializedLength(lookup);
     617          12 :     HashNumber hash = 0;
     618          48 :     for (uint32_t i = 0; i < length; i++) {
     619          36 :         JSAtom& lookupAtom = GetAnyBoxedOrUnboxedDenseElement(lookup, i).toString()->asAtom();
     620          36 :         hash = mozilla::AddToHash(hash, lookupAtom.hash());
     621             :     }
     622          12 :     return hash;
     623             : }
     624             : 
     625             : /* static */ bool
     626          10 : TemplateRegistryHashPolicy::match(const Key& key, const Lookup& lookup)
     627             : {
     628          10 :     size_t length = GetAnyBoxedOrUnboxedInitializedLength(lookup);
     629          10 :     if (GetAnyBoxedOrUnboxedInitializedLength(key) != length)
     630           0 :         return false;
     631             : 
     632          40 :     for (uint32_t i = 0; i < length; i++) {
     633          30 :         JSAtom* a = &GetAnyBoxedOrUnboxedDenseElement(key, i).toString()->asAtom();
     634          30 :         JSAtom* b = &GetAnyBoxedOrUnboxedDenseElement(lookup, i).toString()->asAtom();
     635          30 :         if (a != b)
     636           0 :             return false;
     637             :     }
     638             : 
     639          10 :     return true;
     640             : }
     641             : 
     642             : bool
     643          11 : JSCompartment::getTemplateLiteralObject(JSContext* cx, HandleObject rawStrings,
     644             :                                         MutableHandleObject templateObj)
     645             : {
     646          11 :     if (TemplateRegistry::AddPtr p = templateLiteralMap_.lookupForAdd(rawStrings)) {
     647          10 :         templateObj.set(p->value());
     648             : 
     649             :         // The template object must have been frozen when it was added to the
     650             :         // registry.
     651          10 :         MOZ_ASSERT(!templateObj->nonProxyIsExtensible());
     652             :     } else {
     653           1 :         MOZ_ASSERT(templateObj->nonProxyIsExtensible());
     654           2 :         RootedValue rawValue(cx, ObjectValue(*rawStrings));
     655           1 :         if (!DefineProperty(cx, templateObj, cx->names().raw, rawValue, nullptr, nullptr, 0))
     656           0 :             return false;
     657           1 :         if (!FreezeObject(cx, rawStrings))
     658           0 :             return false;
     659           1 :         if (!FreezeObject(cx, templateObj))
     660           0 :             return false;
     661             : 
     662           1 :         if (!templateLiteralMap_.relookupOrAdd(p, rawStrings, templateObj))
     663           0 :             return false;
     664             :     }
     665             : 
     666          11 :     return true;
     667             : }
     668             : 
     669             : JSObject*
     670           0 : JSCompartment::getExistingTemplateLiteralObject(JSObject* rawStrings)
     671             : {
     672           0 :     TemplateRegistry::Ptr p = templateLiteralMap_.lookup(rawStrings);
     673           0 :     MOZ_ASSERT(p);
     674           0 :     return p->value();
     675             : }
     676             : 
     677             : void
     678           0 : JSCompartment::traceOutgoingCrossCompartmentWrappers(JSTracer* trc)
     679             : {
     680           0 :     MOZ_ASSERT(JS::CurrentThreadIsHeapMajorCollecting());
     681           0 :     MOZ_ASSERT(!zone()->isCollectingFromAnyThread() || trc->runtime()->gc.isHeapCompacting());
     682             : 
     683           0 :     for (NonStringWrapperEnum e(this); !e.empty(); e.popFront()) {
     684           0 :         if (e.front().key().is<JSObject*>()) {
     685           0 :             Value v = e.front().value().unbarrieredGet();
     686           0 :             ProxyObject* wrapper = &v.toObject().as<ProxyObject>();
     687             : 
     688             :             /*
     689             :              * We have a cross-compartment wrapper. Its private pointer may
     690             :              * point into the compartment being collected, so we should mark it.
     691             :              */
     692           0 :             TraceEdge(trc, wrapper->slotOfPrivate(), "cross-compartment wrapper");
     693             :         }
     694             :     }
     695           0 : }
     696             : 
     697             : /* static */ void
     698           1 : JSCompartment::traceIncomingCrossCompartmentEdgesForZoneGC(JSTracer* trc)
     699             : {
     700           2 :     gcstats::AutoPhase ap(trc->runtime()->gc.stats(), gcstats::PhaseKind::MARK_CCWS);
     701           1 :     MOZ_ASSERT(JS::CurrentThreadIsHeapMajorCollecting());
     702         223 :     for (CompartmentsIter c(trc->runtime(), SkipAtoms); !c.done(); c.next()) {
     703         222 :         if (!c->zone()->isCollecting())
     704           0 :             c->traceOutgoingCrossCompartmentWrappers(trc);
     705             :     }
     706           1 :     Debugger::traceIncomingCrossCompartmentEdges(trc);
     707           1 : }
     708             : 
     709             : void
     710          26 : JSCompartment::traceGlobal(JSTracer* trc)
     711             : {
     712             :     // Trace things reachable from the compartment's global. Note that these
     713             :     // edges must be swept too in case the compartment is live but the global is
     714             :     // not.
     715             : 
     716          26 :     savedStacks_.trace(trc);
     717             : 
     718             :     // The template registry strongly holds everything in it by design and
     719             :     // spec.
     720          26 :     templateLiteralMap_.trace(trc);
     721             : 
     722             :     // Atoms are always tenured.
     723          26 :     if (!JS::CurrentThreadIsHeapMinorCollecting())
     724          26 :         varNames_.trace(trc);
     725          26 : }
     726             : 
     727             : void
     728        2637 : JSCompartment::traceRoots(JSTracer* trc, js::gc::GCRuntime::TraceOrMarkRuntime traceOrMark)
     729             : {
     730        2637 :     if (objectMetadataState.is<PendingMetadata>()) {
     731             :         TraceRoot(trc,
     732           0 :                   &objectMetadataState.as<PendingMetadata>(),
     733           0 :                   "on-stack object pending metadata");
     734             :     }
     735             : 
     736        2637 :     if (!JS::CurrentThreadIsHeapMinorCollecting()) {
     737             :         // The global is never nursery allocated, so we don't need to
     738             :         // trace it when doing a minor collection.
     739             :         //
     740             :         // If a compartment is on-stack, we mark its global so that
     741             :         // JSContext::global() remains valid.
     742         222 :         if (enterCompartmentDepth && global_.unbarrieredGet())
     743           0 :             TraceRoot(trc, global_.unsafeUnbarrieredForTracing(), "on-stack compartment global");
     744             :     }
     745             : 
     746             :     // Nothing below here needs to be treated as a root if we aren't marking
     747             :     // this zone for a collection.
     748        2637 :     if (traceOrMark == js::gc::GCRuntime::MarkRuntime && !zone()->isCollectingFromAnyThread())
     749           0 :         return;
     750             : 
     751             :     // During a GC, these are treated as weak pointers.
     752        2637 :     if (traceOrMark == js::gc::GCRuntime::TraceRuntime) {
     753        2415 :         if (watchpointMap)
     754           0 :             watchpointMap->trace(trc);
     755             :     }
     756             : 
     757             :     /* Mark debug scopes, if present */
     758        2637 :     if (debugEnvs)
     759           0 :         debugEnvs->trace(trc);
     760             : 
     761        2637 :     if (lazyArrayBuffers)
     762           0 :         lazyArrayBuffers->trace(trc);
     763             : 
     764        2637 :     if (objectMetadataTable)
     765           0 :         objectMetadataTable->trace(trc);
     766             : 
     767             :     // If code coverage is only enabled with the Debugger or the LCovOutput,
     768             :     // then the following comment holds.
     769             :     //
     770             :     // The scriptCountsMap maps JSScript weak-pointers to ScriptCounts
     771             :     // structures. It uses a HashMap instead of a WeakMap, so that we can keep
     772             :     // the data alive for the JSScript::finalize call. Thus, we do not trace the
     773             :     // keys of the HashMap to avoid adding a strong reference to the JSScript
     774             :     // pointers.
     775             :     //
     776             :     // If the code coverage is either enabled with the --dump-bytecode command
     777             :     // line option, or with the PCCount JSFriend API functions, then we mark the
     778             :     // keys of the map to hold the JSScript alive.
     779        5890 :     if (scriptCountsMap &&
     780        2637 :         trc->runtime()->profilingScripts &&
     781           0 :         !JS::CurrentThreadIsHeapMinorCollecting())
     782             :     {
     783           0 :         MOZ_ASSERT_IF(!trc->runtime()->isBeingDestroyed(), collectCoverage());
     784           0 :         for (ScriptCountsMap::Range r = scriptCountsMap->all(); !r.empty(); r.popFront()) {
     785           0 :             JSScript* script = const_cast<JSScript*>(r.front().key());
     786           0 :             MOZ_ASSERT(script->hasScriptCounts());
     787           0 :             TraceRoot(trc, &script, "profilingScripts");
     788           0 :             MOZ_ASSERT(script == r.front().key(), "const_cast is only a work-around");
     789             :         }
     790             :     }
     791             : 
     792        2637 :     if (nonSyntacticLexicalEnvironments_)
     793          15 :         nonSyntacticLexicalEnvironments_->trace(trc);
     794             : }
     795             : 
     796             : void
     797           0 : JSCompartment::finishRoots()
     798             : {
     799           0 :     if (watchpointMap)
     800           0 :         watchpointMap->clear();
     801             : 
     802           0 :     if (debugEnvs)
     803           0 :         debugEnvs->finish();
     804             : 
     805           0 :     if (lazyArrayBuffers)
     806           0 :         lazyArrayBuffers->clear();
     807             : 
     808           0 :     if (objectMetadataTable)
     809           0 :         objectMetadataTable->clear();
     810             : 
     811           0 :     clearScriptCounts();
     812           0 :     clearScriptNames();
     813             : 
     814           0 :     if (nonSyntacticLexicalEnvironments_)
     815           0 :         nonSyntacticLexicalEnvironments_->clear();
     816           0 : }
     817             : 
     818             : void
     819        2415 : JSCompartment::sweepAfterMinorGC(JSTracer* trc)
     820             : {
     821        2415 :     globalWriteBarriered = 0;
     822             : 
     823        2415 :     InnerViewTable& table = innerViews.get();
     824        2415 :     if (table.needsSweepAfterMinorGC())
     825           0 :         table.sweepAfterMinorGC();
     826             : 
     827        2415 :     crossCompartmentWrappers.sweepAfterMinorGC(trc);
     828        2415 : }
     829             : 
     830             : void
     831           0 : JSCompartment::sweepSavedStacks()
     832             : {
     833           0 :     savedStacks_.sweep();
     834           0 : }
     835             : 
     836             : void
     837           0 : JSCompartment::sweepTemplateLiteralMap()
     838             : {
     839           0 :     templateLiteralMap_.sweep();
     840           0 : }
     841             : 
     842             : void
     843           0 : JSCompartment::sweepGlobalObject()
     844             : {
     845           0 :     if (global_ && IsAboutToBeFinalized(&global_))
     846           0 :         global_.set(nullptr);
     847           0 : }
     848             : 
     849             : void
     850           0 : JSCompartment::sweepSelfHostingScriptSource()
     851             : {
     852           0 :     if (selfHostingScriptSource.unbarrieredGet() &&
     853           0 :         IsAboutToBeFinalized(&selfHostingScriptSource))
     854             :     {
     855           0 :         selfHostingScriptSource.set(nullptr);
     856             :     }
     857           0 : }
     858             : 
     859             : void
     860           0 : JSCompartment::sweepJitCompartment(FreeOp* fop)
     861             : {
     862           0 :     if (jitCompartment_)
     863           0 :         jitCompartment_->sweep(fop, this);
     864           0 : }
     865             : 
     866             : void
     867           0 : JSCompartment::sweepRegExps()
     868             : {
     869             :     /*
     870             :      * JIT code increments activeWarmUpCounter for any RegExpShared used by jit
     871             :      * code for the lifetime of the JIT script. Thus, we must perform
     872             :      * sweeping after clearing jit code.
     873             :      */
     874           0 :     regExps.sweep(runtimeFromAnyThread());
     875           0 : }
     876             : 
     877             : void
     878           0 : JSCompartment::sweepDebugEnvironments()
     879             : {
     880           0 :     JSRuntime* rt = runtimeFromAnyThread();
     881           0 :     if (debugEnvs)
     882           0 :         debugEnvs->sweep(rt);
     883           0 : }
     884             : 
     885             : void
     886           0 : JSCompartment::sweepNativeIterators()
     887             : {
     888             :     /* Sweep list of native iterators. */
     889           0 :     NativeIterator* ni = enumerators->next();
     890           0 :     while (ni != enumerators) {
     891           0 :         JSObject* iterObj = ni->iterObj();
     892           0 :         NativeIterator* next = ni->next();
     893           0 :         if (gc::IsAboutToBeFinalizedUnbarriered(&iterObj))
     894           0 :             ni->unlink();
     895           0 :         ni = next;
     896             :     }
     897           0 : }
     898             : 
     899             : /*
     900             :  * Remove dead wrappers from the table. We must sweep all compartments, since
     901             :  * string entries in the crossCompartmentWrappers table are not marked during
     902             :  * markCrossCompartmentWrappers.
     903             :  */
     904             : void
     905           0 : JSCompartment::sweepCrossCompartmentWrappers()
     906             : {
     907           0 :     crossCompartmentWrappers.sweep();
     908           0 : }
     909             : 
     910             : void
     911           0 : JSCompartment::sweepVarNames()
     912             : {
     913           0 :     varNames_.sweep();
     914           0 : }
     915             : 
     916             : void
     917           0 : JSCompartment::sweepWatchpoints()
     918             : {
     919           0 :     if (watchpointMap)
     920           0 :         watchpointMap->sweep();
     921           0 : }
     922             : 
     923             : namespace {
     924             : struct TraceRootFunctor {
     925             :     JSTracer* trc;
     926             :     const char* name;
     927           0 :     TraceRootFunctor(JSTracer* trc, const char* name) : trc(trc), name(name) {}
     928           0 :     template <class T> void operator()(T* t) { return TraceRoot(trc, t, name); }
     929             : };
     930             : struct NeedsSweepUnbarrieredFunctor {
     931        2101 :     template <class T> bool operator()(T* t) const { return IsAboutToBeFinalizedUnbarriered(t); }
     932             : };
     933             : } // namespace (anonymous)
     934             : 
     935             : void
     936           0 : CrossCompartmentKey::trace(JSTracer* trc)
     937             : {
     938           0 :     applyToWrapped(TraceRootFunctor(trc, "CrossCompartmentKey::wrapped"));
     939           0 :     applyToDebugger(TraceRootFunctor(trc, "CrossCompartmentKey::debugger"));
     940           0 : }
     941             : 
     942             : bool
     943        2101 : CrossCompartmentKey::needsSweep()
     944             : {
     945        8404 :     return applyToWrapped(NeedsSweepUnbarrieredFunctor()) ||
     946        6303 :            applyToDebugger(NeedsSweepUnbarrieredFunctor());
     947             : }
     948             : 
     949             : void
     950           0 : JSCompartment::sweepTemplateObjects()
     951             : {
     952           0 :     if (mappedArgumentsTemplate_ && IsAboutToBeFinalized(&mappedArgumentsTemplate_))
     953           0 :         mappedArgumentsTemplate_.set(nullptr);
     954             : 
     955           0 :     if (unmappedArgumentsTemplate_ && IsAboutToBeFinalized(&unmappedArgumentsTemplate_))
     956           0 :         unmappedArgumentsTemplate_.set(nullptr);
     957           0 : }
     958             : 
     959             : /* static */ void
     960           0 : JSCompartment::fixupCrossCompartmentWrappersAfterMovingGC(JSTracer* trc)
     961             : {
     962           0 :     MOZ_ASSERT(trc->runtime()->gc.isHeapCompacting());
     963             : 
     964           0 :     for (CompartmentsIter comp(trc->runtime(), SkipAtoms); !comp.done(); comp.next()) {
     965             :         // Sweep the wrapper map to update keys (wrapped values) in other
     966             :         // compartments that may have been moved.
     967           0 :         comp->sweepCrossCompartmentWrappers();
     968             :         // Trace the wrappers in the map to update their cross-compartment edges
     969             :         // to wrapped values in other compartments that may have been moved.
     970           0 :         comp->traceOutgoingCrossCompartmentWrappers(trc);
     971             :     }
     972           0 : }
     973             : 
     974             : void
     975           0 : JSCompartment::fixupAfterMovingGC()
     976             : {
     977           0 :     MOZ_ASSERT(zone()->isGCCompacting());
     978             : 
     979           0 :     purge();
     980           0 :     fixupGlobal();
     981           0 :     objectGroups.fixupTablesAfterMovingGC();
     982           0 :     fixupScriptMapsAfterMovingGC();
     983             : 
     984             :     // Sweep the wrapper map to update values (wrapper objects) in this
     985             :     // compartment that may have been moved.
     986           0 :     sweepCrossCompartmentWrappers();
     987           0 : }
     988             : 
     989             : void
     990           0 : JSCompartment::fixupGlobal()
     991             : {
     992           0 :     GlobalObject* global = *global_.unsafeGet();
     993           0 :     if (global)
     994           0 :         global_.set(MaybeForwarded(global));
     995           0 : }
     996             : 
     997             : void
     998           0 : JSCompartment::fixupScriptMapsAfterMovingGC()
     999             : {
    1000             :     // Map entries are removed by JSScript::finalize, but we need to update the
    1001             :     // script pointers here in case they are moved by the GC.
    1002             : 
    1003           0 :     if (scriptCountsMap) {
    1004           0 :         for (ScriptCountsMap::Enum e(*scriptCountsMap); !e.empty(); e.popFront()) {
    1005           0 :             JSScript* script = e.front().key();
    1006           0 :             if (!IsAboutToBeFinalizedUnbarriered(&script) && script != e.front().key())
    1007           0 :                 e.rekeyFront(script);
    1008             :         }
    1009             :     }
    1010             : 
    1011           0 :     if (scriptNameMap) {
    1012           0 :         for (ScriptNameMap::Enum e(*scriptNameMap); !e.empty(); e.popFront()) {
    1013           0 :             JSScript* script = e.front().key();
    1014           0 :             if (!IsAboutToBeFinalizedUnbarriered(&script) && script != e.front().key())
    1015           0 :                 e.rekeyFront(script);
    1016             :         }
    1017             :     }
    1018             : 
    1019           0 :     if (debugScriptMap) {
    1020           0 :         for (DebugScriptMap::Enum e(*debugScriptMap); !e.empty(); e.popFront()) {
    1021           0 :             JSScript* script = e.front().key();
    1022           0 :             if (!IsAboutToBeFinalizedUnbarriered(&script) && script != e.front().key())
    1023           0 :                 e.rekeyFront(script);
    1024             :         }
    1025             :     }
    1026           0 : }
    1027             : 
    1028             : #ifdef JSGC_HASH_TABLE_CHECKS
    1029             : void
    1030           0 : JSCompartment::checkScriptMapsAfterMovingGC()
    1031             : {
    1032           0 :     if (scriptCountsMap) {
    1033           0 :         for (auto r = scriptCountsMap->all(); !r.empty(); r.popFront()) {
    1034           0 :             JSScript* script = r.front().key();
    1035           0 :             CheckGCThingAfterMovingGC(script);
    1036           0 :             auto ptr = scriptCountsMap->lookup(script);
    1037           0 :             MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &r.front());
    1038             :         }
    1039             :     }
    1040             : 
    1041           0 :     if (scriptNameMap) {
    1042           0 :         for (auto r = scriptNameMap->all(); !r.empty(); r.popFront()) {
    1043           0 :             JSScript* script = r.front().key();
    1044           0 :             CheckGCThingAfterMovingGC(script);
    1045           0 :             auto ptr = scriptNameMap->lookup(script);
    1046           0 :             MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &r.front());
    1047             :         }
    1048             :     }
    1049             : 
    1050           0 :     if (debugScriptMap) {
    1051           0 :         for (auto r = debugScriptMap->all(); !r.empty(); r.popFront()) {
    1052           0 :             JSScript* script = r.front().key();
    1053           0 :             CheckGCThingAfterMovingGC(script);
    1054           0 :             DebugScript* ds = r.front().value();
    1055           0 :             for (uint32_t i = 0; i < ds->numSites; i++) {
    1056           0 :                 BreakpointSite* site = ds->breakpoints[i];
    1057           0 :                 if (site && site->type() == BreakpointSite::Type::JS)
    1058           0 :                     CheckGCThingAfterMovingGC(site->asJS()->script);
    1059             :             }
    1060           0 :             auto ptr = debugScriptMap->lookup(script);
    1061           0 :             MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &r.front());
    1062             :         }
    1063             :     }
    1064           0 : }
    1065             : #endif
    1066             : 
    1067             : void
    1068         223 : JSCompartment::purge()
    1069             : {
    1070         223 :     dtoaCache.purge();
    1071         223 :     newProxyCache.purge();
    1072         223 :     lastCachedNativeIterator = nullptr;
    1073         223 :     objectGroups.purge();
    1074         223 : }
    1075             : 
    1076             : void
    1077          15 : JSCompartment::clearTables()
    1078             : {
    1079          15 :     global_.set(nullptr);
    1080             : 
    1081             :     // No scripts should have run in this compartment. This is used when
    1082             :     // merging a compartment that has been used off thread into another
    1083             :     // compartment and zone.
    1084          15 :     MOZ_ASSERT(crossCompartmentWrappers.empty());
    1085          15 :     MOZ_ASSERT(!jitCompartment_);
    1086          15 :     MOZ_ASSERT(!debugEnvs);
    1087          15 :     MOZ_ASSERT(enumerators->next() == enumerators);
    1088          15 :     MOZ_ASSERT(templateLiteralMap_.empty());
    1089             : 
    1090          15 :     objectGroups.clearTables();
    1091          15 :     if (savedStacks_.initialized())
    1092          15 :         savedStacks_.clear();
    1093          15 :     if (varNames_.initialized())
    1094          15 :         varNames_.clear();
    1095          15 : }
    1096             : 
    1097             : void
    1098           0 : JSCompartment::setAllocationMetadataBuilder(const js::AllocationMetadataBuilder *builder)
    1099             : {
    1100             :     // Clear any jitcode in the runtime, which behaves differently depending on
    1101             :     // whether there is a creation callback.
    1102           0 :     ReleaseAllJITCode(runtime_->defaultFreeOp());
    1103             : 
    1104           0 :     allocationMetadataBuilder = builder;
    1105           0 : }
    1106             : 
    1107             : void
    1108           0 : JSCompartment::clearObjectMetadata()
    1109             : {
    1110           0 :     js_delete(objectMetadataTable);
    1111           0 :     objectMetadataTable = nullptr;
    1112           0 : }
    1113             : 
    1114             : void
    1115           0 : JSCompartment::setNewObjectMetadata(JSContext* cx, HandleObject obj)
    1116             : {
    1117           0 :     assertSameCompartment(cx, this, obj);
    1118             : 
    1119           0 :     AutoEnterOOMUnsafeRegion oomUnsafe;
    1120           0 :     if (JSObject* metadata = allocationMetadataBuilder->build(cx, obj, oomUnsafe)) {
    1121           0 :         assertSameCompartment(cx, metadata);
    1122           0 :         if (!objectMetadataTable) {
    1123           0 :             objectMetadataTable = cx->new_<ObjectWeakMap>(cx);
    1124           0 :             if (!objectMetadataTable || !objectMetadataTable->init())
    1125           0 :                 oomUnsafe.crash("setNewObjectMetadata");
    1126             :         }
    1127           0 :         if (!objectMetadataTable->add(cx, obj, metadata))
    1128           0 :             oomUnsafe.crash("setNewObjectMetadata");
    1129             :     }
    1130           0 : }
    1131             : 
    1132             : static bool
    1133           0 : AddInnerLazyFunctionsFromScript(JSScript* script, AutoObjectVector& lazyFunctions)
    1134             : {
    1135           0 :     if (!script->hasObjects())
    1136           0 :         return true;
    1137           0 :     ObjectArray* objects = script->objects();
    1138           0 :     for (size_t i = 0; i < objects->length; i++) {
    1139           0 :         JSObject* obj = objects->vector[i];
    1140           0 :         if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpretedLazy()) {
    1141           0 :             if (!lazyFunctions.append(obj))
    1142           0 :                 return false;
    1143             :         }
    1144             :     }
    1145           0 :     return true;
    1146             : }
    1147             : 
    1148             : static bool
    1149           0 : AddLazyFunctionsForCompartment(JSContext* cx, AutoObjectVector& lazyFunctions, AllocKind kind)
    1150             : {
    1151             :     // Find all live root lazy functions in the compartment: those which have a
    1152             :     // source object, indicating that they have a parent, and which do not have
    1153             :     // an uncompiled enclosing script. The last condition is so that we don't
    1154             :     // compile lazy scripts whose enclosing scripts failed to compile,
    1155             :     // indicating that the lazy script did not escape the script.
    1156             :     //
    1157             :     // Some LazyScripts have a non-null |JSScript* script| pointer. We still
    1158             :     // want to delazify in that case: this pointer is weak so the JSScript
    1159             :     // could be destroyed at the next GC.
    1160             : 
    1161           0 :     for (auto i = cx->zone()->cellIter<JSObject>(kind); !i.done(); i.next()) {
    1162           0 :         JSFunction* fun = &i->as<JSFunction>();
    1163             : 
    1164             :         // Sweeping is incremental; take care to not delazify functions that
    1165             :         // are about to be finalized. GC things referenced by objects that are
    1166             :         // about to be finalized (e.g., in slots) may already be freed.
    1167           0 :         if (gc::IsAboutToBeFinalizedUnbarriered(&fun) ||
    1168           0 :             fun->compartment() != cx->compartment())
    1169             :         {
    1170           0 :             continue;
    1171             :         }
    1172             : 
    1173           0 :         if (fun->isInterpretedLazy()) {
    1174           0 :             LazyScript* lazy = fun->lazyScriptOrNull();
    1175           0 :             if (lazy && lazy->sourceObject() && !lazy->hasUncompiledEnclosingScript()) {
    1176           0 :                 if (!lazyFunctions.append(fun))
    1177           0 :                     return false;
    1178             :             }
    1179             :         }
    1180             :     }
    1181             : 
    1182           0 :     return true;
    1183             : }
    1184             : 
    1185             : static bool
    1186           0 : CreateLazyScriptsForCompartment(JSContext* cx)
    1187             : {
    1188           0 :     AutoObjectVector lazyFunctions(cx);
    1189             : 
    1190           0 :     if (!AddLazyFunctionsForCompartment(cx, lazyFunctions, AllocKind::FUNCTION))
    1191           0 :         return false;
    1192             : 
    1193             :     // Methods, for instance {get method() {}}, are extended functions that can
    1194             :     // be relazified, so we need to handle those as well.
    1195           0 :     if (!AddLazyFunctionsForCompartment(cx, lazyFunctions, AllocKind::FUNCTION_EXTENDED))
    1196           0 :         return false;
    1197             : 
    1198             :     // Create scripts for each lazy function, updating the list of functions to
    1199             :     // process with any newly exposed inner functions in created scripts.
    1200             :     // A function cannot be delazified until its outer script exists.
    1201           0 :     RootedFunction fun(cx);
    1202           0 :     for (size_t i = 0; i < lazyFunctions.length(); i++) {
    1203           0 :         fun = &lazyFunctions[i]->as<JSFunction>();
    1204             : 
    1205             :         // lazyFunctions may have been populated with multiple functions for
    1206             :         // a lazy script.
    1207           0 :         if (!fun->isInterpretedLazy())
    1208           0 :             continue;
    1209             : 
    1210           0 :         bool lazyScriptHadNoScript = !fun->lazyScript()->maybeScript();
    1211             : 
    1212           0 :         JSScript* script = JSFunction::getOrCreateScript(cx, fun);
    1213           0 :         if (!script)
    1214           0 :             return false;
    1215           0 :         if (lazyScriptHadNoScript && !AddInnerLazyFunctionsFromScript(script, lazyFunctions))
    1216           0 :             return false;
    1217             :     }
    1218             : 
    1219           0 :     return true;
    1220             : }
    1221             : 
    1222             : bool
    1223           0 : JSCompartment::ensureDelazifyScriptsForDebugger(JSContext* cx)
    1224             : {
    1225           0 :     AutoCompartmentUnchecked ac(cx, this);
    1226           0 :     if (needsDelazificationForDebugger() && !CreateLazyScriptsForCompartment(cx))
    1227           0 :         return false;
    1228           0 :     debugModeBits &= ~DebuggerNeedsDelazification;
    1229           0 :     return true;
    1230             : }
    1231             : 
    1232             : void
    1233           0 : JSCompartment::updateDebuggerObservesFlag(unsigned flag)
    1234             : {
    1235           0 :     MOZ_ASSERT(isDebuggee());
    1236           0 :     MOZ_ASSERT(flag == DebuggerObservesAllExecution ||
    1237             :                flag == DebuggerObservesCoverage ||
    1238             :                flag == DebuggerObservesAsmJS ||
    1239             :                flag == DebuggerObservesBinarySource);
    1240             : 
    1241           0 :     GlobalObject* global = zone()->runtimeFromActiveCooperatingThread()->gc.isForegroundSweeping()
    1242           0 :                            ? unsafeUnbarrieredMaybeGlobal()
    1243           0 :                            : maybeGlobal();
    1244           0 :     const GlobalObject::DebuggerVector* v = global->getDebuggers();
    1245           0 :     for (auto p = v->begin(); p != v->end(); p++) {
    1246           0 :         Debugger* dbg = *p;
    1247           0 :         if (flag == DebuggerObservesAllExecution ? dbg->observesAllExecution() :
    1248           0 :             flag == DebuggerObservesCoverage ? dbg->observesCoverage() :
    1249           0 :             flag == DebuggerObservesAsmJS ? dbg->observesAsmJS() :
    1250           0 :             dbg->observesBinarySource())
    1251             :         {
    1252           0 :             debugModeBits |= flag;
    1253           0 :             return;
    1254             :         }
    1255             :     }
    1256             : 
    1257           0 :     debugModeBits &= ~flag;
    1258             : }
    1259             : 
    1260             : void
    1261          15 : JSCompartment::unsetIsDebuggee()
    1262             : {
    1263          15 :     if (isDebuggee()) {
    1264           0 :         debugModeBits &= ~DebuggerObservesMask;
    1265           0 :         DebugEnvironments::onCompartmentUnsetIsDebuggee(this);
    1266             :     }
    1267          15 : }
    1268             : 
    1269             : void
    1270           0 : JSCompartment::updateDebuggerObservesCoverage()
    1271             : {
    1272           0 :     bool previousState = debuggerObservesCoverage();
    1273           0 :     updateDebuggerObservesFlag(DebuggerObservesCoverage);
    1274           0 :     if (previousState == debuggerObservesCoverage())
    1275           0 :         return;
    1276             : 
    1277           0 :     if (debuggerObservesCoverage()) {
    1278             :         // Interrupt any running interpreter frame. The scriptCounts are
    1279             :         // allocated on demand when a script resumes its execution.
    1280           0 :         JSContext* cx = TlsContext.get();
    1281           0 :         for (ActivationIterator iter(cx); !iter.done(); ++iter) {
    1282           0 :             if (iter->isInterpreter())
    1283           0 :                 iter->asInterpreter()->enableInterruptsUnconditionally();
    1284             :         }
    1285           0 :         return;
    1286             :     }
    1287             : 
    1288             :     // If code coverage is enabled by any other means, keep it.
    1289           0 :     if (collectCoverage())
    1290           0 :         return;
    1291             : 
    1292           0 :     clearScriptCounts();
    1293           0 :     clearScriptNames();
    1294             : }
    1295             : 
    1296             : bool
    1297         613 : JSCompartment::collectCoverage() const
    1298             : {
    1299         613 :     return collectCoverageForPGO() ||
    1300         613 :            collectCoverageForDebug();
    1301             : }
    1302             : 
    1303             : bool
    1304         613 : JSCompartment::collectCoverageForPGO() const
    1305             : {
    1306         613 :     return !JitOptions.disablePgo;
    1307             : }
    1308             : 
    1309             : bool
    1310       11796 : JSCompartment::collectCoverageForDebug() const
    1311             : {
    1312       23592 :     return debuggerObservesCoverage() ||
    1313       23592 :            runtimeFromAnyThread()->profilingScripts ||
    1314       23592 :            runtimeFromAnyThread()->lcovOutput().isEnabled();
    1315             : }
    1316             : 
    1317             : void
    1318           0 : JSCompartment::clearScriptCounts()
    1319             : {
    1320           0 :     if (!scriptCountsMap)
    1321           0 :         return;
    1322             : 
    1323             :     // Clear all hasScriptCounts_ flags of JSScript, in order to release all
    1324             :     // ScriptCounts entry of the current compartment.
    1325           0 :     for (ScriptCountsMap::Range r = scriptCountsMap->all(); !r.empty(); r.popFront()) {
    1326           0 :         ScriptCounts* value = r.front().value();
    1327           0 :         r.front().key()->takeOverScriptCountsMapEntry(value);
    1328           0 :         js_delete(value);
    1329             :     }
    1330             : 
    1331           0 :     js_delete(scriptCountsMap);
    1332           0 :     scriptCountsMap = nullptr;
    1333             : }
    1334             : 
    1335             : void
    1336           0 : JSCompartment::clearScriptNames()
    1337             : {
    1338           0 :     if (!scriptNameMap)
    1339           0 :         return;
    1340             : 
    1341           0 :     for (ScriptNameMap::Range r = scriptNameMap->all(); !r.empty(); r.popFront())
    1342           0 :         js_delete(r.front().value());
    1343             : 
    1344           0 :     js_delete(scriptNameMap);
    1345           0 :     scriptNameMap = nullptr;
    1346             : }
    1347             : 
    1348             : void
    1349           0 : JSCompartment::clearBreakpointsIn(FreeOp* fop, js::Debugger* dbg, HandleObject handler)
    1350             : {
    1351           0 :     for (auto script = zone()->cellIter<JSScript>(); !script.done(); script.next()) {
    1352           0 :         if (script->compartment() == this && script->hasAnyBreakpointsOrStepMode())
    1353           0 :             script->clearBreakpointsIn(fop, dbg, handler);
    1354             :     }
    1355           0 : }
    1356             : 
    1357             : void
    1358           0 : JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
    1359             :                                       size_t* tiAllocationSiteTables,
    1360             :                                       size_t* tiArrayTypeTables,
    1361             :                                       size_t* tiObjectTypeTables,
    1362             :                                       size_t* compartmentObject,
    1363             :                                       size_t* compartmentTables,
    1364             :                                       size_t* innerViewsArg,
    1365             :                                       size_t* lazyArrayBuffersArg,
    1366             :                                       size_t* objectMetadataTablesArg,
    1367             :                                       size_t* crossCompartmentWrappersArg,
    1368             :                                       size_t* savedStacksSet,
    1369             :                                       size_t* varNamesSet,
    1370             :                                       size_t* nonSyntacticLexicalEnvironmentsArg,
    1371             :                                       size_t* templateLiteralMap,
    1372             :                                       size_t* jitCompartment,
    1373             :                                       size_t* privateData)
    1374             : {
    1375           0 :     *compartmentObject += mallocSizeOf(this);
    1376           0 :     objectGroups.addSizeOfExcludingThis(mallocSizeOf, tiAllocationSiteTables,
    1377             :                                         tiArrayTypeTables, tiObjectTypeTables,
    1378           0 :                                         compartmentTables);
    1379           0 :     wasm.addSizeOfExcludingThis(mallocSizeOf, compartmentTables);
    1380           0 :     *innerViewsArg += innerViews.sizeOfExcludingThis(mallocSizeOf);
    1381           0 :     if (lazyArrayBuffers)
    1382           0 :         *lazyArrayBuffersArg += lazyArrayBuffers->sizeOfIncludingThis(mallocSizeOf);
    1383           0 :     if (objectMetadataTable)
    1384           0 :         *objectMetadataTablesArg += objectMetadataTable->sizeOfIncludingThis(mallocSizeOf);
    1385           0 :     *crossCompartmentWrappersArg += crossCompartmentWrappers.sizeOfExcludingThis(mallocSizeOf);
    1386           0 :     *savedStacksSet += savedStacks_.sizeOfExcludingThis(mallocSizeOf);
    1387           0 :     *varNamesSet += varNames_.sizeOfExcludingThis(mallocSizeOf);
    1388           0 :     if (nonSyntacticLexicalEnvironments_)
    1389           0 :         *nonSyntacticLexicalEnvironmentsArg +=
    1390           0 :             nonSyntacticLexicalEnvironments_->sizeOfIncludingThis(mallocSizeOf);
    1391           0 :     *templateLiteralMap += templateLiteralMap_.sizeOfExcludingThis(mallocSizeOf);
    1392           0 :     if (jitCompartment_)
    1393           0 :         *jitCompartment += jitCompartment_->sizeOfIncludingThis(mallocSizeOf);
    1394             : 
    1395           0 :     auto callback = runtime_->sizeOfIncludingThisCompartmentCallback;
    1396           0 :     if (callback)
    1397           0 :         *privateData += callback(mallocSizeOf, this);
    1398           0 : }
    1399             : 
    1400             : void
    1401           0 : JSCompartment::reportTelemetry()
    1402             : {
    1403             :     // Only report telemetry for web content and add-ons, not chrome JS.
    1404           0 :     if (isSystem_)
    1405           0 :         return;
    1406             : 
    1407             :     // Hazard analysis can't tell that the telemetry callbacks don't GC.
    1408           0 :     JS::AutoSuppressGCAnalysis nogc;
    1409             : 
    1410           0 :     int id = creationOptions_.addonIdOrNull()
    1411           0 :              ? JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS
    1412           0 :              : JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT;
    1413             : 
    1414             :     // Call back into Firefox's Telemetry reporter.
    1415           0 :     for (size_t i = 0; i < size_t(DeprecatedLanguageExtension::Count); i++) {
    1416           0 :         if (sawDeprecatedLanguageExtension[i])
    1417           0 :             runtime_->addTelemetry(id, i);
    1418             :     }
    1419             : }
    1420             : 
    1421             : void
    1422           0 : JSCompartment::addTelemetry(const char* filename, DeprecatedLanguageExtension e)
    1423             : {
    1424             :     // Only report telemetry for web content and add-ons, not chrome JS.
    1425           0 :     if (isSystem_)
    1426           0 :         return;
    1427           0 :     if (!creationOptions_.addonIdOrNull() && (!filename || strncmp(filename, "http", 4) != 0))
    1428           0 :         return;
    1429             : 
    1430           0 :     sawDeprecatedLanguageExtension[size_t(e)] = true;
    1431             : }
    1432             : 
    1433             : HashNumber
    1434          56 : JSCompartment::randomHashCode()
    1435             : {
    1436          56 :     ensureRandomNumberGenerator();
    1437          56 :     return HashNumber(randomNumberGenerator.ref().next());
    1438             : }
    1439             : 
    1440             : mozilla::HashCodeScrambler
    1441         408 : JSCompartment::randomHashCodeScrambler()
    1442             : {
    1443         816 :     return mozilla::HashCodeScrambler(randomKeyGenerator_.next(),
    1444         816 :                                       randomKeyGenerator_.next());
    1445             : }
    1446             : 
    1447       16469 : AutoSetNewObjectMetadata::AutoSetNewObjectMetadata(JSContext* cx
    1448       16469 :                                                    MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
    1449             :     : CustomAutoRooter(cx)
    1450       16469 :     , cx_(cx->helperThread() ? nullptr : cx)
    1451       32938 :     , prevState_(cx->compartment()->objectMetadataState)
    1452             : {
    1453       16469 :     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
    1454       16469 :     if (cx_)
    1455       16168 :         cx_->compartment()->objectMetadataState = NewObjectMetadataState(DelayMetadata());
    1456       16469 : }
    1457             : 
    1458       32938 : AutoSetNewObjectMetadata::~AutoSetNewObjectMetadata()
    1459             : {
    1460             :     // If we don't have a cx, we didn't change the metadata state, so no need to
    1461             :     // reset it here.
    1462       16469 :     if (!cx_)
    1463         301 :         return;
    1464             : 
    1465       16168 :     if (!cx_->isExceptionPending() && cx_->compartment()->hasObjectPendingMetadata()) {
    1466             :         // This destructor often runs upon exit from a function that is
    1467             :         // returning an unrooted pointer to a Cell. The allocation metadata
    1468             :         // callback often allocates; if it causes a GC, then the Cell pointer
    1469             :         // being returned won't be traced or relocated.
    1470             :         //
    1471             :         // The only extant callbacks are those internal to SpiderMonkey that
    1472             :         // capture the JS stack. In fact, we're considering removing general
    1473             :         // callbacks altogther in bug 1236748. Since it's not running arbitrary
    1474             :         // code, it's adequate to simply suppress GC while we run the callback.
    1475       32336 :         AutoSuppressGC autoSuppressGC(cx_);
    1476             : 
    1477       16168 :         JSObject* obj = cx_->compartment()->objectMetadataState.as<PendingMetadata>();
    1478             : 
    1479             :         // Make sure to restore the previous state before setting the object's
    1480             :         // metadata. SetNewObjectMetadata asserts that the state is not
    1481             :         // PendingMetadata in order to ensure that metadata callbacks are called
    1482             :         // in order.
    1483       16168 :         cx_->compartment()->objectMetadataState = prevState_;
    1484             : 
    1485       16168 :         obj = SetNewObjectMetadata(cx_, obj);
    1486             :     } else {
    1487           0 :         cx_->compartment()->objectMetadataState = prevState_;
    1488             :     }
    1489       16469 : }

Generated by: LCOV version 1.13