1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/debug/debug-scopes.h"
6
7 #include <memory>
8
9 #include "src/ast/ast.h"
10 #include "src/ast/scopes.h"
11 #include "src/debug/debug.h"
12 #include "src/frames-inl.h"
13 #include "src/globals.h"
14 #include "src/isolate-inl.h"
15 #include "src/parsing/parse-info.h"
16 #include "src/parsing/parsing.h"
17 #include "src/parsing/rewriter.h"
18
19 namespace v8 {
20 namespace internal {
21
ScopeIterator(Isolate * isolate,FrameInspector * frame_inspector,ScopeIterator::Option option)22 ScopeIterator::ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector,
23 ScopeIterator::Option option)
24 : isolate_(isolate),
25 frame_inspector_(frame_inspector),
26 nested_scope_chain_(4),
27 seen_script_scope_(false) {
28 if (!frame_inspector->GetContext()->IsContext()) {
29 // Optimized frame, context or function cannot be materialized. Give up.
30 return;
31 }
32
33 context_ = Handle<Context>::cast(frame_inspector->GetContext());
34
35 // We should not instantiate a ScopeIterator for wasm frames.
36 DCHECK(frame_inspector->GetScript()->type() != Script::TYPE_WASM);
37
38 // Catch the case when the debugger stops in an internal function.
39 Handle<JSFunction> function = GetFunction();
40 Handle<SharedFunctionInfo> shared_info(function->shared());
41 Handle<ScopeInfo> scope_info(shared_info->scope_info());
42 if (shared_info->script()->IsUndefined(isolate)) {
43 while (context_->closure() == *function) {
44 context_ = Handle<Context>(context_->previous(), isolate_);
45 }
46 return;
47 }
48
49 // Currently it takes too much time to find nested scopes due to script
50 // parsing. Sometimes we want to run the ScopeIterator as fast as possible
51 // (for example, while collecting async call stacks on every
52 // addEventListener call), even if we drop some nested scopes.
53 // Later we may optimize getting the nested scopes (cache the result?)
54 // and include nested scopes into the "fast" iteration case as well.
55 bool ignore_nested_scopes = (option == IGNORE_NESTED_SCOPES);
56 bool collect_non_locals = (option == COLLECT_NON_LOCALS);
57 if (!ignore_nested_scopes && shared_info->HasDebugInfo()) {
58 // The source position at return is always the end of the function,
59 // which is not consistent with the current scope chain. Therefore all
60 // nested with, catch and block contexts are skipped, and we can only
61 // inspect the function scope.
62 // This can only happen if we set a break point inside right before the
63 // return, which requires a debug info to be available.
64 Handle<DebugInfo> debug_info(shared_info->GetDebugInfo());
65
66 // Find the break point where execution has stopped.
67 BreakLocation location = BreakLocation::FromFrame(debug_info, GetFrame());
68
69 ignore_nested_scopes = location.IsReturn();
70 }
71
72 if (ignore_nested_scopes) {
73 if (scope_info->HasContext()) {
74 context_ = Handle<Context>(context_->declaration_context(), isolate_);
75 } else {
76 while (context_->closure() == *function) {
77 context_ = Handle<Context>(context_->previous(), isolate_);
78 }
79 }
80 if (scope_info->scope_type() == FUNCTION_SCOPE) {
81 nested_scope_chain_.Add(ExtendedScopeInfo(scope_info,
82 shared_info->start_position(),
83 shared_info->end_position()));
84 }
85 if (!collect_non_locals) return;
86 }
87
88 // Reparse the code and analyze the scopes.
89 // Check whether we are in global, eval or function code.
90 std::unique_ptr<ParseInfo> info;
91 if (scope_info->scope_type() != FUNCTION_SCOPE) {
92 // Global or eval code.
93 Handle<Script> script(Script::cast(shared_info->script()));
94 info.reset(new ParseInfo(script));
95 if (scope_info->scope_type() == EVAL_SCOPE) {
96 info->set_eval();
97 if (!function->context()->IsNativeContext()) {
98 info->set_outer_scope_info(handle(function->context()->scope_info()));
99 }
100 // Language mode may be inherited from the eval caller.
101 // Retrieve it from shared function info.
102 info->set_language_mode(shared_info->language_mode());
103 } else if (scope_info->scope_type() == MODULE_SCOPE) {
104 info->set_module();
105 } else {
106 DCHECK(scope_info->scope_type() == SCRIPT_SCOPE);
107 }
108 } else {
109 // Inner function.
110 info.reset(new ParseInfo(shared_info));
111 }
112 if (parsing::ParseAny(info.get()) && Rewriter::Rewrite(info.get())) {
113 DeclarationScope* scope = info->literal()->scope();
114 if (!ignore_nested_scopes || collect_non_locals) {
115 CollectNonLocals(info.get(), scope);
116 }
117 if (!ignore_nested_scopes) {
118 DeclarationScope::Analyze(info.get(), AnalyzeMode::kDebugger);
119 RetrieveScopeChain(scope);
120 }
121 } else {
122 // A failed reparse indicates that the preparser has diverged from the
123 // parser or that the preparse data given to the initial parse has been
124 // faulty. We fail in debug mode but in release mode we only provide the
125 // information we get from the context chain but nothing about
126 // completely stack allocated scopes or stack allocated locals.
127 // Or it could be due to stack overflow.
128 // Silently fail by presenting an empty context chain.
129 CHECK(isolate_->has_pending_exception());
130 isolate_->clear_pending_exception();
131 context_ = Handle<Context>();
132 }
133 UnwrapEvaluationContext();
134 }
135
ScopeIterator(Isolate * isolate,Handle<JSFunction> function)136 ScopeIterator::ScopeIterator(Isolate* isolate, Handle<JSFunction> function)
137 : isolate_(isolate),
138 frame_inspector_(NULL),
139 context_(function->context()),
140 seen_script_scope_(false) {
141 if (!function->shared()->IsSubjectToDebugging()) context_ = Handle<Context>();
142 UnwrapEvaluationContext();
143 }
144
ScopeIterator(Isolate * isolate,Handle<JSGeneratorObject> generator)145 ScopeIterator::ScopeIterator(Isolate* isolate,
146 Handle<JSGeneratorObject> generator)
147 : isolate_(isolate),
148 frame_inspector_(NULL),
149 context_(generator->context()),
150 seen_script_scope_(false) {
151 if (!generator->function()->shared()->IsSubjectToDebugging()) {
152 context_ = Handle<Context>();
153 }
154 UnwrapEvaluationContext();
155 }
156
UnwrapEvaluationContext()157 void ScopeIterator::UnwrapEvaluationContext() {
158 while (true) {
159 if (context_.is_null()) return;
160 if (!context_->IsDebugEvaluateContext()) return;
161 Handle<Object> wrapped(context_->get(Context::WRAPPED_CONTEXT_INDEX),
162 isolate_);
163 if (wrapped->IsContext()) {
164 context_ = Handle<Context>::cast(wrapped);
165 } else {
166 context_ = Handle<Context>(context_->previous(), isolate_);
167 }
168 }
169 }
170
171
MaterializeScopeDetails()172 MUST_USE_RESULT MaybeHandle<JSObject> ScopeIterator::MaterializeScopeDetails() {
173 // Calculate the size of the result.
174 Handle<FixedArray> details =
175 isolate_->factory()->NewFixedArray(kScopeDetailsSize);
176 // Fill in scope details.
177 details->set(kScopeDetailsTypeIndex, Smi::FromInt(Type()));
178 Handle<JSObject> scope_object;
179 ASSIGN_RETURN_ON_EXCEPTION(isolate_, scope_object, ScopeObject(), JSObject);
180 details->set(kScopeDetailsObjectIndex, *scope_object);
181 Handle<JSFunction> js_function = HasContext()
182 ? handle(CurrentContext()->closure())
183 : Handle<JSFunction>::null();
184 if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript) {
185 return isolate_->factory()->NewJSArrayWithElements(details);
186 }
187
188 int start_position = 0;
189 int end_position = 0;
190 if (!nested_scope_chain_.is_empty()) {
191 js_function = GetFunction();
192 start_position = nested_scope_chain_.last().start_position;
193 end_position = nested_scope_chain_.last().end_position;
194 } else if (!js_function.is_null()) {
195 start_position = js_function->shared()->start_position();
196 end_position = js_function->shared()->end_position();
197 }
198
199 if (!js_function.is_null()) {
200 Handle<String> closure_name = JSFunction::GetDebugName(js_function);
201 if (!closure_name.is_null() && closure_name->length() != 0) {
202 details->set(kScopeDetailsNameIndex, *closure_name);
203 }
204 details->set(kScopeDetailsStartPositionIndex, Smi::FromInt(start_position));
205 details->set(kScopeDetailsEndPositionIndex, Smi::FromInt(end_position));
206 details->set(kScopeDetailsFunctionIndex, *js_function);
207 }
208 return isolate_->factory()->NewJSArrayWithElements(details);
209 }
210
211
Next()212 void ScopeIterator::Next() {
213 DCHECK(!Done());
214 ScopeType scope_type = Type();
215 if (scope_type == ScopeTypeGlobal) {
216 // The global scope is always the last in the chain.
217 DCHECK(context_->IsNativeContext());
218 context_ = Handle<Context>();
219 } else if (scope_type == ScopeTypeScript) {
220 seen_script_scope_ = true;
221 if (context_->IsScriptContext()) {
222 context_ = Handle<Context>(context_->previous(), isolate_);
223 }
224 if (!nested_scope_chain_.is_empty()) {
225 DCHECK_EQ(nested_scope_chain_.last().scope_info->scope_type(),
226 SCRIPT_SCOPE);
227 nested_scope_chain_.RemoveLast();
228 DCHECK(nested_scope_chain_.is_empty());
229 }
230 CHECK(context_->IsNativeContext());
231 } else if (nested_scope_chain_.is_empty()) {
232 context_ = Handle<Context>(context_->previous(), isolate_);
233 } else {
234 do {
235 if (nested_scope_chain_.last().scope_info->HasContext()) {
236 DCHECK(context_->previous() != NULL);
237 context_ = Handle<Context>(context_->previous(), isolate_);
238 }
239 nested_scope_chain_.RemoveLast();
240 if (nested_scope_chain_.is_empty()) break;
241 // Repeat to skip hidden scopes.
242 } while (nested_scope_chain_.last().is_hidden());
243 }
244 UnwrapEvaluationContext();
245 }
246
247
248 // Return the type of the current scope.
Type()249 ScopeIterator::ScopeType ScopeIterator::Type() {
250 DCHECK(!Done());
251 if (!nested_scope_chain_.is_empty()) {
252 Handle<ScopeInfo> scope_info = nested_scope_chain_.last().scope_info;
253 switch (scope_info->scope_type()) {
254 case FUNCTION_SCOPE:
255 DCHECK(context_->IsFunctionContext() || !scope_info->HasContext());
256 return ScopeTypeLocal;
257 case MODULE_SCOPE:
258 DCHECK(context_->IsModuleContext());
259 return ScopeTypeModule;
260 case SCRIPT_SCOPE:
261 DCHECK(context_->IsScriptContext() || context_->IsNativeContext());
262 return ScopeTypeScript;
263 case WITH_SCOPE:
264 DCHECK(context_->IsWithContext() || context_->IsDebugEvaluateContext());
265 return ScopeTypeWith;
266 case CATCH_SCOPE:
267 DCHECK(context_->IsCatchContext());
268 return ScopeTypeCatch;
269 case BLOCK_SCOPE:
270 DCHECK(!scope_info->HasContext() || context_->IsBlockContext());
271 return ScopeTypeBlock;
272 case EVAL_SCOPE:
273 DCHECK(!scope_info->HasContext() || context_->IsEvalContext());
274 return ScopeTypeEval;
275 }
276 UNREACHABLE();
277 }
278 if (context_->IsNativeContext()) {
279 DCHECK(context_->global_object()->IsJSGlobalObject());
280 // If we are at the native context and have not yet seen script scope,
281 // fake it.
282 return seen_script_scope_ ? ScopeTypeGlobal : ScopeTypeScript;
283 }
284 if (context_->IsFunctionContext() || context_->IsEvalContext()) {
285 return ScopeTypeClosure;
286 }
287 if (context_->IsCatchContext()) {
288 return ScopeTypeCatch;
289 }
290 if (context_->IsBlockContext()) {
291 return ScopeTypeBlock;
292 }
293 if (context_->IsModuleContext()) {
294 return ScopeTypeModule;
295 }
296 if (context_->IsScriptContext()) {
297 return ScopeTypeScript;
298 }
299 DCHECK(context_->IsWithContext() || context_->IsDebugEvaluateContext());
300 return ScopeTypeWith;
301 }
302
303
ScopeObject()304 MaybeHandle<JSObject> ScopeIterator::ScopeObject() {
305 DCHECK(!Done());
306 switch (Type()) {
307 case ScopeIterator::ScopeTypeGlobal:
308 return Handle<JSObject>(CurrentContext()->global_proxy());
309 case ScopeIterator::ScopeTypeScript:
310 return MaterializeScriptScope();
311 case ScopeIterator::ScopeTypeLocal:
312 // Materialize the content of the local scope into a JSObject.
313 DCHECK(nested_scope_chain_.length() == 1);
314 return MaterializeLocalScope();
315 case ScopeIterator::ScopeTypeWith:
316 return WithContextExtension();
317 case ScopeIterator::ScopeTypeCatch:
318 return MaterializeCatchScope();
319 case ScopeIterator::ScopeTypeClosure:
320 // Materialize the content of the closure scope into a JSObject.
321 return MaterializeClosure();
322 case ScopeIterator::ScopeTypeBlock:
323 case ScopeIterator::ScopeTypeEval:
324 return MaterializeInnerScope();
325 case ScopeIterator::ScopeTypeModule:
326 return MaterializeModuleScope();
327 }
328 UNREACHABLE();
329 return Handle<JSObject>();
330 }
331
332
HasContext()333 bool ScopeIterator::HasContext() {
334 ScopeType type = Type();
335 if (type == ScopeTypeBlock || type == ScopeTypeLocal ||
336 type == ScopeTypeEval) {
337 if (!nested_scope_chain_.is_empty()) {
338 return nested_scope_chain_.last().scope_info->HasContext();
339 }
340 }
341 return true;
342 }
343
344
SetVariableValue(Handle<String> variable_name,Handle<Object> new_value)345 bool ScopeIterator::SetVariableValue(Handle<String> variable_name,
346 Handle<Object> new_value) {
347 DCHECK(!Done());
348 switch (Type()) {
349 case ScopeIterator::ScopeTypeGlobal:
350 break;
351 case ScopeIterator::ScopeTypeLocal:
352 return SetLocalVariableValue(variable_name, new_value);
353 case ScopeIterator::ScopeTypeWith:
354 break;
355 case ScopeIterator::ScopeTypeCatch:
356 return SetCatchVariableValue(variable_name, new_value);
357 case ScopeIterator::ScopeTypeClosure:
358 return SetClosureVariableValue(variable_name, new_value);
359 case ScopeIterator::ScopeTypeScript:
360 return SetScriptVariableValue(variable_name, new_value);
361 case ScopeIterator::ScopeTypeBlock:
362 case ScopeIterator::ScopeTypeEval:
363 return SetInnerScopeVariableValue(variable_name, new_value);
364 case ScopeIterator::ScopeTypeModule:
365 // TODO(neis): Implement.
366 break;
367 }
368 return false;
369 }
370
371
CurrentScopeInfo()372 Handle<ScopeInfo> ScopeIterator::CurrentScopeInfo() {
373 DCHECK(!Done());
374 if (!nested_scope_chain_.is_empty()) {
375 return nested_scope_chain_.last().scope_info;
376 } else if (context_->IsBlockContext() || context_->IsFunctionContext() ||
377 context_->IsEvalContext()) {
378 return Handle<ScopeInfo>(context_->scope_info());
379 }
380 return Handle<ScopeInfo>::null();
381 }
382
383
CurrentContext()384 Handle<Context> ScopeIterator::CurrentContext() {
385 DCHECK(!Done());
386 if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript ||
387 nested_scope_chain_.is_empty()) {
388 return context_;
389 } else if (nested_scope_chain_.last().scope_info->HasContext()) {
390 return context_;
391 } else {
392 return Handle<Context>();
393 }
394 }
395
GetNonLocals()396 Handle<StringSet> ScopeIterator::GetNonLocals() { return non_locals_; }
397
398 #ifdef DEBUG
399 // Debug print of the content of the current scope.
DebugPrint()400 void ScopeIterator::DebugPrint() {
401 OFStream os(stdout);
402 DCHECK(!Done());
403 switch (Type()) {
404 case ScopeIterator::ScopeTypeGlobal:
405 os << "Global:\n";
406 CurrentContext()->Print(os);
407 break;
408
409 case ScopeIterator::ScopeTypeLocal: {
410 os << "Local:\n";
411 GetFunction()->shared()->scope_info()->Print();
412 if (!CurrentContext().is_null()) {
413 CurrentContext()->Print(os);
414 if (CurrentContext()->has_extension()) {
415 Handle<HeapObject> extension(CurrentContext()->extension(), isolate_);
416 if (extension->IsJSContextExtensionObject()) {
417 extension->Print(os);
418 }
419 }
420 }
421 break;
422 }
423
424 case ScopeIterator::ScopeTypeWith:
425 os << "With:\n";
426 CurrentContext()->extension()->Print(os);
427 break;
428
429 case ScopeIterator::ScopeTypeCatch:
430 os << "Catch:\n";
431 CurrentContext()->extension()->Print(os);
432 CurrentContext()->get(Context::THROWN_OBJECT_INDEX)->Print(os);
433 break;
434
435 case ScopeIterator::ScopeTypeClosure:
436 os << "Closure:\n";
437 CurrentContext()->Print(os);
438 if (CurrentContext()->has_extension()) {
439 Handle<HeapObject> extension(CurrentContext()->extension(), isolate_);
440 if (extension->IsJSContextExtensionObject()) {
441 extension->Print(os);
442 }
443 }
444 break;
445
446 case ScopeIterator::ScopeTypeScript:
447 os << "Script:\n";
448 CurrentContext()
449 ->global_object()
450 ->native_context()
451 ->script_context_table()
452 ->Print(os);
453 break;
454
455 default:
456 UNREACHABLE();
457 }
458 PrintF("\n");
459 }
460 #endif
461
RetrieveScopeChain(DeclarationScope * scope)462 void ScopeIterator::RetrieveScopeChain(DeclarationScope* scope) {
463 DCHECK_NOT_NULL(scope);
464 int source_position = frame_inspector_->GetSourcePosition();
465 GetNestedScopeChain(isolate_, scope, source_position);
466 }
467
CollectNonLocals(ParseInfo * info,DeclarationScope * scope)468 void ScopeIterator::CollectNonLocals(ParseInfo* info, DeclarationScope* scope) {
469 DCHECK_NOT_NULL(scope);
470 DCHECK(non_locals_.is_null());
471 non_locals_ = scope->CollectNonLocals(info, StringSet::New(isolate_));
472 }
473
474
MaterializeScriptScope()475 MaybeHandle<JSObject> ScopeIterator::MaterializeScriptScope() {
476 Handle<JSGlobalObject> global(CurrentContext()->global_object());
477 Handle<ScriptContextTable> script_contexts(
478 global->native_context()->script_context_table());
479
480 Handle<JSObject> script_scope =
481 isolate_->factory()->NewJSObjectWithNullProto();
482
483 for (int context_index = 0; context_index < script_contexts->used();
484 context_index++) {
485 Handle<Context> context =
486 ScriptContextTable::GetContext(script_contexts, context_index);
487 Handle<ScopeInfo> scope_info(context->scope_info());
488 CopyContextLocalsToScopeObject(scope_info, context, script_scope);
489 }
490 return script_scope;
491 }
492
493
MaterializeLocalScope()494 MaybeHandle<JSObject> ScopeIterator::MaterializeLocalScope() {
495 Handle<JSFunction> function = GetFunction();
496
497 Handle<JSObject> local_scope =
498 isolate_->factory()->NewJSObjectWithNullProto();
499 frame_inspector_->MaterializeStackLocals(local_scope, function);
500
501 Handle<Context> frame_context =
502 Handle<Context>::cast(frame_inspector_->GetContext());
503
504 HandleScope scope(isolate_);
505 Handle<SharedFunctionInfo> shared(function->shared());
506 Handle<ScopeInfo> scope_info(shared->scope_info());
507
508 if (!scope_info->HasContext()) return local_scope;
509
510 // Fill all context locals.
511 Handle<Context> function_context(frame_context->closure_context());
512 CopyContextLocalsToScopeObject(scope_info, function_context, local_scope);
513
514 // Finally copy any properties from the function context extension.
515 // These will be variables introduced by eval.
516 if (function_context->closure() == *function &&
517 !function_context->IsNativeContext()) {
518 CopyContextExtensionToScopeObject(function_context, local_scope,
519 KeyCollectionMode::kIncludePrototypes);
520 }
521
522 return local_scope;
523 }
524
525
526 // Create a plain JSObject which materializes the closure content for the
527 // context.
MaterializeClosure()528 Handle<JSObject> ScopeIterator::MaterializeClosure() {
529 Handle<Context> context = CurrentContext();
530 DCHECK(context->IsFunctionContext() || context->IsEvalContext());
531
532 Handle<SharedFunctionInfo> shared(context->closure()->shared());
533 Handle<ScopeInfo> scope_info(shared->scope_info());
534
535 // Allocate and initialize a JSObject with all the content of this function
536 // closure.
537 Handle<JSObject> closure_scope =
538 isolate_->factory()->NewJSObjectWithNullProto();
539
540 // Fill all context locals to the context extension.
541 CopyContextLocalsToScopeObject(scope_info, context, closure_scope);
542
543 // Finally copy any properties from the function context extension. This will
544 // be variables introduced by eval.
545 CopyContextExtensionToScopeObject(context, closure_scope,
546 KeyCollectionMode::kOwnOnly);
547
548 return closure_scope;
549 }
550
551
552 // Create a plain JSObject which materializes the scope for the specified
553 // catch context.
MaterializeCatchScope()554 Handle<JSObject> ScopeIterator::MaterializeCatchScope() {
555 Handle<Context> context = CurrentContext();
556 DCHECK(context->IsCatchContext());
557 Handle<String> name(context->catch_name());
558 Handle<Object> thrown_object(context->get(Context::THROWN_OBJECT_INDEX),
559 isolate_);
560 Handle<JSObject> catch_scope =
561 isolate_->factory()->NewJSObjectWithNullProto();
562 JSObject::SetOwnPropertyIgnoreAttributes(catch_scope, name, thrown_object,
563 NONE)
564 .Check();
565 return catch_scope;
566 }
567
568 // Retrieve the with-context extension object. If the extension object is
569 // a proxy, return an empty object.
WithContextExtension()570 Handle<JSObject> ScopeIterator::WithContextExtension() {
571 Handle<Context> context = CurrentContext();
572 DCHECK(context->IsWithContext());
573 if (context->extension_receiver()->IsJSProxy()) {
574 return isolate_->factory()->NewJSObjectWithNullProto();
575 }
576 return handle(JSObject::cast(context->extension_receiver()));
577 }
578
579 // Create a plain JSObject which materializes the block scope for the specified
580 // block context.
MaterializeInnerScope()581 Handle<JSObject> ScopeIterator::MaterializeInnerScope() {
582 Handle<JSObject> inner_scope =
583 isolate_->factory()->NewJSObjectWithNullProto();
584
585 Handle<Context> context = Handle<Context>::null();
586 if (!nested_scope_chain_.is_empty()) {
587 Handle<ScopeInfo> scope_info = nested_scope_chain_.last().scope_info;
588 frame_inspector_->MaterializeStackLocals(inner_scope, scope_info);
589 if (scope_info->HasContext()) context = CurrentContext();
590 } else {
591 context = CurrentContext();
592 }
593
594 if (!context.is_null()) {
595 // Fill all context locals.
596 CopyContextLocalsToScopeObject(CurrentScopeInfo(), context, inner_scope);
597 CopyContextExtensionToScopeObject(context, inner_scope,
598 KeyCollectionMode::kOwnOnly);
599 }
600 return inner_scope;
601 }
602
603
604 // Create a plain JSObject which materializes the module scope for the specified
605 // module context.
MaterializeModuleScope()606 MaybeHandle<JSObject> ScopeIterator::MaterializeModuleScope() {
607 Handle<Context> context = CurrentContext();
608 DCHECK(context->IsModuleContext());
609 Handle<ScopeInfo> scope_info(context->scope_info());
610 Handle<JSObject> module_scope =
611 isolate_->factory()->NewJSObjectWithNullProto();
612 CopyContextLocalsToScopeObject(scope_info, context, module_scope);
613 CopyModuleVarsToScopeObject(scope_info, context, module_scope);
614 return module_scope;
615 }
616
SetParameterValue(Handle<ScopeInfo> scope_info,JavaScriptFrame * frame,Handle<String> parameter_name,Handle<Object> new_value)617 bool ScopeIterator::SetParameterValue(Handle<ScopeInfo> scope_info,
618 JavaScriptFrame* frame,
619 Handle<String> parameter_name,
620 Handle<Object> new_value) {
621 // Setting stack locals of optimized frames is not supported.
622 if (frame->is_optimized()) return false;
623 HandleScope scope(isolate_);
624 for (int i = 0; i < scope_info->ParameterCount(); ++i) {
625 if (String::Equals(handle(scope_info->ParameterName(i)), parameter_name)) {
626 frame->SetParameterValue(i, *new_value);
627 return true;
628 }
629 }
630 return false;
631 }
632
SetStackVariableValue(Handle<ScopeInfo> scope_info,Handle<String> variable_name,Handle<Object> new_value)633 bool ScopeIterator::SetStackVariableValue(Handle<ScopeInfo> scope_info,
634 Handle<String> variable_name,
635 Handle<Object> new_value) {
636 if (frame_inspector_ == nullptr) return false;
637 JavaScriptFrame* frame = GetFrame();
638 // Setting stack locals of optimized frames is not supported.
639 if (frame->is_optimized()) return false;
640 HandleScope scope(isolate_);
641 for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
642 if (String::Equals(handle(scope_info->StackLocalName(i)), variable_name)) {
643 frame->SetExpression(scope_info->StackLocalIndex(i), *new_value);
644 return true;
645 }
646 }
647 return false;
648 }
649
SetContextVariableValue(Handle<ScopeInfo> scope_info,Handle<Context> context,Handle<String> variable_name,Handle<Object> new_value)650 bool ScopeIterator::SetContextVariableValue(Handle<ScopeInfo> scope_info,
651 Handle<Context> context,
652 Handle<String> variable_name,
653 Handle<Object> new_value) {
654 HandleScope scope(isolate_);
655 for (int i = 0; i < scope_info->ContextLocalCount(); i++) {
656 Handle<String> next_name(scope_info->ContextLocalName(i));
657 if (String::Equals(variable_name, next_name)) {
658 VariableMode mode;
659 InitializationFlag init_flag;
660 MaybeAssignedFlag maybe_assigned_flag;
661 int context_index = ScopeInfo::ContextSlotIndex(
662 scope_info, next_name, &mode, &init_flag, &maybe_assigned_flag);
663 context->set(context_index, *new_value);
664 return true;
665 }
666 }
667
668 if (context->has_extension()) {
669 Handle<JSObject> ext(context->extension_object());
670 Maybe<bool> maybe = JSReceiver::HasOwnProperty(ext, variable_name);
671 DCHECK(maybe.IsJust());
672 if (maybe.FromJust()) {
673 // We don't expect this to do anything except replacing property value.
674 JSObject::SetOwnPropertyIgnoreAttributes(ext, variable_name, new_value,
675 NONE)
676 .Check();
677 return true;
678 }
679 }
680
681 return false;
682 }
683
SetLocalVariableValue(Handle<String> variable_name,Handle<Object> new_value)684 bool ScopeIterator::SetLocalVariableValue(Handle<String> variable_name,
685 Handle<Object> new_value) {
686 JavaScriptFrame* frame = GetFrame();
687 Handle<ScopeInfo> scope_info(frame->function()->shared()->scope_info());
688
689 // Parameter might be shadowed in context. Don't stop here.
690 bool result = SetParameterValue(scope_info, frame, variable_name, new_value);
691
692 // Stack locals.
693 if (SetStackVariableValue(scope_info, variable_name, new_value)) {
694 return true;
695 }
696
697 if (scope_info->HasContext() &&
698 SetContextVariableValue(scope_info, CurrentContext(), variable_name,
699 new_value)) {
700 return true;
701 }
702
703 return result;
704 }
705
SetInnerScopeVariableValue(Handle<String> variable_name,Handle<Object> new_value)706 bool ScopeIterator::SetInnerScopeVariableValue(Handle<String> variable_name,
707 Handle<Object> new_value) {
708 Handle<ScopeInfo> scope_info = CurrentScopeInfo();
709 DCHECK(scope_info->scope_type() == BLOCK_SCOPE ||
710 scope_info->scope_type() == EVAL_SCOPE);
711
712 // Setting stack locals of optimized frames is not supported.
713 if (SetStackVariableValue(scope_info, variable_name, new_value)) {
714 return true;
715 }
716
717 if (HasContext() && SetContextVariableValue(scope_info, CurrentContext(),
718 variable_name, new_value)) {
719 return true;
720 }
721
722 return false;
723 }
724
725 // This method copies structure of MaterializeClosure method above.
SetClosureVariableValue(Handle<String> variable_name,Handle<Object> new_value)726 bool ScopeIterator::SetClosureVariableValue(Handle<String> variable_name,
727 Handle<Object> new_value) {
728 DCHECK(CurrentContext()->IsFunctionContext() ||
729 CurrentContext()->IsEvalContext());
730 return SetContextVariableValue(CurrentScopeInfo(), CurrentContext(),
731 variable_name, new_value);
732 }
733
SetScriptVariableValue(Handle<String> variable_name,Handle<Object> new_value)734 bool ScopeIterator::SetScriptVariableValue(Handle<String> variable_name,
735 Handle<Object> new_value) {
736 Handle<String> internalized_variable_name =
737 isolate_->factory()->InternalizeString(variable_name);
738 Handle<Context> context = CurrentContext();
739 Handle<ScriptContextTable> script_contexts(
740 context->global_object()->native_context()->script_context_table());
741 ScriptContextTable::LookupResult lookup_result;
742 if (ScriptContextTable::Lookup(script_contexts, internalized_variable_name,
743 &lookup_result)) {
744 Handle<Context> script_context = ScriptContextTable::GetContext(
745 script_contexts, lookup_result.context_index);
746 script_context->set(lookup_result.slot_index, *new_value);
747 return true;
748 }
749
750 return false;
751 }
752
SetCatchVariableValue(Handle<String> variable_name,Handle<Object> new_value)753 bool ScopeIterator::SetCatchVariableValue(Handle<String> variable_name,
754 Handle<Object> new_value) {
755 Handle<Context> context = CurrentContext();
756 DCHECK(context->IsCatchContext());
757 Handle<String> name(context->catch_name());
758 if (!String::Equals(name, variable_name)) {
759 return false;
760 }
761 context->set(Context::THROWN_OBJECT_INDEX, *new_value);
762 return true;
763 }
764
765
CopyContextLocalsToScopeObject(Handle<ScopeInfo> scope_info,Handle<Context> context,Handle<JSObject> scope_object)766 void ScopeIterator::CopyContextLocalsToScopeObject(
767 Handle<ScopeInfo> scope_info, Handle<Context> context,
768 Handle<JSObject> scope_object) {
769 Isolate* isolate = scope_info->GetIsolate();
770 int local_count = scope_info->ContextLocalCount();
771 if (local_count == 0) return;
772 // Fill all context locals to the context extension.
773 for (int i = 0; i < local_count; ++i) {
774 Handle<String> name(scope_info->ContextLocalName(i));
775 if (ScopeInfo::VariableIsSynthetic(*name)) continue;
776 int context_index = Context::MIN_CONTEXT_SLOTS + i;
777 Handle<Object> value = Handle<Object>(context->get(context_index), isolate);
778 // Reflect variables under TDZ as undefined in scope object.
779 if (value->IsTheHole(isolate)) continue;
780 // This should always succeed.
781 // TODO(verwaest): Use AddDataProperty instead.
782 JSObject::SetOwnPropertyIgnoreAttributes(scope_object, name, value, NONE)
783 .Check();
784 }
785 }
786
CopyModuleVarsToScopeObject(Handle<ScopeInfo> scope_info,Handle<Context> context,Handle<JSObject> scope_object)787 void ScopeIterator::CopyModuleVarsToScopeObject(Handle<ScopeInfo> scope_info,
788 Handle<Context> context,
789 Handle<JSObject> scope_object) {
790 Isolate* isolate = scope_info->GetIsolate();
791
792 int module_variable_count =
793 Smi::cast(scope_info->get(scope_info->ModuleVariableCountIndex()))
794 ->value();
795 for (int i = 0; i < module_variable_count; ++i) {
796 Handle<String> local_name;
797 Handle<Object> value;
798 {
799 String* name;
800 int index;
801 scope_info->ModuleVariable(i, &name, &index);
802 CHECK(!ScopeInfo::VariableIsSynthetic(name));
803 local_name = handle(name, isolate);
804 value = Module::LoadVariable(handle(context->module(), isolate), index);
805 }
806
807 // Reflect variables under TDZ as undefined in scope object.
808 if (value->IsTheHole(isolate)) continue;
809 // This should always succeed.
810 // TODO(verwaest): Use AddDataProperty instead.
811 JSObject::SetOwnPropertyIgnoreAttributes(scope_object, local_name, value,
812 NONE)
813 .Check();
814 }
815 }
816
CopyContextExtensionToScopeObject(Handle<Context> context,Handle<JSObject> scope_object,KeyCollectionMode mode)817 void ScopeIterator::CopyContextExtensionToScopeObject(
818 Handle<Context> context, Handle<JSObject> scope_object,
819 KeyCollectionMode mode) {
820 if (context->extension_object() == nullptr) return;
821 Handle<JSObject> extension(context->extension_object());
822 Handle<FixedArray> keys =
823 KeyAccumulator::GetKeys(extension, mode, ENUMERABLE_STRINGS)
824 .ToHandleChecked();
825
826 for (int i = 0; i < keys->length(); i++) {
827 // Names of variables introduced by eval are strings.
828 DCHECK(keys->get(i)->IsString());
829 Handle<String> key(String::cast(keys->get(i)));
830 Handle<Object> value =
831 Object::GetPropertyOrElement(extension, key).ToHandleChecked();
832 JSObject::SetOwnPropertyIgnoreAttributes(scope_object, key, value, NONE)
833 .Check();
834 }
835 }
836
GetNestedScopeChain(Isolate * isolate,Scope * scope,int position)837 void ScopeIterator::GetNestedScopeChain(Isolate* isolate, Scope* scope,
838 int position) {
839 if (scope->is_function_scope()) {
840 // Do not collect scopes of nested inner functions inside the current one.
841 // Nested arrow functions could have the same end positions.
842 Handle<JSFunction> function = frame_inspector_->GetFunction();
843 if (scope->start_position() > function->shared()->start_position() &&
844 scope->end_position() <= function->shared()->end_position()) {
845 return;
846 }
847 }
848 if (scope->is_hidden()) {
849 // We need to add this chain element in case the scope has a context
850 // associated. We need to keep the scope chain and context chain in sync.
851 nested_scope_chain_.Add(ExtendedScopeInfo(scope->scope_info()));
852 } else {
853 nested_scope_chain_.Add(ExtendedScopeInfo(
854 scope->scope_info(), scope->start_position(), scope->end_position()));
855 }
856 for (Scope* inner_scope = scope->inner_scope(); inner_scope != nullptr;
857 inner_scope = inner_scope->sibling()) {
858 int beg_pos = inner_scope->start_position();
859 int end_pos = inner_scope->end_position();
860 DCHECK((beg_pos >= 0 && end_pos >= 0) || inner_scope->is_hidden());
861 if (beg_pos <= position && position < end_pos) {
862 GetNestedScopeChain(isolate, inner_scope, position);
863 return;
864 }
865 }
866 }
867
868 } // namespace internal
869 } // namespace v8
870