[debugger] Various break-related functionality in test wrapper
This CL adds simple implementation of break and stepping-related functionality as required by the debug-step.js test. This includes * stepOver, stepInto, stepOut * setBreakPoint * clearBreakPoint * evaluate Some of these, e.g. setBreakPoint are not fully implemented for all cases but only for the ones we need right now. One interesting result of this is that using the inspector protocol is roughly 14x slower for debug-step.js (14s instead of 0.5s). One cause of this seems to be iteration over all object properties in toProtocolValue, which is used to serialize JS objects before being sent over the wire (e.g. FrameMirrors). This is something that should be fixed at some point. In the meantime, the test now runs 100 instead of 1000 iterations. BUG=v8:5530 Review-Url: https://codereview.chromium.org/2447073007 Cr-Commit-Position: refs/heads/master@{#40636}
This commit is contained in:
parent
c8d2a8cf16
commit
83b560b0e5
@ -1429,6 +1429,7 @@ RUNTIME_FUNCTION(Runtime_DebugGetPrototype) {
|
||||
|
||||
|
||||
// Patches script source (should be called upon BeforeCompile event).
|
||||
// TODO(5530): Remove once uses in debug.js are gone.
|
||||
RUNTIME_FUNCTION(Runtime_DebugSetScriptSource) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
@ -1569,6 +1570,7 @@ RUNTIME_FUNCTION(Runtime_GetScript) {
|
||||
return *Script::GetWrapper(found);
|
||||
}
|
||||
|
||||
// TODO(5530): Remove once uses in debug.js are gone.
|
||||
RUNTIME_FUNCTION(Runtime_ScriptLineCount) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
@ -1583,6 +1585,7 @@ RUNTIME_FUNCTION(Runtime_ScriptLineCount) {
|
||||
return Smi::FromInt(line_ends_array->length());
|
||||
}
|
||||
|
||||
// TODO(5530): Remove once uses in debug.js are gone.
|
||||
RUNTIME_FUNCTION(Runtime_ScriptLineStartPosition) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
@ -1609,6 +1612,7 @@ RUNTIME_FUNCTION(Runtime_ScriptLineStartPosition) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(5530): Remove once uses in debug.js are gone.
|
||||
RUNTIME_FUNCTION(Runtime_ScriptLineEndPosition) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
@ -1661,62 +1665,49 @@ static Handle<Object> GetJSPositionInfo(Handle<Script> script, int position,
|
||||
return jsinfo;
|
||||
}
|
||||
|
||||
// Get information on a specific source line and column possibly offset by a
|
||||
// fixed source position. This function is used to find a source position from
|
||||
// a line and column position. The fixed source position offset is typically
|
||||
// used to find a source position in a function based on a line and column in
|
||||
// the source for the function alone. The offset passed will then be the
|
||||
// start position of the source for the function within the full script source.
|
||||
// Note that incoming line and column parameters may be undefined, and are
|
||||
// assumed to be passed *with* offsets.
|
||||
RUNTIME_FUNCTION(Runtime_ScriptLocationFromLine) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 4);
|
||||
CONVERT_ARG_CHECKED(JSValue, script, 0);
|
||||
|
||||
CHECK(script->value()->IsScript());
|
||||
Handle<Script> script_handle = Handle<Script>(Script::cast(script->value()));
|
||||
namespace {
|
||||
|
||||
Handle<Object> ScriptLocationFromLine(Isolate* isolate, Handle<Script> script,
|
||||
Handle<Object> opt_line,
|
||||
Handle<Object> opt_column,
|
||||
int32_t offset) {
|
||||
// Line and column are possibly undefined and we need to handle these cases,
|
||||
// additionally subtracting corresponding offsets.
|
||||
|
||||
int32_t line;
|
||||
if (args[1]->IsNull(isolate) || args[1]->IsUndefined(isolate)) {
|
||||
if (opt_line->IsNull(isolate) || opt_line->IsUndefined(isolate)) {
|
||||
line = 0;
|
||||
} else {
|
||||
CHECK(args[1]->IsNumber());
|
||||
line = NumberToInt32(args[1]) - script_handle->line_offset();
|
||||
CHECK(opt_line->IsNumber());
|
||||
line = NumberToInt32(*opt_line) - script->line_offset();
|
||||
}
|
||||
|
||||
int32_t column;
|
||||
if (args[2]->IsNull(isolate) || args[2]->IsUndefined(isolate)) {
|
||||
if (opt_column->IsNull(isolate) || opt_column->IsUndefined(isolate)) {
|
||||
column = 0;
|
||||
} else {
|
||||
CHECK(args[2]->IsNumber());
|
||||
column = NumberToInt32(args[2]);
|
||||
if (line == 0) column -= script_handle->column_offset();
|
||||
CHECK(opt_column->IsNumber());
|
||||
column = NumberToInt32(*opt_column);
|
||||
if (line == 0) column -= script->column_offset();
|
||||
}
|
||||
|
||||
CONVERT_NUMBER_CHECKED(int32_t, offset_position, Int32, args[3]);
|
||||
|
||||
if (line < 0 || column < 0 || offset_position < 0) {
|
||||
return isolate->heap()->null_value();
|
||||
if (line < 0 || column < 0 || offset < 0) {
|
||||
return isolate->factory()->null_value();
|
||||
}
|
||||
|
||||
Script::InitLineEnds(script_handle);
|
||||
Script::InitLineEnds(script);
|
||||
|
||||
FixedArray* line_ends_array = FixedArray::cast(script_handle->line_ends());
|
||||
FixedArray* line_ends_array = FixedArray::cast(script->line_ends());
|
||||
const int line_count = line_ends_array->length();
|
||||
|
||||
int position;
|
||||
if (line == 0) {
|
||||
position = offset_position + column;
|
||||
position = offset + column;
|
||||
} else {
|
||||
Script::PositionInfo info;
|
||||
if (!script_handle->GetPositionInfo(offset_position, &info,
|
||||
Script::NO_OFFSET) ||
|
||||
if (!script->GetPositionInfo(offset, &info, Script::NO_OFFSET) ||
|
||||
info.line + line >= line_count) {
|
||||
return isolate->heap()->null_value();
|
||||
return isolate->factory()->null_value();
|
||||
}
|
||||
|
||||
const int offset_line = info.line + line;
|
||||
@ -1727,10 +1718,65 @@ RUNTIME_FUNCTION(Runtime_ScriptLocationFromLine) {
|
||||
position = offset_line_position + column;
|
||||
}
|
||||
|
||||
return *GetJSPositionInfo(script_handle, position, Script::NO_OFFSET,
|
||||
isolate);
|
||||
return GetJSPositionInfo(script, position, Script::NO_OFFSET, isolate);
|
||||
}
|
||||
|
||||
// Slow traversal over all scripts on the heap.
|
||||
bool GetScriptById(Isolate* isolate, int needle, Handle<Script>* result) {
|
||||
Script::Iterator iterator(isolate);
|
||||
Script* script = NULL;
|
||||
while ((script = iterator.Next()) != NULL) {
|
||||
if (script->id() == needle) {
|
||||
*result = handle(script);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Get information on a specific source line and column possibly offset by a
|
||||
// fixed source position. This function is used to find a source position from
|
||||
// a line and column position. The fixed source position offset is typically
|
||||
// used to find a source position in a function based on a line and column in
|
||||
// the source for the function alone. The offset passed will then be the
|
||||
// start position of the source for the function within the full script source.
|
||||
// Note that incoming line and column parameters may be undefined, and are
|
||||
// assumed to be passed *with* offsets.
|
||||
// TODO(5530): Remove once uses in debug.js are gone.
|
||||
RUNTIME_FUNCTION(Runtime_ScriptLocationFromLine) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 4);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSValue, script, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, opt_line, 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, opt_column, 2);
|
||||
CONVERT_NUMBER_CHECKED(int32_t, offset, Int32, args[3]);
|
||||
|
||||
CHECK(script->value()->IsScript());
|
||||
Handle<Script> script_handle = Handle<Script>(Script::cast(script->value()));
|
||||
|
||||
return *ScriptLocationFromLine(isolate, script_handle, opt_line, opt_column,
|
||||
offset);
|
||||
}
|
||||
|
||||
// TODO(5530): Rename once conflicting function has been deleted.
|
||||
RUNTIME_FUNCTION(Runtime_ScriptLocationFromLine2) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 4);
|
||||
CONVERT_NUMBER_CHECKED(int32_t, scriptid, Int32, args[0]);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, opt_line, 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, opt_column, 2);
|
||||
CONVERT_NUMBER_CHECKED(int32_t, offset, Int32, args[3]);
|
||||
|
||||
Handle<Script> script;
|
||||
CHECK(GetScriptById(isolate, scriptid, &script));
|
||||
|
||||
return *ScriptLocationFromLine(isolate, script, opt_line, opt_column, offset);
|
||||
}
|
||||
|
||||
// TODO(5530): Remove once uses in debug.js are gone.
|
||||
RUNTIME_FUNCTION(Runtime_ScriptPositionInfo) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 3);
|
||||
@ -1748,6 +1794,7 @@ RUNTIME_FUNCTION(Runtime_ScriptPositionInfo) {
|
||||
|
||||
// Returns the given line as a string, or null if line is out of bounds.
|
||||
// The parameter line is expected to include the script's line offset.
|
||||
// TODO(5530): Remove once uses in debug.js are gone.
|
||||
RUNTIME_FUNCTION(Runtime_ScriptSourceLine) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
@ -1850,6 +1897,7 @@ RUNTIME_FUNCTION(Runtime_DebugBreakInOptimizedCode) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// TODO(5530): Remove once uses in debug.js are gone.
|
||||
RUNTIME_FUNCTION(Runtime_GetWasmFunctionOffsetTable) {
|
||||
DCHECK(args.length() == 1);
|
||||
HandleScope scope(isolate);
|
||||
@ -1865,6 +1913,7 @@ RUNTIME_FUNCTION(Runtime_GetWasmFunctionOffsetTable) {
|
||||
return *isolate->factory()->NewJSArrayWithElements(elements);
|
||||
}
|
||||
|
||||
// TODO(5530): Remove once uses in debug.js are gone.
|
||||
RUNTIME_FUNCTION(Runtime_DisassembleWasmFunction) {
|
||||
DCHECK(args.length() == 1);
|
||||
HandleScope scope(isolate);
|
||||
|
@ -55,7 +55,7 @@ RUNTIME_FUNCTION(Runtime_FunctionRemovePrototype) {
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
// TODO(5530): Remove once uses in debug.js are gone.
|
||||
RUNTIME_FUNCTION(Runtime_FunctionGetScript) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(1, args.length());
|
||||
@ -71,6 +71,20 @@ RUNTIME_FUNCTION(Runtime_FunctionGetScript) {
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_FunctionGetScriptId) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(1, args.length());
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, function, 0);
|
||||
|
||||
if (function->IsJSFunction()) {
|
||||
Handle<Object> script(
|
||||
Handle<JSFunction>::cast(function)->shared()->script(), isolate);
|
||||
if (script->IsScript()) {
|
||||
return Smi::FromInt(Handle<Script>::cast(script)->id());
|
||||
}
|
||||
}
|
||||
return Smi::FromInt(-1);
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_FunctionGetSourceCode) {
|
||||
HandleScope scope(isolate);
|
||||
|
@ -187,6 +187,7 @@ namespace internal {
|
||||
F(ScriptLineStartPosition, 2, 1) \
|
||||
F(ScriptLineEndPosition, 2, 1) \
|
||||
F(ScriptLocationFromLine, 4, 1) \
|
||||
F(ScriptLocationFromLine2, 4, 1) \
|
||||
F(ScriptPositionInfo, 3, 1) \
|
||||
F(ScriptSourceLine, 2, 1) \
|
||||
F(DebugPrepareStepInIfStepping, 1, 1) \
|
||||
@ -221,6 +222,7 @@ namespace internal {
|
||||
F(FunctionSetName, 2, 1) \
|
||||
F(FunctionRemovePrototype, 1, 1) \
|
||||
F(FunctionGetScript, 1, 1) \
|
||||
F(FunctionGetScriptId, 1, 1) \
|
||||
F(FunctionGetSourceCode, 1, 1) \
|
||||
F(FunctionGetScriptSourcePosition, 1, 1) \
|
||||
F(FunctionGetContextData, 1, 1) \
|
||||
|
61
test/debugger/debugger/debug-step-2.js
Normal file
61
test/debugger/debugger/debug-step-2.js
Normal file
@ -0,0 +1,61 @@
|
||||
// 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.
|
||||
|
||||
const Debug = new DebugWrapper();
|
||||
Debug.enable();
|
||||
|
||||
// This test tests that full code compiled without debug break slots
|
||||
// is recompiled with debug break slots when debugging is started.
|
||||
|
||||
var bp;
|
||||
var done = false;
|
||||
var step_count = 0;
|
||||
|
||||
// Debug event listener which steps until the global variable done is true.
|
||||
function listener(event, exec_state, event_data, data) {
|
||||
if (event == Debug.DebugEvent.Break) {
|
||||
if (!done) Debug.stepOver();
|
||||
step_count++;
|
||||
}
|
||||
};
|
||||
|
||||
// Set the global variables state to prpare the stepping test.
|
||||
function prepare_step_test() {
|
||||
done = false;
|
||||
step_count = 0;
|
||||
}
|
||||
|
||||
// Test function to step through.
|
||||
function f() {
|
||||
var i = 1;
|
||||
var j = 2;
|
||||
done = true;
|
||||
};
|
||||
|
||||
prepare_step_test();
|
||||
f();
|
||||
|
||||
// Add the debug event listener.
|
||||
Debug.setListener(listener);
|
||||
|
||||
bp = Debug.setBreakPoint(f, 1);
|
||||
|
||||
prepare_step_test();
|
||||
f();
|
||||
assertEquals(4, step_count);
|
||||
Debug.clearBreakPoint(bp);
|
||||
|
||||
// Set a breakpoint on the first var statement (line 1).
|
||||
bp = Debug.setBreakPoint(f, 1);
|
||||
|
||||
// Step through the function ensuring that the var statements are hit as well.
|
||||
prepare_step_test();
|
||||
f();
|
||||
assertEquals(4, step_count);
|
||||
|
||||
// Clear the breakpoint and check that no stepping happens.
|
||||
Debug.clearBreakPoint(bp);
|
||||
prepare_step_test();
|
||||
f();
|
||||
assertEquals(0, step_count);
|
68
test/debugger/debugger/debug-step-3.js
Normal file
68
test/debugger/debugger/debug-step-3.js
Normal file
@ -0,0 +1,68 @@
|
||||
// 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.
|
||||
|
||||
const Debug = new DebugWrapper();
|
||||
Debug.enable();
|
||||
|
||||
// This test tests that full code compiled without debug break slots
|
||||
// is recompiled with debug break slots when debugging is started.
|
||||
|
||||
var bp;
|
||||
var done = false;
|
||||
var step_count = 0;
|
||||
var set_bp = false
|
||||
|
||||
// Debug event listener which steps until the global variable done is true.
|
||||
function listener(event, exec_state, event_data, data) {
|
||||
if (event == Debug.DebugEvent.Break) {
|
||||
if (!done) Debug.stepOver();
|
||||
step_count++;
|
||||
}
|
||||
};
|
||||
|
||||
// Set the global variables state to prepare the stepping test.
|
||||
function prepare_step_test() {
|
||||
done = false;
|
||||
step_count = 0;
|
||||
}
|
||||
|
||||
// Test function to step through.
|
||||
function f() {
|
||||
var a = 0;
|
||||
if (set_bp) { bp = Debug.setBreakPoint(f, 3); }
|
||||
var i = 1;
|
||||
var j = 2;
|
||||
done = true;
|
||||
};
|
||||
|
||||
prepare_step_test();
|
||||
f();
|
||||
|
||||
// Add the debug event listener.
|
||||
Debug.setListener(listener);
|
||||
|
||||
// Make f set a breakpoint with an activation on the stack.
|
||||
prepare_step_test();
|
||||
set_bp = true;
|
||||
f();
|
||||
assertEquals(4, step_count);
|
||||
Debug.clearBreakPoint(bp);
|
||||
|
||||
// Set a breakpoint on the first var statement (line 1).
|
||||
set_bp = false;
|
||||
bp = Debug.setBreakPoint(f, 3);
|
||||
|
||||
// Step through the function ensuring that the var statements are hit as well.
|
||||
prepare_step_test();
|
||||
f();
|
||||
assertEquals(4, step_count);
|
||||
|
||||
// Clear the breakpoint and check that no stepping happens.
|
||||
Debug.clearBreakPoint(bp);
|
||||
prepare_step_test();
|
||||
f();
|
||||
assertEquals(0, step_count);
|
||||
|
||||
// Get rid of the debug event listener.
|
||||
Debug.setListener(null);
|
80
test/debugger/debugger/debug-step-4.js
Normal file
80
test/debugger/debugger/debug-step-4.js
Normal file
@ -0,0 +1,80 @@
|
||||
// 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.
|
||||
|
||||
const Debug = new DebugWrapper();
|
||||
Debug.enable();
|
||||
|
||||
// Tests how debugger can step over not necessarily in the top frame.
|
||||
|
||||
// Simple 3 functions, that protocol their execution state in global
|
||||
// variable state.
|
||||
var state;
|
||||
|
||||
function f() {
|
||||
var a = 1978;
|
||||
for (state[2] = 0; state[2] < 3; state[2]++) {
|
||||
void String(a);
|
||||
}
|
||||
}
|
||||
function g() {
|
||||
for (state[1] = 0; state[1] < 3; state[1]++) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
function h() {
|
||||
state = [-1, -1, -1];
|
||||
for (state[0] = 0; state[0] < 3; state[0]++) {
|
||||
g();
|
||||
}
|
||||
}
|
||||
|
||||
function TestCase(expected_final_state) {
|
||||
var listener_exception = null;
|
||||
var state_snapshot;
|
||||
var listener_state;
|
||||
var bp;
|
||||
|
||||
function listener(event, exec_state, event_data, data) {
|
||||
const location = exec_state.frames[0].location
|
||||
print("Here (" + event + "/" + listener_state + "): " +
|
||||
location.lineNumber + ":" + location.columnNumber);
|
||||
try {
|
||||
if (event == Debug.DebugEvent.Break) {
|
||||
if (listener_state == 0) {
|
||||
Debug.clearBreakPoint(bp);
|
||||
Debug.stepOver();
|
||||
listener_state = 1;
|
||||
} else if (listener_state == 1) {
|
||||
state_snapshot = String(state);
|
||||
print("State: " + state_snapshot);
|
||||
Debug.setListener(null);
|
||||
listener_state = 2;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
listener_exception = e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Add the debug event listener.
|
||||
listener_state = 0;
|
||||
Debug.setListener(listener);
|
||||
bp = Debug.setBreakPoint(f, 1);
|
||||
|
||||
h();
|
||||
Debug.setListener(null);
|
||||
if (listener_exception !== null) {
|
||||
print("Exception caught: " + listener_exception);
|
||||
assertUnreachable();
|
||||
}
|
||||
|
||||
assertEquals(expected_final_state, state_snapshot);
|
||||
}
|
||||
|
||||
|
||||
// Warm-up -- make sure all is compiled and ready for breakpoint.
|
||||
h();
|
||||
|
||||
TestCase("0,0,-1");
|
43
test/debugger/debugger/debug-step.js
Normal file
43
test/debugger/debugger/debug-step.js
Normal file
@ -0,0 +1,43 @@
|
||||
// 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.
|
||||
|
||||
Debug = new DebugWrapper();
|
||||
Debug.enable();
|
||||
|
||||
// Simple debug event handler which performs 100 steps and then retrieves
|
||||
// the resulting value of "i" in f().
|
||||
|
||||
function listener(event, exec_state, event_data, data) {
|
||||
if (event == Debug.DebugEvent.Break) {
|
||||
if (step_count > 0) {
|
||||
Debug.stepInto();
|
||||
step_count--;
|
||||
} else {
|
||||
const frameid = exec_state.frames[0].callFrameId;
|
||||
result = Debug.evaluate(frameid, "i").value;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Add the debug event listener.
|
||||
Debug.setListener(listener);
|
||||
|
||||
// Test debug event for break point.
|
||||
function f() {
|
||||
var i; // Line 1.
|
||||
for (i = 0; i < 100; i++) { // Line 2.
|
||||
x = 1; // Line 3.
|
||||
}
|
||||
};
|
||||
|
||||
// Set a breakpoint on the for statement (line 1).
|
||||
Debug.setBreakPoint(f, 1);
|
||||
|
||||
// Check that performing 100 steps will make i 33.
|
||||
let step_count = 100;
|
||||
let result = -1;
|
||||
|
||||
f();
|
||||
|
||||
assertEquals(33, result);
|
@ -15,16 +15,6 @@ function receive(message) {
|
||||
activeWrapper.receiveMessage(message);
|
||||
}
|
||||
|
||||
// TODO(jgruber): Determine which of these are still required and possible.
|
||||
// Debug events which can occur in the V8 JavaScript engine.
|
||||
const DebugEvent = { Break: 1,
|
||||
Exception: 2,
|
||||
NewFunction: 3,
|
||||
BeforeCompile: 4,
|
||||
AfterCompile: 5,
|
||||
CompileError: 6,
|
||||
AsyncTaskEvent: 7 };
|
||||
|
||||
class DebugWrapper {
|
||||
constructor() {
|
||||
// Message dictionary storing {id, message} pairs.
|
||||
@ -35,27 +25,83 @@ class DebugWrapper {
|
||||
this.nextMessageId = 0;
|
||||
|
||||
// The listener method called on certain events.
|
||||
this.listener = () => undefined;
|
||||
this.listener = undefined;
|
||||
|
||||
// TODO(jgruber): Determine which of these are still required and possible.
|
||||
// Debug events which can occur in the V8 JavaScript engine.
|
||||
this.DebugEvent = { Break: 1
|
||||
, Exception: 2
|
||||
, NewFunction: 3
|
||||
, BeforeCompile: 4
|
||||
, AfterCompile: 5
|
||||
, CompileError: 6
|
||||
, AsyncTaskEvent: 7
|
||||
};
|
||||
|
||||
// Register as the active wrapper.
|
||||
assertTrue(activeWrapper === undefined);
|
||||
activeWrapper = this;
|
||||
}
|
||||
|
||||
enable() {
|
||||
const {msgid, msg} = this.createMessage("Debugger.enable");
|
||||
enable() { this.sendMessageForMethodChecked("Debugger.enable"); }
|
||||
disable() { this.sendMessageForMethodChecked("Debugger.disable"); }
|
||||
|
||||
setListener(listener) { this.listener = listener; }
|
||||
|
||||
stepOver() { this.sendMessageForMethodChecked("Debugger.stepOver"); }
|
||||
stepInto() { this.sendMessageForMethodChecked("Debugger.stepInto"); }
|
||||
stepOut() { this.sendMessageForMethodChecked("Debugger.stepOut"); }
|
||||
|
||||
// Returns the resulting breakpoint id.
|
||||
setBreakPoint(func, opt_line, opt_column, opt_condition) {
|
||||
assertTrue(%IsFunction(func));
|
||||
assertFalse(%FunctionIsAPIFunction(func));
|
||||
|
||||
// TODO(jgruber): We handle only script breakpoints for now.
|
||||
// TODO(jgruber): Handle conditions.
|
||||
|
||||
const scriptid = %FunctionGetScriptId(func);
|
||||
assertTrue(scriptid != -1);
|
||||
|
||||
const offset = %FunctionGetScriptSourcePosition(func);
|
||||
const loc =
|
||||
%ScriptLocationFromLine2(scriptid, opt_line, opt_column, offset);
|
||||
|
||||
const {msgid, msg} = this.createMessage(
|
||||
"Debugger.setBreakpoint",
|
||||
{ location : { scriptId : scriptid.toString()
|
||||
, lineNumber : loc.line
|
||||
, columnNumber : loc.column
|
||||
}
|
||||
});
|
||||
this.sendMessage(msg);
|
||||
|
||||
const reply = this.receivedMessages[msgid];
|
||||
const breakid = reply.result.breakpointId;
|
||||
assertTrue(breakid !== undefined);
|
||||
|
||||
return breakid;
|
||||
}
|
||||
|
||||
clearBreakPoint(breakid) {
|
||||
const {msgid, msg} = this.createMessage(
|
||||
"Debugger.removeBreakpoint", { breakpointId : breakid });
|
||||
this.sendMessage(msg);
|
||||
assertTrue(this.receivedMessages[msgid] !== undefined);
|
||||
}
|
||||
|
||||
disable() {
|
||||
const {msgid, msg} = this.createMessage("Debugger.disable");
|
||||
// Returns the serialized result of the given expression. For example:
|
||||
// {"type":"number", "value":33, "description":"33"}.
|
||||
evaluate(frameid, expression) {
|
||||
const {msgid, msg} = this.createMessage(
|
||||
"Debugger.evaluateOnCallFrame",
|
||||
{ callFrameId : frameid
|
||||
, expression : expression
|
||||
});
|
||||
this.sendMessage(msg);
|
||||
assertTrue(this.receivedMessages[msgid] !== undefined);
|
||||
}
|
||||
|
||||
setListener(listener) {
|
||||
this.listener = listener;
|
||||
const reply = this.receivedMessages[msgid];
|
||||
return reply.result.result;
|
||||
}
|
||||
|
||||
// --- Internal methods. -----------------------------------------------------
|
||||
@ -90,23 +136,46 @@ class DebugWrapper {
|
||||
send(message);
|
||||
}
|
||||
|
||||
sendMessageForMethodChecked(method) {
|
||||
const {msgid, msg} = this.createMessage(method);
|
||||
this.sendMessage(msg);
|
||||
assertTrue(this.receivedMessages[msgid] !== undefined);
|
||||
}
|
||||
|
||||
// --- Message handlers. -----------------------------------------------------
|
||||
|
||||
dispatchMessage(message) {
|
||||
const method = message.method;
|
||||
if (method == "Debugger.scriptParsed") {
|
||||
if (method == "Debugger.paused") {
|
||||
this.handleDebuggerPaused(message);
|
||||
} else if (method == "Debugger.scriptParsed") {
|
||||
this.handleDebuggerScriptParsed(message);
|
||||
}
|
||||
}
|
||||
|
||||
handleDebuggerPaused(message) {
|
||||
const params = message.params;
|
||||
|
||||
// TODO(jgruber): Arguments as needed.
|
||||
let execState = { frames: params.callFrames };
|
||||
this.invokeListener(this.DebugEvent.Break, execState);
|
||||
}
|
||||
|
||||
handleDebuggerScriptParsed(message) {
|
||||
const params = message.params;
|
||||
let eventData = { scriptId : params.scriptId
|
||||
, eventType : DebugEvent.AfterCompile
|
||||
, eventType : this.DebugEvent.AfterCompile
|
||||
}
|
||||
|
||||
// TODO(jgruber): Arguments as needed. Still completely missing exec_state,
|
||||
// and eventData used to contain the script mirror instead of its id.
|
||||
this.listener(DebugEvent.AfterCompile, undefined, eventData, undefined);
|
||||
this.invokeListener(this.DebugEvent.AfterCompile, undefined, eventData,
|
||||
undefined);
|
||||
}
|
||||
|
||||
invokeListener(event, exec_state, event_data, data) {
|
||||
if (this.listener) {
|
||||
this.listener(event, exec_state, event_data, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ class DebuggerTestSuite(testsuite.TestSuite):
|
||||
|
||||
def GetFlagsForTestCase(self, testcase, context):
|
||||
source = self.GetSourceForTest(testcase)
|
||||
flags = ["--enable-inspector"] + context.mode_flags
|
||||
flags = ["--enable-inspector", "--allow-natives-syntax"] + context.mode_flags
|
||||
flags_match = re.findall(FLAGS_PATTERN, source)
|
||||
for match in flags_match:
|
||||
flags += match.strip().split()
|
||||
|
20
test/debugger/wrapper/break-on-debugger-stmt.js
Normal file
20
test/debugger/wrapper/break-on-debugger-stmt.js
Normal file
@ -0,0 +1,20 @@
|
||||
// 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.
|
||||
|
||||
function f() { debugger; debugger; }
|
||||
|
||||
const Debug = new DebugWrapper();
|
||||
Debug.enable();
|
||||
|
||||
let breakEventCount = 0;
|
||||
Debug.setListener(function(event, exec_state, event_data, data) {
|
||||
if (event != Debug.DebugEvent.Break) return;
|
||||
breakEventCount++;
|
||||
});
|
||||
|
||||
assertEquals(0, breakEventCount);
|
||||
f();
|
||||
f();
|
||||
f();
|
||||
assertEquals(6, breakEventCount);
|
@ -7,7 +7,7 @@ let compileCount = 0;
|
||||
const Debug = new DebugWrapper();
|
||||
|
||||
Debug.setListener(function(event, exec_state, event_data, data) {
|
||||
if (event != DebugEvent.AfterCompile) return;
|
||||
if (event != Debug.DebugEvent.AfterCompile) return;
|
||||
compileCount++;
|
||||
});
|
||||
|
||||
|
@ -1,89 +0,0 @@
|
||||
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Flags: --expose-debug-as debug
|
||||
|
||||
// This test tests that full code compiled without debug break slots
|
||||
// is recompiled with debug break slots when debugging is started.
|
||||
|
||||
// Get the Debug object exposed from the debug context global object.
|
||||
Debug = debug.Debug
|
||||
|
||||
var bp;
|
||||
var done = false;
|
||||
var step_count = 0;
|
||||
|
||||
// Debug event listener which steps until the global variable done is true.
|
||||
function listener(event, exec_state, event_data, data) {
|
||||
if (event == Debug.DebugEvent.Break) {
|
||||
if (!done) exec_state.prepareStep(Debug.StepAction.StepNext);
|
||||
step_count++;
|
||||
}
|
||||
};
|
||||
|
||||
// Set the global variables state to prpare the stepping test.
|
||||
function prepare_step_test() {
|
||||
done = false;
|
||||
step_count = 0;
|
||||
}
|
||||
|
||||
// Test function to step through.
|
||||
function f() {
|
||||
var i = 1;
|
||||
var j = 2;
|
||||
done = true;
|
||||
};
|
||||
|
||||
prepare_step_test();
|
||||
f();
|
||||
|
||||
// Add the debug event listener.
|
||||
Debug.setListener(listener);
|
||||
|
||||
bp = Debug.setBreakPoint(f, 1);
|
||||
|
||||
prepare_step_test();
|
||||
f();
|
||||
assertEquals(4, step_count);
|
||||
Debug.clearBreakPoint(bp);
|
||||
|
||||
// Set a breakpoint on the first var statement (line 1).
|
||||
bp = Debug.setBreakPoint(f, 1);
|
||||
|
||||
// Step through the function ensuring that the var statements are hit as well.
|
||||
prepare_step_test();
|
||||
f();
|
||||
assertEquals(4, step_count);
|
||||
|
||||
// Clear the breakpoint and check that no stepping happens.
|
||||
Debug.clearBreakPoint(bp);
|
||||
prepare_step_test();
|
||||
f();
|
||||
assertEquals(0, step_count);
|
||||
|
||||
// Get rid of the debug event listener.
|
||||
Debug.setListener(null);
|
@ -1,94 +0,0 @@
|
||||
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Flags: --expose-debug-as debug
|
||||
|
||||
// This test tests that full code compiled without debug break slots
|
||||
// is recompiled with debug break slots when debugging is started.
|
||||
|
||||
// Get the Debug object exposed from the debug context global object.
|
||||
Debug = debug.Debug
|
||||
|
||||
var bp;
|
||||
var done = false;
|
||||
var step_count = 0;
|
||||
var set_bp = false
|
||||
|
||||
// Debug event listener which steps until the global variable done is true.
|
||||
function listener(event, exec_state, event_data, data) {
|
||||
if (event == Debug.DebugEvent.Break) {
|
||||
if (!done) exec_state.prepareStep(Debug.StepAction.StepNext);
|
||||
step_count++;
|
||||
}
|
||||
};
|
||||
|
||||
// Set the global variables state to prpare the stepping test.
|
||||
function prepare_step_test() {
|
||||
done = false;
|
||||
step_count = 0;
|
||||
}
|
||||
|
||||
// Test function to step through.
|
||||
function f() {
|
||||
var a = 0;
|
||||
if (set_bp) { bp = Debug.setBreakPoint(f, 3); }
|
||||
var i = 1;
|
||||
var j = 2;
|
||||
done = true;
|
||||
};
|
||||
|
||||
prepare_step_test();
|
||||
f();
|
||||
|
||||
// Add the debug event listener.
|
||||
Debug.setListener(listener);
|
||||
|
||||
// Make f set a breakpoint with an activation on the stack.
|
||||
prepare_step_test();
|
||||
set_bp = true;
|
||||
f();
|
||||
// TODO(1782): Fix issue to bring back this assert.
|
||||
//assertEquals(4, step_count);
|
||||
Debug.clearBreakPoint(bp);
|
||||
|
||||
// Set a breakpoint on the first var statement (line 1).
|
||||
set_bp = false;
|
||||
bp = Debug.setBreakPoint(f, 3);
|
||||
|
||||
// Step through the function ensuring that the var statements are hit as well.
|
||||
prepare_step_test();
|
||||
f();
|
||||
assertEquals(4, step_count);
|
||||
|
||||
// Clear the breakpoint and check that no stepping happens.
|
||||
Debug.clearBreakPoint(bp);
|
||||
prepare_step_test();
|
||||
f();
|
||||
assertEquals(0, step_count);
|
||||
|
||||
// Get rid of the debug event listener.
|
||||
Debug.setListener(null);
|
@ -1,103 +0,0 @@
|
||||
// Copyright 2008 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Flags: --expose-debug-as debug
|
||||
// Get the Debug object exposed from the debug context global object.
|
||||
Debug = debug.Debug
|
||||
|
||||
// Tests how debugger can step over not necessarily in the top frame.
|
||||
|
||||
// Simple 3 functions, that protocol their execution state in global
|
||||
// variable state.
|
||||
var state;
|
||||
|
||||
function f() {
|
||||
var a = 1978;
|
||||
for (state[2] = 0; state[2] < 3; state[2]++) {
|
||||
void String(a);
|
||||
}
|
||||
}
|
||||
function g() {
|
||||
for (state[1] = 0; state[1] < 3; state[1]++) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
function h() {
|
||||
state = [-1, -1, -1];
|
||||
for (state[0] = 0; state[0] < 3; state[0]++) {
|
||||
g();
|
||||
}
|
||||
}
|
||||
|
||||
function TestCase(expected_final_state) {
|
||||
var listener_exception = null;
|
||||
var state_snapshot;
|
||||
var listener_state;
|
||||
var bp;
|
||||
|
||||
function listener(event, exec_state, event_data, data) {
|
||||
print("Here ("+event+"/"+listener_state+"): " +
|
||||
exec_state.frame(0).sourceLineText());
|
||||
try {
|
||||
if (event == Debug.DebugEvent.Break) {
|
||||
if (listener_state == 0) {
|
||||
Debug.clearBreakPoint(bp);
|
||||
exec_state.prepareStep(Debug.StepAction.StepNext);
|
||||
listener_state = 1;
|
||||
} else if (listener_state == 1) {
|
||||
state_snapshot = String(state);
|
||||
print("State: " + state_snapshot);
|
||||
Debug.setListener(null);
|
||||
listener_state = 2;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
listener_exception = e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Add the debug event listener.
|
||||
listener_state = 0;
|
||||
Debug.setListener(listener);
|
||||
bp = Debug.setBreakPoint(f, 1);
|
||||
|
||||
h();
|
||||
Debug.setListener(null);
|
||||
if (listener_exception !== null) {
|
||||
print("Exception caught: " + listener_exception);
|
||||
assertUnreachable();
|
||||
}
|
||||
|
||||
assertEquals(expected_final_state, state_snapshot);
|
||||
}
|
||||
|
||||
|
||||
// Warm-up -- make sure all is compiled and ready for breakpoint.
|
||||
h();
|
||||
|
||||
TestCase("0,0,-1");
|
@ -1,72 +0,0 @@
|
||||
// Copyright 2008 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Flags: --expose-debug-as debug
|
||||
// Get the Debug object exposed from the debug context global object.
|
||||
Debug = debug.Debug
|
||||
|
||||
// Simple debug event handler which first time hit will perform 1000 steps and
|
||||
// second time hit will evaluate and store the value of "i". If requires that
|
||||
// the global property "state" is initially zero.
|
||||
|
||||
var bp1, bp2;
|
||||
|
||||
function listener(event, exec_state, event_data, data) {
|
||||
if (event == Debug.DebugEvent.Break) {
|
||||
if (step_count > 0) {
|
||||
exec_state.prepareStep(Debug.StepAction.StepIn);
|
||||
step_count--;
|
||||
} else {
|
||||
result = exec_state.frame().evaluate("i").value();
|
||||
// Clear the break point on line 2 if set.
|
||||
if (bp2) {
|
||||
Debug.clearBreakPoint(bp2);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Add the debug event listener.
|
||||
Debug.setListener(listener);
|
||||
|
||||
// Test debug event for break point.
|
||||
function f() {
|
||||
var i; // Line 1.
|
||||
for (i = 0; i < 1000; i++) { // Line 2.
|
||||
x = 1; // Line 3.
|
||||
}
|
||||
};
|
||||
|
||||
// Set a breakpoint on the for statement (line 1).
|
||||
bp1 = Debug.setBreakPoint(f, 1);
|
||||
|
||||
// Check that performing 1000 steps will make i 333.
|
||||
var step_count = 1000;
|
||||
result = -1;
|
||||
f();
|
||||
assertEquals(333, result);
|
||||
Debug.setListener(null);
|
Loading…
Reference in New Issue
Block a user