|  | // Copyright 2015 the V8 project authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "src/debug/debug-scopes.h" | 
|  |  | 
|  | #include <memory> | 
|  |  | 
|  | #include "src/ast/ast.h" | 
|  | #include "src/ast/scopes.h" | 
|  | #include "src/common/globals.h" | 
|  | #include "src/debug/debug.h" | 
|  | #include "src/execution/frames-inl.h" | 
|  | #include "src/execution/isolate-inl.h" | 
|  | #include "src/objects/js-generator-inl.h" | 
|  | #include "src/objects/source-text-module.h" | 
|  | #include "src/objects/string-set-inl.h" | 
|  | #include "src/parsing/parse-info.h" | 
|  | #include "src/parsing/parsing.h" | 
|  | #include "src/parsing/rewriter.h" | 
|  | #include "src/utils/ostreams.h" | 
|  |  | 
|  | namespace v8 { | 
|  | namespace internal { | 
|  |  | 
|  | ScopeIterator::ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector, | 
|  | ReparseStrategy strategy) | 
|  | : isolate_(isolate), | 
|  | frame_inspector_(frame_inspector), | 
|  | function_(frame_inspector_->GetFunction()), | 
|  | script_(frame_inspector_->GetScript()) { | 
|  | if (!frame_inspector->GetContext()->IsContext()) { | 
|  | // Optimized frame, context or function cannot be materialized. Give up. | 
|  | return; | 
|  | } | 
|  | context_ = Handle<Context>::cast(frame_inspector->GetContext()); | 
|  |  | 
|  | // We should not instantiate a ScopeIterator for wasm frames. | 
|  | DCHECK_NE(Script::TYPE_WASM, frame_inspector->GetScript()->type()); | 
|  |  | 
|  | TryParseAndRetrieveScopes(strategy); | 
|  | } | 
|  |  | 
|  | ScopeIterator::~ScopeIterator() = default; | 
|  |  | 
|  | Handle<Object> ScopeIterator::GetFunctionDebugName() const { | 
|  | if (!function_.is_null()) return JSFunction::GetDebugName(function_); | 
|  |  | 
|  | if (!context_->IsNativeContext()) { | 
|  | DisallowHeapAllocation no_gc; | 
|  | ScopeInfo closure_info = context_->closure_context().scope_info(); | 
|  | Handle<String> debug_name(closure_info.FunctionDebugName(), isolate_); | 
|  | if (debug_name->length() > 0) return debug_name; | 
|  | } | 
|  | return isolate_->factory()->undefined_value(); | 
|  | } | 
|  |  | 
|  | ScopeIterator::ScopeIterator(Isolate* isolate, Handle<JSFunction> function) | 
|  | : isolate_(isolate), context_(function->context(), isolate) { | 
|  | if (!function->shared().IsSubjectToDebugging()) { | 
|  | context_ = Handle<Context>(); | 
|  | return; | 
|  | } | 
|  | script_ = handle(Script::cast(function->shared().script()), isolate); | 
|  | UnwrapEvaluationContext(); | 
|  | } | 
|  |  | 
|  | ScopeIterator::ScopeIterator(Isolate* isolate, | 
|  | Handle<JSGeneratorObject> generator) | 
|  | : isolate_(isolate), | 
|  | generator_(generator), | 
|  | function_(generator->function(), isolate), | 
|  | context_(generator->context(), isolate), | 
|  | script_(Script::cast(function_->shared().script()), isolate) { | 
|  | CHECK(function_->shared().IsSubjectToDebugging()); | 
|  | TryParseAndRetrieveScopes(ReparseStrategy::kFunctionLiteral); | 
|  | } | 
|  |  | 
|  | void ScopeIterator::Restart() { | 
|  | DCHECK_NOT_NULL(frame_inspector_); | 
|  | function_ = frame_inspector_->GetFunction(); | 
|  | context_ = Handle<Context>::cast(frame_inspector_->GetContext()); | 
|  | current_scope_ = start_scope_; | 
|  | DCHECK_NOT_NULL(current_scope_); | 
|  | UnwrapEvaluationContext(); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Takes the scope of a parsed script, a function and a break location | 
|  | // inside the function. The result is the innermost lexical scope around | 
|  | // the break point, which serves as the starting point of the ScopeIterator. | 
|  | // And the scope of the function that was passed in (called closure scope). | 
|  | // | 
|  | // The start scope is guaranteed to be either the closure scope itself, | 
|  | // or a child of the closure scope. | 
|  | class ScopeChainRetriever { | 
|  | public: | 
|  | ScopeChainRetriever(DeclarationScope* scope, Handle<JSFunction> function, | 
|  | int position) | 
|  | : scope_(scope), | 
|  | break_scope_start_(function->shared().StartPosition()), | 
|  | break_scope_end_(function->shared().EndPosition()), | 
|  | is_default_constructor_( | 
|  | IsDefaultConstructor(function->shared().kind())), | 
|  | position_(position) { | 
|  | DCHECK_NOT_NULL(scope); | 
|  | RetrieveScopes(); | 
|  | } | 
|  |  | 
|  | DeclarationScope* ClosureScope() { return closure_scope_; } | 
|  | Scope* StartScope() { return start_scope_; } | 
|  |  | 
|  | private: | 
|  | DeclarationScope* scope_; | 
|  | const int break_scope_start_; | 
|  | const int break_scope_end_; | 
|  | const bool is_default_constructor_; | 
|  | const int position_; | 
|  |  | 
|  | DeclarationScope* closure_scope_ = nullptr; | 
|  | Scope* start_scope_ = nullptr; | 
|  |  | 
|  | void RetrieveScopes() { | 
|  | if (is_default_constructor_) { | 
|  | // Even though the DefaultBaseConstructor is a child of a Class scope, the | 
|  | // source positions are *not* nested. This means the actual scope for the | 
|  | // DefaultBaseConstructor needs to be found by doing a DFS. | 
|  | RetrieveScopeChainDefaultConstructor(scope_); | 
|  | } else { | 
|  | RetrieveScopeChain(); | 
|  | } | 
|  | DCHECK_NOT_NULL(closure_scope_); | 
|  | DCHECK_NOT_NULL(start_scope_); | 
|  | } | 
|  |  | 
|  | bool RetrieveScopeChainDefaultConstructor(Scope* scope) { | 
|  | const int beg_pos = scope->start_position(); | 
|  | const int end_pos = scope->end_position(); | 
|  | if (beg_pos == position_ && end_pos == position_) { | 
|  | DCHECK(scope->is_function_scope()); | 
|  | DCHECK( | 
|  | IsDefaultConstructor(scope->AsDeclarationScope()->function_kind())); | 
|  | start_scope_ = scope; | 
|  | closure_scope_ = scope->AsDeclarationScope(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | for (Scope* inner_scope = scope->inner_scope(); inner_scope != nullptr; | 
|  | inner_scope = inner_scope->sibling()) { | 
|  | if (RetrieveScopeChainDefaultConstructor(inner_scope)) return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void RetrieveScopeChain() { | 
|  | Scope* parent = nullptr; | 
|  | Scope* current = scope_; | 
|  | SetClosureScopeIfFound(current); | 
|  |  | 
|  | while (parent != current) { | 
|  | parent = current; | 
|  | for (Scope* inner_scope = current->inner_scope(); inner_scope != nullptr; | 
|  | inner_scope = inner_scope->sibling()) { | 
|  | if (SetClosureScopeIfFound(inner_scope) || | 
|  | ContainsPosition(inner_scope)) { | 
|  | current = inner_scope; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | start_scope_ = current; | 
|  | } | 
|  |  | 
|  | bool SetClosureScopeIfFound(Scope* scope) { | 
|  | const int start = scope->start_position(); | 
|  | const int end = scope->end_position(); | 
|  | if (start == break_scope_start_ && end == break_scope_end_) { | 
|  | closure_scope_ = scope->AsDeclarationScope(); | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool ContainsPosition(Scope* scope) { | 
|  | const int start = scope->start_position(); | 
|  | const int end = scope->end_position(); | 
|  | // In case the closure_scope_ hasn't been found yet, we are less strict | 
|  | // about recursing downwards. This might be the case for nested arrow | 
|  | // functions that have the same end position. | 
|  | const bool position_fits_end = | 
|  | closure_scope_ ? position_ < end : position_ <= end; | 
|  | return start < position_ && position_fits_end; | 
|  | } | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | void ScopeIterator::TryParseAndRetrieveScopes(ReparseStrategy strategy) { | 
|  | // Catch the case when the debugger stops in an internal function. | 
|  | Handle<SharedFunctionInfo> shared_info(function_->shared(), isolate_); | 
|  | Handle<ScopeInfo> scope_info(shared_info->scope_info(), isolate_); | 
|  | if (shared_info->script().IsUndefined(isolate_)) { | 
|  | current_scope_ = closure_scope_ = nullptr; | 
|  | context_ = handle(function_->context(), isolate_); | 
|  | function_ = Handle<JSFunction>(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Class fields initializer functions don't have any scope | 
|  | // information. We short circuit the parsing of the class literal | 
|  | // and return an empty context here. | 
|  | if (IsClassMembersInitializerFunction(shared_info->kind())) { | 
|  | current_scope_ = closure_scope_ = nullptr; | 
|  | context_ = Handle<Context>(); | 
|  | function_ = Handle<JSFunction>(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | bool ignore_nested_scopes = false; | 
|  | if (shared_info->HasBreakInfo() && frame_inspector_ != nullptr) { | 
|  | // The source position at return is always the end of the function, | 
|  | // which is not consistent with the current scope chain. Therefore all | 
|  | // nested with, catch and block contexts are skipped, and we can only | 
|  | // inspect the function scope. | 
|  | // This can only happen if we set a break point inside right before the | 
|  | // return, which requires a debug info to be available. | 
|  | Handle<DebugInfo> debug_info(shared_info->GetDebugInfo(), isolate_); | 
|  |  | 
|  | // Find the break point where execution has stopped. | 
|  | BreakLocation location = BreakLocation::FromFrame(debug_info, GetFrame()); | 
|  |  | 
|  | ignore_nested_scopes = location.IsReturn(); | 
|  | } | 
|  |  | 
|  | // Reparse the code and analyze the scopes. | 
|  | // Depending on the choosen strategy, the whole script or just | 
|  | // the closure is re-parsed for function scopes. | 
|  | Handle<Script> script(Script::cast(shared_info->script()), isolate_); | 
|  |  | 
|  | // Pick between flags for a single function compilation, or an eager | 
|  | // compilation of the whole script. | 
|  | UnoptimizedCompileFlags flags = | 
|  | (scope_info->scope_type() == FUNCTION_SCOPE && | 
|  | strategy == ReparseStrategy::kFunctionLiteral) | 
|  | ? UnoptimizedCompileFlags::ForFunctionCompile(isolate_, *shared_info) | 
|  | : UnoptimizedCompileFlags::ForScriptCompile(isolate_, *script) | 
|  | .set_is_eager(true); | 
|  |  | 
|  | MaybeHandle<ScopeInfo> maybe_outer_scope; | 
|  | if (scope_info->scope_type() == EVAL_SCOPE || script->is_wrapped()) { | 
|  | flags.set_is_eval(true); | 
|  | if (!context_->IsNativeContext()) { | 
|  | maybe_outer_scope = handle(context_->scope_info(), isolate_); | 
|  | } | 
|  | // Language mode may be inherited from the eval caller. | 
|  | // Retrieve it from shared function info. | 
|  | flags.set_outer_language_mode(shared_info->language_mode()); | 
|  | } else if (scope_info->scope_type() == MODULE_SCOPE) { | 
|  | DCHECK(flags.is_module()); | 
|  | } else { | 
|  | DCHECK(scope_info->scope_type() == SCRIPT_SCOPE || | 
|  | scope_info->scope_type() == FUNCTION_SCOPE); | 
|  | } | 
|  |  | 
|  | UnoptimizedCompileState compile_state(isolate_); | 
|  |  | 
|  | info_ = std::make_unique<ParseInfo>(isolate_, flags, &compile_state); | 
|  |  | 
|  | const bool parse_result = | 
|  | flags.is_toplevel() | 
|  | ? parsing::ParseProgram(info_.get(), script, maybe_outer_scope, | 
|  | isolate_, parsing::ReportStatisticsMode::kNo) | 
|  | : parsing::ParseFunction(info_.get(), shared_info, isolate_, | 
|  | parsing::ReportStatisticsMode::kNo); | 
|  |  | 
|  | if (parse_result) { | 
|  | DeclarationScope* literal_scope = info_->literal()->scope(); | 
|  |  | 
|  | ScopeChainRetriever scope_chain_retriever(literal_scope, function_, | 
|  | GetSourcePosition()); | 
|  | start_scope_ = scope_chain_retriever.StartScope(); | 
|  | current_scope_ = start_scope_; | 
|  |  | 
|  | // In case of a FUNCTION_SCOPE, the ScopeIterator expects | 
|  | // {closure_scope_} to be set to the scope of the function. | 
|  | closure_scope_ = scope_info->scope_type() == FUNCTION_SCOPE | 
|  | ? scope_chain_retriever.ClosureScope() | 
|  | : literal_scope; | 
|  |  | 
|  | if (ignore_nested_scopes) { | 
|  | current_scope_ = closure_scope_; | 
|  | start_scope_ = current_scope_; | 
|  | // ignore_nested_scopes is only used for the return-position breakpoint, | 
|  | // so we can safely assume that the closure context for the current | 
|  | // function exists if it needs one. | 
|  | if (closure_scope_->NeedsContext()) { | 
|  | context_ = handle(context_->closure_context(), isolate_); | 
|  | } | 
|  | } | 
|  |  | 
|  | UnwrapEvaluationContext(); | 
|  | } else { | 
|  | // A failed reparse indicates that the preparser has diverged from the | 
|  | // parser, that the preparse data given to the initial parse was faulty, or | 
|  | // a stack overflow. | 
|  | // TODO(leszeks): This error is pretty unexpected, so we could report the | 
|  | // error in debug mode. Better to not fail in release though, in case it's | 
|  | // just a stack overflow. | 
|  |  | 
|  | // Silently fail by presenting an empty context chain. | 
|  | context_ = Handle<Context>(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ScopeIterator::UnwrapEvaluationContext() { | 
|  | if (!context_->IsDebugEvaluateContext()) return; | 
|  | Context current = *context_; | 
|  | do { | 
|  | Object wrapped = current.get(Context::WRAPPED_CONTEXT_INDEX); | 
|  | if (wrapped.IsContext()) { | 
|  | current = Context::cast(wrapped); | 
|  | } else { | 
|  | DCHECK(!current.previous().is_null()); | 
|  | current = current.previous(); | 
|  | } | 
|  | } while (current.IsDebugEvaluateContext()); | 
|  | context_ = handle(current, isolate_); | 
|  | } | 
|  |  | 
|  | Handle<JSObject> ScopeIterator::MaterializeScopeDetails() { | 
|  | // Calculate the size of the result. | 
|  | Handle<FixedArray> details = | 
|  | isolate_->factory()->NewFixedArray(kScopeDetailsSize); | 
|  | // Fill in scope details. | 
|  | details->set(kScopeDetailsTypeIndex, Smi::FromInt(Type())); | 
|  | Handle<JSObject> scope_object = ScopeObject(Mode::ALL); | 
|  | details->set(kScopeDetailsObjectIndex, *scope_object); | 
|  | if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript) { | 
|  | return isolate_->factory()->NewJSArrayWithElements(details); | 
|  | } else if (HasContext()) { | 
|  | Handle<Object> closure_name = GetFunctionDebugName(); | 
|  | details->set(kScopeDetailsNameIndex, *closure_name); | 
|  | details->set(kScopeDetailsStartPositionIndex, | 
|  | Smi::FromInt(start_position())); | 
|  | details->set(kScopeDetailsEndPositionIndex, Smi::FromInt(end_position())); | 
|  | if (InInnerScope()) { | 
|  | details->set(kScopeDetailsFunctionIndex, *function_); | 
|  | } | 
|  | } | 
|  | return isolate_->factory()->NewJSArrayWithElements(details); | 
|  | } | 
|  |  | 
|  | bool ScopeIterator::HasPositionInfo() { | 
|  | return InInnerScope() || !context_->IsNativeContext(); | 
|  | } | 
|  |  | 
|  | int ScopeIterator::start_position() { | 
|  | if (InInnerScope()) return current_scope_->start_position(); | 
|  | if (context_->IsNativeContext()) return 0; | 
|  | return context_->closure_context().scope_info().StartPosition(); | 
|  | } | 
|  |  | 
|  | int ScopeIterator::end_position() { | 
|  | if (InInnerScope()) return current_scope_->end_position(); | 
|  | if (context_->IsNativeContext()) return 0; | 
|  | return context_->closure_context().scope_info().EndPosition(); | 
|  | } | 
|  |  | 
|  | bool ScopeIterator::DeclaresLocals(Mode mode) const { | 
|  | ScopeType type = Type(); | 
|  |  | 
|  | if (type == ScopeTypeWith) return mode == Mode::ALL; | 
|  | if (type == ScopeTypeGlobal) return mode == Mode::ALL; | 
|  |  | 
|  | bool declares_local = false; | 
|  | auto visitor = [&](Handle<String> name, Handle<Object> value, | 
|  | ScopeType scope_type) { | 
|  | declares_local = true; | 
|  | return true; | 
|  | }; | 
|  | VisitScope(visitor, mode); | 
|  | return declares_local; | 
|  | } | 
|  |  | 
|  | bool ScopeIterator::HasContext() const { | 
|  | return !InInnerScope() || NeedsAndHasContext(); | 
|  | } | 
|  |  | 
|  | bool ScopeIterator::NeedsAndHasContext() const { | 
|  | if (!current_scope_->NeedsContext()) return false; | 
|  | // Generally, if a scope needs a context, then we can assume that it has a | 
|  | // context. However, the stack check during function entry happens before the | 
|  | // function has a chance to create and push its own context, so we must check | 
|  | // for the case where the function is executing in its parent context. This | 
|  | // case is only possible in function scopes; top-level code (modules and | 
|  | // non-module scripts) begin execution in the context they need and don't have | 
|  | // a separate step to push the correct context. | 
|  | return !(current_scope_ == closure_scope_ && | 
|  | current_scope_->is_function_scope() && !function_.is_null() && | 
|  | function_->context() == *context_); | 
|  | } | 
|  |  | 
|  | void ScopeIterator::AdvanceOneScope() { | 
|  | if (NeedsAndHasContext()) { | 
|  | DCHECK(!context_->previous().is_null()); | 
|  | context_ = handle(context_->previous(), isolate_); | 
|  | } | 
|  | DCHECK(current_scope_->outer_scope() != nullptr); | 
|  | current_scope_ = current_scope_->outer_scope(); | 
|  | } | 
|  |  | 
|  | void ScopeIterator::AdvanceToNonHiddenScope() { | 
|  | do { | 
|  | AdvanceOneScope(); | 
|  | } while (current_scope_->is_hidden()); | 
|  | } | 
|  |  | 
|  | void ScopeIterator::AdvanceContext() { | 
|  | DCHECK(!context_->IsNativeContext()); | 
|  | context_ = handle(context_->previous(), isolate_); | 
|  |  | 
|  | // While advancing one context, we need to advance at least one | 
|  | // scope, but until we hit the next scope that actually requires | 
|  | // a context. All the locals collected along the way build the | 
|  | // blocklist for debug-evaluate for this context. | 
|  | locals_ = StringSet::New(isolate_); | 
|  | do { | 
|  | if (!current_scope_ || !current_scope_->outer_scope()) break; | 
|  |  | 
|  | current_scope_ = current_scope_->outer_scope(); | 
|  | CollectLocalsFromCurrentScope(); | 
|  | } while (!NeedsAndHasContext()); | 
|  | } | 
|  |  | 
|  | void ScopeIterator::Next() { | 
|  | DCHECK(!Done()); | 
|  |  | 
|  | ScopeType scope_type = Type(); | 
|  |  | 
|  | if (scope_type == ScopeTypeGlobal) { | 
|  | // The global scope is always the last in the chain. | 
|  | DCHECK(context_->IsNativeContext()); | 
|  | context_ = Handle<Context>(); | 
|  | DCHECK(Done()); | 
|  | return; | 
|  | } | 
|  |  | 
|  | bool leaving_closure = current_scope_ == closure_scope_; | 
|  |  | 
|  | if (scope_type == ScopeTypeScript) { | 
|  | DCHECK_IMPLIES(InInnerScope() && !leaving_closure, | 
|  | current_scope_->is_script_scope()); | 
|  | seen_script_scope_ = true; | 
|  | if (context_->IsScriptContext()) { | 
|  | context_ = handle(context_->previous(), isolate_); | 
|  | } | 
|  | } else if (!InInnerScope()) { | 
|  | AdvanceContext(); | 
|  | } else { | 
|  | DCHECK_NOT_NULL(current_scope_); | 
|  | AdvanceToNonHiddenScope(); | 
|  |  | 
|  | if (leaving_closure) { | 
|  | DCHECK(current_scope_ != closure_scope_); | 
|  | // Edge case when we just go past {closure_scope_}. This case | 
|  | // already needs to start collecting locals for the blocklist. | 
|  | locals_ = StringSet::New(isolate_); | 
|  | CollectLocalsFromCurrentScope(); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (leaving_closure) function_ = Handle<JSFunction>(); | 
|  |  | 
|  | UnwrapEvaluationContext(); | 
|  | } | 
|  |  | 
|  | // Return the type of the current scope. | 
|  | ScopeIterator::ScopeType ScopeIterator::Type() const { | 
|  | DCHECK(!Done()); | 
|  | if (InInnerScope()) { | 
|  | switch (current_scope_->scope_type()) { | 
|  | case FUNCTION_SCOPE: | 
|  | DCHECK_IMPLIES(NeedsAndHasContext(), | 
|  | context_->IsFunctionContext() || | 
|  | context_->IsDebugEvaluateContext()); | 
|  | return ScopeTypeLocal; | 
|  | case MODULE_SCOPE: | 
|  | DCHECK_IMPLIES(NeedsAndHasContext(), context_->IsModuleContext()); | 
|  | return ScopeTypeModule; | 
|  | case SCRIPT_SCOPE: | 
|  | DCHECK_IMPLIES(NeedsAndHasContext(), context_->IsScriptContext() || | 
|  | context_->IsNativeContext()); | 
|  | return ScopeTypeScript; | 
|  | case WITH_SCOPE: | 
|  | DCHECK_IMPLIES(NeedsAndHasContext(), context_->IsWithContext()); | 
|  | return ScopeTypeWith; | 
|  | case CATCH_SCOPE: | 
|  | DCHECK(context_->IsCatchContext()); | 
|  | return ScopeTypeCatch; | 
|  | case BLOCK_SCOPE: | 
|  | case CLASS_SCOPE: | 
|  | DCHECK_IMPLIES(NeedsAndHasContext(), context_->IsBlockContext()); | 
|  | return ScopeTypeBlock; | 
|  | case EVAL_SCOPE: | 
|  | DCHECK_IMPLIES(NeedsAndHasContext(), context_->IsEvalContext()); | 
|  | return ScopeTypeEval; | 
|  | } | 
|  | UNREACHABLE(); | 
|  | } | 
|  | if (context_->IsNativeContext()) { | 
|  | DCHECK(context_->global_object().IsJSGlobalObject()); | 
|  | // If we are at the native context and have not yet seen script scope, | 
|  | // fake it. | 
|  | return seen_script_scope_ ? ScopeTypeGlobal : ScopeTypeScript; | 
|  | } | 
|  | if (context_->IsFunctionContext() || context_->IsEvalContext() || | 
|  | context_->IsDebugEvaluateContext()) { | 
|  | return ScopeTypeClosure; | 
|  | } | 
|  | if (context_->IsCatchContext()) { | 
|  | return ScopeTypeCatch; | 
|  | } | 
|  | if (context_->IsBlockContext()) { | 
|  | return ScopeTypeBlock; | 
|  | } | 
|  | if (context_->IsModuleContext()) { | 
|  | return ScopeTypeModule; | 
|  | } | 
|  | if (context_->IsScriptContext()) { | 
|  | return ScopeTypeScript; | 
|  | } | 
|  | DCHECK(context_->IsWithContext()); | 
|  | return ScopeTypeWith; | 
|  | } | 
|  |  | 
|  | Handle<JSObject> ScopeIterator::ScopeObject(Mode mode) { | 
|  | DCHECK(!Done()); | 
|  |  | 
|  | ScopeType type = Type(); | 
|  | if (type == ScopeTypeGlobal) { | 
|  | DCHECK_EQ(Mode::ALL, mode); | 
|  | return handle(context_->global_proxy(), isolate_); | 
|  | } | 
|  | if (type == ScopeTypeWith) { | 
|  | DCHECK_EQ(Mode::ALL, mode); | 
|  | return WithContextExtension(); | 
|  | } | 
|  |  | 
|  | Handle<JSObject> scope = isolate_->factory()->NewJSObjectWithNullProto(); | 
|  | auto visitor = [=](Handle<String> name, Handle<Object> value, | 
|  | ScopeType scope_type) { | 
|  | if (value->IsTheHole(isolate_)) { | 
|  | // Reflect variables under TDZ as undefined in scope object. | 
|  | if (scope_type == ScopeTypeScript && | 
|  | JSReceiver::HasOwnProperty(scope, name).FromMaybe(true)) { | 
|  | // We also use the hole to represent overridden let-declarations via | 
|  | // REPL mode in a script context. Catch this case. | 
|  | return false; | 
|  | } | 
|  | value = isolate_->factory()->undefined_value(); | 
|  | } | 
|  | JSObject::AddProperty(isolate_, scope, name, value, NONE); | 
|  | return false; | 
|  | }; | 
|  |  | 
|  | VisitScope(visitor, mode); | 
|  | return scope; | 
|  | } | 
|  |  | 
|  | void ScopeIterator::VisitScope(const Visitor& visitor, Mode mode) const { | 
|  | switch (Type()) { | 
|  | case ScopeTypeLocal: | 
|  | case ScopeTypeClosure: | 
|  | case ScopeTypeCatch: | 
|  | case ScopeTypeBlock: | 
|  | case ScopeTypeEval: | 
|  | return VisitLocalScope(visitor, mode, Type()); | 
|  | case ScopeTypeModule: | 
|  | if (InInnerScope()) { | 
|  | return VisitLocalScope(visitor, mode, Type()); | 
|  | } | 
|  | DCHECK_EQ(Mode::ALL, mode); | 
|  | return VisitModuleScope(visitor); | 
|  | case ScopeTypeScript: | 
|  | DCHECK_EQ(Mode::ALL, mode); | 
|  | return VisitScriptScope(visitor); | 
|  | case ScopeTypeWith: | 
|  | case ScopeTypeGlobal: | 
|  | UNREACHABLE(); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool ScopeIterator::SetVariableValue(Handle<String> name, | 
|  | Handle<Object> value) { | 
|  | DCHECK(!Done()); | 
|  | name = isolate_->factory()->InternalizeString(name); | 
|  | switch (Type()) { | 
|  | case ScopeTypeGlobal: | 
|  | case ScopeTypeWith: | 
|  | break; | 
|  |  | 
|  | case ScopeTypeEval: | 
|  | case ScopeTypeBlock: | 
|  | case ScopeTypeCatch: | 
|  | case ScopeTypeModule: | 
|  | if (InInnerScope()) return SetLocalVariableValue(name, value); | 
|  | if (Type() == ScopeTypeModule && SetModuleVariableValue(name, value)) { | 
|  | return true; | 
|  | } | 
|  | return SetContextVariableValue(name, value); | 
|  |  | 
|  | case ScopeTypeLocal: | 
|  | case ScopeTypeClosure: | 
|  | if (InInnerScope()) { | 
|  | DCHECK_EQ(ScopeTypeLocal, Type()); | 
|  | if (SetLocalVariableValue(name, value)) return true; | 
|  | // There may not be an associated context since we're InInnerScope(). | 
|  | if (!NeedsAndHasContext()) return false; | 
|  | } else { | 
|  | DCHECK_EQ(ScopeTypeClosure, Type()); | 
|  | if (SetContextVariableValue(name, value)) return true; | 
|  | } | 
|  | // The above functions only set variables statically declared in the | 
|  | // function. There may be eval-introduced variables. Check them in | 
|  | // SetContextExtensionValue. | 
|  | return SetContextExtensionValue(name, value); | 
|  |  | 
|  | case ScopeTypeScript: | 
|  | return SetScriptVariableValue(name, value); | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool ScopeIterator::ClosureScopeHasThisReference() const { | 
|  | return !closure_scope_->has_this_declaration() && | 
|  | closure_scope_->HasThisReference(); | 
|  | } | 
|  |  | 
|  | void ScopeIterator::CollectLocalsFromCurrentScope() { | 
|  | DCHECK(locals_->IsStringSet()); | 
|  | for (Variable* var : *current_scope_->locals()) { | 
|  | if (var->location() == VariableLocation::PARAMETER || | 
|  | var->location() == VariableLocation::LOCAL) { | 
|  | locals_ = StringSet::Add(isolate_, locals_, var->name()); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | #ifdef DEBUG | 
|  | // Debug print of the content of the current scope. | 
|  | void ScopeIterator::DebugPrint() { | 
|  | StdoutStream os; | 
|  | DCHECK(!Done()); | 
|  | switch (Type()) { | 
|  | case ScopeIterator::ScopeTypeGlobal: | 
|  | os << "Global:\n"; | 
|  | context_->Print(os); | 
|  | break; | 
|  |  | 
|  | case ScopeIterator::ScopeTypeLocal: { | 
|  | os << "Local:\n"; | 
|  | if (NeedsAndHasContext()) { | 
|  | context_->Print(os); | 
|  | if (context_->has_extension()) { | 
|  | Handle<HeapObject> extension(context_->extension(), isolate_); | 
|  | DCHECK(extension->IsJSContextExtensionObject()); | 
|  | extension->Print(os); | 
|  | } | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | case ScopeIterator::ScopeTypeWith: | 
|  | os << "With:\n"; | 
|  | context_->extension().Print(os); | 
|  | break; | 
|  |  | 
|  | case ScopeIterator::ScopeTypeCatch: | 
|  | os << "Catch:\n"; | 
|  | context_->extension().Print(os); | 
|  | context_->get(Context::THROWN_OBJECT_INDEX).Print(os); | 
|  | break; | 
|  |  | 
|  | case ScopeIterator::ScopeTypeClosure: | 
|  | os << "Closure:\n"; | 
|  | context_->Print(os); | 
|  | if (context_->has_extension()) { | 
|  | Handle<HeapObject> extension(context_->extension(), isolate_); | 
|  | DCHECK(extension->IsJSContextExtensionObject()); | 
|  | extension->Print(os); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case ScopeIterator::ScopeTypeScript: | 
|  | os << "Script:\n"; | 
|  | context_->global_object().native_context().script_context_table().Print( | 
|  | os); | 
|  | break; | 
|  |  | 
|  | default: | 
|  | UNREACHABLE(); | 
|  | } | 
|  | PrintF("\n"); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | int ScopeIterator::GetSourcePosition() { | 
|  | if (frame_inspector_) { | 
|  | return frame_inspector_->GetSourcePosition(); | 
|  | } else { | 
|  | DCHECK(!generator_.is_null()); | 
|  | SharedFunctionInfo::EnsureSourcePositionsAvailable( | 
|  | isolate_, handle(generator_->function().shared(), isolate_)); | 
|  | return generator_->source_position(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ScopeIterator::VisitScriptScope(const Visitor& visitor) const { | 
|  | Handle<JSGlobalObject> global(context_->global_object(), isolate_); | 
|  | Handle<ScriptContextTable> script_contexts( | 
|  | global->native_context().script_context_table(), isolate_); | 
|  |  | 
|  | // Skip the first script since that just declares 'this'. | 
|  | for (int context_index = 1; | 
|  | context_index < script_contexts->synchronized_used(); context_index++) { | 
|  | Handle<Context> context = ScriptContextTable::GetContext( | 
|  | isolate_, script_contexts, context_index); | 
|  | Handle<ScopeInfo> scope_info(context->scope_info(), isolate_); | 
|  | if (VisitContextLocals(visitor, scope_info, context, ScopeTypeScript)) | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | void ScopeIterator::VisitModuleScope(const Visitor& visitor) const { | 
|  | DCHECK(context_->IsModuleContext()); | 
|  |  | 
|  | Handle<ScopeInfo> scope_info(context_->scope_info(), isolate_); | 
|  | if (VisitContextLocals(visitor, scope_info, context_, ScopeTypeModule)) | 
|  | return; | 
|  |  | 
|  | int count_index = scope_info->ModuleVariableCountIndex(); | 
|  | int module_variable_count = Smi::cast(scope_info->get(count_index)).value(); | 
|  |  | 
|  | Handle<SourceTextModule> module(context_->module(), isolate_); | 
|  |  | 
|  | for (int i = 0; i < module_variable_count; ++i) { | 
|  | int index; | 
|  | Handle<String> name; | 
|  | { | 
|  | String raw_name; | 
|  | scope_info->ModuleVariable(i, &raw_name, &index); | 
|  | if (ScopeInfo::VariableIsSynthetic(raw_name)) continue; | 
|  | name = handle(raw_name, isolate_); | 
|  | } | 
|  | Handle<Object> value = | 
|  | SourceTextModule::LoadVariable(isolate_, module, index); | 
|  |  | 
|  | if (visitor(name, value, ScopeTypeModule)) return; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool ScopeIterator::VisitContextLocals(const Visitor& visitor, | 
|  | Handle<ScopeInfo> scope_info, | 
|  | Handle<Context> context, | 
|  | ScopeType scope_type) const { | 
|  | // Fill all context locals to the context extension. | 
|  | for (int i = 0; i < scope_info->ContextLocalCount(); ++i) { | 
|  | Handle<String> name(scope_info->ContextLocalName(i), isolate_); | 
|  | if (ScopeInfo::VariableIsSynthetic(*name)) continue; | 
|  | int context_index = scope_info->ContextHeaderLength() + i; | 
|  | Handle<Object> value(context->get(context_index), isolate_); | 
|  | if (visitor(name, value, scope_type)) return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool ScopeIterator::VisitLocals(const Visitor& visitor, Mode mode, | 
|  | ScopeType scope_type) const { | 
|  | if (mode == Mode::STACK && current_scope_->is_declaration_scope() && | 
|  | current_scope_->AsDeclarationScope()->has_this_declaration()) { | 
|  | // TODO(bmeurer): We should refactor the general variable lookup | 
|  | // around "this", since the current way is rather hacky when the | 
|  | // receiver is context-allocated. | 
|  | auto this_var = current_scope_->AsDeclarationScope()->receiver(); | 
|  | Handle<Object> receiver = | 
|  | this_var->location() == VariableLocation::CONTEXT | 
|  | ? handle(context_->get(this_var->index()), isolate_) | 
|  | : frame_inspector_ == nullptr | 
|  | ? handle(generator_->receiver(), isolate_) | 
|  | : frame_inspector_->GetReceiver(); | 
|  | if (receiver->IsOptimizedOut(isolate_)) { | 
|  | receiver = isolate_->factory()->undefined_value(); | 
|  | } | 
|  | if (visitor(isolate_->factory()->this_string(), receiver, scope_type)) | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (current_scope_->is_function_scope()) { | 
|  | Variable* function_var = | 
|  | current_scope_->AsDeclarationScope()->function_var(); | 
|  | if (function_var != nullptr) { | 
|  | Handle<JSFunction> function = frame_inspector_ == nullptr | 
|  | ? function_ | 
|  | : frame_inspector_->GetFunction(); | 
|  | Handle<String> name = function_var->name(); | 
|  | if (visitor(name, function, scope_type)) return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (Variable* var : *current_scope_->locals()) { | 
|  | DCHECK(!var->is_this()); | 
|  | if (ScopeInfo::VariableIsSynthetic(*var->name())) continue; | 
|  |  | 
|  | int index = var->index(); | 
|  | Handle<Object> value; | 
|  | switch (var->location()) { | 
|  | case VariableLocation::LOOKUP: | 
|  | UNREACHABLE(); | 
|  | break; | 
|  |  | 
|  | case VariableLocation::REPL_GLOBAL: | 
|  | // REPL declared variables are ignored for now. | 
|  | case VariableLocation::UNALLOCATED: | 
|  | continue; | 
|  |  | 
|  | case VariableLocation::PARAMETER: { | 
|  | if (frame_inspector_ == nullptr) { | 
|  | // Get the variable from the suspended generator. | 
|  | DCHECK(!generator_.is_null()); | 
|  | FixedArray parameters_and_registers = | 
|  | generator_->parameters_and_registers(); | 
|  | DCHECK_LT(index, parameters_and_registers.length()); | 
|  | value = handle(parameters_and_registers.get(index), isolate_); | 
|  | } else { | 
|  | value = frame_inspector_->GetParameter(index); | 
|  |  | 
|  | if (value->IsOptimizedOut(isolate_)) { | 
|  | value = isolate_->factory()->undefined_value(); | 
|  | } | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | case VariableLocation::LOCAL: | 
|  | if (frame_inspector_ == nullptr) { | 
|  | // Get the variable from the suspended generator. | 
|  | DCHECK(!generator_.is_null()); | 
|  | FixedArray parameters_and_registers = | 
|  | generator_->parameters_and_registers(); | 
|  | int parameter_count = | 
|  | function_->shared().scope_info().ParameterCount(); | 
|  | index += parameter_count; | 
|  | DCHECK_LT(index, parameters_and_registers.length()); | 
|  | value = handle(parameters_and_registers.get(index), isolate_); | 
|  | } else { | 
|  | value = frame_inspector_->GetExpression(index); | 
|  | if (value->IsOptimizedOut(isolate_)) { | 
|  | // We'll rematerialize this later. | 
|  | if (current_scope_->is_declaration_scope() && | 
|  | current_scope_->AsDeclarationScope()->arguments() == var) { | 
|  | continue; | 
|  | } | 
|  | value = isolate_->factory()->undefined_value(); | 
|  | } | 
|  | } | 
|  | break; | 
|  |  | 
|  | case VariableLocation::CONTEXT: | 
|  | if (mode == Mode::STACK) continue; | 
|  | DCHECK(var->IsContextSlot()); | 
|  | value = handle(context_->get(index), isolate_); | 
|  | break; | 
|  |  | 
|  | case VariableLocation::MODULE: { | 
|  | if (mode == Mode::STACK) continue; | 
|  | // if (var->IsExport()) continue; | 
|  | Handle<SourceTextModule> module(context_->module(), isolate_); | 
|  | value = SourceTextModule::LoadVariable(isolate_, module, var->index()); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (visitor(var->name(), value, scope_type)) return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Retrieve the with-context extension object. If the extension object is | 
|  | // a proxy, return an empty object. | 
|  | Handle<JSObject> ScopeIterator::WithContextExtension() { | 
|  | DCHECK(context_->IsWithContext()); | 
|  | if (context_->extension_receiver().IsJSProxy()) { | 
|  | return isolate_->factory()->NewJSObjectWithNullProto(); | 
|  | } | 
|  | return handle(JSObject::cast(context_->extension_receiver()), isolate_); | 
|  | } | 
|  |  | 
|  | // Create a plain JSObject which materializes the block scope for the specified | 
|  | // block context. | 
|  | void ScopeIterator::VisitLocalScope(const Visitor& visitor, Mode mode, | 
|  | ScopeType scope_type) const { | 
|  | if (InInnerScope()) { | 
|  | if (VisitLocals(visitor, mode, scope_type)) return; | 
|  | if (mode == Mode::STACK && Type() == ScopeTypeLocal) { | 
|  | // Hide |this| in arrow functions that may be embedded in other functions | 
|  | // but don't force |this| to be context-allocated. Otherwise we'd find the | 
|  | // wrong |this| value. | 
|  | if (!closure_scope_->has_this_declaration() && | 
|  | !closure_scope_->HasThisReference()) { | 
|  | if (visitor(isolate_->factory()->this_string(), | 
|  | isolate_->factory()->undefined_value(), scope_type)) | 
|  | return; | 
|  | } | 
|  | // Add |arguments| to the function scope even if it wasn't used. | 
|  | // Currently we don't yet support materializing the arguments object of | 
|  | // suspended generators. We'd need to read the arguments out from the | 
|  | // suspended generator rather than from an activation as | 
|  | // FunctionGetArguments does. | 
|  | if (frame_inspector_ != nullptr && !closure_scope_->is_arrow_scope() && | 
|  | (closure_scope_->arguments() == nullptr || | 
|  | frame_inspector_->GetExpression(closure_scope_->arguments()->index()) | 
|  | ->IsOptimizedOut(isolate_))) { | 
|  | JavaScriptFrame* frame = GetFrame(); | 
|  | Handle<JSObject> arguments = Accessors::FunctionGetArguments( | 
|  | frame, frame_inspector_->inlined_frame_index()); | 
|  | if (visitor(isolate_->factory()->arguments_string(), arguments, | 
|  | scope_type)) | 
|  | return; | 
|  | } | 
|  | } | 
|  | } else { | 
|  | DCHECK_EQ(Mode::ALL, mode); | 
|  | Handle<ScopeInfo> scope_info(context_->scope_info(), isolate_); | 
|  | if (VisitContextLocals(visitor, scope_info, context_, scope_type)) return; | 
|  | } | 
|  |  | 
|  | if (mode == Mode::ALL && HasContext()) { | 
|  | DCHECK(!context_->IsScriptContext()); | 
|  | DCHECK(!context_->IsNativeContext()); | 
|  | DCHECK(!context_->IsWithContext()); | 
|  | if (!context_->scope_info().SloppyEvalCanExtendVars()) return; | 
|  | if (context_->extension_object().is_null()) return; | 
|  | Handle<JSObject> extension(context_->extension_object(), isolate_); | 
|  | Handle<FixedArray> keys = | 
|  | KeyAccumulator::GetKeys(extension, KeyCollectionMode::kOwnOnly, | 
|  | ENUMERABLE_STRINGS) | 
|  | .ToHandleChecked(); | 
|  |  | 
|  | for (int i = 0; i < keys->length(); i++) { | 
|  | // Names of variables introduced by eval are strings. | 
|  | DCHECK(keys->get(i).IsString()); | 
|  | Handle<String> key(String::cast(keys->get(i)), isolate_); | 
|  | Handle<Object> value = JSReceiver::GetDataProperty(extension, key); | 
|  | if (visitor(key, value, scope_type)) return; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | bool ScopeIterator::SetLocalVariableValue(Handle<String> variable_name, | 
|  | Handle<Object> new_value) { | 
|  | // TODO(verwaest): Walk parameters backwards, not forwards. | 
|  | // TODO(verwaest): Use VariableMap rather than locals() list for lookup. | 
|  | for (Variable* var : *current_scope_->locals()) { | 
|  | if (String::Equals(isolate_, var->name(), variable_name)) { | 
|  | int index = var->index(); | 
|  | switch (var->location()) { | 
|  | case VariableLocation::LOOKUP: | 
|  | case VariableLocation::UNALLOCATED: | 
|  | // Drop assignments to unallocated locals. | 
|  | DCHECK(var->is_this() || | 
|  | *variable_name == ReadOnlyRoots(isolate_).arguments_string()); | 
|  | return false; | 
|  |  | 
|  | case VariableLocation::REPL_GLOBAL: | 
|  | // Assignments to REPL declared variables are ignored for now. | 
|  | return false; | 
|  |  | 
|  | case VariableLocation::PARAMETER: { | 
|  | if (var->is_this()) return false; | 
|  | if (frame_inspector_ == nullptr) { | 
|  | // Set the variable in the suspended generator. | 
|  | DCHECK(!generator_.is_null()); | 
|  | Handle<FixedArray> parameters_and_registers( | 
|  | generator_->parameters_and_registers(), isolate_); | 
|  | DCHECK_LT(index, parameters_and_registers->length()); | 
|  | parameters_and_registers->set(index, *new_value); | 
|  | } else { | 
|  | JavaScriptFrame* frame = GetFrame(); | 
|  | if (frame->is_optimized()) return false; | 
|  |  | 
|  | frame->SetParameterValue(index, *new_value); | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | case VariableLocation::LOCAL: | 
|  | if (frame_inspector_ == nullptr) { | 
|  | // Set the variable in the suspended generator. | 
|  | DCHECK(!generator_.is_null()); | 
|  | int parameter_count = | 
|  | function_->shared().scope_info().ParameterCount(); | 
|  | index += parameter_count; | 
|  | Handle<FixedArray> parameters_and_registers( | 
|  | generator_->parameters_and_registers(), isolate_); | 
|  | DCHECK_LT(index, parameters_and_registers->length()); | 
|  | parameters_and_registers->set(index, *new_value); | 
|  | } else { | 
|  | // Set the variable on the stack. | 
|  | JavaScriptFrame* frame = GetFrame(); | 
|  | if (frame->is_optimized()) return false; | 
|  |  | 
|  | frame->SetExpression(index, *new_value); | 
|  | } | 
|  | return true; | 
|  |  | 
|  | case VariableLocation::CONTEXT: | 
|  | DCHECK(var->IsContextSlot()); | 
|  | context_->set(index, *new_value); | 
|  | return true; | 
|  |  | 
|  | case VariableLocation::MODULE: | 
|  | if (!var->IsExport()) return false; | 
|  | Handle<SourceTextModule> module(context_->module(), isolate_); | 
|  | SourceTextModule::StoreVariable(module, var->index(), new_value); | 
|  | return true; | 
|  | } | 
|  | UNREACHABLE(); | 
|  | } | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool ScopeIterator::SetContextExtensionValue(Handle<String> variable_name, | 
|  | Handle<Object> new_value) { | 
|  | if (!context_->has_extension()) return false; | 
|  |  | 
|  | DCHECK(context_->extension_object().IsJSContextExtensionObject()); | 
|  | Handle<JSObject> ext(context_->extension_object(), isolate_); | 
|  | LookupIterator it(isolate_, ext, variable_name, LookupIterator::OWN); | 
|  | Maybe<bool> maybe = JSReceiver::HasProperty(&it); | 
|  | DCHECK(maybe.IsJust()); | 
|  | if (!maybe.FromJust()) return false; | 
|  |  | 
|  | CHECK(Object::SetDataProperty(&it, new_value).ToChecked()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool ScopeIterator::SetContextVariableValue(Handle<String> variable_name, | 
|  | Handle<Object> new_value) { | 
|  | DisallowHeapAllocation no_gc; | 
|  | VariableMode mode; | 
|  | InitializationFlag flag; | 
|  | MaybeAssignedFlag maybe_assigned_flag; | 
|  | IsStaticFlag is_static_flag; | 
|  | int slot_index = | 
|  | ScopeInfo::ContextSlotIndex(context_->scope_info(), *variable_name, &mode, | 
|  | &flag, &maybe_assigned_flag, &is_static_flag); | 
|  | if (slot_index < 0) return false; | 
|  |  | 
|  | context_->set(slot_index, *new_value); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool ScopeIterator::SetModuleVariableValue(Handle<String> variable_name, | 
|  | Handle<Object> new_value) { | 
|  | DisallowHeapAllocation no_gc; | 
|  | int cell_index; | 
|  | VariableMode mode; | 
|  | InitializationFlag init_flag; | 
|  | MaybeAssignedFlag maybe_assigned_flag; | 
|  | cell_index = context_->scope_info().ModuleIndex( | 
|  | *variable_name, &mode, &init_flag, &maybe_assigned_flag); | 
|  |  | 
|  | // Setting imports is currently not supported. | 
|  | if (SourceTextModuleDescriptor::GetCellIndexKind(cell_index) != | 
|  | SourceTextModuleDescriptor::kExport) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | Handle<SourceTextModule> module(context_->module(), isolate_); | 
|  | SourceTextModule::StoreVariable(module, cell_index, new_value); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool ScopeIterator::SetScriptVariableValue(Handle<String> variable_name, | 
|  | Handle<Object> new_value) { | 
|  | Handle<ScriptContextTable> script_contexts( | 
|  | context_->global_object().native_context().script_context_table(), | 
|  | isolate_); | 
|  | ScriptContextTable::LookupResult lookup_result; | 
|  | if (ScriptContextTable::Lookup(isolate_, *script_contexts, *variable_name, | 
|  | &lookup_result)) { | 
|  | Handle<Context> script_context = ScriptContextTable::GetContext( | 
|  | isolate_, script_contexts, lookup_result.context_index); | 
|  | script_context->set(lookup_result.slot_index, *new_value); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | }  // namespace internal | 
|  | }  // namespace v8 |