Bug 687724 (part 2) - Make JSCompartment::global() fallible. r=luke.
authorNicholas Nethercote <nnethercote@mozilla.com>
Tue, 03 Jul 2012 17:25:15 -0700
changeset 98499 7919de7e468c
parent 98498 869b97c469b5
child 98500 e586043f5b58
push id23059
push usereakhgari@mozilla.com
push dateFri, 06 Jul 2012 14:45:18 +0000
treeherdermozilla-central@a8f682801a6d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs687724
milestone16.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 687724 (part 2) - Make JSCompartment::global() fallible. r=luke.
js/src/gc/Marking.cpp
js/src/jsapi.cpp
js/src/jscntxtinlines.h
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsobj.cpp
js/src/jsobjinlines.h
js/src/jstypedarray.cpp
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -629,18 +629,21 @@ ScanBaseShape(GCMarker *gcmarker, BaseSh
     base->assertConsistency();
 
     if (base->hasGetterObject())
         PushMarkStack(gcmarker, base->getterObject());
 
     if (base->hasSetterObject())
         PushMarkStack(gcmarker, base->setterObject());
 
-    if (JSObject *parent = base->getObjectParent())
+    if (JSObject *parent = base->getObjectParent()) {
         PushMarkStack(gcmarker, parent);
+    } else if (GlobalObject *global = base->compartment()->maybeGlobal()) {
+        PushMarkStack(gcmarker, global);
+    }
 
     /*
      * All children of the owned base shape are consistent with its
      * unowned one, thus we do not need to trace through children of the
      * unowned base shape.
      */
     if (base->isOwned()) {
         UnownedBaseShape *unowned = base->baseUnowned();
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1383,18 +1383,18 @@ Class dummy_class = {
 
 } /*namespace js */
 
 JS_PUBLIC_API(JSCrossCompartmentCall *)
 JS_EnterCrossCompartmentCallScript(JSContext *cx, JSScript *target)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-    GlobalObject &global = target->compartment()->global();
-    return JS_EnterCrossCompartmentCall(cx, &global);
+    GlobalObject *global = target->compartment()->maybeGlobal();
+    return global ? JS_EnterCrossCompartmentCall(cx, global) : NULL;
 }
 
 JS_PUBLIC_API(JSCrossCompartmentCall *)
 JS_EnterCrossCompartmentCallStackFrame(JSContext *cx, JSStackFrame *target)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
 
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -224,17 +224,17 @@ class CompartmentChecker
     JSCompartment *compartment;
 
   public:
     explicit CompartmentChecker(JSContext *cx)
       : context(cx), compartment(cx->compartment)
     {
         if (cx->compartment) {
             GlobalObject *global = GetGlobalForScopeChain(cx);
-            JS_ASSERT(cx->compartment->global() == *global);
+            JS_ASSERT(cx->compartment->maybeGlobal() == global);
         }
     }
 
     /*
      * Set a breakpoint here (break js::CompartmentChecker::fail) to debug
      * compartment mismatches.
      */
     static void fail(JSCompartment *c1, JSCompartment *c2) {
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -486,16 +486,19 @@ JSCompartment::sweep(FreeOp *fop, bool r
 
     sweepBreakpoints(fop);
 
     {
         gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_DISCARD_CODE);
         discardJitCode(fop);
     }
 
+    if (global_ && !IsObjectMarked(&global_))
+        global_ = NULL;
+
     /* JIT code can hold references on RegExpShared, so sweep regexps after clearing code. */
     regExps.sweep(rt);
 
     if (!activeAnalysis && !gcPreserveCode) {
         gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_DISCARD_ANALYSIS);
 
         /*
          * Clear the analysis pool, but don't release its data yet. While
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -114,22 +114,33 @@ class AutoDebugModeGC;
 struct JSCompartment
 {
     JSRuntime                    *rt;
     JSPrincipals                 *principals;
 
   private:
     js::GlobalObject             *global_;
   public:
-    js::GlobalObject &global() const {
-        JS_ASSERT(global_->compartment() == this);
-        return *global_;
+    // Nb: global_ might be NULL, 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.
+    //
+    // In contrast, JSObject::global() is infallible because marking a JSObject
+    // always marks its global as well.
+    // TODO: add infallible JSScript::global() and JSContext::global()
+    //
+    js::GlobalObject *maybeGlobal() const {
+        JS_ASSERT_IF(global_, global_->compartment() == this);
+        return global_;
     }
 
     void initGlobal(js::GlobalObject &global) {
+        JS_ASSERT(global.compartment() == this);
         JS_ASSERT(!global_);
         global_ = &global;
     }
 
   public:
     js::gc::ArenaLists           arenas;
 
   private:
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -2343,17 +2343,17 @@ js_Object(JSContext *cx, unsigned argc, 
 
 static inline JSObject *
 NewObject(JSContext *cx, Class *clasp, types::TypeObject *type_, JSObject *parent,
           gc::AllocKind kind)
 {
     JS_ASSERT(clasp != &ArrayClass);
     JS_ASSERT_IF(clasp == &FunctionClass,
                  kind == JSFunction::FinalizeKind || kind == JSFunction::ExtendedFinalizeKind);
-    JS_ASSERT_IF(parent, parent->global() == cx->compartment->global());
+    JS_ASSERT_IF(parent, &parent->global() == cx->compartment->maybeGlobal());
 
     RootedTypeObject type(cx, type_);
 
     RootedShape shape(cx);
     shape = EmptyShape::getInitialShape(cx, clasp, type->proto, parent, kind);
     if (!shape)
         return NULL;
 
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -1229,17 +1229,17 @@ JSObject::isWrapper() const
 }
 
 inline js::GlobalObject &
 JSObject::global() const
 {
     JSObject *obj = const_cast<JSObject *>(this);
     while (JSObject *parent = obj->getParent())
         obj = parent;
-    JS_ASSERT(obj->asGlobal() == compartment()->global());
+    JS_ASSERT(&obj->asGlobal() == compartment()->maybeGlobal());
     return obj->asGlobal();
 }
 
 static inline bool
 js_IsCallable(const js::Value &v)
 {
     return v.isObject() && v.toObject().isCallable();
 }
--- a/js/src/jstypedarray.cpp
+++ b/js/src/jstypedarray.cpp
@@ -1516,17 +1516,17 @@ class TypedArrayTemplate
     // Define an accessor for a read-only property that invokes a native getter
     template<Value ValueGetter(JSObject *)>
     static bool
     DefineGetter(JSContext *cx, PropertyName *name, HandleObject proto)
     {
         RootedId id(cx, NameToId(name));
         unsigned flags = JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_SHARED | JSPROP_GETTER;
 
-        Rooted<GlobalObject*> global(cx, &cx->compartment->global());
+        Rooted<GlobalObject*> global(cx, cx->compartment->maybeGlobal());
         JSObject *getter = js_NewFunction(cx, NULL, Getter<ValueGetter>, 0, 0, global, NULL);
         if (!getter)
             return false;
 
         return DefineNativeProperty(cx, proto, id, UndefinedValue(),
                                     JS_DATA_TO_FUNC_PTR(PropertyOp, getter), NULL,
                                     flags, 0, 0);
     }
@@ -3003,17 +3003,17 @@ IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Floa
         NULL,                /* clear       */                                 \
     }                                                                          \
 }
 
 template<class ArrayType>
 static inline JSObject *
 InitTypedArrayClass(JSContext *cx)
 {
-    Rooted<GlobalObject*> global(cx, &cx->compartment->global());
+    Rooted<GlobalObject*> global(cx, cx->compartment->maybeGlobal());
     RootedObject proto(cx, global->createBlankPrototype(cx, ArrayType::protoClass()));
     if (!proto)
         return NULL;
 
     RootedFunction ctor(cx);
     ctor = global->createConstructor(cx, ArrayType::class_constructor,
                                      cx->runtime->atomState.classAtoms[ArrayType::key], 3);
     if (!ctor)
@@ -3080,17 +3080,17 @@ Class TypedArray::protoClasses[TYPE_MAX]
     IMPL_TYPED_ARRAY_PROTO_CLASS(Float32Array),
     IMPL_TYPED_ARRAY_PROTO_CLASS(Float64Array),
     IMPL_TYPED_ARRAY_PROTO_CLASS(Uint8ClampedArray)
 };
 
 static JSObject *
 InitArrayBufferClass(JSContext *cx)
 {
-    Rooted<GlobalObject*> global(cx, &cx->compartment->global());
+    Rooted<GlobalObject*> global(cx, cx->compartment->maybeGlobal());
     RootedObject arrayBufferProto(cx, global->createBlankPrototype(cx, &ArrayBufferObject::protoClass));
     if (!arrayBufferProto)
         return NULL;
 
     RootedFunction ctor(cx);
     ctor = global->createConstructor(cx, ArrayBufferObject::class_constructor,
                                      CLASS_NAME(cx, ArrayBuffer), 1);
     if (!ctor)
@@ -3193,30 +3193,30 @@ DataViewGetter(JSContext *cx, unsigned a
 
 template<Value ValueGetter(DataViewObject&)>
 bool
 DefineDataViewGetter(JSContext *cx, PropertyName *name, HandleObject proto)
 {
     RootedId id(cx, NameToId(name));
     unsigned flags = JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_SHARED | JSPROP_GETTER;
 
-    Rooted<GlobalObject*> global(cx, &cx->compartment->global());
+    Rooted<GlobalObject*> global(cx, cx->compartment->maybeGlobal());
     JSObject *getter = js_NewFunction(cx, NULL, DataViewGetter<ValueGetter>, 0, 0, global, NULL);
     if (!getter)
         return false;
 
     return DefineNativeProperty(cx, proto, id, UndefinedValue(),
                                 JS_DATA_TO_FUNC_PTR(PropertyOp, getter), NULL,
                                 flags, 0, 0);
 }
 
 JSObject *
 DataViewObject::initClass(JSContext *cx)
 {
-    Rooted<GlobalObject*> global(cx, &cx->compartment->global());
+    Rooted<GlobalObject*> global(cx, cx->compartment->maybeGlobal());
     RootedObject proto(cx, global->createBlankPrototype(cx, &DataViewObject::protoClass));
     if (!proto)
         return NULL;
 
     RootedFunction ctor(cx, global->createConstructor(cx, DataViewObject::class_constructor,
                                                       CLASS_NAME(cx, DataView), 3));
     if (!ctor)
         return NULL;