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/scopes.h"
10 #include "src/debug/debug.h"
11 #include "src/frames-inl.h"
12 #include "src/globals.h"
13 #include "src/isolate-inl.h"
14 #include "src/parsing/parse-info.h"
15 #include "src/parsing/parser.h"
16 #include "src/parsing/rewriter.h"
17 
18 namespace v8 {
19 namespace internal {
20 
ScopeIterator(Isolate * isolate,FrameInspector * frame_inspector,ScopeIterator::Option option)21 ScopeIterator::ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector,
22                              ScopeIterator::Option option)
23     : isolate_(isolate),
24       frame_inspector_(frame_inspector),
25       nested_scope_chain_(4),
26       seen_script_scope_(false),
27       failed_(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   Zone zone(isolate->allocator(), ZONE_NAME);
91   std::unique_ptr<ParseInfo> info;
92   if (scope_info->scope_type() != FUNCTION_SCOPE) {
93     // Global or eval code.
94     Handle<Script> script(Script::cast(shared_info->script()));
95     info.reset(new ParseInfo(&zone, script));
96     if (scope_info->scope_type() == EVAL_SCOPE) {
97       info->set_eval();
98       if (!function->context()->IsNativeContext()) {
99         info->set_outer_scope_info(handle(function->context()->scope_info()));
100       }
101       // Language mode may be inherited from the eval caller.
102       // Retrieve it from shared function info.
103       info->set_language_mode(shared_info->language_mode());
104     } else if (scope_info->scope_type() == MODULE_SCOPE) {
105       info->set_module();
106     } else {
107       DCHECK(scope_info->scope_type() == SCRIPT_SCOPE);
108     }
109   } else {
110     // Inner function.
111     info.reset(new ParseInfo(&zone, shared_info));
112   }
113   if (Parser::ParseStatic(info.get()) && Rewriter::Rewrite(info.get())) {
114     DeclarationScope* scope = info->literal()->scope();
115     if (!ignore_nested_scopes || collect_non_locals) {
116       CollectNonLocals(info.get(), scope);
117     }
118     if (!ignore_nested_scopes) {
119       DeclarationScope::Analyze(info.get(), AnalyzeMode::kDebugger);
120       RetrieveScopeChain(scope);
121     }
122   } else if (!ignore_nested_scopes) {
123     // A failed reparse indicates that the preparser has diverged from the
124     // parser or that the preparse data given to the initial parse has been
125     // faulty. We fail in debug mode but in release mode we only provide the
126     // information we get from the context chain but nothing about
127     // completely stack allocated scopes or stack allocated locals.
128     // Or it could be due to stack overflow.
129     DCHECK(isolate_->has_pending_exception());
130     failed_ = true;
131   }
132   UnwrapEvaluationContext();
133 }
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       failed_(false) {
142   if (!function->shared()->IsSubjectToDebugging()) context_ = Handle<Context>();
143   UnwrapEvaluationContext();
144 }
145 
ScopeIterator(Isolate * isolate,Handle<JSGeneratorObject> generator)146 ScopeIterator::ScopeIterator(Isolate* isolate,
147                              Handle<JSGeneratorObject> generator)
148     : isolate_(isolate),
149       frame_inspector_(NULL),
150       context_(generator->context()),
151       seen_script_scope_(false),
152       failed_(false) {
153   if (!generator->function()->shared()->IsSubjectToDebugging()) {
154     context_ = Handle<Context>();
155   }
156   UnwrapEvaluationContext();
157 }
158 
UnwrapEvaluationContext()159 void ScopeIterator::UnwrapEvaluationContext() {
160   while (true) {
161     if (context_.is_null()) return;
162     if (!context_->IsDebugEvaluateContext()) return;
163     Handle<Object> wrapped(context_->get(Context::WRAPPED_CONTEXT_INDEX),
164                            isolate_);
165     if (wrapped->IsContext()) {
166       context_ = Handle<Context>::cast(wrapped);
167     } else {
168       context_ = Handle<Context>(context_->previous(), isolate_);
169     }
170   }
171 }
172 
173 
MaterializeScopeDetails()174 MUST_USE_RESULT MaybeHandle<JSObject> ScopeIterator::MaterializeScopeDetails() {
175   // Calculate the size of the result.
176   Handle<FixedArray> details =
177       isolate_->factory()->NewFixedArray(kScopeDetailsSize);
178   // Fill in scope details.
179   details->set(kScopeDetailsTypeIndex, Smi::FromInt(Type()));
180   Handle<JSObject> scope_object;
181   ASSIGN_RETURN_ON_EXCEPTION(isolate_, scope_object, ScopeObject(), JSObject);
182   details->set(kScopeDetailsObjectIndex, *scope_object);
183   Handle<JSFunction> js_function = HasContext()
184                                        ? handle(CurrentContext()->closure())
185                                        : Handle<JSFunction>::null();
186   if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript) {
187     return isolate_->factory()->NewJSArrayWithElements(details);
188   }
189 
190   int start_position = 0;
191   int end_position = 0;
192   if (!nested_scope_chain_.is_empty()) {
193     js_function = GetFunction();
194     start_position = nested_scope_chain_.last().start_position;
195     end_position = nested_scope_chain_.last().end_position;
196   } else if (!js_function.is_null()) {
197     start_position = js_function->shared()->start_position();
198     end_position = js_function->shared()->end_position();
199   }
200 
201   if (!js_function.is_null()) {
202     Handle<String> closure_name = JSFunction::GetDebugName(js_function);
203     if (!closure_name.is_null() && closure_name->length() != 0) {
204       details->set(kScopeDetailsNameIndex, *closure_name);
205     }
206     details->set(kScopeDetailsStartPositionIndex, Smi::FromInt(start_position));
207     details->set(kScopeDetailsEndPositionIndex, Smi::FromInt(end_position));
208     details->set(kScopeDetailsFunctionIndex, *js_function);
209   }
210   return isolate_->factory()->NewJSArrayWithElements(details);
211 }
212 
213 
Next()214 void ScopeIterator::Next() {
215   DCHECK(!failed_);
216   ScopeType scope_type = Type();
217   if (scope_type == ScopeTypeGlobal) {
218     // The global scope is always the last in the chain.
219     DCHECK(context_->IsNativeContext());
220     context_ = Handle<Context>();
221   } else if (scope_type == ScopeTypeScript) {
222     seen_script_scope_ = true;
223     if (context_->IsScriptContext()) {
224       context_ = Handle<Context>(context_->previous(), isolate_);
225     }
226     if (!nested_scope_chain_.is_empty()) {
227       DCHECK_EQ(nested_scope_chain_.last().scope_info->scope_type(),
228                 SCRIPT_SCOPE);
229       nested_scope_chain_.RemoveLast();
230       DCHECK(nested_scope_chain_.is_empty());
231     }
232     CHECK(context_->IsNativeContext());
233   } else if (nested_scope_chain_.is_empty()) {
234     context_ = Handle<Context>(context_->previous(), isolate_);
235   } else {
236     do {
237       if (nested_scope_chain_.last().scope_info->HasContext()) {
238         DCHECK(context_->previous() != NULL);
239         context_ = Handle<Context>(context_->previous(), isolate_);
240       }
241       nested_scope_chain_.RemoveLast();
242       if (nested_scope_chain_.is_empty()) break;
243       // Repeat to skip hidden scopes.
244     } while (nested_scope_chain_.last().is_hidden());
245   }
246   UnwrapEvaluationContext();
247 }
248 
249 
250 // Return the type of the current scope.
Type()251 ScopeIterator::ScopeType ScopeIterator::Type() {
252   DCHECK(!failed_);
253   if (!nested_scope_chain_.is_empty()) {
254     Handle<ScopeInfo> scope_info = nested_scope_chain_.last().scope_info;
255     switch (scope_info->scope_type()) {
256       case FUNCTION_SCOPE:
257         DCHECK(context_->IsFunctionContext() || !scope_info->HasContext());
258         return ScopeTypeLocal;
259       case MODULE_SCOPE:
260         DCHECK(context_->IsModuleContext());
261         return ScopeTypeModule;
262       case SCRIPT_SCOPE:
263         DCHECK(context_->IsScriptContext() || context_->IsNativeContext());
264         return ScopeTypeScript;
265       case WITH_SCOPE:
266         DCHECK(context_->IsWithContext() || context_->IsDebugEvaluateContext());
267         return ScopeTypeWith;
268       case CATCH_SCOPE:
269         DCHECK(context_->IsCatchContext());
270         return ScopeTypeCatch;
271       case BLOCK_SCOPE:
272         DCHECK(!scope_info->HasContext() || context_->IsBlockContext());
273         return ScopeTypeBlock;
274       case EVAL_SCOPE:
275         DCHECK(!scope_info->HasContext() || context_->IsFunctionContext());
276         return ScopeTypeEval;
277     }
278     UNREACHABLE();
279   }
280   if (context_->IsNativeContext()) {
281     DCHECK(context_->global_object()->IsJSGlobalObject());
282     // If we are at the native context and have not yet seen script scope,
283     // fake it.
284     return seen_script_scope_ ? ScopeTypeGlobal : ScopeTypeScript;
285   }
286   if (context_->IsFunctionContext()) {
287     return ScopeTypeClosure;
288   }
289   if (context_->IsCatchContext()) {
290     return ScopeTypeCatch;
291   }
292   if (context_->IsBlockContext()) {
293     return ScopeTypeBlock;
294   }
295   if (context_->IsModuleContext()) {
296     return ScopeTypeModule;
297   }
298   if (context_->IsScriptContext()) {
299     return ScopeTypeScript;
300   }
301   DCHECK(context_->IsWithContext() || context_->IsDebugEvaluateContext());
302   return ScopeTypeWith;
303 }
304 
305 
ScopeObject()306 MaybeHandle<JSObject> ScopeIterator::ScopeObject() {
307   DCHECK(!failed_);
308   switch (Type()) {
309     case ScopeIterator::ScopeTypeGlobal:
310       return Handle<JSObject>(CurrentContext()->global_proxy());
311     case ScopeIterator::ScopeTypeScript:
312       return MaterializeScriptScope();
313     case ScopeIterator::ScopeTypeLocal:
314       // Materialize the content of the local scope into a JSObject.
315       DCHECK(nested_scope_chain_.length() == 1);
316       return MaterializeLocalScope();
317     case ScopeIterator::ScopeTypeWith:
318       return WithContextExtension();
319     case ScopeIterator::ScopeTypeCatch:
320       return MaterializeCatchScope();
321     case ScopeIterator::ScopeTypeClosure:
322       // Materialize the content of the closure scope into a JSObject.
323       return MaterializeClosure();
324     case ScopeIterator::ScopeTypeBlock:
325     case ScopeIterator::ScopeTypeEval:
326       return MaterializeInnerScope();
327     case ScopeIterator::ScopeTypeModule:
328       return MaterializeModuleScope();
329   }
330   UNREACHABLE();
331   return Handle<JSObject>();
332 }
333 
334 
HasContext()335 bool ScopeIterator::HasContext() {
336   ScopeType type = Type();
337   if (type == ScopeTypeBlock || type == ScopeTypeLocal ||
338       type == ScopeTypeEval) {
339     if (!nested_scope_chain_.is_empty()) {
340       return nested_scope_chain_.last().scope_info->HasContext();
341     }
342   }
343   return true;
344 }
345 
346 
SetVariableValue(Handle<String> variable_name,Handle<Object> new_value)347 bool ScopeIterator::SetVariableValue(Handle<String> variable_name,
348                                      Handle<Object> new_value) {
349   DCHECK(!failed_);
350   switch (Type()) {
351     case ScopeIterator::ScopeTypeGlobal:
352       break;
353     case ScopeIterator::ScopeTypeLocal:
354       return SetLocalVariableValue(variable_name, new_value);
355     case ScopeIterator::ScopeTypeWith:
356       break;
357     case ScopeIterator::ScopeTypeCatch:
358       return SetCatchVariableValue(variable_name, new_value);
359     case ScopeIterator::ScopeTypeClosure:
360       return SetClosureVariableValue(variable_name, new_value);
361     case ScopeIterator::ScopeTypeScript:
362       return SetScriptVariableValue(variable_name, new_value);
363     case ScopeIterator::ScopeTypeBlock:
364     case ScopeIterator::ScopeTypeEval:
365       return SetInnerScopeVariableValue(variable_name, new_value);
366     case ScopeIterator::ScopeTypeModule:
367       // TODO(neis): Implement.
368       break;
369   }
370   return false;
371 }
372 
373 
CurrentScopeInfo()374 Handle<ScopeInfo> ScopeIterator::CurrentScopeInfo() {
375   DCHECK(!failed_);
376   if (!nested_scope_chain_.is_empty()) {
377     return nested_scope_chain_.last().scope_info;
378   } else if (context_->IsBlockContext()) {
379     return Handle<ScopeInfo>(context_->scope_info());
380   } else if (context_->IsFunctionContext()) {
381     return Handle<ScopeInfo>(context_->closure()->shared()->scope_info());
382   }
383   return Handle<ScopeInfo>::null();
384 }
385 
386 
CurrentContext()387 Handle<Context> ScopeIterator::CurrentContext() {
388   DCHECK(!failed_);
389   if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript ||
390       nested_scope_chain_.is_empty()) {
391     return context_;
392   } else if (nested_scope_chain_.last().scope_info->HasContext()) {
393     return context_;
394   } else {
395     return Handle<Context>();
396   }
397 }
398 
GetNonLocals()399 Handle<StringSet> ScopeIterator::GetNonLocals() { return non_locals_; }
400 
401 #ifdef DEBUG
402 // Debug print of the content of the current scope.
DebugPrint()403 void ScopeIterator::DebugPrint() {
404   OFStream os(stdout);
405   DCHECK(!failed_);
406   switch (Type()) {
407     case ScopeIterator::ScopeTypeGlobal:
408       os << "Global:\n";
409       CurrentContext()->Print(os);
410       break;
411 
412     case ScopeIterator::ScopeTypeLocal: {
413       os << "Local:\n";
414       GetFunction()->shared()->scope_info()->Print();
415       if (!CurrentContext().is_null()) {
416         CurrentContext()->Print(os);
417         if (CurrentContext()->has_extension()) {
418           Handle<HeapObject> extension(CurrentContext()->extension(), isolate_);
419           if (extension->IsJSContextExtensionObject()) {
420             extension->Print(os);
421           }
422         }
423       }
424       break;
425     }
426 
427     case ScopeIterator::ScopeTypeWith:
428       os << "With:\n";
429       CurrentContext()->extension()->Print(os);
430       break;
431 
432     case ScopeIterator::ScopeTypeCatch:
433       os << "Catch:\n";
434       CurrentContext()->extension()->Print(os);
435       CurrentContext()->get(Context::THROWN_OBJECT_INDEX)->Print(os);
436       break;
437 
438     case ScopeIterator::ScopeTypeClosure:
439       os << "Closure:\n";
440       CurrentContext()->Print(os);
441       if (CurrentContext()->has_extension()) {
442         Handle<HeapObject> extension(CurrentContext()->extension(), isolate_);
443         if (extension->IsJSContextExtensionObject()) {
444           extension->Print(os);
445         }
446       }
447       break;
448 
449     case ScopeIterator::ScopeTypeScript:
450       os << "Script:\n";
451       CurrentContext()
452           ->global_object()
453           ->native_context()
454           ->script_context_table()
455           ->Print(os);
456       break;
457 
458     default:
459       UNREACHABLE();
460   }
461   PrintF("\n");
462 }
463 #endif
464 
RetrieveScopeChain(DeclarationScope * scope)465 void ScopeIterator::RetrieveScopeChain(DeclarationScope* scope) {
466   DCHECK_NOT_NULL(scope);
467   int source_position = frame_inspector_->GetSourcePosition();
468   GetNestedScopeChain(isolate_, scope, source_position);
469 }
470 
CollectNonLocals(ParseInfo * info,DeclarationScope * scope)471 void ScopeIterator::CollectNonLocals(ParseInfo* info, DeclarationScope* scope) {
472   DCHECK_NOT_NULL(scope);
473   DCHECK(non_locals_.is_null());
474   non_locals_ = scope->CollectNonLocals(info, StringSet::New(isolate_));
475 }
476 
477 
MaterializeScriptScope()478 MaybeHandle<JSObject> ScopeIterator::MaterializeScriptScope() {
479   Handle<JSGlobalObject> global(CurrentContext()->global_object());
480   Handle<ScriptContextTable> script_contexts(
481       global->native_context()->script_context_table());
482 
483   Handle<JSObject> script_scope =
484       isolate_->factory()->NewJSObjectWithNullProto();
485 
486   for (int context_index = 0; context_index < script_contexts->used();
487        context_index++) {
488     Handle<Context> context =
489         ScriptContextTable::GetContext(script_contexts, context_index);
490     Handle<ScopeInfo> scope_info(context->scope_info());
491     CopyContextLocalsToScopeObject(scope_info, context, script_scope);
492   }
493   return script_scope;
494 }
495 
496 
MaterializeLocalScope()497 MaybeHandle<JSObject> ScopeIterator::MaterializeLocalScope() {
498   Handle<JSFunction> function = GetFunction();
499 
500   Handle<JSObject> local_scope =
501       isolate_->factory()->NewJSObjectWithNullProto();
502   frame_inspector_->MaterializeStackLocals(local_scope, function);
503 
504   Handle<Context> frame_context =
505       Handle<Context>::cast(frame_inspector_->GetContext());
506 
507   HandleScope scope(isolate_);
508   Handle<SharedFunctionInfo> shared(function->shared());
509   Handle<ScopeInfo> scope_info(shared->scope_info());
510 
511   if (!scope_info->HasContext()) return local_scope;
512 
513   // Fill all context locals.
514   Handle<Context> function_context(frame_context->closure_context());
515   CopyContextLocalsToScopeObject(scope_info, function_context, local_scope);
516 
517   // Finally copy any properties from the function context extension.
518   // These will be variables introduced by eval.
519   if (function_context->closure() == *function &&
520       !function_context->IsNativeContext()) {
521     CopyContextExtensionToScopeObject(function_context, local_scope,
522                                       KeyCollectionMode::kIncludePrototypes);
523   }
524 
525   return local_scope;
526 }
527 
528 
529 // Create a plain JSObject which materializes the closure content for the
530 // context.
MaterializeClosure()531 Handle<JSObject> ScopeIterator::MaterializeClosure() {
532   Handle<Context> context = CurrentContext();
533   DCHECK(context->IsFunctionContext());
534 
535   Handle<SharedFunctionInfo> shared(context->closure()->shared());
536   Handle<ScopeInfo> scope_info(shared->scope_info());
537 
538   // Allocate and initialize a JSObject with all the content of this function
539   // closure.
540   Handle<JSObject> closure_scope =
541       isolate_->factory()->NewJSObjectWithNullProto();
542 
543   // Fill all context locals to the context extension.
544   CopyContextLocalsToScopeObject(scope_info, context, closure_scope);
545 
546   // Finally copy any properties from the function context extension. This will
547   // be variables introduced by eval.
548   CopyContextExtensionToScopeObject(context, closure_scope,
549                                     KeyCollectionMode::kOwnOnly);
550 
551   return closure_scope;
552 }
553 
554 
555 // Create a plain JSObject which materializes the scope for the specified
556 // catch context.
MaterializeCatchScope()557 Handle<JSObject> ScopeIterator::MaterializeCatchScope() {
558   Handle<Context> context = CurrentContext();
559   DCHECK(context->IsCatchContext());
560   Handle<String> name(context->catch_name());
561   Handle<Object> thrown_object(context->get(Context::THROWN_OBJECT_INDEX),
562                                isolate_);
563   Handle<JSObject> catch_scope =
564       isolate_->factory()->NewJSObjectWithNullProto();
565   JSObject::SetOwnPropertyIgnoreAttributes(catch_scope, name, thrown_object,
566                                            NONE)
567       .Check();
568   return catch_scope;
569 }
570 
571 // Retrieve the with-context extension object. If the extension object is
572 // a proxy, return an empty object.
WithContextExtension()573 Handle<JSObject> ScopeIterator::WithContextExtension() {
574   Handle<Context> context = CurrentContext();
575   DCHECK(context->IsWithContext());
576   if (context->extension_receiver()->IsJSProxy()) {
577     return isolate_->factory()->NewJSObjectWithNullProto();
578   }
579   return handle(JSObject::cast(context->extension_receiver()));
580 }
581 
582 // Create a plain JSObject which materializes the block scope for the specified
583 // block context.
MaterializeInnerScope()584 Handle<JSObject> ScopeIterator::MaterializeInnerScope() {
585   Handle<JSObject> inner_scope =
586       isolate_->factory()->NewJSObjectWithNullProto();
587 
588   Handle<Context> context = Handle<Context>::null();
589   if (!nested_scope_chain_.is_empty()) {
590     Handle<ScopeInfo> scope_info = nested_scope_chain_.last().scope_info;
591     frame_inspector_->MaterializeStackLocals(inner_scope, scope_info);
592     if (scope_info->HasContext()) context = CurrentContext();
593   } else {
594     context = CurrentContext();
595   }
596 
597   if (!context.is_null()) {
598     // Fill all context locals.
599     CopyContextLocalsToScopeObject(CurrentScopeInfo(), context, inner_scope);
600     CopyContextExtensionToScopeObject(context, inner_scope,
601                                       KeyCollectionMode::kOwnOnly);
602   }
603   return inner_scope;
604 }
605 
606 
607 // Create a plain JSObject which materializes the module scope for the specified
608 // module context.
MaterializeModuleScope()609 MaybeHandle<JSObject> ScopeIterator::MaterializeModuleScope() {
610   Handle<Context> context = CurrentContext();
611   DCHECK(context->IsModuleContext());
612   Handle<ScopeInfo> scope_info(context->scope_info());
613   Handle<JSObject> module_scope =
614       isolate_->factory()->NewJSObjectWithNullProto();
615   CopyContextLocalsToScopeObject(scope_info, context, module_scope);
616   CopyModuleVarsToScopeObject(scope_info, context, module_scope);
617   return module_scope;
618 }
619 
SetParameterValue(Handle<ScopeInfo> scope_info,JavaScriptFrame * frame,Handle<String> parameter_name,Handle<Object> new_value)620 bool ScopeIterator::SetParameterValue(Handle<ScopeInfo> scope_info,
621                                       JavaScriptFrame* frame,
622                                       Handle<String> parameter_name,
623                                       Handle<Object> new_value) {
624   // Setting stack locals of optimized frames is not supported.
625   if (frame->is_optimized()) return false;
626   HandleScope scope(isolate_);
627   for (int i = 0; i < scope_info->ParameterCount(); ++i) {
628     if (String::Equals(handle(scope_info->ParameterName(i)), parameter_name)) {
629       frame->SetParameterValue(i, *new_value);
630       return true;
631     }
632   }
633   return false;
634 }
635 
SetStackVariableValue(Handle<ScopeInfo> scope_info,Handle<String> variable_name,Handle<Object> new_value)636 bool ScopeIterator::SetStackVariableValue(Handle<ScopeInfo> scope_info,
637                                           Handle<String> variable_name,
638                                           Handle<Object> new_value) {
639   if (frame_inspector_ == nullptr) return false;
640   JavaScriptFrame* frame = GetFrame();
641   // Setting stack locals of optimized frames is not supported.
642   if (frame->is_optimized()) return false;
643   HandleScope scope(isolate_);
644   for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
645     if (String::Equals(handle(scope_info->StackLocalName(i)), variable_name)) {
646       frame->SetExpression(scope_info->StackLocalIndex(i), *new_value);
647       return true;
648     }
649   }
650   return false;
651 }
652 
SetContextVariableValue(Handle<ScopeInfo> scope_info,Handle<Context> context,Handle<String> variable_name,Handle<Object> new_value)653 bool ScopeIterator::SetContextVariableValue(Handle<ScopeInfo> scope_info,
654                                             Handle<Context> context,
655                                             Handle<String> variable_name,
656                                             Handle<Object> new_value) {
657   HandleScope scope(isolate_);
658   for (int i = 0; i < scope_info->ContextLocalCount(); i++) {
659     Handle<String> next_name(scope_info->ContextLocalName(i));
660     if (String::Equals(variable_name, next_name)) {
661       VariableMode mode;
662       InitializationFlag init_flag;
663       MaybeAssignedFlag maybe_assigned_flag;
664       int context_index = ScopeInfo::ContextSlotIndex(
665           scope_info, next_name, &mode, &init_flag, &maybe_assigned_flag);
666       context->set(context_index, *new_value);
667       return true;
668     }
669   }
670 
671   if (context->has_extension()) {
672     Handle<JSObject> ext(context->extension_object());
673     Maybe<bool> maybe = JSReceiver::HasOwnProperty(ext, variable_name);
674     DCHECK(maybe.IsJust());
675     if (maybe.FromJust()) {
676       // We don't expect this to do anything except replacing property value.
677       JSObject::SetOwnPropertyIgnoreAttributes(ext, variable_name, new_value,
678                                                NONE)
679           .Check();
680       return true;
681     }
682   }
683 
684   return false;
685 }
686 
SetLocalVariableValue(Handle<String> variable_name,Handle<Object> new_value)687 bool ScopeIterator::SetLocalVariableValue(Handle<String> variable_name,
688                                           Handle<Object> new_value) {
689   JavaScriptFrame* frame = GetFrame();
690   Handle<ScopeInfo> scope_info(frame->function()->shared()->scope_info());
691 
692   // Parameter might be shadowed in context. Don't stop here.
693   bool result = SetParameterValue(scope_info, frame, variable_name, new_value);
694 
695   // Stack locals.
696   if (SetStackVariableValue(scope_info, variable_name, new_value)) {
697     return true;
698   }
699 
700   if (scope_info->HasContext() &&
701       SetContextVariableValue(scope_info, CurrentContext(), variable_name,
702                               new_value)) {
703     return true;
704   }
705 
706   return result;
707 }
708 
SetInnerScopeVariableValue(Handle<String> variable_name,Handle<Object> new_value)709 bool ScopeIterator::SetInnerScopeVariableValue(Handle<String> variable_name,
710                                                Handle<Object> new_value) {
711   Handle<ScopeInfo> scope_info = CurrentScopeInfo();
712   DCHECK(scope_info->scope_type() == BLOCK_SCOPE ||
713          scope_info->scope_type() == EVAL_SCOPE);
714 
715   // Setting stack locals of optimized frames is not supported.
716   if (SetStackVariableValue(scope_info, variable_name, new_value)) {
717     return true;
718   }
719 
720   if (HasContext() && SetContextVariableValue(scope_info, CurrentContext(),
721                                               variable_name, new_value)) {
722     return true;
723   }
724 
725   return false;
726 }
727 
728 // This method copies structure of MaterializeClosure method above.
SetClosureVariableValue(Handle<String> variable_name,Handle<Object> new_value)729 bool ScopeIterator::SetClosureVariableValue(Handle<String> variable_name,
730                                             Handle<Object> new_value) {
731   DCHECK(CurrentContext()->IsFunctionContext());
732   return SetContextVariableValue(CurrentScopeInfo(), CurrentContext(),
733                                  variable_name, new_value);
734 }
735 
SetScriptVariableValue(Handle<String> variable_name,Handle<Object> new_value)736 bool ScopeIterator::SetScriptVariableValue(Handle<String> variable_name,
737                                            Handle<Object> new_value) {
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, 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     Handle<JSFunction> function = frame_inspector_->GetFunction();
842     if (scope->end_position() < function->shared()->end_position()) return;
843   }
844   if (scope->is_hidden()) {
845     // We need to add this chain element in case the scope has a context
846     // associated. We need to keep the scope chain and context chain in sync.
847     nested_scope_chain_.Add(ExtendedScopeInfo(scope->scope_info()));
848   } else {
849     nested_scope_chain_.Add(ExtendedScopeInfo(
850         scope->scope_info(), scope->start_position(), scope->end_position()));
851   }
852   for (Scope* inner_scope = scope->inner_scope(); inner_scope != nullptr;
853        inner_scope = inner_scope->sibling()) {
854     int beg_pos = inner_scope->start_position();
855     int end_pos = inner_scope->end_position();
856     DCHECK((beg_pos >= 0 && end_pos >= 0) || inner_scope->is_hidden());
857     if (beg_pos <= position && position < end_pos) {
858       GetNestedScopeChain(isolate, inner_scope, position);
859       return;
860     }
861   }
862 }
863 
864 }  // namespace internal
865 }  // namespace v8
866