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