• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/debug/debug-scopes.h"
6 
7 #include <memory>
8 
9 #include "src/ast/ast.h"
10 #include "src/ast/scopes.h"
11 #include "src/debug/debug.h"
12 #include "src/frames-inl.h"
13 #include "src/globals.h"
14 #include "src/isolate-inl.h"
15 #include "src/objects/js-generator-inl.h"
16 #include "src/objects/module.h"
17 #include "src/parsing/parse-info.h"
18 #include "src/parsing/parsing.h"
19 #include "src/parsing/rewriter.h"
20 
21 namespace v8 {
22 namespace internal {
23 
ScopeIterator(Isolate * isolate,FrameInspector * frame_inspector,ScopeIterator::Option option)24 ScopeIterator::ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector,
25                              ScopeIterator::Option option)
26     : isolate_(isolate),
27       frame_inspector_(frame_inspector),
28       function_(frame_inspector_->GetFunction()),
29       script_(frame_inspector_->GetScript()) {
30   if (!frame_inspector->GetContext()->IsContext()) {
31     // Optimized frame, context or function cannot be materialized. Give up.
32     return;
33   }
34   context_ = Handle<Context>::cast(frame_inspector->GetContext());
35 
36   // We should not instantiate a ScopeIterator for wasm frames.
37   DCHECK_NE(Script::TYPE_WASM, frame_inspector->GetScript()->type());
38 
39   TryParseAndRetrieveScopes(option);
40 }
41 
~ScopeIterator()42 ScopeIterator::~ScopeIterator() { delete info_; }
43 
GetFunctionDebugName() const44 Handle<Object> ScopeIterator::GetFunctionDebugName() const {
45   if (!function_.is_null()) return JSFunction::GetDebugName(function_);
46 
47   if (!context_->IsNativeContext()) {
48     DisallowHeapAllocation no_gc;
49     ScopeInfo* closure_info = context_->closure_context()->scope_info();
50     Handle<String> debug_name(closure_info->FunctionDebugName(), isolate_);
51     if (debug_name->length() > 0) return debug_name;
52   }
53   return isolate_->factory()->undefined_value();
54 }
55 
ScopeIterator(Isolate * isolate,Handle<JSFunction> function)56 ScopeIterator::ScopeIterator(Isolate* isolate, Handle<JSFunction> function)
57     : isolate_(isolate),
58       context_(function->context(), isolate),
59       script_(Script::cast(function->shared()->script()), isolate) {
60   if (!function->shared()->IsSubjectToDebugging()) {
61     context_ = Handle<Context>();
62     return;
63   }
64   UnwrapEvaluationContext();
65 }
66 
ScopeIterator(Isolate * isolate,Handle<JSGeneratorObject> generator)67 ScopeIterator::ScopeIterator(Isolate* isolate,
68                              Handle<JSGeneratorObject> generator)
69     : isolate_(isolate),
70       generator_(generator),
71       function_(generator->function(), isolate),
72       context_(generator->context(), isolate),
73       script_(Script::cast(function_->shared()->script()), isolate) {
74   if (!function_->shared()->IsSubjectToDebugging()) {
75     context_ = Handle<Context>();
76     return;
77   }
78   TryParseAndRetrieveScopes(DEFAULT);
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 
TryParseAndRetrieveScopes(ScopeIterator::Option option)90 void ScopeIterator::TryParseAndRetrieveScopes(ScopeIterator::Option option) {
91   // Catch the case when the debugger stops in an internal function.
92   Handle<SharedFunctionInfo> shared_info(function_->shared(), isolate_);
93   Handle<ScopeInfo> scope_info(shared_info->scope_info(), isolate_);
94   if (shared_info->script()->IsUndefined(isolate_)) {
95     current_scope_ = closure_scope_ = nullptr;
96     context_ = handle(function_->context(), isolate_);
97     function_ = Handle<JSFunction>();
98     return;
99   }
100 
101   DCHECK_NE(IGNORE_NESTED_SCOPES, option);
102   bool ignore_nested_scopes = false;
103   if (shared_info->HasBreakInfo() && frame_inspector_ != nullptr) {
104     // The source position at return is always the end of the function,
105     // which is not consistent with the current scope chain. Therefore all
106     // nested with, catch and block contexts are skipped, and we can only
107     // inspect the function scope.
108     // This can only happen if we set a break point inside right before the
109     // return, which requires a debug info to be available.
110     Handle<DebugInfo> debug_info(shared_info->GetDebugInfo(), isolate_);
111 
112     // Find the break point where execution has stopped.
113     BreakLocation location = BreakLocation::FromFrame(debug_info, GetFrame());
114 
115     ignore_nested_scopes = location.IsReturn();
116   }
117 
118   // Reparse the code and analyze the scopes.
119   // Check whether we are in global, eval or function code.
120   if (scope_info->scope_type() == FUNCTION_SCOPE) {
121     // Inner function.
122     info_ = new ParseInfo(isolate_, shared_info);
123   } else {
124     // Global or eval code.
125     Handle<Script> script(Script::cast(shared_info->script()), isolate_);
126     info_ = new ParseInfo(isolate_, script);
127     if (scope_info->scope_type() == EVAL_SCOPE) {
128       info_->set_eval();
129       if (!context_->IsNativeContext()) {
130         info_->set_outer_scope_info(handle(context_->scope_info(), isolate_));
131       }
132       // Language mode may be inherited from the eval caller.
133       // Retrieve it from shared function info.
134       info_->set_language_mode(shared_info->language_mode());
135     } else if (scope_info->scope_type() == MODULE_SCOPE) {
136       DCHECK(info_->is_module());
137     } else {
138       DCHECK_EQ(SCRIPT_SCOPE, scope_info->scope_type());
139     }
140   }
141 
142   if (parsing::ParseAny(info_, shared_info, isolate_) &&
143       Rewriter::Rewrite(info_)) {
144     info_->ast_value_factory()->Internalize(isolate_);
145     closure_scope_ = info_->literal()->scope();
146 
147     if (option == COLLECT_NON_LOCALS) {
148       DCHECK(non_locals_.is_null());
149       non_locals_ = info_->literal()->scope()->CollectNonLocals(
150           isolate_, info_, StringSet::New(isolate_));
151     }
152 
153     CHECK(DeclarationScope::Analyze(info_));
154     if (ignore_nested_scopes) {
155       current_scope_ = closure_scope_;
156       start_scope_ = current_scope_;
157       if (closure_scope_->NeedsContext()) {
158         context_ = handle(context_->closure_context(), isolate_);
159       }
160     } else {
161       RetrieveScopeChain(closure_scope_);
162     }
163     UnwrapEvaluationContext();
164   } else {
165     // A failed reparse indicates that the preparser has diverged from the
166     // parser or that the preparse data given to the initial parse has been
167     // faulty. We fail in debug mode but in release mode we only provide the
168     // information we get from the context chain but nothing about
169     // completely stack allocated scopes or stack allocated locals.
170     // Or it could be due to stack overflow.
171     // Silently fail by presenting an empty context chain.
172     CHECK(isolate_->has_pending_exception());
173     isolate_->clear_pending_exception();
174     context_ = Handle<Context>();
175   }
176 }
177 
UnwrapEvaluationContext()178 void ScopeIterator::UnwrapEvaluationContext() {
179   if (!context_->IsDebugEvaluateContext()) return;
180   Context* current = *context_;
181   do {
182     Object* wrapped = current->get(Context::WRAPPED_CONTEXT_INDEX);
183     if (wrapped->IsContext()) {
184       current = Context::cast(wrapped);
185     } else {
186       DCHECK_NOT_NULL(current->previous());
187       current = current->previous();
188     }
189   } while (current->IsDebugEvaluateContext());
190   context_ = handle(current, isolate_);
191 }
192 
MaterializeScopeDetails()193 Handle<JSObject> ScopeIterator::MaterializeScopeDetails() {
194   // Calculate the size of the result.
195   Handle<FixedArray> details =
196       isolate_->factory()->NewFixedArray(kScopeDetailsSize);
197   // Fill in scope details.
198   details->set(kScopeDetailsTypeIndex, Smi::FromInt(Type()));
199   Handle<JSObject> scope_object = ScopeObject(Mode::ALL);
200   details->set(kScopeDetailsObjectIndex, *scope_object);
201   if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript) {
202     return isolate_->factory()->NewJSArrayWithElements(details);
203   } else if (HasContext()) {
204     Handle<Object> closure_name = GetFunctionDebugName();
205     details->set(kScopeDetailsNameIndex, *closure_name);
206     details->set(kScopeDetailsStartPositionIndex,
207                  Smi::FromInt(start_position()));
208     details->set(kScopeDetailsEndPositionIndex, Smi::FromInt(end_position()));
209     if (InInnerScope()) {
210       details->set(kScopeDetailsFunctionIndex, *function_);
211     }
212   }
213   return isolate_->factory()->NewJSArrayWithElements(details);
214 }
215 
HasPositionInfo()216 bool ScopeIterator::HasPositionInfo() {
217   return InInnerScope() || !context_->IsNativeContext();
218 }
219 
start_position()220 int ScopeIterator::start_position() {
221   if (InInnerScope()) return current_scope_->start_position();
222   if (context_->IsNativeContext()) return 0;
223   return context_->closure_context()->scope_info()->StartPosition();
224 }
225 
end_position()226 int ScopeIterator::end_position() {
227   if (InInnerScope()) return current_scope_->end_position();
228   if (context_->IsNativeContext()) return 0;
229   return context_->closure_context()->scope_info()->EndPosition();
230 }
231 
DeclaresLocals(Mode mode) const232 bool ScopeIterator::DeclaresLocals(Mode mode) const {
233   ScopeType type = Type();
234 
235   if (type == ScopeTypeWith) return mode == Mode::ALL;
236   if (type == ScopeTypeGlobal) return mode == Mode::ALL;
237 
238   bool declares_local = false;
239   auto visitor = [&](Handle<String> name, Handle<Object> value) {
240     declares_local = true;
241     return true;
242   };
243   VisitScope(visitor, mode);
244   return declares_local;
245 }
246 
HasContext() const247 bool ScopeIterator::HasContext() const {
248   return !InInnerScope() || current_scope_->NeedsContext();
249 }
250 
Next()251 void ScopeIterator::Next() {
252   DCHECK(!Done());
253 
254   ScopeType scope_type = Type();
255 
256   if (scope_type == ScopeTypeGlobal) {
257     // The global scope is always the last in the chain.
258     DCHECK(context_->IsNativeContext());
259     context_ = Handle<Context>();
260     DCHECK(Done());
261     return;
262   }
263 
264   bool inner = InInnerScope();
265   if (current_scope_ == closure_scope_) function_ = Handle<JSFunction>();
266 
267   if (scope_type == ScopeTypeScript) {
268     DCHECK_IMPLIES(InInnerScope(), current_scope_->is_script_scope());
269     seen_script_scope_ = true;
270     if (context_->IsScriptContext()) {
271       context_ = handle(context_->previous(), isolate_);
272     }
273   } else if (!inner) {
274     DCHECK(!context_->IsNativeContext());
275     context_ = handle(context_->previous(), isolate_);
276   } else {
277     DCHECK_NOT_NULL(current_scope_);
278     do {
279       if (current_scope_->NeedsContext()) {
280         DCHECK_NOT_NULL(context_->previous());
281         context_ = handle(context_->previous(), isolate_);
282       }
283       DCHECK_IMPLIES(InInnerScope(), current_scope_->outer_scope() != nullptr);
284       current_scope_ = current_scope_->outer_scope();
285       // Repeat to skip hidden scopes.
286     } while (current_scope_->is_hidden());
287   }
288 
289   UnwrapEvaluationContext();
290 }
291 
292 
293 // Return the type of the current scope.
Type() const294 ScopeIterator::ScopeType ScopeIterator::Type() const {
295   DCHECK(!Done());
296   if (InInnerScope()) {
297     switch (current_scope_->scope_type()) {
298       case FUNCTION_SCOPE:
299         DCHECK_IMPLIES(current_scope_->NeedsContext(),
300                        context_->IsFunctionContext());
301         return ScopeTypeLocal;
302       case MODULE_SCOPE:
303         DCHECK_IMPLIES(current_scope_->NeedsContext(),
304                        context_->IsModuleContext());
305         return ScopeTypeModule;
306       case SCRIPT_SCOPE:
307         DCHECK_IMPLIES(
308             current_scope_->NeedsContext(),
309             context_->IsScriptContext() || context_->IsNativeContext());
310         return ScopeTypeScript;
311       case WITH_SCOPE:
312         DCHECK_IMPLIES(
313             current_scope_->NeedsContext(),
314             context_->IsWithContext() || context_->IsDebugEvaluateContext());
315         return ScopeTypeWith;
316       case CATCH_SCOPE:
317         DCHECK(context_->IsCatchContext());
318         return ScopeTypeCatch;
319       case BLOCK_SCOPE:
320         DCHECK_IMPLIES(current_scope_->NeedsContext(),
321                        context_->IsBlockContext());
322         return ScopeTypeBlock;
323       case EVAL_SCOPE:
324         DCHECK_IMPLIES(current_scope_->NeedsContext(),
325                        context_->IsEvalContext());
326         return ScopeTypeEval;
327     }
328     UNREACHABLE();
329   }
330   if (context_->IsNativeContext()) {
331     DCHECK(context_->global_object()->IsJSGlobalObject());
332     // If we are at the native context and have not yet seen script scope,
333     // fake it.
334     return seen_script_scope_ ? ScopeTypeGlobal : ScopeTypeScript;
335   }
336   if (context_->IsFunctionContext() || context_->IsEvalContext()) {
337     return ScopeTypeClosure;
338   }
339   if (context_->IsCatchContext()) {
340     return ScopeTypeCatch;
341   }
342   if (context_->IsBlockContext()) {
343     return ScopeTypeBlock;
344   }
345   if (context_->IsModuleContext()) {
346     return ScopeTypeModule;
347   }
348   if (context_->IsScriptContext()) {
349     return ScopeTypeScript;
350   }
351   DCHECK(context_->IsWithContext() || context_->IsDebugEvaluateContext());
352   return ScopeTypeWith;
353 }
354 
ScopeObject(Mode mode)355 Handle<JSObject> ScopeIterator::ScopeObject(Mode mode) {
356   DCHECK(!Done());
357 
358   ScopeType type = Type();
359   if (type == ScopeTypeGlobal) {
360     DCHECK_EQ(Mode::ALL, mode);
361     return handle(context_->global_proxy(), isolate_);
362   }
363   if (type == ScopeTypeWith) {
364     DCHECK_EQ(Mode::ALL, mode);
365     return WithContextExtension();
366   }
367 
368   Handle<JSObject> scope = isolate_->factory()->NewJSObjectWithNullProto();
369   auto visitor = [=](Handle<String> name, Handle<Object> value) {
370     JSObject::AddProperty(isolate_, scope, name, value, NONE);
371     return false;
372   };
373 
374   VisitScope(visitor, mode);
375   return scope;
376 }
377 
VisitScope(const Visitor & visitor,Mode mode) const378 void ScopeIterator::VisitScope(const Visitor& visitor, Mode mode) const {
379   switch (Type()) {
380     case ScopeTypeLocal:
381     case ScopeTypeClosure:
382     case ScopeTypeCatch:
383     case ScopeTypeBlock:
384     case ScopeTypeEval:
385       return VisitLocalScope(visitor, mode);
386     case ScopeTypeModule:
387       if (InInnerScope()) {
388         return VisitLocalScope(visitor, mode);
389       }
390       DCHECK_EQ(Mode::ALL, mode);
391       return VisitModuleScope(visitor);
392     case ScopeTypeScript:
393       DCHECK_EQ(Mode::ALL, mode);
394       return VisitScriptScope(visitor);
395     case ScopeTypeWith:
396     case ScopeTypeGlobal:
397       UNREACHABLE();
398   }
399 }
400 
SetVariableValue(Handle<String> name,Handle<Object> value)401 bool ScopeIterator::SetVariableValue(Handle<String> name,
402                                      Handle<Object> value) {
403   DCHECK(!Done());
404   name = isolate_->factory()->InternalizeString(name);
405   switch (Type()) {
406     case ScopeTypeGlobal:
407     case ScopeTypeWith:
408       break;
409 
410     case ScopeTypeEval:
411     case ScopeTypeBlock:
412     case ScopeTypeCatch:
413     case ScopeTypeModule:
414       if (InInnerScope()) return SetLocalVariableValue(name, value);
415       if (Type() == ScopeTypeModule && SetModuleVariableValue(name, value)) {
416         return true;
417       }
418       return SetContextVariableValue(name, value);
419 
420     case ScopeTypeLocal:
421     case ScopeTypeClosure:
422       if (InInnerScope()) {
423         DCHECK_EQ(ScopeTypeLocal, Type());
424         if (SetLocalVariableValue(name, value)) return true;
425         // There may not be an associated context since we're InInnerScope().
426         if (!current_scope_->NeedsContext()) return false;
427       } else {
428         DCHECK_EQ(ScopeTypeClosure, Type());
429         if (SetContextVariableValue(name, value)) return true;
430       }
431       // The above functions only set variables statically declared in the
432       // function. There may be eval-introduced variables. Check them in
433       // SetContextExtensionValue.
434       return SetContextExtensionValue(name, value);
435 
436     case ScopeTypeScript:
437       return SetScriptVariableValue(name, value);
438   }
439   return false;
440 }
441 
GetNonLocals()442 Handle<StringSet> ScopeIterator::GetNonLocals() { return non_locals_; }
443 
444 #ifdef DEBUG
445 // Debug print of the content of the current scope.
DebugPrint()446 void ScopeIterator::DebugPrint() {
447   StdoutStream os;
448   DCHECK(!Done());
449   switch (Type()) {
450     case ScopeIterator::ScopeTypeGlobal:
451       os << "Global:\n";
452       context_->Print(os);
453       break;
454 
455     case ScopeIterator::ScopeTypeLocal: {
456       os << "Local:\n";
457       if (current_scope_->NeedsContext()) {
458         context_->Print(os);
459         if (context_->has_extension()) {
460           Handle<HeapObject> extension(context_->extension(), isolate_);
461           DCHECK(extension->IsJSContextExtensionObject());
462           extension->Print(os);
463         }
464       }
465       break;
466     }
467 
468     case ScopeIterator::ScopeTypeWith:
469       os << "With:\n";
470       context_->extension()->Print(os);
471       break;
472 
473     case ScopeIterator::ScopeTypeCatch:
474       os << "Catch:\n";
475       context_->extension()->Print(os);
476       context_->get(Context::THROWN_OBJECT_INDEX)->Print(os);
477       break;
478 
479     case ScopeIterator::ScopeTypeClosure:
480       os << "Closure:\n";
481       context_->Print(os);
482       if (context_->has_extension()) {
483         Handle<HeapObject> extension(context_->extension(), isolate_);
484         DCHECK(extension->IsJSContextExtensionObject());
485         extension->Print(os);
486       }
487       break;
488 
489     case ScopeIterator::ScopeTypeScript:
490       os << "Script:\n";
491       context_->global_object()
492           ->native_context()
493           ->script_context_table()
494           ->Print(os);
495       break;
496 
497     default:
498       UNREACHABLE();
499   }
500   PrintF("\n");
501 }
502 #endif
503 
GetSourcePosition()504 int ScopeIterator::GetSourcePosition() {
505   if (frame_inspector_) {
506     return frame_inspector_->GetSourcePosition();
507   } else {
508     DCHECK(!generator_.is_null());
509     return generator_->source_position();
510   }
511 }
512 
RetrieveScopeChain(DeclarationScope * scope)513 void ScopeIterator::RetrieveScopeChain(DeclarationScope* scope) {
514   DCHECK_NOT_NULL(scope);
515 
516   const int position = GetSourcePosition();
517 
518   Scope* parent = nullptr;
519   Scope* current = scope;
520   while (parent != current) {
521     parent = current;
522     for (Scope* inner_scope = current->inner_scope(); inner_scope != nullptr;
523          inner_scope = inner_scope->sibling()) {
524       int beg_pos = inner_scope->start_position();
525       int end_pos = inner_scope->end_position();
526       DCHECK((beg_pos >= 0 && end_pos >= 0) || inner_scope->is_hidden());
527       if (beg_pos <= position && position < end_pos) {
528         // Don't walk into inner functions.
529         if (!inner_scope->is_function_scope()) {
530           current = inner_scope;
531         }
532         break;
533       }
534     }
535   }
536 
537   start_scope_ = current;
538   current_scope_ = current;
539 }
540 
VisitScriptScope(const Visitor & visitor) const541 void ScopeIterator::VisitScriptScope(const Visitor& visitor) const {
542   Handle<JSGlobalObject> global(context_->global_object(), isolate_);
543   Handle<ScriptContextTable> script_contexts(
544       global->native_context()->script_context_table(), isolate_);
545 
546   // Skip the first script since that just declares 'this'.
547   for (int context_index = 1; context_index < script_contexts->used();
548        context_index++) {
549     Handle<Context> context = ScriptContextTable::GetContext(
550         isolate_, script_contexts, context_index);
551     Handle<ScopeInfo> scope_info(context->scope_info(), isolate_);
552     if (VisitContextLocals(visitor, scope_info, context)) return;
553   }
554 }
555 
VisitModuleScope(const Visitor & visitor) const556 void ScopeIterator::VisitModuleScope(const Visitor& visitor) const {
557   DCHECK(context_->IsModuleContext());
558 
559   Handle<ScopeInfo> scope_info(context_->scope_info(), isolate_);
560   if (VisitContextLocals(visitor, scope_info, context_)) return;
561 
562   int count_index = scope_info->ModuleVariableCountIndex();
563   int module_variable_count = Smi::cast(scope_info->get(count_index))->value();
564 
565   Handle<Module> module(context_->module(), isolate_);
566 
567   for (int i = 0; i < module_variable_count; ++i) {
568     int index;
569     Handle<String> name;
570     {
571       String* raw_name;
572       scope_info->ModuleVariable(i, &raw_name, &index);
573       CHECK(!ScopeInfo::VariableIsSynthetic(raw_name));
574       name = handle(raw_name, isolate_);
575     }
576     Handle<Object> value = Module::LoadVariable(isolate_, module, index);
577 
578     // Reflect variables under TDZ as undeclared in scope object.
579     if (value->IsTheHole(isolate_)) continue;
580     if (visitor(name, value)) return;
581   }
582 }
583 
VisitContextLocals(const Visitor & visitor,Handle<ScopeInfo> scope_info,Handle<Context> context) const584 bool ScopeIterator::VisitContextLocals(const Visitor& visitor,
585                                        Handle<ScopeInfo> scope_info,
586                                        Handle<Context> context) const {
587   // Fill all context locals to the context extension.
588   for (int i = 0; i < scope_info->ContextLocalCount(); ++i) {
589     Handle<String> name(scope_info->ContextLocalName(i), isolate_);
590     if (ScopeInfo::VariableIsSynthetic(*name)) continue;
591     int context_index = Context::MIN_CONTEXT_SLOTS + i;
592     Handle<Object> value(context->get(context_index), isolate_);
593     // Reflect variables under TDZ as undefined in scope object.
594     if (value->IsTheHole(isolate_)) continue;
595     if (visitor(name, value)) return true;
596   }
597   return false;
598 }
599 
VisitLocals(const Visitor & visitor,Mode mode) const600 bool ScopeIterator::VisitLocals(const Visitor& visitor, Mode mode) const {
601   for (Variable* var : *current_scope_->locals()) {
602     if (!var->is_this() && ScopeInfo::VariableIsSynthetic(*var->name())) {
603       continue;
604     }
605 
606     int index = var->index();
607     Handle<Object> value;
608     switch (var->location()) {
609       case VariableLocation::LOOKUP:
610         UNREACHABLE();
611         break;
612 
613       case VariableLocation::UNALLOCATED:
614         if (!var->is_this()) continue;
615         // No idea why we only add it sometimes.
616         if (mode == Mode::ALL) continue;
617         // No idea why this diverges...
618         value = frame_inspector_->GetReceiver();
619         break;
620 
621       case VariableLocation::PARAMETER: {
622         if (frame_inspector_ == nullptr) {
623           // Get the variable from the suspended generator.
624           DCHECK(!generator_.is_null());
625           if (var->is_this()) {
626             value = handle(generator_->receiver(), isolate_);
627           } else {
628             FixedArray* parameters_and_registers =
629                 generator_->parameters_and_registers();
630             DCHECK_LT(index, parameters_and_registers->length());
631             value = handle(parameters_and_registers->get(index), isolate_);
632           }
633         } else {
634           value = var->is_this() ? frame_inspector_->GetReceiver()
635                                  : frame_inspector_->GetParameter(index);
636 
637           if (value->IsOptimizedOut(isolate_)) {
638             value = isolate_->factory()->undefined_value();
639           } else if (var->is_this() && value->IsTheHole(isolate_)) {
640             value = isolate_->factory()->undefined_value();
641           }
642         }
643         break;
644       }
645 
646       case VariableLocation::LOCAL:
647         if (frame_inspector_ == nullptr) {
648           // Get the variable from the suspended generator.
649           DCHECK(!generator_.is_null());
650           FixedArray* parameters_and_registers =
651               generator_->parameters_and_registers();
652           int parameter_count =
653               function_->shared()->scope_info()->ParameterCount();
654           index += parameter_count;
655           DCHECK_LT(index, parameters_and_registers->length());
656           value = handle(parameters_and_registers->get(index), isolate_);
657           if (value->IsTheHole(isolate_)) {
658             value = isolate_->factory()->undefined_value();
659           }
660         } else {
661           value = frame_inspector_->GetExpression(index);
662           if (value->IsOptimizedOut(isolate_)) {
663             // We'll rematerialize this later.
664             if (current_scope_->is_declaration_scope() &&
665                 current_scope_->AsDeclarationScope()->arguments() == var) {
666               continue;
667             }
668             value = isolate_->factory()->undefined_value();
669           } else if (value->IsTheHole(isolate_)) {
670             // Reflect variables under TDZ as undeclared in scope object.
671             continue;
672           }
673         }
674         break;
675 
676       case VariableLocation::CONTEXT:
677         if (mode == Mode::STACK) continue;
678         // TODO(verwaest): Why don't we want to show it if it's there?...
679         if (var->is_this()) continue;
680         DCHECK(var->IsContextSlot());
681         value = handle(context_->get(index), isolate_);
682         // Reflect variables under TDZ as undeclared in scope object.
683         if (value->IsTheHole(isolate_)) continue;
684         break;
685 
686       case VariableLocation::MODULE: {
687         if (mode == Mode::STACK) continue;
688         // if (var->IsExport()) continue;
689         Handle<Module> module(context_->module(), isolate_);
690         value = Module::LoadVariable(isolate_, module, var->index());
691         // Reflect variables under TDZ as undeclared in scope object.
692         if (value->IsTheHole(isolate_)) continue;
693         break;
694       }
695     }
696 
697     if (visitor(var->name(), value)) return true;
698   }
699   return false;
700 }
701 
702 // Retrieve the with-context extension object. If the extension object is
703 // a proxy, return an empty object.
WithContextExtension()704 Handle<JSObject> ScopeIterator::WithContextExtension() {
705   DCHECK(context_->IsWithContext());
706   if (context_->extension_receiver()->IsJSProxy()) {
707     return isolate_->factory()->NewJSObjectWithNullProto();
708   }
709   return handle(JSObject::cast(context_->extension_receiver()), isolate_);
710 }
711 
712 // Create a plain JSObject which materializes the block scope for the specified
713 // block context.
VisitLocalScope(const Visitor & visitor,Mode mode) const714 void ScopeIterator::VisitLocalScope(const Visitor& visitor, Mode mode) const {
715   if (InInnerScope()) {
716     if (VisitLocals(visitor, mode)) return;
717     if (mode == Mode::STACK && Type() == ScopeTypeLocal) {
718       // Hide |this| in arrow functions that may be embedded in other functions
719       // but don't force |this| to be context-allocated. Otherwise we'd find the
720       // wrong |this| value.
721       if (!closure_scope_->has_this_declaration() &&
722           !non_locals_->Has(isolate_, isolate_->factory()->this_string())) {
723         if (visitor(isolate_->factory()->this_string(),
724                     isolate_->factory()->undefined_value()))
725           return;
726       }
727       // Add |arguments| to the function scope even if it wasn't used.
728       // Currently we don't yet support materializing the arguments object of
729       // suspended generators. We'd need to read the arguments out from the
730       // suspended generator rather than from an activation as
731       // FunctionGetArguments does.
732       if (frame_inspector_ != nullptr && !closure_scope_->is_arrow_scope() &&
733           (closure_scope_->arguments() == nullptr ||
734            frame_inspector_->GetExpression(closure_scope_->arguments()->index())
735                ->IsOptimizedOut(isolate_))) {
736         JavaScriptFrame* frame = GetFrame();
737         Handle<JSObject> arguments = Accessors::FunctionGetArguments(
738             frame, frame_inspector_->inlined_frame_index());
739         if (visitor(isolate_->factory()->arguments_string(), arguments)) return;
740       }
741     }
742   } else {
743     DCHECK_EQ(Mode::ALL, mode);
744     Handle<ScopeInfo> scope_info(context_->scope_info(), isolate_);
745     if (VisitContextLocals(visitor, scope_info, context_)) return;
746   }
747 
748   if (mode == Mode::ALL && HasContext()) {
749     DCHECK(!context_->IsScriptContext());
750     DCHECK(!context_->IsNativeContext());
751     DCHECK(!context_->IsWithContext());
752     if (!context_->scope_info()->CallsSloppyEval()) return;
753     if (context_->extension_object() == nullptr) return;
754     Handle<JSObject> extension(context_->extension_object(), isolate_);
755     Handle<FixedArray> keys =
756         KeyAccumulator::GetKeys(extension, KeyCollectionMode::kOwnOnly,
757                                 ENUMERABLE_STRINGS)
758             .ToHandleChecked();
759 
760     for (int i = 0; i < keys->length(); i++) {
761       // Names of variables introduced by eval are strings.
762       DCHECK(keys->get(i)->IsString());
763       Handle<String> key(String::cast(keys->get(i)), isolate_);
764       Handle<Object> value = JSReceiver::GetDataProperty(extension, key);
765       if (visitor(key, value)) return;
766     }
767   }
768 }
769 
SetLocalVariableValue(Handle<String> variable_name,Handle<Object> new_value)770 bool ScopeIterator::SetLocalVariableValue(Handle<String> variable_name,
771                                           Handle<Object> new_value) {
772   // TODO(verwaest): Walk parameters backwards, not forwards.
773   // TODO(verwaest): Use VariableMap rather than locals() list for lookup.
774   for (Variable* var : *current_scope_->locals()) {
775     if (String::Equals(isolate_, var->name(), variable_name)) {
776       int index = var->index();
777       switch (var->location()) {
778         case VariableLocation::LOOKUP:
779         case VariableLocation::UNALLOCATED:
780           // Drop assignments to unallocated locals.
781           DCHECK(var->is_this() ||
782                  *variable_name == ReadOnlyRoots(isolate_).arguments_string());
783           return false;
784 
785         case VariableLocation::PARAMETER: {
786           if (var->is_this()) return false;
787           if (frame_inspector_ == nullptr) {
788             // Set the variable in the suspended generator.
789             DCHECK(!generator_.is_null());
790             Handle<FixedArray> parameters_and_registers(
791                 generator_->parameters_and_registers(), isolate_);
792             DCHECK_LT(index, parameters_and_registers->length());
793             parameters_and_registers->set(index, *new_value);
794           } else {
795             JavaScriptFrame* frame = GetFrame();
796             if (frame->is_optimized()) return false;
797 
798             frame->SetParameterValue(index, *new_value);
799           }
800           return true;
801         }
802 
803         case VariableLocation::LOCAL:
804           if (frame_inspector_ == nullptr) {
805             // Set the variable in the suspended generator.
806             DCHECK(!generator_.is_null());
807             int parameter_count =
808                 function_->shared()->scope_info()->ParameterCount();
809             index += parameter_count;
810             Handle<FixedArray> parameters_and_registers(
811                 generator_->parameters_and_registers(), isolate_);
812             DCHECK_LT(index, parameters_and_registers->length());
813             parameters_and_registers->set(index, *new_value);
814           } else {
815             // Set the variable on the stack.
816             JavaScriptFrame* frame = GetFrame();
817             if (frame->is_optimized()) return false;
818 
819             frame->SetExpression(index, *new_value);
820           }
821           return true;
822 
823         case VariableLocation::CONTEXT:
824           DCHECK(var->IsContextSlot());
825           context_->set(index, *new_value);
826           return true;
827 
828         case VariableLocation::MODULE:
829           if (!var->IsExport()) return false;
830           Handle<Module> module(context_->module(), isolate_);
831           Module::StoreVariable(module, var->index(), new_value);
832           return true;
833       }
834       UNREACHABLE();
835     }
836   }
837 
838   return false;
839 }
840 
SetContextExtensionValue(Handle<String> variable_name,Handle<Object> new_value)841 bool ScopeIterator::SetContextExtensionValue(Handle<String> variable_name,
842                                              Handle<Object> new_value) {
843   if (!context_->has_extension()) return false;
844 
845   DCHECK(context_->extension_object()->IsJSContextExtensionObject());
846   Handle<JSObject> ext(context_->extension_object(), isolate_);
847   LookupIterator it(isolate_, ext, variable_name, LookupIterator::OWN);
848   Maybe<bool> maybe = JSReceiver::HasOwnProperty(ext, variable_name);
849   DCHECK(maybe.IsJust());
850   if (!maybe.FromJust()) return false;
851 
852   CHECK(Object::SetDataProperty(&it, new_value).ToChecked());
853   return true;
854 }
855 
SetContextVariableValue(Handle<String> variable_name,Handle<Object> new_value)856 bool ScopeIterator::SetContextVariableValue(Handle<String> variable_name,
857                                             Handle<Object> new_value) {
858   Handle<ScopeInfo> scope_info(context_->scope_info(), isolate_);
859 
860   VariableMode mode;
861   InitializationFlag flag;
862   MaybeAssignedFlag maybe_assigned_flag;
863   int slot_index = ScopeInfo::ContextSlotIndex(scope_info, variable_name, &mode,
864                                                &flag, &maybe_assigned_flag);
865   if (slot_index < 0) return false;
866 
867   context_->set(slot_index, *new_value);
868   return true;
869 }
870 
SetModuleVariableValue(Handle<String> variable_name,Handle<Object> new_value)871 bool ScopeIterator::SetModuleVariableValue(Handle<String> variable_name,
872                                            Handle<Object> new_value) {
873   int cell_index;
874   VariableMode mode;
875   InitializationFlag init_flag;
876   MaybeAssignedFlag maybe_assigned_flag;
877   cell_index = context_->scope_info()->ModuleIndex(
878       variable_name, &mode, &init_flag, &maybe_assigned_flag);
879 
880   // Setting imports is currently not supported.
881   if (ModuleDescriptor::GetCellIndexKind(cell_index) !=
882       ModuleDescriptor::kExport) {
883     return false;
884   }
885 
886   Handle<Module> module(context_->module(), isolate_);
887   Module::StoreVariable(module, cell_index, new_value);
888   return true;
889 }
890 
SetScriptVariableValue(Handle<String> variable_name,Handle<Object> new_value)891 bool ScopeIterator::SetScriptVariableValue(Handle<String> variable_name,
892                                            Handle<Object> new_value) {
893   Handle<ScriptContextTable> script_contexts(
894       context_->global_object()->native_context()->script_context_table(),
895       isolate_);
896   ScriptContextTable::LookupResult lookup_result;
897   if (ScriptContextTable::Lookup(isolate_, script_contexts, variable_name,
898                                  &lookup_result)) {
899     Handle<Context> script_context = ScriptContextTable::GetContext(
900         isolate_, script_contexts, lookup_result.context_index);
901     script_context->set(lookup_result.slot_index, *new_value);
902     return true;
903   }
904 
905   return false;
906 }
907 
908 }  // namespace internal
909 }  // namespace v8
910