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