| // Exercise the call to ScriptDebugPrologue in js_InternalInterpret. |
| |
| // This may change, but as of this writing, inline caches (ICs) are |
| // disabled in debug mode, and those are the only users of the out-of-line entry |
| // points for JIT code (arityCheckEntry, argsCheckEntry, fastEntry); debug |
| // mode uses only invokeEntry. This means most of the bytecode tails in |
| // js_InternalInterpret that might call ScriptPrologue or ScriptEpilogue are |
| // unreachable in debug mode: they're only called from the out-of-line entry |
| // points. |
| // |
| // The exception is REJOIN_THIS_PROTOTYPE, which can be reached reliably if you |
| // add a JS_GC call to stubs::GetPropNoCache. JIT code calls that stub to |
| // retrieve the 'prototype' property of a function called as a constructor, if |
| // TI can't establish the exact identity of that prototype's value at compile |
| // time. Thus the preoccupation with constructors here. |
| |
| load(libdir + "asserts.js"); |
| |
| var debuggee = newGlobal(); |
| var dbg = Debugger(debuggee); |
| var hits, savedFrame; |
| |
| // Allow the constructor to return normally. |
| dbg.onEnterFrame = function (frame) { |
| hits++; |
| if (frame.constructing) { |
| savedFrame = frame; |
| assertEq(savedFrame.live, true); |
| return undefined; |
| } |
| return undefined; |
| }; |
| hits = 0; |
| debuggee.hits = 0; |
| savedFrame = undefined; |
| assertEq(typeof debuggee.eval("function f(){ hits++; } f.prototype = {}; new f;"), "object"); |
| assertEq(hits, 2); |
| assertEq(savedFrame.live, false); |
| assertEq(debuggee.hits, 1); |
| |
| // Force an early return from the constructor. |
| dbg.onEnterFrame = function (frame) { |
| hits++; |
| if (frame.constructing) { |
| savedFrame = frame; |
| assertEq(savedFrame.live, true); |
| return { return: "pass" }; |
| } |
| return undefined; |
| }; |
| hits = 0; |
| debuggee.hits = 0; |
| savedFrame = undefined; |
| assertEq(typeof debuggee.eval("function f(){ hits++; } f.prototype = {}; new f;"), "object"); |
| assertEq(hits, 2); |
| assertEq(savedFrame.live, false); |
| assertEq(debuggee.hits, 0); |
| |
| // Force the constructor to throw an exception. |
| dbg.onEnterFrame = function (frame) { |
| hits++; |
| if (frame.constructing) { |
| savedFrame = frame; |
| assertEq(savedFrame.live, true); |
| return { throw: "pass" }; |
| } |
| return undefined; |
| }; |
| hits = 0; |
| debuggee.hits = 0; |
| savedFrame = undefined; |
| assertThrowsValue(function () { |
| debuggee.eval("function f(){ hits++ } f.prototype = {}; new f;"); |
| }, "pass"); |
| assertEq(hits, 2); |
| assertEq(savedFrame.live, false); |
| assertEq(debuggee.hits, 0); |
| |
| // Ensure that forcing an early return only returns from one JS call. |
| debuggee.eval("function g() { var result = new f; g_hits++; return result; }"); |
| dbg.onEnterFrame = function (frame) { |
| hits++; |
| if (frame.constructing) { |
| savedFrame = frame; |
| assertEq(savedFrame.live, true); |
| return { return: "pass" }; |
| } |
| return undefined; |
| }; |
| hits = 0; |
| debuggee.hits = 0; |
| debuggee.g_hits = 0; |
| savedFrame = undefined; |
| assertEq(typeof debuggee.eval("g();"), "object"); |
| assertEq(hits, 3); |
| assertEq(savedFrame.live, false); |
| assertEq(debuggee.hits, 0); |
| assertEq(debuggee.g_hits, 1); |