// Copyright 2016 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Flags: --no-always-opt --no-opt // The first part of this file is copied over from debug-set-variable-value.js // (a few tests were removed because they make no sense for modules). The second // part is new. var Debug = debug.Debug; // Accepts a function/closure 'fun' that must have a debugger statement inside. // A variable 'variable_name' must be initialized before debugger statement // and returned after the statement. The test will alter variable value when // on debugger statement and check that returned value reflects the change. function RunPauseTest(scope_number, expected_old_result, variable_name, new_value, expected_new_result, fun) { var actual_old_result = fun(); assertEquals(expected_old_result, actual_old_result); var listener_delegate; var listener_called = false; var exception = null; function listener_delegate(exec_state) { var scope = exec_state.frame(0).scope(scope_number); scope.setVariableValue(variable_name, new_value); } function listener(event, exec_state, event_data, data) { try { if (event == Debug.DebugEvent.Break) { listener_called = true; listener_delegate(exec_state); } } catch (e) { exception = e; } } // Add the debug event listener. Debug.setListener(listener); var actual_new_result; try { actual_new_result = fun(); } finally { Debug.setListener(null); } if (exception != null) { assertUnreachable("Exception in listener\n" + exception.stack); } assertTrue(listener_called); assertEquals(expected_new_result, actual_new_result); } function ClosureTestCase(scope_index, old_result, variable_name, new_value, new_result, success_expected, factory) { this.scope_index_ = scope_index; this.old_result_ = old_result; this.variable_name_ = variable_name; this.new_value_ = new_value; this.new_result_ = new_result; this.success_expected_ = success_expected; this.factory_ = factory; } ClosureTestCase.prototype.run_pause_test = function() { var th = this; var fun = this.factory_(true); this.run_and_catch_(function() { RunPauseTest(th.scope_index_ + 1, th.old_result_, th.variable_name_, th.new_value_, th.new_result_, fun); }); } ClosureTestCase.prototype.run_and_catch_ = function(runnable) { if (this.success_expected_) { runnable(); } else { assertThrows(runnable); } } // Test scopes visible from closures. var closure_test_cases = [ new ClosureTestCase(0, 'cat', 'v1', 5, 5, true, function Factory(debug_stop) { var v1 = 'cat'; return function() { if (debug_stop) debugger; return v1; } }), new ClosureTestCase(0, 4, 't', 7, 9, true, function Factory(debug_stop) { var t = 2; var r = eval("t"); return function() { if (debug_stop) debugger; return r + t; } }), new ClosureTestCase(0, 6, 't', 10, 13, true, function Factory(debug_stop) { var t = 2; var r = eval("t = 3"); return function() { if (debug_stop) debugger; return r + t; } }), new ClosureTestCase(2, 'capybara', 'foo', 77, 77, true, function Factory(debug_stop) { var foo = "capybara"; return (function() { var bar = "fish"; try { throw {name: "test exception"}; } catch (e) { return function() { if (debug_stop) debugger; bar = "beast"; return foo; } } })(); }), new ClosureTestCase(0, 'AlphaBeta', 'eee', 5, '5Beta', true, function Factory(debug_stop) { var foo = "Beta"; return (function() { var bar = "fish"; try { throw "Alpha"; } catch (eee) { return function() { if (debug_stop) debugger; return eee + foo; } } })(); }) ]; for (var i = 0; i < closure_test_cases.length; i++) { closure_test_cases[i].run_pause_test(); } // Test local scope. RunPauseTest(0, 'HelloYou', 'u', 'We', 'HelloWe', (function Factory() { return function() { var u = "You"; var v = "Hello"; debugger; return v + u; } })()); RunPauseTest(0, 'Helloworld', 'p', 'GoodBye', 'HelloGoodBye', (function Factory() { function H(p) { var v = "Hello"; debugger; return v + p; } return function() { return H("world"); } })()); RunPauseTest(0, 'mouse', 'v1', 'dog', 'dog', (function Factory() { return function() { var v1 = 'cat'; eval("v1 = 'mouse'"); debugger; return v1; } })()); // Check that we correctly update local variable that // is referenced from an inner closure. RunPauseTest(0, 'Blue', 'v', 'Green', 'Green', (function Factory() { return function() { function A() { var v = "Blue"; function Inner() { return void v; } debugger; return v; } return A(); } })()); // Check that we correctly update parameter, that is known to be stored // both on stack and in heap. RunPauseTest(0, 5, 'p', 2012, 2012, (function Factory() { return function() { function A(p) { function Inner() { return void p; } debugger; return p; } return A(5); } })()); //////////////////////////////////////////////////////////////////////////////// // From here on we test the module scope. //////////////////////////////////////////////////////////////////////////////// // Non-existing variable. { let exception; function listener(event, exec_state) { if (event == Debug.DebugEvent.Break) { let module_scope = exec_state.frame().scope(1); assertEquals(debug.ScopeType.Module, module_scope.scopeType()); try { module_scope.setVariableValue('spargel', 42); } catch(e) { exception = e; } } } Debug.setListener(listener); assertThrows(() => spargel, ReferenceError); debugger; assertThrows(() => spargel, ReferenceError); assertTrue(exception !== undefined); } // Local (non-exported) variable. let salat = 12; { function listener(event, exec_state) { if (event == Debug.DebugEvent.Break) { let module_scope = exec_state.frame().scope(1); assertEquals(debug.ScopeType.Module, module_scope.scopeType()); module_scope.setVariableValue('salat', 42); } } Debug.setListener(listener); assertEquals(12, salat); debugger; assertEquals(42, salat); } // Local (non-exported) variable, nested access. let salad = 12; { function listener(event, exec_state) { if (event == Debug.DebugEvent.Break) { let scope_count = exec_state.frame().scopeCount(); let module_scope = exec_state.frame().scope(1); assertEquals(debug.ScopeType.Module, module_scope.scopeType()); module_scope.setVariableValue('salad', 42); } } Debug.setListener(listener); function foo() { assertEquals(12, salad); debugger; assertEquals(42, salad); }; foo(); } // Exported variable. export let salami = 1; { function listener(event, exec_state) { if (event == Debug.DebugEvent.Break) { let module_scope = exec_state.frame().scope(1); assertEquals(debug.ScopeType.Module, module_scope.scopeType()); module_scope.setVariableValue('salami', 2); } } Debug.setListener(listener); assertEquals(1, salami); debugger; assertEquals(2, salami); } // Exported variable, nested access. export let ham = 1; { function listener(event, exec_state) { if (event == Debug.DebugEvent.Break) { let scope_count = exec_state.frame().scopeCount(); let module_scope = exec_state.frame().scope(1); assertEquals(debug.ScopeType.Module, module_scope.scopeType()); module_scope.setVariableValue('ham', 2); } } Debug.setListener(listener); function foo() { assertEquals(1, ham); debugger; assertEquals(2, ham); }; foo(); } // Imported variable. Setting is currently not supported. import { salami as wurst } from "./debug-modules-set-variable-value.mjs"; { let exception; function listener(event, exec_state) { if (event == Debug.DebugEvent.Break) { let module_scope = exec_state.frame().scope(1); assertEquals(debug.ScopeType.Module, module_scope.scopeType()); try { module_scope.setVariableValue('wurst', 3); } catch(e) { exception = e; } } } Debug.setListener(listener); assertEquals(2, wurst); debugger; assertEquals(2, wurst); assertTrue(exception !== undefined); } // Imported variable, nested access. Setting is currently not supported. import { salami as wurstl } from "./debug-modules-set-variable-value.mjs"; { let exception; function listener(event, exec_state) { if (event == Debug.DebugEvent.Break) { let scope_count = exec_state.frame().scopeCount(); let module_scope = exec_state.frame().scope(2); assertEquals(debug.ScopeType.Module, module_scope.scopeType()); try { module_scope.setVariableValue('wurstl', 3); } catch(e) { exception = e; } } } Debug.setListener(listener); function foo() { assertEquals(2, wurstl); debugger; assertEquals(2, wurstl); assertTrue(exception !== undefined); }; foo(); } Debug.setListener(null);