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();
if (scope_type == ScopeIterator::ScopeTypeLocal) {
DCHECK_EQ(FUNCTION_SCOPE, it.CurrentScopeInfo()->scope_type());
Handle<JSObject> materialized = factory->NewJSObjectWithNullProto();
Handle<Context> local_context =
it.HasContext() ? it.CurrentContext() : outer_context;
Handle<StringSet> non_locals = it.GetNonLocals();
MaterializeReceiver(materialized, local_context, local_function,
non_locals);
MaterializeStackLocals(materialized, local_function, &frame_inspector);
ContextChainElement context_chain_element; ContextChainElement context_chain_element;
context_chain_element.scope_info = it.CurrentScopeInfo(); if (scope_type == ScopeIterator::ScopeTypeLocal ||
context_chain_element.materialized_object = materialized; scope_iterator_.DeclaresLocals(ScopeIterator::Mode::STACK)) {
// Non-locals that are already being referenced by the current function context_chain_element.materialized_object =
// are guaranteed to be correctly resolved. scope_iterator_.ScopeObject(ScopeIterator::Mode::STACK);
context_chain_element.whitelist = non_locals;
if (it.HasContext()) {
context_chain_element.wrapped_context = it.CurrentContext();
} }
context_chain_.push_back(context_chain_element); if (scope_iterator_.HasContext()) {
evaluation_context_ = outer_context; context_chain_element.wrapped_context = scope_iterator_.CurrentContext();
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); if (scope_type == ScopeIterator::ScopeTypeLocal) {
} else if (scope_type == ScopeIterator::ScopeTypeBlock || context_chain_element.whitelist = scope_iterator_.GetNonLocals();
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); context_chain_.push_back(context_chain_element);
} else {
break;
}
} }
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++) {
void DebugEvaluate::ContextBuilder::MaterializeReceiver( DCHECK(keys->get(i)->IsString());
Handle<JSObject> target, Handle<Context> local_context, Handle<String> key(String::cast(keys->get(i)), isolate_);
Handle<JSFunction> local_function, Handle<StringSet> non_locals) { Handle<Object> value =
Handle<Object> recv = isolate_->factory()->undefined_value(); JSReceiver::GetDataProperty(element.materialized_object, key);
Handle<String> name = isolate_->factory()->this_string(); scope_iterator_.SetVariableValue(key, value);
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(); }
} scope_iterator_.Next();
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() {
......
This diff is collapsed.
...@@ -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,59 +97,49 @@ class ScopeIterator { ...@@ -94,59 +97,49 @@ 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); Handle<Object> new_value);
bool SetClosureVariableValue(Handle<String> variable_name, bool SetContextExtensionValue(Handle<String> variable_name,
Handle<Object> new_value); Handle<Object> new_value);
bool SetScriptVariableValue(Handle<String> variable_name, bool SetScriptVariableValue(Handle<String> variable_name,
Handle<Object> new_value); Handle<Object> new_value);
...@@ -154,36 +147,13 @@ class ScopeIterator { ...@@ -154,36 +147,13 @@ class ScopeIterator {
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.
TranslatedState::iterator parameter_frame = frame_it;
if (parameter_frame != state->begin()) {
parameter_frame--;
}
int parameter_count;
if (parameter_frame->kind() == TranslatedFrame::kArgumentsAdaptor) {
parameter_count = parameter_frame->height() - 1; // Ignore the receiver.
} else {
parameter_frame = frame_it;
parameter_count =
frame_it->shared_info()->internal_formal_parameter_count(); frame_it->shared_info()->internal_formal_parameter_count();
} TranslatedFrame::iterator stack_it = frame_it->begin();
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