• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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