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