Bug 1292564 - Fix OOM handling while constructing DebugScopes. r=terrence, a=ritu
authorJon Coppeard <jcoppeard@mozilla.com>
Thu, 11 Aug 2016 10:41:07 +0100
changeset 347757 b263ff7776f278a5c0e0f4d70e1d6b209f4c4542
parent 347756 dd4b4df3eb2ac6f1e5812db4d55dbbdbfd2d7827
child 347758 c6110450ff8f25ee4ce5073a9a4a4cb48474ecd5
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence, ritu
bugs1292564
milestone50.0a2
Bug 1292564 - Fix OOM handling while constructing DebugScopes. r=terrence, a=ritu
js/src/jit-test/tests/gc/bug-1292564.js
js/src/vm/ScopeObject.cpp
js/src/vm/ScopeObject.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1292564.js
@@ -0,0 +1,12 @@
+// |jit-test| allow-oom
+
+if (!('oomTest' in this))
+    quit();
+
+oomTest(() => {
+    let global = newGlobal();
+    Debugger(global).onDebuggerStatement = function (frame) {
+        frame.eval("f")
+    }
+    global.eval("debugger")
+}, false);
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -2614,29 +2614,30 @@ DebugScopes::hasDebugScope(JSContext* cx
         MOZ_ASSERT(CanUseDebugScopeMaps(cx));
         return &obj->as<DebugScopeObject>();
     }
 
     return nullptr;
 }
 
 bool
-DebugScopes::addDebugScope(JSContext* cx, ScopeObject& scope, DebugScopeObject& debugScope)
+DebugScopes::addDebugScope(JSContext* cx, Handle<ScopeObject*> scope,
+                           Handle<DebugScopeObject*> debugScope)
 {
-    MOZ_ASSERT(cx->compartment() == scope.compartment());
-    MOZ_ASSERT(cx->compartment() == debugScope.compartment());
+    MOZ_ASSERT(cx->compartment() == scope->compartment());
+    MOZ_ASSERT(cx->compartment() == debugScope->compartment());
 
     if (!CanUseDebugScopeMaps(cx))
         return true;
 
     DebugScopes* scopes = ensureCompartmentData(cx);
     if (!scopes)
         return false;
 
-    return scopes->proxiedScopes.add(cx, &scope, &debugScope);
+    return scopes->proxiedScopes.add(cx, scope, debugScope);
 }
 
 DebugScopeObject*
 DebugScopes::hasDebugScope(JSContext* cx, const ScopeIter& si)
 {
     MOZ_ASSERT(!si.hasSyntacticScopeObject());
 
     DebugScopes* scopes = cx->compartment()->debugScopes;
@@ -2646,42 +2647,42 @@ DebugScopes::hasDebugScope(JSContext* cx
     if (MissingScopeMap::Ptr p = scopes->missingScopes.lookup(MissingScopeKey(si))) {
         MOZ_ASSERT(CanUseDebugScopeMaps(cx));
         return p->value();
     }
     return nullptr;
 }
 
 bool
-DebugScopes::addDebugScope(JSContext* cx, const ScopeIter& si, DebugScopeObject& debugScope)
+DebugScopes::addDebugScope(JSContext* cx, const ScopeIter& si, Handle<DebugScopeObject*> debugScope)
 {
     MOZ_ASSERT(!si.hasSyntacticScopeObject());
-    MOZ_ASSERT(cx->compartment() == debugScope.compartment());
+    MOZ_ASSERT(cx->compartment() == debugScope->compartment());
     // Generators should always reify their scopes.
     MOZ_ASSERT_IF(si.type() == ScopeIter::Call, !si.fun().isGenerator());
 
     if (!CanUseDebugScopeMaps(cx))
         return true;
 
     DebugScopes* scopes = ensureCompartmentData(cx);
     if (!scopes)
         return false;
 
     MissingScopeKey key(si);
     MOZ_ASSERT(!scopes->missingScopes.has(key));
-    if (!scopes->missingScopes.put(key, ReadBarriered<DebugScopeObject*>(&debugScope))) {
+    if (!scopes->missingScopes.put(key, ReadBarriered<DebugScopeObject*>(debugScope))) {
         ReportOutOfMemory(cx);
         return false;
     }
 
     // Only add to liveScopes if we synthesized the debug scope on a live
     // frame.
     if (si.withinInitialFrame()) {
-        MOZ_ASSERT(!scopes->liveScopes.has(&debugScope.scope()));
-        if (!scopes->liveScopes.put(&debugScope.scope(), LiveScopeVal(si))) {
+        MOZ_ASSERT(!scopes->liveScopes.has(&debugScope->scope()));
+        if (!scopes->liveScopes.put(&debugScope->scope(), LiveScopeVal(si))) {
             ReportOutOfMemory(cx);
             return false;
         }
     }
 
     return true;
 }
 
@@ -2973,21 +2974,21 @@ GetDebugScopeForScope(JSContext* cx, con
     JSObject& maybeDecl = scope->enclosingScope();
     if (maybeDecl.is<DeclEnvObject>()) {
         MOZ_ASSERT(CallObjectLambdaName(scope->as<CallObject>().callee()));
         enclosingDebug = DebugScopeObject::create(cx, maybeDecl.as<DeclEnvObject>(), enclosingDebug);
         if (!enclosingDebug)
             return nullptr;
     }
 
-    DebugScopeObject* debugScope = DebugScopeObject::create(cx, *scope, enclosingDebug);
+    Rooted<DebugScopeObject*> debugScope(cx, DebugScopeObject::create(cx, *scope, enclosingDebug));
     if (!debugScope)
         return nullptr;
 
-    if (!DebugScopes::addDebugScope(cx, *scope, *debugScope))
+    if (!DebugScopes::addDebugScope(cx, scope, debugScope))
         return nullptr;
 
     return debugScope;
 }
 
 static DebugScopeObject*
 GetDebugScopeForMissing(JSContext* cx, const ScopeIter& si)
 {
@@ -3007,17 +3008,17 @@ GetDebugScopeForMissing(JSContext* cx, c
      * objects, we only use the pretend call object to access callee, bindings
      * and to receive dynamically added properties. Together, this provides the
      * nice invariant that every DebugScopeObject has a ScopeObject.
      *
      * Note: to preserve scopeChain depth invariants, these lazily-reified
      * scopes must not be put on the frame's scope chain; instead, they are
      * maintained via DebugScopes hooks.
      */
-    DebugScopeObject* debugScope = nullptr;
+    Rooted<DebugScopeObject*> debugScope(cx);
     switch (si.type()) {
       case ScopeIter::Module:
           MOZ_CRASH(); // TODO: Implement debug scopes for modules.
           break;
 
       case ScopeIter::Call: {
         RootedFunction callee(cx, &si.fun());
         // Generators should always reify their scopes.
@@ -3070,17 +3071,17 @@ GetDebugScopeForMissing(JSContext* cx, c
       case ScopeIter::Eval:
         MOZ_CRASH("should already have a scope");
       case ScopeIter::NonSyntactic:
         MOZ_CRASH("non-syntactic scopes cannot be synthesized");
     }
     if (!debugScope)
         return nullptr;
 
-    if (!DebugScopes::addDebugScope(cx, si, *debugScope))
+    if (!DebugScopes::addDebugScope(cx, si, debugScope))
         return nullptr;
 
     return debugScope;
 }
 
 static JSObject*
 GetDebugScopeForNonScopeObject(const ScopeIter& si)
 {
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -1352,20 +1352,22 @@ class DebugScopes
   public:
     void mark(JSTracer* trc);
     void sweep(JSRuntime* rt);
 #ifdef JS_GC_ZEAL
     void checkHashTablesAfterMovingGC(JSRuntime* rt);
 #endif
 
     static DebugScopeObject* hasDebugScope(JSContext* cx, ScopeObject& scope);
-    static bool addDebugScope(JSContext* cx, ScopeObject& scope, DebugScopeObject& debugScope);
+    static bool addDebugScope(JSContext* cx, Handle<ScopeObject*> scope,
+                              Handle<DebugScopeObject*> debugScope);
 
     static DebugScopeObject* hasDebugScope(JSContext* cx, const ScopeIter& si);
-    static bool addDebugScope(JSContext* cx, const ScopeIter& si, DebugScopeObject& debugScope);
+    static bool addDebugScope(JSContext* cx, const ScopeIter& si,
+                              Handle<DebugScopeObject*> debugScope);
 
     static bool updateLiveScopes(JSContext* cx);
     static LiveScopeVal* hasLiveScope(ScopeObject& scope);
     static void unsetPrevUpToDateUntil(JSContext* cx, AbstractFramePtr frame);
 
     // When a frame bails out from Ion to Baseline, there might be missing
     // scopes keyed on, and live scopes containing, the old
     // RematerializedFrame. Forward those values to the new BaselineFrame.
@@ -1609,20 +1611,14 @@ bool
 AnalyzeEntrainedVariables(JSContext* cx, HandleScript script);
 #endif
 
 } // namespace js
 
 namespace JS {
 
 template <>
-struct DeletePolicy<js::DebugScopeObject>
-{
-    explicit DeletePolicy(JSRuntime* rt) : rt_(rt) {}
-    void operator()(const js::DebugScopeObject* ptr);
-
-  private:
-    JSRuntime* rt_;
-};
+struct DeletePolicy<js::DebugScopes> : public js::GCManagedDeletePolicy<js::DebugScopes>
+{};
 
 } // namespace JS
 
 #endif /* vm_ScopeObject_h */