Commit 83b560b0 authored by jgruber's avatar jgruber Committed by Commit bot

[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}
parent c8d2a8cf
...@@ -1429,6 +1429,7 @@ RUNTIME_FUNCTION(Runtime_DebugGetPrototype) { ...@@ -1429,6 +1429,7 @@ RUNTIME_FUNCTION(Runtime_DebugGetPrototype) {
// Patches script source (should be called upon BeforeCompile event). // Patches script source (should be called upon BeforeCompile event).
// TODO(5530): Remove once uses in debug.js are gone.
RUNTIME_FUNCTION(Runtime_DebugSetScriptSource) { RUNTIME_FUNCTION(Runtime_DebugSetScriptSource) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK(args.length() == 2); DCHECK(args.length() == 2);
...@@ -1569,6 +1570,7 @@ RUNTIME_FUNCTION(Runtime_GetScript) { ...@@ -1569,6 +1570,7 @@ RUNTIME_FUNCTION(Runtime_GetScript) {
return *Script::GetWrapper(found); return *Script::GetWrapper(found);
} }
// TODO(5530): Remove once uses in debug.js are gone.
RUNTIME_FUNCTION(Runtime_ScriptLineCount) { RUNTIME_FUNCTION(Runtime_ScriptLineCount) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK(args.length() == 1); DCHECK(args.length() == 1);
...@@ -1583,6 +1585,7 @@ RUNTIME_FUNCTION(Runtime_ScriptLineCount) { ...@@ -1583,6 +1585,7 @@ RUNTIME_FUNCTION(Runtime_ScriptLineCount) {
return Smi::FromInt(line_ends_array->length()); return Smi::FromInt(line_ends_array->length());
} }
// TODO(5530): Remove once uses in debug.js are gone.
RUNTIME_FUNCTION(Runtime_ScriptLineStartPosition) { RUNTIME_FUNCTION(Runtime_ScriptLineStartPosition) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK(args.length() == 2); DCHECK(args.length() == 2);
...@@ -1609,6 +1612,7 @@ RUNTIME_FUNCTION(Runtime_ScriptLineStartPosition) { ...@@ -1609,6 +1612,7 @@ RUNTIME_FUNCTION(Runtime_ScriptLineStartPosition) {
} }
} }
// TODO(5530): Remove once uses in debug.js are gone.
RUNTIME_FUNCTION(Runtime_ScriptLineEndPosition) { RUNTIME_FUNCTION(Runtime_ScriptLineEndPosition) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK(args.length() == 2); DCHECK(args.length() == 2);
...@@ -1661,62 +1665,49 @@ static Handle<Object> GetJSPositionInfo(Handle<Script> script, int position, ...@@ -1661,62 +1665,49 @@ static Handle<Object> GetJSPositionInfo(Handle<Script> script, int position,
return jsinfo; return jsinfo;
} }
// Get information on a specific source line and column possibly offset by a namespace {
// 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()));
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, // Line and column are possibly undefined and we need to handle these cases,
// additionally subtracting corresponding offsets. // additionally subtracting corresponding offsets.
int32_t line; int32_t line;
if (args[1]->IsNull(isolate) || args[1]->IsUndefined(isolate)) { if (opt_line->IsNull(isolate) || opt_line->IsUndefined(isolate)) {
line = 0; line = 0;
} else { } else {
CHECK(args[1]->IsNumber()); CHECK(opt_line->IsNumber());
line = NumberToInt32(args[1]) - script_handle->line_offset(); line = NumberToInt32(*opt_line) - script->line_offset();
} }
int32_t column; int32_t column;
if (args[2]->IsNull(isolate) || args[2]->IsUndefined(isolate)) { if (opt_column->IsNull(isolate) || opt_column->IsUndefined(isolate)) {
column = 0; column = 0;
} else { } else {
CHECK(args[2]->IsNumber()); CHECK(opt_column->IsNumber());
column = NumberToInt32(args[2]); column = NumberToInt32(*opt_column);
if (line == 0) column -= script_handle->column_offset(); if (line == 0) column -= script->column_offset();
} }
CONVERT_NUMBER_CHECKED(int32_t, offset_position, Int32, args[3]); if (line < 0 || column < 0 || offset < 0) {
return isolate->factory()->null_value();
if (line < 0 || column < 0 || offset_position < 0) {
return isolate->heap()->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(); const int line_count = line_ends_array->length();
int position; int position;
if (line == 0) { if (line == 0) {
position = offset_position + column; position = offset + column;
} else { } else {
Script::PositionInfo info; Script::PositionInfo info;
if (!script_handle->GetPositionInfo(offset_position, &info, if (!script->GetPositionInfo(offset, &info, Script::NO_OFFSET) ||
Script::NO_OFFSET) ||
info.line + line >= line_count) { info.line + line >= line_count) {
return isolate->heap()->null_value(); return isolate->factory()->null_value();
} }
const int offset_line = info.line + line; const int offset_line = info.line + line;
...@@ -1727,10 +1718,65 @@ RUNTIME_FUNCTION(Runtime_ScriptLocationFromLine) { ...@@ -1727,10 +1718,65 @@ RUNTIME_FUNCTION(Runtime_ScriptLocationFromLine) {
position = offset_line_position + column; position = offset_line_position + column;
} }
return *GetJSPositionInfo(script_handle, position, Script::NO_OFFSET, return GetJSPositionInfo(script, position, Script::NO_OFFSET, isolate);
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) { RUNTIME_FUNCTION(Runtime_ScriptPositionInfo) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK(args.length() == 3); DCHECK(args.length() == 3);
...@@ -1748,6 +1794,7 @@ RUNTIME_FUNCTION(Runtime_ScriptPositionInfo) { ...@@ -1748,6 +1794,7 @@ RUNTIME_FUNCTION(Runtime_ScriptPositionInfo) {
// Returns the given line as a string, or null if line is out of bounds. // 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. // 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) { RUNTIME_FUNCTION(Runtime_ScriptSourceLine) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK(args.length() == 2); DCHECK(args.length() == 2);
...@@ -1850,6 +1897,7 @@ RUNTIME_FUNCTION(Runtime_DebugBreakInOptimizedCode) { ...@@ -1850,6 +1897,7 @@ RUNTIME_FUNCTION(Runtime_DebugBreakInOptimizedCode) {
return NULL; return NULL;
} }
// TODO(5530): Remove once uses in debug.js are gone.
RUNTIME_FUNCTION(Runtime_GetWasmFunctionOffsetTable) { RUNTIME_FUNCTION(Runtime_GetWasmFunctionOffsetTable) {
DCHECK(args.length() == 1); DCHECK(args.length() == 1);
HandleScope scope(isolate); HandleScope scope(isolate);
...@@ -1865,6 +1913,7 @@ RUNTIME_FUNCTION(Runtime_GetWasmFunctionOffsetTable) { ...@@ -1865,6 +1913,7 @@ RUNTIME_FUNCTION(Runtime_GetWasmFunctionOffsetTable) {
return *isolate->factory()->NewJSArrayWithElements(elements); return *isolate->factory()->NewJSArrayWithElements(elements);
} }
// TODO(5530): Remove once uses in debug.js are gone.
RUNTIME_FUNCTION(Runtime_DisassembleWasmFunction) { RUNTIME_FUNCTION(Runtime_DisassembleWasmFunction) {
DCHECK(args.length() == 1); DCHECK(args.length() == 1);
HandleScope scope(isolate); HandleScope scope(isolate);
......
...@@ -55,7 +55,7 @@ RUNTIME_FUNCTION(Runtime_FunctionRemovePrototype) { ...@@ -55,7 +55,7 @@ RUNTIME_FUNCTION(Runtime_FunctionRemovePrototype) {
return isolate->heap()->undefined_value(); return isolate->heap()->undefined_value();
} }
// TODO(5530): Remove once uses in debug.js are gone.
RUNTIME_FUNCTION(Runtime_FunctionGetScript) { RUNTIME_FUNCTION(Runtime_FunctionGetScript) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK_EQ(1, args.length()); DCHECK_EQ(1, args.length());
...@@ -71,6 +71,20 @@ RUNTIME_FUNCTION(Runtime_FunctionGetScript) { ...@@ -71,6 +71,20 @@ RUNTIME_FUNCTION(Runtime_FunctionGetScript) {
return isolate->heap()->undefined_value(); 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) { RUNTIME_FUNCTION(Runtime_FunctionGetSourceCode) {
HandleScope scope(isolate); HandleScope scope(isolate);
......
...@@ -187,6 +187,7 @@ namespace internal { ...@@ -187,6 +187,7 @@ namespace internal {
F(ScriptLineStartPosition, 2, 1) \ F(ScriptLineStartPosition, 2, 1) \
F(ScriptLineEndPosition, 2, 1) \ F(ScriptLineEndPosition, 2, 1) \
F(ScriptLocationFromLine, 4, 1) \ F(ScriptLocationFromLine, 4, 1) \
F(ScriptLocationFromLine2, 4, 1) \
F(ScriptPositionInfo, 3, 1) \ F(ScriptPositionInfo, 3, 1) \
F(ScriptSourceLine, 2, 1) \ F(ScriptSourceLine, 2, 1) \
F(DebugPrepareStepInIfStepping, 1, 1) \ F(DebugPrepareStepInIfStepping, 1, 1) \
...@@ -221,6 +222,7 @@ namespace internal { ...@@ -221,6 +222,7 @@ namespace internal {
F(FunctionSetName, 2, 1) \ F(FunctionSetName, 2, 1) \
F(FunctionRemovePrototype, 1, 1) \ F(FunctionRemovePrototype, 1, 1) \
F(FunctionGetScript, 1, 1) \ F(FunctionGetScript, 1, 1) \
F(FunctionGetScriptId, 1, 1) \
F(FunctionGetSourceCode, 1, 1) \ F(FunctionGetSourceCode, 1, 1) \
F(FunctionGetScriptSourcePosition, 1, 1) \ F(FunctionGetScriptSourcePosition, 1, 1) \
F(FunctionGetContextData, 1, 1) \ F(FunctionGetContextData, 1, 1) \
......
// Copyright 2011 the V8 project authors. All rights reserved. // Copyright 2016 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without // Use of this source code is governed by a BSD-style license that can be
// modification, are permitted provided that the following conditions are // found in the LICENSE file.
// 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 const Debug = new DebugWrapper();
Debug.enable();
// This test tests that full code compiled without debug break slots // This test tests that full code compiled without debug break slots
// is recompiled with debug break slots when debugging is started. // 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 bp;
var done = false; var done = false;
var step_count = 0; var step_count = 0;
...@@ -40,7 +15,7 @@ var step_count = 0; ...@@ -40,7 +15,7 @@ var step_count = 0;
// Debug event listener which steps until the global variable done is true. // Debug event listener which steps until the global variable done is true.
function listener(event, exec_state, event_data, data) { function listener(event, exec_state, event_data, data) {
if (event == Debug.DebugEvent.Break) { if (event == Debug.DebugEvent.Break) {
if (!done) exec_state.prepareStep(Debug.StepAction.StepNext); if (!done) Debug.stepOver();
step_count++; step_count++;
} }
}; };
...@@ -84,6 +59,3 @@ Debug.clearBreakPoint(bp); ...@@ -84,6 +59,3 @@ Debug.clearBreakPoint(bp);
prepare_step_test(); prepare_step_test();
f(); f();
assertEquals(0, step_count); assertEquals(0, step_count);
// Get rid of the debug event listener.
Debug.setListener(null);
// Copyright 2011 the V8 project authors. All rights reserved. // Copyright 2016 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without // Use of this source code is governed by a BSD-style license that can be
// modification, are permitted provided that the following conditions are // found in the LICENSE file.
// 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 const Debug = new DebugWrapper();
Debug.enable();
// This test tests that full code compiled without debug break slots // This test tests that full code compiled without debug break slots
// is recompiled with debug break slots when debugging is started. // 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 bp;
var done = false; var done = false;
var step_count = 0; var step_count = 0;
...@@ -41,12 +16,12 @@ var set_bp = false ...@@ -41,12 +16,12 @@ var set_bp = false
// Debug event listener which steps until the global variable done is true. // Debug event listener which steps until the global variable done is true.
function listener(event, exec_state, event_data, data) { function listener(event, exec_state, event_data, data) {
if (event == Debug.DebugEvent.Break) { if (event == Debug.DebugEvent.Break) {
if (!done) exec_state.prepareStep(Debug.StepAction.StepNext); if (!done) Debug.stepOver();
step_count++; step_count++;
} }
}; };
// Set the global variables state to prpare the stepping test. // Set the global variables state to prepare the stepping test.
function prepare_step_test() { function prepare_step_test() {
done = false; done = false;
step_count = 0; step_count = 0;
...@@ -71,8 +46,7 @@ Debug.setListener(listener); ...@@ -71,8 +46,7 @@ Debug.setListener(listener);
prepare_step_test(); prepare_step_test();
set_bp = true; set_bp = true;
f(); f();
// TODO(1782): Fix issue to bring back this assert. assertEquals(4, step_count);
//assertEquals(4, step_count);
Debug.clearBreakPoint(bp); Debug.clearBreakPoint(bp);
// Set a breakpoint on the first var statement (line 1). // Set a breakpoint on the first var statement (line 1).
......
// Copyright 2008 the V8 project authors. All rights reserved. // Copyright 2016 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without // Use of this source code is governed by a BSD-style license that can be
// modification, are permitted provided that the following conditions are // found in the LICENSE file.
// 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 const Debug = new DebugWrapper();
// Get the Debug object exposed from the debug context global object. Debug.enable();
Debug = debug.Debug
// Tests how debugger can step over not necessarily in the top frame. // Tests how debugger can step over not necessarily in the top frame.
...@@ -60,13 +36,14 @@ function TestCase(expected_final_state) { ...@@ -60,13 +36,14 @@ function TestCase(expected_final_state) {
var bp; var bp;
function listener(event, exec_state, event_data, data) { function listener(event, exec_state, event_data, data) {
print("Here ("+event+"/"+listener_state+"): " + const location = exec_state.frames[0].location
exec_state.frame(0).sourceLineText()); print("Here (" + event + "/" + listener_state + "): " +
location.lineNumber + ":" + location.columnNumber);
try { try {
if (event == Debug.DebugEvent.Break) { if (event == Debug.DebugEvent.Break) {
if (listener_state == 0) { if (listener_state == 0) {
Debug.clearBreakPoint(bp); Debug.clearBreakPoint(bp);
exec_state.prepareStep(Debug.StepAction.StepNext); Debug.stepOver();
listener_state = 1; listener_state = 1;
} else if (listener_state == 1) { } else if (listener_state == 1) {
state_snapshot = String(state); state_snapshot = String(state);
......
// 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) { ...@@ -15,16 +15,6 @@ function receive(message) {
activeWrapper.receiveMessage(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 { class DebugWrapper {
constructor() { constructor() {
// Message dictionary storing {id, message} pairs. // Message dictionary storing {id, message} pairs.
...@@ -35,27 +25,83 @@ class DebugWrapper { ...@@ -35,27 +25,83 @@ class DebugWrapper {
this.nextMessageId = 0; this.nextMessageId = 0;
// The listener method called on certain events. // 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. // Register as the active wrapper.
assertTrue(activeWrapper === undefined); assertTrue(activeWrapper === undefined);
activeWrapper = this; activeWrapper = this;
} }
enable() { enable() { this.sendMessageForMethodChecked("Debugger.enable"); }
const {msgid, msg} = this.createMessage("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); this.sendMessage(msg);
assertTrue(this.receivedMessages[msgid] !== undefined);
const reply = this.receivedMessages[msgid];
const breakid = reply.result.breakpointId;
assertTrue(breakid !== undefined);
return breakid;
} }
disable() { clearBreakPoint(breakid) {
const {msgid, msg} = this.createMessage("Debugger.disable"); const {msgid, msg} = this.createMessage(
"Debugger.removeBreakpoint", { breakpointId : breakid });
this.sendMessage(msg); this.sendMessage(msg);
assertTrue(this.receivedMessages[msgid] !== undefined); assertTrue(this.receivedMessages[msgid] !== undefined);
} }
setListener(listener) { // Returns the serialized result of the given expression. For example:
this.listener = listener; // {"type":"number", "value":33, "description":"33"}.
evaluate(frameid, expression) {
const {msgid, msg} = this.createMessage(
"Debugger.evaluateOnCallFrame",
{ callFrameId : frameid
, expression : expression
});
this.sendMessage(msg);
const reply = this.receivedMessages[msgid];
return reply.result.result;
} }
// --- Internal methods. ----------------------------------------------------- // --- Internal methods. -----------------------------------------------------
...@@ -90,23 +136,46 @@ class DebugWrapper { ...@@ -90,23 +136,46 @@ class DebugWrapper {
send(message); send(message);
} }
sendMessageForMethodChecked(method) {
const {msgid, msg} = this.createMessage(method);
this.sendMessage(msg);
assertTrue(this.receivedMessages[msgid] !== undefined);
}
// --- Message handlers. ----------------------------------------------------- // --- Message handlers. -----------------------------------------------------
dispatchMessage(message) { dispatchMessage(message) {
const method = message.method; const method = message.method;
if (method == "Debugger.scriptParsed") { if (method == "Debugger.paused") {
this.handleDebuggerPaused(message);
} else if (method == "Debugger.scriptParsed") {
this.handleDebuggerScriptParsed(message); 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) { handleDebuggerScriptParsed(message) {
const params = message.params; const params = message.params;
let eventData = { scriptId : params.scriptId let eventData = { scriptId : params.scriptId
, eventType : DebugEvent.AfterCompile , eventType : this.DebugEvent.AfterCompile
} }
// TODO(jgruber): Arguments as needed. Still completely missing exec_state, // TODO(jgruber): Arguments as needed. Still completely missing exec_state,
// and eventData used to contain the script mirror instead of its id. // 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): ...@@ -33,7 +33,7 @@ class DebuggerTestSuite(testsuite.TestSuite):
def GetFlagsForTestCase(self, testcase, context): def GetFlagsForTestCase(self, testcase, context):
source = self.GetSourceForTest(testcase) 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) flags_match = re.findall(FLAGS_PATTERN, source)
for match in flags_match: for match in flags_match:
flags += match.strip().split() flags += match.strip().split()
......
// 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; ...@@ -7,7 +7,7 @@ let compileCount = 0;
const Debug = new DebugWrapper(); const Debug = new DebugWrapper();
Debug.setListener(function(event, exec_state, event_data, data) { Debug.setListener(function(event, exec_state, event_data, data) {
if (event != DebugEvent.AfterCompile) return; if (event != Debug.DebugEvent.AfterCompile) return;
compileCount++; compileCount++;
}); });
......
// 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);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment