Commit a5b5f8e9 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."

Change-Id: I0ad97057600d0a0f1dd4c71d5f8245dafb908154
Reviewed-on: https://chromium-review.googlesource.com/1103576Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Commit-Queue: Toon Verwaest <verwaest@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53781}
parent a93d30d5
...@@ -2369,21 +2369,8 @@ void Scope::AllocateScopeInfosRecursively(Isolate* isolate, ...@@ -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 // static
void DeclarationScope::AllocateScopeInfos(ParseInfo* info, Isolate* isolate, void DeclarationScope::AllocateScopeInfos(ParseInfo* info, Isolate* isolate) {
AnalyzeMode mode) {
DeclarationScope* scope = info->literal()->scope(); DeclarationScope* scope = info->literal()->scope();
if (!scope->scope_info_.is_null()) return; // Allocated by outer function. if (!scope->scope_info_.is_null()) return; // Allocated by outer function.
...@@ -2393,9 +2380,6 @@ void DeclarationScope::AllocateScopeInfos(ParseInfo* info, Isolate* isolate, ...@@ -2393,9 +2380,6 @@ void DeclarationScope::AllocateScopeInfos(ParseInfo* info, Isolate* isolate,
} }
scope->AllocateScopeInfosRecursively(isolate, outer_scope); 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. // 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 // Since the top-most scope will end up in a shared function info, make sure
......
...@@ -78,8 +78,6 @@ class SloppyBlockFunctionMap : public ZoneHashMap { ...@@ -78,8 +78,6 @@ class SloppyBlockFunctionMap : public ZoneHashMap {
int count_; int count_;
}; };
enum class AnalyzeMode { kRegular, kDebugger };
// Global invariants after AST construction: Each reference (i.e. identifier) // Global invariants after AST construction: Each reference (i.e. identifier)
// to a JavaScript variable (including global properties) is represented by a // to a JavaScript variable (including global properties) is represented by a
// VariableProxy node. Immediately after AST construction and before variable // VariableProxy node. Immediately after AST construction and before variable
...@@ -367,7 +365,8 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) { ...@@ -367,7 +365,8 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
// Whether this needs to be represented by a runtime context. // Whether this needs to be represented by a runtime context.
bool NeedsContext() const { bool NeedsContext() const {
// Catch scopes always have heap slots. // 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; return num_heap_slots() > 0;
} }
...@@ -813,7 +812,7 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope { ...@@ -813,7 +812,7 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
// The local variable 'arguments' if we need to allocate it; nullptr // The local variable 'arguments' if we need to allocate it; nullptr
// otherwise. // otherwise.
Variable* arguments() const { Variable* arguments() const {
DCHECK(!is_arrow_scope() || arguments_ == nullptr); DCHECK_IMPLIES(is_arrow_scope(), arguments_ == nullptr);
return arguments_; return arguments_;
} }
...@@ -868,8 +867,7 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope { ...@@ -868,8 +867,7 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
// Allocate ScopeInfos for top scope and any inner scopes that need them. // Allocate ScopeInfos for top scope and any inner scopes that need them.
// Does nothing if ScopeInfo is already allocated. // Does nothing if ScopeInfo is already allocated.
static void AllocateScopeInfos(ParseInfo* info, Isolate* isolate, static void AllocateScopeInfos(ParseInfo* info, Isolate* isolate);
AnalyzeMode mode);
Handle<StringSet> CollectNonLocals(ParseInfo* info, Handle<StringSet> CollectNonLocals(ParseInfo* info,
Handle<StringSet> non_locals); Handle<StringSet> non_locals);
......
...@@ -288,8 +288,7 @@ void UnoptimizedCompileJob::FinalizeOnMainThread(Isolate* isolate) { ...@@ -288,8 +288,7 @@ void UnoptimizedCompileJob::FinalizeOnMainThread(Isolate* isolate) {
// Internalize ast values onto the heap. // Internalize ast values onto the heap.
parse_info_->ast_value_factory()->Internalize(isolate); parse_info_->ast_value_factory()->Internalize(isolate);
// Allocate scope infos for the literal. // Allocate scope infos for the literal.
DeclarationScope::AllocateScopeInfos(parse_info_.get(), isolate, DeclarationScope::AllocateScopeInfos(parse_info_.get(), isolate);
AnalyzeMode::kRegular);
if (compilation_job_->state() == CompilationJob::State::kFailed || if (compilation_job_->state() == CompilationJob::State::kFailed ||
!Compiler::FinalizeCompilationJob(compilation_job_.release(), shared_, !Compiler::FinalizeCompilationJob(compilation_job_.release(), shared_,
isolate)) { isolate)) {
......
...@@ -509,8 +509,7 @@ bool FinalizeUnoptimizedCode( ...@@ -509,8 +509,7 @@ bool FinalizeUnoptimizedCode(
DCHECK(AllowCompilation::IsAllowed(isolate)); DCHECK(AllowCompilation::IsAllowed(isolate));
// Allocate scope infos for the literal. // Allocate scope infos for the literal.
DeclarationScope::AllocateScopeInfos(parse_info, isolate, DeclarationScope::AllocateScopeInfos(parse_info, isolate);
AnalyzeMode::kRegular);
// Finalize the outer-most function's compilation job. // Finalize the outer-most function's compilation job.
if (FinalizeUnoptimizedCompilationJob(outer_function_job, shared_info, if (FinalizeUnoptimizedCompilationJob(outer_function_job, shared_info,
......
...@@ -159,20 +159,23 @@ MaybeHandle<Object> DebugEvaluate::Evaluate( ...@@ -159,20 +159,23 @@ MaybeHandle<Object> DebugEvaluate::Evaluate(
return result; return result;
} }
Handle<SharedFunctionInfo> DebugEvaluate::ContextBuilder::outer_info() const {
return handle(frame_inspector_.GetFunction()->shared(), isolate_);
}
DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate, DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate,
JavaScriptFrame* frame, JavaScriptFrame* frame,
int inlined_jsframe_index) int inlined_jsframe_index)
: isolate_(isolate), : isolate_(isolate),
frame_(frame), frame_inspector_(frame, inlined_jsframe_index, isolate),
inlined_jsframe_index_(inlined_jsframe_index) { scope_iterator_(isolate, &frame_inspector_,
FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate); ScopeIterator::COLLECT_NON_LOCALS) {
Handle<JSFunction> local_function = frame_inspector.GetFunction(); Handle<Context> outer_context(frame_inspector_.GetFunction()->context());
Handle<Context> outer_context(local_function->context());
evaluation_context_ = outer_context; evaluation_context_ = outer_context;
outer_info_ = handle(local_function->shared(), isolate);
Factory* factory = isolate->factory(); Factory* factory = isolate->factory();
if (scope_iterator_.Done()) return;
// To evaluate as if we were running eval at the point of the debug break, // To evaluate as if we were running eval at the point of the debug break,
// we reconstruct the context chain as follows: // we reconstruct the context chain as follows:
// - To make stack-allocated variables visible, we materialize them and // - To make stack-allocated variables visible, we materialize them and
...@@ -189,64 +192,32 @@ DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate, ...@@ -189,64 +192,32 @@ DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate,
// - Look up in the materialized stack variables. // - Look up in the materialized stack variables.
// - Look up in the original context. // - Look up in the original context.
// - Check the whitelist to find out whether to skip contexts during lookup. // - Check the whitelist to find out whether to skip contexts during lookup.
const ScopeIterator::Option option = ScopeIterator::COLLECT_NON_LOCALS; for (; scope_iterator_.InInnerScope(); scope_iterator_.Next()) {
for (ScopeIterator it(isolate, &frame_inspector, option); !it.Done(); ScopeIterator::ScopeType scope_type = scope_iterator_.Type();
it.Next()) { if (scope_type == ScopeIterator::ScopeTypeScript) break;
ScopeIterator::ScopeType scope_type = it.Type(); 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) { if (scope_type == ScopeIterator::ScopeTypeLocal) {
DCHECK_EQ(FUNCTION_SCOPE, it.CurrentScopeInfo()->scope_type()); context_chain_element.whitelist = scope_iterator_.GetNonLocals();
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_.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(); for (auto rit = context_chain_.rbegin(); rit != context_chain_.rend();
rit++) { rit++) {
ContextChainElement element = *rit; ContextChainElement element = *rit;
Handle<ScopeInfo> scope_info(ScopeInfo::CreateForWithScope( scope_info = ScopeInfo::CreateForWithScope(isolate, scope_info);
isolate, evaluation_context_->IsNativeContext()
? Handle<ScopeInfo>::null()
: Handle<ScopeInfo>(evaluation_context_->scope_info())));
scope_info->SetIsDebugEvaluateScope(); scope_info->SetIsDebugEvaluateScope();
evaluation_context_ = factory->NewDebugEvaluateContext( evaluation_context_ = factory->NewDebugEvaluateContext(
evaluation_context_, scope_info, element.materialized_object, evaluation_context_, scope_info, element.materialized_object,
...@@ -256,62 +227,24 @@ DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate, ...@@ -256,62 +227,24 @@ DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate,
void DebugEvaluate::ContextBuilder::UpdateValues() { void DebugEvaluate::ContextBuilder::UpdateValues() {
scope_iterator_.Restart();
for (ContextChainElement& element : context_chain_) { for (ContextChainElement& element : context_chain_) {
if (!element.materialized_object.is_null()) { if (!element.materialized_object.is_null()) {
// Write back potential changes to materialized stack locals to the stack. Handle<FixedArray> keys =
FrameInspector(frame_, inlined_jsframe_index_, isolate_) KeyAccumulator::GetKeys(element.materialized_object,
.UpdateStackLocalsFromMaterializedObject(element.materialized_object, KeyCollectionMode::kOwnOnly,
element.scope_info); 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);
}
} }
} scope_iterator_.Next();
}
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();
} }
} }
......
...@@ -7,7 +7,8 @@ ...@@ -7,7 +7,8 @@
#include <vector> #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.h"
#include "src/objects/shared-function-info.h" #include "src/objects/shared-function-info.h"
#include "src/objects/string-table.h" #include "src/objects/string-table.h"
...@@ -70,31 +71,20 @@ class DebugEvaluate : public AllStatic { ...@@ -70,31 +71,20 @@ class DebugEvaluate : public AllStatic {
void UpdateValues(); void UpdateValues();
Handle<Context> evaluation_context() const { return evaluation_context_; } Handle<Context> evaluation_context() const { return evaluation_context_; }
Handle<SharedFunctionInfo> outer_info() const { return outer_info_; } Handle<SharedFunctionInfo> outer_info() const;
private: private:
struct ContextChainElement { struct ContextChainElement {
Handle<ScopeInfo> scope_info;
Handle<Context> wrapped_context; Handle<Context> wrapped_context;
Handle<JSObject> materialized_object; Handle<JSObject> materialized_object;
Handle<StringSet> whitelist; 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_; Handle<Context> evaluation_context_;
std::vector<ContextChainElement> context_chain_; std::vector<ContextChainElement> context_chain_;
Isolate* isolate_; Isolate* isolate_;
JavaScriptFrame* frame_; FrameInspector frame_inspector_;
int inlined_jsframe_index_; ScopeIterator scope_iterator_;
}; };
static MaybeHandle<Object> Evaluate(Isolate* isolate, static MaybeHandle<Object> Evaluate(Isolate* isolate,
......
...@@ -15,6 +15,7 @@ namespace internal { ...@@ -15,6 +15,7 @@ namespace internal {
FrameInspector::FrameInspector(StandardFrame* frame, int inlined_frame_index, FrameInspector::FrameInspector(StandardFrame* frame, int inlined_frame_index,
Isolate* isolate) Isolate* isolate)
: frame_(frame), : frame_(frame),
inlined_frame_index_(inlined_frame_index),
isolate_(isolate) { isolate_(isolate) {
// Extract the relevant information from the frame summary and discard it. // Extract the relevant information from the frame summary and discard it.
FrameSummary summary = FrameSummary::Get(frame, inlined_frame_index); FrameSummary summary = FrameSummary::Get(frame, inlined_frame_index);
...@@ -82,97 +83,6 @@ bool FrameInspector::IsWasm() { return frame_->is_wasm(); } ...@@ -82,97 +83,6 @@ bool FrameInspector::IsWasm() { return frame_->is_wasm(); }
bool FrameInspector::IsJavaScript() { return frame_->is_java_script(); } 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( bool FrameInspector::ParameterIsShadowedByContextLocal(
Handle<ScopeInfo> info, Handle<String> parameter_name) { Handle<ScopeInfo> info, Handle<String> parameter_name) {
VariableMode mode; VariableMode mode;
......
...@@ -22,7 +22,7 @@ class FrameInspector { ...@@ -22,7 +22,7 @@ class FrameInspector {
~FrameInspector(); ~FrameInspector();
int GetParametersCount(); int GetParametersCount();
Handle<JSFunction> GetFunction() { return function_; } Handle<JSFunction> GetFunction() const { return function_; }
Handle<Script> GetScript() { return script_; } Handle<Script> GetScript() { return script_; }
Handle<Object> GetParameter(int index); Handle<Object> GetParameter(int index);
Handle<Object> GetExpression(int index); Handle<Object> GetExpression(int index);
...@@ -41,21 +41,14 @@ class FrameInspector { ...@@ -41,21 +41,14 @@ class FrameInspector {
: JavaScriptFrame::cast(frame_); : JavaScriptFrame::cast(frame_);
} }
JavaScriptFrame* GetArgumentsFrame() { return javascript_frame(); } int inlined_frame_index() const { return inlined_frame_index_; }
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);
private: private:
bool ParameterIsShadowedByContextLocal(Handle<ScopeInfo> info, bool ParameterIsShadowedByContextLocal(Handle<ScopeInfo> info,
Handle<String> parameter_name); Handle<String> parameter_name);
StandardFrame* frame_; StandardFrame* frame_;
int inlined_frame_index_;
std::unique_ptr<DeoptimizedFrameInfo> deoptimized_frame_; std::unique_ptr<DeoptimizedFrameInfo> deoptimized_frame_;
wasm::WasmInterpreter::FramePtr wasm_interpreted_frame_; wasm::WasmInterpreter::FramePtr wasm_interpreted_frame_;
Isolate* isolate_; Isolate* isolate_;
......
...@@ -75,26 +75,8 @@ void DebugScopeIterator::Advance() { ...@@ -75,26 +75,8 @@ void DebugScopeIterator::Advance() {
} }
bool DebugScopeIterator::ShouldIgnore() { bool DebugScopeIterator::ShouldIgnore() {
// Almost always Script scope will be empty, so just filter out that noise. if (GetType() == debug::ScopeIterator::ScopeTypeLocal) return false;
// Also drop empty Block, Eval and Script scopes, should we get any. return !iterator_.DeclaresLocals(i::ScopeIterator::Mode::ALL);
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;
} }
v8::debug::ScopeIterator::ScopeType DebugScopeIterator::GetType() { v8::debug::ScopeIterator::ScopeType DebugScopeIterator::GetType() {
...@@ -104,11 +86,8 @@ v8::debug::ScopeIterator::ScopeType DebugScopeIterator::GetType() { ...@@ -104,11 +86,8 @@ v8::debug::ScopeIterator::ScopeType DebugScopeIterator::GetType() {
v8::Local<v8::Object> DebugScopeIterator::GetObject() { v8::Local<v8::Object> DebugScopeIterator::GetObject() {
DCHECK(!Done()); DCHECK(!Done());
Handle<JSObject> value; Handle<JSObject> value = iterator_.ScopeObject(i::ScopeIterator::Mode::ALL);
if (iterator_.ScopeObject().ToHandle(&value)) { return Utils::ToLocal(value);
return Utils::ToLocal(value);
}
return v8::Local<v8::Object>();
} }
v8::Local<v8::Function> DebugScopeIterator::GetFunction() { v8::Local<v8::Function> DebugScopeIterator::GetFunction() {
......
...@@ -25,24 +25,22 @@ ScopeIterator::ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector, ...@@ -25,24 +25,22 @@ ScopeIterator::ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector,
: isolate_(isolate), : isolate_(isolate),
frame_inspector_(frame_inspector), frame_inspector_(frame_inspector),
function_(frame_inspector_->GetFunction()), function_(frame_inspector_->GetFunction()),
script_(frame_inspector_->GetScript()), script_(frame_inspector_->GetScript()) {
seen_script_scope_(false) {
if (!frame_inspector->GetContext()->IsContext()) { if (!frame_inspector->GetContext()->IsContext()) {
// Optimized frame, context or function cannot be materialized. Give up. // Optimized frame, context or function cannot be materialized. Give up.
return; return;
} }
context_ = Handle<Context>::cast(frame_inspector->GetContext()); context_ = Handle<Context>::cast(frame_inspector->GetContext());
context_ = Handle<Context>::cast(frame_inspector->GetContext());
// We should not instantiate a ScopeIterator for wasm frames. // 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); TryParseAndRetrieveScopes(option);
} }
ScopeIterator::~ScopeIterator() { delete info_; }
Handle<Object> ScopeIterator::GetFunctionDebugName() const { Handle<Object> ScopeIterator::GetFunctionDebugName() const {
if (HasNestedScopeChain()) return JSFunction::GetDebugName(function_);
if (!context_->IsNativeContext()) { if (!context_->IsNativeContext()) {
DisallowHeapAllocation no_gc; DisallowHeapAllocation no_gc;
ScopeInfo* closure_info = context_->closure_context()->scope_info(); ScopeInfo* closure_info = context_->closure_context()->scope_info();
...@@ -55,9 +53,11 @@ Handle<Object> ScopeIterator::GetFunctionDebugName() const { ...@@ -55,9 +53,11 @@ Handle<Object> ScopeIterator::GetFunctionDebugName() const {
ScopeIterator::ScopeIterator(Isolate* isolate, Handle<JSFunction> function) ScopeIterator::ScopeIterator(Isolate* isolate, Handle<JSFunction> function)
: isolate_(isolate), : isolate_(isolate),
context_(function->context()), context_(function->context()),
script_(Script::cast(function->shared()->script())), script_(Script::cast(function->shared()->script())) {
seen_script_scope_(false) { if (!function->shared()->IsSubjectToDebugging()) {
if (!function->shared()->IsSubjectToDebugging()) context_ = Handle<Context>(); context_ = Handle<Context>();
return;
}
UnwrapEvaluationContext(); UnwrapEvaluationContext();
} }
...@@ -67,8 +67,7 @@ ScopeIterator::ScopeIterator(Isolate* isolate, ...@@ -67,8 +67,7 @@ ScopeIterator::ScopeIterator(Isolate* isolate,
generator_(generator), generator_(generator),
function_(generator->function()), function_(generator->function()),
context_(generator->context()), context_(generator->context()),
script_(Script::cast(function_->shared()->script())), script_(Script::cast(function_->shared()->script())) {
seen_script_scope_(false) {
if (!function_->shared()->IsSubjectToDebugging()) { if (!function_->shared()->IsSubjectToDebugging()) {
context_ = Handle<Context>(); context_ = Handle<Context>();
return; return;
...@@ -76,26 +75,29 @@ ScopeIterator::ScopeIterator(Isolate* isolate, ...@@ -76,26 +75,29 @@ ScopeIterator::ScopeIterator(Isolate* isolate,
TryParseAndRetrieveScopes(DEFAULT); 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) { void ScopeIterator::TryParseAndRetrieveScopes(ScopeIterator::Option option) {
// Catch the case when the debugger stops in an internal function. // Catch the case when the debugger stops in an internal function.
Handle<SharedFunctionInfo> shared_info(function_->shared()); Handle<SharedFunctionInfo> shared_info(function_->shared());
Handle<ScopeInfo> scope_info(shared_info->scope_info()); Handle<ScopeInfo> scope_info(shared_info->scope_info());
if (shared_info->script()->IsUndefined(isolate_)) { if (shared_info->script()->IsUndefined(isolate_)) {
current_scope_ = closure_scope_ = nullptr;
context_ = handle(function_->context(), isolate_); context_ = handle(function_->context(), isolate_);
function_ = Handle<JSFunction>(); function_ = Handle<JSFunction>();
return; return;
} }
// Currently it takes too much time to find nested scopes due to script DCHECK_NE(IGNORE_NESTED_SCOPES, option);
// parsing. Sometimes we want to run the ScopeIterator as fast as possible bool ignore_nested_scopes = false;
// (for example, while collecting async call stacks on every if (shared_info->HasBreakInfo() && frame_inspector_ != nullptr) {
// 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) {
// The source position at return is always the end of the function, // The source position at return is always the end of the function,
// which is not consistent with the current scope chain. Therefore all // which is not consistent with the current scope chain. Therefore all
// nested with, catch and block contexts are skipped, and we can only // nested with, catch and block contexts are skipped, and we can only
...@@ -110,58 +112,52 @@ void ScopeIterator::TryParseAndRetrieveScopes(ScopeIterator::Option option) { ...@@ -110,58 +112,52 @@ void ScopeIterator::TryParseAndRetrieveScopes(ScopeIterator::Option option) {
ignore_nested_scopes = location.IsReturn(); 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. // Reparse the code and analyze the scopes.
// Check whether we are in global, eval or function code. // 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. // Global or eval code.
Handle<Script> script(Script::cast(shared_info->script())); 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) { if (scope_info->scope_type() == EVAL_SCOPE) {
info->set_eval(); info_->set_eval();
if (!function_->context()->IsNativeContext()) { if (!context_->IsNativeContext()) {
info->set_outer_scope_info( info_->set_outer_scope_info(handle(context_->scope_info(), isolate_));
handle(function_->context()->scope_info(), isolate_));
} }
// Language mode may be inherited from the eval caller. // Language mode may be inherited from the eval caller.
// Retrieve it from shared function info. // 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) { } else if (scope_info->scope_type() == MODULE_SCOPE) {
DCHECK(info->is_module()); DCHECK(info_->is_module());
} else { } 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_, shared_info, isolate_) &&
} Rewriter::Rewrite(info_)) {
if (parsing::ParseAny(info.get(), shared_info, isolate_) && info_->ast_value_factory()->Internalize(isolate_);
Rewriter::Rewrite(info.get())) { closure_scope_ = info_->literal()->scope();
info->ast_value_factory()->Internalize(isolate_);
DeclarationScope* scope = info->literal()->scope(); if (option == COLLECT_NON_LOCALS) {
if (!ignore_nested_scopes || collect_non_locals) { DCHECK(non_locals_.is_null());
CollectNonLocals(info.get(), scope); non_locals_ = info_->literal()->scope()->CollectNonLocals(
info_, StringSet::New(isolate_));
} }
if (!ignore_nested_scopes) {
if (DeclarationScope::Analyze(info.get())) { CHECK(DeclarationScope::Analyze(info_));
DeclarationScope::AllocateScopeInfos(info.get(), isolate_, if (ignore_nested_scopes) {
AnalyzeMode::kDebugger); current_scope_ = closure_scope_;
RetrieveScopeChain(scope); start_scope_ = current_scope_;
if (closure_scope_->NeedsContext()) {
context_ = handle(context_->closure_context(), isolate_);
} }
} else {
RetrieveScopeChain(closure_scope_);
} }
UnwrapEvaluationContext();
} else { } else {
// A failed reparse indicates that the preparser has diverged from the // A failed reparse indicates that the preparser has diverged from the
// parser or that the preparse data given to the initial parse has been // parser or that the preparse data given to the initial parse has been
...@@ -174,32 +170,30 @@ void ScopeIterator::TryParseAndRetrieveScopes(ScopeIterator::Option option) { ...@@ -174,32 +170,30 @@ void ScopeIterator::TryParseAndRetrieveScopes(ScopeIterator::Option option) {
isolate_->clear_pending_exception(); isolate_->clear_pending_exception();
context_ = Handle<Context>(); context_ = Handle<Context>();
} }
UnwrapEvaluationContext();
} }
void ScopeIterator::UnwrapEvaluationContext() { void ScopeIterator::UnwrapEvaluationContext() {
while (true) { if (!context_->IsDebugEvaluateContext()) return;
if (context_.is_null()) return; Context* current = *context_;
if (!context_->IsDebugEvaluateContext()) return; do {
Handle<Object> wrapped(context_->get(Context::WRAPPED_CONTEXT_INDEX), Object* wrapped = current->get(Context::WRAPPED_CONTEXT_INDEX);
isolate_);
if (wrapped->IsContext()) { if (wrapped->IsContext()) {
context_ = Handle<Context>::cast(wrapped); current = Context::cast(wrapped);
} else { } 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> Handle<JSObject> ScopeIterator::MaterializeScopeDetails() {
ScopeIterator::MaterializeScopeDetails() {
// Calculate the size of the result. // Calculate the size of the result.
Handle<FixedArray> details = Handle<FixedArray> details =
isolate_->factory()->NewFixedArray(kScopeDetailsSize); isolate_->factory()->NewFixedArray(kScopeDetailsSize);
// Fill in scope details. // Fill in scope details.
details->set(kScopeDetailsTypeIndex, Smi::FromInt(Type())); details->set(kScopeDetailsTypeIndex, Smi::FromInt(Type()));
Handle<JSObject> scope_object; Handle<JSObject> scope_object = ScopeObject(Mode::ALL);
ASSIGN_RETURN_ON_EXCEPTION(isolate_, scope_object, ScopeObject(), JSObject);
details->set(kScopeDetailsObjectIndex, *scope_object); details->set(kScopeDetailsObjectIndex, *scope_object);
if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript) { if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript) {
return isolate_->factory()->NewJSArrayWithElements(details); return isolate_->factory()->NewJSArrayWithElements(details);
...@@ -209,7 +203,7 @@ ScopeIterator::MaterializeScopeDetails() { ...@@ -209,7 +203,7 @@ ScopeIterator::MaterializeScopeDetails() {
details->set(kScopeDetailsStartPositionIndex, details->set(kScopeDetailsStartPositionIndex,
Smi::FromInt(start_position())); Smi::FromInt(start_position()));
details->set(kScopeDetailsEndPositionIndex, Smi::FromInt(end_position())); details->set(kScopeDetailsEndPositionIndex, Smi::FromInt(end_position()));
if (HasNestedScopeChain()) { if (InInnerScope()) {
details->set(kScopeDetailsFunctionIndex, *function_); details->set(kScopeDetailsFunctionIndex, *function_);
} }
} }
...@@ -217,87 +211,115 @@ ScopeIterator::MaterializeScopeDetails() { ...@@ -217,87 +211,115 @@ ScopeIterator::MaterializeScopeDetails() {
} }
bool ScopeIterator::HasPositionInfo() { bool ScopeIterator::HasPositionInfo() {
return HasNestedScopeChain() || !context_->IsNativeContext(); return InInnerScope() || !context_->IsNativeContext();
} }
int ScopeIterator::start_position() { int ScopeIterator::start_position() {
if (HasNestedScopeChain()) { if (InInnerScope()) return current_scope_->start_position();
return LastNestedScopeChain().start_position;
}
if (context_->IsNativeContext()) return 0; if (context_->IsNativeContext()) return 0;
return context_->closure_context()->scope_info()->StartPosition(); return context_->closure_context()->scope_info()->StartPosition();
} }
int ScopeIterator::end_position() { int ScopeIterator::end_position() {
if (HasNestedScopeChain()) { if (InInnerScope()) return current_scope_->end_position();
return LastNestedScopeChain().end_position;
}
if (context_->IsNativeContext()) return 0; if (context_->IsNativeContext()) return 0;
return context_->closure_context()->scope_info()->EndPosition(); 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() { void ScopeIterator::Next() {
DCHECK(!Done()); DCHECK(!Done());
ScopeType scope_type = Type(); ScopeType scope_type = Type();
if (scope_type == ScopeTypeGlobal) { if (scope_type == ScopeTypeGlobal) {
// The global scope is always the last in the chain. // The global scope is always the last in the chain.
DCHECK(context_->IsNativeContext()); DCHECK(context_->IsNativeContext());
context_ = Handle<Context>(); 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; seen_script_scope_ = true;
if (context_->IsScriptContext()) { if (context_->IsScriptContext()) {
context_ = Handle<Context>(context_->previous(), isolate_); context_ = handle(context_->previous(), isolate_);
} }
if (HasNestedScopeChain()) { } else if (!inner) {
DCHECK_EQ(LastNestedScopeChain().scope_info->scope_type(), SCRIPT_SCOPE); DCHECK(!context_->IsNativeContext());
nested_scope_chain_.pop_back(); context_ = handle(context_->previous(), isolate_);
DCHECK(!HasNestedScopeChain());
}
CHECK(context_->IsNativeContext());
} else if (!HasNestedScopeChain()) {
context_ = Handle<Context>(context_->previous(), isolate_);
} else { } else {
DCHECK_NOT_NULL(current_scope_);
do { do {
if (LastNestedScopeChain().scope_info->HasContext()) { if (current_scope_->NeedsContext()) {
DCHECK(context_->previous() != nullptr); DCHECK_NOT_NULL(context_->previous());
context_ = Handle<Context>(context_->previous(), isolate_); context_ = handle(context_->previous(), isolate_);
} }
nested_scope_chain_.pop_back(); DCHECK_IMPLIES(InInnerScope(), current_scope_->outer_scope() != nullptr);
if (!HasNestedScopeChain()) break; current_scope_ = current_scope_->outer_scope();
// Repeat to skip hidden scopes. // Repeat to skip hidden scopes.
} while (LastNestedScopeChain().is_hidden()); } while (current_scope_->is_hidden());
} }
if (!HasNestedScopeChain()) function_ = Handle<JSFunction>();
UnwrapEvaluationContext(); UnwrapEvaluationContext();
} }
// Return the type of the current scope. // Return the type of the current scope.
ScopeIterator::ScopeType ScopeIterator::Type() { ScopeIterator::ScopeType ScopeIterator::Type() const {
DCHECK(!Done()); DCHECK(!Done());
if (HasNestedScopeChain()) { if (InInnerScope()) {
Handle<ScopeInfo> scope_info = LastNestedScopeChain().scope_info; switch (current_scope_->scope_type()) {
switch (scope_info->scope_type()) {
case FUNCTION_SCOPE: case FUNCTION_SCOPE:
DCHECK(context_->IsFunctionContext() || !scope_info->HasContext()); DCHECK_IMPLIES(current_scope_->NeedsContext(),
context_->IsFunctionContext());
return ScopeTypeLocal; return ScopeTypeLocal;
case MODULE_SCOPE: case MODULE_SCOPE:
DCHECK(context_->IsModuleContext()); DCHECK_IMPLIES(current_scope_->NeedsContext(),
context_->IsModuleContext());
return ScopeTypeModule; return ScopeTypeModule;
case SCRIPT_SCOPE: case SCRIPT_SCOPE:
DCHECK(context_->IsScriptContext() || context_->IsNativeContext()); DCHECK_IMPLIES(
current_scope_->NeedsContext(),
context_->IsScriptContext() || context_->IsNativeContext());
return ScopeTypeScript; return ScopeTypeScript;
case WITH_SCOPE: case WITH_SCOPE:
DCHECK(context_->IsWithContext() || context_->IsDebugEvaluateContext()); DCHECK_IMPLIES(
current_scope_->NeedsContext(),
context_->IsWithContext() || context_->IsDebugEvaluateContext());
return ScopeTypeWith; return ScopeTypeWith;
case CATCH_SCOPE: case CATCH_SCOPE:
DCHECK(context_->IsCatchContext()); DCHECK(context_->IsCatchContext());
return ScopeTypeCatch; return ScopeTypeCatch;
case BLOCK_SCOPE: case BLOCK_SCOPE:
DCHECK(!scope_info->HasContext() || context_->IsBlockContext()); DCHECK_IMPLIES(current_scope_->NeedsContext(),
context_->IsBlockContext());
return ScopeTypeBlock; return ScopeTypeBlock;
case EVAL_SCOPE: case EVAL_SCOPE:
DCHECK(!scope_info->HasContext() || context_->IsEvalContext()); DCHECK_IMPLIES(current_scope_->NeedsContext(),
context_->IsEvalContext());
return ScopeTypeEval; return ScopeTypeEval;
} }
UNREACHABLE(); UNREACHABLE();
...@@ -327,94 +349,91 @@ ScopeIterator::ScopeType ScopeIterator::Type() { ...@@ -327,94 +349,91 @@ ScopeIterator::ScopeType ScopeIterator::Type() {
return ScopeTypeWith; return ScopeTypeWith;
} }
Handle<JSObject> ScopeIterator::ScopeObject(Mode mode) {
MaybeHandle<JSObject> ScopeIterator::ScopeObject() {
DCHECK(!Done()); 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(); ScopeType type = Type();
if (type == ScopeTypeBlock || type == ScopeTypeLocal || if (type == ScopeTypeGlobal) {
type == ScopeTypeEval) { DCHECK_EQ(Mode::ALL, mode);
if (HasNestedScopeChain()) { return handle(context_->global_proxy(), isolate_);
return LastNestedScopeChain().scope_info->HasContext(); }
} 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, VisitScope(visitor, mode);
Handle<Object> new_value) { return scope;
DCHECK(!Done()); }
void ScopeIterator::VisitScope(const Visitor& visitor, Mode mode) const {
switch (Type()) { switch (Type()) {
case ScopeIterator::ScopeTypeGlobal: case ScopeTypeLocal:
break; case ScopeTypeClosure:
case ScopeIterator::ScopeTypeLocal: case ScopeTypeCatch:
return SetLocalVariableValue(variable_name, new_value); case ScopeTypeBlock:
case ScopeIterator::ScopeTypeWith: case ScopeTypeEval:
break; return VisitLocalScope(visitor, mode);
case ScopeIterator::ScopeTypeClosure: case ScopeTypeModule:
return SetClosureVariableValue(variable_name, new_value); if (InInnerScope()) {
case ScopeIterator::ScopeTypeScript: return VisitLocalScope(visitor, mode);
return SetScriptVariableValue(variable_name, new_value); }
case ScopeIterator::ScopeTypeCatch: DCHECK_EQ(Mode::ALL, mode);
case ScopeIterator::ScopeTypeBlock: return VisitModuleScope(visitor);
case ScopeIterator::ScopeTypeEval: case ScopeTypeScript:
return SetInnerScopeVariableValue(variable_name, new_value); DCHECK_EQ(Mode::ALL, mode);
case ScopeIterator::ScopeTypeModule: return VisitScriptScope(visitor);
return SetModuleVariableValue(variable_name, new_value); case ScopeTypeWith:
break; case ScopeTypeGlobal:
UNREACHABLE();
} }
return false;
} }
bool ScopeIterator::SetVariableValue(Handle<String> name,
Handle<ScopeInfo> ScopeIterator::CurrentScopeInfo() { Handle<Object> value) {
DCHECK(!Done()); DCHECK(!Done());
if (HasNestedScopeChain()) { name = isolate_->factory()->InternalizeString(name);
return LastNestedScopeChain().scope_info; switch (Type()) {
} else if (context_->IsBlockContext() || context_->IsFunctionContext() || case ScopeTypeGlobal:
context_->IsEvalContext() || context_->IsCatchContext()) { case ScopeTypeWith:
return Handle<ScopeInfo>(context_->scope_info()); break;
}
return Handle<ScopeInfo>::null();
}
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() { case ScopeTypeScript:
DCHECK(!Done()); return SetScriptVariableValue(name, value);
if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript ||
!HasNestedScopeChain()) {
return context_;
} else if (LastNestedScopeChain().scope_info->HasContext()) {
return context_;
} else {
return Handle<Context>::null();
} }
return false;
} }
Handle<StringSet> ScopeIterator::GetNonLocals() { return non_locals_; } Handle<StringSet> ScopeIterator::GetNonLocals() { return non_locals_; }
...@@ -427,19 +446,17 @@ void ScopeIterator::DebugPrint() { ...@@ -427,19 +446,17 @@ void ScopeIterator::DebugPrint() {
switch (Type()) { switch (Type()) {
case ScopeIterator::ScopeTypeGlobal: case ScopeIterator::ScopeTypeGlobal:
os << "Global:\n"; os << "Global:\n";
CurrentContext()->Print(os); context_->Print(os);
break; break;
case ScopeIterator::ScopeTypeLocal: { case ScopeIterator::ScopeTypeLocal: {
os << "Local:\n"; os << "Local:\n";
function_->shared()->scope_info()->Print(); if (current_scope_->NeedsContext()) {
if (!CurrentContext().is_null()) { context_->Print(os);
CurrentContext()->Print(os); if (context_->has_extension()) {
if (CurrentContext()->has_extension()) { Handle<HeapObject> extension(context_->extension(), isolate_);
Handle<HeapObject> extension(CurrentContext()->extension(), isolate_); DCHECK(extension->IsJSContextExtensionObject());
if (extension->IsJSContextExtensionObject()) { extension->Print(os);
extension->Print(os);
}
} }
} }
break; break;
...@@ -447,30 +464,28 @@ void ScopeIterator::DebugPrint() { ...@@ -447,30 +464,28 @@ void ScopeIterator::DebugPrint() {
case ScopeIterator::ScopeTypeWith: case ScopeIterator::ScopeTypeWith:
os << "With:\n"; os << "With:\n";
CurrentContext()->extension()->Print(os); context_->extension()->Print(os);
break; break;
case ScopeIterator::ScopeTypeCatch: case ScopeIterator::ScopeTypeCatch:
os << "Catch:\n"; os << "Catch:\n";
CurrentContext()->extension()->Print(os); context_->extension()->Print(os);
CurrentContext()->get(Context::THROWN_OBJECT_INDEX)->Print(os); context_->get(Context::THROWN_OBJECT_INDEX)->Print(os);
break; break;
case ScopeIterator::ScopeTypeClosure: case ScopeIterator::ScopeTypeClosure:
os << "Closure:\n"; os << "Closure:\n";
CurrentContext()->Print(os); context_->Print(os);
if (CurrentContext()->has_extension()) { if (context_->has_extension()) {
Handle<HeapObject> extension(CurrentContext()->extension(), isolate_); Handle<HeapObject> extension(context_->extension(), isolate_);
if (extension->IsJSContextExtensionObject()) { DCHECK(extension->IsJSContextExtensionObject());
extension->Print(os); extension->Print(os);
}
} }
break; break;
case ScopeIterator::ScopeTypeScript: case ScopeIterator::ScopeTypeScript:
os << "Script:\n"; os << "Script:\n";
CurrentContext() context_->global_object()
->global_object()
->native_context() ->native_context()
->script_context_table() ->script_context_table()
->Print(os); ->Print(os);
...@@ -494,380 +509,388 @@ int ScopeIterator::GetSourcePosition() { ...@@ -494,380 +509,388 @@ int ScopeIterator::GetSourcePosition() {
void ScopeIterator::RetrieveScopeChain(DeclarationScope* scope) { void ScopeIterator::RetrieveScopeChain(DeclarationScope* scope) {
DCHECK_NOT_NULL(scope); DCHECK_NOT_NULL(scope);
GetNestedScopeChain(isolate_, scope, GetSourcePosition());
}
void ScopeIterator::CollectNonLocals(ParseInfo* info, DeclarationScope* scope) { const int position = GetSourcePosition();
DCHECK_NOT_NULL(scope);
DCHECK(non_locals_.is_null()); Scope* parent = nullptr;
non_locals_ = scope->CollectNonLocals(info, StringSet::New(isolate_)); 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() { void ScopeIterator::VisitScriptScope(const Visitor& visitor) const {
Handle<JSGlobalObject> global(CurrentContext()->global_object()); Handle<JSGlobalObject> global(context_->global_object(), isolate_);
Handle<ScriptContextTable> script_contexts( Handle<ScriptContextTable> script_contexts(
global->native_context()->script_context_table()); global->native_context()->script_context_table(), isolate_);
Handle<JSObject> script_scope =
isolate_->factory()->NewJSObjectWithNullProto();
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++) { context_index++) {
Handle<Context> context = Handle<Context> context =
ScriptContextTable::GetContext(script_contexts, context_index); ScriptContextTable::GetContext(script_contexts, context_index);
Handle<ScopeInfo> scope_info(context->scope_info()); 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, void ScopeIterator::VisitModuleScope(const Visitor& visitor) const {
Handle<ScopeInfo> scope_info) { DCHECK(context_->IsModuleContext());
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;
// Skip the parameter if it is context-allocated. Handle<ScopeInfo> scope_info(context_->scope_info());
VariableMode mode; if (VisitContextLocals(visitor, scope_info, context_)) return;
InitializationFlag init_flag;
MaybeAssignedFlag maybe_assigned_flag; int count_index = scope_info->ModuleVariableCountIndex();
if (ScopeInfo::ContextSlotIndex(scope_info, name, &mode, &init_flag, int module_variable_count = Smi::cast(scope_info->get(count_index))->value();
&maybe_assigned_flag) != -1) {
continue;
}
Handle<Object> value(parameters_and_registers->get(i), isolate_); Handle<Module> module(context_->module(), isolate_);
DCHECK(!value.is_identical_to(isolate_->factory()->stale_register()));
// TODO(yangguo): We convert optimized out values to {undefined} when they for (int i = 0; i < module_variable_count; ++i) {
// are passed to the debugger. Eventually we should handle them somehow. int index;
if (value->IsTheHole(isolate_) || value->IsOptimizedOut(isolate_)) { Handle<String> name;
value = isolate_->factory()->undefined_value(); {
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) // Reflect variables under TDZ as undeclared in scope object.
.Check(); if (value->IsTheHole(isolate_)) continue;
if (visitor(name, value)) return;
} }
}
for (int i = 0; i < scope_info->StackLocalCount(); ++i) { bool ScopeIterator::VisitContextLocals(const Visitor& visitor,
Handle<String> name = handle(scope_info->StackLocalName(i), isolate_); 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; if (ScopeInfo::VariableIsSynthetic(*name)) continue;
Handle<Object> value(parameters_and_registers->get( int context_index = Context::MIN_CONTEXT_SLOTS + i;
parameter_count + scope_info->StackLocalIndex(i)), Handle<Object> value(context->get(context_index), isolate_);
isolate_); // Reflect variables under TDZ as undefined in scope object.
DCHECK(!value.is_identical_to(isolate_->factory()->stale_register())); if (value->IsTheHole(isolate_)) continue;
if (visitor(name, value)) return true;
// 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();
} }
return false;
} }
MaybeHandle<JSObject> ScopeIterator::MaterializeLocalScope() { bool ScopeIterator::VisitLocals(const Visitor& visitor, Mode mode) const {
DCHECK(HasNestedScopeChain()); for (Variable* var : *current_scope_->locals()) {
Handle<SharedFunctionInfo> shared(function_->shared()); if (!var->is_this() && ScopeInfo::VariableIsSynthetic(*var->name())) {
Handle<ScopeInfo> scope_info(shared->scope_info()); continue;
}
Handle<JSObject> local_scope =
isolate_->factory()->NewJSObjectWithNullProto();
MaterializeStackLocals(local_scope, scope_info);
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. case VariableLocation::LOCAL:
Handle<Context> function_context(context_->closure_context()); if (frame_inspector_ == nullptr) {
DCHECK_EQ(context_->scope_info(), *scope_info); // Get the variable from the suspended generator.
CopyContextLocalsToScopeObject(scope_info, function_context, local_scope); 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. if (visitor(var->name(), value)) return true;
// These will be variables introduced by eval.
if (!function_context->IsNativeContext()) {
CopyContextExtensionToScopeObject(function_context, local_scope,
KeyCollectionMode::kIncludePrototypes);
} }
return false;
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;
} }
// Retrieve the with-context extension object. If the extension object is // Retrieve the with-context extension object. If the extension object is
// a proxy, return an empty object. // a proxy, return an empty object.
Handle<JSObject> ScopeIterator::WithContextExtension() { Handle<JSObject> ScopeIterator::WithContextExtension() {
Handle<Context> context = CurrentContext(); DCHECK(context_->IsWithContext());
DCHECK(context->IsWithContext()); if (context_->extension_receiver()->IsJSProxy()) {
if (context->extension_receiver()->IsJSProxy()) {
return isolate_->factory()->NewJSObjectWithNullProto(); 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 // Create a plain JSObject which materializes the block scope for the specified
// block context. // block context.
Handle<JSObject> ScopeIterator::MaterializeInnerScope() { void ScopeIterator::VisitLocalScope(const Visitor& visitor, Mode mode) const {
Handle<JSObject> inner_scope = if (InInnerScope()) {
isolate_->factory()->NewJSObjectWithNullProto(); if (VisitLocals(visitor, mode)) return;
if (mode == Mode::STACK && Type() == ScopeTypeLocal) {
Handle<Context> context = Handle<Context>::null(); // Hide |this| in arrow functions that may be embedded in other functions
if (HasNestedScopeChain()) { // but don't force |this| to be context-allocated. Otherwise we'd find the
Handle<ScopeInfo> scope_info = LastNestedScopeChain().scope_info; // wrong |this| value.
MaterializeStackLocals(inner_scope, scope_info); if (!closure_scope_->has_this_declaration() &&
if (scope_info->HasContext()) context = CurrentContext(); !non_locals_->Has(isolate_->factory()->this_string())) {
} else { if (visitor(isolate_->factory()->this_string(),
context = CurrentContext(); isolate_->factory()->undefined_value()))
} return;
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;
} }
frame->SetParameterValue(i, *new_value); // Add |arguments| to the function scope even if it wasn't used.
return true; // 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
return false; // FunctionGetArguments does.
} if (frame_inspector_ != nullptr && !closure_scope_->is_arrow_scope() &&
(closure_scope_->arguments() == nullptr ||
bool ScopeIterator::SetStackVariableValue(Handle<ScopeInfo> scope_info, frame_inspector_->GetExpression(closure_scope_->arguments()->index())
Handle<String> variable_name, ->IsOptimizedOut(isolate_))) {
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.
JavaScriptFrame* frame = GetFrame(); JavaScriptFrame* frame = GetFrame();
if (frame->is_optimized()) return false; Handle<JSObject> arguments = Accessors::FunctionGetArguments(
frame->SetExpression(stack_local_index, *new_value); frame, frame_inspector_->inlined_frame_index());
} else { if (visitor(isolate_->factory()->arguments_string(), arguments)) return;
// 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);
} }
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, bool ScopeIterator::SetLocalVariableValue(Handle<String> variable_name,
Handle<Context> context, Handle<Object> new_value) {
Handle<String> variable_name, // TODO(verwaest): Walk parameters backwards, not forwards.
Handle<Object> new_value) { // TODO(verwaest): Use VariableMap rather than locals() list for lookup.
HandleScope scope(isolate_); for (Variable* var : *current_scope_->locals()) {
for (int i = 0; i < scope_info->ContextLocalCount(); i++) { if (String::Equals(var->name(), variable_name)) {
Handle<String> next_name(scope_info->ContextLocalName(i)); int index = var->index();
if (String::Equals(variable_name, next_name)) { switch (var->location()) {
VariableMode mode; case VariableLocation::LOOKUP:
InitializationFlag init_flag; case VariableLocation::UNALLOCATED:
MaybeAssignedFlag maybe_assigned_flag; // Drop assignments to unallocated locals.
int context_index = ScopeInfo::ContextSlotIndex( DCHECK(var->is_this() ||
scope_info, next_name, &mode, &init_flag, &maybe_assigned_flag); *variable_name == isolate_->heap()->arguments_string());
context->set(context_index, *new_value); return false;
return true;
} 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. case VariableLocation::LOCAL:
if (!context->IsModuleContext() && context->has_extension()) { if (frame_inspector_ == nullptr) {
Handle<JSObject> ext(context->extension_object()); // Set the variable in the suspended generator.
Maybe<bool> maybe = JSReceiver::HasOwnProperty(ext, variable_name); DCHECK(!generator_.is_null());
DCHECK(maybe.IsJust()); int parameter_count =
if (maybe.FromJust()) { function_->shared()->scope_info()->ParameterCount();
// We don't expect this to do anything except replacing property value. index += parameter_count;
JSObject::SetOwnPropertyIgnoreAttributes(ext, variable_name, new_value, Handle<FixedArray> parameters_and_registers(
NONE) generator_->parameters_and_registers(), isolate_);
.Check(); DCHECK_LT(index, parameters_and_registers->length());
return true; 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; return false;
} }
bool ScopeIterator::SetLocalVariableValue(Handle<String> variable_name, bool ScopeIterator::SetContextExtensionValue(Handle<String> variable_name,
Handle<Object> new_value) { Handle<Object> new_value) {
Handle<ScopeInfo> scope_info; if (!context_->has_extension()) return false;
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_);
}
// Parameter might be shadowed in context. Don't stop here. DCHECK(context_->extension_object()->IsJSContextExtensionObject());
bool result = SetParameterValue(scope_info, variable_name, new_value); 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. CHECK(Object::SetDataProperty(&it, new_value).ToChecked());
if (SetStackVariableValue(scope_info, variable_name, new_value)) { return true;
return true; }
}
if (scope_info->HasContext() && bool ScopeIterator::SetContextVariableValue(Handle<String> variable_name,
SetContextVariableValue(scope_info, context_, variable_name, new_value)) { Handle<Object> new_value) {
return true; 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, bool ScopeIterator::SetModuleVariableValue(Handle<String> variable_name,
Handle<Object> new_value) { 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; int cell_index;
{ VariableMode mode;
VariableMode mode; InitializationFlag init_flag;
InitializationFlag init_flag; MaybeAssignedFlag maybe_assigned_flag;
MaybeAssignedFlag maybe_assigned_flag; cell_index = context_->scope_info()->ModuleIndex(
cell_index = scope_info->ModuleIndex(variable_name, &mode, &init_flag, variable_name, &mode, &init_flag, &maybe_assigned_flag);
&maybe_assigned_flag);
}
// Setting imports is currently not supported. // Setting imports is currently not supported.
bool found = ModuleDescriptor::GetCellIndexKind(cell_index) == if (ModuleDescriptor::GetCellIndexKind(cell_index) !=
ModuleDescriptor::kExport; ModuleDescriptor::kExport) {
if (found) { return false;
Module::StoreVariable(handle(context->module(), isolate_), cell_index,
new_value);
} }
return found;
}
bool ScopeIterator::SetInnerScopeVariableValue(Handle<String> variable_name, Handle<Module> module(context_->module(), isolate_);
Handle<Object> new_value) { Module::StoreVariable(module, cell_index, new_value);
Handle<ScopeInfo> scope_info = CurrentScopeInfo(); return true;
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);
} }
bool ScopeIterator::SetScriptVariableValue(Handle<String> variable_name, bool ScopeIterator::SetScriptVariableValue(Handle<String> variable_name,
Handle<Object> new_value) { Handle<Object> new_value) {
Handle<String> internalized_variable_name =
isolate_->factory()->InternalizeString(variable_name);
Handle<Context> context = CurrentContext();
Handle<ScriptContextTable> script_contexts( Handle<ScriptContextTable> script_contexts(
context->global_object()->native_context()->script_context_table()); context_->global_object()->native_context()->script_context_table());
ScriptContextTable::LookupResult lookup_result; ScriptContextTable::LookupResult lookup_result;
if (ScriptContextTable::Lookup(script_contexts, internalized_variable_name, if (ScriptContextTable::Lookup(script_contexts, variable_name,
&lookup_result)) { &lookup_result)) {
Handle<Context> script_context = ScriptContextTable::GetContext( Handle<Context> script_context = ScriptContextTable::GetContext(
script_contexts, lookup_result.context_index); script_contexts, lookup_result.context_index);
...@@ -878,116 +901,5 @@ bool ScopeIterator::SetScriptVariableValue(Handle<String> variable_name, ...@@ -878,116 +901,5 @@ bool ScopeIterator::SetScriptVariableValue(Handle<String> variable_name,
return false; 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 internal
} // namespace v8 } // namespace v8
...@@ -48,32 +48,35 @@ class ScopeIterator { ...@@ -48,32 +48,35 @@ class ScopeIterator {
ScopeIterator(Isolate* isolate, Handle<JSFunction> function); ScopeIterator(Isolate* isolate, Handle<JSFunction> function);
ScopeIterator(Isolate* isolate, Handle<JSGeneratorObject> generator); ScopeIterator(Isolate* isolate, Handle<JSGeneratorObject> generator);
~ScopeIterator();
V8_WARN_UNUSED_RESULT MaybeHandle<JSObject> MaterializeScopeDetails(); Handle<JSObject> MaterializeScopeDetails();
// More scopes? // More scopes?
bool Done() { return context_.is_null(); } bool Done() const { return context_.is_null(); }
// Move to the next scope. // Move to the next scope.
void Next(); void Next();
// Restart to the first scope and context.
void Restart();
// Return the type of the current scope. // 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. // 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. // Set variable value and return true on success.
bool SetVariableValue(Handle<String> variable_name, Handle<Object> new_value); 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. // Populate the set with collected non-local variable names.
Handle<StringSet> GetNonLocals(); Handle<StringSet> GetNonLocals();
...@@ -94,96 +97,63 @@ class ScopeIterator { ...@@ -94,96 +97,63 @@ class ScopeIterator {
void DebugPrint(); void DebugPrint();
#endif #endif
private: bool InInnerScope() const { return !function_.is_null(); }
struct ExtendedScopeInfo { bool HasContext() const;
ExtendedScopeInfo(Handle<ScopeInfo> info, int start, int end) Handle<Context> CurrentContext() const {
: scope_info(info), start_position(start), end_position(end) {} DCHECK(HasContext());
explicit ExtendedScopeInfo(Handle<ScopeInfo> info) return context_;
: 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; }
};
private:
Isolate* isolate_; Isolate* isolate_;
ParseInfo* info_ = nullptr;
FrameInspector* const frame_inspector_ = nullptr; FrameInspector* const frame_inspector_ = nullptr;
Handle<JSGeneratorObject> generator_; Handle<JSGeneratorObject> generator_;
Handle<JSFunction> function_; Handle<JSFunction> function_;
Handle<ScopeInfo> function_scope_info_;
Handle<Context> context_; Handle<Context> context_;
Handle<Script> script_; Handle<Script> script_;
std::vector<ExtendedScopeInfo> nested_scope_chain_;
Handle<StringSet> non_locals_; 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() { inline JavaScriptFrame* GetFrame() const {
return frame_inspector_->GetArgumentsFrame(); return frame_inspector_->javascript_frame();
} }
Handle<Context> GetContext();
int GetSourcePosition(); int GetSourcePosition();
void MaterializeStackLocals(Handle<JSObject> local_scope,
Handle<ScopeInfo> scope_info);
void TryParseAndRetrieveScopes(ScopeIterator::Option option); void TryParseAndRetrieveScopes(ScopeIterator::Option option);
void RetrieveScopeChain(DeclarationScope* scope); void RetrieveScopeChain(DeclarationScope* scope);
void CollectNonLocals(ParseInfo* info, DeclarationScope* scope);
void UnwrapEvaluationContext(); void UnwrapEvaluationContext();
V8_WARN_UNUSED_RESULT MaybeHandle<JSObject> MaterializeScriptScope(); typedef std::function<bool(Handle<String> name, Handle<Object> value)>
V8_WARN_UNUSED_RESULT MaybeHandle<JSObject> MaterializeLocalScope(); Visitor;
V8_WARN_UNUSED_RESULT MaybeHandle<JSObject> MaterializeModuleScope();
Handle<JSObject> MaterializeClosure();
Handle<JSObject> MaterializeInnerScope();
Handle<JSObject> WithContextExtension(); Handle<JSObject> WithContextExtension();
bool SetLocalVariableValue(Handle<String> variable_name, bool SetLocalVariableValue(Handle<String> variable_name,
Handle<Object> new_value); Handle<Object> new_value);
bool SetInnerScopeVariableValue(Handle<String> variable_name, bool SetContextVariableValue(Handle<String> variable_name,
Handle<Object> new_value);
bool SetClosureVariableValue(Handle<String> variable_name,
Handle<Object> new_value); Handle<Object> new_value);
bool SetContextExtensionValue(Handle<String> variable_name,
Handle<Object> new_value);
bool SetScriptVariableValue(Handle<String> variable_name, bool SetScriptVariableValue(Handle<String> variable_name,
Handle<Object> new_value); Handle<Object> new_value);
bool SetModuleVariableValue(Handle<String> variable_name, bool SetModuleVariableValue(Handle<String> variable_name,
Handle<Object> new_value); Handle<Object> new_value);
// Helper functions. // Helper functions.
bool SetParameterValue(Handle<ScopeInfo> scope_info, void VisitScope(const Visitor& visitor, Mode mode) const;
Handle<String> parameter_name, void VisitLocalScope(const Visitor& visitor, Mode mode) const;
Handle<Object> new_value); void VisitScriptScope(const Visitor& visitor) const;
bool SetStackVariableValue(Handle<ScopeInfo> scope_info, void VisitModuleScope(const Visitor& visitor) const;
Handle<String> variable_name, bool VisitLocals(const Visitor& visitor, Mode mode) const;
Handle<Object> new_value); bool VisitContextLocals(const Visitor& visitor, Handle<ScopeInfo> scope_info,
bool SetContextVariableValue(Handle<ScopeInfo> scope_info, Handle<Context> context) const;
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();
DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator); DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
}; };
......
...@@ -2198,52 +2198,28 @@ Handle<Object> GetValueForDebugger(TranslatedFrame::iterator it, ...@@ -2198,52 +2198,28 @@ Handle<Object> GetValueForDebugger(TranslatedFrame::iterator it,
DeoptimizedFrameInfo::DeoptimizedFrameInfo(TranslatedState* state, DeoptimizedFrameInfo::DeoptimizedFrameInfo(TranslatedState* state,
TranslatedState::iterator frame_it, TranslatedState::iterator frame_it,
Isolate* isolate) { Isolate* isolate) {
// If the previous frame is an adaptor frame, we will take the parameters int parameter_count =
// from there. frame_it->shared_info()->internal_formal_parameter_count();
TranslatedState::iterator parameter_frame = frame_it; TranslatedFrame::iterator stack_it = frame_it->begin();
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.
// Figure out whether there is a construct stub frame on top of // Get the function. Note that this might materialize the function.
// the parameter frame. // In case the debugger mutates this value, we should deoptimize
has_construct_stub_ = // the function and remember the value in the materialized value store.
parameter_frame != state->begin() && function_ = Handle<JSFunction>::cast(stack_it->GetValue());
(parameter_frame - 1)->kind() == TranslatedFrame::kConstructStub; stack_it++; // Skip the function.
stack_it++; // Skip the receiver.
DCHECK_EQ(TranslatedFrame::kInterpretedFunction, frame_it->kind()); DCHECK_EQ(TranslatedFrame::kInterpretedFunction, frame_it->kind());
source_position_ = Deoptimizer::ComputeSourcePositionFromBytecodeArray( source_position_ = Deoptimizer::ComputeSourcePositionFromBytecodeArray(
*frame_it->shared_info(), frame_it->node_id()); *frame_it->shared_info(), frame_it->node_id());
TranslatedFrame::iterator value_it = frame_it->begin(); DCHECK_EQ(parameter_count,
// Get the function. Note that this might materialize the function. function_->shared()->internal_formal_parameter_count());
// 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());
parameters_.resize(static_cast<size_t>(parameter_count)); parameters_.resize(static_cast<size_t>(parameter_count));
for (int i = 0; i < parameter_count; i++) { 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); 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++; stack_it++;
} }
......
...@@ -1025,12 +1025,6 @@ class DeoptimizedFrameInfo : public Malloced { ...@@ -1025,12 +1025,6 @@ class DeoptimizedFrameInfo : public Malloced {
// Get the frame context. // Get the frame context.
Handle<Object> GetContext() { return 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. // Get an incoming argument.
Handle<Object> GetParameter(int index) { Handle<Object> GetParameter(int index) {
DCHECK(0 <= index && index < parameters_count()); DCHECK(0 <= index && index < parameters_count());
...@@ -1062,7 +1056,6 @@ class DeoptimizedFrameInfo : public Malloced { ...@@ -1062,7 +1056,6 @@ class DeoptimizedFrameInfo : public Malloced {
Handle<JSFunction> function_; Handle<JSFunction> function_;
Handle<Object> context_; Handle<Object> context_;
bool has_construct_stub_;
std::vector<Handle<Object> > parameters_; std::vector<Handle<Object> > parameters_;
std::vector<Handle<Object> > expression_stack_; std::vector<Handle<Object> > expression_stack_;
int source_position_; int source_position_;
......
...@@ -740,6 +740,7 @@ int ScopeInfo::StackSlotIndex(String* name) const { ...@@ -740,6 +740,7 @@ int ScopeInfo::StackSlotIndex(String* name) const {
int ScopeInfo::ModuleIndex(Handle<String> name, VariableMode* mode, int ScopeInfo::ModuleIndex(Handle<String> name, VariableMode* mode,
InitializationFlag* init_flag, InitializationFlag* init_flag,
MaybeAssignedFlag* maybe_assigned_flag) { MaybeAssignedFlag* maybe_assigned_flag) {
DCHECK(name->IsInternalizedString());
DCHECK_EQ(scope_type(), MODULE_SCOPE); DCHECK_EQ(scope_type(), MODULE_SCOPE);
DCHECK_NOT_NULL(mode); DCHECK_NOT_NULL(mode);
DCHECK_NOT_NULL(init_flag); DCHECK_NOT_NULL(init_flag);
......
...@@ -344,7 +344,7 @@ RUNTIME_FUNCTION(Runtime_GetGeneratorScopeDetails) { ...@@ -344,7 +344,7 @@ RUNTIME_FUNCTION(Runtime_GetGeneratorScopeDetails) {
return isolate->heap()->undefined_value(); return isolate->heap()->undefined_value();
} }
RETURN_RESULT_OR_FAILURE(isolate, it.MaterializeScopeDetails()); return *it.MaterializeScopeDetails();
} }
static bool SetScopeVariableValue(ScopeIterator* it, int index, static bool SetScopeVariableValue(ScopeIterator* it, int index,
......
...@@ -648,8 +648,7 @@ TEST(ScopeUsesArgumentsSuperThis) { ...@@ -648,8 +648,7 @@ TEST(ScopeUsesArgumentsSuperThis) {
CHECK(i::Rewriter::Rewrite(&info)); CHECK(i::Rewriter::Rewrite(&info));
info.ast_value_factory()->Internalize(isolate); info.ast_value_factory()->Internalize(isolate);
CHECK(i::DeclarationScope::Analyze(&info)); CHECK(i::DeclarationScope::Analyze(&info));
i::DeclarationScope::AllocateScopeInfos(&info, isolate, i::DeclarationScope::AllocateScopeInfos(&info, isolate);
i::AnalyzeMode::kRegular);
CHECK_NOT_NULL(info.literal()); CHECK_NOT_NULL(info.literal());
i::DeclarationScope* script_scope = info.literal()->scope(); i::DeclarationScope* script_scope = info.literal()->scope();
...@@ -10126,8 +10125,7 @@ TEST(LexicalLoopVariable) { ...@@ -10126,8 +10125,7 @@ TEST(LexicalLoopVariable) {
CHECK(i::parsing::ParseProgram(&info, isolate)); CHECK(i::parsing::ParseProgram(&info, isolate));
CHECK(i::Rewriter::Rewrite(&info)); CHECK(i::Rewriter::Rewrite(&info));
CHECK(i::DeclarationScope::Analyze(&info)); CHECK(i::DeclarationScope::Analyze(&info));
i::DeclarationScope::AllocateScopeInfos(&info, isolate, i::DeclarationScope::AllocateScopeInfos(&info, isolate);
i::AnalyzeMode::kRegular);
CHECK_NOT_NULL(info.literal()); CHECK_NOT_NULL(info.literal());
i::DeclarationScope* script_scope = info.literal()->scope(); i::DeclarationScope* script_scope = info.literal()->scope();
......
...@@ -142,7 +142,7 @@ listener_delegate = function(exec_state) { ...@@ -142,7 +142,7 @@ listener_delegate = function(exec_state) {
{exported_var: undefined, imported_var: undefined}, {exported_var: undefined, imported_var: undefined},
0, exec_state); 0, exec_state);
CheckScopeDoesNotHave( CheckScopeDoesNotHave(
["local_var", "doesntexist", "local_let", "exported_let", "imported_let"], ["doesntexist", "exported_let", "imported_let"],
0, exec_state); 0, exec_state);
}; };
debugger; debugger;
...@@ -163,7 +163,7 @@ listener_delegate = function(exec_state) { ...@@ -163,7 +163,7 @@ listener_delegate = function(exec_state) {
CheckScopeContent( CheckScopeContent(
{exported_let: 3, exported_var: 4, {exported_let: 3, exported_var: 4,
imported_let: 3, imported_var: 4}, 0, exec_state); imported_let: 3, imported_var: 4}, 0, exec_state);
CheckScopeDoesNotHave(["local_var", "local_let"], 0, exec_state); CheckScopeDoesNotHave([], 0, exec_state);
}; };
debugger; debugger;
EndTest(); EndTest();
...@@ -181,7 +181,7 @@ listener_delegate = function(exec_state) { ...@@ -181,7 +181,7 @@ listener_delegate = function(exec_state) {
CheckScopeContent( CheckScopeContent(
{exported_let: 13, exported_var: 14, {exported_let: 13, exported_var: 14,
imported_let: 13, imported_var: 14}, 0, exec_state); imported_let: 13, imported_var: 14}, 0, exec_state);
CheckScopeDoesNotHave(["local_var", "local_let"], 0, exec_state); CheckScopeDoesNotHave([], 0, exec_state);
}; };
debugger; debugger;
EndTest(); EndTest();
...@@ -4,7 +4,7 @@ Running test: testTotal ...@@ -4,7 +4,7 @@ Running test: testTotal
foo1 (module1:7:2) foo1 (module1:7:2)
foo2 (module2:6:9) foo2 (module2:6:9)
(anonymous) (module3:4:0) (anonymous) (module3:4:0)
local:foo1 local
[ [
[0] : c1 = 12 [0] : c1 = 12
[1] : g1 = 2 [1] : g1 = 2
...@@ -94,7 +94,7 @@ foo1 = ...@@ -94,7 +94,7 @@ foo1 =
objectId : <objectId> objectId : <objectId>
type : function type : function
} }
local:foo2 local
[ [
[0] : c2 = 22 [0] : c2 = 22
] ]
...@@ -180,7 +180,8 @@ foo2 = ...@@ -180,7 +180,8 @@ foo2 =
module module
[ [
[0] : foo2 = function foo2() { let c2 = 22; return foo1() + a2 + b2 + c2; } [0] : foo2 = function foo2() { let c2 = 22; return foo1() + a2 + b2 + c2; }
[1] : b3 = 31 [1] : a3 = 30
[2] : b3 = 31
] ]
global global
[ [
...@@ -205,6 +206,20 @@ foo2 = ...@@ -205,6 +206,20 @@ foo2 =
objectId : <objectId> objectId : <objectId>
type : function type : function
} }
a3 =
{
description : 30
type : number
value : 30
}
Evaluating: ++a3
updated a3 =
{
description : 31
type : number
value : 31
}
Evaluating: --a3
b3 = b3 =
{ {
description : 31 description : 31
...@@ -224,7 +239,7 @@ Running test: testAnother ...@@ -224,7 +239,7 @@ Running test: testAnother
(anonymous) (module4:5:13) (anonymous) (module4:5:13)
bar (module4:5:24) bar (module4:5:24)
(anonymous) (module4:7:0) (anonymous) (module4:7:0)
local local:bar
[ [
] ]
closure:bar closure:bar
...@@ -295,6 +310,12 @@ updated a = ...@@ -295,6 +310,12 @@ updated a =
value : 1 value : 1
} }
Evaluating: --a Evaluating: --a
module
[
[0] : a = 1
[1] : b = 2
[2] : bar = function bar() { let a = 0; (() => {a; debugger;})(); }
]
global global
[ [
... ...
...@@ -310,6 +331,41 @@ Array = ...@@ -310,6 +331,41 @@ Array =
objectId : <objectId> objectId : <objectId>
type : function 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 Running test: testDifferentModuleVariables
(anonymous) (module5:5:0) (anonymous) (module5:5:0)
...@@ -485,6 +541,10 @@ Evaluating: --x ...@@ -485,6 +541,10 @@ Evaluating: --x
Running test: testLocalVariableToplevel Running test: testLocalVariableToplevel
(anonymous) (module7:2:0) (anonymous) (module7:2:0)
module
[
[0] : x = 5
]
global global
[ [
... ...
...@@ -501,3 +561,17 @@ Array = ...@@ -501,3 +561,17 @@ Array =
objectId : <objectId> objectId : <objectId>
type : function 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