// 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/debug/debug.h" #include "src/frames-inl.h" #include "src/globals.h" #include "src/isolate-inl.h" #include "src/parsing/parse-info.h" #include "src/parsing/parsing.h" #include "src/parsing/rewriter.h" namespace v8 { namespace internal { ScopeIterator::ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector, ScopeIterator::Option option) : isolate_(isolate), frame_inspector_(frame_inspector), nested_scope_chain_(4), seen_script_scope_(false) { 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(frame_inspector->GetScript()->type() != Script::TYPE_WASM); // Catch the case when the debugger stops in an internal function. Handle<JSFunction> function = GetFunction(); Handle<SharedFunctionInfo> shared_info(function->shared()); Handle<ScopeInfo> scope_info(shared_info->scope_info()); if (shared_info->script()->IsUndefined(isolate)) { while (context_->closure() == *function) { context_ = Handle<Context>(context_->previous(), isolate_); } return; } // Currently it takes too much time to find nested scopes due to script // parsing. Sometimes we want to run the ScopeIterator as fast as possible // (for example, while collecting async call stacks on every // addEventListener call), even if we drop some nested scopes. // Later we may optimize getting the nested scopes (cache the result?) // and include nested scopes into the "fast" iteration case as well. bool ignore_nested_scopes = (option == IGNORE_NESTED_SCOPES); bool collect_non_locals = (option == COLLECT_NON_LOCALS); if (!ignore_nested_scopes && shared_info->HasDebugInfo()) { // 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()); // Find the break point where execution has stopped. BreakLocation location = BreakLocation::FromFrame(debug_info, GetFrame()); ignore_nested_scopes = location.IsReturn(); } if (ignore_nested_scopes) { if (scope_info->HasContext()) { context_ = Handle<Context>(context_->declaration_context(), isolate_); } else { while (context_->closure() == *function) { context_ = Handle<Context>(context_->previous(), isolate_); } } if (scope_info->scope_type() == FUNCTION_SCOPE) { nested_scope_chain_.Add(ExtendedScopeInfo(scope_info, shared_info->start_position(), shared_info->end_position())); } if (!collect_non_locals) return; } // Reparse the code and analyze the scopes. // Check whether we are in global, eval or function code. std::unique_ptr<ParseInfo> info; if (scope_info->scope_type() != FUNCTION_SCOPE) { // Global or eval code. Handle<Script> script(Script::cast(shared_info->script())); info.reset(new ParseInfo(script)); if (scope_info->scope_type() == EVAL_SCOPE) { info->set_eval(); if (!function->context()->IsNativeContext()) { info->set_outer_scope_info(handle(function->context()->scope_info())); } // Language mode may be inherited from the eval caller. // Retrieve it from shared function info. info->set_language_mode(shared_info->language_mode()); } else if (scope_info->scope_type() == MODULE_SCOPE) { info->set_module(); } else { DCHECK(scope_info->scope_type() == SCRIPT_SCOPE); } } else { // Inner function. info.reset(new ParseInfo(shared_info)); } if (parsing::ParseAny(info.get()) && Rewriter::Rewrite(info.get())) { DeclarationScope* scope = info->literal()->scope(); if (!ignore_nested_scopes || collect_non_locals) { CollectNonLocals(info.get(), scope); } if (!ignore_nested_scopes) { DeclarationScope::Analyze(info.get(), AnalyzeMode::kDebugger); RetrieveScopeChain(scope); } } else { // A failed reparse indicates that the preparser has diverged from the // parser or that the preparse data given to the initial parse has been // faulty. We fail in debug mode but in release mode we only provide the // information we get from the context chain but nothing about // completely stack allocated scopes or stack allocated locals. // Or it could be due to stack overflow. // Silently fail by presenting an empty context chain. CHECK(isolate_->has_pending_exception()); isolate_->clear_pending_exception(); context_ = Handle<Context>(); } UnwrapEvaluationContext(); } ScopeIterator::ScopeIterator(Isolate* isolate, Handle<JSFunction> function) : isolate_(isolate), frame_inspector_(NULL), context_(function->context()), seen_script_scope_(false) { if (!function->shared()->IsSubjectToDebugging()) context_ = Handle<Context>(); UnwrapEvaluationContext(); } ScopeIterator::ScopeIterator(Isolate* isolate, Handle<JSGeneratorObject> generator) : isolate_(isolate), frame_inspector_(NULL), context_(generator->context()), seen_script_scope_(false) { if (!generator->function()->shared()->IsSubjectToDebugging()) { context_ = Handle<Context>(); } UnwrapEvaluationContext(); } void ScopeIterator::UnwrapEvaluationContext() { while (true) { if (context_.is_null()) return; if (!context_->IsDebugEvaluateContext()) return; Handle<Object> wrapped(context_->get(Context::WRAPPED_CONTEXT_INDEX), isolate_); if (wrapped->IsContext()) { context_ = Handle<Context>::cast(wrapped); } else { context_ = Handle<Context>(context_->previous(), isolate_); } } } MUST_USE_RESULT MaybeHandle<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; ASSIGN_RETURN_ON_EXCEPTION(isolate_, scope_object, ScopeObject(), JSObject); details->set(kScopeDetailsObjectIndex, *scope_object); Handle<JSFunction> js_function = HasContext() ? handle(CurrentContext()->closure()) : Handle<JSFunction>::null(); if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript) { return isolate_->factory()->NewJSArrayWithElements(details); } int start_position = 0; int end_position = 0; if (!nested_scope_chain_.is_empty()) { js_function = GetFunction(); start_position = nested_scope_chain_.last().start_position; end_position = nested_scope_chain_.last().end_position; } else if (!js_function.is_null()) { start_position = js_function->shared()->start_position(); end_position = js_function->shared()->end_position(); } if (!js_function.is_null()) { Handle<String> closure_name = JSFunction::GetDebugName(js_function); if (!closure_name.is_null() && closure_name->length() != 0) { details->set(kScopeDetailsNameIndex, *closure_name); } details->set(kScopeDetailsStartPositionIndex, Smi::FromInt(start_position)); details->set(kScopeDetailsEndPositionIndex, Smi::FromInt(end_position)); details->set(kScopeDetailsFunctionIndex, *js_function); } return isolate_->factory()->NewJSArrayWithElements(details); } 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>(); } else if (scope_type == ScopeTypeScript) { seen_script_scope_ = true; if (context_->IsScriptContext()) { context_ = Handle<Context>(context_->previous(), isolate_); } if (!nested_scope_chain_.is_empty()) { DCHECK_EQ(nested_scope_chain_.last().scope_info->scope_type(), SCRIPT_SCOPE); nested_scope_chain_.RemoveLast(); DCHECK(nested_scope_chain_.is_empty()); } CHECK(context_->IsNativeContext()); } else if (nested_scope_chain_.is_empty()) { context_ = Handle<Context>(context_->previous(), isolate_); } else { do { if (nested_scope_chain_.last().scope_info->HasContext()) { DCHECK(context_->previous() != NULL); context_ = Handle<Context>(context_->previous(), isolate_); } nested_scope_chain_.RemoveLast(); if (nested_scope_chain_.is_empty()) break; // Repeat to skip hidden scopes. } while (nested_scope_chain_.last().is_hidden()); } UnwrapEvaluationContext(); } // Return the type of the current scope. ScopeIterator::ScopeType ScopeIterator::Type() { DCHECK(!Done()); if (!nested_scope_chain_.is_empty()) { Handle<ScopeInfo> scope_info = nested_scope_chain_.last().scope_info; switch (scope_info->scope_type()) { case FUNCTION_SCOPE: DCHECK(context_->IsFunctionContext() || !scope_info->HasContext()); return ScopeTypeLocal; case MODULE_SCOPE: DCHECK(context_->IsModuleContext()); return ScopeTypeModule; case SCRIPT_SCOPE: DCHECK(context_->IsScriptContext() || context_->IsNativeContext()); return ScopeTypeScript; case WITH_SCOPE: DCHECK(context_->IsWithContext() || context_->IsDebugEvaluateContext()); return ScopeTypeWith; case CATCH_SCOPE: DCHECK(context_->IsCatchContext()); return ScopeTypeCatch; case BLOCK_SCOPE: DCHECK(!scope_info->HasContext() || context_->IsBlockContext()); return ScopeTypeBlock; case EVAL_SCOPE: DCHECK(!scope_info->HasContext() || 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()) { 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() || context_->IsDebugEvaluateContext()); return ScopeTypeWith; } MaybeHandle<JSObject> ScopeIterator::ScopeObject() { DCHECK(!Done()); switch (Type()) { case ScopeIterator::ScopeTypeGlobal: return Handle<JSObject>(CurrentContext()->global_proxy()); case ScopeIterator::ScopeTypeScript: return MaterializeScriptScope(); case ScopeIterator::ScopeTypeLocal: // Materialize the content of the local scope into a JSObject. DCHECK(nested_scope_chain_.length() == 1); return MaterializeLocalScope(); case ScopeIterator::ScopeTypeWith: return WithContextExtension(); case ScopeIterator::ScopeTypeCatch: return MaterializeCatchScope(); case ScopeIterator::ScopeTypeClosure: // Materialize the content of the closure scope into a JSObject. return MaterializeClosure(); case ScopeIterator::ScopeTypeBlock: case ScopeIterator::ScopeTypeEval: return MaterializeInnerScope(); case ScopeIterator::ScopeTypeModule: return MaterializeModuleScope(); } UNREACHABLE(); return Handle<JSObject>(); } bool ScopeIterator::HasContext() { ScopeType type = Type(); if (type == ScopeTypeBlock || type == ScopeTypeLocal || type == ScopeTypeEval) { if (!nested_scope_chain_.is_empty()) { return nested_scope_chain_.last().scope_info->HasContext(); } } return true; } bool ScopeIterator::SetVariableValue(Handle<String> variable_name, Handle<Object> new_value) { DCHECK(!Done()); switch (Type()) { case ScopeIterator::ScopeTypeGlobal: break; case ScopeIterator::ScopeTypeLocal: return SetLocalVariableValue(variable_name, new_value); case ScopeIterator::ScopeTypeWith: break; case ScopeIterator::ScopeTypeCatch: return SetCatchVariableValue(variable_name, new_value); case ScopeIterator::ScopeTypeClosure: return SetClosureVariableValue(variable_name, new_value); case ScopeIterator::ScopeTypeScript: return SetScriptVariableValue(variable_name, new_value); case ScopeIterator::ScopeTypeBlock: case ScopeIterator::ScopeTypeEval: return SetInnerScopeVariableValue(variable_name, new_value); case ScopeIterator::ScopeTypeModule: // TODO(neis): Implement. break; } return false; } Handle<ScopeInfo> ScopeIterator::CurrentScopeInfo() { DCHECK(!Done()); if (!nested_scope_chain_.is_empty()) { return nested_scope_chain_.last().scope_info; } else if (context_->IsBlockContext() || context_->IsFunctionContext() || context_->IsEvalContext()) { return Handle<ScopeInfo>(context_->scope_info()); } return Handle<ScopeInfo>::null(); } Handle<Context> ScopeIterator::CurrentContext() { DCHECK(!Done()); if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript || nested_scope_chain_.is_empty()) { return context_; } else if (nested_scope_chain_.last().scope_info->HasContext()) { return context_; } else { return Handle<Context>(); } } Handle<StringSet> ScopeIterator::GetNonLocals() { return non_locals_; } #ifdef DEBUG // Debug print of the content of the current scope. void ScopeIterator::DebugPrint() { OFStream os(stdout); DCHECK(!Done()); switch (Type()) { case ScopeIterator::ScopeTypeGlobal: os << "Global:\n"; CurrentContext()->Print(os); break; case ScopeIterator::ScopeTypeLocal: { os << "Local:\n"; GetFunction()->shared()->scope_info()->Print(); if (!CurrentContext().is_null()) { CurrentContext()->Print(os); if (CurrentContext()->has_extension()) { Handle<HeapObject> extension(CurrentContext()->extension(), isolate_); if (extension->IsJSContextExtensionObject()) { extension->Print(os); } } } break; } case ScopeIterator::ScopeTypeWith: os << "With:\n"; CurrentContext()->extension()->Print(os); break; case ScopeIterator::ScopeTypeCatch: os << "Catch:\n"; CurrentContext()->extension()->Print(os); CurrentContext()->get(Context::THROWN_OBJECT_INDEX)->Print(os); break; case ScopeIterator::ScopeTypeClosure: os << "Closure:\n"; CurrentContext()->Print(os); if (CurrentContext()->has_extension()) { Handle<HeapObject> extension(CurrentContext()->extension(), isolate_); if (extension->IsJSContextExtensionObject()) { extension->Print(os); } } break; case ScopeIterator::ScopeTypeScript: os << "Script:\n"; CurrentContext() ->global_object() ->native_context() ->script_context_table() ->Print(os); break; default: UNREACHABLE(); } PrintF("\n"); } #endif void ScopeIterator::RetrieveScopeChain(DeclarationScope* scope) { DCHECK_NOT_NULL(scope); int source_position = frame_inspector_->GetSourcePosition(); GetNestedScopeChain(isolate_, scope, source_position); } void ScopeIterator::CollectNonLocals(ParseInfo* info, DeclarationScope* scope) { DCHECK_NOT_NULL(scope); DCHECK(non_locals_.is_null()); non_locals_ = scope->CollectNonLocals(info, StringSet::New(isolate_)); } MaybeHandle<JSObject> ScopeIterator::MaterializeScriptScope() { Handle<JSGlobalObject> global(CurrentContext()->global_object()); Handle<ScriptContextTable> script_contexts( global->native_context()->script_context_table()); Handle<JSObject> script_scope = isolate_->factory()->NewJSObjectWithNullProto(); for (int context_index = 0; context_index < script_contexts->used(); context_index++) { Handle<Context> context = ScriptContextTable::GetContext(script_contexts, context_index); Handle<ScopeInfo> scope_info(context->scope_info()); CopyContextLocalsToScopeObject(scope_info, context, script_scope); } return script_scope; } MaybeHandle<JSObject> ScopeIterator::MaterializeLocalScope() { Handle<JSFunction> function = GetFunction(); Handle<JSObject> local_scope = isolate_->factory()->NewJSObjectWithNullProto(); frame_inspector_->MaterializeStackLocals(local_scope, function); Handle<Context> frame_context = Handle<Context>::cast(frame_inspector_->GetContext()); HandleScope scope(isolate_); Handle<SharedFunctionInfo> shared(function->shared()); Handle<ScopeInfo> scope_info(shared->scope_info()); if (!scope_info->HasContext()) return local_scope; // Fill all context locals. Handle<Context> function_context(frame_context->closure_context()); CopyContextLocalsToScopeObject(scope_info, function_context, local_scope); // Finally copy any properties from the function context extension. // These will be variables introduced by eval. if (function_context->closure() == *function && !function_context->IsNativeContext()) { CopyContextExtensionToScopeObject(function_context, local_scope, KeyCollectionMode::kIncludePrototypes); } return local_scope; } // Create a plain JSObject which materializes the closure content for the // context. Handle<JSObject> ScopeIterator::MaterializeClosure() { Handle<Context> context = CurrentContext(); DCHECK(context->IsFunctionContext() || context->IsEvalContext()); Handle<SharedFunctionInfo> shared(context->closure()->shared()); Handle<ScopeInfo> scope_info(shared->scope_info()); // Allocate and initialize a JSObject with all the content of this function // closure. Handle<JSObject> closure_scope = isolate_->factory()->NewJSObjectWithNullProto(); // Fill all context locals to the context extension. CopyContextLocalsToScopeObject(scope_info, context, closure_scope); // Finally copy any properties from the function context extension. This will // be variables introduced by eval. CopyContextExtensionToScopeObject(context, closure_scope, KeyCollectionMode::kOwnOnly); return closure_scope; } // Create a plain JSObject which materializes the scope for the specified // catch context. Handle<JSObject> ScopeIterator::MaterializeCatchScope() { Handle<Context> context = CurrentContext(); DCHECK(context->IsCatchContext()); Handle<String> name(context->catch_name()); Handle<Object> thrown_object(context->get(Context::THROWN_OBJECT_INDEX), isolate_); Handle<JSObject> catch_scope = isolate_->factory()->NewJSObjectWithNullProto(); JSObject::SetOwnPropertyIgnoreAttributes(catch_scope, name, thrown_object, NONE) .Check(); return catch_scope; } // Retrieve the with-context extension object. If the extension object is // a proxy, return an empty object. Handle<JSObject> ScopeIterator::WithContextExtension() { Handle<Context> context = CurrentContext(); DCHECK(context->IsWithContext()); if (context->extension_receiver()->IsJSProxy()) { return isolate_->factory()->NewJSObjectWithNullProto(); } return handle(JSObject::cast(context->extension_receiver())); } // Create a plain JSObject which materializes the block scope for the specified // block context. Handle<JSObject> ScopeIterator::MaterializeInnerScope() { Handle<JSObject> inner_scope = isolate_->factory()->NewJSObjectWithNullProto(); Handle<Context> context = Handle<Context>::null(); if (!nested_scope_chain_.is_empty()) { Handle<ScopeInfo> scope_info = nested_scope_chain_.last().scope_info; frame_inspector_->MaterializeStackLocals(inner_scope, scope_info); if (scope_info->HasContext()) context = CurrentContext(); } else { context = CurrentContext(); } if (!context.is_null()) { // Fill all context locals. CopyContextLocalsToScopeObject(CurrentScopeInfo(), context, inner_scope); CopyContextExtensionToScopeObject(context, inner_scope, KeyCollectionMode::kOwnOnly); } return inner_scope; } // Create a plain JSObject which materializes the module scope for the specified // module context. MaybeHandle<JSObject> ScopeIterator::MaterializeModuleScope() { Handle<Context> context = CurrentContext(); DCHECK(context->IsModuleContext()); Handle<ScopeInfo> scope_info(context->scope_info()); Handle<JSObject> module_scope = isolate_->factory()->NewJSObjectWithNullProto(); CopyContextLocalsToScopeObject(scope_info, context, module_scope); CopyModuleVarsToScopeObject(scope_info, context, module_scope); return module_scope; } bool ScopeIterator::SetParameterValue(Handle<ScopeInfo> scope_info, JavaScriptFrame* frame, Handle<String> parameter_name, Handle<Object> new_value) { // Setting stack locals of optimized frames is not supported. if (frame->is_optimized()) return false; HandleScope scope(isolate_); for (int i = 0; i < scope_info->ParameterCount(); ++i) { if (String::Equals(handle(scope_info->ParameterName(i)), parameter_name)) { frame->SetParameterValue(i, *new_value); return true; } } return false; } bool ScopeIterator::SetStackVariableValue(Handle<ScopeInfo> scope_info, Handle<String> variable_name, Handle<Object> new_value) { if (frame_inspector_ == nullptr) return false; JavaScriptFrame* frame = GetFrame(); // Setting stack locals of optimized frames is not supported. if (frame->is_optimized()) return false; HandleScope scope(isolate_); for (int i = 0; i < scope_info->StackLocalCount(); ++i) { if (String::Equals(handle(scope_info->StackLocalName(i)), variable_name)) { frame->SetExpression(scope_info->StackLocalIndex(i), *new_value); return true; } } return false; } bool ScopeIterator::SetContextVariableValue(Handle<ScopeInfo> scope_info, Handle<Context> context, Handle<String> variable_name, Handle<Object> new_value) { HandleScope scope(isolate_); for (int i = 0; i < scope_info->ContextLocalCount(); i++) { Handle<String> next_name(scope_info->ContextLocalName(i)); if (String::Equals(variable_name, next_name)) { VariableMode mode; InitializationFlag init_flag; MaybeAssignedFlag maybe_assigned_flag; int context_index = ScopeInfo::ContextSlotIndex( scope_info, next_name, &mode, &init_flag, &maybe_assigned_flag); context->set(context_index, *new_value); return true; } } if (context->has_extension()) { Handle<JSObject> ext(context->extension_object()); Maybe<bool> maybe = JSReceiver::HasOwnProperty(ext, variable_name); DCHECK(maybe.IsJust()); if (maybe.FromJust()) { // We don't expect this to do anything except replacing property value. JSObject::SetOwnPropertyIgnoreAttributes(ext, variable_name, new_value, NONE) .Check(); return true; } } return false; } bool ScopeIterator::SetLocalVariableValue(Handle<String> variable_name, Handle<Object> new_value) { JavaScriptFrame* frame = GetFrame(); Handle<ScopeInfo> scope_info(frame->function()->shared()->scope_info()); // Parameter might be shadowed in context. Don't stop here. bool result = SetParameterValue(scope_info, frame, variable_name, new_value); // Stack locals. if (SetStackVariableValue(scope_info, variable_name, new_value)) { return true; } if (scope_info->HasContext() && SetContextVariableValue(scope_info, CurrentContext(), variable_name, new_value)) { return true; } return result; } bool ScopeIterator::SetInnerScopeVariableValue(Handle<String> variable_name, Handle<Object> new_value) { Handle<ScopeInfo> scope_info = CurrentScopeInfo(); DCHECK(scope_info->scope_type() == BLOCK_SCOPE || scope_info->scope_type() == EVAL_SCOPE); // Setting stack locals of optimized frames is not supported. if (SetStackVariableValue(scope_info, variable_name, new_value)) { return true; } if (HasContext() && SetContextVariableValue(scope_info, CurrentContext(), variable_name, new_value)) { return true; } return false; } // This method copies structure of MaterializeClosure method above. bool ScopeIterator::SetClosureVariableValue(Handle<String> variable_name, Handle<Object> new_value) { DCHECK(CurrentContext()->IsFunctionContext() || CurrentContext()->IsEvalContext()); return SetContextVariableValue(CurrentScopeInfo(), CurrentContext(), variable_name, new_value); } bool ScopeIterator::SetScriptVariableValue(Handle<String> variable_name, Handle<Object> new_value) { Handle<String> internalized_variable_name = isolate_->factory()->InternalizeString(variable_name); Handle<Context> context = CurrentContext(); Handle<ScriptContextTable> script_contexts( context->global_object()->native_context()->script_context_table()); ScriptContextTable::LookupResult lookup_result; if (ScriptContextTable::Lookup(script_contexts, internalized_variable_name, &lookup_result)) { Handle<Context> script_context = ScriptContextTable::GetContext( script_contexts, lookup_result.context_index); script_context->set(lookup_result.slot_index, *new_value); return true; } return false; } bool ScopeIterator::SetCatchVariableValue(Handle<String> variable_name, Handle<Object> new_value) { Handle<Context> context = CurrentContext(); DCHECK(context->IsCatchContext()); Handle<String> name(context->catch_name()); if (!String::Equals(name, variable_name)) { return false; } context->set(Context::THROWN_OBJECT_INDEX, *new_value); return true; } void ScopeIterator::CopyContextLocalsToScopeObject( Handle<ScopeInfo> scope_info, Handle<Context> context, Handle<JSObject> scope_object) { Isolate* isolate = scope_info->GetIsolate(); int local_count = scope_info->ContextLocalCount(); if (local_count == 0) return; // Fill all context locals to the context extension. for (int i = 0; i < local_count; ++i) { Handle<String> name(scope_info->ContextLocalName(i)); if (ScopeInfo::VariableIsSynthetic(*name)) continue; int context_index = Context::MIN_CONTEXT_SLOTS + i; Handle<Object> value = Handle<Object>(context->get(context_index), isolate); // Reflect variables under TDZ as undefined in scope object. if (value->IsTheHole(isolate)) continue; // This should always succeed. // TODO(verwaest): Use AddDataProperty instead. JSObject::SetOwnPropertyIgnoreAttributes(scope_object, name, value, NONE) .Check(); } } void ScopeIterator::CopyModuleVarsToScopeObject(Handle<ScopeInfo> scope_info, Handle<Context> context, Handle<JSObject> scope_object) { Isolate* isolate = scope_info->GetIsolate(); int module_variable_count = Smi::cast(scope_info->get(scope_info->ModuleVariableCountIndex())) ->value(); for (int i = 0; i < module_variable_count; ++i) { Handle<String> local_name; Handle<Object> value; { String* name; int index; scope_info->ModuleVariable(i, &name, &index); CHECK(!ScopeInfo::VariableIsSynthetic(name)); local_name = handle(name, isolate); value = Module::LoadVariable(handle(context->module(), isolate), index); } // Reflect variables under TDZ as undefined in scope object. if (value->IsTheHole(isolate)) continue; // This should always succeed. // TODO(verwaest): Use AddDataProperty instead. JSObject::SetOwnPropertyIgnoreAttributes(scope_object, local_name, value, NONE) .Check(); } } void ScopeIterator::CopyContextExtensionToScopeObject( Handle<Context> context, Handle<JSObject> scope_object, KeyCollectionMode mode) { if (context->extension_object() == nullptr) return; Handle<JSObject> extension(context->extension_object()); Handle<FixedArray> keys = KeyAccumulator::GetKeys(extension, mode, 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))); Handle<Object> value = Object::GetPropertyOrElement(extension, key).ToHandleChecked(); JSObject::SetOwnPropertyIgnoreAttributes(scope_object, key, value, NONE) .Check(); } } void ScopeIterator::GetNestedScopeChain(Isolate* isolate, Scope* scope, int position) { if (scope->is_function_scope()) { // Do not collect scopes of nested inner functions inside the current one. // Nested arrow functions could have the same end positions. Handle<JSFunction> function = frame_inspector_->GetFunction(); if (scope->start_position() > function->shared()->start_position() && scope->end_position() <= function->shared()->end_position()) { return; } } if (scope->is_hidden()) { // We need to add this chain element in case the scope has a context // associated. We need to keep the scope chain and context chain in sync. nested_scope_chain_.Add(ExtendedScopeInfo(scope->scope_info())); } else { nested_scope_chain_.Add(ExtendedScopeInfo( scope->scope_info(), scope->start_position(), scope->end_position())); } for (Scope* inner_scope = scope->inner_scope(); inner_scope != nullptr; inner_scope = inner_scope->sibling()) { int beg_pos = inner_scope->start_position(); int end_pos = inner_scope->end_position(); DCHECK((beg_pos >= 0 && end_pos >= 0) || inner_scope->is_hidden()); if (beg_pos <= position && position < end_pos) { GetNestedScopeChain(isolate, inner_scope, position); return; } } } } // namespace internal } // namespace v8