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