Bug 1026477: Handle function declarations on DebugScopeObjects correctly. r=shu
authorJim Blandy <jimb@mozilla.com>
Thu, 18 Dec 2014 13:56:27 -0800
changeset 225450 b717156bf98959771966739441165654ae04659c
parent 225449 a21c59ad2af3f1848da35501d712f3925b3f125b
child 225451 4ba8480c860809190ebcb29cf17b6cbb4bfe9dca
push id28163
push userphilringnalda@gmail.com
push dateSat, 24 Jan 2015 16:27:39 +0000
treeherdermozilla-central@1cf171c1a177 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersshu
bugs1026477
milestone38.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 1026477: Handle function declarations on DebugScopeObjects correctly. r=shu
js/src/jit-test/tests/debug/Frame-eval-26.js
js/src/jit-test/tests/debug/Frame-eval-27.js
js/src/vm/Interpreter.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Frame-eval-26.js
@@ -0,0 +1,16 @@
+// Bug 1026477: Defining functions with D.F.p.eval works, even if there's
+// already a non-aliased var binding for the identifier.
+
+var g = newGlobal();
+var dbg = new Debugger(g);
+dbg.onDebuggerStatement = function (frame) {
+  frame.older.eval('function f() { }');
+};
+
+// When the compiler sees the 'debugger' statement, it marks all variables as
+// aliased, but we want to test the case where f is in a stack frame slot, so we
+// put the 'debugger' statement in a separate function, and use frame.older to
+// get back to the anonymous function's frame.
+g.eval('function q() { debugger; }');
+assertEq(typeof g.eval('(function () { var f = 42; q(); return f; })();'),
+         "function");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Frame-eval-27.js
@@ -0,0 +1,13 @@
+// Bug 1026477: Defining functions with D.F.p.eval works, even if there's
+// already a var binding for the identifier.
+
+var g = newGlobal();
+var dbg = new Debugger(g);
+dbg.onDebuggerStatement = function (frame) {
+  frame.eval('function f() { }');
+};
+
+// When the compiler sees the 'debugger' statement, it marks all variables as
+// aliased, so f will live in a Call object.
+assertEq(typeof g.eval('(function () { var f = 42; debugger; return f;})();'),
+         "function");
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -3720,18 +3720,26 @@ js::DefFunOperation(JSContext *cx, Handl
     unsigned attrs = script->isActiveEval()
                      ? JSPROP_ENUMERATE
                      : JSPROP_ENUMERATE | JSPROP_PERMANENT;
 
     /* Steps 5d, 5f. */
     if (!shape || pobj != parent)
         return DefineProperty(cx, parent, name, rval, nullptr, nullptr, attrs);
 
-    /* Step 5e. */
-    MOZ_ASSERT(parent->isNative());
+    /*
+     * Step 5e.
+     *
+     * A DebugScopeObject is okay here, and sometimes necessary. If
+     * Debugger.Frame.prototype.eval defines a function with the same name as an
+     * extant variable in the frame, the DebugScopeObject takes care of storing
+     * the function in the stack frame (for non-aliased variables) or on the
+     * scope object (for aliased).
+     */
+    MOZ_ASSERT(parent->isNative() || parent->is<DebugScopeObject>());
     if (parent->is<GlobalObject>()) {
         if (shape->configurable())
             return DefineProperty(cx, parent, name, rval, nullptr, nullptr, attrs);
 
         if (shape->isAccessorDescriptor() || !shape->writable() || !shape->enumerable()) {
             JSAutoByteString bytes;
             if (AtomToPrintableString(cx, name, &bytes)) {
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REDEFINE_PROP,