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