[inspector] implemented blackboxing inside v8
V8 has internal mechanism to ignore steps and breaks inside internal scripts, in this CL it's reused for blackboxing implementation. Advantages: - much faster blackboxing implementation (before we at least wrap and collect current call stack for each step), - get rid of StepFrame action and potential pause in blackboxed code after N StepFrame steps, - simplification of debugger agent logic. Disadvtanges: - currently when user was paused in blackboxed code (e.g. on breakpoint) and then makes step action, debugger ignores blackboxed state of the script and allows to use step actions as usual - this behavior is regressed, we still able to support it on frontend side. Current state and proposed changes for blackboxing: https://docs.google.com/document/d/1hnzaXPAN8_QC5ENxIgxgMNDbXLraM_OXT73rAyijTF8/edit?usp=sharing BUG=v8:5842 R=yangguo@chromium.org,dgozman@chromium.org,alph@chromium.org Review-Url: https://codereview.chromium.org/2633803002 Cr-Commit-Position: refs/heads/master@{#42614}
This commit is contained in:
parent
d90e6e12e6
commit
ac50c79a3e
29
src/api.cc
29
src/api.cc
@ -9035,11 +9035,17 @@ void debug::PrepareStep(Isolate* v8_isolate, StepAction action) {
|
||||
isolate->debug()->PrepareStep(static_cast<i::StepAction>(action));
|
||||
}
|
||||
|
||||
void debug::ClearStepping(Isolate* v8_isolate) {
|
||||
bool debug::HasNonBlackboxedFrameOnStack(Isolate* v8_isolate) {
|
||||
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
|
||||
ENTER_V8(isolate);
|
||||
// Clear all current stepping setup.
|
||||
isolate->debug()->ClearStepping();
|
||||
i::HandleScope scope(isolate);
|
||||
for (i::StackTraceFrameIterator it(isolate); !it.done(); it.Advance()) {
|
||||
if (!it.is_javascript()) continue;
|
||||
i::Handle<i::SharedFunctionInfo> shared(
|
||||
it.javascript_frame()->function()->shared());
|
||||
if (!isolate->debug()->IsBlackboxed(shared)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
v8::Isolate* debug::Script::GetIsolate() const {
|
||||
@ -9314,11 +9320,22 @@ MaybeLocal<UnboundScript> debug::CompileInspectorScript(Isolate* v8_isolate,
|
||||
RETURN_ESCAPED(ToApiHandle<UnboundScript>(result));
|
||||
}
|
||||
|
||||
void debug::SetDebugEventListener(Isolate* v8_isolate,
|
||||
debug::DebugEventListener* listener) {
|
||||
void debug::SetDebugDelegate(Isolate* v8_isolate,
|
||||
debug::DebugDelegate* delegate) {
|
||||
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
|
||||
ENTER_V8(isolate);
|
||||
isolate->debug()->SetDebugEventListener(listener);
|
||||
isolate->debug()->SetDebugDelegate(delegate);
|
||||
}
|
||||
|
||||
void debug::ResetBlackboxedStateCache(Isolate* v8_isolate,
|
||||
v8::Local<debug::Script> script) {
|
||||
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
|
||||
ENTER_V8(isolate);
|
||||
i::DisallowHeapAllocation no_gc;
|
||||
i::SharedFunctionInfo::ScriptIterator iter(Utils::OpenHandle(*script));
|
||||
while (i::SharedFunctionInfo* info = iter.Next()) {
|
||||
info->set_computed_debug_is_blackboxed(false);
|
||||
}
|
||||
}
|
||||
|
||||
Local<String> CpuProfileNode::GetFunctionName() const {
|
||||
|
@ -86,13 +86,13 @@ void ChangeBreakOnException(Isolate* isolate, ExceptionBreakState state);
|
||||
enum StepAction {
|
||||
StepOut = 0, // Step out of the current function.
|
||||
StepNext = 1, // Step to the next statement in the current function.
|
||||
StepIn = 2, // Step into new functions invoked or the next statement
|
||||
StepIn = 2 // Step into new functions invoked or the next statement
|
||||
// in the current function.
|
||||
StepFrame = 3 // Step into a new frame or return to previous frame.
|
||||
};
|
||||
|
||||
void PrepareStep(Isolate* isolate, StepAction action);
|
||||
void ClearStepping(Isolate* isolate);
|
||||
|
||||
bool HasNonBlackboxedFrameOnStack(Isolate* isolate);
|
||||
|
||||
/**
|
||||
* Out-of-memory callback function.
|
||||
@ -147,9 +147,9 @@ void GetLoadedScripts(Isolate* isolate, PersistentValueVector<Script>& scripts);
|
||||
MaybeLocal<UnboundScript> CompileInspectorScript(Isolate* isolate,
|
||||
Local<String> source);
|
||||
|
||||
class DebugEventListener {
|
||||
class DebugDelegate {
|
||||
public:
|
||||
virtual ~DebugEventListener() {}
|
||||
virtual ~DebugDelegate() {}
|
||||
virtual void PromiseEventOccurred(debug::PromiseDebugActionType type,
|
||||
int id) {}
|
||||
virtual void ScriptCompiled(v8::Local<Script> script,
|
||||
@ -161,9 +161,17 @@ class DebugEventListener {
|
||||
v8::Local<v8::Object> exec_state,
|
||||
v8::Local<v8::Value> exception,
|
||||
bool is_promise_rejection, bool is_uncaught) {}
|
||||
virtual bool IsFunctionBlackboxed(v8::Local<debug::Script> script,
|
||||
const debug::Location& start,
|
||||
const debug::Location& end) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
void SetDebugEventListener(Isolate* isolate, DebugEventListener* listener);
|
||||
void SetDebugDelegate(Isolate* isolate, DebugDelegate* listener);
|
||||
|
||||
void ResetBlackboxedStateCache(Isolate* isolate,
|
||||
v8::Local<debug::Script> script);
|
||||
|
||||
} // namespace debug
|
||||
} // namespace v8
|
||||
|
@ -840,7 +840,8 @@ void Debug::FloodWithOneShot(Handle<JSFunction> function,
|
||||
// Debug utility functions are not subject to debugging.
|
||||
if (function->native_context() == *debug_context()) return;
|
||||
|
||||
if (!function->shared()->IsSubjectToDebugging()) {
|
||||
if (!function->shared()->IsSubjectToDebugging() ||
|
||||
IsBlackboxed(function->shared())) {
|
||||
// Builtin functions are not subject to stepping, but need to be
|
||||
// deoptimized, because optimized code does not check for debug
|
||||
// step in at call sites.
|
||||
@ -959,7 +960,8 @@ void Debug::PrepareStepOnThrow() {
|
||||
|
||||
// Find the closest Javascript frame we can flood with one-shots.
|
||||
while (!it.done() &&
|
||||
!it.frame()->function()->shared()->IsSubjectToDebugging()) {
|
||||
(!it.frame()->function()->shared()->IsSubjectToDebugging() ||
|
||||
IsBlackboxed(it.frame()->function()->shared()))) {
|
||||
it.Advance();
|
||||
}
|
||||
|
||||
@ -1019,6 +1021,8 @@ void Debug::PrepareStep(StepAction step_action) {
|
||||
if (location.IsReturn()) step_action = StepOut;
|
||||
// A step-next at a tail call is a step-out.
|
||||
if (location.IsTailCall() && step_action == StepNext) step_action = StepOut;
|
||||
// A step-next in blackboxed function is a step-out.
|
||||
if (step_action == StepNext && IsBlackboxed(shared)) step_action = StepOut;
|
||||
|
||||
thread_local_.last_statement_position_ =
|
||||
summary.abstract_code()->SourceStatementPosition(summary.code_offset());
|
||||
@ -1034,8 +1038,10 @@ void Debug::PrepareStep(StepAction step_action) {
|
||||
// Advance to caller frame.
|
||||
frames_it.Advance();
|
||||
// Skip native and extension functions on the stack.
|
||||
while (!frames_it.done() &&
|
||||
!frames_it.frame()->function()->shared()->IsSubjectToDebugging()) {
|
||||
while (
|
||||
!frames_it.done() &&
|
||||
(!frames_it.frame()->function()->shared()->IsSubjectToDebugging() ||
|
||||
IsBlackboxed(frames_it.frame()->function()->shared()))) {
|
||||
// Builtin functions are not subject to stepping, but need to be
|
||||
// deoptimized to include checks for step-in at call sites.
|
||||
Deoptimizer::DeoptimizeFunction(frames_it.frame()->function());
|
||||
@ -1751,15 +1757,18 @@ void Debug::OnException(Handle<Object> exception, Handle<Object> promise) {
|
||||
}
|
||||
|
||||
{
|
||||
// Check whether the break location is muted.
|
||||
JavaScriptFrameIterator it(isolate_);
|
||||
if (!it.done() && IsMutedAtCurrentLocation(it.frame())) return;
|
||||
// Check whether the top frame is blackboxed or the break location is muted.
|
||||
if (!it.done() && (IsBlackboxed(it.frame()->function()->shared()) ||
|
||||
IsMutedAtCurrentLocation(it.frame()))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DebugScope debug_scope(this);
|
||||
if (debug_scope.failed()) return;
|
||||
|
||||
if (debug_event_listener_) {
|
||||
if (debug_delegate_) {
|
||||
HandleScope scope(isolate_);
|
||||
|
||||
// Create the execution state.
|
||||
@ -1767,7 +1776,7 @@ void Debug::OnException(Handle<Object> exception, Handle<Object> promise) {
|
||||
// Bail out and don't call debugger if exception.
|
||||
if (!MakeExecutionState().ToHandle(&exec_state)) return;
|
||||
|
||||
debug_event_listener_->ExceptionThrown(
|
||||
debug_delegate_->ExceptionThrown(
|
||||
GetDebugEventContext(isolate_),
|
||||
v8::Utils::ToLocal(Handle<JSObject>::cast(exec_state)),
|
||||
v8::Utils::ToLocal(exception), promise->IsJSObject(), uncaught);
|
||||
@ -1797,7 +1806,7 @@ void Debug::OnDebugBreak(Handle<Object> break_points_hit) {
|
||||
PrintBreakLocation();
|
||||
#endif // DEBUG
|
||||
|
||||
if (debug_event_listener_) {
|
||||
if (debug_delegate_) {
|
||||
HandleScope scope(isolate_);
|
||||
|
||||
// Create the execution state.
|
||||
@ -1807,7 +1816,7 @@ void Debug::OnDebugBreak(Handle<Object> break_points_hit) {
|
||||
|
||||
bool previous = in_debug_event_listener_;
|
||||
in_debug_event_listener_ = true;
|
||||
debug_event_listener_->BreakProgramRequested(
|
||||
debug_delegate_->BreakProgramRequested(
|
||||
GetDebugEventContext(isolate_),
|
||||
v8::Utils::ToLocal(Handle<JSObject>::cast(exec_state)),
|
||||
v8::Utils::ToLocal(break_points_hit));
|
||||
@ -1891,11 +1900,46 @@ int Debug::NextAsyncTaskId(Handle<JSObject> promise) {
|
||||
return async_id->value();
|
||||
}
|
||||
|
||||
namespace {
|
||||
debug::Location GetDebugLocation(Handle<Script> script, int source_position) {
|
||||
Script::PositionInfo info;
|
||||
Script::GetPositionInfo(script, source_position, &info, Script::WITH_OFFSET);
|
||||
return debug::Location(info.line, info.column);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool Debug::IsBlackboxed(SharedFunctionInfo* shared) {
|
||||
HandleScope scope(isolate_);
|
||||
Handle<SharedFunctionInfo> shared_function_info(shared);
|
||||
return IsBlackboxed(shared_function_info);
|
||||
}
|
||||
|
||||
bool Debug::IsBlackboxed(Handle<SharedFunctionInfo> shared) {
|
||||
if (!debug_delegate_) return false;
|
||||
if (!shared->computed_debug_is_blackboxed()) {
|
||||
bool is_blackboxed = false;
|
||||
if (shared->script()->IsScript()) {
|
||||
HandleScope handle_scope(isolate_);
|
||||
Handle<Script> script(Script::cast(shared->script()));
|
||||
if (script->type() == i::Script::TYPE_NORMAL) {
|
||||
debug::Location start =
|
||||
GetDebugLocation(script, shared->start_position());
|
||||
debug::Location end = GetDebugLocation(script, shared->end_position());
|
||||
is_blackboxed = debug_delegate_->IsFunctionBlackboxed(
|
||||
ToApiHandle<debug::Script>(script), start, end);
|
||||
}
|
||||
}
|
||||
shared->set_debug_is_blackboxed(is_blackboxed);
|
||||
shared->set_computed_debug_is_blackboxed(true);
|
||||
}
|
||||
return shared->debug_is_blackboxed();
|
||||
}
|
||||
|
||||
void Debug::OnAsyncTaskEvent(debug::PromiseDebugActionType type, int id) {
|
||||
if (in_debug_scope() || ignore_events()) return;
|
||||
|
||||
if (debug_event_listener_) {
|
||||
debug_event_listener_->PromiseEventOccurred(type, id);
|
||||
if (debug_delegate_) {
|
||||
debug_delegate_->PromiseEventOccurred(type, id);
|
||||
if (!non_inspector_listener_exists()) return;
|
||||
}
|
||||
|
||||
@ -1967,9 +2011,9 @@ void Debug::ProcessCompileEvent(v8::DebugEvent event, Handle<Script> script) {
|
||||
DebugScope debug_scope(this);
|
||||
if (debug_scope.failed()) return;
|
||||
|
||||
if (debug_event_listener_) {
|
||||
debug_event_listener_->ScriptCompiled(ToApiHandle<debug::Script>(script),
|
||||
event != v8::AfterCompile);
|
||||
if (debug_delegate_) {
|
||||
debug_delegate_->ScriptCompiled(ToApiHandle<debug::Script>(script),
|
||||
event != v8::AfterCompile);
|
||||
if (!non_inspector_listener_exists()) return;
|
||||
}
|
||||
|
||||
@ -2013,15 +2057,13 @@ void Debug::SetEventListener(Handle<Object> callback,
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
|
||||
void Debug::SetDebugEventListener(debug::DebugEventListener* listener) {
|
||||
debug_event_listener_ = listener;
|
||||
void Debug::SetDebugDelegate(debug::DebugDelegate* delegate) {
|
||||
debug_delegate_ = delegate;
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void Debug::UpdateState() {
|
||||
bool is_active =
|
||||
!event_listener_.is_null() || debug_event_listener_ != nullptr;
|
||||
bool is_active = !event_listener_.is_null() || debug_delegate_ != nullptr;
|
||||
if (is_active || in_debug_scope()) {
|
||||
// Note that the debug context could have already been loaded to
|
||||
// bootstrap test cases.
|
||||
@ -2078,6 +2120,11 @@ void Debug::HandleDebugBreak() {
|
||||
if (fun && fun->IsJSFunction()) {
|
||||
// Don't stop in builtin functions.
|
||||
if (!JSFunction::cast(fun)->shared()->IsSubjectToDebugging()) return;
|
||||
if (isolate_->stack_guard()->CheckDebugBreak() &&
|
||||
IsBlackboxed(JSFunction::cast(fun)->shared())) {
|
||||
Deoptimizer::DeoptimizeFunction(JSFunction::cast(fun));
|
||||
return;
|
||||
}
|
||||
JSGlobalObject* global =
|
||||
JSFunction::cast(fun)->context()->global_object();
|
||||
// Don't stop in debugger functions.
|
||||
|
@ -356,7 +356,9 @@ class Debug {
|
||||
|
||||
int NextAsyncTaskId(Handle<JSObject> promise);
|
||||
|
||||
void SetDebugEventListener(debug::DebugEventListener* listener);
|
||||
bool IsBlackboxed(Handle<SharedFunctionInfo> shared);
|
||||
|
||||
void SetDebugDelegate(debug::DebugDelegate* delegate);
|
||||
|
||||
// Returns whether the operation succeeded. Compilation can only be triggered
|
||||
// if a valid closure is passed as the second argument, otherwise the shared
|
||||
@ -494,6 +496,8 @@ class Debug {
|
||||
return !event_listener_.is_null() && !event_listener_->IsForeign();
|
||||
}
|
||||
|
||||
bool IsBlackboxed(SharedFunctionInfo* shared);
|
||||
|
||||
void OnException(Handle<Object> exception, Handle<Object> promise);
|
||||
|
||||
// Constructors for debug event objects.
|
||||
@ -554,7 +558,7 @@ class Debug {
|
||||
Handle<Object> event_listener_;
|
||||
Handle<Object> event_listener_data_;
|
||||
|
||||
debug::DebugEventListener* debug_event_listener_ = nullptr;
|
||||
debug::DebugDelegate* debug_delegate_ = nullptr;
|
||||
|
||||
// Debugger is active, i.e. there is a debug event listener attached.
|
||||
bool is_active_;
|
||||
|
@ -9,4 +9,5 @@ include_rules = [
|
||||
"+src/tracing",
|
||||
"-include/v8-debug.h",
|
||||
"+src/debug/debug-interface.h",
|
||||
"+src/debug/interface-types.h",
|
||||
]
|
||||
|
@ -54,7 +54,6 @@ static const char skipAllPauses[] = "skipAllPauses";
|
||||
|
||||
} // namespace DebuggerAgentState
|
||||
|
||||
static const int kMaxSkipStepFrameCount = 128;
|
||||
static const char kBacktraceObjectGroup[] = "backtrace";
|
||||
static const char kDebuggerNotEnabled[] = "Debugger agent is not enabled";
|
||||
static const char kDebuggerNotPaused[] =
|
||||
@ -134,13 +133,8 @@ V8DebuggerAgentImpl::V8DebuggerAgentImpl(
|
||||
m_isolate(m_inspector->isolate()),
|
||||
m_breakReason(protocol::Debugger::Paused::ReasonEnum::Other),
|
||||
m_scheduledDebuggerStep(NoStep),
|
||||
m_skipNextDebuggerStepOut(false),
|
||||
m_javaScriptPauseScheduled(false),
|
||||
m_steppingFromFramework(false),
|
||||
m_pausingOnNativeEvent(false),
|
||||
m_skippedStepFrameCount(0),
|
||||
m_recursionLevelForStepOut(0),
|
||||
m_recursionLevelForStepFrame(0),
|
||||
m_skipAllPauses(false) {
|
||||
clearBreakDetails();
|
||||
}
|
||||
@ -190,21 +184,17 @@ Response V8DebuggerAgentImpl::disable() {
|
||||
m_pausedContext.Reset();
|
||||
JavaScriptCallFrames emptyCallFrames;
|
||||
m_pausedCallFrames.swap(emptyCallFrames);
|
||||
m_scripts.clear();
|
||||
m_blackboxedPositions.clear();
|
||||
m_blackboxPattern.reset();
|
||||
resetBlackboxedStateCache();
|
||||
m_scripts.clear();
|
||||
m_breakpointIdToDebuggerBreakpointIds.clear();
|
||||
m_debugger->setAsyncCallStackDepth(this, 0);
|
||||
m_continueToLocationBreakpointId = String16();
|
||||
clearBreakDetails();
|
||||
m_scheduledDebuggerStep = NoStep;
|
||||
m_skipNextDebuggerStepOut = false;
|
||||
m_javaScriptPauseScheduled = false;
|
||||
m_steppingFromFramework = false;
|
||||
m_pausingOnNativeEvent = false;
|
||||
m_skippedStepFrameCount = 0;
|
||||
m_recursionLevelForStepFrame = 0;
|
||||
m_skipAllPauses = false;
|
||||
m_blackboxPattern = nullptr;
|
||||
m_state->remove(DebuggerAgentState::blackboxPattern);
|
||||
m_enabled = false;
|
||||
m_state->setBoolean(DebuggerAgentState::debuggerEnabled, false);
|
||||
@ -434,28 +424,10 @@ Response V8DebuggerAgentImpl::continueToLocation(
|
||||
return resume();
|
||||
}
|
||||
|
||||
bool V8DebuggerAgentImpl::isCurrentCallStackEmptyOrBlackboxed() {
|
||||
DCHECK(enabled());
|
||||
JavaScriptCallFrames callFrames = m_debugger->currentCallFrames();
|
||||
for (size_t index = 0; index < callFrames.size(); ++index) {
|
||||
if (!isCallFrameWithUnknownScriptOrBlackboxed(callFrames[index].get()))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool V8DebuggerAgentImpl::isTopPausedCallFrameBlackboxed() {
|
||||
DCHECK(enabled());
|
||||
JavaScriptCallFrame* frame =
|
||||
m_pausedCallFrames.size() ? m_pausedCallFrames[0].get() : nullptr;
|
||||
return isCallFrameWithUnknownScriptOrBlackboxed(frame);
|
||||
}
|
||||
|
||||
bool V8DebuggerAgentImpl::isCallFrameWithUnknownScriptOrBlackboxed(
|
||||
JavaScriptCallFrame* frame) {
|
||||
if (!frame) return true;
|
||||
ScriptsMap::iterator it =
|
||||
m_scripts.find(String16::fromInteger(frame->sourceID()));
|
||||
bool V8DebuggerAgentImpl::isFunctionBlackboxed(const String16& scriptId,
|
||||
const v8::debug::Location& start,
|
||||
const v8::debug::Location& end) {
|
||||
ScriptsMap::iterator it = m_scripts.find(scriptId);
|
||||
if (it == m_scripts.end()) {
|
||||
// Unknown scripts are blackboxed.
|
||||
return true;
|
||||
@ -466,48 +438,24 @@ bool V8DebuggerAgentImpl::isCallFrameWithUnknownScriptOrBlackboxed(
|
||||
m_blackboxPattern->match(scriptSourceURL) != -1)
|
||||
return true;
|
||||
}
|
||||
auto itBlackboxedPositions =
|
||||
m_blackboxedPositions.find(String16::fromInteger(frame->sourceID()));
|
||||
auto itBlackboxedPositions = m_blackboxedPositions.find(scriptId);
|
||||
if (itBlackboxedPositions == m_blackboxedPositions.end()) return false;
|
||||
|
||||
const std::vector<std::pair<int, int>>& ranges =
|
||||
itBlackboxedPositions->second;
|
||||
auto itRange = std::lower_bound(
|
||||
auto itStartRange = std::lower_bound(
|
||||
ranges.begin(), ranges.end(),
|
||||
std::make_pair(frame->line(), frame->column()), positionComparator);
|
||||
std::make_pair(start.GetLineNumber(), start.GetColumnNumber()),
|
||||
positionComparator);
|
||||
auto itEndRange = std::lower_bound(
|
||||
itStartRange, ranges.end(),
|
||||
std::make_pair(end.GetLineNumber(), end.GetColumnNumber()),
|
||||
positionComparator);
|
||||
// Ranges array contains positions in script where blackbox state is changed.
|
||||
// [(0,0) ... ranges[0]) isn't blackboxed, [ranges[0] ... ranges[1]) is
|
||||
// blackboxed...
|
||||
return std::distance(ranges.begin(), itRange) % 2;
|
||||
}
|
||||
|
||||
V8DebuggerAgentImpl::SkipPauseRequest
|
||||
V8DebuggerAgentImpl::shouldSkipExceptionPause(
|
||||
JavaScriptCallFrame* topCallFrame) {
|
||||
if (m_steppingFromFramework) return RequestNoSkip;
|
||||
if (isCallFrameWithUnknownScriptOrBlackboxed(topCallFrame))
|
||||
return RequestContinue;
|
||||
return RequestNoSkip;
|
||||
}
|
||||
|
||||
V8DebuggerAgentImpl::SkipPauseRequest V8DebuggerAgentImpl::shouldSkipStepPause(
|
||||
JavaScriptCallFrame* topCallFrame) {
|
||||
if (m_steppingFromFramework) return RequestNoSkip;
|
||||
|
||||
if (m_skipNextDebuggerStepOut) {
|
||||
m_skipNextDebuggerStepOut = false;
|
||||
if (m_scheduledDebuggerStep == StepOut) return RequestStepOut;
|
||||
}
|
||||
|
||||
if (!isCallFrameWithUnknownScriptOrBlackboxed(topCallFrame))
|
||||
return RequestNoSkip;
|
||||
|
||||
if (m_skippedStepFrameCount >= kMaxSkipStepFrameCount) return RequestStepOut;
|
||||
|
||||
if (!m_skippedStepFrameCount) m_recursionLevelForStepFrame = 1;
|
||||
|
||||
++m_skippedStepFrameCount;
|
||||
return RequestStepFrame;
|
||||
return itStartRange == itEndRange &&
|
||||
std::distance(ranges.begin(), itStartRange) % 2;
|
||||
}
|
||||
|
||||
std::unique_ptr<protocol::Debugger::Location>
|
||||
@ -641,8 +589,6 @@ void V8DebuggerAgentImpl::schedulePauseOnNextStatement(
|
||||
return;
|
||||
m_breakReason = breakReason;
|
||||
m_breakAuxData = std::move(data);
|
||||
m_pausingOnNativeEvent = true;
|
||||
m_skipNextDebuggerStepOut = false;
|
||||
m_debugger->setPauseOnNextStatement(true);
|
||||
}
|
||||
|
||||
@ -652,16 +598,12 @@ void V8DebuggerAgentImpl::schedulePauseOnNextStatementIfSteppingInto() {
|
||||
m_debugger->isPaused())
|
||||
return;
|
||||
clearBreakDetails();
|
||||
m_pausingOnNativeEvent = false;
|
||||
m_skippedStepFrameCount = 0;
|
||||
m_recursionLevelForStepFrame = 0;
|
||||
m_debugger->setPauseOnNextStatement(true);
|
||||
}
|
||||
|
||||
void V8DebuggerAgentImpl::cancelPauseOnNextStatement() {
|
||||
if (m_javaScriptPauseScheduled || m_debugger->isPaused()) return;
|
||||
clearBreakDetails();
|
||||
m_pausingOnNativeEvent = false;
|
||||
m_debugger->setPauseOnNextStatement(false);
|
||||
}
|
||||
|
||||
@ -672,8 +614,6 @@ Response V8DebuggerAgentImpl::pause() {
|
||||
clearBreakDetails();
|
||||
m_javaScriptPauseScheduled = true;
|
||||
m_scheduledDebuggerStep = NoStep;
|
||||
m_skippedStepFrameCount = 0;
|
||||
m_steppingFromFramework = false;
|
||||
m_debugger->setPauseOnNextStatement(true);
|
||||
return Response::OK();
|
||||
}
|
||||
@ -681,7 +621,6 @@ Response V8DebuggerAgentImpl::pause() {
|
||||
Response V8DebuggerAgentImpl::resume() {
|
||||
if (m_pausedContext.IsEmpty()) return Response::Error(kDebuggerNotPaused);
|
||||
m_scheduledDebuggerStep = NoStep;
|
||||
m_steppingFromFramework = false;
|
||||
m_session->releaseObjectGroup(kBacktraceObjectGroup);
|
||||
m_debugger->continueProgram();
|
||||
return Response::OK();
|
||||
@ -694,7 +633,6 @@ Response V8DebuggerAgentImpl::stepOver() {
|
||||
!m_pausedCallFrames.empty() ? m_pausedCallFrames[0].get() : nullptr;
|
||||
if (frame && frame->isAtReturn()) return stepInto();
|
||||
m_scheduledDebuggerStep = StepOver;
|
||||
m_steppingFromFramework = isTopPausedCallFrameBlackboxed();
|
||||
m_session->releaseObjectGroup(kBacktraceObjectGroup);
|
||||
m_debugger->stepOverStatement();
|
||||
return Response::OK();
|
||||
@ -703,7 +641,6 @@ Response V8DebuggerAgentImpl::stepOver() {
|
||||
Response V8DebuggerAgentImpl::stepInto() {
|
||||
if (m_pausedContext.IsEmpty()) return Response::Error(kDebuggerNotPaused);
|
||||
m_scheduledDebuggerStep = StepInto;
|
||||
m_steppingFromFramework = isTopPausedCallFrameBlackboxed();
|
||||
m_session->releaseObjectGroup(kBacktraceObjectGroup);
|
||||
m_debugger->stepIntoStatement();
|
||||
return Response::OK();
|
||||
@ -712,9 +649,7 @@ Response V8DebuggerAgentImpl::stepInto() {
|
||||
Response V8DebuggerAgentImpl::stepOut() {
|
||||
if (m_pausedContext.IsEmpty()) return Response::Error(kDebuggerNotPaused);
|
||||
m_scheduledDebuggerStep = StepOut;
|
||||
m_skipNextDebuggerStepOut = false;
|
||||
m_recursionLevelForStepOut = 1;
|
||||
m_steppingFromFramework = isTopPausedCallFrameBlackboxed();
|
||||
m_session->releaseObjectGroup(kBacktraceObjectGroup);
|
||||
m_debugger->stepOutOfFunction();
|
||||
return Response::OK();
|
||||
@ -811,6 +746,7 @@ Response V8DebuggerAgentImpl::setBlackboxPatterns(
|
||||
std::unique_ptr<protocol::Array<String16>> patterns) {
|
||||
if (!patterns->length()) {
|
||||
m_blackboxPattern = nullptr;
|
||||
resetBlackboxedStateCache();
|
||||
m_state->remove(DebuggerAgentState::blackboxPattern);
|
||||
return Response::OK();
|
||||
}
|
||||
@ -826,6 +762,7 @@ Response V8DebuggerAgentImpl::setBlackboxPatterns(
|
||||
String16 pattern = patternBuilder.toString();
|
||||
Response response = setBlackboxPattern(pattern);
|
||||
if (!response.isSuccess()) return response;
|
||||
resetBlackboxedStateCache();
|
||||
m_state->setString(DebuggerAgentState::blackboxPattern, pattern);
|
||||
return Response::OK();
|
||||
}
|
||||
@ -839,15 +776,23 @@ Response V8DebuggerAgentImpl::setBlackboxPattern(const String16& pattern) {
|
||||
return Response::OK();
|
||||
}
|
||||
|
||||
void V8DebuggerAgentImpl::resetBlackboxedStateCache() {
|
||||
for (const auto& it : m_scripts) {
|
||||
it.second->resetBlackboxedStateCache();
|
||||
}
|
||||
}
|
||||
|
||||
Response V8DebuggerAgentImpl::setBlackboxedRanges(
|
||||
const String16& scriptId,
|
||||
std::unique_ptr<protocol::Array<protocol::Debugger::ScriptPosition>>
|
||||
inPositions) {
|
||||
if (m_scripts.find(scriptId) == m_scripts.end())
|
||||
auto it = m_scripts.find(scriptId);
|
||||
if (it == m_scripts.end())
|
||||
return Response::Error("No script with passed id.");
|
||||
|
||||
if (!inPositions->length()) {
|
||||
m_blackboxedPositions.erase(scriptId);
|
||||
it->second->resetBlackboxedStateCache();
|
||||
return Response::OK();
|
||||
}
|
||||
|
||||
@ -873,6 +818,7 @@ Response V8DebuggerAgentImpl::setBlackboxedRanges(
|
||||
}
|
||||
|
||||
m_blackboxedPositions[scriptId] = positions;
|
||||
it->second->resetBlackboxedStateCache();
|
||||
return Response::OK();
|
||||
}
|
||||
|
||||
@ -901,27 +847,6 @@ void V8DebuggerAgentImpl::changeJavaScriptRecursionLevel(int step) {
|
||||
// switch stepping to step into a next JS task, as if we exited to a
|
||||
// blackboxed framework.
|
||||
m_scheduledDebuggerStep = StepInto;
|
||||
m_skipNextDebuggerStepOut = false;
|
||||
}
|
||||
}
|
||||
if (m_recursionLevelForStepFrame) {
|
||||
m_recursionLevelForStepFrame += step;
|
||||
if (!m_recursionLevelForStepFrame) {
|
||||
// We have walked through a blackboxed framework and got back to where we
|
||||
// started.
|
||||
// If there was no stepping scheduled, we should cancel the stepping
|
||||
// explicitly,
|
||||
// since there may be a scheduled StepFrame left.
|
||||
// Otherwise, if we were stepping in/over, the StepFrame will stop at the
|
||||
// right location,
|
||||
// whereas if we were stepping out, we should continue doing so after
|
||||
// debugger pauses
|
||||
// from the old StepFrame.
|
||||
m_skippedStepFrameCount = 0;
|
||||
if (m_scheduledDebuggerStep == NoStep)
|
||||
m_debugger->clearStepping();
|
||||
else if (m_scheduledDebuggerStep == StepOut)
|
||||
m_skipNextDebuggerStepOut = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1072,6 +997,11 @@ void V8DebuggerAgentImpl::didParseSource(
|
||||
ScriptsMap::iterator scriptIterator = m_scripts.find(scriptId);
|
||||
DCHECK(scriptIterator != m_scripts.end());
|
||||
V8DebuggerScript* scriptRef = scriptIterator->second.get();
|
||||
// V8 could create functions for parsed scripts before reporting and asks
|
||||
// inspector about blackboxed state, we should reset state each time when we
|
||||
// make any change that change isFunctionBlackboxed output - adding parsed
|
||||
// script is changing.
|
||||
scriptRef->resetBlackboxedStateCache();
|
||||
|
||||
Maybe<String16> sourceMapURLParam = scriptRef->sourceMappingURL();
|
||||
Maybe<protocol::DictionaryValue> executionContextAuxDataParam(
|
||||
@ -1121,35 +1051,19 @@ void V8DebuggerAgentImpl::didParseSource(
|
||||
}
|
||||
}
|
||||
|
||||
V8DebuggerAgentImpl::SkipPauseRequest V8DebuggerAgentImpl::didPause(
|
||||
v8::Local<v8::Context> context, v8::Local<v8::Value> exception,
|
||||
const std::vector<String16>& hitBreakpoints, bool isPromiseRejection,
|
||||
bool isUncaught, bool isOOMBreak) {
|
||||
JavaScriptCallFrames callFrames = m_debugger->currentCallFrames(1);
|
||||
JavaScriptCallFrame* topCallFrame =
|
||||
!callFrames.empty() ? callFrames.begin()->get() : nullptr;
|
||||
|
||||
V8DebuggerAgentImpl::SkipPauseRequest result;
|
||||
if (isOOMBreak)
|
||||
result = RequestNoSkip;
|
||||
else if (m_skipAllPauses)
|
||||
result = RequestContinue;
|
||||
else if (!hitBreakpoints.empty())
|
||||
result = RequestNoSkip; // Don't skip explicit breakpoints even if set in
|
||||
// frameworks.
|
||||
else if (!exception.IsEmpty())
|
||||
result = shouldSkipExceptionPause(topCallFrame);
|
||||
else if (m_scheduledDebuggerStep != NoStep || m_javaScriptPauseScheduled ||
|
||||
m_pausingOnNativeEvent)
|
||||
result = shouldSkipStepPause(topCallFrame);
|
||||
else
|
||||
result = RequestNoSkip;
|
||||
|
||||
m_skipNextDebuggerStepOut = false;
|
||||
if (result != RequestNoSkip) return result;
|
||||
// Skip pauses inside V8 internal scripts and on syntax errors.
|
||||
if (!topCallFrame) return RequestContinue;
|
||||
|
||||
bool V8DebuggerAgentImpl::didPause(v8::Local<v8::Context> context,
|
||||
v8::Local<v8::Value> exception,
|
||||
const std::vector<String16>& hitBreakpoints,
|
||||
bool isPromiseRejection, bool isUncaught,
|
||||
bool isOOMBreak) {
|
||||
if (!isOOMBreak) {
|
||||
if (m_skipAllPauses) return false;
|
||||
JavaScriptCallFrames callFrames = m_debugger->currentCallFrames(1);
|
||||
JavaScriptCallFrame* topCallFrame =
|
||||
!callFrames.empty() ? callFrames.begin()->get() : nullptr;
|
||||
// Skip pauses inside V8 internal scripts and on syntax errors.
|
||||
if (!topCallFrame) return false;
|
||||
}
|
||||
DCHECK(m_pausedContext.IsEmpty());
|
||||
JavaScriptCallFrames frames = m_debugger->currentCallFrames();
|
||||
m_pausedCallFrames.swap(frames);
|
||||
@ -1205,16 +1119,12 @@ V8DebuggerAgentImpl::SkipPauseRequest V8DebuggerAgentImpl::didPause(
|
||||
currentAsyncStackTrace());
|
||||
m_scheduledDebuggerStep = NoStep;
|
||||
m_javaScriptPauseScheduled = false;
|
||||
m_steppingFromFramework = false;
|
||||
m_pausingOnNativeEvent = false;
|
||||
m_skippedStepFrameCount = 0;
|
||||
m_recursionLevelForStepFrame = 0;
|
||||
|
||||
if (!m_continueToLocationBreakpointId.isEmpty()) {
|
||||
m_debugger->removeBreakpoint(m_continueToLocationBreakpointId);
|
||||
m_continueToLocationBreakpointId = "";
|
||||
}
|
||||
return result;
|
||||
return true;
|
||||
}
|
||||
|
||||
void V8DebuggerAgentImpl::didContinue() {
|
||||
@ -1229,14 +1139,11 @@ void V8DebuggerAgentImpl::breakProgram(
|
||||
const String16& breakReason,
|
||||
std::unique_ptr<protocol::DictionaryValue> data) {
|
||||
if (!enabled() || m_skipAllPauses || !m_pausedContext.IsEmpty() ||
|
||||
isCurrentCallStackEmptyOrBlackboxed() ||
|
||||
!m_debugger->breakpointsActivated())
|
||||
!m_debugger->canBreakProgram())
|
||||
return;
|
||||
m_breakReason = breakReason;
|
||||
m_breakAuxData = std::move(data);
|
||||
m_scheduledDebuggerStep = NoStep;
|
||||
m_steppingFromFramework = false;
|
||||
m_pausingOnNativeEvent = false;
|
||||
m_debugger->breakProgram();
|
||||
}
|
||||
|
||||
@ -1274,8 +1181,9 @@ void V8DebuggerAgentImpl::removeBreakpointAt(const String16& scriptId,
|
||||
void V8DebuggerAgentImpl::reset() {
|
||||
if (!enabled()) return;
|
||||
m_scheduledDebuggerStep = NoStep;
|
||||
m_scripts.clear();
|
||||
m_blackboxedPositions.clear();
|
||||
resetBlackboxedStateCache();
|
||||
m_scripts.clear();
|
||||
m_breakpointIdToDebuggerBreakpointIds.clear();
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "src/base/macros.h"
|
||||
#include "src/debug/interface-types.h"
|
||||
#include "src/inspector/java-script-call-frame.h"
|
||||
#include "src/inspector/protocol/Debugger.h"
|
||||
#include "src/inspector/protocol/Forward.h"
|
||||
@ -29,14 +30,6 @@ using protocol::Response;
|
||||
|
||||
class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
|
||||
public:
|
||||
enum SkipPauseRequest {
|
||||
RequestNoSkip,
|
||||
RequestContinue,
|
||||
RequestStepInto,
|
||||
RequestStepOut,
|
||||
RequestStepFrame
|
||||
};
|
||||
|
||||
enum BreakpointSource {
|
||||
UserBreakpointSource,
|
||||
DebugCommandBreakpointSource,
|
||||
@ -134,24 +127,23 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
|
||||
void reset();
|
||||
|
||||
// Interface for V8InspectorImpl
|
||||
SkipPauseRequest didPause(v8::Local<v8::Context>,
|
||||
v8::Local<v8::Value> exception,
|
||||
const std::vector<String16>& hitBreakpoints,
|
||||
bool isPromiseRejection, bool isUncaught,
|
||||
bool isOOMBreak);
|
||||
bool didPause(v8::Local<v8::Context>, v8::Local<v8::Value> exception,
|
||||
const std::vector<String16>& hitBreakpoints,
|
||||
bool isPromiseRejection, bool isUncaught, bool isOOMBreak);
|
||||
void didContinue();
|
||||
void didParseSource(std::unique_ptr<V8DebuggerScript>, bool success);
|
||||
void willExecuteScript(int scriptId);
|
||||
void didExecuteScript();
|
||||
|
||||
bool isFunctionBlackboxed(const String16& scriptId,
|
||||
const v8::debug::Location& start,
|
||||
const v8::debug::Location& end);
|
||||
|
||||
v8::Isolate* isolate() { return m_isolate; }
|
||||
|
||||
private:
|
||||
void enableImpl();
|
||||
|
||||
SkipPauseRequest shouldSkipExceptionPause(JavaScriptCallFrame* topCallFrame);
|
||||
SkipPauseRequest shouldSkipStepPause(JavaScriptCallFrame* topCallFrame);
|
||||
|
||||
void schedulePauseOnNextStatementIfSteppingInto();
|
||||
|
||||
Response currentCallFrames(
|
||||
@ -167,14 +159,11 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
|
||||
void removeBreakpointImpl(const String16& breakpointId);
|
||||
void clearBreakDetails();
|
||||
|
||||
bool isCurrentCallStackEmptyOrBlackboxed();
|
||||
bool isTopPausedCallFrameBlackboxed();
|
||||
bool isCallFrameWithUnknownScriptOrBlackboxed(JavaScriptCallFrame*);
|
||||
|
||||
void internalSetAsyncCallStackDepth(int);
|
||||
void increaseCachedSkipStackGeneration();
|
||||
|
||||
Response setBlackboxPattern(const String16& pattern);
|
||||
void resetBlackboxedStateCache();
|
||||
|
||||
using ScriptsMap =
|
||||
protocol::HashMap<String16, std::unique_ptr<V8DebuggerScript>>;
|
||||
@ -202,14 +191,9 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
|
||||
String16 m_breakReason;
|
||||
std::unique_ptr<protocol::DictionaryValue> m_breakAuxData;
|
||||
DebuggerStep m_scheduledDebuggerStep;
|
||||
bool m_skipNextDebuggerStepOut;
|
||||
bool m_javaScriptPauseScheduled;
|
||||
bool m_steppingFromFramework;
|
||||
bool m_pausingOnNativeEvent;
|
||||
|
||||
int m_skippedStepFrameCount;
|
||||
int m_recursionLevelForStepOut;
|
||||
int m_recursionLevelForStepFrame;
|
||||
bool m_skipAllPauses;
|
||||
|
||||
std::unique_ptr<V8Regex> m_blackboxPattern;
|
||||
|
@ -148,6 +148,11 @@ class ActualScript : public V8DebuggerScript {
|
||||
return script->GetPossibleBreakpoints(start, end, locations);
|
||||
}
|
||||
|
||||
void resetBlackboxedStateCache() override {
|
||||
v8::HandleScope scope(m_isolate);
|
||||
v8::debug::ResetBlackboxedStateCache(m_isolate, m_script.Get(m_isolate));
|
||||
}
|
||||
|
||||
private:
|
||||
String16 GetNameOrSourceUrl(v8::Local<v8::debug::Script> script) {
|
||||
v8::Local<v8::String> name;
|
||||
@ -197,6 +202,8 @@ class WasmVirtualScript : public V8DebuggerScript {
|
||||
return false;
|
||||
}
|
||||
|
||||
void resetBlackboxedStateCache() override {}
|
||||
|
||||
private:
|
||||
static const String16& emptyString() {
|
||||
static const String16 singleEmptyString;
|
||||
|
@ -73,6 +73,7 @@ class V8DebuggerScript {
|
||||
virtual bool getPossibleBreakpoints(
|
||||
const v8::debug::Location& start, const v8::debug::Location& end,
|
||||
std::vector<v8::debug::Location>* locations) = 0;
|
||||
virtual void resetBlackboxedStateCache() = 0;
|
||||
|
||||
protected:
|
||||
V8DebuggerScript(v8::Isolate*, String16 id, String16 url);
|
||||
|
@ -30,6 +30,18 @@ inline v8::Local<v8::Boolean> v8Boolean(bool value, v8::Isolate* isolate) {
|
||||
return value ? v8::True(isolate) : v8::False(isolate);
|
||||
}
|
||||
|
||||
V8DebuggerAgentImpl* agentForScript(V8InspectorImpl* inspector,
|
||||
v8::Local<v8::debug::Script> script) {
|
||||
v8::Local<v8::Value> contextData;
|
||||
if (!script->ContextData().ToLocal(&contextData) || !contextData->IsInt32()) {
|
||||
return nullptr;
|
||||
}
|
||||
int contextId = static_cast<int>(contextData.As<v8::Int32>()->Value());
|
||||
int contextGroupId = inspector->contextGroupId(contextId);
|
||||
if (!contextGroupId) return nullptr;
|
||||
return inspector->enabledDebuggerAgentForGroup(contextGroupId);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
static bool inLiveEditScope = false;
|
||||
@ -68,7 +80,7 @@ void V8Debugger::enable() {
|
||||
if (m_enableCount++) return;
|
||||
DCHECK(!enabled());
|
||||
v8::HandleScope scope(m_isolate);
|
||||
v8::debug::SetDebugEventListener(m_isolate, this);
|
||||
v8::debug::SetDebugDelegate(m_isolate, this);
|
||||
v8::debug::SetOutOfMemoryCallback(m_isolate, &V8Debugger::v8OOMCallback,
|
||||
this);
|
||||
m_debuggerContext.Reset(m_isolate, v8::debug::GetDebugContext(m_isolate));
|
||||
@ -85,7 +97,7 @@ void V8Debugger::disable() {
|
||||
m_debuggerContext.Reset();
|
||||
allAsyncTasksCanceled();
|
||||
m_wasmTranslation.Clear();
|
||||
v8::debug::SetDebugEventListener(m_isolate, nullptr);
|
||||
v8::debug::SetDebugDelegate(m_isolate, nullptr);
|
||||
v8::debug::SetOutOfMemoryCallback(m_isolate, nullptr, nullptr);
|
||||
m_isolate->RestoreOriginalHeapLimit();
|
||||
}
|
||||
@ -243,7 +255,7 @@ void V8Debugger::setPauseOnNextStatement(bool pause) {
|
||||
|
||||
bool V8Debugger::canBreakProgram() {
|
||||
if (!m_breakpointsActivated) return false;
|
||||
return m_isolate->InContext();
|
||||
return v8::debug::HasNonBlackboxedFrameOnStack(m_isolate);
|
||||
}
|
||||
|
||||
void V8Debugger::breakProgram() {
|
||||
@ -296,11 +308,6 @@ void V8Debugger::stepOutOfFunction() {
|
||||
continueProgram();
|
||||
}
|
||||
|
||||
void V8Debugger::clearStepping() {
|
||||
DCHECK(enabled());
|
||||
v8::debug::ClearStepping(m_isolate);
|
||||
}
|
||||
|
||||
Response V8Debugger::setScriptSource(
|
||||
const String16& sourceID, v8::Local<v8::String> newSource, bool dryRun,
|
||||
Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails,
|
||||
@ -480,10 +487,10 @@ void V8Debugger::handleProgramBreak(v8::Local<v8::Context> pausedContext,
|
||||
|
||||
m_pausedContext = pausedContext;
|
||||
m_executionState = executionState;
|
||||
V8DebuggerAgentImpl::SkipPauseRequest result =
|
||||
bool shouldPause =
|
||||
agent->didPause(pausedContext, exception, breakpointIds,
|
||||
isPromiseRejection, isUncaught, m_scheduledOOMBreak);
|
||||
if (result == V8DebuggerAgentImpl::RequestNoSkip) {
|
||||
if (shouldPause) {
|
||||
m_runningNestedMessageLoop = true;
|
||||
int groupId = m_inspector->contextGroupId(pausedContext);
|
||||
DCHECK(groupId);
|
||||
@ -502,14 +509,6 @@ void V8Debugger::handleProgramBreak(v8::Local<v8::Context> pausedContext,
|
||||
m_scheduledOOMBreak = false;
|
||||
m_pausedContext.Clear();
|
||||
m_executionState.Clear();
|
||||
|
||||
if (result == V8DebuggerAgentImpl::RequestStepFrame) {
|
||||
v8::debug::PrepareStep(m_isolate, v8::debug::StepFrame);
|
||||
} else if (result == V8DebuggerAgentImpl::RequestStepInto) {
|
||||
v8::debug::PrepareStep(m_isolate, v8::debug::StepIn);
|
||||
} else if (result == V8DebuggerAgentImpl::RequestStepOut) {
|
||||
v8::debug::PrepareStep(m_isolate, v8::debug::StepOut);
|
||||
}
|
||||
}
|
||||
|
||||
void V8Debugger::v8OOMCallback(void* data) {
|
||||
@ -521,15 +520,7 @@ void V8Debugger::v8OOMCallback(void* data) {
|
||||
|
||||
void V8Debugger::ScriptCompiled(v8::Local<v8::debug::Script> script,
|
||||
bool has_compile_error) {
|
||||
v8::Local<v8::Value> contextData;
|
||||
if (!script->ContextData().ToLocal(&contextData) || !contextData->IsInt32()) {
|
||||
return;
|
||||
}
|
||||
int contextId = static_cast<int>(contextData.As<v8::Int32>()->Value());
|
||||
int contextGroupId = m_inspector->contextGroupId(contextId);
|
||||
if (!contextGroupId) return;
|
||||
V8DebuggerAgentImpl* agent =
|
||||
m_inspector->enabledDebuggerAgentForGroup(contextGroupId);
|
||||
V8DebuggerAgentImpl* agent = agentForScript(m_inspector, script);
|
||||
if (!agent) return;
|
||||
if (script->IsWasm()) {
|
||||
m_wasmTranslation.AddScript(script.As<v8::debug::WasmScript>(), agent);
|
||||
@ -562,6 +553,15 @@ void V8Debugger::ExceptionThrown(v8::Local<v8::Context> pausedContext,
|
||||
v8::Local<v8::Array>(), isPromiseRejection, isUncaught);
|
||||
}
|
||||
|
||||
bool V8Debugger::IsFunctionBlackboxed(v8::Local<v8::debug::Script> script,
|
||||
const v8::debug::Location& start,
|
||||
const v8::debug::Location& end) {
|
||||
V8DebuggerAgentImpl* agent = agentForScript(m_inspector, script);
|
||||
if (!agent) return false;
|
||||
return agent->isFunctionBlackboxed(String16::fromInteger(script->Id()), start,
|
||||
end);
|
||||
}
|
||||
|
||||
void V8Debugger::PromiseEventOccurred(v8::debug::PromiseDebugActionType type,
|
||||
int id) {
|
||||
if (!m_maxAsyncCallStackDepth) return;
|
||||
|
@ -26,7 +26,7 @@ class V8StackTraceImpl;
|
||||
|
||||
using protocol::Response;
|
||||
|
||||
class V8Debugger : public v8::debug::DebugEventListener {
|
||||
class V8Debugger : public v8::debug::DebugDelegate {
|
||||
public:
|
||||
V8Debugger(v8::Isolate*, V8InspectorImpl*);
|
||||
~V8Debugger();
|
||||
@ -48,7 +48,6 @@ class V8Debugger : public v8::debug::DebugEventListener {
|
||||
void stepIntoStatement();
|
||||
void stepOverStatement();
|
||||
void stepOutOfFunction();
|
||||
void clearStepping();
|
||||
|
||||
Response setScriptSource(
|
||||
const String16& sourceID, v8::Local<v8::String> newSource, bool dryRun,
|
||||
@ -145,6 +144,9 @@ class V8Debugger : public v8::debug::DebugEventListener {
|
||||
v8::Local<v8::Object> exec_state,
|
||||
v8::Local<v8::Value> exception,
|
||||
bool is_promise_rejection, bool is_uncaught) override;
|
||||
bool IsFunctionBlackboxed(v8::Local<v8::debug::Script> script,
|
||||
const v8::debug::Location& start,
|
||||
const v8::debug::Location& end) override;
|
||||
|
||||
v8::Isolate* m_isolate;
|
||||
V8InspectorImpl* m_inspector;
|
||||
|
@ -6227,6 +6227,10 @@ BOOL_ACCESSORS(SharedFunctionInfo, debugger_hints, has_no_side_effect,
|
||||
kHasNoSideEffect)
|
||||
BOOL_ACCESSORS(SharedFunctionInfo, debugger_hints, computed_has_no_side_effect,
|
||||
kComputedHasNoSideEffect)
|
||||
BOOL_ACCESSORS(SharedFunctionInfo, debugger_hints, debug_is_blackboxed,
|
||||
kDebugIsBlackboxed)
|
||||
BOOL_ACCESSORS(SharedFunctionInfo, debugger_hints, computed_debug_is_blackboxed,
|
||||
kComputedDebugIsBlackboxed)
|
||||
|
||||
bool Script::HasValidSource() {
|
||||
Object* src = this->source();
|
||||
|
@ -7374,6 +7374,12 @@ class SharedFunctionInfo: public HeapObject {
|
||||
// Indicates that |has_no_side_effect| has been computed and set.
|
||||
DECL_BOOLEAN_ACCESSORS(computed_has_no_side_effect)
|
||||
|
||||
// Indicates that the function should be skipped during stepping.
|
||||
DECL_BOOLEAN_ACCESSORS(debug_is_blackboxed)
|
||||
|
||||
// Indicates that |debug_is_blackboxed| has been computed and set.
|
||||
DECL_BOOLEAN_ACCESSORS(computed_debug_is_blackboxed)
|
||||
|
||||
// The function's name if it is non-empty, otherwise the inferred name.
|
||||
String* DebugName();
|
||||
|
||||
@ -7768,6 +7774,8 @@ class SharedFunctionInfo: public HeapObject {
|
||||
kDeserialized,
|
||||
kHasNoSideEffect,
|
||||
kComputedHasNoSideEffect,
|
||||
kDebugIsBlackboxed,
|
||||
kComputedDebugIsBlackboxed,
|
||||
};
|
||||
|
||||
// kFunctionKind has to be byte-aligned
|
||||
|
56
test/inspector/debugger/framework-break-expected.txt
Normal file
56
test/inspector/debugger/framework-break-expected.txt
Normal file
@ -0,0 +1,56 @@
|
||||
Checks that breaks in framework code correctly processed.
|
||||
|
||||
Running test: testConsoleAssert
|
||||
> all frames in framework:
|
||||
> mixed, top frame in framework:
|
||||
frameworkAssert (framework.js:9:10)
|
||||
(anonymous) (user.js:0:0)
|
||||
|
||||
|
||||
Running test: testCaughtException
|
||||
> all frames in framework:
|
||||
> mixed, top frame in framework:
|
||||
|
||||
Running test: testUncaughtException
|
||||
> all frames in framework:
|
||||
> mixed, top frame in framework:
|
||||
|
||||
Running test: testBreakpoint
|
||||
> all frames in framework:
|
||||
breakpoint (framework.js:24:2)
|
||||
(anonymous) (framework.js:0:0)
|
||||
|
||||
> mixed, top frame in framework:
|
||||
breakpoint (framework.js:24:2)
|
||||
(anonymous) (user.js:0:0)
|
||||
|
||||
|
||||
Running test: testDebuggerStatement
|
||||
> all frames in framework:
|
||||
debuggerStatement (framework.js:28:2)
|
||||
(anonymous) (framework.js:0:0)
|
||||
|
||||
> mixed, top frame in framework:
|
||||
debuggerStatement (framework.js:28:2)
|
||||
(anonymous) (user.js:0:0)
|
||||
|
||||
|
||||
Running test: testSyncDOMBreakpoint
|
||||
> all frames in framework:
|
||||
> mixed, top frame in framework:
|
||||
syncDOMBreakpoint (framework.js:32:2)
|
||||
(anonymous) (user.js:0:0)
|
||||
|
||||
|
||||
Running test: testAsyncDOMBreakpoint
|
||||
> all frames in framework:
|
||||
(anonymous) (user.js:0:0)
|
||||
|
||||
|
||||
Running test: testCaughtSyntaxError
|
||||
> all frames in framework:
|
||||
> mixed, top frame in framework:
|
||||
|
||||
Running test: testCaughtJSONParseError
|
||||
> all frames in framework:
|
||||
> mixed, top frame in framework:
|
189
test/inspector/debugger/framework-break.js
Normal file
189
test/inspector/debugger/framework-break.js
Normal file
@ -0,0 +1,189 @@
|
||||
// Copyright 2017 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.
|
||||
|
||||
print('Checks that breaks in framework code correctly processed.');
|
||||
|
||||
InspectorTest.addScript(
|
||||
`
|
||||
function frameworkAssert() {
|
||||
console.assert(false);
|
||||
}
|
||||
|
||||
function throwCaughtError() {
|
||||
try {
|
||||
throw new Error();
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
function throwUncaughtError() {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
function breakpoint() {
|
||||
return 239;
|
||||
}
|
||||
|
||||
function debuggerStatement() {
|
||||
debugger;
|
||||
}
|
||||
|
||||
function syncDOMBreakpoint() {
|
||||
breakProgram('', '');
|
||||
}
|
||||
|
||||
function asyncDOMBreakpoint() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
function throwCaughtSyntaxError() {
|
||||
try {
|
||||
eval('}');
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
function throwFromJSONParse() {
|
||||
try {
|
||||
JSON.parse('ping');
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
//# sourceURL=framework.js`,
|
||||
7, 26);
|
||||
|
||||
InspectorTest.setupScriptMap();
|
||||
Protocol.Debugger.onPaused(message => {
|
||||
InspectorTest.logCallFrames(message.params.callFrames);
|
||||
InspectorTest.log('');
|
||||
Protocol.Debugger.resume();
|
||||
});
|
||||
|
||||
Protocol.Debugger.enable();
|
||||
Protocol.Debugger.setBlackboxPatterns({patterns: ['framework\.js']});
|
||||
|
||||
InspectorTest.runTestSuite([
|
||||
function testConsoleAssert(next) {
|
||||
Protocol.Debugger.setPauseOnExceptions({state: 'all'})
|
||||
.then(() => InspectorTest.log('> all frames in framework:'))
|
||||
.then(
|
||||
() => Protocol.Runtime.evaluate(
|
||||
{expression: 'frameworkAssert()//# sourceURL=framework.js'}))
|
||||
.then(() => InspectorTest.log('> mixed, top frame in framework:'))
|
||||
.then(
|
||||
() => Protocol.Runtime.evaluate(
|
||||
{expression: 'frameworkAssert()//# sourceURL=user.js'}))
|
||||
.then(() => Protocol.Debugger.setPauseOnExceptions({state: 'none'}))
|
||||
.then(next);
|
||||
},
|
||||
|
||||
function testCaughtException(next) {
|
||||
Protocol.Debugger.setPauseOnExceptions({state: 'all'})
|
||||
.then(() => InspectorTest.log('> all frames in framework:'))
|
||||
.then(
|
||||
() => Protocol.Runtime.evaluate(
|
||||
{expression: 'throwCaughtError()//# sourceURL=framework.js'}))
|
||||
.then(() => InspectorTest.log('> mixed, top frame in framework:'))
|
||||
.then(
|
||||
() => Protocol.Runtime.evaluate(
|
||||
{expression: 'throwCaughtError()//# sourceURL=user.js'}))
|
||||
.then(() => Protocol.Debugger.setPauseOnExceptions({state: 'none'}))
|
||||
.then(next);
|
||||
},
|
||||
|
||||
function testUncaughtException(next) {
|
||||
Protocol.Debugger.setPauseOnExceptions({state: 'all'})
|
||||
.then(() => InspectorTest.log('> all frames in framework:'))
|
||||
.then(
|
||||
() => Protocol.Runtime.evaluate(
|
||||
{expression: 'throwUncaughtError()//# sourceURL=framework.js'}))
|
||||
.then(() => InspectorTest.log('> mixed, top frame in framework:'))
|
||||
.then(
|
||||
() => Protocol.Runtime.evaluate(
|
||||
{expression: 'throwUncaughtError()//# sourceURL=user.js'}))
|
||||
.then(() => Protocol.Debugger.setPauseOnExceptions({state: 'none'}))
|
||||
.then(next);
|
||||
},
|
||||
|
||||
function testBreakpoint(next) {
|
||||
Protocol.Debugger.setBreakpointByUrl({lineNumber: 24, url: 'framework.js'})
|
||||
.then(() => InspectorTest.log('> all frames in framework:'))
|
||||
.then(
|
||||
() => Protocol.Runtime.evaluate(
|
||||
{expression: 'breakpoint()//# sourceURL=framework.js'}))
|
||||
.then(() => InspectorTest.log('> mixed, top frame in framework:'))
|
||||
.then(
|
||||
() => Protocol.Runtime.evaluate(
|
||||
{expression: 'breakpoint()//# sourceURL=user.js'}))
|
||||
.then(next);
|
||||
},
|
||||
|
||||
function testDebuggerStatement(next) {
|
||||
InspectorTest.log('> all frames in framework:');
|
||||
Protocol.Runtime
|
||||
.evaluate({expression: 'debuggerStatement()//# sourceURL=framework.js'})
|
||||
.then(() => InspectorTest.log('> mixed, top frame in framework:'))
|
||||
.then(
|
||||
() => Protocol.Runtime.evaluate(
|
||||
{expression: 'debuggerStatement()//# sourceURL=user.js'}))
|
||||
.then(next);
|
||||
},
|
||||
|
||||
function testSyncDOMBreakpoint(next) {
|
||||
InspectorTest.log('> all frames in framework:');
|
||||
Protocol.Runtime
|
||||
.evaluate({expression: 'syncDOMBreakpoint()//# sourceURL=framework.js'})
|
||||
.then(() => InspectorTest.log('> mixed, top frame in framework:'))
|
||||
.then(
|
||||
() => Protocol.Runtime.evaluate(
|
||||
{expression: 'syncDOMBreakpoint()//# sourceURL=user.js'}))
|
||||
.then(next);
|
||||
},
|
||||
|
||||
function testAsyncDOMBreakpoint(next) {
|
||||
schedulePauseOnNextStatement('', '');
|
||||
InspectorTest.log('> all frames in framework:');
|
||||
Protocol.Runtime
|
||||
.evaluate(
|
||||
{expression: 'asyncDOMBreakpoint()//# sourceURL=framework.js'})
|
||||
.then(() => cancelPauseOnNextStatement())
|
||||
.then(
|
||||
() => Protocol.Runtime.evaluate(
|
||||
{expression: '42//# sourceURL=user.js'}))
|
||||
.then(() => schedulePauseOnNextStatement('', ''))
|
||||
.then(
|
||||
() => Protocol.Runtime.evaluate(
|
||||
{expression: 'asyncDOMBreakpoint()//# sourceURL=user.js'}))
|
||||
.then(next);
|
||||
},
|
||||
|
||||
function testCaughtSyntaxError(next) {
|
||||
Protocol.Debugger.setPauseOnExceptions({state: 'all'})
|
||||
.then(() => InspectorTest.log('> all frames in framework:'))
|
||||
.then(() => Protocol.Runtime.evaluate({
|
||||
expression: 'throwCaughtSyntaxError()//# sourceURL=framework.js'
|
||||
}))
|
||||
.then(() => InspectorTest.log('> mixed, top frame in framework:'))
|
||||
.then(
|
||||
() => Protocol.Runtime.evaluate(
|
||||
{expression: 'throwCaughtSyntaxError()//# sourceURL=user.js'}))
|
||||
.then(() => Protocol.Debugger.setPauseOnExceptions({state: 'none'}))
|
||||
.then(next);
|
||||
},
|
||||
|
||||
function testCaughtJSONParseError(next) {
|
||||
Protocol.Debugger.setPauseOnExceptions({state: 'all'})
|
||||
.then(() => InspectorTest.log('> all frames in framework:'))
|
||||
.then(
|
||||
() => Protocol.Runtime.evaluate(
|
||||
{expression: 'throwFromJSONParse()//# sourceURL=framework.js'}))
|
||||
.then(() => InspectorTest.log('> mixed, top frame in framework:'))
|
||||
.then(
|
||||
() => Protocol.Runtime.evaluate(
|
||||
{expression: 'throwFromJSONParse()//# sourceURL=user.js'}))
|
||||
.then(() => Protocol.Debugger.setPauseOnExceptions({state: 'none'}))
|
||||
.then(next);
|
||||
}
|
||||
]);
|
140
test/inspector/debugger/framework-precise-ranges-expected.txt
Normal file
140
test/inspector/debugger/framework-precise-ranges-expected.txt
Normal file
@ -0,0 +1,140 @@
|
||||
Checks framework debugging with blackboxed ranges.
|
||||
|
||||
Running test: testEntireScript
|
||||
{
|
||||
id : <messageId>
|
||||
result : {
|
||||
}
|
||||
}
|
||||
|
||||
Running test: testFooNotBlackboxed
|
||||
{
|
||||
id : <messageId>
|
||||
result : {
|
||||
}
|
||||
}
|
||||
foo (test.js:8:12)
|
||||
testFunction (test.js:15:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
foo (test.js:9:2)
|
||||
testFunction (test.js:15:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
foo (test.js:10:0)
|
||||
testFunction (test.js:15:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
|
||||
Running test: testFooBlackboxed
|
||||
{
|
||||
id : <messageId>
|
||||
result : {
|
||||
}
|
||||
}
|
||||
testFunction (test.js:14:21)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
testFunction (test.js:15:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
boo (test.js:12:2)
|
||||
foo (test.js:9:9)
|
||||
testFunction (test.js:15:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
boo (test.js:13:0)
|
||||
foo (test.js:9:9)
|
||||
testFunction (test.js:15:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
testFunction (test.js:16:0)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
|
||||
Running test: testBooPartiallyBlackboxed1
|
||||
{
|
||||
id : <messageId>
|
||||
result : {
|
||||
}
|
||||
}
|
||||
foo (test.js:8:12)
|
||||
testFunction (test.js:15:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
foo (test.js:9:2)
|
||||
testFunction (test.js:15:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
boo (test.js:12:2)
|
||||
foo (test.js:9:9)
|
||||
testFunction (test.js:15:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
boo (test.js:13:0)
|
||||
foo (test.js:9:9)
|
||||
testFunction (test.js:15:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
foo (test.js:10:0)
|
||||
testFunction (test.js:15:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
|
||||
Running test: testBooPartiallyBlackboxed2
|
||||
{
|
||||
id : <messageId>
|
||||
result : {
|
||||
}
|
||||
}
|
||||
foo (test.js:8:12)
|
||||
testFunction (test.js:15:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
foo (test.js:9:2)
|
||||
testFunction (test.js:15:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
boo (test.js:12:2)
|
||||
foo (test.js:9:9)
|
||||
testFunction (test.js:15:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
boo (test.js:13:0)
|
||||
foo (test.js:9:9)
|
||||
testFunction (test.js:15:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
foo (test.js:10:0)
|
||||
testFunction (test.js:15:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
|
||||
Running test: testBooPartiallyBlackboxed3
|
||||
{
|
||||
id : <messageId>
|
||||
result : {
|
||||
}
|
||||
}
|
||||
foo (test.js:8:12)
|
||||
testFunction (test.js:15:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
foo (test.js:9:2)
|
||||
testFunction (test.js:15:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
boo (test.js:12:2)
|
||||
foo (test.js:9:9)
|
||||
testFunction (test.js:15:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
boo (test.js:13:0)
|
||||
foo (test.js:9:9)
|
||||
testFunction (test.js:15:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
foo (test.js:10:0)
|
||||
testFunction (test.js:15:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
78
test/inspector/debugger/framework-precise-ranges.js
Normal file
78
test/inspector/debugger/framework-precise-ranges.js
Normal file
@ -0,0 +1,78 @@
|
||||
// Copyright 2017 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.
|
||||
|
||||
print('Checks framework debugging with blackboxed ranges.');
|
||||
|
||||
InspectorTest.addScript(
|
||||
`
|
||||
function foo() {
|
||||
return boo();
|
||||
}
|
||||
function boo() {
|
||||
return 42;
|
||||
}
|
||||
function testFunction() {
|
||||
foo();
|
||||
}
|
||||
//# sourceURL=test.js`,
|
||||
7, 26);
|
||||
|
||||
InspectorTest.setupScriptMap();
|
||||
Protocol.Debugger.onPaused(message => {
|
||||
InspectorTest.logCallFrames(message.params.callFrames);
|
||||
InspectorTest.log('');
|
||||
Protocol.Debugger.stepInto();
|
||||
});
|
||||
var scriptId;
|
||||
Protocol.Debugger.onScriptParsed(message => {
|
||||
if (message.params.url === 'test.js') {
|
||||
scriptId = message.params.scriptId;
|
||||
}
|
||||
});
|
||||
|
||||
Protocol.Debugger.enable()
|
||||
.then(() => Protocol.Debugger.setBlackboxPatterns({patterns: ['expr\.js']}))
|
||||
.then(() => InspectorTest.runTestSuite(testSuite));
|
||||
|
||||
var testSuite = [
|
||||
function testEntireScript(next) {
|
||||
testPositions([position(0, 0)]).then(next);
|
||||
},
|
||||
function testFooNotBlackboxed(next) {
|
||||
testPositions([position(11, 0)]).then(next);
|
||||
},
|
||||
function testFooBlackboxed(next) {
|
||||
testPositions([position(8, 0), position(10, 3)]).then(next);
|
||||
},
|
||||
function testBooPartiallyBlackboxed1(next) {
|
||||
// first line is not blackboxed, second and third - blackboxed.
|
||||
testPositions([position(12, 0)]).then(next);
|
||||
},
|
||||
function testBooPartiallyBlackboxed2(next) {
|
||||
// first line is blackboxed, second - not, third - blackboxed.
|
||||
testPositions([
|
||||
position(11, 0), position(12, 0), position(13, 0)
|
||||
]).then(next);
|
||||
},
|
||||
function testBooPartiallyBlackboxed3(next) {
|
||||
// first line is blackboxed, second and third - not.
|
||||
testPositions([
|
||||
position(11, 0), position(12, 0), position(14, 0)
|
||||
]).then(next);
|
||||
}
|
||||
];
|
||||
|
||||
function testPositions(positions) {
|
||||
schedulePauseOnNextStatement('', '');
|
||||
return Protocol.Debugger
|
||||
.setBlackboxedRanges({scriptId: scriptId, positions: positions})
|
||||
.then(InspectorTest.logMessage)
|
||||
.then(
|
||||
() => Protocol.Runtime.evaluate(
|
||||
{expression: 'testFunction()//# sourceURL=expr.js'}));
|
||||
}
|
||||
|
||||
function position(line, column) {
|
||||
return {lineNumber: line, columnNumber: column};
|
||||
}
|
100
test/inspector/debugger/framework-stepping-expected.txt
Normal file
100
test/inspector/debugger/framework-stepping-expected.txt
Normal file
@ -0,0 +1,100 @@
|
||||
Checks stepping with blackboxed frames on stack
|
||||
|
||||
Running test: testStepIntoFromUser
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
Executing stepInto...
|
||||
Executing stepInto...
|
||||
userFoo (user.js:23:2)
|
||||
frameworkCall (framework.js:10:23)
|
||||
testStepFromUser (user.js:31:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
Executing stepInto...
|
||||
Executing stepInto...
|
||||
userBoo (user.js:27:2)
|
||||
frameworkCall (framework.js:10:23)
|
||||
testStepFromUser (user.js:31:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
Executing stepInto...
|
||||
Executing stepInto...
|
||||
testStepFromUser (user.js:32:0)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
Executing resume...
|
||||
|
||||
Running test: testStepOverFromUser
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
Executing stepInto...
|
||||
Executing stepInto...
|
||||
userFoo (user.js:23:2)
|
||||
frameworkCall (framework.js:10:23)
|
||||
testStepFromUser (user.js:31:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
Executing stepOver...
|
||||
Executing stepOver...
|
||||
userBoo (user.js:27:2)
|
||||
frameworkCall (framework.js:10:23)
|
||||
testStepFromUser (user.js:31:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
Executing stepOver...
|
||||
Executing stepOver...
|
||||
testStepFromUser (user.js:32:0)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
Executing resume...
|
||||
|
||||
Running test: testStepOutFromUser
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
Executing stepInto...
|
||||
Executing stepInto...
|
||||
userFoo (user.js:23:2)
|
||||
frameworkCall (framework.js:10:23)
|
||||
testStepFromUser (user.js:31:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
Executing stepOut...
|
||||
testStepFromUser (user.js:32:0)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
Executing resume...
|
||||
|
||||
Running test: testStepIntoFromFramework
|
||||
frameworkBreakAndCall (framework.js:14:2)
|
||||
testStepFromFramework (user.js:35:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
Executing stepInto...
|
||||
userFoo (user.js:23:2)
|
||||
frameworkBreakAndCall (framework.js:15:23)
|
||||
testStepFromFramework (user.js:35:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
Executing resume...
|
||||
|
||||
Running test: testStepOverFromFramework
|
||||
frameworkBreakAndCall (framework.js:14:2)
|
||||
testStepFromFramework (user.js:35:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
Executing stepOver...
|
||||
testStepFromFramework (user.js:36:0)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
Executing resume...
|
||||
|
||||
Running test: testStepOutFromFramework
|
||||
frameworkBreakAndCall (framework.js:14:2)
|
||||
testStepFromFramework (user.js:35:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
Executing stepOut...
|
||||
testStepFromFramework (user.js:36:0)
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
Executing resume...
|
113
test/inspector/debugger/framework-stepping.js
Normal file
113
test/inspector/debugger/framework-stepping.js
Normal file
@ -0,0 +1,113 @@
|
||||
// Copyright 2017 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.
|
||||
|
||||
print('Checks stepping with blackboxed frames on stack');
|
||||
|
||||
InspectorTest.addScript(
|
||||
`
|
||||
function frameworkCall(funcs) {
|
||||
for (var f of funcs) f();
|
||||
}
|
||||
|
||||
function frameworkBreakAndCall(funcs) {
|
||||
debugger;
|
||||
for (var f of funcs) f();
|
||||
}
|
||||
//# sourceURL=framework.js`,
|
||||
8, 4);
|
||||
|
||||
InspectorTest.addScript(
|
||||
`
|
||||
function userFoo() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
function userBoo() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
function testStepFromUser() {
|
||||
frameworkCall([userFoo, userBoo])
|
||||
}
|
||||
|
||||
function testStepFromFramework() {
|
||||
frameworkBreakAndCall([userFoo, userBoo]);
|
||||
}
|
||||
//# sourceURL=user.js`,
|
||||
21, 4);
|
||||
|
||||
InspectorTest.setupScriptMap();
|
||||
|
||||
Protocol.Debugger.enable()
|
||||
.then(
|
||||
() => Protocol.Debugger.setBlackboxPatterns(
|
||||
{patterns: ['framework\.js']}))
|
||||
.then(() => InspectorTest.runTestSuite(testSuite));
|
||||
|
||||
var testSuite = [
|
||||
function testStepIntoFromUser(next) {
|
||||
schedulePauseOnNextStatement('', '');
|
||||
test('testStepFromUser()', [
|
||||
'print', // before testStepFromUser call
|
||||
'stepInto', 'stepInto', 'print', // userFoo
|
||||
'stepInto', 'stepInto', 'print', // userBoo
|
||||
'stepInto', 'stepInto', 'print' // testStepFromUser
|
||||
]).then(next);
|
||||
},
|
||||
|
||||
function testStepOverFromUser(next) {
|
||||
schedulePauseOnNextStatement('', '');
|
||||
test('testStepFromUser()', [
|
||||
'print', // before testStepFromUser call
|
||||
'stepInto', 'stepInto', 'print', // userFoo
|
||||
'stepOver', 'stepOver', 'print', // userBoo
|
||||
'stepOver', 'stepOver', 'print' // testStepFromUser
|
||||
]).then(next);
|
||||
},
|
||||
|
||||
function testStepOutFromUser(next) {
|
||||
schedulePauseOnNextStatement('', '');
|
||||
test('testStepFromUser()', [
|
||||
'print', // before testStepFromUser call
|
||||
'stepInto', 'stepInto', 'print', // userFoo
|
||||
'stepOut', 'print' // testStepFromUser
|
||||
]).then(next);
|
||||
},
|
||||
|
||||
function testStepIntoFromFramework(next) {
|
||||
test('testStepFromFramework()', [
|
||||
'print', // frameworkBreakAndCall
|
||||
'stepInto', 'print', // userFoo
|
||||
]).then(next);
|
||||
},
|
||||
|
||||
function testStepOverFromFramework(next) {
|
||||
test('testStepFromFramework()', [
|
||||
'print', // frameworkBreakAndCall
|
||||
'stepOver', 'print', // testStepFromFramework
|
||||
]).then(next);
|
||||
},
|
||||
|
||||
function testStepOutFromFramework(next) {
|
||||
test('testStepFromFramework()', [
|
||||
'print', // frameworkBreakAndCall
|
||||
'stepOut', 'print', // testStepFromFramework
|
||||
]).then(next);
|
||||
}
|
||||
];
|
||||
|
||||
function test(entryExpression, actions) {
|
||||
Protocol.Debugger.onPaused(message => {
|
||||
var action = actions.shift() || 'resume';
|
||||
if (action === 'print') {
|
||||
InspectorTest.logCallFrames(message.params.callFrames);
|
||||
InspectorTest.log('');
|
||||
action = actions.shift() || 'resume';
|
||||
}
|
||||
if (action) InspectorTest.log(`Executing ${action}...`);
|
||||
Protocol.Debugger[action]();
|
||||
});
|
||||
return Protocol.Runtime.evaluate(
|
||||
{expression: entryExpression + '//# sourceURL=expr.js'});
|
||||
}
|
@ -90,10 +90,15 @@ function setIncorrectRanges(scriptId, response)
|
||||
function setMixedSourceRanges(scriptId)
|
||||
{
|
||||
Protocol.Debugger.onPaused(runAction);
|
||||
Protocol.Debugger.setBlackboxedRanges({
|
||||
scriptId: scriptId,
|
||||
positions: [ { lineNumber: 8, columnNumber: 0 }, { lineNumber: 15, columnNumber: 0 } ] // blackbox ranges for mixed.js
|
||||
}).then(runAction);
|
||||
Protocol.Debugger
|
||||
.setBlackboxedRanges({
|
||||
scriptId: scriptId,
|
||||
positions: [
|
||||
{lineNumber: 6, columnNumber: 0},
|
||||
{lineNumber: 14, columnNumber: 0}
|
||||
] // blackbox ranges for mixed.js
|
||||
})
|
||||
.then(runAction);
|
||||
}
|
||||
|
||||
var actions = [ "stepOut", "print", "stepOut", "print", "stepOut", "print",
|
||||
|
Loading…
Reference in New Issue
Block a user