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