• 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/common/globals.h"
12 #include "src/debug/debug.h"
13 #include "src/execution/frames-inl.h"
14 #include "src/execution/isolate-inl.h"
15 #include "src/objects/js-generator-inl.h"
16 #include "src/objects/source-text-module.h"
17 #include "src/objects/string-set-inl.h"
18 #include "src/parsing/parse-info.h"
19 #include "src/parsing/parsing.h"
20 #include "src/parsing/rewriter.h"
21 #include "src/utils/ostreams.h"
22 
23 namespace v8 {
24 namespace internal {
25 
ScopeIterator(Isolate * isolate,FrameInspector * frame_inspector,ReparseStrategy strategy)26 ScopeIterator::ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector,
27                              ReparseStrategy strategy)
28     : isolate_(isolate),
29       frame_inspector_(frame_inspector),
30       function_(frame_inspector_->GetFunction()),
31       script_(frame_inspector_->GetScript()) {
32   if (!frame_inspector->GetContext()->IsContext()) {
33     // Optimized frame, context or function cannot be materialized. Give up.
34     return;
35   }
36   context_ = Handle<Context>::cast(frame_inspector->GetContext());
37 
38 #if V8_ENABLE_WEBASSEMBLY
39   // We should not instantiate a ScopeIterator for wasm frames.
40   DCHECK_NE(Script::TYPE_WASM, frame_inspector->GetScript()->type());
41 #endif  // V8_ENABLE_WEBASSEMBLY
42 
43   TryParseAndRetrieveScopes(strategy);
44 }
45 
46 ScopeIterator::~ScopeIterator() = default;
47 
GetFunctionDebugName() const48 Handle<Object> ScopeIterator::GetFunctionDebugName() const {
49   if (!function_.is_null()) return JSFunction::GetDebugName(function_);
50 
51   if (!context_->IsNativeContext()) {
52     DisallowGarbageCollection no_gc;
53     ScopeInfo closure_info = context_->closure_context().scope_info();
54     Handle<String> debug_name(closure_info.FunctionDebugName(), isolate_);
55     if (debug_name->length() > 0) return debug_name;
56   }
57   return isolate_->factory()->undefined_value();
58 }
59 
ScopeIterator(Isolate * isolate,Handle<JSFunction> function)60 ScopeIterator::ScopeIterator(Isolate* isolate, Handle<JSFunction> function)
61     : isolate_(isolate), context_(function->context(), isolate) {
62   if (!function->shared().IsSubjectToDebugging()) {
63     context_ = Handle<Context>();
64     return;
65   }
66   script_ = handle(Script::cast(function->shared().script()), isolate);
67   UnwrapEvaluationContext();
68 }
69 
ScopeIterator(Isolate * isolate,Handle<JSGeneratorObject> generator)70 ScopeIterator::ScopeIterator(Isolate* isolate,
71                              Handle<JSGeneratorObject> generator)
72     : isolate_(isolate),
73       generator_(generator),
74       function_(generator->function(), isolate),
75       context_(generator->context(), isolate),
76       script_(Script::cast(function_->shared().script()), isolate) {
77   CHECK(function_->shared().IsSubjectToDebugging());
78   TryParseAndRetrieveScopes(ReparseStrategy::kFunctionLiteral);
79 }
80 
Restart()81 void ScopeIterator::Restart() {
82   DCHECK_NOT_NULL(frame_inspector_);
83   function_ = frame_inspector_->GetFunction();
84   context_ = Handle<Context>::cast(frame_inspector_->GetContext());
85   current_scope_ = start_scope_;
86   DCHECK_NOT_NULL(current_scope_);
87   UnwrapEvaluationContext();
88 }
89 
90 namespace {
91 
92 // Takes the scope of a parsed script, a function and a break location
93 // inside the function. The result is the innermost lexical scope around
94 // the break point, which serves as the starting point of the ScopeIterator.
95 // And the scope of the function that was passed in (called closure scope).
96 //
97 // The start scope is guaranteed to be either the closure scope itself,
98 // or a child of the closure scope.
99 class ScopeChainRetriever {
100  public:
ScopeChainRetriever(DeclarationScope * scope,Handle<JSFunction> function,int position)101   ScopeChainRetriever(DeclarationScope* scope, Handle<JSFunction> function,
102                       int position)
103       : scope_(scope),
104         break_scope_start_(function->shared().StartPosition()),
105         break_scope_end_(function->shared().EndPosition()),
106         is_default_constructor_(
107             IsDefaultConstructor(function->shared().kind())),
108         position_(position) {
109     DCHECK_NOT_NULL(scope);
110     RetrieveScopes();
111   }
112 
ClosureScope()113   DeclarationScope* ClosureScope() { return closure_scope_; }
StartScope()114   Scope* StartScope() { return start_scope_; }
115 
116  private:
117   DeclarationScope* scope_;
118   const int break_scope_start_;
119   const int break_scope_end_;
120   const bool is_default_constructor_;
121   const int position_;
122 
123   DeclarationScope* closure_scope_ = nullptr;
124   Scope* start_scope_ = nullptr;
125 
RetrieveScopes()126   void RetrieveScopes() {
127     if (is_default_constructor_) {
128       // Even though the DefaultBaseConstructor is a child of a Class scope, the
129       // source positions are *not* nested. This means the actual scope for the
130       // DefaultBaseConstructor needs to be found by doing a DFS.
131       RetrieveScopeChainDefaultConstructor(scope_);
132     } else {
133       RetrieveScopeChain();
134     }
135     DCHECK_NOT_NULL(closure_scope_);
136     DCHECK_NOT_NULL(start_scope_);
137   }
138 
RetrieveScopeChainDefaultConstructor(Scope * scope)139   bool RetrieveScopeChainDefaultConstructor(Scope* scope) {
140     const int beg_pos = scope->start_position();
141     const int end_pos = scope->end_position();
142     if (beg_pos == position_ && end_pos == position_) {
143       DCHECK(scope->is_function_scope());
144       DCHECK(
145           IsDefaultConstructor(scope->AsDeclarationScope()->function_kind()));
146       start_scope_ = scope;
147       closure_scope_ = scope->AsDeclarationScope();
148       return true;
149     }
150 
151     for (Scope* inner_scope = scope->inner_scope(); inner_scope != nullptr;
152          inner_scope = inner_scope->sibling()) {
153       if (RetrieveScopeChainDefaultConstructor(inner_scope)) return true;
154     }
155     return false;
156   }
157 
RetrieveScopeChain()158   void RetrieveScopeChain() {
159     Scope* parent = nullptr;
160     Scope* current = scope_;
161     SetClosureScopeIfFound(current);
162 
163     while (parent != current) {
164       parent = current;
165       for (Scope* inner_scope = current->inner_scope(); inner_scope != nullptr;
166            inner_scope = inner_scope->sibling()) {
167         if (SetClosureScopeIfFound(inner_scope) ||
168             ContainsPosition(inner_scope)) {
169           current = inner_scope;
170           break;
171         }
172       }
173     }
174     start_scope_ = current;
175   }
176 
SetClosureScopeIfFound(Scope * scope)177   bool SetClosureScopeIfFound(Scope* scope) {
178     const int start = scope->start_position();
179     const int end = scope->end_position();
180     if (start == break_scope_start_ && end == break_scope_end_) {
181       closure_scope_ = scope->AsDeclarationScope();
182       return true;
183     }
184     return false;
185   }
186 
ContainsPosition(Scope * scope)187   bool ContainsPosition(Scope* scope) {
188     const int start = scope->start_position();
189     const int end = scope->end_position();
190     // In case the closure_scope_ hasn't been found yet, we are less strict
191     // about recursing downwards. This might be the case for nested arrow
192     // functions that have the same end position.
193     const bool position_fits_end =
194         closure_scope_ ? position_ < end : position_ <= end;
195     // While we're evaluating a class, the calling function will have a class
196     // context on the stack with a range that starts at Token::CLASS, and the
197     // source position will also point to Token::CLASS.  To identify the
198     // matching scope we include start in the accepted range for class scopes.
199     const bool position_fits_start =
200         scope->is_class_scope() ? start <= position_ : start < position_;
201     return position_fits_start && position_fits_end;
202   }
203 };
204 
205 }  // namespace
206 
TryParseAndRetrieveScopes(ReparseStrategy strategy)207 void ScopeIterator::TryParseAndRetrieveScopes(ReparseStrategy strategy) {
208   // Catch the case when the debugger stops in an internal function.
209   Handle<SharedFunctionInfo> shared_info(function_->shared(), isolate_);
210   Handle<ScopeInfo> scope_info(shared_info->scope_info(), isolate_);
211   if (shared_info->script().IsUndefined(isolate_)) {
212     current_scope_ = closure_scope_ = nullptr;
213     context_ = handle(function_->context(), isolate_);
214     function_ = Handle<JSFunction>();
215     return;
216   }
217 
218   // Class fields initializer functions don't have any scope
219   // information. We short circuit the parsing of the class literal
220   // and return an empty context here.
221   if (IsClassMembersInitializerFunction(shared_info->kind())) {
222     current_scope_ = closure_scope_ = nullptr;
223     context_ = Handle<Context>();
224     function_ = Handle<JSFunction>();
225     return;
226   }
227 
228   bool ignore_nested_scopes = false;
229   if (shared_info->HasBreakInfo() && frame_inspector_ != nullptr) {
230     // The source position at return is always the end of the function,
231     // which is not consistent with the current scope chain. Therefore all
232     // nested with, catch and block contexts are skipped, and we can only
233     // inspect the function scope.
234     // This can only happen if we set a break point inside right before the
235     // return, which requires a debug info to be available.
236     Handle<DebugInfo> debug_info(shared_info->GetDebugInfo(), isolate_);
237 
238     // Find the break point where execution has stopped.
239     BreakLocation location = BreakLocation::FromFrame(debug_info, GetFrame());
240 
241     ignore_nested_scopes = location.IsReturn();
242   }
243 
244   // Reparse the code and analyze the scopes.
245   // Depending on the choosen strategy, the whole script or just
246   // the closure is re-parsed for function scopes.
247   Handle<Script> script(Script::cast(shared_info->script()), isolate_);
248 
249   // Pick between flags for a single function compilation, or an eager
250   // compilation of the whole script.
251   UnoptimizedCompileFlags flags =
252       (scope_info->scope_type() == FUNCTION_SCOPE &&
253        strategy == ReparseStrategy::kFunctionLiteral)
254           ? UnoptimizedCompileFlags::ForFunctionCompile(isolate_, *shared_info)
255           : UnoptimizedCompileFlags::ForScriptCompile(isolate_, *script)
256                 .set_is_eager(true);
257 
258   MaybeHandle<ScopeInfo> maybe_outer_scope;
259   if (scope_info->scope_type() == EVAL_SCOPE || script->is_wrapped()) {
260     flags.set_is_eval(true);
261     if (!context_->IsNativeContext()) {
262       maybe_outer_scope = handle(context_->scope_info(), isolate_);
263     }
264     // Language mode may be inherited from the eval caller.
265     // Retrieve it from shared function info.
266     flags.set_outer_language_mode(shared_info->language_mode());
267   } else if (scope_info->scope_type() == MODULE_SCOPE) {
268     DCHECK(script->origin_options().IsModule());
269     DCHECK(flags.is_module());
270   } else {
271     DCHECK(scope_info->scope_type() == SCRIPT_SCOPE ||
272            scope_info->scope_type() == FUNCTION_SCOPE);
273   }
274 
275   UnoptimizedCompileState compile_state;
276 
277   reusable_compile_state_ =
278       std::make_unique<ReusableUnoptimizedCompileState>(isolate_);
279   info_ = std::make_unique<ParseInfo>(isolate_, flags, &compile_state,
280                                       reusable_compile_state_.get());
281 
282   const bool parse_result =
283       flags.is_toplevel()
284           ? parsing::ParseProgram(info_.get(), script, maybe_outer_scope,
285                                   isolate_, parsing::ReportStatisticsMode::kNo)
286           : parsing::ParseFunction(info_.get(), shared_info, isolate_,
287                                    parsing::ReportStatisticsMode::kNo);
288 
289   if (parse_result) {
290     DeclarationScope* literal_scope = info_->literal()->scope();
291 
292     ScopeChainRetriever scope_chain_retriever(literal_scope, function_,
293                                               GetSourcePosition());
294     start_scope_ = scope_chain_retriever.StartScope();
295     current_scope_ = start_scope_;
296 
297     // In case of a FUNCTION_SCOPE, the ScopeIterator expects
298     // {closure_scope_} to be set to the scope of the function.
299     closure_scope_ = scope_info->scope_type() == FUNCTION_SCOPE
300                          ? scope_chain_retriever.ClosureScope()
301                          : literal_scope;
302 
303     if (ignore_nested_scopes) {
304       current_scope_ = closure_scope_;
305       start_scope_ = current_scope_;
306       // ignore_nested_scopes is only used for the return-position breakpoint,
307       // so we can safely assume that the closure context for the current
308       // function exists if it needs one.
309       if (closure_scope_->NeedsContext()) {
310         context_ = handle(context_->closure_context(), isolate_);
311       }
312     }
313 
314     UnwrapEvaluationContext();
315   } else {
316     // A failed reparse indicates that the preparser has diverged from the
317     // parser, that the preparse data given to the initial parse was faulty, or
318     // a stack overflow.
319     // TODO(leszeks): This error is pretty unexpected, so we could report the
320     // error in debug mode. Better to not fail in release though, in case it's
321     // just a stack overflow.
322 
323     // Silently fail by presenting an empty context chain.
324     context_ = Handle<Context>();
325   }
326 }
327 
UnwrapEvaluationContext()328 void ScopeIterator::UnwrapEvaluationContext() {
329   if (!context_->IsDebugEvaluateContext()) return;
330   Context current = *context_;
331   do {
332     Object wrapped = current.get(Context::WRAPPED_CONTEXT_INDEX);
333     if (wrapped.IsContext()) {
334       current = Context::cast(wrapped);
335     } else {
336       DCHECK(!current.previous().is_null());
337       current = current.previous();
338     }
339   } while (current.IsDebugEvaluateContext());
340   context_ = handle(current, isolate_);
341 }
342 
MaterializeScopeDetails()343 Handle<JSObject> ScopeIterator::MaterializeScopeDetails() {
344   // Calculate the size of the result.
345   Handle<FixedArray> details =
346       isolate_->factory()->NewFixedArray(kScopeDetailsSize);
347   // Fill in scope details.
348   details->set(kScopeDetailsTypeIndex, Smi::FromInt(Type()));
349   Handle<JSObject> scope_object = ScopeObject(Mode::ALL);
350   details->set(kScopeDetailsObjectIndex, *scope_object);
351   if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript) {
352     return isolate_->factory()->NewJSArrayWithElements(details);
353   } else if (HasContext()) {
354     Handle<Object> closure_name = GetFunctionDebugName();
355     details->set(kScopeDetailsNameIndex, *closure_name);
356     details->set(kScopeDetailsStartPositionIndex,
357                  Smi::FromInt(start_position()));
358     details->set(kScopeDetailsEndPositionIndex, Smi::FromInt(end_position()));
359     if (InInnerScope()) {
360       details->set(kScopeDetailsFunctionIndex, *function_);
361     }
362   }
363   return isolate_->factory()->NewJSArrayWithElements(details);
364 }
365 
HasPositionInfo()366 bool ScopeIterator::HasPositionInfo() {
367   return InInnerScope() || !context_->IsNativeContext();
368 }
369 
start_position()370 int ScopeIterator::start_position() {
371   if (InInnerScope()) return current_scope_->start_position();
372   if (context_->IsNativeContext()) return 0;
373   return context_->closure_context().scope_info().StartPosition();
374 }
375 
end_position()376 int ScopeIterator::end_position() {
377   if (InInnerScope()) return current_scope_->end_position();
378   if (context_->IsNativeContext()) return 0;
379   return context_->closure_context().scope_info().EndPosition();
380 }
381 
DeclaresLocals(Mode mode) const382 bool ScopeIterator::DeclaresLocals(Mode mode) const {
383   ScopeType type = Type();
384 
385   if (type == ScopeTypeWith) return mode == Mode::ALL;
386   if (type == ScopeTypeGlobal) return mode == Mode::ALL;
387 
388   bool declares_local = false;
389   auto visitor = [&](Handle<String> name, Handle<Object> value,
390                      ScopeType scope_type) {
391     declares_local = true;
392     return true;
393   };
394   VisitScope(visitor, mode);
395   return declares_local;
396 }
397 
HasContext() const398 bool ScopeIterator::HasContext() const {
399   return !InInnerScope() || NeedsAndHasContext();
400 }
401 
NeedsAndHasContext() const402 bool ScopeIterator::NeedsAndHasContext() const {
403   if (!current_scope_->NeedsContext()) return false;
404   // Generally, if a scope needs a context, then we can assume that it has a
405   // context. However, the stack check during function entry happens before the
406   // function has a chance to create and push its own context, so we must check
407   // for the case where the function is executing in its parent context. This
408   // case is only possible in function scopes; top-level code (modules and
409   // non-module scripts) begin execution in the context they need and don't have
410   // a separate step to push the correct context.
411   return !(current_scope_ == closure_scope_ &&
412            current_scope_->is_function_scope() && !function_.is_null() &&
413            function_->context() == *context_);
414 }
415 
AdvanceOneScope()416 void ScopeIterator::AdvanceOneScope() {
417   if (NeedsAndHasContext()) {
418     DCHECK(!context_->previous().is_null());
419     context_ = handle(context_->previous(), isolate_);
420   }
421   DCHECK(current_scope_->outer_scope() != nullptr);
422   current_scope_ = current_scope_->outer_scope();
423 }
424 
AdvanceToNonHiddenScope()425 void ScopeIterator::AdvanceToNonHiddenScope() {
426   do {
427     AdvanceOneScope();
428   } while (current_scope_->is_hidden());
429 }
430 
AdvanceContext()431 void ScopeIterator::AdvanceContext() {
432   DCHECK(!context_->IsNativeContext());
433   context_ = handle(context_->previous(), isolate_);
434 
435   // While advancing one context, we need to advance at least one
436   // scope, but until we hit the next scope that actually requires
437   // a context. All the locals collected along the way build the
438   // blocklist for debug-evaluate for this context.
439   locals_ = StringSet::New(isolate_);
440   do {
441     if (!current_scope_ || !current_scope_->outer_scope()) break;
442 
443     current_scope_ = current_scope_->outer_scope();
444     CollectLocalsFromCurrentScope();
445   } while (!NeedsAndHasContext());
446 }
447 
Next()448 void ScopeIterator::Next() {
449   DCHECK(!Done());
450 
451   ScopeType scope_type = Type();
452 
453   if (scope_type == ScopeTypeGlobal) {
454     // The global scope is always the last in the chain.
455     DCHECK(context_->IsNativeContext());
456     context_ = Handle<Context>();
457     DCHECK(Done());
458     return;
459   }
460 
461   bool leaving_closure = current_scope_ == closure_scope_;
462 
463   if (scope_type == ScopeTypeScript) {
464     DCHECK_IMPLIES(InInnerScope() && !leaving_closure,
465                    current_scope_->is_script_scope());
466     seen_script_scope_ = true;
467     if (context_->IsScriptContext()) {
468       context_ = handle(context_->previous(), isolate_);
469     }
470   } else if (!InInnerScope()) {
471     AdvanceContext();
472   } else {
473     DCHECK_NOT_NULL(current_scope_);
474     AdvanceToNonHiddenScope();
475 
476     if (leaving_closure) {
477       DCHECK(current_scope_ != closure_scope_);
478       // Edge case when we just go past {closure_scope_}. This case
479       // already needs to start collecting locals for the blocklist.
480       locals_ = StringSet::New(isolate_);
481       CollectLocalsFromCurrentScope();
482     }
483   }
484 
485   if (leaving_closure) function_ = Handle<JSFunction>();
486 
487   UnwrapEvaluationContext();
488 }
489 
490 // Return the type of the current scope.
Type() const491 ScopeIterator::ScopeType ScopeIterator::Type() const {
492   DCHECK(!Done());
493   if (InInnerScope()) {
494     switch (current_scope_->scope_type()) {
495       case FUNCTION_SCOPE:
496         DCHECK_IMPLIES(NeedsAndHasContext(),
497                        context_->IsFunctionContext() ||
498                            context_->IsDebugEvaluateContext());
499         return ScopeTypeLocal;
500       case MODULE_SCOPE:
501         DCHECK_IMPLIES(NeedsAndHasContext(), context_->IsModuleContext());
502         return ScopeTypeModule;
503       case SCRIPT_SCOPE:
504         DCHECK_IMPLIES(NeedsAndHasContext(), context_->IsScriptContext() ||
505                                                  context_->IsNativeContext());
506         return ScopeTypeScript;
507       case WITH_SCOPE:
508         DCHECK_IMPLIES(NeedsAndHasContext(), context_->IsWithContext());
509         return ScopeTypeWith;
510       case CATCH_SCOPE:
511         DCHECK(context_->IsCatchContext());
512         return ScopeTypeCatch;
513       case BLOCK_SCOPE:
514       case CLASS_SCOPE:
515         DCHECK_IMPLIES(NeedsAndHasContext(), context_->IsBlockContext());
516         return ScopeTypeBlock;
517       case EVAL_SCOPE:
518         DCHECK_IMPLIES(NeedsAndHasContext(), context_->IsEvalContext());
519         return ScopeTypeEval;
520     }
521     UNREACHABLE();
522   }
523   if (context_->IsNativeContext()) {
524     DCHECK(context_->global_object().IsJSGlobalObject());
525     // If we are at the native context and have not yet seen script scope,
526     // fake it.
527     return seen_script_scope_ ? ScopeTypeGlobal : ScopeTypeScript;
528   }
529   if (context_->IsFunctionContext() || context_->IsEvalContext() ||
530       context_->IsDebugEvaluateContext()) {
531     return ScopeTypeClosure;
532   }
533   if (context_->IsCatchContext()) {
534     return ScopeTypeCatch;
535   }
536   if (context_->IsBlockContext()) {
537     return ScopeTypeBlock;
538   }
539   if (context_->IsModuleContext()) {
540     return ScopeTypeModule;
541   }
542   if (context_->IsScriptContext()) {
543     return ScopeTypeScript;
544   }
545   DCHECK(context_->IsWithContext());
546   return ScopeTypeWith;
547 }
548 
ScopeObject(Mode mode)549 Handle<JSObject> ScopeIterator::ScopeObject(Mode mode) {
550   DCHECK(!Done());
551 
552   ScopeType type = Type();
553   if (type == ScopeTypeGlobal) {
554     DCHECK_EQ(Mode::ALL, mode);
555     return handle(context_->global_proxy(), isolate_);
556   }
557   if (type == ScopeTypeWith) {
558     DCHECK_EQ(Mode::ALL, mode);
559     return WithContextExtension();
560   }
561 
562   Handle<JSObject> scope = isolate_->factory()->NewSlowJSObjectWithNullProto();
563   auto visitor = [=](Handle<String> name, Handle<Object> value,
564                      ScopeType scope_type) {
565     if (value->IsTheHole(isolate_)) {
566       // Reflect variables under TDZ as undefined in scope object.
567       if (scope_type == ScopeTypeScript &&
568           JSReceiver::HasOwnProperty(isolate_, scope, name).FromMaybe(true)) {
569         // We also use the hole to represent overridden let-declarations via
570         // REPL mode in a script context. Catch this case.
571         return false;
572       }
573       value = isolate_->factory()->undefined_value();
574     }
575     // Overwrite properties. Sometimes names in the same scope can collide, e.g.
576     // with extension objects introduced via local eval.
577     JSObject::SetPropertyOrElement(isolate_, scope, name, value,
578                                    Just(ShouldThrow::kDontThrow))
579         .Check();
580     return false;
581   };
582 
583   VisitScope(visitor, mode);
584   return scope;
585 }
586 
VisitScope(const Visitor & visitor,Mode mode) const587 void ScopeIterator::VisitScope(const Visitor& visitor, Mode mode) const {
588   switch (Type()) {
589     case ScopeTypeLocal:
590     case ScopeTypeClosure:
591     case ScopeTypeCatch:
592     case ScopeTypeBlock:
593     case ScopeTypeEval:
594       return VisitLocalScope(visitor, mode, Type());
595     case ScopeTypeModule:
596       if (InInnerScope()) {
597         return VisitLocalScope(visitor, mode, Type());
598       }
599       DCHECK_EQ(Mode::ALL, mode);
600       return VisitModuleScope(visitor);
601     case ScopeTypeScript:
602       DCHECK_EQ(Mode::ALL, mode);
603       return VisitScriptScope(visitor);
604     case ScopeTypeWith:
605     case ScopeTypeGlobal:
606       UNREACHABLE();
607   }
608 }
609 
SetVariableValue(Handle<String> name,Handle<Object> value)610 bool ScopeIterator::SetVariableValue(Handle<String> name,
611                                      Handle<Object> value) {
612   DCHECK(!Done());
613   name = isolate_->factory()->InternalizeString(name);
614   switch (Type()) {
615     case ScopeTypeGlobal:
616     case ScopeTypeWith:
617       break;
618 
619     case ScopeTypeEval:
620     case ScopeTypeBlock:
621     case ScopeTypeCatch:
622     case ScopeTypeModule:
623       if (InInnerScope()) return SetLocalVariableValue(name, value);
624       if (Type() == ScopeTypeModule && SetModuleVariableValue(name, value)) {
625         return true;
626       }
627       return SetContextVariableValue(name, value);
628 
629     case ScopeTypeLocal:
630     case ScopeTypeClosure:
631       if (InInnerScope()) {
632         DCHECK_EQ(ScopeTypeLocal, Type());
633         if (SetLocalVariableValue(name, value)) return true;
634         // There may not be an associated context since we're InInnerScope().
635         if (!NeedsAndHasContext()) return false;
636       } else {
637         DCHECK_EQ(ScopeTypeClosure, Type());
638         if (SetContextVariableValue(name, value)) return true;
639       }
640       // The above functions only set variables statically declared in the
641       // function. There may be eval-introduced variables. Check them in
642       // SetContextExtensionValue.
643       return SetContextExtensionValue(name, value);
644 
645     case ScopeTypeScript:
646       return SetScriptVariableValue(name, value);
647   }
648   return false;
649 }
650 
ClosureScopeHasThisReference() const651 bool ScopeIterator::ClosureScopeHasThisReference() const {
652   return !closure_scope_->has_this_declaration() &&
653          closure_scope_->HasThisReference();
654 }
655 
CollectLocalsFromCurrentScope()656 void ScopeIterator::CollectLocalsFromCurrentScope() {
657   DCHECK(locals_->IsStringSet());
658   for (Variable* var : *current_scope_->locals()) {
659     if (var->location() == VariableLocation::PARAMETER ||
660         var->location() == VariableLocation::LOCAL) {
661       locals_ = StringSet::Add(isolate_, locals_, var->name());
662     }
663   }
664 }
665 
666 #ifdef DEBUG
667 // Debug print of the content of the current scope.
DebugPrint()668 void ScopeIterator::DebugPrint() {
669   StdoutStream os;
670   DCHECK(!Done());
671   switch (Type()) {
672     case ScopeIterator::ScopeTypeGlobal:
673       os << "Global:\n";
674       context_->Print(os);
675       break;
676 
677     case ScopeIterator::ScopeTypeLocal: {
678       os << "Local:\n";
679       if (NeedsAndHasContext()) {
680         context_->Print(os);
681         if (context_->has_extension()) {
682           Handle<HeapObject> extension(context_->extension(), isolate_);
683           DCHECK(extension->IsJSContextExtensionObject());
684           extension->Print(os);
685         }
686       }
687       break;
688     }
689 
690     case ScopeIterator::ScopeTypeWith:
691       os << "With:\n";
692       context_->extension().Print(os);
693       break;
694 
695     case ScopeIterator::ScopeTypeCatch:
696       os << "Catch:\n";
697       context_->extension().Print(os);
698       context_->get(Context::THROWN_OBJECT_INDEX).Print(os);
699       break;
700 
701     case ScopeIterator::ScopeTypeClosure:
702       os << "Closure:\n";
703       context_->Print(os);
704       if (context_->has_extension()) {
705         Handle<HeapObject> extension(context_->extension(), isolate_);
706         DCHECK(extension->IsJSContextExtensionObject());
707         extension->Print(os);
708       }
709       break;
710 
711     case ScopeIterator::ScopeTypeScript:
712       os << "Script:\n";
713       context_->global_object().native_context().script_context_table().Print(
714           os);
715       break;
716 
717     default:
718       UNREACHABLE();
719   }
720   PrintF("\n");
721 }
722 #endif
723 
GetSourcePosition()724 int ScopeIterator::GetSourcePosition() {
725   if (frame_inspector_) {
726     return frame_inspector_->GetSourcePosition();
727   } else {
728     DCHECK(!generator_.is_null());
729     SharedFunctionInfo::EnsureSourcePositionsAvailable(
730         isolate_, handle(generator_->function().shared(), isolate_));
731     return generator_->source_position();
732   }
733 }
734 
VisitScriptScope(const Visitor & visitor) const735 void ScopeIterator::VisitScriptScope(const Visitor& visitor) const {
736   Handle<JSGlobalObject> global(context_->global_object(), isolate_);
737   Handle<ScriptContextTable> script_contexts(
738       global->native_context().script_context_table(), isolate_);
739 
740   // Skip the first script since that just declares 'this'.
741   for (int context_index = 1;
742        context_index < script_contexts->used(kAcquireLoad); context_index++) {
743     Handle<Context> context = ScriptContextTable::GetContext(
744         isolate_, script_contexts, context_index);
745     Handle<ScopeInfo> scope_info(context->scope_info(), isolate_);
746     if (VisitContextLocals(visitor, scope_info, context, ScopeTypeScript))
747       return;
748   }
749 }
750 
VisitModuleScope(const Visitor & visitor) const751 void ScopeIterator::VisitModuleScope(const Visitor& visitor) const {
752   DCHECK(context_->IsModuleContext());
753 
754   Handle<ScopeInfo> scope_info(context_->scope_info(), isolate_);
755   if (VisitContextLocals(visitor, scope_info, context_, ScopeTypeModule))
756     return;
757 
758   int module_variable_count = scope_info->ModuleVariableCount();
759 
760   Handle<SourceTextModule> module(context_->module(), isolate_);
761 
762   for (int i = 0; i < module_variable_count; ++i) {
763     int index;
764     Handle<String> name;
765     {
766       String raw_name;
767       scope_info->ModuleVariable(i, &raw_name, &index);
768       if (ScopeInfo::VariableIsSynthetic(raw_name)) continue;
769       name = handle(raw_name, isolate_);
770     }
771     Handle<Object> value =
772         SourceTextModule::LoadVariable(isolate_, module, index);
773 
774     if (visitor(name, value, ScopeTypeModule)) return;
775   }
776 }
777 
VisitContextLocals(const Visitor & visitor,Handle<ScopeInfo> scope_info,Handle<Context> context,ScopeType scope_type) const778 bool ScopeIterator::VisitContextLocals(const Visitor& visitor,
779                                        Handle<ScopeInfo> scope_info,
780                                        Handle<Context> context,
781                                        ScopeType scope_type) const {
782   // Fill all context locals to the context extension.
783   for (auto it : ScopeInfo::IterateLocalNames(scope_info)) {
784     Handle<String> name(it->name(), isolate_);
785     if (ScopeInfo::VariableIsSynthetic(*name)) continue;
786     int context_index = scope_info->ContextHeaderLength() + it->index();
787     Handle<Object> value(context->get(context_index), isolate_);
788     if (visitor(name, value, scope_type)) return true;
789   }
790   return false;
791 }
792 
VisitLocals(const Visitor & visitor,Mode mode,ScopeType scope_type) const793 bool ScopeIterator::VisitLocals(const Visitor& visitor, Mode mode,
794                                 ScopeType scope_type) const {
795   if (mode == Mode::STACK && current_scope_->is_declaration_scope() &&
796       current_scope_->AsDeclarationScope()->has_this_declaration()) {
797     // TODO(bmeurer): We should refactor the general variable lookup
798     // around "this", since the current way is rather hacky when the
799     // receiver is context-allocated.
800     auto this_var = current_scope_->AsDeclarationScope()->receiver();
801     Handle<Object> receiver =
802         this_var->location() == VariableLocation::CONTEXT
803             ? handle(context_->get(this_var->index()), isolate_)
804             : frame_inspector_ == nullptr
805                   ? handle(generator_->receiver(), isolate_)
806                   : frame_inspector_->GetReceiver();
807     if (receiver->IsOptimizedOut(isolate_)) {
808       receiver = isolate_->factory()->undefined_value();
809     }
810     if (visitor(isolate_->factory()->this_string(), receiver, scope_type))
811       return true;
812   }
813 
814   if (current_scope_->is_function_scope()) {
815     Variable* function_var =
816         current_scope_->AsDeclarationScope()->function_var();
817     if (function_var != nullptr) {
818       Handle<JSFunction> function = frame_inspector_ == nullptr
819                                         ? function_
820                                         : frame_inspector_->GetFunction();
821       Handle<String> name = function_var->name();
822       if (visitor(name, function, scope_type)) return true;
823     }
824   }
825 
826   for (Variable* var : *current_scope_->locals()) {
827     if (ScopeInfo::VariableIsSynthetic(*var->name())) continue;
828 
829     int index = var->index();
830     Handle<Object> value;
831     switch (var->location()) {
832       case VariableLocation::LOOKUP:
833         UNREACHABLE();
834 
835       case VariableLocation::REPL_GLOBAL:
836         // REPL declared variables are ignored for now.
837       case VariableLocation::UNALLOCATED:
838         continue;
839 
840       case VariableLocation::PARAMETER: {
841         if (frame_inspector_ == nullptr) {
842           // Get the variable from the suspended generator.
843           DCHECK(!generator_.is_null());
844           FixedArray parameters_and_registers =
845               generator_->parameters_and_registers();
846           DCHECK_LT(index, parameters_and_registers.length());
847           value = handle(parameters_and_registers.get(index), isolate_);
848         } else {
849           value = frame_inspector_->GetParameter(index);
850 
851           if (value->IsOptimizedOut(isolate_)) {
852             value = isolate_->factory()->undefined_value();
853           }
854         }
855         break;
856       }
857 
858       case VariableLocation::LOCAL:
859         if (frame_inspector_ == nullptr) {
860           // Get the variable from the suspended generator.
861           DCHECK(!generator_.is_null());
862           FixedArray parameters_and_registers =
863               generator_->parameters_and_registers();
864           int parameter_count =
865               function_->shared().scope_info().ParameterCount();
866           index += parameter_count;
867           DCHECK_LT(index, parameters_and_registers.length());
868           value = handle(parameters_and_registers.get(index), isolate_);
869         } else {
870           value = frame_inspector_->GetExpression(index);
871           if (value->IsOptimizedOut(isolate_)) {
872             // We'll rematerialize this later.
873             if (current_scope_->is_declaration_scope() &&
874                 current_scope_->AsDeclarationScope()->arguments() == var) {
875               continue;
876             }
877             value = isolate_->factory()->undefined_value();
878           }
879         }
880         break;
881 
882       case VariableLocation::CONTEXT:
883         if (mode == Mode::STACK) continue;
884         DCHECK(var->IsContextSlot());
885         value = handle(context_->get(index), isolate_);
886         break;
887 
888       case VariableLocation::MODULE: {
889         if (mode == Mode::STACK) continue;
890         // if (var->IsExport()) continue;
891         Handle<SourceTextModule> module(context_->module(), isolate_);
892         value = SourceTextModule::LoadVariable(isolate_, module, var->index());
893         break;
894       }
895     }
896 
897     if (visitor(var->name(), value, scope_type)) return true;
898   }
899   return false;
900 }
901 
902 // Retrieve the with-context extension object. If the extension object is
903 // a proxy, return an empty object.
WithContextExtension()904 Handle<JSObject> ScopeIterator::WithContextExtension() {
905   DCHECK(context_->IsWithContext());
906   if (context_->extension_receiver().IsJSProxy()) {
907     return isolate_->factory()->NewSlowJSObjectWithNullProto();
908   }
909   return handle(JSObject::cast(context_->extension_receiver()), isolate_);
910 }
911 
912 // Create a plain JSObject which materializes the block scope for the specified
913 // block context.
VisitLocalScope(const Visitor & visitor,Mode mode,ScopeType scope_type) const914 void ScopeIterator::VisitLocalScope(const Visitor& visitor, Mode mode,
915                                     ScopeType scope_type) const {
916   if (InInnerScope()) {
917     if (VisitLocals(visitor, mode, scope_type)) return;
918     if (mode == Mode::STACK && Type() == ScopeTypeLocal) {
919       // Hide |this| in arrow functions that may be embedded in other functions
920       // but don't force |this| to be context-allocated. Otherwise we'd find the
921       // wrong |this| value.
922       if (!closure_scope_->has_this_declaration() &&
923           !closure_scope_->HasThisReference()) {
924         if (visitor(isolate_->factory()->this_string(),
925                     isolate_->factory()->undefined_value(), scope_type))
926           return;
927       }
928       // Add |arguments| to the function scope even if it wasn't used.
929       // Currently we don't yet support materializing the arguments object of
930       // suspended generators. We'd need to read the arguments out from the
931       // suspended generator rather than from an activation as
932       // FunctionGetArguments does.
933       if (frame_inspector_ != nullptr && !closure_scope_->is_arrow_scope() &&
934           (closure_scope_->arguments() == nullptr ||
935            frame_inspector_->GetExpression(closure_scope_->arguments()->index())
936                ->IsOptimizedOut(isolate_))) {
937         JavaScriptFrame* frame = GetFrame();
938         Handle<JSObject> arguments = Accessors::FunctionGetArguments(
939             frame, frame_inspector_->inlined_frame_index());
940         if (visitor(isolate_->factory()->arguments_string(), arguments,
941                     scope_type))
942           return;
943       }
944     }
945   } else {
946     DCHECK_EQ(Mode::ALL, mode);
947     Handle<ScopeInfo> scope_info(context_->scope_info(), isolate_);
948     if (VisitContextLocals(visitor, scope_info, context_, scope_type)) return;
949   }
950 
951   if (mode == Mode::ALL && HasContext()) {
952     DCHECK(!context_->IsScriptContext());
953     DCHECK(!context_->IsNativeContext());
954     DCHECK(!context_->IsWithContext());
955     if (!context_->scope_info().SloppyEvalCanExtendVars()) return;
956     if (context_->extension_object().is_null()) return;
957     Handle<JSObject> extension(context_->extension_object(), isolate_);
958     Handle<FixedArray> keys =
959         KeyAccumulator::GetKeys(extension, KeyCollectionMode::kOwnOnly,
960                                 ENUMERABLE_STRINGS)
961             .ToHandleChecked();
962 
963     for (int i = 0; i < keys->length(); i++) {
964       // Names of variables introduced by eval are strings.
965       DCHECK(keys->get(i).IsString());
966       Handle<String> key(String::cast(keys->get(i)), isolate_);
967       Handle<Object> value =
968           JSReceiver::GetDataProperty(isolate_, extension, key);
969       if (visitor(key, value, scope_type)) return;
970     }
971   }
972 }
973 
SetLocalVariableValue(Handle<String> variable_name,Handle<Object> new_value)974 bool ScopeIterator::SetLocalVariableValue(Handle<String> variable_name,
975                                           Handle<Object> new_value) {
976   // TODO(verwaest): Walk parameters backwards, not forwards.
977   // TODO(verwaest): Use VariableMap rather than locals() list for lookup.
978   for (Variable* var : *current_scope_->locals()) {
979     if (String::Equals(isolate_, var->name(), variable_name)) {
980       int index = var->index();
981       switch (var->location()) {
982         case VariableLocation::LOOKUP:
983         case VariableLocation::UNALLOCATED:
984           // Drop assignments to unallocated locals.
985           DCHECK(var->is_this() ||
986                  *variable_name == ReadOnlyRoots(isolate_).arguments_string());
987           return false;
988 
989         case VariableLocation::REPL_GLOBAL:
990           // Assignments to REPL declared variables are ignored for now.
991           return false;
992 
993         case VariableLocation::PARAMETER: {
994           if (var->is_this()) return false;
995           if (frame_inspector_ == nullptr) {
996             // Set the variable in the suspended generator.
997             DCHECK(!generator_.is_null());
998             Handle<FixedArray> parameters_and_registers(
999                 generator_->parameters_and_registers(), isolate_);
1000             DCHECK_LT(index, parameters_and_registers->length());
1001             parameters_and_registers->set(index, *new_value);
1002           } else {
1003             JavaScriptFrame* frame = GetFrame();
1004             if (frame->is_optimized()) return false;
1005 
1006             frame->SetParameterValue(index, *new_value);
1007           }
1008           return true;
1009         }
1010 
1011         case VariableLocation::LOCAL:
1012           if (frame_inspector_ == nullptr) {
1013             // Set the variable in the suspended generator.
1014             DCHECK(!generator_.is_null());
1015             int parameter_count =
1016                 function_->shared().scope_info().ParameterCount();
1017             index += parameter_count;
1018             Handle<FixedArray> parameters_and_registers(
1019                 generator_->parameters_and_registers(), isolate_);
1020             DCHECK_LT(index, parameters_and_registers->length());
1021             parameters_and_registers->set(index, *new_value);
1022           } else {
1023             // Set the variable on the stack.
1024             JavaScriptFrame* frame = GetFrame();
1025             if (frame->is_optimized()) return false;
1026 
1027             frame->SetExpression(index, *new_value);
1028           }
1029           return true;
1030 
1031         case VariableLocation::CONTEXT:
1032           DCHECK(var->IsContextSlot());
1033           context_->set(index, *new_value);
1034           return true;
1035 
1036         case VariableLocation::MODULE:
1037           if (!var->IsExport()) return false;
1038           Handle<SourceTextModule> module(context_->module(), isolate_);
1039           SourceTextModule::StoreVariable(module, var->index(), new_value);
1040           return true;
1041       }
1042       UNREACHABLE();
1043     }
1044   }
1045 
1046   return false;
1047 }
1048 
SetContextExtensionValue(Handle<String> variable_name,Handle<Object> new_value)1049 bool ScopeIterator::SetContextExtensionValue(Handle<String> variable_name,
1050                                              Handle<Object> new_value) {
1051   if (!context_->has_extension()) return false;
1052 
1053   DCHECK(context_->extension_object().IsJSContextExtensionObject());
1054   Handle<JSObject> ext(context_->extension_object(), isolate_);
1055   LookupIterator it(isolate_, ext, variable_name, LookupIterator::OWN);
1056   Maybe<bool> maybe = JSReceiver::HasProperty(&it);
1057   DCHECK(maybe.IsJust());
1058   if (!maybe.FromJust()) return false;
1059 
1060   CHECK(Object::SetDataProperty(&it, new_value).ToChecked());
1061   return true;
1062 }
1063 
SetContextVariableValue(Handle<String> variable_name,Handle<Object> new_value)1064 bool ScopeIterator::SetContextVariableValue(Handle<String> variable_name,
1065                                             Handle<Object> new_value) {
1066   int slot_index = context_->scope_info().ContextSlotIndex(variable_name);
1067   if (slot_index < 0) return false;
1068   context_->set(slot_index, *new_value);
1069   return true;
1070 }
1071 
SetModuleVariableValue(Handle<String> variable_name,Handle<Object> new_value)1072 bool ScopeIterator::SetModuleVariableValue(Handle<String> variable_name,
1073                                            Handle<Object> new_value) {
1074   DisallowGarbageCollection no_gc;
1075   int cell_index;
1076   VariableMode mode;
1077   InitializationFlag init_flag;
1078   MaybeAssignedFlag maybe_assigned_flag;
1079   cell_index = context_->scope_info().ModuleIndex(
1080       *variable_name, &mode, &init_flag, &maybe_assigned_flag);
1081 
1082   // Setting imports is currently not supported.
1083   if (SourceTextModuleDescriptor::GetCellIndexKind(cell_index) !=
1084       SourceTextModuleDescriptor::kExport) {
1085     return false;
1086   }
1087 
1088   Handle<SourceTextModule> module(context_->module(), isolate_);
1089   SourceTextModule::StoreVariable(module, cell_index, new_value);
1090   return true;
1091 }
1092 
SetScriptVariableValue(Handle<String> variable_name,Handle<Object> new_value)1093 bool ScopeIterator::SetScriptVariableValue(Handle<String> variable_name,
1094                                            Handle<Object> new_value) {
1095   Handle<ScriptContextTable> script_contexts(
1096       context_->global_object().native_context().script_context_table(),
1097       isolate_);
1098   VariableLookupResult lookup_result;
1099   if (script_contexts->Lookup(variable_name, &lookup_result)) {
1100     Handle<Context> script_context = ScriptContextTable::GetContext(
1101         isolate_, script_contexts, lookup_result.context_index);
1102     script_context->set(lookup_result.slot_index, *new_value);
1103     return true;
1104   }
1105 
1106   return false;
1107 }
1108 
1109 }  // namespace internal
1110 }  // namespace v8
1111