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);
|