[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:
parent
b4f1e24078
commit
c5e9416b1d
4
BUILD.gn
4
BUILD.gn
@ -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",
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
||||
|
203
src/debug/debug-scope-iterator.cc
Normal file
203
src/debug/debug-scope-iterator.cc
Normal 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
|
64
src/debug/debug-scope-iterator.h
Normal file
64
src/debug/debug-scope-iterator.h
Normal 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_
|
@ -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
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
153
src/debug/debug-stack-trace-iterator.cc
Normal file
153
src/debug/debug-stack-trace-iterator.cc
Normal 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
|
46
src/debug/debug-stack-trace-iterator.h
Normal file
46
src/debug/debug-stack-trace-iterator.h
Normal 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_
|
@ -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",
|
||||
|
@ -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;
|
||||
})();
|
||||
|
@ -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() {}
|
||||
|
@ -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',
|
||||
|
@ -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
|
@ -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_
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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(¤tCallFramesV8)) {
|
||||
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(
|
||||
|
@ -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.
|
||||
|
@ -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',
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
@ -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();
|
||||
})();
|
Loading…
Reference in New Issue
Block a user