Bug 1363200 - JSAPI for realms: Add JS::Realm opaque type and GC rooting policy for it. r=sfink
authorJason Orendorff <jorendorff@mozilla.com>
Thu, 29 Jun 2017 09:57:46 -0700
changeset 422832 1ebe8f22c28fa699b248fd77cdec824361d709d1
parent 422831 c2635f48c240ed1281bd034711f9f36534a058cd
child 422833 7556c838fcf7baf4000f54b068411874e1b9849d
push id7761
push userjlund@mozilla.com
push dateFri, 15 Sep 2017 00:19:52 +0000
treeherdermozilla-beta@c38455951db4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1363200
milestone57.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 1363200 - JSAPI for realms: Add JS::Realm opaque type and GC rooting policy for it. r=sfink
js/public/GCPolicyAPI.h
js/public/Realm.h
js/public/TraceKind.h
js/public/TypeDecls.h
js/src/jscompartment.h
js/src/jscompartmentinlines.h
js/src/jsgc.cpp
js/src/vm/Realm.cpp
--- a/js/public/GCPolicyAPI.h
+++ b/js/public/GCPolicyAPI.h
@@ -172,11 +172,13 @@ struct GCPolicy<mozilla::Maybe<T>>
     }
     static bool needsSweep(mozilla::Maybe<T>* tp) {
         if (tp->isSome())
             return GCPolicy<T>::needsSweep(tp->ptr());
         return false;
     }
 };
 
+template <> struct GCPolicy<JS::Realm*>;  // see Realm.h
+
 } // namespace JS
 
 #endif // GCPolicyAPI_h
--- a/js/public/Realm.h
+++ b/js/public/Realm.h
@@ -8,23 +8,62 @@
  * Ways to get various per-Realm objects. All the getters declared in this
  * header operate on the Realm corresponding to the current compartment on the
  * JSContext.
  */
 
 #ifndef js_Realm_h
 #define js_Realm_h
 
-#include "jstypes.h"
+#include "jspubtd.h"
+#include "js/GCPolicyAPI.h"
+#include "js/TypeDecls.h"  // forward-declaration of JS::Realm
 
-struct JSContext;
-class JSObject;
+namespace js {
+namespace gc {
+JS_PUBLIC_API(void) TraceRealm(JSTracer* trc, JS::Realm* realm, const char* name);
+JS_PUBLIC_API(bool) RealmNeedsSweep(JS::Realm* realm);
+}
+}
 
 namespace JS {
 
+// Each Realm holds a strong reference to its GlobalObject, and vice versa.
+template <>
+struct GCPolicy<Realm*>
+{
+    static Realm* initial() { return nullptr; }
+    static void trace(JSTracer* trc, Realm** vp, const char* name) {
+        if (*vp)
+            ::js::gc::TraceRealm(trc, *vp, name);
+    }
+    static bool needsSweep(Realm** vp) {
+        return *vp && ::js::gc::RealmNeedsSweep(*vp);
+    }
+};
+
+// Get the current realm, if any. The ECMAScript spec calls this "the current
+// Realm Record".
+extern JS_PUBLIC_API(Realm*)
+GetCurrentRealmOrNull(JSContext* cx);
+
+// Return the compartment that contains a given realm.
+inline JSCompartment*
+GetCompartmentForRealm(Realm* realm) {
+    // Implementation note: For now, realms are a fiction; we treat realms and
+    // compartments as being one-to-one, but they are actually identical.
+    return reinterpret_cast<JSCompartment*>(realm);
+}
+
+// Return the realm in a given compartment.
+inline Realm*
+GetRealmForCompartment(JSCompartment* compartment) {
+    return reinterpret_cast<Realm*>(compartment);
+}
+
 extern JS_PUBLIC_API(JSObject*)
 GetRealmObjectPrototype(JSContext* cx);
 
 extern JS_PUBLIC_API(JSObject*)
 GetRealmFunctionPrototype(JSContext* cx);
 
 extern JS_PUBLIC_API(JSObject*)
 GetRealmArrayPrototype(JSContext* cx);
--- a/js/public/TraceKind.h
+++ b/js/public/TraceKind.h
@@ -128,27 +128,31 @@ template <TraceKind traceKind> struct Ma
 #define JS_EXPAND_DEF(name, _0, _1) \
     template <> struct MapTraceKindToRootKind<JS::TraceKind::name> { \
         static const JS::RootKind kind = JS::RootKind::name; \
     };
 JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF)
 #undef JS_EXPAND_DEF
 
 // Specify the RootKind for all types. Value and jsid map to special cases;
-// pointer types we can derive directly from the TraceKind; everything else
+// Cell pointer types we can derive directly from the TraceKind; everything else
 // should go in the Traceable list and use GCPolicy<T>::trace for tracing.
 template <typename T>
 struct MapTypeToRootKind {
     static const JS::RootKind kind = JS::RootKind::Traceable;
 };
 template <typename T>
 struct MapTypeToRootKind<T*> {
     static const JS::RootKind kind =
         JS::MapTraceKindToRootKind<JS::MapTypeToTraceKind<T>::kind>::kind;
 };
+template <> struct MapTypeToRootKind<JS::Realm*> {
+    // Not a pointer to a GC cell. Use GCPolicy.
+    static const JS::RootKind kind = JS::RootKind::Traceable;
+};
 template <typename T>
 struct MapTypeToRootKind<mozilla::UniquePtr<T>> {
     static const JS::RootKind kind = JS::MapTypeToRootKind<T>::kind;
 };
 template <> struct MapTypeToRootKind<JS::Value> {
     static const JS::RootKind kind = JS::RootKind::Value;
 };
 template <> struct MapTypeToRootKind<jsid> {
--- a/js/public/TypeDecls.h
+++ b/js/public/TypeDecls.h
@@ -32,16 +32,17 @@ class JSAddonId;
 struct jsid;
 
 namespace JS {
 
 typedef unsigned char Latin1Char;
 
 class Symbol;
 class Value;
+class Realm;
 template <typename T> class Handle;
 template <typename T> class MutableHandle;
 template <typename T> class Rooted;
 template <typename T> class PersistentRooted;
 
 typedef Handle<JSFunction*> HandleFunction;
 typedef Handle<jsid>        HandleId;
 typedef Handle<JSObject*>   HandleObject;
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -663,32 +663,35 @@ struct JSCompartment
     }
 
     // Note: Unrestricted access to the zone's runtime from an arbitrary
     // thread can easily lead to races. Use this method very carefully.
     JSRuntime* runtimeFromAnyThread() const {
         return runtime_;
     }
 
-    /*
-     * Nb: global_ might be nullptr, if (a) it's the atoms compartment, or
-     * (b) the compartment's global has been collected.  The latter can happen
-     * if e.g. a string in a compartment is rooted but no object is, and thus
-     * the global isn't rooted, and thus the global can be finalized while the
-     * compartment lives on.
+    /* The global object for this compartment.
+     *
+     * This returns nullptr if this is the atoms compartment.  (The global_
+     * field is also null briefly during GC, after the global object is
+     * collected; but when that happens the JSCompartment is destroyed during
+     * the same GC.)
      *
      * In contrast, JSObject::global() is infallible because marking a JSObject
      * always marks its global as well.
      * TODO: add infallible JSScript::global()
      */
     inline js::GlobalObject* maybeGlobal() const;
 
     /* An unbarriered getter for use while tracing. */
     inline js::GlobalObject* unsafeUnbarrieredMaybeGlobal() const;
 
+    /* True if a global object exists, but it's being collected. */
+    inline bool globalIsAboutToBeFinalized();
+
     inline void initGlobal(js::GlobalObject& global);
 
   public:
     void*                        data;
 
   private:
     const js::AllocationMetadataBuilder *allocationMetadataBuilder;
 
--- a/js/src/jscompartmentinlines.h
+++ b/js/src/jscompartmentinlines.h
@@ -6,16 +6,17 @@
 
 #ifndef jscompartmentinlines_h
 #define jscompartmentinlines_h
 
 #include "jscompartment.h"
 #include "jsiter.h"
 
 #include "gc/Barrier.h"
+#include "gc/Marking.h"
 
 #include "jscntxtinlines.h"
 
 inline void
 JSCompartment::initGlobal(js::GlobalObject& global)
 {
     MOZ_ASSERT(global.compartment() == this);
     MOZ_ASSERT(!global_);
@@ -30,16 +31,23 @@ JSCompartment::maybeGlobal() const
 }
 
 js::GlobalObject*
 JSCompartment::unsafeUnbarrieredMaybeGlobal() const
 {
     return *global_.unsafeGet();
 }
 
+inline bool
+JSCompartment::globalIsAboutToBeFinalized()
+{
+    MOZ_ASSERT(zone_->isGCSweeping());
+    return global_ && js::gc::IsAboutToBeFinalizedUnbarriered(global_.unsafeGet());
+}
+
 template <typename T>
 js::AutoCompartment::AutoCompartment(JSContext* cx, const T& target)
   : cx_(cx),
     origin_(cx->compartment()),
     maybeLock_(nullptr)
 {
     cx_->enterCompartmentOf(target);
 }
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -3459,20 +3459,19 @@ Zone::destroy(FreeOp* fop)
     fop->delete_(this);
     fop->runtime()->gc.stats().sweptZone();
 }
 
 /*
  * It's simpler if we preserve the invariant that every zone has at least one
  * compartment. If we know we're deleting the entire zone, then
  * SweepCompartments is allowed to delete all compartments. In this case,
- * |keepAtleastOne| is false. If some objects remain in the zone so that it
- * cannot be deleted, then we set |keepAtleastOne| to true, which prohibits
- * SweepCompartments from deleting every compartment. Instead, it preserves an
- * arbitrary compartment in the zone.
+ * |keepAtleastOne| is false. If any cells remain alive in the zone, set
+ * |keepAtleastOne| true to prohibit sweepCompartments from deleting every
+ * compartment. Instead, it preserves an arbitrary compartment in the zone.
  */
 void
 Zone::sweepCompartments(FreeOp* fop, bool keepAtleastOne, bool destroyingRuntime)
 {
     MOZ_ASSERT(!compartments().empty());
 
     mozilla::DebugOnly<JSRuntime*> rt = runtimeFromActiveCooperatingThread();
 
--- a/js/src/vm/Realm.cpp
+++ b/js/src/vm/Realm.cpp
@@ -2,22 +2,44 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "js/Realm.h"
 
 #include "jscntxt.h"
-#include "jscompartment.h" // For JSContext::global
+#include "jscompartment.h"
 
 #include "vm/GlobalObject.h"
 
+#include "jscompartmentinlines.h"
+
 using namespace js;
 
+JS_PUBLIC_API(void)
+gc::TraceRealm(JSTracer* trc, JS::Realm* realm, const char* name)
+{
+    // The way GC works with compartments is basically incomprehensible.
+    // For Realms, what we want is very simple: each Realm has a strong
+    // reference to its GlobalObject, and vice versa.
+    //
+    // Here we simply trace our side of that edge. During GC,
+    // GCRuntime::traceRuntimeCommon() marks all other compartment roots, for
+    // all compartments.
+    JS::GetCompartmentForRealm(realm)->traceGlobal(trc);
+}
+
+JS_PUBLIC_API(bool)
+gc::RealmNeedsSweep(JS::Realm* realm)
+{
+    return JS::GetCompartmentForRealm(realm)->globalIsAboutToBeFinalized();
+}
+
+
 JS_PUBLIC_API(JSObject*)
 JS::GetRealmObjectPrototype(JSContext* cx)
 {
     CHECK_REQUEST(cx);
     return GlobalObject::getOrCreateObjectPrototype(cx, cx->global());
 }
 
 JS_PUBLIC_API(JSObject*)