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 327706 498dfbe07a6c21af0ac2bd165f41b58a97cd241e
parent 327705 e185710e8818fceef549c86b18518b2c48d029a5
child 327707 71d499e82b2cd5096b6f92f0c565cb6cfd5c6039
push id9858
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 14:37:10 +0000
treeherdermozilla-aurora@203106ef6cb6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1281529
milestone50.0a1
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;