Bug 1281529 part 1 - Make JSContext inherit from JSRuntime. r=luke
authorJan de Mooij <jdemooij@mozilla.com>
Fri, 24 Jun 2016 14:16:47 +0200
changeset 302623 498dfbe07a6c21af0ac2bd165f41b58a97cd241e
parent 302622 e185710e8818fceef549c86b18518b2c48d029a5
child 302624 71d499e82b2cd5096b6f92f0c565cb6cfd5c6039
push id78801
push userjandemooij@gmail.com
push dateSat, 25 Jun 2016 13:10:36 +0000
treeherdermozilla-inbound@498dfbe07a6c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1281529
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1281529 part 1 - Make JSContext inherit from JSRuntime. r=luke
js/src/gc/RootMarking.cpp
js/src/jsapi.cpp
js/src/jsatominlines.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsexn.cpp
js/src/jsgc.cpp
js/src/jsgc.h
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
js/src/vm/TypeInference-inl.h
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -218,26 +218,23 @@ AutoGCRooter::trace(JSTracer* trc)
     MOZ_ASSERT(tag_ >= 0);
     if (Value* vp = static_cast<AutoArrayRooter*>(this)->array)
         TraceRootRange(trc, tag_, vp, "JS::AutoArrayRooter.array");
 }
 
 /* static */ void
 AutoGCRooter::traceAll(JSTracer* trc)
 {
-    if (JSContext* cx = trc->runtime()->maybeContextFromMainThread())
-        traceAllInContext(cx, trc);
+    traceAllInContext(trc->runtime()->contextFromMainThread(), trc);
 }
 
 /* static */ void
 AutoGCRooter::traceAllWrappers(JSTracer* trc)
 {
-    JSContext* cx = trc->runtime()->maybeContextFromMainThread();
-    if (!cx)
-        return;
+    JSContext* cx = trc->runtime()->contextFromMainThread();
 
     for (AutoGCRooter* gcr = cx->roots.autoGCRooters_; gcr; gcr = gcr->down) {
         if (gcr->tag_ == WRAPVECTOR || gcr->tag_ == WRAPPER)
             gcr->trace(trc);
     }
 }
 
 void
@@ -315,18 +312,17 @@ js::gc::GCRuntime::markRuntime(JSTracer*
             MarkWellKnownSymbols(trc);
             jit::JitRuntime::Mark(trc, lock);
         }
     }
 
     if (rt->isHeapMinorCollecting())
         jit::JitRuntime::MarkJitcodeGlobalTableUnconditionally(trc);
 
-    if (JSContext* cx = rt->maybeContextFromMainThread())
-        cx->mark(trc);
+    rt->contextFromMainThread()->mark(trc);
 
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
         c->traceRoots(trc, traceOrMark);
 
     MarkInterpreterActivations(rt, trc);
 
     jit::MarkJitActivations(rt, trc);
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -461,32 +461,24 @@ JS_NewRuntime(uint32_t maxbytes, uint32_
 {
     MOZ_ASSERT(JS::detail::libraryInitState == JS::detail::InitState::Running,
                "must call JS_Init prior to creating any JSRuntimes");
 
     // Make sure that all parent runtimes are the topmost parent.
     while (parentRuntime && parentRuntime->parentRuntime)
         parentRuntime = parentRuntime->parentRuntime;
 
-    JSRuntime* rt = js_new<JSRuntime>(parentRuntime);
-    if (!rt)
-        return nullptr;
-
-    if (!rt->init(maxbytes, maxNurseryBytes)) {
-        JS_DestroyRuntime(rt);
-        return nullptr;
-    }
-
-    return rt;
+    return NewContext(maxbytes, maxNurseryBytes, parentRuntime);
 }
 
 JS_PUBLIC_API(void)
 JS_DestroyRuntime(JSRuntime* rt)
 {
-    js_delete(rt);
+    JSContext* cx = rt->contextFromMainThread();
+    DestroyContext(cx);
 }
 
 static JS_CurrentEmbedderTimeFunction currentEmbedderTimeFunction;
 
 JS_PUBLIC_API(void)
 JS_SetCurrentEmbedderTimeFunction(JS_CurrentEmbedderTimeFunction timeFn)
 {
     currentEmbedderTimeFunction = timeFn;
--- a/js/src/jsatominlines.h
+++ b/js/src/jsatominlines.h
@@ -205,22 +205,16 @@ ClassName(JSProtoKey key, JSAtomState& a
     JS_STATIC_ASSERT(offsetof(JSAtomState, Null) +
                      JSProto_LIMIT * sizeof(ImmutablePropertyNamePtr) <=
                      sizeof(JSAtomState));
     JS_STATIC_ASSERT(JSProto_Null == 0);
     return (&atomState.Null)[key];
 }
 
 inline Handle<PropertyName*>
-ClassName(JSProtoKey key, JSRuntime* rt)
-{
-    return ClassName(key, *rt->commonNames);
-}
-
-inline Handle<PropertyName*>
 ClassName(JSProtoKey key, ExclusiveContext* cx)
 {
     return ClassName(key, cx->names());
 }
 
 } // namespace js
 
 #endif /* jsatominlines_h */
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -82,27 +82,37 @@ js::AutoCycleDetector::~AutoCycleDetecto
 
 void
 js::TraceCycleDetectionSet(JSTracer* trc, AutoCycleDetector::Set& set)
 {
     for (AutoCycleDetector::Set::Enum e(set); !e.empty(); e.popFront())
         TraceRoot(trc, &e.mutableFront(), "cycle detector table entry");
 }
 
-JSContext*
-js::NewContext(JSRuntime* rt)
+bool
+JSContext::init(uint32_t maxBytes, uint32_t maxNurseryBytes)
 {
-    MOZ_ASSERT(!rt->maybeContextFromMainThread());
+    if (!JSRuntime::init(maxBytes, maxNurseryBytes))
+        return false;
 
-    JS_AbortIfWrongThread(rt);
+    return true;
+}
 
-    JSContext* cx = js_new<JSContext>(rt);
+JSContext*
+js::NewContext(uint32_t maxBytes, uint32_t maxNurseryBytes, JSRuntime* parentRuntime)
+{
+    JSContext* cx = js_new<JSContext>(parentRuntime);
     if (!cx)
         return nullptr;
 
+    if (!cx->init(maxBytes, maxNurseryBytes)) {
+        js_delete(cx);
+        return nullptr;
+    }
+
     return cx;
 }
 
 void
 js::DestroyContext(JSContext* cx)
 {
     JSRuntime* rt = cx->runtime();
     JS_AbortIfWrongThread(rt);
@@ -853,18 +863,19 @@ ExclusiveContext::recoverFromOutOfMemory
         }
         return;
     }
     // Keep in sync with addPendingOutOfMemory.
     if (ParseTask* task = helperThread()->parseTask())
         task->outOfMemory = false;
 }
 
-JSContext::JSContext(JSRuntime* rt)
-  : ExclusiveContext(rt, &rt->mainThread, Context_JS),
+JSContext::JSContext(JSRuntime* parentRuntime)
+  : ExclusiveContext(this, &this->JSRuntime::mainThread, Context_JS),
+    JSRuntime(this, parentRuntime),
     throwing(false),
     unwrappedException_(this),
     overRecursed_(false),
     propagatingForcedReturn_(false),
     liveVolatileJitFrameIterators_(nullptr),
     reportGranularity(JS_DEFAULT_JITREPORT_GRANULARITY),
     resolvingList(nullptr),
     generatingError(false),
@@ -873,16 +884,18 @@ JSContext::JSContext(JSRuntime* rt)
     jitIsBroken(false)
 {
     MOZ_ASSERT(static_cast<ContextFriendFields*>(this) ==
                ContextFriendFields::get(this));
 }
 
 JSContext::~JSContext()
 {
+    destroyRuntime();
+
     /* Free the stuff hanging off of cx. */
     MOZ_ASSERT(!resolvingList);
 }
 
 bool
 JSContext::getPendingException(MutableHandleValue rval)
 {
     MOZ_ASSERT(throwing);
@@ -1017,19 +1030,18 @@ JSContext::sizeOfIncludingThis(mozilla::
      * added later.
      */
     return mallocSizeOf(this) + cycleDetectorSet.sizeOfExcludingThis(mallocSizeOf);
 }
 
 void
 JSContext::mark(JSTracer* trc)
 {
-    /* Stack frames and slots are traced by StackSpace::mark. */
-
-    TraceCycleDetectionSet(trc, cycleDetectorSet);
+    if (cycleDetectorSet.initialized())
+        TraceCycleDetectionSet(trc, cycleDetectorSet);
 
     if (compartment_)
         compartment_->mark();
 }
 
 void*
 ExclusiveContext::stackLimitAddressForJitCode(StackKind kind)
 {
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -290,21 +290,39 @@ class ExclusiveContext : public ContextF
     void addPendingOverRecursed();
     void addPendingOutOfMemory();
 };
 
 void ReportOverRecursed(JSContext* cx, unsigned errorNumber);
 
 } /* namespace js */
 
-struct JSContext : public js::ExclusiveContext
+struct JSContext : public js::ExclusiveContext,
+                   public JSRuntime
 {
-    explicit JSContext(JSRuntime* rt);
+    explicit JSContext(JSRuntime* parentRuntime);
     ~JSContext();
 
+    bool init(uint32_t maxBytes, uint32_t maxNurseryBytes);
+
+    // For names that exist in both ExclusiveContext and JSRuntime, pick the
+    // ExclusiveContext version.
+    using ExclusiveContext::atomsCompartment;
+    using ExclusiveContext::buildIdOp;
+    using ExclusiveContext::emptyString;
+    using ExclusiveContext::jitSupportsSimd;
+    using ExclusiveContext::make_pod_array;
+    using ExclusiveContext::make_unique;
+    using ExclusiveContext::new_;
+    using ExclusiveContext::permanentAtoms;
+    using ExclusiveContext::pod_calloc;
+    using ExclusiveContext::pod_malloc;
+    using ExclusiveContext::staticStrings;
+    using ExclusiveContext::wellKnownSymbols;
+
     JSRuntime* runtime() const { return runtime_; }
     js::PerThreadData& mainThread() const { return runtime()->mainThread; }
 
     static size_t offsetOfRuntime() {
         return offsetof(JSContext, runtime_);
     }
     static size_t offsetOfCompartment() {
         return offsetof(JSContext, compartment_);
@@ -486,17 +504,17 @@ struct MOZ_RAII AutoResolving {
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 /*
  * Create and destroy functions for JSContext, which is manually allocated
  * and exclusively owned.
  */
 extern JSContext*
-NewContext(JSRuntime* rt);
+NewContext(uint32_t maxBytes, uint32_t maxNurseryBytes, JSRuntime* parentRuntime);
 
 extern void
 DestroyContext(JSContext* cx);
 
 enum ErrorArgumentsType {
     ArgumentsAreUnicode,
     ArgumentsAreASCII
 };
@@ -712,16 +730,20 @@ class MOZ_RAII AutoLockForExclusiveAcces
     explicit AutoLockForExclusiveAccess(ExclusiveContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         init(cx->runtime_);
     }
     explicit AutoLockForExclusiveAccess(JSRuntime* rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         init(rt);
     }
+    explicit AutoLockForExclusiveAccess(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+        init(cx->runtime());
+    }
     ~AutoLockForExclusiveAccess() {
         if (runtime->numExclusiveThreads) {
 #ifdef DEBUG
             MOZ_ASSERT(runtime->exclusiveAccessOwner == PR_GetCurrentThread());
             runtime->exclusiveAccessOwner = nullptr;
 #endif
             runtime->exclusiveAccessLock.unlock();
         } else {
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -517,17 +517,17 @@ js::GetErrorTypeName(JSRuntime* rt, int1
      * is prepended before "uncaught exception: "
      */
     if (exnType < 0 || exnType >= JSEXN_LIMIT ||
         exnType == JSEXN_INTERNALERR || exnType == JSEXN_WARN)
     {
         return nullptr;
     }
     JSProtoKey key = GetExceptionProtoKey(JSExnType(exnType));
-    return ClassName(key, rt);
+    return ClassName(key, rt->contextFromMainThread());
 }
 
 void
 js::ErrorToException(JSContext* cx, const char* message, JSErrorReport* reportp,
                      JSErrorCallback callback, void* userRef)
 {
     MOZ_ASSERT(reportp);
     MOZ_ASSERT(!JSREPORT_IS_WARNING(reportp->flags));
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -6991,18 +6991,18 @@ AutoSuppressGC::AutoSuppressGC(Exclusive
 }
 
 AutoSuppressGC::AutoSuppressGC(JSCompartment* comp)
   : suppressGC_(comp->runtimeFromMainThread()->mainThread.suppressGC)
 {
     suppressGC_++;
 }
 
-AutoSuppressGC::AutoSuppressGC(JSRuntime* rt)
-  : suppressGC_(rt->mainThread.suppressGC)
+AutoSuppressGC::AutoSuppressGC(JSContext* cx)
+  : suppressGC_(cx->mainThread().suppressGC)
 {
     suppressGC_++;
 }
 
 bool
 js::UninlinedIsInsideNursery(const gc::Cell* cell)
 {
     return IsInsideNursery(cell);
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -1271,17 +1271,17 @@ MaybeVerifyBarriers(JSContext* cx, bool 
  */
 class MOZ_RAII JS_HAZ_GC_SUPPRESSED AutoSuppressGC
 {
     int32_t& suppressGC_;
 
   public:
     explicit AutoSuppressGC(ExclusiveContext* cx);
     explicit AutoSuppressGC(JSCompartment* comp);
-    explicit AutoSuppressGC(JSRuntime* rt);
+    explicit AutoSuppressGC(JSContext* cx);
 
     ~AutoSuppressGC()
     {
         suppressGC_--;
     }
 };
 
 // A singly linked list of zones.
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -123,17 +123,17 @@ static const JSWrapObjectCallbacks Defau
 };
 
 static size_t
 ReturnZeroSize(const void* p)
 {
     return 0;
 }
 
-JSRuntime::JSRuntime(JSRuntime* parentRuntime)
+JSRuntime::JSRuntime(JSContext* cx, JSRuntime* parentRuntime)
   : mainThread(this),
     jitTop(nullptr),
     jitJSContext(nullptr),
     jitActivation(nullptr),
     jitStackLimit_(0xbad),
     jitStackLimitNoInterrupt_(0xbad),
 #ifdef DEBUG
     ionBailAfter_(0),
@@ -168,17 +168,17 @@ JSRuntime::JSRuntime(JSRuntime* parentRu
     numExclusiveThreads(0),
     numCompartments(0),
     localeCallbacks(nullptr),
     defaultLocale(nullptr),
     defaultVersion_(JSVERSION_DEFAULT),
     ownerThread_(nullptr),
     ownerThreadNative_(0),
     tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
-    context_(nullptr),
+    context_(cx),
     jitRuntime_(nullptr),
     selfHostingGlobal_(nullptr),
     nativeStackBase(GetNativeStackBase()),
     destroyCompartmentCallback(nullptr),
     sizeOfIncludingThisCompartmentCallback(nullptr),
     destroyZoneCallback(nullptr),
     sweepZoneCallback(nullptr),
     compartmentNameCallback(nullptr),
@@ -365,30 +365,22 @@ JSRuntime::init(uint32_t maxbytes, uint3
         return false;
 
     if (!parentRuntime) {
         sharedImmutableStrings_ = js::SharedImmutableStringsCache::Create();
         if (!sharedImmutableStrings_)
             return false;
     }
 
-    context_ = NewContext(this);
-    if (!context_)
-        return false;
-
     return true;
 }
 
-JSRuntime::~JSRuntime()
+void
+JSRuntime::destroyRuntime()
 {
-    if (context_) {
-        DestroyContext(context_);
-        context_ = nullptr;
-    }
-
     MOZ_ASSERT(!isHeapBusy());
     MOZ_ASSERT(childRuntimeCount == 0);
 
     fx.destroyInstance();
 
     if (gcInitialized) {
         /* Free source hook early, as its destructor may want to delete roots. */
         sourceHook = nullptr;
@@ -752,17 +744,17 @@ JSRuntime::triggerActivityCallback(bool 
 
     /*
      * The activity callback must not trigger a GC: it would create a cirular
      * dependency between entering a request and Rooted's requirement of being
      * in a request. In practice this callback already cannot trigger GC. The
      * suppression serves to inform the exact rooting hazard analysis of this
      * property and ensures that it remains true in the future.
      */
-    AutoSuppressGC suppress(this);
+    AutoSuppressGC suppress(contextFromMainThread());
 
     activityCallback(activityCallbackArg, active);
 }
 
 FreeOp::~FreeOp()
 {
     for (size_t i = 0; i < freeLaterList.length(); i++)
         free_(freeLaterList[i]);
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -1049,27 +1049,20 @@ struct JSRuntime : public JS::shadow::Ru
     }
     bool hasJitRuntime() const {
         return !!jitRuntime_;
     }
     js::InterpreterStack& interpreterStack() {
         return interpreterStack_;
     }
 
-    // The runtime's context can be nullptr only while we're initializing or
-    // destroying the runtime.
-    JSContext* maybeContextFromMainThread() {
+    JSContext* contextFromMainThread() {
         MOZ_ASSERT(CurrentThreadCanAccessRuntime(this));
         return context_;
     }
-    JSContext* contextFromMainThread() {
-        JSContext* cx = maybeContextFromMainThread();
-        MOZ_ASSERT(cx);
-        return cx;
-    }
 
     bool enqueuePromiseJob(JSContext* cx, js::HandleFunction job, js::HandleObject promise);
     void addUnhandledRejectedPromise(JSContext* cx, js::HandleObject promise);
     void removeUnhandledRejectedPromise(JSContext* cx, js::HandleObject promise);
 
     //-------------------------------------------------------------------------
     // Self-hosting support
     //-------------------------------------------------------------------------
@@ -1482,23 +1475,28 @@ struct JSRuntime : public JS::shadow::Ru
   private:
     static mozilla::Atomic<size_t> liveRuntimesCount;
 
   public:
     static bool hasLiveRuntimes() {
         return liveRuntimesCount > 0;
     }
 
-    explicit JSRuntime(JSRuntime* parentRuntime);
-    ~JSRuntime();
+  protected:
+    JSRuntime(JSContext* cx, JSRuntime* parentRuntime);
+
+    // destroyRuntime is used instead of a destructor, to ensure the downcast
+    // to JSContext remains valid. The final GC triggered here depends on this.
+    void destroyRuntime();
 
     bool init(uint32_t maxbytes, uint32_t maxNurseryBytes);
 
     JSRuntime* thisFromCtor() { return this; }
 
+  public:
     /*
      * Call this after allocating memory held by GC things, to update memory
      * pressure counters or report the OOM error if necessary. If oomError and
      * cx is not null the function also reports OOM error.
      *
      * The function must be called outside the GC lock and in case of OOM error
      * the caller must ensure that no deadlock possible during OOM reporting.
      */
--- a/js/src/vm/TypeInference-inl.h
+++ b/js/src/vm/TypeInference-inl.h
@@ -293,17 +293,18 @@ struct AutoEnterAnalysis
 
     explicit AutoEnterAnalysis(ExclusiveContext* cx)
       : suppressGC(cx), oom(cx->zone()), suppressMetadata(cx)
     {
         init(cx->defaultFreeOp(), cx->zone());
     }
 
     AutoEnterAnalysis(FreeOp* fop, Zone* zone)
-      : suppressGC(zone->runtimeFromMainThread()), oom(zone), suppressMetadata(zone)
+      : suppressGC(zone->runtimeFromMainThread()->contextFromMainThread()),
+        oom(zone), suppressMetadata(zone)
     {
         init(fop, zone);
     }
 
     ~AutoEnterAnalysis()
     {
         if (this != zone->types.activeAnalysis)
             return;