|
1 // Comprehensive test of get/setVariable on many kinds of environments and |
|
2 // bindings. |
|
3 |
|
4 load(libdir + "asserts.js"); |
|
5 |
|
6 var cases = [ |
|
7 // global bindings and bindings on the global prototype chain |
|
8 "x = VAL; @@", |
|
9 "var x = VAL; @@", |
|
10 "Object.prototype.x = VAL; @@", |
|
11 |
|
12 // let, catch, and comprehension bindings |
|
13 "let x = VAL; @@", |
|
14 "{ let x = VAL; @@ }", |
|
15 "let (x = VAL) { @@ }", |
|
16 "try { throw VAL; } catch (x) { @@ }", |
|
17 "try { throw VAL; } catch (x) { @@ }", |
|
18 "for (let x of [VAL]) { @@ }", |
|
19 "for each (let x in [VAL]) { @@ }", |
|
20 "switch (0) { default: let x = VAL; @@ }", |
|
21 "[function () { @@ }() for (x of [VAL])];", |
|
22 // "((function () { @@ })() for (x of [VAL])).next();", // bug 709367 |
|
23 |
|
24 // arguments |
|
25 "function f(x) { @@ } f(VAL);", |
|
26 "function f([w, x]) { @@ } f([0, VAL]);", |
|
27 "function f({v: x}) { @@ } f({v: VAL});", |
|
28 "function f([w, {v: x}]) { @@ } f([0, {v: VAL}]);", |
|
29 |
|
30 // bindings in functions |
|
31 "function f() { var x = VAL; @@ } f();", |
|
32 "function f() { let x = VAL; @@ } f();", |
|
33 "function f([x]) { let x = VAL; @@ } f(['fail']);", |
|
34 "function f(x) { { let x = VAL; @@ } } f('fail');", |
|
35 "function f() { function x() {} x = VAL; @@ } f();", |
|
36 |
|
37 // dynamic bindings |
|
38 "function f(s) { eval(s); @@ } f('var x = VAL');", |
|
39 "function f(s) { let (x = 'fail') { eval(s); } x = VAL; @@ } f('var x;');", |
|
40 "var x = VAL; function f(s) { eval('var x = 0;'); eval(s); @@ } f('delete x;');", |
|
41 "function f(obj) { with (obj) { @@ } } f({x: VAL});", |
|
42 "function f(obj) { with (obj) { @@ } } f(Object.create({x: VAL}));", |
|
43 "function f(b) { if (b) { function x(){} } x = VAL; @@ } f(1);", |
|
44 ]; |
|
45 |
|
46 var nextval = 1000; |
|
47 |
|
48 function test(code, debugStmts, followupStmts) { |
|
49 var val = nextval++; |
|
50 var hits = 0; |
|
51 |
|
52 var g = newGlobal(); |
|
53 g.eval("function debugMe() { var x = 'wrong-x'; debugger; }"); |
|
54 g.capture = null; |
|
55 |
|
56 var dbg = Debugger(g); |
|
57 dbg.onDebuggerStatement = function (frame) { |
|
58 if (frame.callee !== null && frame.callee.name == 'debugMe') |
|
59 frame = frame.older; |
|
60 var env = frame.environment.find("x"); |
|
61 assertEq(env.getVariable("x"), val) |
|
62 assertEq(env.setVariable("x", 'ok'), undefined); |
|
63 assertEq(env.getVariable("x"), 'ok'); |
|
64 |
|
65 // setVariable cannot create new variables. |
|
66 assertThrowsInstanceOf(function () { env.setVariable("newVar", 0); }, TypeError); |
|
67 hits++; |
|
68 }; |
|
69 |
|
70 code = code.replace("@@", debugStmts); |
|
71 if (followupStmts !== undefined) |
|
72 code += " " + followupStmts; |
|
73 code = code.replace(/VAL/g, uneval(val)); |
|
74 g.eval(code); |
|
75 assertEq(hits, 1); |
|
76 } |
|
77 |
|
78 for (var s of cases) { |
|
79 // Test triggering the debugger right in the scope in which x is bound. |
|
80 test(s, "debugger; assertEq(x, 'ok');"); |
|
81 |
|
82 // Test calling a function that triggers the debugger. |
|
83 test(s, "debugMe(); assertEq(x, 'ok');"); |
|
84 |
|
85 // Test triggering the debugger from a scope nested in x's scope. |
|
86 test(s, "let (y = 'irrelevant') { (function (z) { let (zz = y) { debugger; }})(); } assertEq(x, 'ok');"), |
|
87 |
|
88 // Test closing over the variable and triggering the debugger later, after |
|
89 // leaving the variable's scope. |
|
90 test(s, "capture = {dbg: function () { debugger; }, get x() { return x; }};", |
|
91 "assertEq(capture.x, VAL); capture.dbg(); assertEq(capture.x, 'ok');"); |
|
92 } |