Make the throw hook tests pass in the methodjit. Add a simpler version of one of the tests.
authorJason Orendorff <jorendorff@mozilla.com>
Mon, 16 May 2011 17:20:04 -0500
changeset 74426 cb456a20aeef907002c4e7a79e7f8bf4373d4727
parent 74425 4efc9b37f30aac73f6092c231c322892074a12c7
child 74427 2a7cbe40d6284ede0a25eb1bddfb4c9e99696a9f
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
milestone6.0a1
Make the throw hook tests pass in the methodjit. Add a simpler version of one of the tests.
js/src/jit-test/tests/debug/hooks-throw-02.js
js/src/jit-test/tests/debug/hooks-throw-03.js
js/src/jit-test/tests/debug/hooks-throw-04.js
js/src/methodjit/InvokeHelpers.cpp
--- a/js/src/jit-test/tests/debug/hooks-throw-02.js
+++ b/js/src/jit-test/tests/debug/hooks-throw-02.js
@@ -18,43 +18,33 @@ g.eval("(" + function () {
                 debuggeeGlobal.log += s;
             }
         };
     } + ")();");
 
 var log;
 
 function k() {
-    try {
-        throw new Error("oops");  // hook call 1
-    } finally {
-        log += 'k-finally, ';
-    } // hook call 2
+    throw new Error("oops");  // hook call 1
 }
 
 function j() {
-    k();  // hook call 3
+    k();  // hook call 2
     log += 'j-unreached, ';
 }
 
 function h() {
-    try {
-        j();  // hook call 4
-        log += 'h-unreached, ';
-    } catch (exc) {
-        log += 'h-catch, ';
-        throw exc; // hook call 5
-    }
+    j();  // hook call 3
+    log += 'h-unreached, ';
 }
 
 function f() {
     try {
-        h(); // hook call 6
+        h(); // hook call 4
     } catch (exc) {
-        log += 'f-catch, ';
+        log += 'f-catch';
     }
-    log += 'f-after, ';
 }
 
 log = '';
 f();
 g.dbg.enabled = false;
-assertEq(log, '!kjhf, k-finally, !kjhf, !jhf, !hf, h-catch, !hf, !f, f-catch, f-after, ');
+assertEq(log, '!kjhf, !jhf, !hf, !f, f-catch');
--- a/js/src/jit-test/tests/debug/hooks-throw-03.js
+++ b/js/src/jit-test/tests/debug/hooks-throw-03.js
@@ -1,21 +1,60 @@
 // |jit-test| debug
-// hooks.throw is not called for exceptions thrown and handled in the debugger.
+// The throw hook is called multiple times as the stack unwinds.
 
 var g = newGlobal('new-compartment');
-var dbg = Debug(g);
-g.log = '';
-dbg.hooks = {
-    debuggerHandler: function (frame) {
-        try {
-            throw new Error("oops");
-        } catch (exc) {
-            g.log += exc.message;
-        }
-    },
-    throw: function (frame) {
-        g.log += 'BAD';
+g.debuggeeGlobal = this;
+g.dbg = null;
+g.eval("(" + function () {
+        dbg = new Debug(debuggeeGlobal);
+        dbg.hooks = {
+            throw: function (frame, exc) {
+                assertEq(frame instanceof Debug.Frame, true);
+                assertEq(exc instanceof Debug.Object, true);
+                var s = '!';
+                for (var f = frame; f; f = f.older)
+                    if (f.type === "call")
+                        s += f.callee.name;
+                s += ', ';
+                debuggeeGlobal.log += s;
+            }
+        };
+    } + ")();");
+
+var log;
+
+function k() {
+    try {
+        throw new Error("oops");  // hook call 1
+    } finally {
+        log += 'k-finally, ';
+    } // hook call 2
+}
+
+function j() {
+    k();  // hook call 3
+    log += 'j-unreached, ';
+}
+
+function h() {
+    try {
+        j();  // hook call 4
+        log += 'h-unreached, ';
+    } catch (exc) {
+        log += 'h-catch, ';
+        throw exc; // hook call 5
     }
-};
+}
 
-g.eval("debugger; log += ' ok';");
-assertEq(g.log, 'oops ok');
+function f() {
+    try {
+        h(); // hook call 6
+    } catch (exc) {
+        log += 'f-catch, ';
+    }
+    log += 'f-after, ';
+}
+
+log = '';
+f();
+g.dbg.enabled = false;
+assertEq(log, '!kjhf, k-finally, !kjhf, !jhf, !hf, h-catch, !hf, !f, f-catch, f-after, ');
copy from js/src/jit-test/tests/debug/hooks-throw-03.js
copy to js/src/jit-test/tests/debug/hooks-throw-04.js
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -53,16 +53,17 @@
 #include "jstypes.h"
 #include "methodjit/StubCalls.h"
 #include "jstracer.h"
 #include "jspropertycache.h"
 #include "methodjit/MonoIC.h"
 #include "jsanalyze.h"
 #include "methodjit/BaseCompiler.h"
 #include "methodjit/ICRepatcher.h"
+#include "jsdbg.h"
 
 #include "jsinterpinlines.h"
 #include "jspropertycacheinlines.h"
 #include "jsscopeinlines.h"
 #include "jsscriptinlines.h"
 #include "jsstrinlines.h"
 #include "jsobjinlines.h"
 #include "jscntxtinlines.h"
@@ -493,42 +494,47 @@ js_InternalThrow(VMFrame &f)
             return NULL;
 
         InlineReturn(f);
     }
 
     // Make sure sp is up to date.
     JS_ASSERT(&cx->regs() == &f.regs);
 
-    // Call the throw hook if necessary
-    JSThrowHook handler = f.cx->debugHooks->throwHook;
-    if (handler) {
-        Value rval;
-        switch (handler(cx, cx->fp()->script(), cx->regs().pc, Jsvalify(&rval),
-                        cx->debugHooks->throwHookData)) {
-          case JSTRAP_ERROR:
-            cx->clearPendingException();
-            return NULL;
-
-          case JSTRAP_RETURN:
-            cx->clearPendingException();
-            cx->fp()->setReturnValue(rval);
-            return cx->jaegerCompartment()->forceReturnFromExternC();
-
-          case JSTRAP_THROW:
-            cx->setPendingException(rval);
-            break;
-
-          default:
-            break;
-        }
-    }
-
     jsbytecode *pc = NULL;
     for (;;) {
+        // Call the throw hook if necessary
+        JSThrowHook handler = cx->debugHooks->throwHook;
+        if (handler || !cx->compartment->getDebuggers().empty()) {
+            Value rval;
+            JSTrapStatus st = Debug::onThrow(cx, &rval);
+            if (st == JSTRAP_CONTINUE && handler) {
+                st = handler(cx, cx->fp()->script(), cx->regs().pc, Jsvalify(&rval),
+                             cx->debugHooks->throwHookData);
+            }
+
+            switch (st) {
+              case JSTRAP_ERROR:
+                cx->clearPendingException();
+                return NULL;
+
+              case JSTRAP_RETURN:
+                cx->clearPendingException();
+                cx->fp()->setReturnValue(rval);
+                return cx->jaegerCompartment()->forceReturnFromExternC();
+
+              case JSTRAP_THROW:
+                cx->setPendingException(rval);
+                break;
+
+              default:
+                break;
+            }
+        }
+
         pc = FindExceptionHandler(cx);
         if (pc)
             break;
 
         // The JIT guarantees that ScriptEpilogue() has always been run
         // upon exiting to its caller. This is important for consistency,
         // where execution modes make similar guarantees about prologues
         // and epilogues. RunTracer(), Interpret(), and Invoke() all