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