[inspector] move stack trace and scope inspection to native

This CL moves us much closer to the point where we can remove debugger-script.js and usage of debugger context from inspector.
There are three main parts left:
- managing breakpoints,
- inspecting stack and scopes (this CL),
- LiveEdit.

In this CL I moved all stack/scope inspection to native. As side effect running debugger and inspector tests are 10-20% faster (it's significant since not all of tests requesting break).

R=yangguo@chromium.org,jgruber@chromium.org

Bug: chromium:652939
Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel;master.tryserver.chromium.linux:linux_chromium_rel_ng
Change-Id: I409396a687e18e9c0554c0c9c35b6e1064627be8
Reviewed-on: https://chromium-review.googlesource.com/580645
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: Yang Guo <yangguo@chromium.org>
Commit-Queue: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#46947}
This commit is contained in:
Alexey Kozyatinskiy 2017-07-26 20:58:01 -07:00 committed by Commit Bot
parent b4f1e24078
commit c5e9416b1d
24 changed files with 888 additions and 1198 deletions

View File

@ -1475,8 +1475,12 @@ v8_source_set("v8_base") {
"src/debug/debug-frames.cc",
"src/debug/debug-frames.h",
"src/debug/debug-interface.h",
"src/debug/debug-scope-iterator.cc",
"src/debug/debug-scope-iterator.h",
"src/debug/debug-scopes.cc",
"src/debug/debug-scopes.h",
"src/debug/debug-stack-trace-iterator.cc",
"src/debug/debug-stack-trace-iterator.h",
"src/debug/debug.cc",
"src/debug/debug.h",
"src/debug/interface-types.h",

View File

@ -9646,10 +9646,6 @@ int debug::Script::GetSourceOffset(const debug::Location& location) const {
v8::debug::Location debug::Script::GetSourceLocation(int offset) const {
i::Handle<i::Script> script = Utils::OpenHandle(this);
if (script->type() == i::Script::TYPE_WASM) {
// TODO(clemensh): Return the proper thing for wasm.
return v8::debug::Location();
}
i::Script::PositionInfo info;
i::Script::GetPositionInfo(script, offset, &info, i::Script::WITH_OFFSET);
return debug::Location(info.line, info.column);
@ -9728,7 +9724,8 @@ int debug::Location::GetColumnNumber() const {
}
bool debug::Location::IsEmpty() const {
return line_number_ == -1 && column_number_ == -1;
return line_number_ == v8::Function::kLineOffsetNotFound &&
column_number_ == v8::Function::kLineOffsetNotFound;
}
void debug::GetLoadedScripts(v8::Isolate* v8_isolate,

View File

@ -5,8 +5,6 @@
#ifndef V8_DEBUG_DEBUG_INTERFACE_H_
#define V8_DEBUG_DEBUG_INTERFACE_H_
#include <functional>
#include "include/v8-debug.h"
#include "include/v8-util.h"
#include "include/v8.h"
@ -319,6 +317,71 @@ class V8_EXPORT_PRIVATE Coverage {
explicit Coverage(i::Coverage* coverage) : coverage_(coverage) {}
i::Coverage* coverage_;
};
class ScopeIterator {
public:
static std::unique_ptr<ScopeIterator> CreateForFunction(
v8::Isolate* isolate, v8::Local<v8::Function> func);
static std::unique_ptr<ScopeIterator> CreateForGeneratorObject(
v8::Isolate* isolate, v8::Local<v8::Object> generator);
ScopeIterator() = default;
virtual ~ScopeIterator() = default;
enum ScopeType {
ScopeTypeGlobal = 0,
ScopeTypeLocal,
ScopeTypeWith,
ScopeTypeClosure,
ScopeTypeCatch,
ScopeTypeBlock,
ScopeTypeScript,
ScopeTypeEval,
ScopeTypeModule
};
virtual bool Done() = 0;
virtual void Advance() = 0;
virtual ScopeType GetType() = 0;
virtual v8::Local<v8::Object> GetObject() = 0;
virtual v8::Local<v8::Function> GetFunction() = 0;
virtual debug::Location GetStartLocation() = 0;
virtual debug::Location GetEndLocation() = 0;
virtual bool SetVariableValue(v8::Local<v8::String> name,
v8::Local<v8::Value> value) = 0;
private:
DISALLOW_COPY_AND_ASSIGN(ScopeIterator);
};
class StackTraceIterator {
public:
static std::unique_ptr<StackTraceIterator> Create(Isolate* isolate,
int index = 0);
StackTraceIterator() = default;
virtual ~StackTraceIterator() = default;
virtual bool Done() const = 0;
virtual void Advance() = 0;
virtual int GetContextId() const = 0;
virtual v8::Local<v8::Value> GetReceiver() const = 0;
virtual v8::Local<v8::Value> GetReturnValue() const = 0;
virtual v8::Local<v8::String> GetFunctionName() const = 0;
virtual v8::Local<v8::debug::Script> GetScript() const = 0;
virtual debug::Location GetSourceLocation() const = 0;
virtual v8::Local<v8::Function> GetFunction() const = 0;
virtual std::unique_ptr<ScopeIterator> GetScopeIterator() const = 0;
virtual bool Restart() = 0;
virtual v8::MaybeLocal<v8::Value> Evaluate(v8::Local<v8::String> source,
bool throw_on_side_effect) = 0;
private:
DISALLOW_COPY_AND_ASSIGN(StackTraceIterator);
};
} // namespace debug
} // namespace v8

View File

@ -0,0 +1,203 @@
// 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.
#include "src/debug/debug-scope-iterator.h"
#include "src/debug/debug.h"
#include "src/debug/liveedit.h"
#include "src/frames-inl.h"
#include "src/isolate.h"
#include "src/wasm/wasm-objects.h"
namespace v8 {
std::unique_ptr<debug::ScopeIterator> debug::ScopeIterator::CreateForFunction(
v8::Isolate* v8_isolate, v8::Local<v8::Function> v8_func) {
return std::unique_ptr<debug::ScopeIterator>(new internal::DebugScopeIterator(
reinterpret_cast<internal::Isolate*>(v8_isolate),
internal::Handle<internal::JSFunction>::cast(
Utils::OpenHandle(*v8_func))));
}
std::unique_ptr<debug::ScopeIterator>
debug::ScopeIterator::CreateForGeneratorObject(
v8::Isolate* v8_isolate, v8::Local<v8::Object> v8_generator) {
internal::Handle<internal::Object> generator =
Utils::OpenHandle(*v8_generator);
DCHECK(generator->IsJSGeneratorObject());
return std::unique_ptr<debug::ScopeIterator>(new internal::DebugScopeIterator(
reinterpret_cast<internal::Isolate*>(v8_isolate),
internal::Handle<internal::JSGeneratorObject>::cast(generator)));
}
namespace internal {
DebugScopeIterator::DebugScopeIterator(Isolate* isolate,
FrameInspector* frame_inspector)
: iterator_(isolate, frame_inspector) {
if (!Done() && ShouldIgnore()) Advance();
}
DebugScopeIterator::DebugScopeIterator(Isolate* isolate,
Handle<JSFunction> function)
: iterator_(isolate, function) {
if (!Done() && ShouldIgnore()) Advance();
}
DebugScopeIterator::DebugScopeIterator(Isolate* isolate,
Handle<JSGeneratorObject> generator)
: iterator_(isolate, generator) {
if (!Done() && ShouldIgnore()) Advance();
}
bool DebugScopeIterator::Done() { return iterator_.Done(); }
void DebugScopeIterator::Advance() {
DCHECK(!Done());
iterator_.Next();
while (!Done() && ShouldIgnore()) {
iterator_.Next();
}
}
bool DebugScopeIterator::ShouldIgnore() {
// Almost always Script scope will be empty, so just filter out that noise.
// Also drop empty Block, Eval and Script scopes, should we get any.
DCHECK(!Done());
debug::ScopeIterator::ScopeType type = GetType();
if (type != debug::ScopeIterator::ScopeTypeBlock &&
type != debug::ScopeIterator::ScopeTypeScript &&
type != debug::ScopeIterator::ScopeTypeEval &&
type != debug::ScopeIterator::ScopeTypeModule) {
return false;
}
// TODO(kozyatinskiy): make this function faster.
Handle<JSObject> value;
if (!iterator_.ScopeObject().ToHandle(&value)) return false;
Handle<FixedArray> keys =
KeyAccumulator::GetKeys(value, KeyCollectionMode::kOwnOnly,
ENUMERABLE_STRINGS,
GetKeysConversion::kConvertToString)
.ToHandleChecked();
return keys->length() == 0;
}
v8::debug::ScopeIterator::ScopeType DebugScopeIterator::GetType() {
DCHECK(!Done());
return static_cast<v8::debug::ScopeIterator::ScopeType>(iterator_.Type());
}
v8::Local<v8::Object> DebugScopeIterator::GetObject() {
DCHECK(!Done());
Handle<JSObject> value;
if (iterator_.ScopeObject().ToHandle(&value)) {
return Utils::ToLocal(value);
}
return v8::Local<v8::Object>();
}
v8::Local<v8::Function> DebugScopeIterator::GetFunction() {
DCHECK(!Done());
Handle<JSFunction> closure = iterator_.GetClosure();
if (closure.is_null()) return v8::Local<v8::Function>();
return Utils::ToLocal(closure);
}
debug::Location DebugScopeIterator::GetStartLocation() {
DCHECK(!Done());
Handle<JSFunction> closure = iterator_.GetClosure();
if (closure.is_null()) return debug::Location();
Object* obj = closure->shared()->script();
if (!obj->IsScript()) return debug::Location();
return ToApiHandle<v8::debug::Script>(handle(Script::cast(obj)))
->GetSourceLocation(iterator_.start_position());
}
debug::Location DebugScopeIterator::GetEndLocation() {
DCHECK(!Done());
Handle<JSFunction> closure = iterator_.GetClosure();
if (closure.is_null()) return debug::Location();
Object* obj = closure->shared()->script();
if (!obj->IsScript()) return debug::Location();
return ToApiHandle<v8::debug::Script>(handle(Script::cast(obj)))
->GetSourceLocation(iterator_.end_position());
}
bool DebugScopeIterator::SetVariableValue(v8::Local<v8::String> name,
v8::Local<v8::Value> value) {
DCHECK(!Done());
return iterator_.SetVariableValue(Utils::OpenHandle(*name),
Utils::OpenHandle(*value));
}
DebugWasmScopeIterator::DebugWasmScopeIterator(Isolate* isolate,
StandardFrame* frame,
int inlined_frame_index)
: isolate_(isolate),
frame_(frame),
inlined_frame_index_(inlined_frame_index),
type_(debug::ScopeIterator::ScopeTypeGlobal) {}
bool DebugWasmScopeIterator::Done() {
return type_ != debug::ScopeIterator::ScopeTypeGlobal &&
type_ != debug::ScopeIterator::ScopeTypeLocal;
}
void DebugWasmScopeIterator::Advance() {
DCHECK(!Done());
if (type_ == debug::ScopeIterator::ScopeTypeGlobal) {
type_ = debug::ScopeIterator::ScopeTypeLocal;
} else {
// We use ScopeTypeWith type as marker for done.
type_ = debug::ScopeIterator::ScopeTypeWith;
}
}
v8::debug::ScopeIterator::ScopeType DebugWasmScopeIterator::GetType() {
DCHECK(!Done());
return type_;
}
v8::Local<v8::Object> DebugWasmScopeIterator::GetObject() {
DCHECK(!Done());
Handle<WasmDebugInfo> debug_info(
WasmInterpreterEntryFrame::cast(frame_)->wasm_instance()->debug_info(),
isolate_);
switch (type_) {
case debug::ScopeIterator::ScopeTypeGlobal:
return Utils::ToLocal(WasmDebugInfo::GetGlobalScopeObject(
debug_info, frame_->fp(), inlined_frame_index_));
case debug::ScopeIterator::ScopeTypeLocal:
return Utils::ToLocal(WasmDebugInfo::GetLocalScopeObject(
debug_info, frame_->fp(), inlined_frame_index_));
default:
return v8::Local<v8::Object>();
}
return v8::Local<v8::Object>();
}
v8::Local<v8::Function> DebugWasmScopeIterator::GetFunction() {
DCHECK(!Done());
return v8::Local<v8::Function>();
}
debug::Location DebugWasmScopeIterator::GetStartLocation() {
DCHECK(!Done());
return debug::Location();
}
debug::Location DebugWasmScopeIterator::GetEndLocation() {
DCHECK(!Done());
return debug::Location();
}
bool DebugWasmScopeIterator::SetVariableValue(v8::Local<v8::String> name,
v8::Local<v8::Value> value) {
DCHECK(!Done());
return false;
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,64 @@
// 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.
#ifndef V8_DEBUG_DEBUG_SCOPE_ITERATOR_H_
#define V8_DEBUG_DEBUG_SCOPE_ITERATOR_H_
#include "src/debug/debug-frames.h"
#include "src/debug/debug-interface.h"
#include "src/debug/debug-scopes.h"
#include "src/frames.h"
namespace v8 {
namespace internal {
class DebugScopeIterator final : public debug::ScopeIterator {
public:
DebugScopeIterator(Isolate* isolate, FrameInspector* frame_inspector);
DebugScopeIterator(Isolate* isolate, Handle<JSFunction> function);
DebugScopeIterator(Isolate* isolate, Handle<JSGeneratorObject> generator);
bool Done() override;
void Advance() override;
ScopeType GetType() override;
v8::Local<v8::Object> GetObject() override;
v8::Local<v8::Function> GetFunction() override;
debug::Location GetStartLocation() override;
debug::Location GetEndLocation() override;
bool SetVariableValue(v8::Local<v8::String> name,
v8::Local<v8::Value> value) override;
private:
bool ShouldIgnore();
v8::internal::ScopeIterator iterator_;
};
class DebugWasmScopeIterator final : public debug::ScopeIterator {
public:
DebugWasmScopeIterator(Isolate* isolate, StandardFrame* frame,
int inlined_frame_index);
bool Done() override;
void Advance() override;
ScopeType GetType() override;
v8::Local<v8::Object> GetObject() override;
v8::Local<v8::Function> GetFunction() override;
debug::Location GetStartLocation() override;
debug::Location GetEndLocation() override;
bool SetVariableValue(v8::Local<v8::String> name,
v8::Local<v8::Value> value) override;
private:
Isolate* isolate_;
StandardFrame* frame_;
int inlined_frame_index_;
ScopeType type_;
};
} // namespace internal
} // namespace v8
#endif // V8_DEBUG_DEBUG_SCOPE_ITERATOR_H_

View File

@ -187,36 +187,49 @@ MUST_USE_RESULT MaybeHandle<JSObject> ScopeIterator::MaterializeScopeDetails() {
Handle<JSObject> scope_object;
ASSIGN_RETURN_ON_EXCEPTION(isolate_, scope_object, ScopeObject(), JSObject);
details->set(kScopeDetailsObjectIndex, *scope_object);
Handle<JSFunction> js_function = HasContext()
? handle(CurrentContext()->closure())
: Handle<JSFunction>::null();
if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript) {
return isolate_->factory()->NewJSArrayWithElements(details);
}
int start_position = 0;
int end_position = 0;
if (!nested_scope_chain_.is_empty()) {
js_function = GetFunction();
start_position = nested_scope_chain_.last().start_position;
end_position = nested_scope_chain_.last().end_position;
} else if (!js_function.is_null()) {
start_position = js_function->shared()->start_position();
end_position = js_function->shared()->end_position();
}
Handle<JSFunction> js_function = GetClosure();
if (!js_function.is_null()) {
Handle<String> closure_name = JSFunction::GetDebugName(js_function);
if (!closure_name.is_null() && closure_name->length() != 0) {
details->set(kScopeDetailsNameIndex, *closure_name);
}
details->set(kScopeDetailsStartPositionIndex, Smi::FromInt(start_position));
details->set(kScopeDetailsEndPositionIndex, Smi::FromInt(end_position));
details->set(kScopeDetailsStartPositionIndex,
Smi::FromInt(start_position()));
details->set(kScopeDetailsEndPositionIndex, Smi::FromInt(end_position()));
details->set(kScopeDetailsFunctionIndex, *js_function);
}
return isolate_->factory()->NewJSArrayWithElements(details);
}
Handle<JSFunction> ScopeIterator::GetClosure() {
if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript)
return Handle<JSFunction>::null();
if (HasNestedScopeChain()) return GetFunction();
return HasContext() ? handle(CurrentContext()->closure())
: Handle<JSFunction>::null();
}
int ScopeIterator::start_position() {
if (HasNestedScopeChain()) {
return LastNestedScopeChain().start_position;
}
if (!HasContext()) return 0;
Handle<JSFunction> js_function = handle(CurrentContext()->closure());
return js_function.is_null() ? 0 : js_function->shared()->start_position();
}
int ScopeIterator::end_position() {
if (HasNestedScopeChain()) {
return LastNestedScopeChain().end_position;
}
if (!HasContext()) return 0;
Handle<JSFunction> js_function = handle(CurrentContext()->closure());
return js_function.is_null() ? 0 : js_function->shared()->end_position();
}
void ScopeIterator::Next() {
DCHECK(!Done());
@ -230,25 +243,24 @@ void ScopeIterator::Next() {
if (context_->IsScriptContext()) {
context_ = Handle<Context>(context_->previous(), isolate_);
}
if (!nested_scope_chain_.is_empty()) {
DCHECK_EQ(nested_scope_chain_.last().scope_info->scope_type(),
SCRIPT_SCOPE);
if (HasNestedScopeChain()) {
DCHECK_EQ(LastNestedScopeChain().scope_info->scope_type(), SCRIPT_SCOPE);
nested_scope_chain_.RemoveLast();
DCHECK(nested_scope_chain_.is_empty());
DCHECK(!HasNestedScopeChain());
}
CHECK(context_->IsNativeContext());
} else if (nested_scope_chain_.is_empty()) {
} else if (!HasNestedScopeChain()) {
context_ = Handle<Context>(context_->previous(), isolate_);
} else {
do {
if (nested_scope_chain_.last().scope_info->HasContext()) {
if (LastNestedScopeChain().scope_info->HasContext()) {
DCHECK(context_->previous() != NULL);
context_ = Handle<Context>(context_->previous(), isolate_);
}
nested_scope_chain_.RemoveLast();
if (nested_scope_chain_.is_empty()) break;
if (!HasNestedScopeChain()) break;
// Repeat to skip hidden scopes.
} while (nested_scope_chain_.last().is_hidden());
} while (LastNestedScopeChain().is_hidden());
}
UnwrapEvaluationContext();
}
@ -257,8 +269,8 @@ void ScopeIterator::Next() {
// Return the type of the current scope.
ScopeIterator::ScopeType ScopeIterator::Type() {
DCHECK(!Done());
if (!nested_scope_chain_.is_empty()) {
Handle<ScopeInfo> scope_info = nested_scope_chain_.last().scope_info;
if (HasNestedScopeChain()) {
Handle<ScopeInfo> scope_info = LastNestedScopeChain().scope_info;
switch (scope_info->scope_type()) {
case FUNCTION_SCOPE:
DCHECK(context_->IsFunctionContext() || !scope_info->HasContext());
@ -342,8 +354,8 @@ bool ScopeIterator::HasContext() {
ScopeType type = Type();
if (type == ScopeTypeBlock || type == ScopeTypeLocal ||
type == ScopeTypeEval) {
if (!nested_scope_chain_.is_empty()) {
return nested_scope_chain_.last().scope_info->HasContext();
if (HasNestedScopeChain()) {
return LastNestedScopeChain().scope_info->HasContext();
}
}
return true;
@ -379,8 +391,8 @@ bool ScopeIterator::SetVariableValue(Handle<String> variable_name,
Handle<ScopeInfo> ScopeIterator::CurrentScopeInfo() {
DCHECK(!Done());
if (!nested_scope_chain_.is_empty()) {
return nested_scope_chain_.last().scope_info;
if (HasNestedScopeChain()) {
return LastNestedScopeChain().scope_info;
} else if (context_->IsBlockContext() || context_->IsFunctionContext() ||
context_->IsEvalContext()) {
return Handle<ScopeInfo>(context_->scope_info());
@ -392,9 +404,9 @@ Handle<ScopeInfo> ScopeIterator::CurrentScopeInfo() {
Handle<Context> ScopeIterator::CurrentContext() {
DCHECK(!Done());
if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript ||
nested_scope_chain_.is_empty()) {
!HasNestedScopeChain()) {
return context_;
} else if (nested_scope_chain_.last().scope_info->HasContext()) {
} else if (LastNestedScopeChain().scope_info->HasContext()) {
return context_;
} else {
return Handle<Context>();
@ -638,8 +650,8 @@ Handle<JSObject> ScopeIterator::MaterializeInnerScope() {
isolate_->factory()->NewJSObjectWithNullProto();
Handle<Context> context = Handle<Context>::null();
if (!nested_scope_chain_.is_empty()) {
Handle<ScopeInfo> scope_info = nested_scope_chain_.last().scope_info;
if (HasNestedScopeChain()) {
Handle<ScopeInfo> scope_info = LastNestedScopeChain().scope_info;
MaterializeStackLocals(inner_scope, scope_info);
if (scope_info->HasContext()) context = CurrentContext();
} else {
@ -970,5 +982,14 @@ void ScopeIterator::GetNestedScopeChain(Isolate* isolate, Scope* scope,
}
}
bool ScopeIterator::HasNestedScopeChain() {
return !nested_scope_chain_.is_empty();
}
ScopeIterator::ExtendedScopeInfo& ScopeIterator::LastNestedScopeChain() {
DCHECK(HasNestedScopeChain());
return nested_scope_chain_.last();
}
} // namespace internal
} // namespace v8

View File

@ -75,6 +75,11 @@ class ScopeIterator {
// Populate the set with collected non-local variable names.
Handle<StringSet> GetNonLocals();
// Return function which represents closure for current scope.
Handle<JSFunction> GetClosure();
int start_position();
int end_position();
#ifdef DEBUG
// Debug print of the content of the current scope.
void DebugPrint();
@ -169,6 +174,9 @@ class ScopeIterator {
void GetNestedScopeChain(Isolate* isolate, Scope* scope,
int statement_position);
bool HasNestedScopeChain();
ExtendedScopeInfo& LastNestedScopeChain();
DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
};

View File

@ -0,0 +1,153 @@
// 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.
#include "src/debug/debug-stack-trace-iterator.h"
#include "src/debug/debug-evaluate.h"
#include "src/debug/debug-scope-iterator.h"
#include "src/debug/debug.h"
#include "src/debug/liveedit.h"
#include "src/frames-inl.h"
#include "src/isolate.h"
namespace v8 {
std::unique_ptr<debug::StackTraceIterator> debug::StackTraceIterator::Create(
v8::Isolate* isolate, int index) {
return std::unique_ptr<debug::StackTraceIterator>(
new internal::DebugStackTraceIterator(
reinterpret_cast<internal::Isolate*>(isolate), index));
}
namespace internal {
DebugStackTraceIterator::DebugStackTraceIterator(Isolate* isolate, int index)
: isolate_(isolate),
iterator_(isolate, isolate->debug()->break_frame_id()),
is_top_frame_(true) {
if (iterator_.done()) return;
List<FrameSummary> frames(FLAG_max_inlining_levels + 1);
iterator_.frame()->Summarize(&frames);
inlined_frame_index_ = frames.length();
Advance();
for (; !Done() && index > 0; --index) Advance();
}
DebugStackTraceIterator::~DebugStackTraceIterator() {}
bool DebugStackTraceIterator::Done() const { return iterator_.done(); }
void DebugStackTraceIterator::Advance() {
while (true) {
--inlined_frame_index_;
for (; inlined_frame_index_ >= 0; --inlined_frame_index_) {
// Omit functions from native and extension scripts.
if (FrameSummary::Get(iterator_.frame(), inlined_frame_index_)
.is_subject_to_debugging()) {
break;
}
is_top_frame_ = false;
}
if (inlined_frame_index_ >= 0) {
frame_inspector_.reset(new FrameInspector(
iterator_.frame(), inlined_frame_index_, isolate_));
break;
}
is_top_frame_ = false;
frame_inspector_.reset();
iterator_.Advance();
if (iterator_.done()) break;
List<FrameSummary> frames(FLAG_max_inlining_levels + 1);
iterator_.frame()->Summarize(&frames);
inlined_frame_index_ = frames.length();
}
}
int DebugStackTraceIterator::GetContextId() const {
DCHECK(!Done());
Object* value =
frame_inspector_->summary().native_context()->debug_context_id();
return (value->IsSmi()) ? Smi::ToInt(value) : 0;
}
v8::Local<v8::Value> DebugStackTraceIterator::GetReceiver() const {
DCHECK(!Done());
Handle<Object> value = frame_inspector_->summary().receiver();
if (value.is_null() || (value->IsSmi() || !value->IsTheHole(isolate_))) {
return Utils::ToLocal(value);
}
return v8::Undefined(reinterpret_cast<v8::Isolate*>(isolate_));
}
v8::Local<v8::Value> DebugStackTraceIterator::GetReturnValue() const {
DCHECK(!Done());
if (frame_inspector_->summary().IsWasm()) return v8::Local<v8::Value>();
bool is_optimized = iterator_.frame()->is_optimized();
if (is_optimized || !is_top_frame_ ||
!isolate_->debug()->IsBreakAtReturn(iterator_.javascript_frame())) {
return v8::Local<v8::Value>();
}
return Utils::ToLocal(isolate_->debug()->return_value_handle());
}
v8::Local<v8::String> DebugStackTraceIterator::GetFunctionName() const {
DCHECK(!Done());
return Utils::ToLocal(frame_inspector_->summary().FunctionName());
}
v8::Local<v8::debug::Script> DebugStackTraceIterator::GetScript() const {
DCHECK(!Done());
Handle<Object> value = frame_inspector_->summary().script();
if (!value->IsScript()) return v8::Local<v8::debug::Script>();
return ToApiHandle<debug::Script>(Handle<Script>::cast(value));
}
debug::Location DebugStackTraceIterator::GetSourceLocation() const {
DCHECK(!Done());
v8::Local<v8::debug::Script> script = GetScript();
if (script.IsEmpty()) return v8::debug::Location();
return script->GetSourceLocation(
frame_inspector_->summary().SourcePosition());
}
v8::Local<v8::Function> DebugStackTraceIterator::GetFunction() const {
DCHECK(!Done());
if (!frame_inspector_->summary().IsJavaScript())
return v8::Local<v8::Function>();
return Utils::ToLocal(frame_inspector_->summary().AsJavaScript().function());
}
std::unique_ptr<v8::debug::ScopeIterator>
DebugStackTraceIterator::GetScopeIterator() const {
DCHECK(!Done());
StandardFrame* frame = iterator_.frame();
if (frame->is_wasm_interpreter_entry()) {
return std::unique_ptr<v8::debug::ScopeIterator>(new DebugWasmScopeIterator(
isolate_, iterator_.frame(), inlined_frame_index_));
}
return std::unique_ptr<v8::debug::ScopeIterator>(
new DebugScopeIterator(isolate_, frame_inspector_.get()));
}
bool DebugStackTraceIterator::Restart() {
DCHECK(!Done());
if (iterator_.is_wasm()) return false;
return !LiveEdit::RestartFrame(iterator_.javascript_frame());
}
v8::MaybeLocal<v8::Value> DebugStackTraceIterator::Evaluate(
v8::Local<v8::String> source, bool throw_on_side_effect) {
DCHECK(!Done());
Handle<Object> value;
if (!DebugEvaluate::Local(isolate_, iterator_.frame()->id(),
inlined_frame_index_, Utils::OpenHandle(*source),
throw_on_side_effect)
.ToHandle(&value)) {
isolate_->OptionalRescheduleException(false);
return v8::MaybeLocal<v8::Value>();
}
return Utils::ToLocal(value);
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,46 @@
// 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.
#ifndef V8_DEBUG_DEBUG_STACK_TRACE_ITERATOR_H_
#define V8_DEBUG_DEBUG_STACK_TRACE_ITERATOR_H_
#include "src/debug/debug-frames.h"
#include "src/debug/debug-interface.h"
#include "src/frames.h"
namespace v8 {
namespace internal {
class DebugStackTraceIterator final : public debug::StackTraceIterator {
public:
DebugStackTraceIterator(Isolate* isolate, int index);
~DebugStackTraceIterator() override;
bool Done() const override;
void Advance() override;
int GetContextId() const override;
v8::Local<v8::Value> GetReceiver() const override;
v8::Local<v8::Value> GetReturnValue() const override;
v8::Local<v8::String> GetFunctionName() const override;
v8::Local<v8::debug::Script> GetScript() const override;
debug::Location GetSourceLocation() const override;
v8::Local<v8::Function> GetFunction() const override;
std::unique_ptr<v8::debug::ScopeIterator> GetScopeIterator() const override;
bool Restart() override;
v8::MaybeLocal<v8::Value> Evaluate(v8::Local<v8::String> source,
bool throw_on_side_effect) override;
private:
Isolate* isolate_;
StackTraceFrameIterator iterator_;
std::unique_ptr<FrameInspector> frame_inspector_;
int inlined_frame_index_;
bool is_top_frame_;
};
} // namespace internal
} // namespace v8
#endif // V8_DEBUG_DEBUG_STACK_TRACE_ITERATOR_H_

View File

@ -136,8 +136,6 @@ v8_source_set("inspector") {
"injected-script.h",
"inspected-context.cc",
"inspected-context.h",
"java-script-call-frame.cc",
"java-script-call-frame.h",
"remote-object-id.cc",
"remote-object-id.h",
"script-breakpoint.h",

View File

@ -33,74 +33,6 @@
var DebuggerScript = {};
/** @type {!Map<!ScopeType, string>} */
DebuggerScript._scopeTypeNames = new Map();
DebuggerScript._scopeTypeNames.set(ScopeType.Global, "global");
DebuggerScript._scopeTypeNames.set(ScopeType.Local, "local");
DebuggerScript._scopeTypeNames.set(ScopeType.With, "with");
DebuggerScript._scopeTypeNames.set(ScopeType.Closure, "closure");
DebuggerScript._scopeTypeNames.set(ScopeType.Catch, "catch");
DebuggerScript._scopeTypeNames.set(ScopeType.Block, "block");
DebuggerScript._scopeTypeNames.set(ScopeType.Script, "script");
DebuggerScript._scopeTypeNames.set(ScopeType.Eval, "eval");
DebuggerScript._scopeTypeNames.set(ScopeType.Module, "module");
/**
* @param {function()} fun
* @return {?Array<!Scope>}
*/
DebuggerScript.getFunctionScopes = function(fun)
{
var mirror = MakeMirror(fun);
if (!mirror.isFunction())
return null;
var functionMirror = /** @type {!FunctionMirror} */(mirror);
var count = functionMirror.scopeCount();
if (count == 0)
return null;
var result = [];
for (var i = 0; i < count; i++) {
var scopeDetails = functionMirror.scope(i).details();
var scopeObject = DebuggerScript._buildScopeObject(scopeDetails.type(), scopeDetails.object());
if (!scopeObject)
continue;
result.push({
type: /** @type {string} */(DebuggerScript._scopeTypeNames.get(scopeDetails.type())),
object: scopeObject,
name: scopeDetails.name() || ""
});
}
return result;
}
/**
* @param {Object} gen
* @return {?Array<!Scope>}
*/
DebuggerScript.getGeneratorScopes = function(gen)
{
var mirror = MakeMirror(gen);
if (!mirror.isGenerator())
return null;
var generatorMirror = /** @type {!GeneratorMirror} */(mirror);
var count = generatorMirror.scopeCount();
if (count == 0)
return null;
var result = [];
for (var i = 0; i < count; i++) {
var scopeDetails = generatorMirror.scope(i).details();
var scopeObject = DebuggerScript._buildScopeObject(scopeDetails.type(), scopeDetails.object());
if (!scopeObject)
continue;
result.push({
type: /** @type {string} */(DebuggerScript._scopeTypeNames.get(scopeDetails.type())),
object: scopeObject,
name: scopeDetails.name() || ""
});
}
return result;
}
/**
* @param {!ExecutionState} execState
* @param {!BreakpointInfo} info
@ -126,19 +58,6 @@ DebuggerScript.removeBreakpoint = function(execState, info)
Debug.findBreakPoint(info.breakpointId, true);
}
/**
* @param {!ExecutionState} execState
* @param {number} limit
* @return {!Array<!JavaScriptCallFrame>}
*/
DebuggerScript.currentCallFrames = function(execState, limit)
{
var frames = [];
for (var i = 0; i < execState.frameCount() && (!limit || i < limit); ++i)
frames.push(DebuggerScript._frameMirrorToJSCallFrame(execState.frame(i)));
return frames;
}
// Returns array in form:
// [ 0, <v8_result_report> ] in case of success
// or [ 1, <general_error_message>, <compiler_message>, <line_number>, <column_number> ] in case of compile error, numbers are 1-based.
@ -203,295 +122,5 @@ DebuggerScript.getBreakpointNumbers = function(breakpoints)
return numbers;
}
// NOTE: This function is performance critical, as it can be run on every
// statement that generates an async event (like addEventListener) to support
// asynchronous call stacks. Thus, when possible, initialize the data lazily.
/**
* @param {!FrameMirror} frameMirror
* @return {!JavaScriptCallFrame}
*/
DebuggerScript._frameMirrorToJSCallFrame = function(frameMirror)
{
// Stuff that can not be initialized lazily (i.e. valid while paused with a valid break_id).
// The frameMirror and scopeMirror can be accessed only while paused on the debugger.
var frameDetails = frameMirror.details();
var funcObject = frameDetails.func();
var scriptObject = frameDetails.script();
var sourcePosition = frameDetails.sourcePosition();
var thisObject = frameDetails.receiver();
var isAtReturn = !!frameDetails.isAtReturn();
var returnValue = isAtReturn ? frameDetails.returnValue() : undefined;
var scopeMirrors = frameMirror.allScopes(false);
/** @type {!Array<number>} */
var scopeTypes = new Array(scopeMirrors.length);
/** @type {?Array<!Object>} */
var scopeObjects = new Array(scopeMirrors.length);
/** @type {!Array<string|undefined>} */
var scopeNames = new Array(scopeMirrors.length);
/** @type {?Array<number>} */
var scopeStartPositions = new Array(scopeMirrors.length);
/** @type {?Array<number>} */
var scopeEndPositions = new Array(scopeMirrors.length);
/** @type {?Array<function()|null>} */
var scopeFunctions = new Array(scopeMirrors.length);
for (var i = 0; i < scopeMirrors.length; ++i) {
var scopeDetails = scopeMirrors[i].details();
scopeTypes[i] = scopeDetails.type();
scopeObjects[i] = scopeDetails.object();
scopeNames[i] = scopeDetails.name();
scopeStartPositions[i] = scopeDetails.startPosition ? scopeDetails.startPosition() : 0;
scopeEndPositions[i] = scopeDetails.endPosition ? scopeDetails.endPosition() : 0;
scopeFunctions[i] = scopeDetails.func ? scopeDetails.func() : null;
}
// Calculated lazily.
var scopeChain;
var funcMirror;
var scriptMirror;
var location;
/** @type {!Array<?RawLocation>} */
var scopeStartLocations;
/** @type {!Array<?RawLocation>} */
var scopeEndLocations;
var details;
/**
* @param {!ScriptMirror|undefined} script
* @param {number} pos
* @return {?RawLocation}
*/
function createLocation(script, pos)
{
if (!script)
return null;
var location = script.locationFromPosition(pos, true);
return {
"lineNumber": location.line,
"columnNumber": location.column,
"scriptId": String(script.id())
}
}
/**
* @return {!Array<!Object>}
*/
function ensureScopeChain()
{
if (!scopeChain) {
scopeChain = [];
scopeStartLocations = [];
scopeEndLocations = [];
for (var i = 0, j = 0; i < scopeObjects.length; ++i) {
var scopeObject = DebuggerScript._buildScopeObject(scopeTypes[i], scopeObjects[i]);
if (scopeObject) {
scopeTypes[j] = scopeTypes[i];
scopeNames[j] = scopeNames[i];
scopeChain[j] = scopeObject;
var funcMirror = scopeFunctions ? MakeMirror(scopeFunctions[i]) : null;
if (!funcMirror || !funcMirror.isFunction())
funcMirror = new UnresolvedFunctionMirror(funcObject);
var script = /** @type {!FunctionMirror} */(funcMirror).script();
scopeStartLocations[j] = createLocation(script, scopeStartPositions[i]);
scopeEndLocations[j] = createLocation(script, scopeEndPositions[i]);
++j;
}
}
scopeTypes.length = scopeChain.length;
scopeNames.length = scopeChain.length;
scopeObjects = null; // Free for GC.
scopeFunctions = null;
scopeStartPositions = null;
scopeEndPositions = null;
}
return scopeChain;
}
/**
* @return {!JavaScriptCallFrameDetails}
*/
function lazyDetails()
{
if (!details) {
var scopeObjects = ensureScopeChain();
var script = ensureScriptMirror();
/** @type {!Array<Scope>} */
var scopes = [];
for (var i = 0; i < scopeObjects.length; ++i) {
var scope = {
"type": /** @type {string} */(DebuggerScript._scopeTypeNames.get(scopeTypes[i])),
"object": scopeObjects[i],
};
if (scopeNames[i])
scope.name = scopeNames[i];
if (scopeStartLocations[i])
scope.startLocation = /** @type {!RawLocation} */(scopeStartLocations[i]);
if (scopeEndLocations[i])
scope.endLocation = /** @type {!RawLocation} */(scopeEndLocations[i]);
scopes.push(scope);
}
details = {
"functionName": ensureFuncMirror().debugName(),
"location": {
"lineNumber": ensureLocation().line,
"columnNumber": ensureLocation().column,
"scriptId": String(script.id())
},
"this": thisObject,
"scopeChain": scopes
};
var functionLocation = ensureFuncMirror().sourceLocation();
if (functionLocation) {
details.functionLocation = {
"lineNumber": functionLocation.line,
"columnNumber": functionLocation.column,
"scriptId": String(script.id())
};
}
if (isAtReturn)
details.returnValue = returnValue;
}
return details;
}
/**
* @return {!FunctionMirror}
*/
function ensureFuncMirror()
{
if (!funcMirror) {
funcMirror = MakeMirror(funcObject);
if (!funcMirror.isFunction())
funcMirror = new UnresolvedFunctionMirror(funcObject);
}
return /** @type {!FunctionMirror} */(funcMirror);
}
/**
* @return {!ScriptMirror}
*/
function ensureScriptMirror()
{
if (!scriptMirror) {
scriptMirror = MakeMirror(scriptObject);
}
return /** @type {!ScriptMirror} */(scriptMirror);
}
/**
* @return {!{line: number, column: number}}
*/
function ensureLocation()
{
if (!location) {
var script = ensureScriptMirror();
location = script.locationFromPosition(sourcePosition, true);
if (!location)
location = { line: 0, column: 0 };
}
return location;
}
/**
* @return {number}
*/
function contextId()
{
var context =
ensureFuncMirror().context() || ensureScriptMirror().context();
if (context && context.data()) return Number(context.data());
return 0;
}
/**
* @param {string} expression
* @param {boolean} throwOnSideEffect
* @return {*}
*/
function evaluate(expression, throwOnSideEffect)
{
return frameMirror.evaluate(expression, throwOnSideEffect).value();
}
/** @return {undefined} */
function restart()
{
return frameMirror.restart();
}
/**
* @param {number} scopeNumber
* @param {string} variableName
* @param {*} newValue
*/
function setVariableValue(scopeNumber, variableName, newValue)
{
var scopeMirror = frameMirror.scope(scopeNumber);
if (!scopeMirror)
throw new Error("Incorrect scope index");
scopeMirror.setVariableValue(variableName, newValue);
}
return {
"contextId": contextId,
"thisObject": thisObject,
"evaluate": evaluate,
"restart": restart,
"setVariableValue": setVariableValue,
"isAtReturn": isAtReturn,
"details": lazyDetails
};
}
/**
* @param {number} scopeType
* @param {!Object} scopeObject
* @return {!Object|undefined}
*/
DebuggerScript._buildScopeObject = function(scopeType, scopeObject)
{
var result;
switch (scopeType) {
case ScopeType.Local:
case ScopeType.Closure:
case ScopeType.Catch:
case ScopeType.Block:
case ScopeType.Script:
case ScopeType.Eval:
case ScopeType.Module:
// For transient objects we create a "persistent" copy that contains
// the same properties.
// Reset scope object prototype to null so that the proto properties
// don't appear in the local scope section.
var properties = /** @type {!ObjectMirror} */(MakeMirror(scopeObject)).properties();
// Almost always Script scope will be empty, so just filter out that noise.
// Also drop empty Block, Eval and Script scopes, should we get any.
if (!properties.length && (scopeType === ScopeType.Script ||
scopeType === ScopeType.Block ||
scopeType === ScopeType.Eval ||
scopeType === ScopeType.Module)) {
break;
}
result = { __proto__: null };
for (var j = 0; j < properties.length; j++) {
var name = properties[j].name();
if (name.length === 0 || name.charAt(0) === ".")
continue; // Skip internal variables like ".arguments" and variables with empty name
result[name] = properties[j].value_;
}
break;
case ScopeType.Global:
case ScopeType.With:
result = scopeObject;
break;
}
return result;
}
return DebuggerScript;
})();

View File

@ -2,43 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/** @typedef {{
type: string,
object: !Object,
name: (string|undefined),
startLocation: (!RawLocation|undefined),
endLocation: (!RawLocation|undefined)
}} */
var Scope;
/** @typedef {{
scriptId: string,
lineNumber: number,
columnNumber: number
}} */
var RawLocation;
/** @typedef {{
functionName: string,
location: !RawLocation,
this: !Object,
scopeChain: !Array<!Scope>,
functionLocation: (RawLocation|undefined),
returnValue: (*|undefined)
}} */
var JavaScriptCallFrameDetails;
/** @typedef {{
contextId: function():number,
thisObject: !Object,
evaluate: function(string, boolean):*,
restart: function():undefined,
setVariableValue: function(number, string, *):undefined,
isAtReturn: boolean,
details: function():!JavaScriptCallFrameDetails
}} */
var JavaScriptCallFrame;
/**
* @const
*/
@ -58,6 +21,17 @@ Debug.scripts = function() {}
*/
Debug.setScriptBreakPointById = function(scriptId, line, column, condition, groupId) {}
/** @typedef {{
* script: number,
* position: number,
* line: number,
* column:number,
* start: number,
* end: number,
* }}
*/
var SourceLocation;
/**
* @param {number} breakId
* @return {!Array<!SourceLocation>}
@ -121,262 +95,8 @@ BreakPoint.prototype.number = function() {}
/** @interface */
function ExecutionState() {}
/**
* @param {string} source
*/
ExecutionState.prototype.evaluateGlobal = function(source) {}
/** @return {number} */
ExecutionState.prototype.frameCount = function() {}
/**
* @param {number} index
* @return {!FrameMirror}
*/
ExecutionState.prototype.frame = function(index) {}
/** @param {number} index */
ExecutionState.prototype.setSelectedFrame = function(index) {}
/** @return {number} */
ExecutionState.prototype.selectedFrame = function() {}
/** @enum */
var ScopeType = { Global: 0,
Local: 1,
With: 2,
Closure: 3,
Catch: 4,
Block: 5,
Script: 6,
Eval: 7,
Module: 8};
/** @typedef {{
* script: number,
* position: number,
* line: number,
* column:number,
* start: number,
* end: number,
* }}
*/
var SourceLocation;
/** @typedef{{
* id: number,
* context_data: (string|undefined),
* }}
*/
var Script;
/** @interface */
function ScopeDetails() {}
/** @return {!Object} */
ScopeDetails.prototype.object = function() {}
/** @return {string|undefined} */
ScopeDetails.prototype.name = function() {}
/** @return {number} */
ScopeDetails.prototype.type = function() {}
/** @interface */
function FrameDetails() {}
/** @return {!Object} */
FrameDetails.prototype.receiver = function() {}
/** @return {function()} */
FrameDetails.prototype.func = function() {}
/** @return {!Object} */
FrameDetails.prototype.script = function() {}
/** @return {boolean} */
FrameDetails.prototype.isAtReturn = function() {}
/** @return {number} */
FrameDetails.prototype.sourcePosition = function() {}
/** @return {*} */
FrameDetails.prototype.returnValue = function() {}
/** @return {number} */
FrameDetails.prototype.scopeCount = function() {}
/**
* @param {*} value
* @return {!Mirror}
*/
function MakeMirror(value) {}
/** @interface */
function Mirror() {}
/** @return {boolean} */
Mirror.prototype.isFunction = function() {}
/** @return {boolean} */
Mirror.prototype.isGenerator = function() {}
/**
* @interface
* @extends {Mirror}
*/
function ObjectMirror() {}
/** @return {!Array<!PropertyMirror>} */
ObjectMirror.prototype.properties = function() {}
/**
* @interface
* @extends {ObjectMirror}
*/
function FunctionMirror () {}
/** @return {number} */
FunctionMirror.prototype.scopeCount = function() {}
/**
* @param {number} index
* @return {!ScopeMirror|undefined}
*/
FunctionMirror.prototype.scope = function(index) {}
/** @return {boolean} */
FunctionMirror.prototype.resolved = function() {}
/** @return {function()} */
FunctionMirror.prototype.value = function() {}
/** @return {string} */
FunctionMirror.prototype.debugName = function() {}
/** @return {!ScriptMirror|undefined} */
FunctionMirror.prototype.script = function() {}
/** @return {!SourceLocation|undefined} */
FunctionMirror.prototype.sourceLocation = function() {}
/** @return {!ContextMirror|undefined} */
FunctionMirror.prototype.context = function() {}
/**
* @constructor
* @param {*} value
*/
function UnresolvedFunctionMirror(value) {}
/**
* @interface
* @extends {ObjectMirror}
*/
function GeneratorMirror () {}
/** @return {number} */
GeneratorMirror.prototype.scopeCount = function() {}
/**
* @param {number} index
* @return {!ScopeMirror|undefined}
*/
GeneratorMirror.prototype.scope = function(index) {}
/**
* @interface
* @extends {Mirror}
*/
function PropertyMirror() {}
/** @return {!Mirror} */
PropertyMirror.prototype.value = function() {}
/** @return {string} */
PropertyMirror.prototype.name = function() {}
/** @type {*} */
PropertyMirror.prototype.value_;
/**
* @interface
* @extends {Mirror}
*/
function FrameMirror() {}
/**
* @param {boolean=} ignoreNestedScopes
* @return {!Array<!ScopeMirror>}
*/
FrameMirror.prototype.allScopes = function(ignoreNestedScopes) {}
/** @return {!FrameDetails} */
FrameMirror.prototype.details = function() {}
/** @return {!ScriptMirror} */
FrameMirror.prototype.script = function() {}
/**
* @param {string} source
* @param {boolean} throwOnSideEffect
*/
FrameMirror.prototype.evaluate = function(source, throwOnSideEffect) {}
FrameMirror.prototype.restart = function() {}
/** @param {number} index */
FrameMirror.prototype.scope = function(index) {}
/**
* @interface
* @extends {Mirror}
*/
function ScriptMirror() {}
/** @return {!Script} */
ScriptMirror.prototype.value = function() {}
/** @return {number} */
ScriptMirror.prototype.id = function() {}
/** @return {ContextMirror} */
ScriptMirror.prototype.context = function() {}
/**
* @param {number} position
* @param {boolean=} includeResourceOffset
*/
ScriptMirror.prototype.locationFromPosition = function(position, includeResourceOffset) {}
/**
* @interface
* @extends {Mirror}
*/
function ScopeMirror() {}
/** @return {!ScopeDetails} */
ScopeMirror.prototype.details = function() {}
/**
* @param {string} name
* @param {*} newValue
*/
ScopeMirror.prototype.setVariableValue = function(name, newValue) {}
/**
* @interface
* @extends {Mirror}
*/
function ContextMirror() {}
/** @return {string|undefined} */
ContextMirror.prototype.data = function() {}

View File

@ -40,8 +40,6 @@
'inspector/injected-script.h',
'inspector/inspected-context.cc',
'inspector/inspected-context.h',
'inspector/java-script-call-frame.cc',
'inspector/java-script-call-frame.h',
'inspector/remote-object-id.cc',
'inspector/remote-object-id.h',
'inspector/script-breakpoint.h',

View File

@ -1,157 +0,0 @@
/*
* Copyright (c) 2010, Google Inc. 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.
*/
#include "src/inspector/java-script-call-frame.h"
#include "src/debug/debug-interface.h"
#include "src/inspector/string-util.h"
namespace v8_inspector {
JavaScriptCallFrame::JavaScriptCallFrame(v8::Local<v8::Context> debuggerContext,
v8::Local<v8::Object> callFrame)
: m_isolate(debuggerContext->GetIsolate()),
m_debuggerContext(m_isolate, debuggerContext),
m_callFrame(m_isolate, callFrame) {}
JavaScriptCallFrame::~JavaScriptCallFrame() {}
int JavaScriptCallFrame::callV8FunctionReturnInt(const char* name) const {
v8::HandleScope handleScope(m_isolate);
v8::MicrotasksScope microtasks(m_isolate,
v8::MicrotasksScope::kDoNotRunMicrotasks);
v8::Local<v8::Context> context =
v8::Local<v8::Context>::New(m_isolate, m_debuggerContext);
v8::Local<v8::Object> callFrame =
v8::Local<v8::Object>::New(m_isolate, m_callFrame);
v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(
callFrame->Get(context, toV8StringInternalized(m_isolate, name))
.ToLocalChecked());
v8::Local<v8::Value> result;
if (!func->Call(context, callFrame, 0, nullptr).ToLocal(&result) ||
!result->IsInt32())
return 0;
return result.As<v8::Int32>()->Value();
}
int JavaScriptCallFrame::contextId() const {
return callV8FunctionReturnInt("contextId");
}
bool JavaScriptCallFrame::isAtReturn() const {
v8::HandleScope handleScope(m_isolate);
v8::Local<v8::Context> context =
v8::Local<v8::Context>::New(m_isolate, m_debuggerContext);
v8::Local<v8::Object> callFrame =
v8::Local<v8::Object>::New(m_isolate, m_callFrame);
v8::Local<v8::Value> result;
if (!callFrame->Get(context, toV8StringInternalized(m_isolate, "isAtReturn"))
.ToLocal(&result) ||
!result->IsBoolean())
return false;
return result.As<v8::Boolean>()->BooleanValue(context).FromMaybe(false);
}
v8::MaybeLocal<v8::Object> JavaScriptCallFrame::details() const {
v8::MicrotasksScope microtasks(m_isolate,
v8::MicrotasksScope::kDoNotRunMicrotasks);
v8::Local<v8::Context> context =
v8::Local<v8::Context>::New(m_isolate, m_debuggerContext);
v8::Local<v8::Object> callFrame =
v8::Local<v8::Object>::New(m_isolate, m_callFrame);
v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(
callFrame->Get(context, toV8StringInternalized(m_isolate, "details"))
.ToLocalChecked());
v8::TryCatch try_catch(m_isolate);
v8::Local<v8::Value> details;
if (func->Call(context, callFrame, 0, nullptr).ToLocal(&details)) {
return v8::Local<v8::Object>::Cast(details);
}
return v8::MaybeLocal<v8::Object>();
}
v8::MaybeLocal<v8::Value> JavaScriptCallFrame::evaluate(
v8::Local<v8::Value> expression, bool throwOnSideEffect) {
v8::MicrotasksScope microtasks(m_isolate,
v8::MicrotasksScope::kRunMicrotasks);
v8::Local<v8::Context> context =
v8::Local<v8::Context>::New(m_isolate, m_debuggerContext);
v8::Local<v8::Object> callFrame =
v8::Local<v8::Object>::New(m_isolate, m_callFrame);
v8::Local<v8::Function> evalFunction = v8::Local<v8::Function>::Cast(
callFrame->Get(context, toV8StringInternalized(m_isolate, "evaluate"))
.ToLocalChecked());
v8::Local<v8::Value> argv[] = {
expression, v8::Boolean::New(m_isolate, throwOnSideEffect)};
return evalFunction->Call(context, callFrame, arraysize(argv), argv);
}
v8::MaybeLocal<v8::Value> JavaScriptCallFrame::restart() {
v8::MicrotasksScope microtasks(m_isolate,
v8::MicrotasksScope::kDoNotRunMicrotasks);
v8::Local<v8::Context> context =
v8::Local<v8::Context>::New(m_isolate, m_debuggerContext);
v8::Local<v8::Object> callFrame =
v8::Local<v8::Object>::New(m_isolate, m_callFrame);
v8::Local<v8::Function> restartFunction = v8::Local<v8::Function>::Cast(
callFrame->Get(context, toV8StringInternalized(m_isolate, "restart"))
.ToLocalChecked());
v8::TryCatch try_catch(m_isolate);
v8::debug::SetLiveEditEnabled(m_isolate, true);
v8::MaybeLocal<v8::Value> result = restartFunction->Call(
m_debuggerContext.Get(m_isolate), callFrame, 0, nullptr);
v8::debug::SetLiveEditEnabled(m_isolate, false);
return result;
}
v8::MaybeLocal<v8::Value> JavaScriptCallFrame::setVariableValue(
int scopeNumber, v8::Local<v8::Value> variableName,
v8::Local<v8::Value> newValue) {
v8::MicrotasksScope microtasks(m_isolate,
v8::MicrotasksScope::kDoNotRunMicrotasks);
v8::Local<v8::Context> context =
v8::Local<v8::Context>::New(m_isolate, m_debuggerContext);
v8::Local<v8::Object> callFrame =
v8::Local<v8::Object>::New(m_isolate, m_callFrame);
v8::Local<v8::Function> setVariableValueFunction =
v8::Local<v8::Function>::Cast(
callFrame
->Get(context,
toV8StringInternalized(m_isolate, "setVariableValue"))
.ToLocalChecked());
v8::Local<v8::Value> argv[] = {
v8::Local<v8::Value>(v8::Integer::New(m_isolate, scopeNumber)),
variableName, newValue};
v8::TryCatch try_catch(m_isolate);
return setVariableValueFunction->Call(context, callFrame, arraysize(argv),
argv);
}
} // namespace v8_inspector

View File

@ -1,81 +0,0 @@
/*
* Copyright (c) 2010, Google Inc. 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.
*/
#ifndef V8_INSPECTOR_JAVASCRIPTCALLFRAME_H_
#define V8_INSPECTOR_JAVASCRIPTCALLFRAME_H_
#include <memory>
#include <vector>
#include "src/base/macros.h"
#include "include/v8.h"
namespace v8_inspector {
class JavaScriptCallFrame {
public:
static std::unique_ptr<JavaScriptCallFrame> create(
v8::Local<v8::Context> debuggerContext, v8::Local<v8::Object> callFrame) {
return std::unique_ptr<JavaScriptCallFrame>(
new JavaScriptCallFrame(debuggerContext, callFrame));
}
~JavaScriptCallFrame();
int contextId() const;
bool isAtReturn() const;
v8::MaybeLocal<v8::Object> details() const;
v8::MaybeLocal<v8::Value> evaluate(v8::Local<v8::Value> expression,
bool throwOnSideEffect);
v8::MaybeLocal<v8::Value> restart();
v8::MaybeLocal<v8::Value> setVariableValue(int scopeNumber,
v8::Local<v8::Value> variableName,
v8::Local<v8::Value> newValue);
private:
JavaScriptCallFrame(v8::Local<v8::Context> debuggerContext,
v8::Local<v8::Object> callFrame);
int callV8FunctionReturnInt(const char* name) const;
v8::Isolate* m_isolate;
v8::Global<v8::Context> m_debuggerContext;
v8::Global<v8::Object> m_callFrame;
DISALLOW_COPY_AND_ASSIGN(JavaScriptCallFrame);
};
using JavaScriptCallFrames = std::vector<std::unique_ptr<JavaScriptCallFrame>>;
} // namespace v8_inspector
#endif // V8_INSPECTOR_JAVASCRIPTCALLFRAME_H_

View File

@ -9,7 +9,6 @@
#include "src/debug/debug-interface.h"
#include "src/inspector/injected-script.h"
#include "src/inspector/inspected-context.h"
#include "src/inspector/java-script-call-frame.h"
#include "src/inspector/protocol/Protocol.h"
#include "src/inspector/remote-object-id.h"
#include "src/inspector/script-breakpoint.h"
@ -35,6 +34,7 @@ using protocol::Debugger::CallFrame;
using protocol::Runtime::ExceptionDetails;
using protocol::Runtime::ScriptId;
using protocol::Runtime::RemoteObject;
using protocol::Debugger::Scope;
namespace DebuggerAgentState {
static const char javaScriptBreakpoints[] = "javaScriptBreakopints";
@ -186,6 +186,68 @@ String16 breakLocationType(v8::debug::BreakLocationType type) {
return String16();
}
String16 scopeType(v8::debug::ScopeIterator::ScopeType type) {
switch (type) {
case v8::debug::ScopeIterator::ScopeTypeGlobal:
return Scope::TypeEnum::Global;
case v8::debug::ScopeIterator::ScopeTypeLocal:
return Scope::TypeEnum::Local;
case v8::debug::ScopeIterator::ScopeTypeWith:
return Scope::TypeEnum::With;
case v8::debug::ScopeIterator::ScopeTypeClosure:
return Scope::TypeEnum::Closure;
case v8::debug::ScopeIterator::ScopeTypeCatch:
return Scope::TypeEnum::Catch;
case v8::debug::ScopeIterator::ScopeTypeBlock:
return Scope::TypeEnum::Block;
case v8::debug::ScopeIterator::ScopeTypeScript:
return Scope::TypeEnum::Script;
case v8::debug::ScopeIterator::ScopeTypeEval:
return Scope::TypeEnum::Eval;
case v8::debug::ScopeIterator::ScopeTypeModule:
return Scope::TypeEnum::Module;
}
UNREACHABLE();
return String16();
}
Response buildScopes(v8::debug::ScopeIterator* iterator,
InjectedScript* injectedScript,
std::unique_ptr<Array<Scope>>* scopes) {
*scopes = Array<Scope>::create();
if (!injectedScript) return Response::OK();
for (; !iterator->Done(); iterator->Advance()) {
std::unique_ptr<RemoteObject> object;
Response result = injectedScript->wrapObject(
iterator->GetObject(), kBacktraceObjectGroup, false, false, &object);
if (!result.isSuccess()) return result;
auto scope = Scope::create()
.setType(scopeType(iterator->GetType()))
.setObject(std::move(object))
.build();
v8::Local<v8::Function> closure = iterator->GetFunction();
if (!closure.IsEmpty()) {
String16 name = toProtocolStringWithTypeCheck(closure->GetDebugName());
if (!name.isEmpty()) scope->setName(name);
String16 scriptId = String16::fromInteger(closure->ScriptId());
v8::debug::Location start = iterator->GetStartLocation();
scope->setStartLocation(protocol::Debugger::Location::create()
.setScriptId(scriptId)
.setLineNumber(start.GetLineNumber())
.setColumnNumber(start.GetColumnNumber())
.build());
v8::debug::Location end = iterator->GetEndLocation();
scope->setEndLocation(protocol::Debugger::Location::create()
.setScriptId(scriptId)
.setLineNumber(end.GetLineNumber())
.setColumnNumber(end.GetColumnNumber())
.build());
}
(*scopes)->addItem(std::move(scope));
}
return Response::OK();
}
} // namespace
V8DebuggerAgentImpl::V8DebuggerAgentImpl(
@ -240,8 +302,6 @@ Response V8DebuggerAgentImpl::disable() {
m_breakpointsActive = false;
}
m_debugger->disable();
JavaScriptCallFrames emptyCallFrames;
m_pausedCallFrames.swap(emptyCallFrames);
m_blackboxedPositions.clear();
m_blackboxPattern.reset();
resetBlackboxedStateCache();
@ -621,7 +681,7 @@ Response V8DebuggerAgentImpl::setScriptSource(
bool compileError = false;
Response response = m_debugger->setScriptSource(
scriptId, newSource, dryRun.fromMaybe(false), optOutCompileError,
&m_pausedCallFrames, stackChanged, &compileError);
stackChanged, &compileError);
if (!response.isSuccess() || compileError) return response;
it->second->setSource(newContent);
@ -641,21 +701,14 @@ Response V8DebuggerAgentImpl::restartFrame(
InjectedScript::CallFrameScope scope(m_session, callFrameId);
Response response = scope.initialize();
if (!response.isSuccess()) return response;
if (scope.frameOrdinal() >= m_pausedCallFrames.size())
int frameOrdinal = static_cast<int>(scope.frameOrdinal());
auto it = v8::debug::StackTraceIterator::Create(m_isolate, frameOrdinal);
if (it->Done()) {
return Response::Error("Could not find call frame with given id");
v8::Local<v8::Value> resultValue;
v8::Local<v8::Boolean> result;
if (!m_pausedCallFrames[scope.frameOrdinal()]->restart().ToLocal(
&resultValue) ||
scope.tryCatch().HasCaught() ||
!resultValue->ToBoolean(scope.context()).ToLocal(&result) ||
!result->Value()) {
}
if (!it->Restart()) {
return Response::InternalError();
}
JavaScriptCallFrames frames = m_debugger->currentCallFrames();
m_pausedCallFrames.swap(frames);
response = currentCallFrames(newCallFrames);
if (!response.isSuccess()) return response;
*asyncStackTrace = currentAsyncStackTrace();
@ -790,17 +843,16 @@ Response V8DebuggerAgentImpl::evaluateOnCallFrame(
InjectedScript::CallFrameScope scope(m_session, callFrameId);
Response response = scope.initialize();
if (!response.isSuccess()) return response;
if (scope.frameOrdinal() >= m_pausedCallFrames.size())
return Response::Error("Could not find call frame with given id");
if (includeCommandLineAPI.fromMaybe(false)) scope.installCommandLineAPI();
if (silent.fromMaybe(false)) scope.ignoreExceptionsAndMuteConsole();
v8::MaybeLocal<v8::Value> maybeResultValue =
m_pausedCallFrames[scope.frameOrdinal()]->evaluate(
toV8String(m_isolate, expression),
throwOnSideEffect.fromMaybe(false));
int frameOrdinal = static_cast<int>(scope.frameOrdinal());
auto it = v8::debug::StackTraceIterator::Create(m_isolate, frameOrdinal);
if (it->Done()) {
return Response::Error("Could not find call frame with given id");
}
v8::MaybeLocal<v8::Value> maybeResultValue = it->Evaluate(
toV8String(m_isolate, expression), throwOnSideEffect.fromMaybe(false));
// Re-initialize after running client's code, as it could have destroyed
// context or session.
response = scope.initialize();
@ -825,13 +877,24 @@ Response V8DebuggerAgentImpl::setVariableValue(
&newValue);
if (!response.isSuccess()) return response;
if (scope.frameOrdinal() >= m_pausedCallFrames.size())
int frameOrdinal = static_cast<int>(scope.frameOrdinal());
auto it = v8::debug::StackTraceIterator::Create(m_isolate, frameOrdinal);
if (it->Done()) {
return Response::Error("Could not find call frame with given id");
v8::MaybeLocal<v8::Value> result =
m_pausedCallFrames[scope.frameOrdinal()]->setVariableValue(
scopeNumber, toV8String(m_isolate, variableName), newValue);
if (scope.tryCatch().HasCaught() || result.IsEmpty())
}
auto scopeIterator = it->GetScopeIterator();
while (!scopeIterator->Done() && scopeNumber > 0) {
--scopeNumber;
scopeIterator->Advance();
}
if (scopeNumber != 0) {
return Response::Error("Could not find scope with given number");
}
if (!scopeIterator->SetVariableValue(toV8String(m_isolate, variableName),
newValue) ||
scope.tryCatch().HasCaught()) {
return Response::InternalError();
}
return Response::OK();
}
@ -929,106 +992,70 @@ Response V8DebuggerAgentImpl::currentCallFrames(
return Response::OK();
}
v8::HandleScope handles(m_isolate);
v8::Local<v8::Context> debuggerContext =
v8::debug::GetDebugContext(m_isolate);
v8::Context::Scope contextScope(debuggerContext);
v8::MicrotasksScope microtasks(m_isolate,
v8::MicrotasksScope::kDoNotRunMicrotasks);
v8::Local<v8::Array> objects = v8::Array::New(m_isolate);
for (size_t frameOrdinal = 0; frameOrdinal < m_pausedCallFrames.size();
++frameOrdinal) {
const std::unique_ptr<JavaScriptCallFrame>& currentCallFrame =
m_pausedCallFrames[frameOrdinal];
v8::Local<v8::Object> details;
if (!currentCallFrame->details().ToLocal(&details))
return Response::InternalError();
int contextId = currentCallFrame->contextId();
*result = Array<CallFrame>::create();
auto iterator = v8::debug::StackTraceIterator::Create(m_isolate);
int frameOrdinal = 0;
for (; !iterator->Done(); iterator->Advance(), frameOrdinal++) {
int contextId = iterator->GetContextId();
InjectedScript* injectedScript = nullptr;
if (contextId) m_session->findInjectedScript(contextId, injectedScript);
String16 callFrameId =
RemoteCallFrameId::serialize(contextId, static_cast<int>(frameOrdinal));
if (!details
->Set(debuggerContext,
toV8StringInternalized(m_isolate, "callFrameId"),
toV8String(m_isolate, callFrameId))
.FromMaybe(false)) {
return Response::InternalError();
}
RemoteCallFrameId::serialize(contextId, frameOrdinal);
v8::Local<v8::debug::Script> script = iterator->GetScript();
DCHECK(!script.IsEmpty());
v8::debug::Location loc = iterator->GetSourceLocation();
std::unique_ptr<Array<Scope>> scopes;
auto scopeIterator = iterator->GetScopeIterator();
Response res = buildScopes(scopeIterator.get(), injectedScript, &scopes);
if (!res.isSuccess()) return res;
std::unique_ptr<RemoteObject> receiver;
if (injectedScript) {
v8::Local<v8::Value> scopeChain;
if (!details
->Get(debuggerContext,
toV8StringInternalized(m_isolate, "scopeChain"))
.ToLocal(&scopeChain) ||
!scopeChain->IsArray()) {
return Response::InternalError();
}
v8::Local<v8::Array> scopeChainArray = scopeChain.As<v8::Array>();
Response response = injectedScript->wrapPropertyInArray(
scopeChainArray, toV8StringInternalized(m_isolate, "object"),
kBacktraceObjectGroup);
if (!response.isSuccess()) return response;
response = injectedScript->wrapObjectProperty(
details, toV8StringInternalized(m_isolate, "this"),
kBacktraceObjectGroup);
if (!response.isSuccess()) return response;
if (details
->Has(debuggerContext,
toV8StringInternalized(m_isolate, "returnValue"))
.FromMaybe(false)) {
response = injectedScript->wrapObjectProperty(
details, toV8StringInternalized(m_isolate, "returnValue"),
kBacktraceObjectGroup);
if (!response.isSuccess()) return response;
}
res = injectedScript->wrapObject(iterator->GetReceiver(),
kBacktraceObjectGroup, false, false,
&receiver);
if (!res.isSuccess()) return res;
} else {
if (!details
->Set(debuggerContext,
toV8StringInternalized(m_isolate, "scopeChain"),
v8::Array::New(m_isolate, 0))
.FromMaybe(false)) {
return Response::InternalError();
}
v8::Local<v8::Object> remoteObject = v8::Object::New(m_isolate);
if (!remoteObject
->Set(debuggerContext, toV8StringInternalized(m_isolate, "type"),
toV8StringInternalized(m_isolate, "undefined"))
.FromMaybe(false)) {
return Response::InternalError();
}
if (!details
->Set(debuggerContext, toV8StringInternalized(m_isolate, "this"),
remoteObject)
.FromMaybe(false)) {
return Response::InternalError();
}
if (!details
->Delete(debuggerContext,
toV8StringInternalized(m_isolate, "returnValue"))
.FromMaybe(false)) {
return Response::InternalError();
}
receiver = RemoteObject::create()
.setType(RemoteObject::TypeEnum::Undefined)
.build();
}
if (!objects->Set(debuggerContext, static_cast<int>(frameOrdinal), details)
.FromMaybe(false)) {
return Response::InternalError();
auto frame =
CallFrame::create()
.setCallFrameId(callFrameId)
.setFunctionName(toProtocolString(iterator->GetFunctionName()))
.setLocation(protocol::Debugger::Location::create()
.setScriptId(String16::fromInteger(script->Id()))
.setLineNumber(loc.GetLineNumber())
.setColumnNumber(loc.GetColumnNumber())
.build())
.setScopeChain(std::move(scopes))
.setThis(std::move(receiver))
.build();
v8::Local<v8::Function> func = iterator->GetFunction();
if (!func.IsEmpty()) {
frame->setFunctionLocation(
protocol::Debugger::Location::create()
.setScriptId(String16::fromInteger(func->ScriptId()))
.setLineNumber(func->GetScriptLineNumber())
.setColumnNumber(func->GetScriptColumnNumber())
.build());
}
v8::Local<v8::Value> returnValue = iterator->GetReturnValue();
if (!returnValue.IsEmpty() && injectedScript) {
std::unique_ptr<RemoteObject> value;
res = injectedScript->wrapObject(returnValue, kBacktraceObjectGroup,
false, false, &value);
if (!res.isSuccess()) return res;
frame->setReturnValue(std::move(value));
}
(*result)->addItem(std::move(frame));
}
std::unique_ptr<protocol::Value> protocolValue;
Response response = toProtocolValue(debuggerContext, objects, &protocolValue);
if (!response.isSuccess()) return response;
protocol::ErrorSupport errorSupport;
*result = Array<CallFrame>::fromValue(protocolValue.get(), &errorSupport);
if (!*result) return Response::Error(errorSupport.errors());
TranslateWasmStackTraceLocations(result->get(),
m_debugger->wasmTranslation());
return Response::OK();
@ -1153,8 +1180,6 @@ void V8DebuggerAgentImpl::didPause(int contextId,
const std::vector<String16>& hitBreakpoints,
bool isPromiseRejection, bool isUncaught,
bool isOOMBreak, bool isAssert) {
JavaScriptCallFrames frames = m_debugger->currentCallFrames();
m_pausedCallFrames.swap(frames);
v8::HandleScope handles(m_isolate);
std::vector<BreakReason> hitReasons;
@ -1243,8 +1268,6 @@ void V8DebuggerAgentImpl::didPause(int contextId,
}
void V8DebuggerAgentImpl::didContinue() {
JavaScriptCallFrames emptyCallFrames;
m_pausedCallFrames.swap(emptyCallFrames);
clearBreakDetails();
m_frontend.resumed();
}

View File

@ -9,7 +9,6 @@
#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"
@ -179,7 +178,6 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
protocol::DictionaryValue* m_state;
protocol::Debugger::Frontend m_frontend;
v8::Isolate* m_isolate;
JavaScriptCallFrames m_pausedCallFrames;
ScriptsMap m_scripts;
BreakpointIdToDebuggerBreakpointIdsMap m_breakpointIdToDebuggerBreakpointIds;
DebugServerBreakpointToBreakpointIdAndSourceMap m_serverBreakpoints;

View File

@ -134,6 +134,31 @@ void cleanupExpiredWeakPointers(Map& map) {
}
}
String16 scopeType(v8::debug::ScopeIterator::ScopeType type) {
switch (type) {
case v8::debug::ScopeIterator::ScopeTypeGlobal:
return protocol::Debugger::Scope::TypeEnum::Global;
case v8::debug::ScopeIterator::ScopeTypeLocal:
return protocol::Debugger::Scope::TypeEnum::Local;
case v8::debug::ScopeIterator::ScopeTypeWith:
return protocol::Debugger::Scope::TypeEnum::With;
case v8::debug::ScopeIterator::ScopeTypeClosure:
return protocol::Debugger::Scope::TypeEnum::Closure;
case v8::debug::ScopeIterator::ScopeTypeCatch:
return protocol::Debugger::Scope::TypeEnum::Catch;
case v8::debug::ScopeIterator::ScopeTypeBlock:
return protocol::Debugger::Scope::TypeEnum::Block;
case v8::debug::ScopeIterator::ScopeTypeScript:
return protocol::Debugger::Scope::TypeEnum::Script;
case v8::debug::ScopeIterator::ScopeTypeEval:
return protocol::Debugger::Scope::TypeEnum::Eval;
case v8::debug::ScopeIterator::ScopeTypeModule:
return protocol::Debugger::Scope::TypeEnum::Module;
}
UNREACHABLE();
return String16();
}
} // namespace
static bool inLiveEditScope = false;
@ -477,8 +502,7 @@ void V8Debugger::clearContinueToLocation() {
Response V8Debugger::setScriptSource(
const String16& sourceID, v8::Local<v8::String> newSource, bool dryRun,
Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails,
JavaScriptCallFrames* newCallFrames, Maybe<bool>* stackChanged,
bool* compileError) {
Maybe<bool>* stackChanged, bool* compileError) {
class EnableLiveEditScope {
public:
explicit EnableLiveEditScope(v8::Isolate* isolate) : m_isolate(isolate) {
@ -536,12 +560,6 @@ Response V8Debugger::setScriptSource(
.ToLocalChecked()
->BooleanValue(context)
.FromJust();
// Call stack may have changed after if the edited function was on the
// stack.
if (!dryRun && isPaused()) {
JavaScriptCallFrames frames = currentCallFrames();
newCallFrames->swap(frames);
}
return Response::OK();
}
// Compile error.
@ -571,30 +589,6 @@ Response V8Debugger::setScriptSource(
return Response::InternalError();
}
JavaScriptCallFrames V8Debugger::currentCallFrames(int limit) {
if (!isPaused()) return JavaScriptCallFrames();
v8::Local<v8::Value> currentCallFramesV8;
v8::Local<v8::Value> argv[] = {m_executionState,
v8::Integer::New(m_isolate, limit)};
if (!callDebuggerMethod("currentCallFrames", arraysize(argv), argv, true)
.ToLocal(&currentCallFramesV8)) {
return JavaScriptCallFrames();
}
if (!currentCallFramesV8->IsArray()) return JavaScriptCallFrames();
v8::Local<v8::Array> callFramesArray = currentCallFramesV8.As<v8::Array>();
JavaScriptCallFrames callFrames;
for (uint32_t i = 0; i < callFramesArray->Length(); ++i) {
v8::Local<v8::Value> callFrameValue;
if (!callFramesArray->Get(debuggerContext(), i).ToLocal(&callFrameValue))
return JavaScriptCallFrames();
if (!callFrameValue->IsObject()) return JavaScriptCallFrames();
v8::Local<v8::Object> callFrameObject = callFrameValue.As<v8::Object>();
callFrames.push_back(JavaScriptCallFrame::create(
debuggerContext(), v8::Local<v8::Object>::Cast(callFrameObject)));
}
return callFrames;
}
void V8Debugger::handleProgramBreak(v8::Local<v8::Context> pausedContext,
v8::Local<v8::Object> executionState,
v8::Local<v8::Value> exception,
@ -843,34 +837,54 @@ v8::MaybeLocal<v8::Value> V8Debugger::getTargetScopes(
if (!enabled()) {
UNREACHABLE();
}
v8::Local<v8::Value> argv[] = {value};
v8::Local<v8::Value> scopesValue;
const char* debuggerMethod = nullptr;
std::unique_ptr<v8::debug::ScopeIterator> iterator;
switch (kind) {
case FUNCTION:
debuggerMethod = "getFunctionScopes";
iterator = v8::debug::ScopeIterator::CreateForFunction(
m_isolate, v8::Local<v8::Function>::Cast(value));
break;
case GENERATOR:
debuggerMethod = "getGeneratorScopes";
v8::Local<v8::debug::GeneratorObject> generatorObject =
v8::debug::GeneratorObject::Cast(value);
if (!generatorObject->IsSuspended()) return v8::MaybeLocal<v8::Value>();
iterator = v8::debug::ScopeIterator::CreateForGeneratorObject(
m_isolate, v8::Local<v8::Object>::Cast(value));
break;
}
if (!callDebuggerMethod(debuggerMethod, 1, argv, true).ToLocal(&scopesValue))
v8::Local<v8::Array> result = v8::Array::New(m_isolate);
if (!result->SetPrototype(context, v8::Null(m_isolate)).FromMaybe(false)) {
return v8::MaybeLocal<v8::Value>();
v8::Local<v8::Value> copied;
if (!copyValueFromDebuggerContext(m_isolate, debuggerContext(), context,
scopesValue)
.ToLocal(&copied) ||
!copied->IsArray())
return v8::MaybeLocal<v8::Value>();
if (!markAsInternal(context, v8::Local<v8::Array>::Cast(copied),
}
for (; !iterator->Done(); iterator->Advance()) {
v8::Local<v8::Object> scope = v8::Object::New(m_isolate);
if (!markAsInternal(context, scope, V8InternalValueType::kScope)) {
return v8::MaybeLocal<v8::Value>();
}
String16 type = scopeType(iterator->GetType());
String16 name;
v8::Local<v8::Function> closure = iterator->GetFunction();
if (!closure.IsEmpty()) {
name = toProtocolStringWithTypeCheck(closure->GetDebugName());
}
v8::Local<v8::Object> object = iterator->GetObject();
createDataProperty(context, scope,
toV8StringInternalized(m_isolate, "type"),
toV8String(m_isolate, type));
createDataProperty(context, scope,
toV8StringInternalized(m_isolate, "name"),
toV8String(m_isolate, name));
createDataProperty(context, scope,
toV8StringInternalized(m_isolate, "object"), object);
createDataProperty(context, result, result->Length(), scope);
}
if (!markAsInternal(context, v8::Local<v8::Array>::Cast(result),
V8InternalValueType::kScopeList))
return v8::MaybeLocal<v8::Value>();
if (!markArrayEntriesAsInternal(context, v8::Local<v8::Array>::Cast(copied),
V8InternalValueType::kScope))
return v8::MaybeLocal<v8::Value>();
return copied;
return result;
}
v8::MaybeLocal<v8::Value> V8Debugger::functionScopes(

View File

@ -10,7 +10,6 @@
#include "src/base/macros.h"
#include "src/debug/debug-interface.h"
#include "src/inspector/java-script-call-frame.h"
#include "src/inspector/protocol/Debugger.h"
#include "src/inspector/protocol/Forward.h"
#include "src/inspector/protocol/Runtime.h"
@ -68,9 +67,7 @@ class V8Debugger : public v8::debug::DebugDelegate {
Response setScriptSource(
const String16& sourceID, v8::Local<v8::String> newSource, bool dryRun,
protocol::Maybe<protocol::Runtime::ExceptionDetails>*,
JavaScriptCallFrames* newCallFrames, protocol::Maybe<bool>* stackChanged,
bool* compileError);
JavaScriptCallFrames currentCallFrames(int limit = 0);
protocol::Maybe<bool>* stackChanged, bool* compileError);
// Each script inherits debug data from v8::Context where it has been
// compiled.

View File

@ -919,8 +919,12 @@
'debug/debug-interface.h',
'debug/debug-frames.cc',
'debug/debug-frames.h',
'debug/debug-scope-iterator.cc',
'debug/debug-scope-iterator.h',
'debug/debug-scopes.cc',
'debug/debug-scopes.h',
'debug/debug-stack-trace-iterator.cc',
'debug/debug-stack-trace-iterator.h',
'debug/debug.cc',
'debug/debug.h',
'debug/interface-types.h',

View File

@ -439,23 +439,14 @@ class InterpreterHandle {
interpreter()->UpdateMemory(mem_start, mem_size);
}
Handle<JSArray> GetScopeDetails(Address frame_pointer, int frame_index,
Handle<WasmDebugInfo> debug_info) {
auto frame = GetInterpretedFrame(frame_pointer, frame_index);
Handle<JSObject> GetGlobalScopeObject(wasm::InterpretedFrame* frame,
Handle<WasmDebugInfo> debug_info) {
Isolate* isolate = debug_info->GetIsolate();
Handle<WasmInstanceObject> instance(debug_info->wasm_instance(), isolate);
Handle<FixedArray> global_scope =
isolate_->factory()->NewFixedArray(ScopeIterator::kScopeDetailsSize);
global_scope->set(ScopeIterator::kScopeDetailsTypeIndex,
Smi::FromInt(ScopeIterator::ScopeTypeGlobal));
// TODO(clemensh): Add globals to the global scope.
Handle<JSObject> global_scope_object =
isolate_->factory()->NewJSObjectWithNullProto();
global_scope->set(ScopeIterator::kScopeDetailsObjectIndex,
*global_scope_object);
// TODO(clemensh): Add globals to the global scope.
if (instance->has_memory_buffer()) {
Handle<String> name = isolate_->factory()->InternalizeOneByteString(
STATIC_CHAR_VECTOR("memory"));
@ -468,16 +459,16 @@ class InterpreterHandle {
uint8_array, NONE)
.Assert();
}
return global_scope_object;
}
Handle<JSObject> GetLocalScopeObject(wasm::InterpretedFrame* frame,
Handle<WasmDebugInfo> debug_info) {
Isolate* isolate = debug_info->GetIsolate();
Handle<WasmInstanceObject> instance(debug_info->wasm_instance(), isolate);
Handle<FixedArray> local_scope =
isolate_->factory()->NewFixedArray(ScopeIterator::kScopeDetailsSize);
local_scope->set(ScopeIterator::kScopeDetailsTypeIndex,
Smi::FromInt(ScopeIterator::ScopeTypeLocal));
Handle<JSObject> local_scope_object =
isolate_->factory()->NewJSObjectWithNullProto();
local_scope->set(ScopeIterator::kScopeDetailsObjectIndex,
*local_scope_object);
// Fill parameters and locals.
int num_params = frame->GetParameterCount();
int num_locals = frame->GetLocalCount();
@ -527,6 +518,32 @@ class InterpreterHandle {
stack_obj, static_cast<uint32_t>(i), value_obj, NONE)
.Assert();
}
return local_scope_object;
}
Handle<JSArray> GetScopeDetails(Address frame_pointer, int frame_index,
Handle<WasmDebugInfo> debug_info) {
auto frame = GetInterpretedFrame(frame_pointer, frame_index);
Isolate* isolate = debug_info->GetIsolate();
Handle<WasmInstanceObject> instance(debug_info->wasm_instance(), isolate);
Handle<FixedArray> global_scope =
isolate_->factory()->NewFixedArray(ScopeIterator::kScopeDetailsSize);
global_scope->set(ScopeIterator::kScopeDetailsTypeIndex,
Smi::FromInt(ScopeIterator::ScopeTypeGlobal));
Handle<JSObject> global_scope_object =
GetGlobalScopeObject(frame.get(), debug_info);
global_scope->set(ScopeIterator::kScopeDetailsObjectIndex,
*global_scope_object);
Handle<FixedArray> local_scope =
isolate_->factory()->NewFixedArray(ScopeIterator::kScopeDetailsSize);
local_scope->set(ScopeIterator::kScopeDetailsTypeIndex,
Smi::FromInt(ScopeIterator::ScopeTypeLocal));
Handle<JSObject> local_scope_object =
GetLocalScopeObject(frame.get(), debug_info);
local_scope->set(ScopeIterator::kScopeDetailsObjectIndex,
*local_scope_object);
Handle<JSArray> global_jsarr =
isolate_->factory()->NewJSArrayWithElements(global_scope);
@ -738,9 +755,24 @@ void WasmDebugInfo::UpdateMemory(JSArrayBuffer* new_memory) {
}
// static
Handle<JSArray> WasmDebugInfo::GetScopeDetails(Handle<WasmDebugInfo> debug_info,
Address frame_pointer,
int frame_index) {
Handle<JSObject> WasmDebugInfo::GetScopeDetails(
Handle<WasmDebugInfo> debug_info, Address frame_pointer, int frame_index) {
InterpreterHandle* interp_handle = GetInterpreterHandle(*debug_info);
return interp_handle->GetScopeDetails(frame_pointer, frame_index, debug_info);
}
// static
Handle<JSObject> WasmDebugInfo::GetGlobalScopeObject(
Handle<WasmDebugInfo> debug_info, Address frame_pointer, int frame_index) {
InterpreterHandle* interp_handle = GetInterpreterHandle(*debug_info);
auto frame = interp_handle->GetInterpretedFrame(frame_pointer, frame_index);
return interp_handle->GetGlobalScopeObject(frame.get(), debug_info);
}
// static
Handle<JSObject> WasmDebugInfo::GetLocalScopeObject(
Handle<WasmDebugInfo> debug_info, Address frame_pointer, int frame_index) {
InterpreterHandle* interp_handle = GetInterpreterHandle(*debug_info);
auto frame = interp_handle->GetInterpretedFrame(frame_pointer, frame_index);
return interp_handle->GetLocalScopeObject(frame.get(), debug_info);
}

View File

@ -655,9 +655,15 @@ class WasmDebugInfo : public FixedArray {
// The global scope contains information about globals and the memory.
// The local scope contains information about parameters, locals, and stack
// values.
static Handle<JSArray> GetScopeDetails(Handle<WasmDebugInfo>,
Address frame_pointer,
int frame_index);
static Handle<JSObject> GetScopeDetails(Handle<WasmDebugInfo>,
Address frame_pointer,
int frame_index);
static Handle<JSObject> GetGlobalScopeObject(Handle<WasmDebugInfo>,
Address frame_pointer,
int frame_index);
static Handle<JSObject> GetLocalScopeObject(Handle<WasmDebugInfo>,
Address frame_pointer,
int frame_index);
};
// TODO(titzer): these should be moved to wasm-objects-inl.h

View File

@ -1,19 +0,0 @@
Locations in script with negative offset.
[
[0] : {
columnNumber : 16
lineNumber : 0
scriptId : <scriptId>
type : debuggerStatement
}
[1] : {
columnNumber : 26
lineNumber : 0
scriptId : <scriptId>
type : return
}
]
foo (:-1:16)
(anonymous) (:0:0)
boo (:0:16)
(anonymous) (:0:0)

View File

@ -1,31 +0,0 @@
// 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.
let {session, contextGroup, Protocol} =
InspectorTest.start('Locations in script with negative offset.');
(async function test() {
contextGroup.addScript(`function foo() { debugger; }
function boo(){ debugger; }
`, -1, -1);
session.setupScriptMap();
Protocol.Debugger.enable();
let {params:{scriptId}} = await Protocol.Debugger.onceScriptParsed();
let {result:{locations}} = await Protocol.Debugger.getPossibleBreakpoints({
start: {scriptId, lineNumber: 0, columnNumber: 0}
});
InspectorTest.logMessage(locations);
Protocol.Runtime.evaluate({expression: 'foo()'});
var {params:{callFrames}} = await Protocol.Debugger.oncePaused();
session.logCallFrames(callFrames);
await Protocol.Debugger.resume();
Protocol.Runtime.evaluate({expression: 'boo()'});
var {params:{callFrames}} = await Protocol.Debugger.oncePaused();
session.logCallFrames(callFrames);
await Protocol.Debugger.resume();
InspectorTest.completeTest();
})();