Commit 4936efb0 authored by Toon Verwaest's avatar Toon Verwaest Committed by Commit Bot

Reland "[debugger] Rewrite the ScopeIterator/DebugEvaluate to use Scope rather...

Reland "[debugger] Rewrite the ScopeIterator/DebugEvaluate to use Scope rather than ScopeInfo for inner scopes."

This is a reland of 9e27d473

Original change's description:
> [debugger] Rewrite the ScopeIterator/DebugEvaluate to use Scope rather than ScopeInfo for inner scopes.
> 
> This CL also bottlenecks all current scope handling in the ScopeIterator, and cleans up frame handling in debug-frames and the deoptimizer.
> 
> Change-Id: I061922a356ce17794262f8d77d5d7c824558fc50
> Reviewed-on: https://chromium-review.googlesource.com/1095094
> Commit-Queue: Toon Verwaest <verwaest@chromium.org>
> Reviewed-by: Georg Neis <neis@chromium.org>
> Reviewed-by: Jaroslav Sevcik <jarin@chromium.org>
> Reviewed-by: Yang Guo <yangguo@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#53741}

Change-Id: I05262fef66d852876b9bb2869339053629c9b51d
Reviewed-on: https://chromium-review.googlesource.com/1102297Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Commit-Queue: Toon Verwaest <verwaest@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53751}
parent a0b2ccea
......@@ -2369,21 +2369,8 @@ void Scope::AllocateScopeInfosRecursively(Isolate* isolate,
}
}
void Scope::AllocateDebuggerScopeInfos(Isolate* isolate,
MaybeHandle<ScopeInfo> outer_scope) {
if (scope_info_.is_null()) {
scope_info_ = ScopeInfo::Create(isolate, zone(), this, outer_scope);
}
MaybeHandle<ScopeInfo> outer = NeedsContext() ? scope_info_ : outer_scope;
for (Scope* scope = inner_scope_; scope != nullptr; scope = scope->sibling_) {
if (scope->is_function_scope()) continue;
scope->AllocateDebuggerScopeInfos(isolate, outer);
}
}
// static
void DeclarationScope::AllocateScopeInfos(ParseInfo* info, Isolate* isolate,
AnalyzeMode mode) {
void DeclarationScope::AllocateScopeInfos(ParseInfo* info, Isolate* isolate) {
DeclarationScope* scope = info->literal()->scope();
if (!scope->scope_info_.is_null()) return; // Allocated by outer function.
......@@ -2393,9 +2380,6 @@ void DeclarationScope::AllocateScopeInfos(ParseInfo* info, Isolate* isolate,
}
scope->AllocateScopeInfosRecursively(isolate, outer_scope);
if (mode == AnalyzeMode::kDebugger) {
scope->AllocateDebuggerScopeInfos(isolate, outer_scope);
}
// The debugger expects all shared function infos to contain a scope info.
// Since the top-most scope will end up in a shared function info, make sure
......
......@@ -78,8 +78,6 @@ class SloppyBlockFunctionMap : public ZoneHashMap {
int count_;
};
enum class AnalyzeMode { kRegular, kDebugger };
// Global invariants after AST construction: Each reference (i.e. identifier)
// to a JavaScript variable (including global properties) is represented by a
// VariableProxy node. Immediately after AST construction and before variable
......@@ -367,7 +365,8 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
// Whether this needs to be represented by a runtime context.
bool NeedsContext() const {
// Catch scopes always have heap slots.
DCHECK(!is_catch_scope() || num_heap_slots() > 0);
DCHECK_IMPLIES(is_catch_scope(), num_heap_slots() > 0);
DCHECK_IMPLIES(is_with_scope(), num_heap_slots() > 0);
return num_heap_slots() > 0;
}
......@@ -813,7 +812,7 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
// The local variable 'arguments' if we need to allocate it; nullptr
// otherwise.
Variable* arguments() const {
DCHECK(!is_arrow_scope() || arguments_ == nullptr);
DCHECK_IMPLIES(is_arrow_scope(), arguments_ == nullptr);
return arguments_;
}
......@@ -868,8 +867,7 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
// Allocate ScopeInfos for top scope and any inner scopes that need them.
// Does nothing if ScopeInfo is already allocated.
static void AllocateScopeInfos(ParseInfo* info, Isolate* isolate,
AnalyzeMode mode);
static void AllocateScopeInfos(ParseInfo* info, Isolate* isolate);
Handle<StringSet> CollectNonLocals(ParseInfo* info,
Handle<StringSet> non_locals);
......
......@@ -288,8 +288,7 @@ void UnoptimizedCompileJob::FinalizeOnMainThread(Isolate* isolate) {
// Internalize ast values onto the heap.
parse_info_->ast_value_factory()->Internalize(isolate);
// Allocate scope infos for the literal.
DeclarationScope::AllocateScopeInfos(parse_info_.get(), isolate,
AnalyzeMode::kRegular);
DeclarationScope::AllocateScopeInfos(parse_info_.get(), isolate);
if (compilation_job_->state() == CompilationJob::State::kFailed ||
!Compiler::FinalizeCompilationJob(compilation_job_.release(), shared_,
isolate)) {
......
......@@ -509,8 +509,7 @@ bool FinalizeUnoptimizedCode(
DCHECK(AllowCompilation::IsAllowed(isolate));
// Allocate scope infos for the literal.
DeclarationScope::AllocateScopeInfos(parse_info, isolate,
AnalyzeMode::kRegular);
DeclarationScope::AllocateScopeInfos(parse_info, isolate);
// Finalize the outer-most function's compilation job.
if (FinalizeUnoptimizedCompilationJob(outer_function_job, shared_info,
......
......@@ -159,20 +159,23 @@ MaybeHandle<Object> DebugEvaluate::Evaluate(
return result;
}
Handle<SharedFunctionInfo> DebugEvaluate::ContextBuilder::outer_info() const {
return handle(frame_inspector_.GetFunction()->shared(), isolate_);
}
DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate,
JavaScriptFrame* frame,
int inlined_jsframe_index)
: isolate_(isolate),
frame_(frame),
inlined_jsframe_index_(inlined_jsframe_index) {
FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate);
Handle<JSFunction> local_function = frame_inspector.GetFunction();
Handle<Context> outer_context(local_function->context());
frame_inspector_(frame, inlined_jsframe_index, isolate),
scope_iterator_(isolate, &frame_inspector_,
ScopeIterator::COLLECT_NON_LOCALS) {
Handle<Context> outer_context(frame_inspector_.GetFunction()->context());
evaluation_context_ = outer_context;
outer_info_ = handle(local_function->shared(), isolate);
Factory* factory = isolate->factory();
if (scope_iterator_.Done()) return;
// To evaluate as if we were running eval at the point of the debug break,
// we reconstruct the context chain as follows:
// - To make stack-allocated variables visible, we materialize them and
......@@ -189,64 +192,32 @@ DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate,
// - Look up in the materialized stack variables.
// - Look up in the original context.
// - Check the whitelist to find out whether to skip contexts during lookup.
const ScopeIterator::Option option = ScopeIterator::COLLECT_NON_LOCALS;
for (ScopeIterator it(isolate, &frame_inspector, option); !it.Done();
it.Next()) {
ScopeIterator::ScopeType scope_type = it.Type();
for (; scope_iterator_.InInnerScope(); scope_iterator_.Next()) {
ScopeIterator::ScopeType scope_type = scope_iterator_.Type();
if (scope_type == ScopeIterator::ScopeTypeScript) break;
ContextChainElement context_chain_element;
if (scope_type == ScopeIterator::ScopeTypeLocal ||
scope_iterator_.DeclaresLocals(ScopeIterator::Mode::STACK)) {
context_chain_element.materialized_object =
scope_iterator_.ScopeObject(ScopeIterator::Mode::STACK);
}
if (scope_iterator_.HasContext()) {
context_chain_element.wrapped_context = scope_iterator_.CurrentContext();
}
if (scope_type == ScopeIterator::ScopeTypeLocal) {
DCHECK_EQ(FUNCTION_SCOPE, it.CurrentScopeInfo()->scope_type());
Handle<JSObject> materialized = factory->NewJSObjectWithNullProto();
Handle<Context> local_context =
it.HasContext() ? it.CurrentContext() : outer_context;
Handle<StringSet> non_locals = it.GetNonLocals();
MaterializeReceiver(materialized, local_context, local_function,
non_locals);
MaterializeStackLocals(materialized, local_function, &frame_inspector);
ContextChainElement context_chain_element;
context_chain_element.scope_info = it.CurrentScopeInfo();
context_chain_element.materialized_object = materialized;
// Non-locals that are already being referenced by the current function
// are guaranteed to be correctly resolved.
context_chain_element.whitelist = non_locals;
if (it.HasContext()) {
context_chain_element.wrapped_context = it.CurrentContext();
}
context_chain_.push_back(context_chain_element);
evaluation_context_ = outer_context;
break;
} else if (scope_type == ScopeIterator::ScopeTypeCatch ||
scope_type == ScopeIterator::ScopeTypeWith ||
scope_type == ScopeIterator::ScopeTypeModule) {
ContextChainElement context_chain_element;
Handle<Context> current_context = it.CurrentContext();
if (!current_context->IsDebugEvaluateContext()) {
context_chain_element.wrapped_context = current_context;
}
context_chain_.push_back(context_chain_element);
} else if (scope_type == ScopeIterator::ScopeTypeBlock ||
scope_type == ScopeIterator::ScopeTypeEval) {
Handle<JSObject> materialized = factory->NewJSObjectWithNullProto();
frame_inspector.MaterializeStackLocals(materialized,
it.CurrentScopeInfo());
ContextChainElement context_chain_element;
context_chain_element.scope_info = it.CurrentScopeInfo();
context_chain_element.materialized_object = materialized;
if (it.HasContext()) {
context_chain_element.wrapped_context = it.CurrentContext();
}
context_chain_.push_back(context_chain_element);
} else {
break;
context_chain_element.whitelist = scope_iterator_.GetNonLocals();
}
context_chain_.push_back(context_chain_element);
}
Handle<ScopeInfo> scope_info =
evaluation_context_->IsNativeContext()
? Handle<ScopeInfo>::null()
: handle(evaluation_context_->scope_info(), isolate);
for (auto rit = context_chain_.rbegin(); rit != context_chain_.rend();
rit++) {
ContextChainElement element = *rit;
Handle<ScopeInfo> scope_info(ScopeInfo::CreateForWithScope(
isolate, evaluation_context_->IsNativeContext()
? Handle<ScopeInfo>::null()
: Handle<ScopeInfo>(evaluation_context_->scope_info())));
scope_info = ScopeInfo::CreateForWithScope(isolate, scope_info);
scope_info->SetIsDebugEvaluateScope();
evaluation_context_ = factory->NewDebugEvaluateContext(
evaluation_context_, scope_info, element.materialized_object,
......@@ -256,62 +227,24 @@ DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate,
void DebugEvaluate::ContextBuilder::UpdateValues() {
scope_iterator_.Restart();
for (ContextChainElement& element : context_chain_) {
if (!element.materialized_object.is_null()) {
// Write back potential changes to materialized stack locals to the stack.
FrameInspector(frame_, inlined_jsframe_index_, isolate_)
.UpdateStackLocalsFromMaterializedObject(element.materialized_object,
element.scope_info);
Handle<FixedArray> keys =
KeyAccumulator::GetKeys(element.materialized_object,
KeyCollectionMode::kOwnOnly,
ENUMERABLE_STRINGS)
.ToHandleChecked();
for (int i = 0; i < keys->length(); i++) {
DCHECK(keys->get(i)->IsString());
Handle<String> key(String::cast(keys->get(i)), isolate_);
Handle<Object> value =
JSReceiver::GetDataProperty(element.materialized_object, key);
scope_iterator_.SetVariableValue(key, value);
}
}
}
}
void DebugEvaluate::ContextBuilder::MaterializeReceiver(
Handle<JSObject> target, Handle<Context> local_context,
Handle<JSFunction> local_function, Handle<StringSet> non_locals) {
Handle<Object> recv = isolate_->factory()->undefined_value();
Handle<String> name = isolate_->factory()->this_string();
if (non_locals->Has(name)) {
// 'this' is allocated in an outer context and is is already being
// referenced by the current function, so it can be correctly resolved.
return;
} else if (local_function->shared()->scope_info()->HasReceiver() &&
!frame_->receiver()->IsTheHole(isolate_)) {
recv = handle(frame_->receiver(), isolate_);
}
JSObject::SetOwnPropertyIgnoreAttributes(target, name, recv, NONE).Check();
}
void DebugEvaluate::ContextBuilder::MaterializeStackLocals(
Handle<JSObject> target, Handle<JSFunction> function,
FrameInspector* frame_inspector) {
bool materialize_arguments_object = true;
// Do not materialize the arguments object for eval or top-level code.
if (function->shared()->is_toplevel()) materialize_arguments_object = false;
// First materialize stack locals (modulo arguments object).
Handle<SharedFunctionInfo> shared(function->shared());
Handle<ScopeInfo> scope_info(shared->scope_info());
frame_inspector->MaterializeStackLocals(target, scope_info,
materialize_arguments_object);
// Then materialize the arguments object.
if (materialize_arguments_object) {
// Skip if "arguments" is already taken and wasn't optimized out (which
// causes {MaterializeStackLocals} above to skip the local variable).
Handle<String> arguments_str = isolate_->factory()->arguments_string();
Maybe<bool> maybe = JSReceiver::HasOwnProperty(target, arguments_str);
DCHECK(maybe.IsJust());
if (maybe.FromJust()) return;
// FunctionGetArguments can't throw an exception.
Handle<JSObject> arguments =
Accessors::FunctionGetArguments(frame_, inlined_jsframe_index_);
JSObject::SetOwnPropertyIgnoreAttributes(target, arguments_str, arguments,
NONE)
.Check();
scope_iterator_.Next();
}
}
......
......@@ -7,7 +7,8 @@
#include <vector>
#include "src/frames.h"
#include "src/debug/debug-frames.h"
#include "src/debug/debug-scopes.h"
#include "src/objects.h"
#include "src/objects/shared-function-info.h"
#include "src/objects/string-table.h"
......@@ -70,31 +71,20 @@ class DebugEvaluate : public AllStatic {
void UpdateValues();
Handle<Context> evaluation_context() const { return evaluation_context_; }
Handle<SharedFunctionInfo> outer_info() const { return outer_info_; }
Handle<SharedFunctionInfo> outer_info() const;
private:
struct ContextChainElement {
Handle<ScopeInfo> scope_info;
Handle<Context> wrapped_context;
Handle<JSObject> materialized_object;
Handle<StringSet> whitelist;
};
void MaterializeReceiver(Handle<JSObject> target,
Handle<Context> local_context,
Handle<JSFunction> local_function,
Handle<StringSet> non_locals);
void MaterializeStackLocals(Handle<JSObject> target,
Handle<JSFunction> function,
FrameInspector* frame_inspector);
Handle<SharedFunctionInfo> outer_info_;
Handle<Context> evaluation_context_;
std::vector<ContextChainElement> context_chain_;
Isolate* isolate_;
JavaScriptFrame* frame_;
int inlined_jsframe_index_;
FrameInspector frame_inspector_;
ScopeIterator scope_iterator_;
};
static MaybeHandle<Object> Evaluate(Isolate* isolate,
......
......@@ -15,6 +15,7 @@ namespace internal {
FrameInspector::FrameInspector(StandardFrame* frame, int inlined_frame_index,
Isolate* isolate)
: frame_(frame),
inlined_frame_index_(inlined_frame_index),
isolate_(isolate) {
// Extract the relevant information from the frame summary and discard it.
FrameSummary summary = FrameSummary::Get(frame, inlined_frame_index);
......@@ -82,97 +83,6 @@ bool FrameInspector::IsWasm() { return frame_->is_wasm(); }
bool FrameInspector::IsJavaScript() { return frame_->is_java_script(); }
// To inspect all the provided arguments the frame might need to be
// replaced with the arguments frame.
void FrameInspector::SetArgumentsFrame(StandardFrame* frame) {
DCHECK(has_adapted_arguments_);
DCHECK(frame->is_arguments_adaptor());
frame_ = frame;
is_optimized_ = frame_->is_optimized();
is_interpreted_ = frame_->is_interpreted();
DCHECK(!is_optimized_);
}
// Create a plain JSObject which materializes the local scope for the specified
// frame.
void FrameInspector::MaterializeStackLocals(Handle<JSObject> target,
Handle<ScopeInfo> scope_info,
bool materialize_arguments_object) {
HandleScope scope(isolate_);
// First fill all parameters.
for (int i = 0; i < scope_info->ParameterCount(); ++i) {
// Do not materialize the parameter if it is shadowed by a context local.
// TODO(yangguo): check whether this is necessary, now that we materialize
// context locals as well.
Handle<String> name(scope_info->ParameterName(i));
if (ScopeInfo::VariableIsSynthetic(*name)) continue;
if (ParameterIsShadowedByContextLocal(scope_info, name)) continue;
Handle<Object> value =
i < GetParametersCount()
? GetParameter(i)
: Handle<Object>::cast(isolate_->factory()->undefined_value());
DCHECK(!value->IsTheHole(isolate_));
JSObject::SetOwnPropertyIgnoreAttributes(target, name, value, NONE).Check();
}
// Second fill all stack locals.
for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
Handle<String> name(scope_info->StackLocalName(i));
if (ScopeInfo::VariableIsSynthetic(*name)) continue;
Handle<Object> value = GetExpression(scope_info->StackLocalIndex(i));
// TODO(yangguo): We convert optimized out values to {undefined} when they
// are passed to the debugger. Eventually we should handle them somehow.
if (value->IsTheHole(isolate_)) {
value = isolate_->factory()->undefined_value();
}
if (value->IsOptimizedOut(isolate_)) {
if (materialize_arguments_object) {
Handle<String> arguments_str = isolate_->factory()->arguments_string();
if (String::Equals(name, arguments_str)) continue;
}
value = isolate_->factory()->undefined_value();
}
JSObject::SetOwnPropertyIgnoreAttributes(target, name, value, NONE).Check();
}
}
void FrameInspector::UpdateStackLocalsFromMaterializedObject(
Handle<JSObject> target, Handle<ScopeInfo> scope_info) {
// Optimized frames and wasm frames are not supported. Simply give up.
if (is_optimized_ || frame_->is_wasm()) return;
HandleScope scope(isolate_);
// Parameters.
for (int i = 0; i < scope_info->ParameterCount(); ++i) {
// Shadowed parameters were not materialized.
Handle<String> name(scope_info->ParameterName(i));
if (ScopeInfo::VariableIsSynthetic(*name)) continue;
if (ParameterIsShadowedByContextLocal(scope_info, name)) continue;
DCHECK(!javascript_frame()->GetParameter(i)->IsTheHole(isolate_));
Handle<Object> value =
Object::GetPropertyOrElement(target, name).ToHandleChecked();
javascript_frame()->SetParameterValue(i, *value);
}
// Stack locals.
for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
Handle<String> name(scope_info->StackLocalName(i));
if (ScopeInfo::VariableIsSynthetic(*name)) continue;
int index = scope_info->StackLocalIndex(i);
if (frame_->GetExpression(index)->IsTheHole(isolate_)) continue;
Handle<Object> value =
Object::GetPropertyOrElement(target, name).ToHandleChecked();
frame_->SetExpression(index, *value);
}
}
bool FrameInspector::ParameterIsShadowedByContextLocal(
Handle<ScopeInfo> info, Handle<String> parameter_name) {
VariableMode mode;
......
......@@ -22,7 +22,7 @@ class FrameInspector {
~FrameInspector();
int GetParametersCount();
Handle<JSFunction> GetFunction() { return function_; }
Handle<JSFunction> GetFunction() const { return function_; }
Handle<Script> GetScript() { return script_; }
Handle<Object> GetParameter(int index);
Handle<Object> GetExpression(int index);
......@@ -41,21 +41,14 @@ class FrameInspector {
: JavaScriptFrame::cast(frame_);
}
JavaScriptFrame* GetArgumentsFrame() { return javascript_frame(); }
void SetArgumentsFrame(StandardFrame* frame);
void MaterializeStackLocals(Handle<JSObject> target,
Handle<ScopeInfo> scope_info,
bool materialize_arguments_object = false);
void UpdateStackLocalsFromMaterializedObject(Handle<JSObject> object,
Handle<ScopeInfo> scope_info);
int inlined_frame_index() const { return inlined_frame_index_; }
private:
bool ParameterIsShadowedByContextLocal(Handle<ScopeInfo> info,
Handle<String> parameter_name);
StandardFrame* frame_;
int inlined_frame_index_;
std::unique_ptr<DeoptimizedFrameInfo> deoptimized_frame_;
wasm::WasmInterpreter::FramePtr wasm_interpreted_frame_;
Isolate* isolate_;
......
......@@ -75,26 +75,8 @@ void DebugScopeIterator::Advance() {
}
bool DebugScopeIterator::ShouldIgnore() {
// Almost always Script scope will be empty, so just filter out that noise.
// Also drop empty Block, Eval and Script scopes, should we get any.
DCHECK(!Done());
debug::ScopeIterator::ScopeType type = GetType();
if (type != debug::ScopeIterator::ScopeTypeBlock &&
type != debug::ScopeIterator::ScopeTypeScript &&
type != debug::ScopeIterator::ScopeTypeEval &&
type != debug::ScopeIterator::ScopeTypeModule) {
return false;
}
// TODO(kozyatinskiy): make this function faster.
Handle<JSObject> value;
if (!iterator_.ScopeObject().ToHandle(&value)) return false;
Handle<FixedArray> keys =
KeyAccumulator::GetKeys(value, KeyCollectionMode::kOwnOnly,
ENUMERABLE_STRINGS,
GetKeysConversion::kConvertToString)
.ToHandleChecked();
return keys->length() == 0;
if (GetType() == debug::ScopeIterator::ScopeTypeLocal) return false;
return !iterator_.DeclaresLocals(i::ScopeIterator::Mode::ALL);
}
v8::debug::ScopeIterator::ScopeType DebugScopeIterator::GetType() {
......@@ -104,11 +86,8 @@ v8::debug::ScopeIterator::ScopeType DebugScopeIterator::GetType() {
v8::Local<v8::Object> DebugScopeIterator::GetObject() {
DCHECK(!Done());
Handle<JSObject> value;
if (iterator_.ScopeObject().ToHandle(&value)) {
return Utils::ToLocal(value);
}
return v8::Local<v8::Object>();
Handle<JSObject> value = iterator_.ScopeObject(i::ScopeIterator::Mode::ALL);
return Utils::ToLocal(value);
}
v8::Local<v8::Function> DebugScopeIterator::GetFunction() {
......
......@@ -25,24 +25,22 @@ ScopeIterator::ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector,
: isolate_(isolate),
frame_inspector_(frame_inspector),
function_(frame_inspector_->GetFunction()),
script_(frame_inspector_->GetScript()),
seen_script_scope_(false) {
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());
context_ = Handle<Context>::cast(frame_inspector->GetContext());
// We should not instantiate a ScopeIterator for wasm frames.
DCHECK(frame_inspector->GetScript()->type() != Script::TYPE_WASM);
DCHECK_NE(Script::TYPE_WASM, frame_inspector->GetScript()->type());
TryParseAndRetrieveScopes(option);
}
ScopeIterator::~ScopeIterator() { delete info_; }
Handle<Object> ScopeIterator::GetFunctionDebugName() const {
if (HasNestedScopeChain()) return JSFunction::GetDebugName(function_);
if (!context_->IsNativeContext()) {
DisallowHeapAllocation no_gc;
ScopeInfo* closure_info = context_->closure_context()->scope_info();
......@@ -55,9 +53,10 @@ Handle<Object> ScopeIterator::GetFunctionDebugName() const {
ScopeIterator::ScopeIterator(Isolate* isolate, Handle<JSFunction> function)
: isolate_(isolate),
context_(function->context()),
script_(Script::cast(function->shared()->script())),
seen_script_scope_(false) {
if (!function->shared()->IsSubjectToDebugging()) context_ = Handle<Context>();
script_(Script::cast(function->shared()->script())) {
if (!function->shared()->IsSubjectToDebugging()) {
context_ = Handle<Context>();
}
UnwrapEvaluationContext();
}
......@@ -67,8 +66,7 @@ ScopeIterator::ScopeIterator(Isolate* isolate,
generator_(generator),
function_(generator->function()),
context_(generator->context()),
script_(Script::cast(function_->shared()->script())),
seen_script_scope_(false) {
script_(Script::cast(function_->shared()->script())) {
if (!function_->shared()->IsSubjectToDebugging()) {
context_ = Handle<Context>();
return;
......@@ -76,26 +74,29 @@ ScopeIterator::ScopeIterator(Isolate* isolate,
TryParseAndRetrieveScopes(DEFAULT);
}
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();
}
void ScopeIterator::TryParseAndRetrieveScopes(ScopeIterator::Option option) {
// Catch the case when the debugger stops in an internal function.
Handle<SharedFunctionInfo> shared_info(function_->shared());
Handle<ScopeInfo> scope_info(shared_info->scope_info());
if (shared_info->script()->IsUndefined(isolate_)) {
current_scope_ = closure_scope_ = nullptr;
context_ = handle(function_->context(), isolate_);
function_ = Handle<JSFunction>();
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->HasBreakInfo() &&
frame_inspector_ != nullptr) {
DCHECK_NE(IGNORE_NESTED_SCOPES, option);
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
......@@ -110,58 +111,52 @@ void ScopeIterator::TryParseAndRetrieveScopes(ScopeIterator::Option option) {
ignore_nested_scopes = location.IsReturn();
}
if (ignore_nested_scopes) {
if (scope_info->HasContext()) {
context_ = Handle<Context>(context_->declaration_context(), isolate_);
} else {
context_ = handle(function_->context(), isolate_);
}
if (scope_info->scope_type() == FUNCTION_SCOPE) {
nested_scope_chain_.emplace_back(scope_info, shared_info->StartPosition(),
shared_info->EndPosition());
}
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) {
if (scope_info->scope_type() == FUNCTION_SCOPE) {
// Inner function.
info_ = new ParseInfo(isolate_, shared_info);
} else {
// Global or eval code.
Handle<Script> script(Script::cast(shared_info->script()));
info.reset(new ParseInfo(isolate_, script));
info_ = new ParseInfo(isolate_, 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(), isolate_));
info_->set_eval();
if (!context_->IsNativeContext()) {
info_->set_outer_scope_info(handle(context_->scope_info(), isolate_));
}
// Language mode may be inherited from the eval caller.
// Retrieve it from shared function info.
info->set_language_mode(shared_info->language_mode());
info_->set_language_mode(shared_info->language_mode());
} else if (scope_info->scope_type() == MODULE_SCOPE) {
DCHECK(info->is_module());
DCHECK(info_->is_module());
} else {
DCHECK(scope_info->scope_type() == SCRIPT_SCOPE);
DCHECK_EQ(SCRIPT_SCOPE, scope_info->scope_type());
}
} else {
// Inner function.
info.reset(new ParseInfo(isolate_, shared_info));
}
if (parsing::ParseAny(info.get(), shared_info, isolate_) &&
Rewriter::Rewrite(info.get())) {
info->ast_value_factory()->Internalize(isolate_);
DeclarationScope* scope = info->literal()->scope();
if (!ignore_nested_scopes || collect_non_locals) {
CollectNonLocals(info.get(), scope);
}
if (parsing::ParseAny(info_, shared_info, isolate_) &&
Rewriter::Rewrite(info_)) {
info_->ast_value_factory()->Internalize(isolate_);
closure_scope_ = info_->literal()->scope();
if (option == COLLECT_NON_LOCALS) {
DCHECK(non_locals_.is_null());
non_locals_ = info_->literal()->scope()->CollectNonLocals(
info_, StringSet::New(isolate_));
}
if (!ignore_nested_scopes) {
if (DeclarationScope::Analyze(info.get())) {
DeclarationScope::AllocateScopeInfos(info.get(), isolate_,
AnalyzeMode::kDebugger);
RetrieveScopeChain(scope);
CHECK(DeclarationScope::Analyze(info_));
if (ignore_nested_scopes) {
current_scope_ = closure_scope_;
start_scope_ = current_scope_;
if (closure_scope_->NeedsContext()) {
context_ = handle(context_->closure_context(), isolate_);
}
} else {
RetrieveScopeChain(closure_scope_);
}
UnwrapEvaluationContext();
} 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
......@@ -174,32 +169,30 @@ void ScopeIterator::TryParseAndRetrieveScopes(ScopeIterator::Option option) {
isolate_->clear_pending_exception();
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 (!context_->IsDebugEvaluateContext()) return;
Context* current = *context_;
do {
Object* wrapped = current->get(Context::WRAPPED_CONTEXT_INDEX);
if (wrapped->IsContext()) {
context_ = Handle<Context>::cast(wrapped);
current = Context::cast(wrapped);
} else {
context_ = Handle<Context>(context_->previous(), isolate_);
DCHECK_NOT_NULL(current->previous());
current = current->previous();
}
}
} while (current->IsDebugEvaluateContext());
context_ = handle(current, isolate_);
}
V8_WARN_UNUSED_RESULT MaybeHandle<JSObject>
ScopeIterator::MaterializeScopeDetails() {
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;
ASSIGN_RETURN_ON_EXCEPTION(isolate_, scope_object, ScopeObject(), JSObject);
Handle<JSObject> scope_object = ScopeObject(Mode::ALL);
details->set(kScopeDetailsObjectIndex, *scope_object);
if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript) {
return isolate_->factory()->NewJSArrayWithElements(details);
......@@ -209,7 +202,7 @@ ScopeIterator::MaterializeScopeDetails() {
details->set(kScopeDetailsStartPositionIndex,
Smi::FromInt(start_position()));
details->set(kScopeDetailsEndPositionIndex, Smi::FromInt(end_position()));
if (HasNestedScopeChain()) {
if (InInnerScope()) {
details->set(kScopeDetailsFunctionIndex, *function_);
}
}
......@@ -217,87 +210,115 @@ ScopeIterator::MaterializeScopeDetails() {
}
bool ScopeIterator::HasPositionInfo() {
return HasNestedScopeChain() || !context_->IsNativeContext();
return InInnerScope() || !context_->IsNativeContext();
}
int ScopeIterator::start_position() {
if (HasNestedScopeChain()) {
return LastNestedScopeChain().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 (HasNestedScopeChain()) {
return LastNestedScopeChain().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) {
declares_local = true;
return true;
};
VisitScope(visitor, mode);
return declares_local;
}
bool ScopeIterator::HasContext() const {
return !InInnerScope() || current_scope_->NeedsContext();
}
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) {
DCHECK(Done());
return;
}
bool inner = InInnerScope();
if (current_scope_ == closure_scope_) function_ = Handle<JSFunction>();
if (scope_type == ScopeTypeScript) {
DCHECK_IMPLIES(InInnerScope(), current_scope_->is_script_scope());
seen_script_scope_ = true;
if (context_->IsScriptContext()) {
context_ = Handle<Context>(context_->previous(), isolate_);
context_ = handle(context_->previous(), isolate_);
}
if (HasNestedScopeChain()) {
DCHECK_EQ(LastNestedScopeChain().scope_info->scope_type(), SCRIPT_SCOPE);
nested_scope_chain_.pop_back();
DCHECK(!HasNestedScopeChain());
}
CHECK(context_->IsNativeContext());
} else if (!HasNestedScopeChain()) {
context_ = Handle<Context>(context_->previous(), isolate_);
} else if (!inner) {
DCHECK(!context_->IsNativeContext());
context_ = handle(context_->previous(), isolate_);
} else {
DCHECK_NOT_NULL(current_scope_);
do {
if (LastNestedScopeChain().scope_info->HasContext()) {
DCHECK(context_->previous() != nullptr);
context_ = Handle<Context>(context_->previous(), isolate_);
if (current_scope_->NeedsContext()) {
DCHECK_NOT_NULL(context_->previous());
context_ = handle(context_->previous(), isolate_);
}
nested_scope_chain_.pop_back();
if (!HasNestedScopeChain()) break;
DCHECK_IMPLIES(InInnerScope(), current_scope_->outer_scope() != nullptr);
current_scope_ = current_scope_->outer_scope();
// Repeat to skip hidden scopes.
} while (LastNestedScopeChain().is_hidden());
} while (current_scope_->is_hidden());
}
if (!HasNestedScopeChain()) function_ = Handle<JSFunction>();
UnwrapEvaluationContext();
}
// Return the type of the current scope.
ScopeIterator::ScopeType ScopeIterator::Type() {
ScopeIterator::ScopeType ScopeIterator::Type() const {
DCHECK(!Done());
if (HasNestedScopeChain()) {
Handle<ScopeInfo> scope_info = LastNestedScopeChain().scope_info;
switch (scope_info->scope_type()) {
if (InInnerScope()) {
switch (current_scope_->scope_type()) {
case FUNCTION_SCOPE:
DCHECK(context_->IsFunctionContext() || !scope_info->HasContext());
DCHECK_IMPLIES(current_scope_->NeedsContext(),
context_->IsFunctionContext());
return ScopeTypeLocal;
case MODULE_SCOPE:
DCHECK(context_->IsModuleContext());
DCHECK_IMPLIES(current_scope_->NeedsContext(),
context_->IsModuleContext());
return ScopeTypeModule;
case SCRIPT_SCOPE:
DCHECK(context_->IsScriptContext() || context_->IsNativeContext());
DCHECK_IMPLIES(
current_scope_->NeedsContext(),
context_->IsScriptContext() || context_->IsNativeContext());
return ScopeTypeScript;
case WITH_SCOPE:
DCHECK(context_->IsWithContext() || context_->IsDebugEvaluateContext());
DCHECK_IMPLIES(
current_scope_->NeedsContext(),
context_->IsWithContext() || context_->IsDebugEvaluateContext());
return ScopeTypeWith;
case CATCH_SCOPE:
DCHECK(context_->IsCatchContext());
return ScopeTypeCatch;
case BLOCK_SCOPE:
DCHECK(!scope_info->HasContext() || context_->IsBlockContext());
DCHECK_IMPLIES(current_scope_->NeedsContext(),
context_->IsBlockContext());
return ScopeTypeBlock;
case EVAL_SCOPE:
DCHECK(!scope_info->HasContext() || context_->IsEvalContext());
DCHECK_IMPLIES(current_scope_->NeedsContext(),
context_->IsEvalContext());
return ScopeTypeEval;
}
UNREACHABLE();
......@@ -327,94 +348,91 @@ ScopeIterator::ScopeType ScopeIterator::Type() {
return ScopeTypeWith;
}
MaybeHandle<JSObject> ScopeIterator::ScopeObject() {
Handle<JSObject> ScopeIterator::ScopeObject(Mode mode) {
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_EQ(1, nested_scope_chain_.size());
return MaterializeLocalScope();
case ScopeIterator::ScopeTypeWith:
return WithContextExtension();
case ScopeIterator::ScopeTypeClosure:
// Materialize the content of the closure scope into a JSObject.
return MaterializeClosure();
case ScopeIterator::ScopeTypeCatch:
case ScopeIterator::ScopeTypeBlock:
case ScopeIterator::ScopeTypeEval:
return MaterializeInnerScope();
case ScopeIterator::ScopeTypeModule:
return MaterializeModuleScope();
}
UNREACHABLE();
}
bool ScopeIterator::HasContext() {
ScopeType type = Type();
if (type == ScopeTypeBlock || type == ScopeTypeLocal ||
type == ScopeTypeEval) {
if (HasNestedScopeChain()) {
return LastNestedScopeChain().scope_info->HasContext();
}
if (type == ScopeTypeGlobal) {
DCHECK_EQ(Mode::ALL, mode);
return handle(context_->global_proxy(), isolate_);
}
if (type == ScopeTypeWith) {
DCHECK_EQ(Mode::ALL, mode);
return WithContextExtension();
}
return true;
}
Handle<JSObject> scope = isolate_->factory()->NewJSObjectWithNullProto();
auto visitor = [=](Handle<String> name, Handle<Object> value) {
JSObject::AddProperty(scope, name, value, NONE);
return false;
};
bool ScopeIterator::SetVariableValue(Handle<String> variable_name,
Handle<Object> new_value) {
DCHECK(!Done());
VisitScope(visitor, mode);
return scope;
}
void ScopeIterator::VisitScope(const Visitor& visitor, Mode mode) const {
switch (Type()) {
case ScopeIterator::ScopeTypeGlobal:
break;
case ScopeIterator::ScopeTypeLocal:
return SetLocalVariableValue(variable_name, new_value);
case ScopeIterator::ScopeTypeWith:
break;
case ScopeIterator::ScopeTypeClosure:
return SetClosureVariableValue(variable_name, new_value);
case ScopeIterator::ScopeTypeScript:
return SetScriptVariableValue(variable_name, new_value);
case ScopeIterator::ScopeTypeCatch:
case ScopeIterator::ScopeTypeBlock:
case ScopeIterator::ScopeTypeEval:
return SetInnerScopeVariableValue(variable_name, new_value);
case ScopeIterator::ScopeTypeModule:
return SetModuleVariableValue(variable_name, new_value);
break;
case ScopeTypeLocal:
case ScopeTypeClosure:
case ScopeTypeCatch:
case ScopeTypeBlock:
case ScopeTypeEval:
return VisitLocalScope(visitor, mode);
case ScopeTypeModule:
if (InInnerScope()) {
return VisitLocalScope(visitor, mode);
}
DCHECK_EQ(Mode::ALL, mode);
return VisitModuleScope(visitor);
case ScopeTypeScript:
DCHECK_EQ(Mode::ALL, mode);
return VisitScriptScope(visitor);
case ScopeTypeWith:
case ScopeTypeGlobal:
UNREACHABLE();
}
return false;
}
Handle<ScopeInfo> ScopeIterator::CurrentScopeInfo() {
bool ScopeIterator::SetVariableValue(Handle<String> name,
Handle<Object> value) {
DCHECK(!Done());
if (HasNestedScopeChain()) {
return LastNestedScopeChain().scope_info;
} else if (context_->IsBlockContext() || context_->IsFunctionContext() ||
context_->IsEvalContext() || context_->IsCatchContext()) {
return Handle<ScopeInfo>(context_->scope_info());
}
return Handle<ScopeInfo>::null();
}
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 (!current_scope_->NeedsContext()) 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);
Handle<Context> ScopeIterator::CurrentContext() {
DCHECK(!Done());
if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript ||
!HasNestedScopeChain()) {
return context_;
} else if (LastNestedScopeChain().scope_info->HasContext()) {
return context_;
} else {
return Handle<Context>::null();
case ScopeTypeScript:
return SetScriptVariableValue(name, value);
}
return false;
}
Handle<StringSet> ScopeIterator::GetNonLocals() { return non_locals_; }
......@@ -427,19 +445,17 @@ void ScopeIterator::DebugPrint() {
switch (Type()) {
case ScopeIterator::ScopeTypeGlobal:
os << "Global:\n";
CurrentContext()->Print(os);
context_->Print(os);
break;
case ScopeIterator::ScopeTypeLocal: {
os << "Local:\n";
function_->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);
}
if (current_scope_->NeedsContext()) {
context_->Print(os);
if (context_->has_extension()) {
Handle<HeapObject> extension(context_->extension(), isolate_);
DCHECK(extension->IsJSContextExtensionObject());
extension->Print(os);
}
}
break;
......@@ -447,30 +463,28 @@ void ScopeIterator::DebugPrint() {
case ScopeIterator::ScopeTypeWith:
os << "With:\n";
CurrentContext()->extension()->Print(os);
context_->extension()->Print(os);
break;
case ScopeIterator::ScopeTypeCatch:
os << "Catch:\n";
CurrentContext()->extension()->Print(os);
CurrentContext()->get(Context::THROWN_OBJECT_INDEX)->Print(os);
context_->extension()->Print(os);
context_->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);
}
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";
CurrentContext()
->global_object()
context_->global_object()
->native_context()
->script_context_table()
->Print(os);
......@@ -494,380 +508,388 @@ int ScopeIterator::GetSourcePosition() {
void ScopeIterator::RetrieveScopeChain(DeclarationScope* scope) {
DCHECK_NOT_NULL(scope);
GetNestedScopeChain(isolate_, scope, GetSourcePosition());
}
void ScopeIterator::CollectNonLocals(ParseInfo* info, DeclarationScope* scope) {
DCHECK_NOT_NULL(scope);
DCHECK(non_locals_.is_null());
non_locals_ = scope->CollectNonLocals(info, StringSet::New(isolate_));
}
const int position = GetSourcePosition();
Scope* parent = nullptr;
Scope* current = scope;
while (parent != current) {
parent = current;
for (Scope* inner_scope = current->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) {
// Don't walk into inner functions.
if (!inner_scope->is_function_scope()) {
current = inner_scope;
}
break;
}
}
}
start_scope_ = current;
current_scope_ = current;
}
MaybeHandle<JSObject> ScopeIterator::MaterializeScriptScope() {
Handle<JSGlobalObject> global(CurrentContext()->global_object());
void ScopeIterator::VisitScriptScope(const Visitor& visitor) const {
Handle<JSGlobalObject> global(context_->global_object(), isolate_);
Handle<ScriptContextTable> script_contexts(
global->native_context()->script_context_table());
Handle<JSObject> script_scope =
isolate_->factory()->NewJSObjectWithNullProto();
global->native_context()->script_context_table(), isolate_);
for (int context_index = 0; context_index < script_contexts->used();
// Skip the first script since that just declares 'this'.
for (int context_index = 1; 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);
if (VisitContextLocals(visitor, scope_info, context)) return;
}
return script_scope;
}
void ScopeIterator::MaterializeStackLocals(Handle<JSObject> local_scope,
Handle<ScopeInfo> scope_info) {
if (frame_inspector_) {
return frame_inspector_->MaterializeStackLocals(local_scope, scope_info);
}
DCHECK(!generator_.is_null());
// Fill all stack locals.
Handle<FixedArray> parameters_and_registers(
generator_->parameters_and_registers());
int parameter_count = scope_info->ParameterCount();
for (int i = 0; i < parameter_count; ++i) {
// Do not materialize the parameter if it is shadowed by a context local.
// TODO(yangguo): check whether this is necessary, now that we materialize
// context locals as well.
Handle<String> name(scope_info->ParameterName(i));
if (ScopeInfo::VariableIsSynthetic(*name)) continue;
void ScopeIterator::VisitModuleScope(const Visitor& visitor) const {
DCHECK(context_->IsModuleContext());
// Skip the parameter if it is context-allocated.
VariableMode mode;
InitializationFlag init_flag;
MaybeAssignedFlag maybe_assigned_flag;
if (ScopeInfo::ContextSlotIndex(scope_info, name, &mode, &init_flag,
&maybe_assigned_flag) != -1) {
continue;
}
Handle<ScopeInfo> scope_info(context_->scope_info());
if (VisitContextLocals(visitor, scope_info, context_)) return;
int count_index = scope_info->ModuleVariableCountIndex();
int module_variable_count = Smi::cast(scope_info->get(count_index))->value();
Handle<Object> value(parameters_and_registers->get(i), isolate_);
DCHECK(!value.is_identical_to(isolate_->factory()->stale_register()));
Handle<Module> module(context_->module(), isolate_);
// TODO(yangguo): We convert optimized out values to {undefined} when they
// are passed to the debugger. Eventually we should handle them somehow.
if (value->IsTheHole(isolate_) || value->IsOptimizedOut(isolate_)) {
value = isolate_->factory()->undefined_value();
for (int i = 0; i < module_variable_count; ++i) {
int index;
Handle<String> name;
{
String* raw_name;
scope_info->ModuleVariable(i, &raw_name, &index);
CHECK(!ScopeInfo::VariableIsSynthetic(raw_name));
name = handle(raw_name, isolate_);
}
Handle<Object> value = Module::LoadVariable(module, index);
JSObject::SetOwnPropertyIgnoreAttributes(local_scope, name, value, NONE)
.Check();
// Reflect variables under TDZ as undeclared in scope object.
if (value->IsTheHole(isolate_)) continue;
if (visitor(name, value)) return;
}
}
for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
Handle<String> name = handle(scope_info->StackLocalName(i), isolate_);
bool ScopeIterator::VisitContextLocals(const Visitor& visitor,
Handle<ScopeInfo> scope_info,
Handle<Context> context) 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));
if (ScopeInfo::VariableIsSynthetic(*name)) continue;
Handle<Object> value(parameters_and_registers->get(
parameter_count + scope_info->StackLocalIndex(i)),
isolate_);
DCHECK(!value.is_identical_to(isolate_->factory()->stale_register()));
// TODO(yangguo): We convert optimized out values to {undefined} when they
// are passed to the debugger. Eventually we should handle them somehow.
if (value->IsTheHole(isolate_) || value->IsOptimizedOut(isolate_)) {
value = isolate_->factory()->undefined_value();
}
JSObject::SetOwnPropertyIgnoreAttributes(local_scope, name, value, NONE)
.Check();
int context_index = Context::MIN_CONTEXT_SLOTS + i;
Handle<Object> value(context->get(context_index), isolate_);
// Reflect variables under TDZ as undefined in scope object.
if (value->IsTheHole(isolate_)) continue;
if (visitor(name, value)) return true;
}
return false;
}
MaybeHandle<JSObject> ScopeIterator::MaterializeLocalScope() {
DCHECK(HasNestedScopeChain());
Handle<SharedFunctionInfo> shared(function_->shared());
Handle<ScopeInfo> scope_info(shared->scope_info());
Handle<JSObject> local_scope =
isolate_->factory()->NewJSObjectWithNullProto();
MaterializeStackLocals(local_scope, scope_info);
bool ScopeIterator::VisitLocals(const Visitor& visitor, Mode mode) const {
for (Variable* var : *current_scope_->locals()) {
if (!var->is_this() && ScopeInfo::VariableIsSynthetic(*var->name())) {
continue;
}
if (!scope_info->HasContext()) return local_scope;
int index = var->index();
Handle<Object> value;
switch (var->location()) {
case VariableLocation::LOOKUP:
UNREACHABLE();
break;
case VariableLocation::UNALLOCATED:
if (!var->is_this()) continue;
// No idea why we only add it sometimes.
if (mode == Mode::ALL) continue;
// No idea why this diverges...
value = frame_inspector_->GetReceiver();
break;
case VariableLocation::PARAMETER: {
if (frame_inspector_ == nullptr) {
// Get the variable from the suspended generator.
DCHECK(!generator_.is_null());
if (var->is_this()) {
value = handle(generator_->receiver(), isolate_);
} else {
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 = var->is_this() ? frame_inspector_->GetReceiver()
: frame_inspector_->GetParameter(index);
if (value->IsOptimizedOut(isolate_)) {
value = isolate_->factory()->undefined_value();
} else if (var->is_this() && value->IsTheHole(isolate_)) {
value = isolate_->factory()->undefined_value();
}
}
break;
}
// Fill all context locals.
Handle<Context> function_context(context_->closure_context());
DCHECK_EQ(context_->scope_info(), *scope_info);
CopyContextLocalsToScopeObject(scope_info, function_context, local_scope);
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_);
if (value->IsTheHole(isolate_)) {
value = isolate_->factory()->undefined_value();
}
} 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();
} else if (value->IsTheHole(isolate_)) {
// Reflect variables under TDZ as undeclared in scope object.
continue;
}
}
break;
case VariableLocation::CONTEXT:
if (mode == Mode::STACK) continue;
// TODO(verwaest): Why don't we want to show it if it's there?...
if (var->is_this()) continue;
DCHECK(var->IsContextSlot());
value = handle(context_->get(index), isolate_);
// Reflect variables under TDZ as undeclared in scope object.
if (value->IsTheHole(isolate_)) continue;
break;
case VariableLocation::MODULE: {
if (mode == Mode::STACK) continue;
// if (var->IsExport()) continue;
Handle<Module> module(context_->module(), isolate_);
value = Module::LoadVariable(module, var->index());
// Reflect variables under TDZ as undeclared in scope object.
if (value->IsTheHole(isolate_)) continue;
break;
}
}
// Finally copy any properties from the function context extension.
// These will be variables introduced by eval.
if (!function_context->IsNativeContext()) {
CopyContextExtensionToScopeObject(function_context, local_scope,
KeyCollectionMode::kIncludePrototypes);
if (visitor(var->name(), value)) return true;
}
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<ScopeInfo> scope_info(context_->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;
return false;
}
// 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()) {
DCHECK(context_->IsWithContext());
if (context_->extension_receiver()->IsJSProxy()) {
return isolate_->factory()->NewJSObjectWithNullProto();
}
return handle(JSObject::cast(context->extension_receiver()), isolate_);
return handle(JSObject::cast(context_->extension_receiver()), isolate_);
}
// 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 (HasNestedScopeChain()) {
Handle<ScopeInfo> scope_info = LastNestedScopeChain().scope_info;
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,
Handle<String> parameter_name,
Handle<Object> new_value) {
// Setting stack locals of optimized frames is not supported.
HandleScope scope(isolate_);
for (int i = 0; i < scope_info->ParameterCount(); ++i) {
if (String::Equals(handle(scope_info->ParameterName(i), isolate_),
parameter_name)) {
// Suspended generators should not get here because all parameters should
// be context-allocated.
DCHECK_NOT_NULL(frame_inspector_);
JavaScriptFrame* frame = GetFrame();
if (frame->is_optimized()) {
return false;
void ScopeIterator::VisitLocalScope(const Visitor& visitor, Mode mode) const {
if (InInnerScope()) {
if (VisitLocals(visitor, mode)) 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() &&
!non_locals_->Has(isolate_->factory()->this_string())) {
if (visitor(isolate_->factory()->this_string(),
isolate_->factory()->undefined_value()))
return;
}
frame->SetParameterValue(i, *new_value);
return true;
}
}
return false;
}
bool ScopeIterator::SetStackVariableValue(Handle<ScopeInfo> scope_info,
Handle<String> variable_name,
Handle<Object> new_value) {
// Setting stack locals of optimized frames is not supported. Suspended
// generators are supported.
HandleScope scope(isolate_);
for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
if (String::Equals(handle(scope_info->StackLocalName(i), isolate_),
variable_name)) {
int stack_local_index = scope_info->StackLocalIndex(i);
if (frame_inspector_ != nullptr) {
// Set the variable on the stack.
// 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();
if (frame->is_optimized()) return false;
frame->SetExpression(stack_local_index, *new_value);
} else {
// Set the variable in the suspended generator.
DCHECK(!generator_.is_null());
Handle<FixedArray> parameters_and_registers(
generator_->parameters_and_registers());
int index_in_parameters_and_registers =
scope_info->ParameterCount() + stack_local_index;
parameters_and_registers->set(index_in_parameters_and_registers,
*new_value);
Handle<JSObject> arguments = Accessors::FunctionGetArguments(
frame, frame_inspector_->inlined_frame_index());
if (visitor(isolate_->factory()->arguments_string(), arguments)) return;
}
return true;
}
} else {
DCHECK_EQ(Mode::ALL, mode);
Handle<ScopeInfo> scope_info(context_->scope_info(), isolate_);
if (VisitContextLocals(visitor, scope_info, context_)) return;
}
if (mode == Mode::ALL && HasContext()) {
DCHECK(!context_->IsScriptContext());
DCHECK(!context_->IsNativeContext());
DCHECK(!context_->IsWithContext());
if (!context_->scope_info()->CallsSloppyEval()) return;
if (context_->extension_object() == nullptr) 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)));
Handle<Object> value = JSReceiver::GetDataProperty(extension, key);
if (visitor(key, value)) return;
}
}
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;
}
}
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(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 == isolate_->heap()->arguments_string());
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;
}
// TODO(neis): Clean up context "extension" mess.
if (!context->IsModuleContext() && 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;
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<Module> module(context_->module(), isolate_);
Module::StoreVariable(module, var->index(), new_value);
return true;
}
UNREACHABLE();
}
}
return false;
}
bool ScopeIterator::SetLocalVariableValue(Handle<String> variable_name,
Handle<Object> new_value) {
Handle<ScopeInfo> scope_info;
if (HasNestedScopeChain()) {
scope_info = handle(function_->shared()->scope_info(), isolate_);
DCHECK_IMPLIES(scope_info->HasContext(),
context_->scope_info() == *scope_info);
} else {
scope_info = handle(context_->scope_info(), isolate_);
}
bool ScopeIterator::SetContextExtensionValue(Handle<String> variable_name,
Handle<Object> new_value) {
if (!context_->has_extension()) return false;
// Parameter might be shadowed in context. Don't stop here.
bool result = SetParameterValue(scope_info, variable_name, new_value);
DCHECK(context_->extension_object()->IsJSContextExtensionObject());
Handle<JSObject> ext(context_->extension_object(), isolate_);
LookupIterator it(ext, variable_name, LookupIterator::OWN);
Maybe<bool> maybe = JSReceiver::HasOwnProperty(ext, variable_name);
DCHECK(maybe.IsJust());
if (!maybe.FromJust()) return false;
// Stack locals.
if (SetStackVariableValue(scope_info, variable_name, new_value)) {
return true;
}
CHECK(Object::SetDataProperty(&it, new_value).ToChecked());
return true;
}
if (scope_info->HasContext() &&
SetContextVariableValue(scope_info, context_, variable_name, new_value)) {
return true;
}
bool ScopeIterator::SetContextVariableValue(Handle<String> variable_name,
Handle<Object> new_value) {
Handle<ScopeInfo> scope_info(context_->scope_info(), isolate_);
VariableMode mode;
InitializationFlag flag;
MaybeAssignedFlag maybe_assigned_flag;
int slot_index = ScopeInfo::ContextSlotIndex(scope_info, variable_name, &mode,
&flag, &maybe_assigned_flag);
if (slot_index < 0) return false;
return result;
context_->set(slot_index, *new_value);
return true;
}
bool ScopeIterator::SetModuleVariableValue(Handle<String> variable_name,
Handle<Object> new_value) {
DCHECK_NOT_NULL(frame_inspector_);
// Get module context and its scope info.
Handle<Context> context = CurrentContext();
while (!context->IsModuleContext()) {
context = handle(context->previous(), isolate_);
}
Handle<ScopeInfo> scope_info(context->scope_info(), isolate_);
DCHECK_EQ(scope_info->scope_type(), MODULE_SCOPE);
if (SetContextVariableValue(scope_info, context, variable_name, new_value)) {
return true;
}
int cell_index;
{
VariableMode mode;
InitializationFlag init_flag;
MaybeAssignedFlag maybe_assigned_flag;
cell_index = scope_info->ModuleIndex(variable_name, &mode, &init_flag,
&maybe_assigned_flag);
}
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.
bool found = ModuleDescriptor::GetCellIndexKind(cell_index) ==
ModuleDescriptor::kExport;
if (found) {
Module::StoreVariable(handle(context->module(), isolate_), cell_index,
new_value);
if (ModuleDescriptor::GetCellIndexKind(cell_index) !=
ModuleDescriptor::kExport) {
return false;
}
return found;
}
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 ||
scope_info->scope_type() == CATCH_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);
Handle<Module> module(context_->module(), isolate_);
Module::StoreVariable(module, cell_index, new_value);
return true;
}
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());
context_->global_object()->native_context()->script_context_table());
ScriptContextTable::LookupResult lookup_result;
if (ScriptContextTable::Lookup(script_contexts, internalized_variable_name,
if (ScriptContextTable::Lookup(script_contexts, variable_name,
&lookup_result)) {
Handle<Context> script_context = ScriptContextTable::GetContext(
script_contexts, lookup_result.context_index);
......@@ -878,116 +900,5 @@ bool ScopeIterator::SetScriptVariableValue(Handle<String> variable_name,
return false;
}
void ScopeIterator::CopyContextLocalsToScopeObject(
Handle<ScopeInfo> scope_info, Handle<Context> context,
Handle<JSObject> scope_object) {
Isolate* isolate = isolate_;
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 = isolate_;
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 = GetFunction();
if (scope->start_position() > function->shared()->StartPosition() &&
scope->end_position() <= function->shared()->EndPosition()) {
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_.emplace_back(scope->scope_info());
} else {
nested_scope_chain_.emplace_back(
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;
}
}
}
bool ScopeIterator::HasNestedScopeChain() const {
return !nested_scope_chain_.empty();
}
ScopeIterator::ExtendedScopeInfo& ScopeIterator::LastNestedScopeChain() {
DCHECK(HasNestedScopeChain());
return nested_scope_chain_.back();
}
} // namespace internal
} // namespace v8
......@@ -48,32 +48,35 @@ class ScopeIterator {
ScopeIterator(Isolate* isolate, Handle<JSFunction> function);
ScopeIterator(Isolate* isolate, Handle<JSGeneratorObject> generator);
~ScopeIterator();
V8_WARN_UNUSED_RESULT MaybeHandle<JSObject> MaterializeScopeDetails();
Handle<JSObject> MaterializeScopeDetails();
// More scopes?
bool Done() { return context_.is_null(); }
bool Done() const { return context_.is_null(); }
// Move to the next scope.
void Next();
// Restart to the first scope and context.
void Restart();
// Return the type of the current scope.
ScopeType Type();
ScopeType Type() const;
// Indicates which variables should be visited. Either only variables from the
// scope that are available on the stack, or all variables.
enum class Mode { STACK, ALL };
// Return the JavaScript object with the content of the current scope.
MaybeHandle<JSObject> ScopeObject();
Handle<JSObject> ScopeObject(Mode mode);
bool HasContext();
// Returns whether the current scope declares any variables.
bool DeclaresLocals(Mode mode) const;
// Set variable value and return true on success.
bool SetVariableValue(Handle<String> variable_name, Handle<Object> new_value);
Handle<ScopeInfo> CurrentScopeInfo();
// Return the context for this scope. For the local context there might not
// be an actual context.
Handle<Context> CurrentContext();
// Populate the set with collected non-local variable names.
Handle<StringSet> GetNonLocals();
......@@ -94,96 +97,63 @@ class ScopeIterator {
void DebugPrint();
#endif
private:
struct ExtendedScopeInfo {
ExtendedScopeInfo(Handle<ScopeInfo> info, int start, int end)
: scope_info(info), start_position(start), end_position(end) {}
explicit ExtendedScopeInfo(Handle<ScopeInfo> info)
: scope_info(info), start_position(-1), end_position(-1) {}
Handle<ScopeInfo> scope_info;
int start_position;
int end_position;
bool is_hidden() { return start_position == -1 && end_position == -1; }
};
bool InInnerScope() const { return !function_.is_null(); }
bool HasContext() const;
Handle<Context> CurrentContext() const {
DCHECK(HasContext());
return context_;
}
private:
Isolate* isolate_;
ParseInfo* info_ = nullptr;
FrameInspector* const frame_inspector_ = nullptr;
Handle<JSGeneratorObject> generator_;
Handle<JSFunction> function_;
Handle<ScopeInfo> function_scope_info_;
Handle<Context> context_;
Handle<Script> script_;
std::vector<ExtendedScopeInfo> nested_scope_chain_;
Handle<StringSet> non_locals_;
bool seen_script_scope_;
DeclarationScope* closure_scope_ = nullptr;
Scope* start_scope_ = nullptr;
Scope* current_scope_ = nullptr;
bool seen_script_scope_ = false;
inline JavaScriptFrame* GetFrame() {
return frame_inspector_->GetArgumentsFrame();
inline JavaScriptFrame* GetFrame() const {
return frame_inspector_->javascript_frame();
}
Handle<Context> GetContext();
int GetSourcePosition();
void MaterializeStackLocals(Handle<JSObject> local_scope,
Handle<ScopeInfo> scope_info);
void TryParseAndRetrieveScopes(ScopeIterator::Option option);
void RetrieveScopeChain(DeclarationScope* scope);
void CollectNonLocals(ParseInfo* info, DeclarationScope* scope);
void UnwrapEvaluationContext();
V8_WARN_UNUSED_RESULT MaybeHandle<JSObject> MaterializeScriptScope();
V8_WARN_UNUSED_RESULT MaybeHandle<JSObject> MaterializeLocalScope();
V8_WARN_UNUSED_RESULT MaybeHandle<JSObject> MaterializeModuleScope();
Handle<JSObject> MaterializeClosure();
Handle<JSObject> MaterializeInnerScope();
typedef std::function<bool(Handle<String> name, Handle<Object> value)>
Visitor;
Handle<JSObject> WithContextExtension();
bool SetLocalVariableValue(Handle<String> variable_name,
Handle<Object> new_value);
bool SetInnerScopeVariableValue(Handle<String> variable_name,
Handle<Object> new_value);
bool SetClosureVariableValue(Handle<String> variable_name,
bool SetContextVariableValue(Handle<String> variable_name,
Handle<Object> new_value);
bool SetContextExtensionValue(Handle<String> variable_name,
Handle<Object> new_value);
bool SetScriptVariableValue(Handle<String> variable_name,
Handle<Object> new_value);
bool SetModuleVariableValue(Handle<String> variable_name,
Handle<Object> new_value);
// Helper functions.
bool SetParameterValue(Handle<ScopeInfo> scope_info,
Handle<String> parameter_name,
Handle<Object> new_value);
bool SetStackVariableValue(Handle<ScopeInfo> scope_info,
Handle<String> variable_name,
Handle<Object> new_value);
bool SetContextVariableValue(Handle<ScopeInfo> scope_info,
Handle<Context> context,
Handle<String> variable_name,
Handle<Object> new_value);
void CopyContextLocalsToScopeObject(Handle<ScopeInfo> scope_info,
Handle<Context> context,
Handle<JSObject> scope_object);
void CopyModuleVarsToScopeObject(Handle<ScopeInfo> scope_info,
Handle<Context> context,
Handle<JSObject> scope_object);
void CopyContextExtensionToScopeObject(Handle<Context> context,
Handle<JSObject> scope_object,
KeyCollectionMode mode);
// Get the chain of nested scopes within this scope for the source statement
// position. The scopes will be added to the list from the outermost scope to
// the innermost scope. Only nested block, catch or with scopes are tracked
// and will be returned, but no inner function scopes.
void GetNestedScopeChain(Isolate* isolate, Scope* scope,
int statement_position);
bool HasNestedScopeChain() const;
ExtendedScopeInfo& LastNestedScopeChain();
void VisitScope(const Visitor& visitor, Mode mode) const;
void VisitLocalScope(const Visitor& visitor, Mode mode) const;
void VisitScriptScope(const Visitor& visitor) const;
void VisitModuleScope(const Visitor& visitor) const;
bool VisitLocals(const Visitor& visitor, Mode mode) const;
bool VisitContextLocals(const Visitor& visitor, Handle<ScopeInfo> scope_info,
Handle<Context> context) const;
DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
};
......
......@@ -2198,52 +2198,28 @@ Handle<Object> GetValueForDebugger(TranslatedFrame::iterator it,
DeoptimizedFrameInfo::DeoptimizedFrameInfo(TranslatedState* state,
TranslatedState::iterator frame_it,
Isolate* isolate) {
// If the previous frame is an adaptor frame, we will take the parameters
// from there.
TranslatedState::iterator parameter_frame = frame_it;
if (parameter_frame != state->begin()) {
parameter_frame--;
}
int parameter_count;
if (parameter_frame->kind() == TranslatedFrame::kArgumentsAdaptor) {
parameter_count = parameter_frame->height() - 1; // Ignore the receiver.
} else {
parameter_frame = frame_it;
parameter_count =
frame_it->shared_info()->internal_formal_parameter_count();
}
TranslatedFrame::iterator parameter_it = parameter_frame->begin();
parameter_it++; // Skip the function.
parameter_it++; // Skip the receiver.
int parameter_count =
frame_it->shared_info()->internal_formal_parameter_count();
TranslatedFrame::iterator stack_it = frame_it->begin();
// Figure out whether there is a construct stub frame on top of
// the parameter frame.
has_construct_stub_ =
parameter_frame != state->begin() &&
(parameter_frame - 1)->kind() == TranslatedFrame::kConstructStub;
// Get the function. Note that this might materialize the function.
// In case the debugger mutates this value, we should deoptimize
// the function and remember the value in the materialized value store.
function_ = Handle<JSFunction>::cast(stack_it->GetValue());
stack_it++; // Skip the function.
stack_it++; // Skip the receiver.
DCHECK_EQ(TranslatedFrame::kInterpretedFunction, frame_it->kind());
source_position_ = Deoptimizer::ComputeSourcePositionFromBytecodeArray(
*frame_it->shared_info(), frame_it->node_id());
TranslatedFrame::iterator value_it = frame_it->begin();
// Get the function. Note that this might materialize the function.
// In case the debugger mutates this value, we should deoptimize
// the function and remember the value in the materialized value store.
function_ = Handle<JSFunction>::cast(value_it->GetValue());
DCHECK_EQ(parameter_count,
function_->shared()->internal_formal_parameter_count());
parameters_.resize(static_cast<size_t>(parameter_count));
for (int i = 0; i < parameter_count; i++) {
Handle<Object> parameter = GetValueForDebugger(parameter_it, isolate);
Handle<Object> parameter = GetValueForDebugger(stack_it, isolate);
SetParameter(i, parameter);
parameter_it++;
}
// Skip the function, the receiver and the arguments.
int skip_count =
frame_it->shared_info()->internal_formal_parameter_count() + 2;
TranslatedFrame::iterator stack_it = frame_it->begin();
for (int i = 0; i < skip_count; i++) {
stack_it++;
}
......
......@@ -1025,12 +1025,6 @@ class DeoptimizedFrameInfo : public Malloced {
// Get the frame context.
Handle<Object> GetContext() { return context_; }
// Check if this frame is preceded by construct stub frame. The bottom-most
// inlined frame might still be called by an uninlined construct stub.
bool HasConstructStub() {
return has_construct_stub_;
}
// Get an incoming argument.
Handle<Object> GetParameter(int index) {
DCHECK(0 <= index && index < parameters_count());
......@@ -1062,7 +1056,6 @@ class DeoptimizedFrameInfo : public Malloced {
Handle<JSFunction> function_;
Handle<Object> context_;
bool has_construct_stub_;
std::vector<Handle<Object> > parameters_;
std::vector<Handle<Object> > expression_stack_;
int source_position_;
......
......@@ -740,6 +740,7 @@ int ScopeInfo::StackSlotIndex(String* name) const {
int ScopeInfo::ModuleIndex(Handle<String> name, VariableMode* mode,
InitializationFlag* init_flag,
MaybeAssignedFlag* maybe_assigned_flag) {
DCHECK(name->IsInternalizedString());
DCHECK_EQ(scope_type(), MODULE_SCOPE);
DCHECK_NOT_NULL(mode);
DCHECK_NOT_NULL(init_flag);
......
......@@ -344,7 +344,7 @@ RUNTIME_FUNCTION(Runtime_GetGeneratorScopeDetails) {
return isolate->heap()->undefined_value();
}
RETURN_RESULT_OR_FAILURE(isolate, it.MaterializeScopeDetails());
return *it.MaterializeScopeDetails();
}
static bool SetScopeVariableValue(ScopeIterator* it, int index,
......
......@@ -648,8 +648,7 @@ TEST(ScopeUsesArgumentsSuperThis) {
CHECK(i::Rewriter::Rewrite(&info));
info.ast_value_factory()->Internalize(isolate);
CHECK(i::DeclarationScope::Analyze(&info));
i::DeclarationScope::AllocateScopeInfos(&info, isolate,
i::AnalyzeMode::kRegular);
i::DeclarationScope::AllocateScopeInfos(&info, isolate);
CHECK_NOT_NULL(info.literal());
i::DeclarationScope* script_scope = info.literal()->scope();
......@@ -10126,8 +10125,7 @@ TEST(LexicalLoopVariable) {
CHECK(i::parsing::ParseProgram(&info, isolate));
CHECK(i::Rewriter::Rewrite(&info));
CHECK(i::DeclarationScope::Analyze(&info));
i::DeclarationScope::AllocateScopeInfos(&info, isolate,
i::AnalyzeMode::kRegular);
i::DeclarationScope::AllocateScopeInfos(&info, isolate);
CHECK_NOT_NULL(info.literal());
i::DeclarationScope* script_scope = info.literal()->scope();
......
......@@ -142,7 +142,7 @@ listener_delegate = function(exec_state) {
{exported_var: undefined, imported_var: undefined},
0, exec_state);
CheckScopeDoesNotHave(
["local_var", "doesntexist", "local_let", "exported_let", "imported_let"],
["doesntexist", "exported_let", "imported_let"],
0, exec_state);
};
debugger;
......@@ -163,7 +163,7 @@ listener_delegate = function(exec_state) {
CheckScopeContent(
{exported_let: 3, exported_var: 4,
imported_let: 3, imported_var: 4}, 0, exec_state);
CheckScopeDoesNotHave(["local_var", "local_let"], 0, exec_state);
CheckScopeDoesNotHave([], 0, exec_state);
};
debugger;
EndTest();
......@@ -181,7 +181,7 @@ listener_delegate = function(exec_state) {
CheckScopeContent(
{exported_let: 13, exported_var: 14,
imported_let: 13, imported_var: 14}, 0, exec_state);
CheckScopeDoesNotHave(["local_var", "local_let"], 0, exec_state);
CheckScopeDoesNotHave([], 0, exec_state);
};
debugger;
EndTest();
......@@ -4,7 +4,7 @@ Running test: testTotal
foo1 (module1:7:2)
foo2 (module2:6:9)
(anonymous) (module3:4:0)
local:foo1
local
[
[0] : c1 = 12
[1] : g1 = 2
......@@ -94,7 +94,7 @@ foo1 =
objectId : <objectId>
type : function
}
local:foo2
local
[
[0] : c2 = 22
]
......@@ -180,7 +180,8 @@ foo2 =
module
[
[0] : foo2 = function foo2() { let c2 = 22; return foo1() + a2 + b2 + c2; }
[1] : b3 = 31
[1] : a3 = 30
[2] : b3 = 31
]
global
[
......@@ -205,6 +206,20 @@ foo2 =
objectId : <objectId>
type : function
}
a3 =
{
description : 30
type : number
value : 30
}
Evaluating: ++a3
updated a3 =
{
description : 31
type : number
value : 31
}
Evaluating: --a3
b3 =
{
description : 31
......@@ -224,7 +239,7 @@ Running test: testAnother
(anonymous) (module4:5:13)
bar (module4:5:24)
(anonymous) (module4:7:0)
local
local:bar
[
]
closure:bar
......@@ -295,6 +310,12 @@ updated a =
value : 1
}
Evaluating: --a
module
[
[0] : a = 1
[1] : b = 2
[2] : bar = function bar() { let a = 0; (() => {a; debugger;})(); }
]
global
[
...
......@@ -310,6 +331,41 @@ Array =
objectId : <objectId>
type : function
}
a =
{
description : 1
type : number
value : 1
}
Evaluating: ++a
updated a =
{
description : 2
type : number
value : 2
}
Evaluating: --a
b =
{
description : 2
type : number
value : 2
}
Evaluating: ++b
updated b =
{
description : 3
type : number
value : 3
}
Evaluating: --b
bar =
{
className : Function
description : function bar() { let a = 0; (() => {a; debugger;})(); }
objectId : <objectId>
type : function
}
Running test: testDifferentModuleVariables
(anonymous) (module5:5:0)
......@@ -485,6 +541,10 @@ Evaluating: --x
Running test: testLocalVariableToplevel
(anonymous) (module7:2:0)
module
[
[0] : x = 5
]
global
[
...
......@@ -501,3 +561,17 @@ Array =
objectId : <objectId>
type : function
}
x =
{
description : 5
type : number
value : 5
}
Evaluating: ++x
updated x =
{
description : 6
type : number
value : 6
}
Evaluating: --x
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment