Bug 1148388 - Handle lost accesses in missing Debugger scopes for block objects. (r=jimb)
authorShu-yu Guo <shu@rfrn.org>
Wed, 08 Apr 2015 20:36:20 -0700
changeset 238275 89c9b9067ed8fce295724ba36928288429704464
parent 238274 9fc22f95a34b0a572c37a161c2b264d2839857e0
child 238276 28de2ef94e204ceceb769df35c4900143ba36745
push id58171
push usershu@rfrn.org
push dateThu, 09 Apr 2015 03:33:53 +0000
treeherdermozilla-inbound@89c9b9067ed8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimb
bugs1148388
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 1148388 - Handle lost accesses in missing Debugger scopes for block objects. (r=jimb)
js/src/jit-test/tests/debug/Frame-eval-29.js
js/src/vm/ScopeObject.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Frame-eval-29.js
@@ -0,0 +1,59 @@
+// Test reading and setting values on "hollow" debug scopes. In testGet and
+// testSet below, f and g *must* be called from a non-heavyweight lambda to
+// trigger the creation of the "hollow" debug scopes for the missing scopes.
+//
+// The reason is that a direct call to f or g that accesses a in testGet or
+// testSet's frame is actually recoverable. The Debugger can synthesize a scope
+// based on the frame. By contorting through a lambda, it becomes unsound to
+// synthesize a scope based on the lambda function's frame. Since f and g are
+// accessing a, which is itself free inside the lambda, the Debugger has no way
+// to tell if the on-stack testGet or testSet frame is the frame that *would
+// have* allocated a scope for the lambda, *had the lambda been heavyweight*.
+// 
+// More concretely, if the inner lambda were returned from testGet and testSet,
+// then called from a different invocation of testGet or testSet, it becomes
+// obvious that it is incorrect to synthesize a scope based on the frame of
+// that different invocation.
+
+load(libdir + "evalInFrame.js");
+
+function f() {
+  // Eval one frame up. Nothing aliases a.
+  evalInFrame(1, "print(a)");
+}
+
+function g() {
+  evalInFrame(1, "a = 43");
+}
+
+function testGet() {
+  {
+    let a = 42;
+    (function () { f(); })();
+  }
+}
+
+function testSet() {
+  {
+    let a = 42;
+    (function () { g(); })();
+  }
+}
+
+var log = "";
+
+try {
+  testGet();
+} catch (e) {
+  // Throws due to a having been optimized out.
+  log += "g";
+}
+
+try {
+  testSet();
+} catch (e) {
+  // Throws due to a having been optimized out.
+  log += "s";
+}
+
+assertEq(log, "gs");
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -1346,20 +1346,28 @@ class DebugScopeProxy : public BaseProxy
                 AbstractFramePtr frame = maybeLiveScope->frame();
                 uint32_t local = block->staticBlock().blockIndexToLocalIndex(i);
                 MOZ_ASSERT(local < frame.script()->nfixed());
                 if (action == GET)
                     vp.set(frame.unaliasedLocal(local));
                 else
                     frame.unaliasedLocal(local) = vp;
             } else {
-                if (action == GET)
+                if (action == GET) {
+                    // A ClonedBlockObject whose static block does not need
+                    // cloning is a "hollow" block object reflected for
+                    // missing block scopes. Their slot values are lost.
+                    if (!block->staticBlock().needsClone()) {
+                        *accessResult = ACCESS_LOST;
+                        return true;
+                    }
                     vp.set(block->var(i, DONT_CHECK_ALIASING));
-                else
+                } else {
                     block->setVar(i, vp, DONT_CHECK_ALIASING);
+                }
             }
 
             *accessResult = ACCESS_UNALIASED;
             return true;
         }
 
         /* The rest of the internal scopes do not have unaliased vars. */
         MOZ_ASSERT(scope->is<DeclEnvObject>() || scope->is<DynamicWithObject>() ||