Commit 294d17a7 authored by sergeyv's avatar sergeyv Committed by Commit bot

Devtools: expose scopes source location to debugger

blink-side cl: https://codereview.chromium.org/1653053002/

BUG=327092
LOG=Y

Review URL: https://codereview.chromium.org/1653083002

Cr-Commit-Position: refs/heads/master@{#34417}
parent d5820158
...@@ -818,23 +818,6 @@ Handle<ScopeInfo> Scope::GetScopeInfo(Isolate* isolate) { ...@@ -818,23 +818,6 @@ Handle<ScopeInfo> Scope::GetScopeInfo(Isolate* isolate) {
} }
void Scope::GetNestedScopeChain(Isolate* isolate,
List<Handle<ScopeInfo> >* chain, int position) {
if (!is_eval_scope()) chain->Add(Handle<ScopeInfo>(GetScopeInfo(isolate)));
for (int i = 0; i < inner_scopes_.length(); i++) {
Scope* scope = inner_scopes_[i];
int beg_pos = scope->start_position();
int end_pos = scope->end_position();
DCHECK(beg_pos >= 0 && end_pos >= 0);
if (beg_pos <= position && position < end_pos) {
scope->GetNestedScopeChain(isolate, chain, position);
return;
}
}
}
void Scope::CollectNonLocals(HashMap* non_locals) { void Scope::CollectNonLocals(HashMap* non_locals) {
// Collect non-local variables referenced in the scope. // Collect non-local variables referenced in the scope.
// TODO(yangguo): store non-local variables explicitly if we can no longer // TODO(yangguo): store non-local variables explicitly if we can no longer
......
...@@ -556,13 +556,6 @@ class Scope: public ZoneObject { ...@@ -556,13 +556,6 @@ class Scope: public ZoneObject {
Handle<ScopeInfo> GetScopeInfo(Isolate* isolate); Handle<ScopeInfo> GetScopeInfo(Isolate* isolate);
// Get the chain of nested scopes within this scope for the source statement
// position. The scopes will be added to the list from the outermost scope to
// the innermost scope. Only nested block, catch or with scopes are tracked
// and will be returned, but no inner function scopes.
void GetNestedScopeChain(Isolate* isolate, List<Handle<ScopeInfo> >* chain,
int statement_position);
void CollectNonLocals(HashMap* non_locals); void CollectNonLocals(HashMap* non_locals);
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
......
...@@ -73,7 +73,9 @@ ScopeIterator::ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector, ...@@ -73,7 +73,9 @@ ScopeIterator::ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector,
} }
} }
if (scope_info->scope_type() == FUNCTION_SCOPE) { if (scope_info->scope_type() == FUNCTION_SCOPE) {
nested_scope_chain_.Add(scope_info); nested_scope_chain_.Add(ExtendedScopeInfo(scope_info,
shared_info->start_position(),
shared_info->end_position()));
} }
if (!collect_non_locals) return; if (!collect_non_locals) return;
} }
...@@ -130,12 +132,34 @@ MUST_USE_RESULT MaybeHandle<JSObject> ScopeIterator::MaterializeScopeDetails() { ...@@ -130,12 +132,34 @@ MUST_USE_RESULT MaybeHandle<JSObject> ScopeIterator::MaterializeScopeDetails() {
Handle<JSObject> scope_object; Handle<JSObject> scope_object;
ASSIGN_RETURN_ON_EXCEPTION(isolate_, scope_object, ScopeObject(), JSObject); ASSIGN_RETURN_ON_EXCEPTION(isolate_, scope_object, ScopeObject(), JSObject);
details->set(kScopeDetailsObjectIndex, *scope_object); details->set(kScopeDetailsObjectIndex, *scope_object);
if (HasContext() && CurrentContext()->closure() != NULL) { Handle<JSFunction> js_function = HasContext()
Handle<String> closure_name = JSFunction::GetDebugName( ? handle(CurrentContext()->closure())
Handle<JSFunction>(CurrentContext()->closure())); : Handle<JSFunction>::null();
if (!js_function.is_null()) {
Handle<String> closure_name = JSFunction::GetDebugName(js_function);
if (!closure_name.is_null() && (closure_name->length() != 0)) if (!closure_name.is_null() && (closure_name->length() != 0))
details->set(kScopeDetailsNameIndex, *closure_name); details->set(kScopeDetailsNameIndex, *closure_name);
} }
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();
}
if (!js_function.is_null()) {
details->set(kScopeDetailsStartPositionIndex, Smi::FromInt(start_position));
details->set(kScopeDetailsEndPositionIndex, Smi::FromInt(end_position));
details->set(kScopeDetailsFunctionIndex, *js_function);
}
return isolate_->factory()->NewJSArrayWithElements(details); return isolate_->factory()->NewJSArrayWithElements(details);
} }
...@@ -155,7 +179,8 @@ void ScopeIterator::Next() { ...@@ -155,7 +179,8 @@ void ScopeIterator::Next() {
context_ = Handle<Context>(context_->previous(), isolate_); context_ = Handle<Context>(context_->previous(), isolate_);
} }
if (!nested_scope_chain_.is_empty()) { if (!nested_scope_chain_.is_empty()) {
DCHECK_EQ(nested_scope_chain_.last()->scope_type(), SCRIPT_SCOPE); DCHECK_EQ(nested_scope_chain_.last().scope_info->scope_type(),
SCRIPT_SCOPE);
nested_scope_chain_.RemoveLast(); nested_scope_chain_.RemoveLast();
DCHECK(nested_scope_chain_.is_empty()); DCHECK(nested_scope_chain_.is_empty());
} }
...@@ -165,7 +190,7 @@ void ScopeIterator::Next() { ...@@ -165,7 +190,7 @@ void ScopeIterator::Next() {
if (nested_scope_chain_.is_empty()) { if (nested_scope_chain_.is_empty()) {
context_ = Handle<Context>(context_->previous(), isolate_); context_ = Handle<Context>(context_->previous(), isolate_);
} else { } else {
if (nested_scope_chain_.last()->HasContext()) { if (nested_scope_chain_.last().scope_info->HasContext()) {
DCHECK(context_->previous() != NULL); DCHECK(context_->previous() != NULL);
context_ = Handle<Context>(context_->previous(), isolate_); context_ = Handle<Context>(context_->previous(), isolate_);
} }
...@@ -178,7 +203,7 @@ void ScopeIterator::Next() { ...@@ -178,7 +203,7 @@ void ScopeIterator::Next() {
ScopeIterator::ScopeType ScopeIterator::Type() { ScopeIterator::ScopeType ScopeIterator::Type() {
DCHECK(!failed_); DCHECK(!failed_);
if (!nested_scope_chain_.is_empty()) { if (!nested_scope_chain_.is_empty()) {
Handle<ScopeInfo> scope_info = nested_scope_chain_.last(); Handle<ScopeInfo> scope_info = nested_scope_chain_.last().scope_info;
switch (scope_info->scope_type()) { switch (scope_info->scope_type()) {
case FUNCTION_SCOPE: case FUNCTION_SCOPE:
DCHECK(context_->IsFunctionContext() || !scope_info->HasContext()); DCHECK(context_->IsFunctionContext() || !scope_info->HasContext());
...@@ -262,7 +287,7 @@ bool ScopeIterator::HasContext() { ...@@ -262,7 +287,7 @@ bool ScopeIterator::HasContext() {
ScopeType type = Type(); ScopeType type = Type();
if (type == ScopeTypeBlock || type == ScopeTypeLocal) { if (type == ScopeTypeBlock || type == ScopeTypeLocal) {
if (!nested_scope_chain_.is_empty()) { if (!nested_scope_chain_.is_empty()) {
return nested_scope_chain_.last()->HasContext(); return nested_scope_chain_.last().scope_info->HasContext();
} }
} }
return true; return true;
...@@ -298,7 +323,7 @@ bool ScopeIterator::SetVariableValue(Handle<String> variable_name, ...@@ -298,7 +323,7 @@ bool ScopeIterator::SetVariableValue(Handle<String> variable_name,
Handle<ScopeInfo> ScopeIterator::CurrentScopeInfo() { Handle<ScopeInfo> ScopeIterator::CurrentScopeInfo() {
DCHECK(!failed_); DCHECK(!failed_);
if (!nested_scope_chain_.is_empty()) { if (!nested_scope_chain_.is_empty()) {
return nested_scope_chain_.last(); return nested_scope_chain_.last().scope_info;
} else if (context_->IsBlockContext()) { } else if (context_->IsBlockContext()) {
return Handle<ScopeInfo>(context_->scope_info()); return Handle<ScopeInfo>(context_->scope_info());
} else if (context_->IsFunctionContext()) { } else if (context_->IsFunctionContext()) {
...@@ -313,7 +338,7 @@ Handle<Context> ScopeIterator::CurrentContext() { ...@@ -313,7 +338,7 @@ Handle<Context> ScopeIterator::CurrentContext() {
if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript || if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript ||
nested_scope_chain_.is_empty()) { nested_scope_chain_.is_empty()) {
return context_; return context_;
} else if (nested_scope_chain_.last()->HasContext()) { } else if (nested_scope_chain_.last().scope_info->HasContext()) {
return context_; return context_;
} else { } else {
return Handle<Context>(); return Handle<Context>();
...@@ -409,7 +434,7 @@ void ScopeIterator::DebugPrint() { ...@@ -409,7 +434,7 @@ void ScopeIterator::DebugPrint() {
void ScopeIterator::RetrieveScopeChain(Scope* scope) { void ScopeIterator::RetrieveScopeChain(Scope* scope) {
if (scope != NULL) { if (scope != NULL) {
int source_position = frame_inspector_->GetSourcePosition(); int source_position = frame_inspector_->GetSourcePosition();
scope->GetNestedScopeChain(isolate_, &nested_scope_chain_, source_position); GetNestedScopeChain(isolate_, scope, source_position);
} else { } else {
// A failed reparse indicates that the preparser has diverged from the // A failed reparse indicates that the preparser has diverged from the
// parser or that the preparse data given to the initial parse has been // parser or that the preparse data given to the initial parse has been
...@@ -541,7 +566,7 @@ Handle<JSObject> ScopeIterator::MaterializeBlockScope() { ...@@ -541,7 +566,7 @@ Handle<JSObject> ScopeIterator::MaterializeBlockScope() {
Handle<Context> context = Handle<Context>::null(); Handle<Context> context = Handle<Context>::null();
if (!nested_scope_chain_.is_empty()) { if (!nested_scope_chain_.is_empty()) {
Handle<ScopeInfo> scope_info = nested_scope_chain_.last(); Handle<ScopeInfo> scope_info = nested_scope_chain_.last().scope_info;
frame_inspector_->MaterializeStackLocals(block_scope, scope_info); frame_inspector_->MaterializeStackLocals(block_scope, scope_info);
if (scope_info->HasContext()) context = CurrentContext(); if (scope_info->HasContext()) context = CurrentContext();
} else { } else {
...@@ -815,5 +840,24 @@ bool ScopeIterator::CopyContextExtensionToScopeObject( ...@@ -815,5 +840,24 @@ bool ScopeIterator::CopyContextExtensionToScopeObject(
return true; return true;
} }
void ScopeIterator::GetNestedScopeChain(Isolate* isolate, Scope* scope,
int position) {
if (!scope->is_eval_scope()) {
nested_scope_chain_.Add(ExtendedScopeInfo(scope->GetScopeInfo(isolate),
scope->start_position(),
scope->end_position()));
}
for (int i = 0; i < scope->inner_scopes()->length(); i++) {
Scope* inner_scope = scope->inner_scopes()->at(i);
int beg_pos = inner_scope->start_position();
int end_pos = inner_scope->end_position();
DCHECK(beg_pos >= 0 && end_pos >= 0);
if (beg_pos <= position && position < end_pos) {
GetNestedScopeChain(isolate, inner_scope, position);
return;
}
}
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -31,7 +31,10 @@ class ScopeIterator { ...@@ -31,7 +31,10 @@ class ScopeIterator {
static const int kScopeDetailsTypeIndex = 0; static const int kScopeDetailsTypeIndex = 0;
static const int kScopeDetailsObjectIndex = 1; static const int kScopeDetailsObjectIndex = 1;
static const int kScopeDetailsNameIndex = 2; static const int kScopeDetailsNameIndex = 2;
static const int kScopeDetailsSize = 3; static const int kScopeDetailsStartPositionIndex = 3;
static const int kScopeDetailsEndPositionIndex = 4;
static const int kScopeDetailsFunctionIndex = 5;
static const int kScopeDetailsSize = 6;
enum Option { DEFAULT, IGNORE_NESTED_SCOPES, COLLECT_NON_LOCALS }; enum Option { DEFAULT, IGNORE_NESTED_SCOPES, COLLECT_NON_LOCALS };
...@@ -83,10 +86,18 @@ class ScopeIterator { ...@@ -83,10 +86,18 @@ class ScopeIterator {
#endif #endif
private: private:
struct ExtendedScopeInfo {
ExtendedScopeInfo(Handle<ScopeInfo> info, int start, int end)
: scope_info(info), start_position(start), end_position(end) {}
Handle<ScopeInfo> scope_info;
int start_position;
int end_position;
};
Isolate* isolate_; Isolate* isolate_;
FrameInspector* const frame_inspector_; FrameInspector* const frame_inspector_;
Handle<Context> context_; Handle<Context> context_;
List<Handle<ScopeInfo> > nested_scope_chain_; List<ExtendedScopeInfo> nested_scope_chain_;
HashMap* non_locals_; HashMap* non_locals_;
bool seen_script_scope_; bool seen_script_scope_;
bool failed_; bool failed_;
...@@ -140,6 +151,13 @@ class ScopeIterator { ...@@ -140,6 +151,13 @@ class ScopeIterator {
Handle<JSObject> scope_object, Handle<JSObject> scope_object,
KeyCollectionType type); KeyCollectionType type);
// Get the chain of nested scopes within this scope for the source statement
// position. The scopes will be added to the list from the outermost scope to
// the innermost scope. Only nested block, catch or with scopes are tracked
// and will be returned, but no inner function scopes.
void GetNestedScopeChain(Isolate* isolate, Scope* scope,
int statement_position);
DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator); DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
}; };
......
...@@ -2177,6 +2177,9 @@ FrameMirror.prototype.toText = function(opt_locals) { ...@@ -2177,6 +2177,9 @@ FrameMirror.prototype.toText = function(opt_locals) {
var kScopeDetailsTypeIndex = 0; var kScopeDetailsTypeIndex = 0;
var kScopeDetailsObjectIndex = 1; var kScopeDetailsObjectIndex = 1;
var kScopeDetailsNameIndex = 2; var kScopeDetailsNameIndex = 2;
var kScopeDetailsStartPositionIndex = 3;
var kScopeDetailsEndPositionIndex = 4;
var kScopeDetailsFunctionIndex = 5;
function ScopeDetails(frame, fun, index, opt_details) { function ScopeDetails(frame, fun, index, opt_details) {
if (frame) { if (frame) {
...@@ -2221,6 +2224,29 @@ ScopeDetails.prototype.name = function() { ...@@ -2221,6 +2224,29 @@ ScopeDetails.prototype.name = function() {
}; };
ScopeDetails.prototype.startPosition = function() {
if (!IS_UNDEFINED(this.break_id_)) {
%CheckExecutionState(this.break_id_);
}
return this.details_[kScopeDetailsStartPositionIndex];
}
ScopeDetails.prototype.endPosition = function() {
if (!IS_UNDEFINED(this.break_id_)) {
%CheckExecutionState(this.break_id_);
}
return this.details_[kScopeDetailsEndPositionIndex];
}
ScopeDetails.prototype.func = function() {
if (!IS_UNDEFINED(this.break_id_)) {
%CheckExecutionState(this.break_id_);
}
return this.details_[kScopeDetailsFunctionIndex];
}
ScopeDetails.prototype.setVariableValueImpl = function(name, new_value) { ScopeDetails.prototype.setVariableValueImpl = function(name, new_value) {
var raw_res; var raw_res;
if (!IS_UNDEFINED(this.break_id_)) { if (!IS_UNDEFINED(this.break_id_)) {
......
...@@ -223,6 +223,21 @@ function CheckScopeContent(content, number, exec_state) { ...@@ -223,6 +223,21 @@ function CheckScopeContent(content, number, exec_state) {
assertTrue(found, "Scope object " + response.body.object.ref + " not found"); assertTrue(found, "Scope object " + response.body.object.ref + " not found");
} }
// Check that the scopes have positions as expected.
function CheckScopeChainPositions(positions, exec_state) {
var all_scopes = exec_state.frame().allScopes();
assertEquals(positions.length, all_scopes.length, "FrameMirror.allScopes length");
for (var i = 0; i < positions.length; i++) {
var scope = exec_state.frame().scope(i);
assertTrue(scope.isScope());
var position = positions[i];
if (!position)
continue;
assertEquals(position.start, scope.details().startPosition())
assertEquals(position.end, scope.details().endPosition())
}
}
// Simple empty local scope. // Simple empty local scope.
BeginTest("Local 1"); BeginTest("Local 1");
...@@ -1109,6 +1124,70 @@ listener_delegate = function(exec_state) { ...@@ -1109,6 +1124,70 @@ listener_delegate = function(exec_state) {
EndTest(); EndTest();
BeginTest("Scope positions");
var code1 = "function f() { \n" +
" var a = 1; \n" +
" function b() { \n" +
" debugger; \n" +
" return a + 1; \n" +
" } \n" +
" b(); \n" +
"} \n" +
"f(); \n";
listener_delegate = function(exec_state) {
CheckScopeChainPositions([{start: 58, end: 118}, {start: 10, end: 162}, {}, {}], exec_state);
}
eval(code1);
EndTest();
function catch_block_2() {
try {
throw 'Exception';
} catch (e) {
with({n:10}) {
debugger;
}
}
};
BeginTest("Scope positions in catch and 'with' statement");
var code2 = "function catch_block() { \n" +
" try { \n" +
" throw 'Exception'; \n" +
" } catch (e) { \n" +
" with({n : 10}) { \n" +
" debugger; \n" +
" } \n" +
" } \n" +
"} \n" +
"catch_block(); \n";
listener_delegate = function(exec_state) {
CheckScopeChainPositions([{start: 131, end: 173}, {start: 94, end: 199}, {start: 20, end: 225}, {}, {}], exec_state);
}
eval(code2);
EndTest();
BeginTest("Scope positions in for statement");
var code3 = "function for_statement() { \n" +
" for (let i = 0; i < 1; i++) { \n" +
" debugger; \n" +
" } \n" +
"} \n" +
"for_statement(); \n";
listener_delegate = function(exec_state) {
CheckScopeChain([debug.ScopeType.Block,
debug.ScopeType.Block,
debug.ScopeType.Local,
debug.ScopeType.Script,
debug.ScopeType.Global], exec_state);
CheckScopeChainPositions([{start: 52, end: 111}, {start: 42, end: 111}, {start: 22, end: 145}, {}, {}], exec_state);
}
eval(code3);
EndTest();
assertEquals(begin_test_count, break_count, assertEquals(begin_test_count, break_count,
'one or more tests did not enter the debugger'); 'one or more tests did not enter the debugger');
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment