Skip to content

Commit 294d17a

Browse files
sergeyvCommit bot
authored andcommitted
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}
1 parent d582015 commit 294d17a

6 files changed

Lines changed: 181 additions & 38 deletions

File tree

src/ast/scopes.cc

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -818,23 +818,6 @@ Handle<ScopeInfo> Scope::GetScopeInfo(Isolate* isolate) {
818818
}
819819

820820

821-
void Scope::GetNestedScopeChain(Isolate* isolate,
822-
List<Handle<ScopeInfo> >* chain, int position) {
823-
if (!is_eval_scope()) chain->Add(Handle<ScopeInfo>(GetScopeInfo(isolate)));
824-
825-
for (int i = 0; i < inner_scopes_.length(); i++) {
826-
Scope* scope = inner_scopes_[i];
827-
int beg_pos = scope->start_position();
828-
int end_pos = scope->end_position();
829-
DCHECK(beg_pos >= 0 && end_pos >= 0);
830-
if (beg_pos <= position && position < end_pos) {
831-
scope->GetNestedScopeChain(isolate, chain, position);
832-
return;
833-
}
834-
}
835-
}
836-
837-
838821
void Scope::CollectNonLocals(HashMap* non_locals) {
839822
// Collect non-local variables referenced in the scope.
840823
// TODO(yangguo): store non-local variables explicitly if we can no longer

src/ast/scopes.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -556,13 +556,6 @@ class Scope: public ZoneObject {
556556

557557
Handle<ScopeInfo> GetScopeInfo(Isolate* isolate);
558558

559-
// Get the chain of nested scopes within this scope for the source statement
560-
// position. The scopes will be added to the list from the outermost scope to
561-
// the innermost scope. Only nested block, catch or with scopes are tracked
562-
// and will be returned, but no inner function scopes.
563-
void GetNestedScopeChain(Isolate* isolate, List<Handle<ScopeInfo> >* chain,
564-
int statement_position);
565-
566559
void CollectNonLocals(HashMap* non_locals);
567560

568561
// ---------------------------------------------------------------------------

src/debug/debug-scopes.cc

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,9 @@ ScopeIterator::ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector,
7373
}
7474
}
7575
if (scope_info->scope_type() == FUNCTION_SCOPE) {
76-
nested_scope_chain_.Add(scope_info);
76+
nested_scope_chain_.Add(ExtendedScopeInfo(scope_info,
77+
shared_info->start_position(),
78+
shared_info->end_position()));
7779
}
7880
if (!collect_non_locals) return;
7981
}
@@ -130,12 +132,34 @@ MUST_USE_RESULT MaybeHandle<JSObject> ScopeIterator::MaterializeScopeDetails() {
130132
Handle<JSObject> scope_object;
131133
ASSIGN_RETURN_ON_EXCEPTION(isolate_, scope_object, ScopeObject(), JSObject);
132134
details->set(kScopeDetailsObjectIndex, *scope_object);
133-
if (HasContext() && CurrentContext()->closure() != NULL) {
134-
Handle<String> closure_name = JSFunction::GetDebugName(
135-
Handle<JSFunction>(CurrentContext()->closure()));
135+
Handle<JSFunction> js_function = HasContext()
136+
? handle(CurrentContext()->closure())
137+
: Handle<JSFunction>::null();
138+
if (!js_function.is_null()) {
139+
Handle<String> closure_name = JSFunction::GetDebugName(js_function);
136140
if (!closure_name.is_null() && (closure_name->length() != 0))
137141
details->set(kScopeDetailsNameIndex, *closure_name);
138142
}
143+
if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript) {
144+
return isolate_->factory()->NewJSArrayWithElements(details);
145+
}
146+
147+
int start_position = 0;
148+
int end_position = 0;
149+
if (!nested_scope_chain_.is_empty()) {
150+
js_function = GetFunction();
151+
start_position = nested_scope_chain_.last().start_position;
152+
end_position = nested_scope_chain_.last().end_position;
153+
} else if (!js_function.is_null()) {
154+
start_position = js_function->shared()->start_position();
155+
end_position = js_function->shared()->end_position();
156+
}
157+
158+
if (!js_function.is_null()) {
159+
details->set(kScopeDetailsStartPositionIndex, Smi::FromInt(start_position));
160+
details->set(kScopeDetailsEndPositionIndex, Smi::FromInt(end_position));
161+
details->set(kScopeDetailsFunctionIndex, *js_function);
162+
}
139163
return isolate_->factory()->NewJSArrayWithElements(details);
140164
}
141165

@@ -155,7 +179,8 @@ void ScopeIterator::Next() {
155179
context_ = Handle<Context>(context_->previous(), isolate_);
156180
}
157181
if (!nested_scope_chain_.is_empty()) {
158-
DCHECK_EQ(nested_scope_chain_.last()->scope_type(), SCRIPT_SCOPE);
182+
DCHECK_EQ(nested_scope_chain_.last().scope_info->scope_type(),
183+
SCRIPT_SCOPE);
159184
nested_scope_chain_.RemoveLast();
160185
DCHECK(nested_scope_chain_.is_empty());
161186
}
@@ -165,7 +190,7 @@ void ScopeIterator::Next() {
165190
if (nested_scope_chain_.is_empty()) {
166191
context_ = Handle<Context>(context_->previous(), isolate_);
167192
} else {
168-
if (nested_scope_chain_.last()->HasContext()) {
193+
if (nested_scope_chain_.last().scope_info->HasContext()) {
169194
DCHECK(context_->previous() != NULL);
170195
context_ = Handle<Context>(context_->previous(), isolate_);
171196
}
@@ -178,7 +203,7 @@ void ScopeIterator::Next() {
178203
ScopeIterator::ScopeType ScopeIterator::Type() {
179204
DCHECK(!failed_);
180205
if (!nested_scope_chain_.is_empty()) {
181-
Handle<ScopeInfo> scope_info = nested_scope_chain_.last();
206+
Handle<ScopeInfo> scope_info = nested_scope_chain_.last().scope_info;
182207
switch (scope_info->scope_type()) {
183208
case FUNCTION_SCOPE:
184209
DCHECK(context_->IsFunctionContext() || !scope_info->HasContext());
@@ -262,7 +287,7 @@ bool ScopeIterator::HasContext() {
262287
ScopeType type = Type();
263288
if (type == ScopeTypeBlock || type == ScopeTypeLocal) {
264289
if (!nested_scope_chain_.is_empty()) {
265-
return nested_scope_chain_.last()->HasContext();
290+
return nested_scope_chain_.last().scope_info->HasContext();
266291
}
267292
}
268293
return true;
@@ -298,7 +323,7 @@ bool ScopeIterator::SetVariableValue(Handle<String> variable_name,
298323
Handle<ScopeInfo> ScopeIterator::CurrentScopeInfo() {
299324
DCHECK(!failed_);
300325
if (!nested_scope_chain_.is_empty()) {
301-
return nested_scope_chain_.last();
326+
return nested_scope_chain_.last().scope_info;
302327
} else if (context_->IsBlockContext()) {
303328
return Handle<ScopeInfo>(context_->scope_info());
304329
} else if (context_->IsFunctionContext()) {
@@ -313,7 +338,7 @@ Handle<Context> ScopeIterator::CurrentContext() {
313338
if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript ||
314339
nested_scope_chain_.is_empty()) {
315340
return context_;
316-
} else if (nested_scope_chain_.last()->HasContext()) {
341+
} else if (nested_scope_chain_.last().scope_info->HasContext()) {
317342
return context_;
318343
} else {
319344
return Handle<Context>();
@@ -409,7 +434,7 @@ void ScopeIterator::DebugPrint() {
409434
void ScopeIterator::RetrieveScopeChain(Scope* scope) {
410435
if (scope != NULL) {
411436
int source_position = frame_inspector_->GetSourcePosition();
412-
scope->GetNestedScopeChain(isolate_, &nested_scope_chain_, source_position);
437+
GetNestedScopeChain(isolate_, scope, source_position);
413438
} else {
414439
// A failed reparse indicates that the preparser has diverged from the
415440
// parser or that the preparse data given to the initial parse has been
@@ -541,7 +566,7 @@ Handle<JSObject> ScopeIterator::MaterializeBlockScope() {
541566

542567
Handle<Context> context = Handle<Context>::null();
543568
if (!nested_scope_chain_.is_empty()) {
544-
Handle<ScopeInfo> scope_info = nested_scope_chain_.last();
569+
Handle<ScopeInfo> scope_info = nested_scope_chain_.last().scope_info;
545570
frame_inspector_->MaterializeStackLocals(block_scope, scope_info);
546571
if (scope_info->HasContext()) context = CurrentContext();
547572
} else {
@@ -815,5 +840,24 @@ bool ScopeIterator::CopyContextExtensionToScopeObject(
815840
return true;
816841
}
817842

843+
void ScopeIterator::GetNestedScopeChain(Isolate* isolate, Scope* scope,
844+
int position) {
845+
if (!scope->is_eval_scope()) {
846+
nested_scope_chain_.Add(ExtendedScopeInfo(scope->GetScopeInfo(isolate),
847+
scope->start_position(),
848+
scope->end_position()));
849+
}
850+
for (int i = 0; i < scope->inner_scopes()->length(); i++) {
851+
Scope* inner_scope = scope->inner_scopes()->at(i);
852+
int beg_pos = inner_scope->start_position();
853+
int end_pos = inner_scope->end_position();
854+
DCHECK(beg_pos >= 0 && end_pos >= 0);
855+
if (beg_pos <= position && position < end_pos) {
856+
GetNestedScopeChain(isolate, inner_scope, position);
857+
return;
858+
}
859+
}
860+
}
861+
818862
} // namespace internal
819863
} // namespace v8

src/debug/debug-scopes.h

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@ class ScopeIterator {
3131
static const int kScopeDetailsTypeIndex = 0;
3232
static const int kScopeDetailsObjectIndex = 1;
3333
static const int kScopeDetailsNameIndex = 2;
34-
static const int kScopeDetailsSize = 3;
34+
static const int kScopeDetailsStartPositionIndex = 3;
35+
static const int kScopeDetailsEndPositionIndex = 4;
36+
static const int kScopeDetailsFunctionIndex = 5;
37+
static const int kScopeDetailsSize = 6;
3538

3639
enum Option { DEFAULT, IGNORE_NESTED_SCOPES, COLLECT_NON_LOCALS };
3740

@@ -83,10 +86,18 @@ class ScopeIterator {
8386
#endif
8487

8588
private:
89+
struct ExtendedScopeInfo {
90+
ExtendedScopeInfo(Handle<ScopeInfo> info, int start, int end)
91+
: scope_info(info), start_position(start), end_position(end) {}
92+
Handle<ScopeInfo> scope_info;
93+
int start_position;
94+
int end_position;
95+
};
96+
8697
Isolate* isolate_;
8798
FrameInspector* const frame_inspector_;
8899
Handle<Context> context_;
89-
List<Handle<ScopeInfo> > nested_scope_chain_;
100+
List<ExtendedScopeInfo> nested_scope_chain_;
90101
HashMap* non_locals_;
91102
bool seen_script_scope_;
92103
bool failed_;
@@ -140,6 +151,13 @@ class ScopeIterator {
140151
Handle<JSObject> scope_object,
141152
KeyCollectionType type);
142153

154+
// Get the chain of nested scopes within this scope for the source statement
155+
// position. The scopes will be added to the list from the outermost scope to
156+
// the innermost scope. Only nested block, catch or with scopes are tracked
157+
// and will be returned, but no inner function scopes.
158+
void GetNestedScopeChain(Isolate* isolate, Scope* scope,
159+
int statement_position);
160+
143161
DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
144162
};
145163

src/debug/mirrors.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2177,6 +2177,9 @@ FrameMirror.prototype.toText = function(opt_locals) {
21772177
var kScopeDetailsTypeIndex = 0;
21782178
var kScopeDetailsObjectIndex = 1;
21792179
var kScopeDetailsNameIndex = 2;
2180+
var kScopeDetailsStartPositionIndex = 3;
2181+
var kScopeDetailsEndPositionIndex = 4;
2182+
var kScopeDetailsFunctionIndex = 5;
21802183

21812184
function ScopeDetails(frame, fun, index, opt_details) {
21822185
if (frame) {
@@ -2221,6 +2224,29 @@ ScopeDetails.prototype.name = function() {
22212224
};
22222225

22232226

2227+
ScopeDetails.prototype.startPosition = function() {
2228+
if (!IS_UNDEFINED(this.break_id_)) {
2229+
%CheckExecutionState(this.break_id_);
2230+
}
2231+
return this.details_[kScopeDetailsStartPositionIndex];
2232+
}
2233+
2234+
2235+
ScopeDetails.prototype.endPosition = function() {
2236+
if (!IS_UNDEFINED(this.break_id_)) {
2237+
%CheckExecutionState(this.break_id_);
2238+
}
2239+
return this.details_[kScopeDetailsEndPositionIndex];
2240+
}
2241+
2242+
ScopeDetails.prototype.func = function() {
2243+
if (!IS_UNDEFINED(this.break_id_)) {
2244+
%CheckExecutionState(this.break_id_);
2245+
}
2246+
return this.details_[kScopeDetailsFunctionIndex];
2247+
}
2248+
2249+
22242250
ScopeDetails.prototype.setVariableValueImpl = function(name, new_value) {
22252251
var raw_res;
22262252
if (!IS_UNDEFINED(this.break_id_)) {

test/mjsunit/debug-scopes.js

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,21 @@ function CheckScopeContent(content, number, exec_state) {
223223
assertTrue(found, "Scope object " + response.body.object.ref + " not found");
224224
}
225225

226+
// Check that the scopes have positions as expected.
227+
function CheckScopeChainPositions(positions, exec_state) {
228+
var all_scopes = exec_state.frame().allScopes();
229+
assertEquals(positions.length, all_scopes.length, "FrameMirror.allScopes length");
230+
for (var i = 0; i < positions.length; i++) {
231+
var scope = exec_state.frame().scope(i);
232+
assertTrue(scope.isScope());
233+
var position = positions[i];
234+
if (!position)
235+
continue;
236+
237+
assertEquals(position.start, scope.details().startPosition())
238+
assertEquals(position.end, scope.details().endPosition())
239+
}
240+
}
226241

227242
// Simple empty local scope.
228243
BeginTest("Local 1");
@@ -1109,6 +1124,70 @@ listener_delegate = function(exec_state) {
11091124

11101125
EndTest();
11111126

1127+
BeginTest("Scope positions");
1128+
var code1 = "function f() { \n" +
1129+
" var a = 1; \n" +
1130+
" function b() { \n" +
1131+
" debugger; \n" +
1132+
" return a + 1; \n" +
1133+
" } \n" +
1134+
" b(); \n" +
1135+
"} \n" +
1136+
"f(); \n";
1137+
1138+
listener_delegate = function(exec_state) {
1139+
CheckScopeChainPositions([{start: 58, end: 118}, {start: 10, end: 162}, {}, {}], exec_state);
1140+
}
1141+
eval(code1);
1142+
EndTest();
1143+
1144+
1145+
function catch_block_2() {
1146+
try {
1147+
throw 'Exception';
1148+
} catch (e) {
1149+
with({n:10}) {
1150+
debugger;
1151+
}
1152+
}
1153+
};
1154+
1155+
BeginTest("Scope positions in catch and 'with' statement");
1156+
var code2 = "function catch_block() { \n" +
1157+
" try { \n" +
1158+
" throw 'Exception'; \n" +
1159+
" } catch (e) { \n" +
1160+
" with({n : 10}) { \n" +
1161+
" debugger; \n" +
1162+
" } \n" +
1163+
" } \n" +
1164+
"} \n" +
1165+
"catch_block(); \n";
1166+
1167+
listener_delegate = function(exec_state) {
1168+
CheckScopeChainPositions([{start: 131, end: 173}, {start: 94, end: 199}, {start: 20, end: 225}, {}, {}], exec_state);
1169+
}
1170+
eval(code2);
1171+
EndTest();
1172+
1173+
BeginTest("Scope positions in for statement");
1174+
var code3 = "function for_statement() { \n" +
1175+
" for (let i = 0; i < 1; i++) { \n" +
1176+
" debugger; \n" +
1177+
" } \n" +
1178+
"} \n" +
1179+
"for_statement(); \n";
1180+
1181+
listener_delegate = function(exec_state) {
1182+
CheckScopeChain([debug.ScopeType.Block,
1183+
debug.ScopeType.Block,
1184+
debug.ScopeType.Local,
1185+
debug.ScopeType.Script,
1186+
debug.ScopeType.Global], exec_state);
1187+
CheckScopeChainPositions([{start: 52, end: 111}, {start: 42, end: 111}, {start: 22, end: 145}, {}, {}], exec_state);
1188+
}
1189+
eval(code3);
1190+
EndTest();
11121191

11131192
assertEquals(begin_test_count, break_count,
11141193
'one or more tests did not enter the debugger');

0 commit comments

Comments
 (0)