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