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) {
}
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) {
// Collect non-local variables referenced in the scope.
// TODO(yangguo): store non-local variables explicitly if we can no longer
......
......@@ -556,13 +556,6 @@ class Scope: public ZoneObject {
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);
// ---------------------------------------------------------------------------
......
......@@ -73,7 +73,9 @@ ScopeIterator::ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector,
}
}
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;
}
......@@ -130,12 +132,34 @@ 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);
if (HasContext() && CurrentContext()->closure() != NULL) {
Handle<String> closure_name = JSFunction::GetDebugName(
Handle<JSFunction>(CurrentContext()->closure()));
Handle<JSFunction> js_function = HasContext()
? handle(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))
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);
}
......@@ -155,7 +179,8 @@ void ScopeIterator::Next() {
context_ = Handle<Context>(context_->previous(), isolate_);
}
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();
DCHECK(nested_scope_chain_.is_empty());
}
......@@ -165,7 +190,7 @@ void ScopeIterator::Next() {
if (nested_scope_chain_.is_empty()) {
context_ = Handle<Context>(context_->previous(), isolate_);
} else {
if (nested_scope_chain_.last()->HasContext()) {
if (nested_scope_chain_.last().scope_info->HasContext()) {
DCHECK(context_->previous() != NULL);
context_ = Handle<Context>(context_->previous(), isolate_);
}
......@@ -178,7 +203,7 @@ void ScopeIterator::Next() {
ScopeIterator::ScopeType ScopeIterator::Type() {
DCHECK(!failed_);
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()) {
case FUNCTION_SCOPE:
DCHECK(context_->IsFunctionContext() || !scope_info->HasContext());
......@@ -262,7 +287,7 @@ bool ScopeIterator::HasContext() {
ScopeType type = Type();
if (type == ScopeTypeBlock || type == ScopeTypeLocal) {
if (!nested_scope_chain_.is_empty()) {
return nested_scope_chain_.last()->HasContext();
return nested_scope_chain_.last().scope_info->HasContext();
}
}
return true;
......@@ -298,7 +323,7 @@ bool ScopeIterator::SetVariableValue(Handle<String> variable_name,
Handle<ScopeInfo> ScopeIterator::CurrentScopeInfo() {
DCHECK(!failed_);
if (!nested_scope_chain_.is_empty()) {
return nested_scope_chain_.last();
return nested_scope_chain_.last().scope_info;
} else if (context_->IsBlockContext()) {
return Handle<ScopeInfo>(context_->scope_info());
} else if (context_->IsFunctionContext()) {
......@@ -313,7 +338,7 @@ Handle<Context> ScopeIterator::CurrentContext() {
if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript ||
nested_scope_chain_.is_empty()) {
return context_;
} else if (nested_scope_chain_.last()->HasContext()) {
} else if (nested_scope_chain_.last().scope_info->HasContext()) {
return context_;
} else {
return Handle<Context>();
......@@ -409,7 +434,7 @@ void ScopeIterator::DebugPrint() {
void ScopeIterator::RetrieveScopeChain(Scope* scope) {
if (scope != NULL) {
int source_position = frame_inspector_->GetSourcePosition();
scope->GetNestedScopeChain(isolate_, &nested_scope_chain_, source_position);
GetNestedScopeChain(isolate_, scope, source_position);
} else {
// A failed reparse indicates that the preparser has diverged from the
// parser or that the preparse data given to the initial parse has been
......@@ -541,7 +566,7 @@ Handle<JSObject> ScopeIterator::MaterializeBlockScope() {
Handle<Context> context = Handle<Context>::null();
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);
if (scope_info->HasContext()) context = CurrentContext();
} else {
......@@ -815,5 +840,24 @@ bool ScopeIterator::CopyContextExtensionToScopeObject(
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 v8
......@@ -31,7 +31,10 @@ class ScopeIterator {
static const int kScopeDetailsTypeIndex = 0;
static const int kScopeDetailsObjectIndex = 1;
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 };
......@@ -83,10 +86,18 @@ class ScopeIterator {
#endif
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_;
FrameInspector* const frame_inspector_;
Handle<Context> context_;
List<Handle<ScopeInfo> > nested_scope_chain_;
List<ExtendedScopeInfo> nested_scope_chain_;
HashMap* non_locals_;
bool seen_script_scope_;
bool failed_;
......@@ -140,6 +151,13 @@ class ScopeIterator {
Handle<JSObject> scope_object,
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);
};
......
......@@ -2177,6 +2177,9 @@ FrameMirror.prototype.toText = function(opt_locals) {
var kScopeDetailsTypeIndex = 0;
var kScopeDetailsObjectIndex = 1;
var kScopeDetailsNameIndex = 2;
var kScopeDetailsStartPositionIndex = 3;
var kScopeDetailsEndPositionIndex = 4;
var kScopeDetailsFunctionIndex = 5;
function ScopeDetails(frame, fun, index, opt_details) {
if (frame) {
......@@ -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) {
var raw_res;
if (!IS_UNDEFINED(this.break_id_)) {
......
......@@ -223,6 +223,21 @@ function CheckScopeContent(content, number, exec_state) {
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.
BeginTest("Local 1");
......@@ -1109,6 +1124,70 @@ listener_delegate = function(exec_state) {
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,
'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