Debug: parameterize 'step over' action with a frame where the step must be performed
R=yangguo@chromium.org Review URL: https://codereview.chromium.org/23533015 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@16581 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
070d18f9ca
commit
48cae75df8
@ -957,12 +957,17 @@ function ExecutionState(break_id) {
|
||||
this.selected_frame = 0;
|
||||
}
|
||||
|
||||
ExecutionState.prototype.prepareStep = function(opt_action, opt_count) {
|
||||
ExecutionState.prototype.prepareStep = function(opt_action, opt_count,
|
||||
opt_callframe) {
|
||||
var action = Debug.StepAction.StepIn;
|
||||
if (!IS_UNDEFINED(opt_action)) action = %ToNumber(opt_action);
|
||||
var count = opt_count ? %ToNumber(opt_count) : 1;
|
||||
var callFrameId = 0;
|
||||
if (!IS_UNDEFINED(opt_callframe)) {
|
||||
callFrameId = opt_callframe.details_.frameId();
|
||||
}
|
||||
|
||||
return %PrepareStep(this.break_id, action, count);
|
||||
return %PrepareStep(this.break_id, action, count, callFrameId);
|
||||
};
|
||||
|
||||
ExecutionState.prototype.evaluateGlobal = function(source, disable_break,
|
||||
|
11
src/debug.cc
11
src/debug.cc
@ -1017,7 +1017,7 @@ Object* Debug::Break(Arguments args) {
|
||||
// Clear queue
|
||||
thread_local_.queued_step_count_ = 0;
|
||||
|
||||
PrepareStep(StepNext, step_count);
|
||||
PrepareStep(StepNext, step_count, StackFrame::NO_ID);
|
||||
} else {
|
||||
// Notify the debug event listeners.
|
||||
isolate_->debugger()->OnDebugBreak(break_points_hit, false);
|
||||
@ -1055,7 +1055,7 @@ Object* Debug::Break(Arguments args) {
|
||||
ClearStepping();
|
||||
|
||||
// Set up for the remaining steps.
|
||||
PrepareStep(step_action, step_count);
|
||||
PrepareStep(step_action, step_count, StackFrame::NO_ID);
|
||||
}
|
||||
|
||||
if (thread_local_.frame_drop_mode_ == FRAMES_UNTOUCHED) {
|
||||
@ -1376,7 +1376,9 @@ bool Debug::IsBreakOnException(ExceptionBreakType type) {
|
||||
}
|
||||
|
||||
|
||||
void Debug::PrepareStep(StepAction step_action, int step_count) {
|
||||
void Debug::PrepareStep(StepAction step_action,
|
||||
int step_count,
|
||||
StackFrame::Id frame_id) {
|
||||
HandleScope scope(isolate_);
|
||||
|
||||
PrepareForBreakPoints();
|
||||
@ -1402,6 +1404,9 @@ void Debug::PrepareStep(StepAction step_action, int step_count) {
|
||||
// If there is no JavaScript stack don't do anything.
|
||||
return;
|
||||
}
|
||||
if (frame_id != StackFrame::NO_ID) {
|
||||
id = frame_id;
|
||||
}
|
||||
JavaScriptFrameIterator frames_it(isolate_, id);
|
||||
JavaScriptFrame* frame = frames_it.frame();
|
||||
|
||||
|
@ -261,7 +261,9 @@ class Debug {
|
||||
void FloodHandlerWithOneShot();
|
||||
void ChangeBreakOnException(ExceptionBreakType type, bool enable);
|
||||
bool IsBreakOnException(ExceptionBreakType type);
|
||||
void PrepareStep(StepAction step_action, int step_count);
|
||||
void PrepareStep(StepAction step_action,
|
||||
int step_count,
|
||||
StackFrame::Id frame_id);
|
||||
void ClearStepping();
|
||||
void ClearStepOut();
|
||||
bool IsStepping() { return thread_local_.step_count_ > 0; }
|
||||
|
@ -12484,7 +12484,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_IsBreakOnException) {
|
||||
// of frames to step down.
|
||||
RUNTIME_FUNCTION(MaybeObject*, Runtime_PrepareStep) {
|
||||
HandleScope scope(isolate);
|
||||
ASSERT(args.length() == 3);
|
||||
ASSERT(args.length() == 4);
|
||||
// Check arguments.
|
||||
Object* check;
|
||||
{ MaybeObject* maybe_check = Runtime_CheckExecutionState(
|
||||
@ -12495,6 +12495,15 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_PrepareStep) {
|
||||
return isolate->Throw(isolate->heap()->illegal_argument_string());
|
||||
}
|
||||
|
||||
CONVERT_NUMBER_CHECKED(int, wrapped_frame_id, Int32, args[3]);
|
||||
|
||||
StackFrame::Id frame_id;
|
||||
if (wrapped_frame_id == 0) {
|
||||
frame_id = StackFrame::NO_ID;
|
||||
} else {
|
||||
frame_id = UnwrapFrameId(wrapped_frame_id);
|
||||
}
|
||||
|
||||
// Get the step action and check validity.
|
||||
StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
|
||||
if (step_action != StepIn &&
|
||||
@ -12505,6 +12514,11 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_PrepareStep) {
|
||||
return isolate->Throw(isolate->heap()->illegal_argument_string());
|
||||
}
|
||||
|
||||
if (frame_id != StackFrame::NO_ID && step_action != StepNext &&
|
||||
step_action != StepMin && step_action != StepOut) {
|
||||
return isolate->ThrowIllegalOperation();
|
||||
}
|
||||
|
||||
// Get the number of steps.
|
||||
int step_count = NumberToInt32(args[2]);
|
||||
if (step_count < 1) {
|
||||
@ -12516,7 +12530,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_PrepareStep) {
|
||||
|
||||
// Prepare step.
|
||||
isolate->debug()->PrepareStep(static_cast<StepAction>(step_action),
|
||||
step_count);
|
||||
step_count,
|
||||
frame_id);
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
@ -504,7 +504,7 @@ namespace internal {
|
||||
F(ClearBreakPoint, 1, 1) \
|
||||
F(ChangeBreakOnException, 2, 1) \
|
||||
F(IsBreakOnException, 1, 1) \
|
||||
F(PrepareStep, 3, 1) \
|
||||
F(PrepareStep, 4, 1) \
|
||||
F(ClearStepping, 0, 1) \
|
||||
F(DebugEvaluate, 6, 1) \
|
||||
F(DebugEvaluateGlobal, 4, 1) \
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "compilation-cache.h"
|
||||
#include "debug.h"
|
||||
#include "deoptimizer.h"
|
||||
#include "frames.h"
|
||||
#include "platform.h"
|
||||
#include "platform/condition-variable.h"
|
||||
#include "platform/socket.h"
|
||||
@ -60,6 +61,7 @@ using ::v8::internal::Debug;
|
||||
using ::v8::internal::Debugger;
|
||||
using ::v8::internal::CommandMessage;
|
||||
using ::v8::internal::CommandMessageQueue;
|
||||
using ::v8::internal::StackFrame;
|
||||
using ::v8::internal::StepAction;
|
||||
using ::v8::internal::StepIn; // From StepAction enum
|
||||
using ::v8::internal::StepNext; // From StepAction enum
|
||||
@ -390,7 +392,7 @@ static void ChangeBreakOnExceptionFromJS(bool caught, bool uncaught) {
|
||||
// Prepare to step to next break location.
|
||||
static void PrepareStep(StepAction step_action) {
|
||||
v8::internal::Debug* debug = v8::internal::Isolate::Current()->debug();
|
||||
debug->PrepareStep(step_action, 1);
|
||||
debug->PrepareStep(step_action, 1, StackFrame::NO_ID);
|
||||
}
|
||||
|
||||
|
||||
|
132
test/mjsunit/debug-step-4-in-frame.js
Normal file
132
test/mjsunit/debug-step-4-in-frame.js
Normal file
@ -0,0 +1,132 @@
|
||||
// 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] < 5; state[2]++) {
|
||||
void String(a);
|
||||
}
|
||||
}
|
||||
function g() {
|
||||
for (state[1] = 0; state[1] < 5; state[1]++) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
function h() {
|
||||
state = [-1, -1, -1];
|
||||
for (state[0] = 0; state[0] < 5; state[0]++) {
|
||||
g();
|
||||
}
|
||||
}
|
||||
|
||||
function TestCase(frame_index, step_count, expected_final_state) {
|
||||
print("Test case, parameters " + frame_index + "/" + step_count);
|
||||
|
||||
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);
|
||||
var context_frame;
|
||||
if (frame_index !== undefined) {
|
||||
context_frame = exec_state.frame(frame_index);
|
||||
}
|
||||
exec_state.prepareStep(Debug.StepAction.StepNext,
|
||||
step_count, context_frame);
|
||||
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();
|
||||
|
||||
|
||||
// Stepping in the default (top) frame.
|
||||
TestCase(undefined, 0, "0,0,-1");
|
||||
TestCase(undefined, 1, "0,0,-1");
|
||||
TestCase(undefined, 2, "0,0,0");
|
||||
TestCase(undefined, 5, "0,0,1");
|
||||
TestCase(undefined, 8, "0,0,3");
|
||||
|
||||
// Stepping in the frame #0 (should be exactly the same as above).
|
||||
TestCase(0, 0, "0,0,-1");
|
||||
TestCase(0, 1, "0,0,-1");
|
||||
TestCase(0, 2, "0,0,0");
|
||||
TestCase(0, 5, "0,0,1");
|
||||
TestCase(0, 8, "0,0,3");
|
||||
|
||||
// Stepping in the frame #1.
|
||||
TestCase(1, 0, "0,0,5");
|
||||
TestCase(1, 3, "0,1,5");
|
||||
TestCase(1, 8, "0,4,5");
|
||||
|
||||
// Stepping in the frame #2.
|
||||
TestCase(2, 3, "1,5,5");
|
||||
TestCase(2, 8, "4,5,5");
|
Loading…
Reference in New Issue
Block a user