summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/debug/Debugger-onEnterFrame-resumption-05.js
blob: 8885aa0d800c4541d3f516287c51b5d47ec2c990 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
// 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({newCompartment: true});
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.onStack, 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.onStack, false);
assertEq(debuggee.hits, 1);

// Force an early return from the constructor.
dbg.onEnterFrame = function (frame) {
    hits++;
    if (frame.constructing) {
        savedFrame = frame;
        assertEq(savedFrame.onStack, 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.onStack, false);
assertEq(debuggee.hits, 0);

// Force the constructor to throw an exception.
dbg.onEnterFrame = function (frame) {
    hits++;
    if (frame.constructing) {
        savedFrame = frame;
        assertEq(savedFrame.onStack, 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.onStack, 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.onStack, 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.onStack, false);
assertEq(debuggee.hits, 0);
assertEq(debuggee.g_hits, 1);