Bug 1160535 part 3 - Make the LazyScript -> JSScript pointer weak. r=jonco,terrence
authorJan de Mooij <jdemooij@mozilla.com>
Thu, 07 May 2015 11:26:34 +0200
changeset 274121 70ba99762486b61c3dd36e1390a8345f46986b80
parent 274120 fdf1b53a61253e0fa9c443be02df85c71bde6f94
child 274122 34781ef1be40cf56cf3edf8bdf60514a6f372e72
push id863
push userraliiev@mozilla.com
push dateMon, 03 Aug 2015 13:22:43 +0000
treeherdermozilla-release@f6321b14228d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco, terrence
bugs1160535
milestone40.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 1160535 part 3 - Make the LazyScript -> JSScript pointer weak. r=jonco,terrence
js/src/gc/Barrier.h
js/src/gc/Marking.cpp
js/src/jsfun.cpp
js/src/jsscript.cpp
js/src/jsscript.h
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -744,16 +744,17 @@ typedef HeapPtr<jsid> HeapId;
 
 typedef ImmutableTenuredPtr<PropertyName*> ImmutablePropertyNamePtr;
 typedef ImmutableTenuredPtr<JS::Symbol*> ImmutableSymbolPtr;
 
 typedef ReadBarriered<DebugScopeObject*> ReadBarrieredDebugScopeObject;
 typedef ReadBarriered<GlobalObject*> ReadBarrieredGlobalObject;
 typedef ReadBarriered<JSFunction*> ReadBarrieredFunction;
 typedef ReadBarriered<JSObject*> ReadBarrieredObject;
+typedef ReadBarriered<JSScript*> ReadBarrieredScript;
 typedef ReadBarriered<ScriptSourceObject*> ReadBarrieredScriptSourceObject;
 typedef ReadBarriered<Shape*> ReadBarrieredShape;
 typedef ReadBarriered<UnownedBaseShape*> ReadBarrieredUnownedBaseShape;
 typedef ReadBarriered<jit::JitCode*> ReadBarrieredJitCode;
 typedef ReadBarriered<ObjectGroup*> ReadBarrieredObjectGroup;
 typedef ReadBarriered<JSAtom*> ReadBarrieredAtom;
 typedef ReadBarriered<JS::Symbol*> ReadBarrieredSymbol;
 
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -808,19 +808,16 @@ LazyScript::traceChildren(JSTracer* trc)
         TraceEdge(trc, &function_, "function");
 
     if (sourceObject_)
         TraceEdge(trc, &sourceObject_, "sourceObject");
 
     if (enclosingScope_)
         TraceEdge(trc, &enclosingScope_, "enclosingScope");
 
-    if (script_)
-        TraceEdge(trc, &script_, "realScript");
-
     // We rely on the fact that atoms are always tenured.
     FreeVariable* freeVariables = this->freeVariables();
     for (auto i : MakeRange(numFreeVariables())) {
         JSAtom* atom = freeVariables[i].atom();
         TraceManuallyBarrieredEdge(trc, &atom, "lazyScriptFreeVariable");
     }
 
     HeapPtrFunction* innerFunctions = this->innerFunctions();
@@ -834,19 +831,16 @@ js::GCMarker::eagerlyMarkChildren(LazySc
         traverse(thing, static_cast<JSObject*>(thing->function_));
 
     if (thing->sourceObject_)
         traverse(thing, static_cast<JSObject*>(thing->sourceObject_));
 
     if (thing->enclosingScope_)
         traverse(thing, static_cast<JSObject*>(thing->enclosingScope_));
 
-    if (thing->script_)
-        traverse(thing, static_cast<JSScript*>(thing->script_));
-
     // We rely on the fact that atoms are always tenured.
     LazyScript::FreeVariable* freeVariables = thing->freeVariables();
     for (auto i : MakeRange(thing->numFreeVariables()))
         traverse(thing, static_cast<JSString*>(freeVariables[i].atom()));
 
     HeapPtrFunction* innerFunctions = thing->innerFunctions();
     for (auto i : MakeRange(thing->numInnerFunctions()))
         traverse(thing, static_cast<JSObject*>(innerFunctions[i]));
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -551,17 +551,17 @@ js::XDRInterpretedFunction(XDRState<mode
 
         if (fun->isStarGenerator())
             firstword |= IsStarGenerator;
 
         if (fun->isInterpretedLazy()) {
             // This can only happen for re-lazified cloned functions, so this
             // does not apply to any JSFunction produced by the parser, only to
             // JSFunction created by the runtime.
-            MOZ_ASSERT(!fun->lazyScript()->maybeScript());
+            MOZ_ASSERT(!fun->lazyScript()->maybeScriptUnbarriered());
 
             // Encode a lazy script.
             firstword |= IsLazy;
             lazy = fun->lazyScript();
         } else {
             // Encode the script.
             script = fun->nonLazyScript();
         }
@@ -1522,21 +1522,16 @@ JSFunction::maybeRelazify(JSRuntime* rt)
     JSScript* script = nonLazyScript();
 
     flags_ &= ~INTERPRETED;
     flags_ |= INTERPRETED_LAZY;
     LazyScript* lazy = script->maybeLazyScript();
     u.i.s.lazy_ = lazy;
     if (lazy) {
         MOZ_ASSERT(!isSelfHostedBuiltin());
-        // If this is the script stored in the lazy script to be cloned
-        // for un-lazifying other functions, reset it so the script can
-        // be freed.
-        if (lazy->maybeScript() == script)
-            lazy->resetScript();
     } else {
         MOZ_ASSERT(isSelfHostedBuiltin());
         MOZ_ASSERT(isExtended());
         MOZ_ASSERT(getExtendedSlot(0).toString()->isAtom());
     }
 }
 
 /* ES5 15.3.4.5.1 and 15.3.4.5.2. */
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1136,17 +1136,17 @@ js::XDRLazyScript(XDRState<mode>* xdr, H
     {
         uint32_t begin;
         uint32_t end;
         uint32_t lineno;
         uint32_t column;
         uint64_t packedFields;
 
         if (mode == XDR_ENCODE) {
-            MOZ_ASSERT(!lazy->maybeScript());
+            MOZ_ASSERT(!lazy->maybeScriptUnbarriered());
             MOZ_ASSERT(fun == lazy->functionNonDelazifying());
 
             begin = lazy->begin();
             end = lazy->end();
             lineno = lazy->lineno();
             column = lazy->column();
             packedFields = lazy->packedFields();
         }
@@ -2728,16 +2728,25 @@ JSScript::finalize(FreeOp* fop)
     destroyDebugScript(fop);
 
     if (data) {
         JS_POISON(data, 0xdb, computedSizeOfData());
         fop->free_(data);
     }
 
     fop->runtime()->lazyScriptCache.remove(this);
+
+    if (lazyScript && lazyScript->maybeScriptUnbarriered() == this) {
+        // In most cases, our LazyScript's script pointer will reference this
+        // script. However, because sweeping can be incremental, it's
+        // possible LazyScript::maybeScript() already null'ed this pointer.
+        // Furthermore, if we unlazified the LazyScript, it will have a
+        // completely different JSScript.
+        lazyScript->resetScript();
+    }
 }
 
 static const uint32_t GSN_CACHE_THRESHOLD = 100;
 
 void
 GSNCache::purge()
 {
     code = nullptr;
@@ -3720,25 +3729,26 @@ LazyScript::LazyScript(JSFunction* fun, 
     column_(column)
 {
     MOZ_ASSERT(begin <= end);
 }
 
 void
 LazyScript::initScript(JSScript* script)
 {
-    MOZ_ASSERT(script && !script_);
-    script_ = script;
+    MOZ_ASSERT(script);
+    MOZ_ASSERT(!script_.unbarrieredGet());
+    script_.set(script);
 }
 
 void
 LazyScript::resetScript()
 {
-    MOZ_ASSERT(script_);
-    script_ = nullptr;
+    MOZ_ASSERT(script_.unbarrieredGet());
+    script_.set(nullptr);
 }
 
 void
 LazyScript::setParent(JSObject* enclosingScope, ScriptSourceObject* sourceObject)
 {
     MOZ_ASSERT(!sourceObject_ && !enclosingScope_);
     MOZ_ASSERT_IF(enclosingScope, function_->compartment() == enclosingScope->compartment());
     MOZ_ASSERT(function_->compartment() == sourceObject->compartment());
@@ -3846,17 +3856,17 @@ LazyScript::Create(ExclusiveContext* cx,
     for (i = 0, num = res->numInnerFunctions(); i < num; i++)
         functions[i].init(dummyFun);
 
     // Set the enclosing scope of the lazy function, this would later be
     // used to define the environment when the function would be used.
     MOZ_ASSERT(!res->sourceObject());
     res->setParent(enclosingScope, &sourceObjectScript->scriptSourceUnwrap());
 
-    MOZ_ASSERT(!res->maybeScript());
+    MOZ_ASSERT(!res->maybeScriptUnbarriered());
     if (script)
         res->initScript(script);
 
     return res;
 }
 
 void
 LazyScript::initRuntimeFields(uint64_t packedFields)
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -1901,18 +1901,19 @@ class LazyScript : public gc::TenuredCel
 
         JSAtom* atom() const { return (JSAtom*)(bits_ & MASK); }
         void setIsHoistedUse() { bits_ |= HOISTED_USE_BIT; }
         bool isHoistedUse() const { return bool(bits_ & HOISTED_USE_BIT); }
     };
 
   private:
     // If non-nullptr, the script has been compiled and this is a forwarding
-    // pointer to the result.
-    HeapPtrScript script_;
+    // pointer to the result. This is a weak pointer: after relazification, we
+    // can collect the script if there are no other pointers to it.
+    ReadBarrieredScript script_;
 
     // Original function with which the lazy script is associated.
     HeapPtrFunction function_;
 
     // Function or block chain in which the script is nested, or nullptr.
     HeapPtrObject enclosingScope_;
 
     // ScriptSourceObject, or nullptr if the script in which this is nested
@@ -2000,19 +2001,25 @@ class LazyScript : public gc::TenuredCel
 
     inline JSFunction* functionDelazifying(JSContext* cx) const;
     JSFunction* functionNonDelazifying() const {
         return function_;
     }
 
     void initScript(JSScript* script);
     void resetScript();
+
     JSScript* maybeScript() {
+        if (script_.unbarrieredGet() && gc::IsAboutToBeFinalized(&script_))
+            script_.set(nullptr);
         return script_;
     }
+    JSScript* maybeScriptUnbarriered() const {
+        return script_.unbarrieredGet();
+    }
 
     JSObject* enclosingScope() const {
         return enclosingScope_;
     }
     ScriptSourceObject* sourceObject() const;
     ScriptSource* scriptSource() const {
         return sourceObject()->source();
     }