Home | History | Annotate | Download | only in debug
      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 
     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 
    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 
    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 
    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.
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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.
    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 
    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 
    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 
    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 
    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.
    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.
    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.
    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.
    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.
    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 
    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 
    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.
    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 
    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 
    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 
    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 
    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