Add Debug.Frame.prototype.live.
authorJason Orendorff <jorendorff@mozilla.com>
Thu, 28 Apr 2011 18:44:28 -0500
changeset 74398 1443d370f7dca1461d9a8a8515803ab2fa3ec1bc
parent 74397 d806bd4f6a1ed852e8a151a297eff997cebc5f1d
child 74399 49acedc64071d70a17704db6f9c707f25584d4d1
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
milestone6.0a1
Add Debug.Frame.prototype.live.
js/src/jit-test/tests/debug/debug-object-25.js
js/src/jit-test/tests/debug/debug-object-26.js
js/src/jit-test/tests/debug/debug-object-27.js
js/src/js.msg
js/src/jsdbg.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/debug-object-25.js
@@ -0,0 +1,44 @@
+// |jit-test| debug
+// Debug.Frame.prototype.live is true for frames on the stack and false for
+// frames that have returned
+
+var desc = Object.getOwnPropertyDescriptor(Debug.Frame.prototype, "live");
+assertEq(typeof desc.get, "function");
+assertEq(desc.set, undefined);
+assertEq(desc.configurable, true);
+assertEq(desc.enumerable, false);
+
+var loc;
+
+var g = newGlobal('new-compartment');
+g.debuggeeGlobal = this;
+g.eval("var hits = 0;");
+g.eval("(" + function () {
+        var a = [];
+        var dbg = Debug(debuggeeGlobal);
+        dbg.hooks = {
+            debuggerHandler: function (frame) {
+                var loc = debuggeeGlobal.loc;
+                a[loc] = frame;
+                for (var i = 0; i < a.length; i++) {
+                    assertEq(a[i] === frame, i === loc);
+                    assertEq(!!(a[i] && a[i].live), i >= loc);
+                }
+                hits++;
+            }
+        };
+    } + ")()");
+
+function f(n) {
+    loc = n; debugger;
+    if (n !== 0) {
+        f(n - 1);
+        loc = n; debugger;
+        eval("f(n - 1);");
+        loc = n; debugger;
+    }
+}
+
+f(4);
+assertEq(g.hits, 16 + 8*3 + 4*3 + 2*3 + 1*3);
+
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/debug-object-26.js
@@ -0,0 +1,35 @@
+// |jit-test| debug
+// Debug.Frame.prototype.live is false for frames that have thrown or been thrown through
+
+load(libdir + "asserts.js");
+
+var g = newGlobal('new-compartment');
+g.debuggeeGlobal = this;
+g.eval("var finalCheck;");
+g.eval("(" + function () {
+        var a = [];
+        var dbg = Debug(debuggeeGlobal);
+        dbg.hooks = {
+            debuggerHandler: function (frame) {
+                a.push(frame);
+                for (var i = 0; i < a.length; i++)
+                    assertEq(a[i].live, true);
+            }
+        };
+        finalCheck = function (n) {
+            assertEq(a.length, n);
+            for (var i = 0; i < n; i++)
+                assertEq(a[i].live, false);
+        };
+    } + ")()");
+
+function f(n) {
+    debugger;
+    if (--n > 0)
+        f(n);
+    else
+        throw "fit";
+}
+
+assertThrowsValue(function () { f(10); }, "fit");
+g.finalCheck(10);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/debug-object-27.js
@@ -0,0 +1,21 @@
+// |jit-test| debug
+// frame.type throws if !frame.live
+
+load(libdir + "asserts.js");
+
+var g = newGlobal('new-compartment');
+g.debuggeeGlobal = this;
+g.eval("var f;");
+g.eval("(" + function () {
+        Debug(debuggeeGlobal).hooks = {
+            debuggerHandler: function (frame) {
+                assertEq(frame.type, "call");
+                assertEq(frame.live, true);
+                f = frame;
+            }
+        };
+    } + ")()");
+
+(function () { debugger; })();
+assertEq(g.f.live, false);
+assertThrowsInstanceOf(function () { g.f.type; }, g.Error);
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -348,8 +348,9 @@ MSG_DEF(JSMSG_CANT_CLONE_OBJECT,      26
 MSG_DEF(JSMSG_NON_NATIVE_SCOPE,       266, 0, JSEXN_TYPEERR, "non-native scope object")
 MSG_DEF(JSMSG_STRICT_FUNCTION_STATEMENT, 267, 0, JSEXN_SYNTAXERR, "in strict mode code, functions may be declared only at top level or immediately within another function")
 MSG_DEF(JSMSG_INVALID_FOR_IN_INIT,    268, 0, JSEXN_SYNTAXERR, "for-in loop let declaration may not have an initializer")
 MSG_DEF(JSMSG_CLEARED_SCOPE,          269, 0, JSEXN_TYPEERR, "attempt to run compile-and-go script on a cleared scope")
 MSG_DEF(JSMSG_INVALID_DATE,           270, 0, JSEXN_RANGEERR, "invalid date")
 MSG_DEF(JSMSG_CCW_REQUIRED,           271, 1, JSEXN_TYPEERR, "{0}: argument must be an object from a different compartment")
 MSG_DEF(JSMSG_DEBUG_BAD_RESUMPTION,   272, 0, JSEXN_TYPEERR, "debugger resumption value must be undefined, {throw: val}, {return: val}, or null")
 MSG_DEF(JSMSG_ASSIGN_FUNCTION_OR_NULL, 273, 1, JSEXN_TYPEERR, "value assigned to {0} must be a function or null")
+MSG_DEF(JSMSG_DEBUG_FRAME_NOT_LIVE,   274, 0, JSEXN_ERR, "stack frame is not live")
--- a/js/src/jsdbg.cpp
+++ b/js/src/jsdbg.cpp
@@ -557,18 +557,49 @@ JSPropertySpec Debug::properties[] = {
 // === Debug.Frame
 
 Class Frame_class = {
     "Frame", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_FRAME_COUNT),
     PropertyStub, PropertyStub, PropertyStub, StrictPropertyStub,
     EnumerateStub, ResolveStub, ConvertStub, FinalizeStub,
 };
 
+JSObject *
+CheckThisFrame(JSContext *cx, Value *vp, const char *fnname, bool checkLive)
+{
+    if (!vp[1].isObject()) {
+        ReportObjectRequired(cx);
+        return NULL;
+    }
+    JSObject *thisobj = &vp[1].toObject();
+    if (thisobj->getClass() != &Frame_class) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
+                             "Debug.Frame", fnname, thisobj->getClass()->name);
+        return NULL;
+    }
+
+    // Check for e.g. Debug.prototype, which is of the Debug JSClass but isn't
+    // really a Debug object.
+    if (!thisobj->getPrivate()) {
+        if (thisobj->getReservedSlot(JSSLOT_FRAME_OWNER).isUndefined()) {
+            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
+                                 "Debug.Frame", fnname, "prototype object");
+            return NULL;
+        }
+        if (checkLive) {
+            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_FRAME_NOT_LIVE,
+                                 "Debug.Frame", fnname);
+            return NULL;
+        }
+    }
+    return thisobj;
+}
+
 #define THIS_FRAME(cx, vp, fnname, thisobj, fp)                              \
-    JSObject *thisobj = CheckThisClass(cx, vp, &Frame_class, fnname);        \
+    JSObject *thisobj = CheckThisFrame(cx, vp, fnname, true);                \
     if (!thisobj)                                                            \
         return false;                                                        \
     JSStackFrame *fp = (JSStackFrame *) thisobj->getPrivate()
 
 JSBool
 Frame_getType(JSContext *cx, uintN argc, Value *vp)
 {
     THIS_FRAME(cx, vp, "get type", thisobj, fp);
@@ -587,25 +618,37 @@ JSBool
 Frame_getGenerator(JSContext *cx, uintN argc, Value *vp)
 {
     THIS_FRAME(cx, vp, "get generator", thisobj, fp);
     vp->setBoolean(fp->isGeneratorFrame());
     return true;
 }
 
 JSBool
+Frame_getLive(JSContext *cx, uintN argc, Value *vp)
+{
+    JSObject *thisobj = CheckThisFrame(cx, vp, "get live", false);
+    if (!thisobj)
+        return false;
+    JSStackFrame *fp = (JSStackFrame *) thisobj->getPrivate();
+    vp->setBoolean(!!fp);
+    return true;
+}
+
+JSBool
 Frame_construct(JSContext *cx, uintN argc, Value *vp)
 {
     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_CONSTRUCTOR, "Debug.Frame");
     return false;
 }
 
 JSPropertySpec Frame_properties[] = {
     JS_PSG("type", Frame_getType, 0),
     JS_PSG("generator", Frame_getGenerator, 0),
+    JS_PSG("live", Frame_getLive, 0),
     JS_PS_END
 };
 
 // === Glue
 
 extern JS_PUBLIC_API(JSBool)
 JS_DefineDebugObject(JSContext *cx, JSObject *obj)
 {