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