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