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/scopes.h"
10 #include "src/debug/debug.h"
11 #include "src/frames-inl.h"
12 #include "src/globals.h"
13 #include "src/isolate-inl.h"
14 #include "src/parsing/parse-info.h"
15 #include "src/parsing/parser.h"
16 #include "src/parsing/rewriter.h"
17
18 namespace v8 {
19 namespace internal {
20
ScopeIterator(Isolate * isolate,FrameInspector * frame_inspector,ScopeIterator::Option option)21 ScopeIterator::ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector,
22 ScopeIterator::Option option)
23 : isolate_(isolate),
24 frame_inspector_(frame_inspector),
25 nested_scope_chain_(4),
26 seen_script_scope_(false),
27 failed_(false) {
28 if (!frame_inspector->GetContext()->IsContext()) {
29 // Optimized frame, context or function cannot be materialized. Give up.
30 return;
31 }
32
33 context_ = Handle<Context>::cast(frame_inspector->GetContext());
34
35 // We should not instantiate a ScopeIterator for wasm frames.
36 DCHECK(frame_inspector->GetScript()->type() != Script::TYPE_WASM);
37
38 // Catch the case when the debugger stops in an internal function.
39 Handle<JSFunction> function = GetFunction();
40 Handle<SharedFunctionInfo> shared_info(function->shared());
41 Handle<ScopeInfo> scope_info(shared_info->scope_info());
42 if (shared_info->script()->IsUndefined(isolate)) {
43 while (context_->closure() == *function) {
44 context_ = Handle<Context>(context_->previous(), isolate_);
45 }
46 return;
47 }
48
49 // Currently it takes too much time to find nested scopes due to script
50 // parsing. Sometimes we want to run the ScopeIterator as fast as possible
51 // (for example, while collecting async call stacks on every
52 // addEventListener call), even if we drop some nested scopes.
53 // Later we may optimize getting the nested scopes (cache the result?)
54 // and include nested scopes into the "fast" iteration case as well.
55 bool ignore_nested_scopes = (option == IGNORE_NESTED_SCOPES);
56 bool collect_non_locals = (option == COLLECT_NON_LOCALS);
57 if (!ignore_nested_scopes && shared_info->HasDebugInfo()) {
58 // The source position at return is always the end of the function,
59 // which is not consistent with the current scope chain. Therefore all
60 // nested with, catch and block contexts are skipped, and we can only
61 // inspect the function scope.
62 // This can only happen if we set a break point inside right before the
63 // return, which requires a debug info to be available.
64 Handle<DebugInfo> debug_info(shared_info->GetDebugInfo());
65
66 // Find the break point where execution has stopped.
67 BreakLocation location = BreakLocation::FromFrame(debug_info, GetFrame());
68
69 ignore_nested_scopes = location.IsReturn();
70 }
71
72 if (ignore_nested_scopes) {
73 if (scope_info->HasContext()) {
74 context_ = Handle<Context>(context_->declaration_context(), isolate_);
75 } else {
76 while (context_->closure() == *function) {
77 context_ = Handle<Context>(context_->previous(), isolate_);
78 }
79 }
80 if (scope_info->scope_type() == FUNCTION_SCOPE) {
81 nested_scope_chain_.Add(ExtendedScopeInfo(scope_info,
82 shared_info->start_position(),
83 shared_info->end_position()));
84 }
85 if (!collect_non_locals) return;
86 }
87
88 // Reparse the code and analyze the scopes.
89 // Check whether we are in global, eval or function code.
90 Zone zone(isolate->allocator(), ZONE_NAME);
91 std::unique_ptr<ParseInfo> info;
92 if (scope_info->scope_type() != FUNCTION_SCOPE) {
93 // Global or eval code.
94 Handle<Script> script(Script::cast(shared_info->script()));
95 info.reset(new ParseInfo(&zone, script));
96 if (scope_info->scope_type() == EVAL_SCOPE) {
97 info->set_eval();
98 if (!function->context()->IsNativeContext()) {
99 info->set_outer_scope_info(handle(function->context()->scope_info()));
100 }
101 // Language mode may be inherited from the eval caller.
102 // Retrieve it from shared function info.
103 info->set_language_mode(shared_info->language_mode());
104 } else if (scope_info->scope_type() == MODULE_SCOPE) {
105 info->set_module();
106 } else {
107 DCHECK(scope_info->scope_type() == SCRIPT_SCOPE);
108 }
109 } else {
110 // Inner function.
111 info.reset(new ParseInfo(&zone, shared_info));
112 }
113 if (Parser::ParseStatic(info.get()) && Rewriter::Rewrite(info.get())) {
114 DeclarationScope* scope = info->literal()->scope();
115 if (!ignore_nested_scopes || collect_non_locals) {
116 CollectNonLocals(info.get(), scope);
117 }
118 if (!ignore_nested_scopes) {
119 DeclarationScope::Analyze(info.get(), AnalyzeMode::kDebugger);
120 RetrieveScopeChain(scope);
121 }
122 } else if (!ignore_nested_scopes) {
123 // A failed reparse indicates that the preparser has diverged from the
124 // parser or that the preparse data given to the initial parse has been
125 // faulty. We fail in debug mode but in release mode we only provide the
126 // information we get from the context chain but nothing about
127 // completely stack allocated scopes or stack allocated locals.
128 // Or it could be due to stack overflow.
129 DCHECK(isolate_->has_pending_exception());
130 failed_ = true;
131 }
132 UnwrapEvaluationContext();
133 }
134
135
ScopeIterator(Isolate * isolate,Handle<JSFunction> function)136 ScopeIterator::ScopeIterator(Isolate* isolate, Handle<JSFunction> function)
137 : isolate_(isolate),
138 frame_inspector_(NULL),
139 context_(function->context()),
140 seen_script_scope_(false),
141 failed_(false) {
142 if (!function->shared()->IsSubjectToDebugging()) context_ = Handle<Context>();
143 UnwrapEvaluationContext();
144 }
145
ScopeIterator(Isolate * isolate,Handle<JSGeneratorObject> generator)146 ScopeIterator::ScopeIterator(Isolate* isolate,
147 Handle<JSGeneratorObject> generator)
148 : isolate_(isolate),
149 frame_inspector_(NULL),
150 context_(generator->context()),
151 seen_script_scope_(false),
152 failed_(false) {
153 if (!generator->function()->shared()->IsSubjectToDebugging()) {
154 context_ = Handle<Context>();
155 }
156 UnwrapEvaluationContext();
157 }
158
UnwrapEvaluationContext()159 void ScopeIterator::UnwrapEvaluationContext() {
160 while (true) {
161 if (context_.is_null()) return;
162 if (!context_->IsDebugEvaluateContext()) return;
163 Handle<Object> wrapped(context_->get(Context::WRAPPED_CONTEXT_INDEX),
164 isolate_);
165 if (wrapped->IsContext()) {
166 context_ = Handle<Context>::cast(wrapped);
167 } else {
168 context_ = Handle<Context>(context_->previous(), isolate_);
169 }
170 }
171 }
172
173
MaterializeScopeDetails()174 MUST_USE_RESULT MaybeHandle<JSObject> ScopeIterator::MaterializeScopeDetails() {
175 // Calculate the size of the result.
176 Handle<FixedArray> details =
177 isolate_->factory()->NewFixedArray(kScopeDetailsSize);
178 // Fill in scope details.
179 details->set(kScopeDetailsTypeIndex, Smi::FromInt(Type()));
180 Handle<JSObject> scope_object;
181 ASSIGN_RETURN_ON_EXCEPTION(isolate_, scope_object, ScopeObject(), JSObject);
182 details->set(kScopeDetailsObjectIndex, *scope_object);
183 Handle<JSFunction> js_function = HasContext()
184 ? handle(CurrentContext()->closure())
185 : Handle<JSFunction>::null();
186 if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript) {
187 return isolate_->factory()->NewJSArrayWithElements(details);
188 }
189
190 int start_position = 0;
191 int end_position = 0;
192 if (!nested_scope_chain_.is_empty()) {
193 js_function = GetFunction();
194 start_position = nested_scope_chain_.last().start_position;
195 end_position = nested_scope_chain_.last().end_position;
196 } else if (!js_function.is_null()) {
197 start_position = js_function->shared()->start_position();
198 end_position = js_function->shared()->end_position();
199 }
200
201 if (!js_function.is_null()) {
202 Handle<String> closure_name = JSFunction::GetDebugName(js_function);
203 if (!closure_name.is_null() && closure_name->length() != 0) {
204 details->set(kScopeDetailsNameIndex, *closure_name);
205 }
206 details->set(kScopeDetailsStartPositionIndex, Smi::FromInt(start_position));
207 details->set(kScopeDetailsEndPositionIndex, Smi::FromInt(end_position));
208 details->set(kScopeDetailsFunctionIndex, *js_function);
209 }
210 return isolate_->factory()->NewJSArrayWithElements(details);
211 }
212
213
Next()214 void ScopeIterator::Next() {
215 DCHECK(!failed_);
216 ScopeType scope_type = Type();
217 if (scope_type == ScopeTypeGlobal) {
218 // The global scope is always the last in the chain.
219 DCHECK(context_->IsNativeContext());
220 context_ = Handle<Context>();
221 } else if (scope_type == ScopeTypeScript) {
222 seen_script_scope_ = true;
223 if (context_->IsScriptContext()) {
224 context_ = Handle<Context>(context_->previous(), isolate_);
225 }
226 if (!nested_scope_chain_.is_empty()) {
227 DCHECK_EQ(nested_scope_chain_.last().scope_info->scope_type(),
228 SCRIPT_SCOPE);
229 nested_scope_chain_.RemoveLast();
230 DCHECK(nested_scope_chain_.is_empty());
231 }
232 CHECK(context_->IsNativeContext());
233 } else if (nested_scope_chain_.is_empty()) {
234 context_ = Handle<Context>(context_->previous(), isolate_);
235 } else {
236 do {
237 if (nested_scope_chain_.last().scope_info->HasContext()) {
238 DCHECK(context_->previous() != NULL);
239 context_ = Handle<Context>(context_->previous(), isolate_);
240 }
241 nested_scope_chain_.RemoveLast();
242 if (nested_scope_chain_.is_empty()) break;
243 // Repeat to skip hidden scopes.
244 } while (nested_scope_chain_.last().is_hidden());
245 }
246 UnwrapEvaluationContext();
247 }
248
249
250 // Return the type of the current scope.
Type()251 ScopeIterator::ScopeType ScopeIterator::Type() {
252 DCHECK(!failed_);
253 if (!nested_scope_chain_.is_empty()) {
254 Handle<ScopeInfo> scope_info = nested_scope_chain_.last().scope_info;
255 switch (scope_info->scope_type()) {
256 case FUNCTION_SCOPE:
257 DCHECK(context_->IsFunctionContext() || !scope_info->HasContext());
258 return ScopeTypeLocal;
259 case MODULE_SCOPE:
260 DCHECK(context_->IsModuleContext());
261 return ScopeTypeModule;
262 case SCRIPT_SCOPE:
263 DCHECK(context_->IsScriptContext() || context_->IsNativeContext());
264 return ScopeTypeScript;
265 case WITH_SCOPE:
266 DCHECK(context_->IsWithContext() || context_->IsDebugEvaluateContext());
267 return ScopeTypeWith;
268 case CATCH_SCOPE:
269 DCHECK(context_->IsCatchContext());
270 return ScopeTypeCatch;
271 case BLOCK_SCOPE:
272 DCHECK(!scope_info->HasContext() || context_->IsBlockContext());
273 return ScopeTypeBlock;
274 case EVAL_SCOPE:
275 DCHECK(!scope_info->HasContext() || context_->IsFunctionContext());
276 return ScopeTypeEval;
277 }
278 UNREACHABLE();
279 }
280 if (context_->IsNativeContext()) {
281 DCHECK(context_->global_object()->IsJSGlobalObject());
282 // If we are at the native context and have not yet seen script scope,
283 // fake it.
284 return seen_script_scope_ ? ScopeTypeGlobal : ScopeTypeScript;
285 }
286 if (context_->IsFunctionContext()) {
287 return ScopeTypeClosure;
288 }
289 if (context_->IsCatchContext()) {
290 return ScopeTypeCatch;
291 }
292 if (context_->IsBlockContext()) {
293 return ScopeTypeBlock;
294 }
295 if (context_->IsModuleContext()) {
296 return ScopeTypeModule;
297 }
298 if (context_->IsScriptContext()) {
299 return ScopeTypeScript;
300 }
301 DCHECK(context_->IsWithContext() || context_->IsDebugEvaluateContext());
302 return ScopeTypeWith;
303 }
304
305
ScopeObject()306 MaybeHandle<JSObject> ScopeIterator::ScopeObject() {
307 DCHECK(!failed_);
308 switch (Type()) {
309 case ScopeIterator::ScopeTypeGlobal:
310 return Handle<JSObject>(CurrentContext()->global_proxy());
311 case ScopeIterator::ScopeTypeScript:
312 return MaterializeScriptScope();
313 case ScopeIterator::ScopeTypeLocal:
314 // Materialize the content of the local scope into a JSObject.
315 DCHECK(nested_scope_chain_.length() == 1);
316 return MaterializeLocalScope();
317 case ScopeIterator::ScopeTypeWith:
318 return WithContextExtension();
319 case ScopeIterator::ScopeTypeCatch:
320 return MaterializeCatchScope();
321 case ScopeIterator::ScopeTypeClosure:
322 // Materialize the content of the closure scope into a JSObject.
323 return MaterializeClosure();
324 case ScopeIterator::ScopeTypeBlock:
325 case ScopeIterator::ScopeTypeEval:
326 return MaterializeInnerScope();
327 case ScopeIterator::ScopeTypeModule:
328 return MaterializeModuleScope();
329 }
330 UNREACHABLE();
331 return Handle<JSObject>();
332 }
333
334
HasContext()335 bool ScopeIterator::HasContext() {
336 ScopeType type = Type();
337 if (type == ScopeTypeBlock || type == ScopeTypeLocal ||
338 type == ScopeTypeEval) {
339 if (!nested_scope_chain_.is_empty()) {
340 return nested_scope_chain_.last().scope_info->HasContext();
341 }
342 }
343 return true;
344 }
345
346
SetVariableValue(Handle<String> variable_name,Handle<Object> new_value)347 bool ScopeIterator::SetVariableValue(Handle<String> variable_name,
348 Handle<Object> new_value) {
349 DCHECK(!failed_);
350 switch (Type()) {
351 case ScopeIterator::ScopeTypeGlobal:
352 break;
353 case ScopeIterator::ScopeTypeLocal:
354 return SetLocalVariableValue(variable_name, new_value);
355 case ScopeIterator::ScopeTypeWith:
356 break;
357 case ScopeIterator::ScopeTypeCatch:
358 return SetCatchVariableValue(variable_name, new_value);
359 case ScopeIterator::ScopeTypeClosure:
360 return SetClosureVariableValue(variable_name, new_value);
361 case ScopeIterator::ScopeTypeScript:
362 return SetScriptVariableValue(variable_name, new_value);
363 case ScopeIterator::ScopeTypeBlock:
364 case ScopeIterator::ScopeTypeEval:
365 return SetInnerScopeVariableValue(variable_name, new_value);
366 case ScopeIterator::ScopeTypeModule:
367 // TODO(neis): Implement.
368 break;
369 }
370 return false;
371 }
372
373
CurrentScopeInfo()374 Handle<ScopeInfo> ScopeIterator::CurrentScopeInfo() {
375 DCHECK(!failed_);
376 if (!nested_scope_chain_.is_empty()) {
377 return nested_scope_chain_.last().scope_info;
378 } else if (context_->IsBlockContext()) {
379 return Handle<ScopeInfo>(context_->scope_info());
380 } else if (context_->IsFunctionContext()) {
381 return Handle<ScopeInfo>(context_->closure()->shared()->scope_info());
382 }
383 return Handle<ScopeInfo>::null();
384 }
385
386
CurrentContext()387 Handle<Context> ScopeIterator::CurrentContext() {
388 DCHECK(!failed_);
389 if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript ||
390 nested_scope_chain_.is_empty()) {
391 return context_;
392 } else if (nested_scope_chain_.last().scope_info->HasContext()) {
393 return context_;
394 } else {
395 return Handle<Context>();
396 }
397 }
398
GetNonLocals()399 Handle<StringSet> ScopeIterator::GetNonLocals() { return non_locals_; }
400
401 #ifdef DEBUG
402 // Debug print of the content of the current scope.
DebugPrint()403 void ScopeIterator::DebugPrint() {
404 OFStream os(stdout);
405 DCHECK(!failed_);
406 switch (Type()) {
407 case ScopeIterator::ScopeTypeGlobal:
408 os << "Global:\n";
409 CurrentContext()->Print(os);
410 break;
411
412 case ScopeIterator::ScopeTypeLocal: {
413 os << "Local:\n";
414 GetFunction()->shared()->scope_info()->Print();
415 if (!CurrentContext().is_null()) {
416 CurrentContext()->Print(os);
417 if (CurrentContext()->has_extension()) {
418 Handle<HeapObject> extension(CurrentContext()->extension(), isolate_);
419 if (extension->IsJSContextExtensionObject()) {
420 extension->Print(os);
421 }
422 }
423 }
424 break;
425 }
426
427 case ScopeIterator::ScopeTypeWith:
428 os << "With:\n";
429 CurrentContext()->extension()->Print(os);
430 break;
431
432 case ScopeIterator::ScopeTypeCatch:
433 os << "Catch:\n";
434 CurrentContext()->extension()->Print(os);
435 CurrentContext()->get(Context::THROWN_OBJECT_INDEX)->Print(os);
436 break;
437
438 case ScopeIterator::ScopeTypeClosure:
439 os << "Closure:\n";
440 CurrentContext()->Print(os);
441 if (CurrentContext()->has_extension()) {
442 Handle<HeapObject> extension(CurrentContext()->extension(), isolate_);
443 if (extension->IsJSContextExtensionObject()) {
444 extension->Print(os);
445 }
446 }
447 break;
448
449 case ScopeIterator::ScopeTypeScript:
450 os << "Script:\n";
451 CurrentContext()
452 ->global_object()
453 ->native_context()
454 ->script_context_table()
455 ->Print(os);
456 break;
457
458 default:
459 UNREACHABLE();
460 }
461 PrintF("\n");
462 }
463 #endif
464
RetrieveScopeChain(DeclarationScope * scope)465 void ScopeIterator::RetrieveScopeChain(DeclarationScope* scope) {
466 DCHECK_NOT_NULL(scope);
467 int source_position = frame_inspector_->GetSourcePosition();
468 GetNestedScopeChain(isolate_, scope, source_position);
469 }
470
CollectNonLocals(ParseInfo * info,DeclarationScope * scope)471 void ScopeIterator::CollectNonLocals(ParseInfo* info, DeclarationScope* scope) {
472 DCHECK_NOT_NULL(scope);
473 DCHECK(non_locals_.is_null());
474 non_locals_ = scope->CollectNonLocals(info, StringSet::New(isolate_));
475 }
476
477
MaterializeScriptScope()478 MaybeHandle<JSObject> ScopeIterator::MaterializeScriptScope() {
479 Handle<JSGlobalObject> global(CurrentContext()->global_object());
480 Handle<ScriptContextTable> script_contexts(
481 global->native_context()->script_context_table());
482
483 Handle<JSObject> script_scope =
484 isolate_->factory()->NewJSObjectWithNullProto();
485
486 for (int context_index = 0; context_index < script_contexts->used();
487 context_index++) {
488 Handle<Context> context =
489 ScriptContextTable::GetContext(script_contexts, context_index);
490 Handle<ScopeInfo> scope_info(context->scope_info());
491 CopyContextLocalsToScopeObject(scope_info, context, script_scope);
492 }
493 return script_scope;
494 }
495
496
MaterializeLocalScope()497 MaybeHandle<JSObject> ScopeIterator::MaterializeLocalScope() {
498 Handle<JSFunction> function = GetFunction();
499
500 Handle<JSObject> local_scope =
501 isolate_->factory()->NewJSObjectWithNullProto();
502 frame_inspector_->MaterializeStackLocals(local_scope, function);
503
504 Handle<Context> frame_context =
505 Handle<Context>::cast(frame_inspector_->GetContext());
506
507 HandleScope scope(isolate_);
508 Handle<SharedFunctionInfo> shared(function->shared());
509 Handle<ScopeInfo> scope_info(shared->scope_info());
510
511 if (!scope_info->HasContext()) return local_scope;
512
513 // Fill all context locals.
514 Handle<Context> function_context(frame_context->closure_context());
515 CopyContextLocalsToScopeObject(scope_info, function_context, local_scope);
516
517 // Finally copy any properties from the function context extension.
518 // These will be variables introduced by eval.
519 if (function_context->closure() == *function &&
520 !function_context->IsNativeContext()) {
521 CopyContextExtensionToScopeObject(function_context, local_scope,
522 KeyCollectionMode::kIncludePrototypes);
523 }
524
525 return local_scope;
526 }
527
528
529 // Create a plain JSObject which materializes the closure content for the
530 // context.
MaterializeClosure()531 Handle<JSObject> ScopeIterator::MaterializeClosure() {
532 Handle<Context> context = CurrentContext();
533 DCHECK(context->IsFunctionContext());
534
535 Handle<SharedFunctionInfo> shared(context->closure()->shared());
536 Handle<ScopeInfo> scope_info(shared->scope_info());
537
538 // Allocate and initialize a JSObject with all the content of this function
539 // closure.
540 Handle<JSObject> closure_scope =
541 isolate_->factory()->NewJSObjectWithNullProto();
542
543 // Fill all context locals to the context extension.
544 CopyContextLocalsToScopeObject(scope_info, context, closure_scope);
545
546 // Finally copy any properties from the function context extension. This will
547 // be variables introduced by eval.
548 CopyContextExtensionToScopeObject(context, closure_scope,
549 KeyCollectionMode::kOwnOnly);
550
551 return closure_scope;
552 }
553
554
555 // Create a plain JSObject which materializes the scope for the specified
556 // catch context.
MaterializeCatchScope()557 Handle<JSObject> ScopeIterator::MaterializeCatchScope() {
558 Handle<Context> context = CurrentContext();
559 DCHECK(context->IsCatchContext());
560 Handle<String> name(context->catch_name());
561 Handle<Object> thrown_object(context->get(Context::THROWN_OBJECT_INDEX),
562 isolate_);
563 Handle<JSObject> catch_scope =
564 isolate_->factory()->NewJSObjectWithNullProto();
565 JSObject::SetOwnPropertyIgnoreAttributes(catch_scope, name, thrown_object,
566 NONE)
567 .Check();
568 return catch_scope;
569 }
570
571 // Retrieve the with-context extension object. If the extension object is
572 // a proxy, return an empty object.
WithContextExtension()573 Handle<JSObject> ScopeIterator::WithContextExtension() {
574 Handle<Context> context = CurrentContext();
575 DCHECK(context->IsWithContext());
576 if (context->extension_receiver()->IsJSProxy()) {
577 return isolate_->factory()->NewJSObjectWithNullProto();
578 }
579 return handle(JSObject::cast(context->extension_receiver()));
580 }
581
582 // Create a plain JSObject which materializes the block scope for the specified
583 // block context.
MaterializeInnerScope()584 Handle<JSObject> ScopeIterator::MaterializeInnerScope() {
585 Handle<JSObject> inner_scope =
586 isolate_->factory()->NewJSObjectWithNullProto();
587
588 Handle<Context> context = Handle<Context>::null();
589 if (!nested_scope_chain_.is_empty()) {
590 Handle<ScopeInfo> scope_info = nested_scope_chain_.last().scope_info;
591 frame_inspector_->MaterializeStackLocals(inner_scope, scope_info);
592 if (scope_info->HasContext()) context = CurrentContext();
593 } else {
594 context = CurrentContext();
595 }
596
597 if (!context.is_null()) {
598 // Fill all context locals.
599 CopyContextLocalsToScopeObject(CurrentScopeInfo(), context, inner_scope);
600 CopyContextExtensionToScopeObject(context, inner_scope,
601 KeyCollectionMode::kOwnOnly);
602 }
603 return inner_scope;
604 }
605
606
607 // Create a plain JSObject which materializes the module scope for the specified
608 // module context.
MaterializeModuleScope()609 MaybeHandle<JSObject> ScopeIterator::MaterializeModuleScope() {
610 Handle<Context> context = CurrentContext();
611 DCHECK(context->IsModuleContext());
612 Handle<ScopeInfo> scope_info(context->scope_info());
613 Handle<JSObject> module_scope =
614 isolate_->factory()->NewJSObjectWithNullProto();
615 CopyContextLocalsToScopeObject(scope_info, context, module_scope);
616 CopyModuleVarsToScopeObject(scope_info, context, module_scope);
617 return module_scope;
618 }
619
SetParameterValue(Handle<ScopeInfo> scope_info,JavaScriptFrame * frame,Handle<String> parameter_name,Handle<Object> new_value)620 bool ScopeIterator::SetParameterValue(Handle<ScopeInfo> scope_info,
621 JavaScriptFrame* frame,
622 Handle<String> parameter_name,
623 Handle<Object> new_value) {
624 // Setting stack locals of optimized frames is not supported.
625 if (frame->is_optimized()) return false;
626 HandleScope scope(isolate_);
627 for (int i = 0; i < scope_info->ParameterCount(); ++i) {
628 if (String::Equals(handle(scope_info->ParameterName(i)), parameter_name)) {
629 frame->SetParameterValue(i, *new_value);
630 return true;
631 }
632 }
633 return false;
634 }
635
SetStackVariableValue(Handle<ScopeInfo> scope_info,Handle<String> variable_name,Handle<Object> new_value)636 bool ScopeIterator::SetStackVariableValue(Handle<ScopeInfo> scope_info,
637 Handle<String> variable_name,
638 Handle<Object> new_value) {
639 if (frame_inspector_ == nullptr) return false;
640 JavaScriptFrame* frame = GetFrame();
641 // Setting stack locals of optimized frames is not supported.
642 if (frame->is_optimized()) return false;
643 HandleScope scope(isolate_);
644 for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
645 if (String::Equals(handle(scope_info->StackLocalName(i)), variable_name)) {
646 frame->SetExpression(scope_info->StackLocalIndex(i), *new_value);
647 return true;
648 }
649 }
650 return false;
651 }
652
SetContextVariableValue(Handle<ScopeInfo> scope_info,Handle<Context> context,Handle<String> variable_name,Handle<Object> new_value)653 bool ScopeIterator::SetContextVariableValue(Handle<ScopeInfo> scope_info,
654 Handle<Context> context,
655 Handle<String> variable_name,
656 Handle<Object> new_value) {
657 HandleScope scope(isolate_);
658 for (int i = 0; i < scope_info->ContextLocalCount(); i++) {
659 Handle<String> next_name(scope_info->ContextLocalName(i));
660 if (String::Equals(variable_name, next_name)) {
661 VariableMode mode;
662 InitializationFlag init_flag;
663 MaybeAssignedFlag maybe_assigned_flag;
664 int context_index = ScopeInfo::ContextSlotIndex(
665 scope_info, next_name, &mode, &init_flag, &maybe_assigned_flag);
666 context->set(context_index, *new_value);
667 return true;
668 }
669 }
670
671 if (context->has_extension()) {
672 Handle<JSObject> ext(context->extension_object());
673 Maybe<bool> maybe = JSReceiver::HasOwnProperty(ext, variable_name);
674 DCHECK(maybe.IsJust());
675 if (maybe.FromJust()) {
676 // We don't expect this to do anything except replacing property value.
677 JSObject::SetOwnPropertyIgnoreAttributes(ext, variable_name, new_value,
678 NONE)
679 .Check();
680 return true;
681 }
682 }
683
684 return false;
685 }
686
SetLocalVariableValue(Handle<String> variable_name,Handle<Object> new_value)687 bool ScopeIterator::SetLocalVariableValue(Handle<String> variable_name,
688 Handle<Object> new_value) {
689 JavaScriptFrame* frame = GetFrame();
690 Handle<ScopeInfo> scope_info(frame->function()->shared()->scope_info());
691
692 // Parameter might be shadowed in context. Don't stop here.
693 bool result = SetParameterValue(scope_info, frame, variable_name, new_value);
694
695 // Stack locals.
696 if (SetStackVariableValue(scope_info, variable_name, new_value)) {
697 return true;
698 }
699
700 if (scope_info->HasContext() &&
701 SetContextVariableValue(scope_info, CurrentContext(), variable_name,
702 new_value)) {
703 return true;
704 }
705
706 return result;
707 }
708
SetInnerScopeVariableValue(Handle<String> variable_name,Handle<Object> new_value)709 bool ScopeIterator::SetInnerScopeVariableValue(Handle<String> variable_name,
710 Handle<Object> new_value) {
711 Handle<ScopeInfo> scope_info = CurrentScopeInfo();
712 DCHECK(scope_info->scope_type() == BLOCK_SCOPE ||
713 scope_info->scope_type() == EVAL_SCOPE);
714
715 // Setting stack locals of optimized frames is not supported.
716 if (SetStackVariableValue(scope_info, variable_name, new_value)) {
717 return true;
718 }
719
720 if (HasContext() && SetContextVariableValue(scope_info, CurrentContext(),
721 variable_name, new_value)) {
722 return true;
723 }
724
725 return false;
726 }
727
728 // This method copies structure of MaterializeClosure method above.
SetClosureVariableValue(Handle<String> variable_name,Handle<Object> new_value)729 bool ScopeIterator::SetClosureVariableValue(Handle<String> variable_name,
730 Handle<Object> new_value) {
731 DCHECK(CurrentContext()->IsFunctionContext());
732 return SetContextVariableValue(CurrentScopeInfo(), CurrentContext(),
733 variable_name, new_value);
734 }
735
SetScriptVariableValue(Handle<String> variable_name,Handle<Object> new_value)736 bool ScopeIterator::SetScriptVariableValue(Handle<String> variable_name,
737 Handle<Object> new_value) {
738 Handle<Context> context = CurrentContext();
739 Handle<ScriptContextTable> script_contexts(
740 context->global_object()->native_context()->script_context_table());
741 ScriptContextTable::LookupResult lookup_result;
742 if (ScriptContextTable::Lookup(script_contexts, variable_name,
743 &lookup_result)) {
744 Handle<Context> script_context = ScriptContextTable::GetContext(
745 script_contexts, lookup_result.context_index);
746 script_context->set(lookup_result.slot_index, *new_value);
747 return true;
748 }
749
750 return false;
751 }
752
SetCatchVariableValue(Handle<String> variable_name,Handle<Object> new_value)753 bool ScopeIterator::SetCatchVariableValue(Handle<String> variable_name,
754 Handle<Object> new_value) {
755 Handle<Context> context = CurrentContext();
756 DCHECK(context->IsCatchContext());
757 Handle<String> name(context->catch_name());
758 if (!String::Equals(name, variable_name)) {
759 return false;
760 }
761 context->set(Context::THROWN_OBJECT_INDEX, *new_value);
762 return true;
763 }
764
765
CopyContextLocalsToScopeObject(Handle<ScopeInfo> scope_info,Handle<Context> context,Handle<JSObject> scope_object)766 void ScopeIterator::CopyContextLocalsToScopeObject(
767 Handle<ScopeInfo> scope_info, Handle<Context> context,
768 Handle<JSObject> scope_object) {
769 Isolate* isolate = scope_info->GetIsolate();
770 int local_count = scope_info->ContextLocalCount();
771 if (local_count == 0) return;
772 // Fill all context locals to the context extension.
773 for (int i = 0; i < local_count; ++i) {
774 Handle<String> name(scope_info->ContextLocalName(i));
775 if (ScopeInfo::VariableIsSynthetic(*name)) continue;
776 int context_index = Context::MIN_CONTEXT_SLOTS + i;
777 Handle<Object> value = Handle<Object>(context->get(context_index), isolate);
778 // Reflect variables under TDZ as undefined in scope object.
779 if (value->IsTheHole(isolate)) continue;
780 // This should always succeed.
781 // TODO(verwaest): Use AddDataProperty instead.
782 JSObject::SetOwnPropertyIgnoreAttributes(scope_object, name, value, NONE)
783 .Check();
784 }
785 }
786
CopyModuleVarsToScopeObject(Handle<ScopeInfo> scope_info,Handle<Context> context,Handle<JSObject> scope_object)787 void ScopeIterator::CopyModuleVarsToScopeObject(Handle<ScopeInfo> scope_info,
788 Handle<Context> context,
789 Handle<JSObject> scope_object) {
790 Isolate* isolate = scope_info->GetIsolate();
791
792 int module_variable_count =
793 Smi::cast(scope_info->get(scope_info->ModuleVariableCountIndex()))
794 ->value();
795 for (int i = 0; i < module_variable_count; ++i) {
796 Handle<String> local_name;
797 Handle<Object> value;
798 {
799 String* name;
800 int index;
801 scope_info->ModuleVariable(i, &name, &index);
802 CHECK(!ScopeInfo::VariableIsSynthetic(name));
803 local_name = handle(name, isolate);
804 value = Module::LoadVariable(handle(context->module(), isolate), index);
805 }
806
807 // Reflect variables under TDZ as undefined in scope object.
808 if (value->IsTheHole(isolate)) continue;
809 // This should always succeed.
810 // TODO(verwaest): Use AddDataProperty instead.
811 JSObject::SetOwnPropertyIgnoreAttributes(scope_object, local_name, value,
812 NONE)
813 .Check();
814 }
815 }
816
CopyContextExtensionToScopeObject(Handle<Context> context,Handle<JSObject> scope_object,KeyCollectionMode mode)817 void ScopeIterator::CopyContextExtensionToScopeObject(
818 Handle<Context> context, Handle<JSObject> scope_object,
819 KeyCollectionMode mode) {
820 if (context->extension_object() == nullptr) return;
821 Handle<JSObject> extension(context->extension_object());
822 Handle<FixedArray> keys =
823 KeyAccumulator::GetKeys(extension, mode, ENUMERABLE_STRINGS)
824 .ToHandleChecked();
825
826 for (int i = 0; i < keys->length(); i++) {
827 // Names of variables introduced by eval are strings.
828 DCHECK(keys->get(i)->IsString());
829 Handle<String> key(String::cast(keys->get(i)));
830 Handle<Object> value =
831 Object::GetPropertyOrElement(extension, key).ToHandleChecked();
832 JSObject::SetOwnPropertyIgnoreAttributes(scope_object, key, value, NONE)
833 .Check();
834 }
835 }
836
GetNestedScopeChain(Isolate * isolate,Scope * scope,int position)837 void ScopeIterator::GetNestedScopeChain(Isolate* isolate, Scope* scope,
838 int position) {
839 if (scope->is_function_scope()) {
840 // Do not collect scopes of nested inner functions inside the current one.
841 Handle<JSFunction> function = frame_inspector_->GetFunction();
842 if (scope->end_position() < function->shared()->end_position()) return;
843 }
844 if (scope->is_hidden()) {
845 // We need to add this chain element in case the scope has a context
846 // associated. We need to keep the scope chain and context chain in sync.
847 nested_scope_chain_.Add(ExtendedScopeInfo(scope->scope_info()));
848 } else {
849 nested_scope_chain_.Add(ExtendedScopeInfo(
850 scope->scope_info(), scope->start_position(), scope->end_position()));
851 }
852 for (Scope* inner_scope = scope->inner_scope(); inner_scope != nullptr;
853 inner_scope = inner_scope->sibling()) {
854 int beg_pos = inner_scope->start_position();
855 int end_pos = inner_scope->end_position();
856 DCHECK((beg_pos >= 0 && end_pos >= 0) || inner_scope->is_hidden());
857 if (beg_pos <= position && position < end_pos) {
858 GetNestedScopeChain(isolate, inner_scope, position);
859 return;
860 }
861 }
862 }
863
864 } // namespace internal
865 } // namespace v8
866