| // Comprehensive test of get/setVariable on many kinds of environments and |
| // bindings. |
| |
| load(libdir + "asserts.js"); |
| |
| var cases = [ |
| // global bindings and bindings on the global prototype chain |
| "x = VAL; @@", |
| "var x = VAL; @@", |
| "Object.prototype.x = VAL; @@", |
| |
| // let, catch, and comprehension bindings |
| "let x = VAL; @@", |
| "{ let x = VAL; @@ }", |
| "try { throw VAL; } catch (x) { @@ }", |
| "try { throw VAL; } catch (x) { @@ }", |
| "for (let x of [VAL]) { @@ }", |
| "for each (let x in [VAL]) { @@ }", |
| "switch (0) { default: let x = VAL; @@ }", |
| "[function () { @@ }() for (x of [VAL])];", |
| // "((function () { @@ })() for (x of [VAL])).next();", // bug 709367 |
| |
| // arguments |
| "function f(x) { @@ } f(VAL);", |
| "function f([w, x]) { @@ } f([0, VAL]);", |
| "function f({v: x}) { @@ } f({v: VAL});", |
| "function f([w, {v: x}]) { @@ } f([0, {v: VAL}]);", |
| |
| // bindings in functions |
| "function f() { var x = VAL; @@ } f();", |
| "function f() { let x = VAL; @@ } f();", |
| "function f() { function x() {} x = VAL; @@ } f();", |
| |
| // dynamic bindings |
| "function f(s) { eval(s); @@ } f('var x = VAL');", |
| "var x = VAL; function f(s) { eval('var x = 0;'); eval(s); @@ } f('delete x;');", |
| "function f(obj) { with (obj) { @@ } } f({x: VAL});", |
| "function f(obj) { with (obj) { @@ } } f(Object.create({x: VAL}));", |
| "function f(b) { if (b) { function x(){} } x = VAL; @@ } f(1);", |
| ]; |
| |
| var nextval = 1000; |
| |
| function test(code, debugStmts, followupStmts) { |
| var val = nextval++; |
| var hits = 0; |
| |
| var g = newGlobal(); |
| g.eval("function debugMe() { var x = 'wrong-x'; debugger; }"); |
| g.capture = null; |
| |
| var dbg = Debugger(g); |
| dbg.onDebuggerStatement = function (frame) { |
| if (frame.callee !== null && frame.callee.name == 'debugMe') |
| frame = frame.older; |
| var env = frame.environment.find("x"); |
| assertEq(env.getVariable("x"), val) |
| assertEq(env.setVariable("x", 'ok'), undefined); |
| assertEq(env.getVariable("x"), 'ok'); |
| |
| // setVariable cannot create new variables. |
| assertThrowsInstanceOf(function () { env.setVariable("newVar", 0); }, TypeError); |
| hits++; |
| }; |
| |
| code = code.replace("@@", debugStmts); |
| if (followupStmts !== undefined) |
| code += " " + followupStmts; |
| code = code.replace(/VAL/g, uneval(val)); |
| g.eval(code); |
| assertEq(hits, 1); |
| } |
| |
| for (var s of cases) { |
| // Test triggering the debugger right in the scope in which x is bound. |
| test(s, "debugger; assertEq(x, 'ok');"); |
| |
| // Test calling a function that triggers the debugger. |
| test(s, "debugMe(); assertEq(x, 'ok');"); |
| |
| // Test triggering the debugger from a scope nested in x's scope. |
| test(s, "{ let y = 'irrelevant'; (function (z) { { let zz = y; debugger; } })(); } assertEq(x, 'ok');"), |
| |
| // Test closing over the variable and triggering the debugger later, after |
| // leaving the variable's scope. |
| test(s, "capture = {dbg: function () { debugger; }, get x() { return x; }};", |
| "assertEq(capture.x, VAL); capture.dbg(); assertEq(capture.x, 'ok');"); |
| } |