Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(192)

Unified Diff: src/debug/debug-evaluate.cc

Issue 1500933002: [debugger] fix debug-evaluate wrt shadowed context var. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: add TODO Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: src/debug/debug-evaluate.cc
diff --git a/src/debug/debug-evaluate.cc b/src/debug/debug-evaluate.cc
index 65bbc3fe50e134c06c2917e0014dc1afd8a80989..9d0d38446e0a6cd27ec9bca7e614544c4026b30b 100644
--- a/src/debug/debug-evaluate.cc
+++ b/src/debug/debug-evaluate.cc
@@ -64,11 +64,16 @@ MaybeHandle<Object> DebugEvaluate::Local(Isolate* isolate,
SaveContext savex(isolate);
isolate->set_context(*(save->context()));
- // Materialize stack locals and the arguments object.
+ // This is not a lot different than DebugEvaluate::Global, except that
+ // variables accessible by the function we are evaluating from are
+ // materialized and included on top of the native context. Changes to
+ // the materialized object are written back afterwards.
ContextBuilder context_builder(isolate, frame, inlined_jsframe_index);
if (isolate->has_pending_exception()) return MaybeHandle<Object>();
- Handle<Object> receiver(frame->receiver(), isolate);
+ Handle<Context> context = isolate->native_context();
+ Handle<JSObject> receiver(context->global_proxy());
+ Handle<SharedFunctionInfo> outer_info(context->closure()->shared(), isolate);
MaybeHandle<Object> maybe_result = Evaluate(
isolate, context_builder.outer_info(),
context_builder.innermost_context(), context_extension, receiver, source);
@@ -119,42 +124,68 @@ DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate,
frame_(frame),
inlined_jsframe_index_(inlined_jsframe_index) {
FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate);
- Handle<JSFunction> function =
+ Handle<JSFunction> local_function =
handle(JSFunction::cast(frame_inspector.GetFunction()));
- Handle<Context> outer_context = handle(function->context(), isolate);
- outer_info_ = handle(function->shared());
+ Handle<Context> outer_context(local_function->context());
+ Handle<Context> native_context = isolate->native_context();
+ Handle<JSFunction> global_function(native_context->closure());
+ outer_info_ = handle(global_function->shared());
Handle<Context> inner_context;
bool stop = false;
- for (ScopeIterator it(isolate, &frame_inspector);
+
+ // Iterate the original context chain to create a context chain that reflects
+ // our needs. The original context chain may look like this:
+ // <native context> <outer contexts> <function context> <inner contexts>
+ // In the resulting context chain, we want to materialize the receiver,
+ // the parameters of the current function, the stack locals. We only
+ // materialize context variables that the function already references,
+ // because only for those variables we can be sure that they will be resolved
+ // correctly. Variables that are not referenced by the function may be
+ // context-allocated and thus accessible, but may be shadowed by stack-
+ // allocated variables and the resolution would be incorrect.
+ // The result will look like this:
+ // <native context> <receiver context>
+ // <materialized stack and accessible context vars> <inner contexts>
+ // All contexts use the closure of the native context, since there is no
+ // function context in the chain. Variables that cannot be resolved are
+ // bound to toplevel (script contexts or global object).
+ // Once debug-evaluate has been executed, the changes to the materialized
+ // objects are written back to the original context chain. Any changes to
+ // the original context chain will therefore be overwritten.
+ const ScopeIterator::Option option = ScopeIterator::COLLECT_NON_LOCALS;
+ for (ScopeIterator it(isolate, &frame_inspector, option);
!it.Failed() && !it.Done() && !stop; it.Next()) {
ScopeIterator::ScopeType scope_type = it.Type();
-
if (scope_type == ScopeIterator::ScopeTypeLocal) {
- Handle<Context> parent_context =
+ DCHECK_EQ(FUNCTION_SCOPE, it.CurrentScopeInfo()->scope_type());
+ it.GetNonLocals(&non_locals_);
+ Handle<Context> local_context =
it.HasContext() ? it.CurrentContext() : outer_context;
// The "this" binding, if any, can't be bound via "with". If we need
// to, add another node onto the outer context to bind "this".
- parent_context = MaterializeReceiver(parent_context, function);
+ Handle<Context> receiver_context =
+ MaterializeReceiver(native_context, local_context, local_function,
+ global_function, it.ThisIsNonLocal());
Handle<JSObject> materialized_function = NewJSObjectWithNullProto();
-
- frame_inspector.MaterializeStackLocals(materialized_function, function);
-
- MaterializeArgumentsObject(materialized_function, function);
+ frame_inspector.MaterializeStackLocals(materialized_function,
+ local_function);
+ MaterializeArgumentsObject(materialized_function, local_function);
+ MaterializeContextChain(materialized_function, local_context);
Handle<Context> with_context = isolate->factory()->NewWithContext(
- function, parent_context, materialized_function);
+ global_function, receiver_context, materialized_function);
ContextChainElement context_chain_element;
- context_chain_element.original_context = it.CurrentContext();
+ context_chain_element.original_context = local_context;
context_chain_element.materialized_object = materialized_function;
context_chain_element.scope_info = it.CurrentScopeInfo();
context_chain_.Add(context_chain_element);
stop = true;
- RecordContextsInChain(&inner_context, with_context, with_context);
+ RecordContextsInChain(&inner_context, receiver_context, with_context);
} else if (scope_type == ScopeIterator::ScopeTypeCatch ||
scope_type == ScopeIterator::ScopeTypeWith) {
Handle<Context> cloned_context = Handle<Context>::cast(
@@ -174,7 +205,7 @@ DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate,
Handle<Context> cloned_context = Handle<Context>::cast(
isolate->factory()->CopyFixedArray(it.CurrentContext()));
Handle<Context> with_context = isolate->factory()->NewWithContext(
- function, cloned_context, materialized_object);
+ global_function, cloned_context, materialized_object);
ContextChainElement context_chain_element;
context_chain_element.original_context = it.CurrentContext();
@@ -186,7 +217,7 @@ DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate,
RecordContextsInChain(&inner_context, cloned_context, with_context);
} else {
Handle<Context> with_context = isolate->factory()->NewWithContext(
- function, outer_context, materialized_object);
+ global_function, outer_context, materialized_object);
ContextChainElement context_chain_element;
context_chain_element.materialized_object = materialized_object;
@@ -207,6 +238,7 @@ DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate,
void DebugEvaluate::ContextBuilder::UpdateValues() {
+ // TODO(yangguo): remove updating values.
for (int i = 0; i < context_chain_.length(); i++) {
ContextChainElement element = context_chain_[i];
if (!element.original_context.is_null() &&
@@ -223,6 +255,11 @@ void DebugEvaluate::ContextBuilder::UpdateValues() {
FrameInspector(frame_, inlined_jsframe_index_, isolate_)
.UpdateStackLocalsFromMaterializedObject(element.materialized_object,
element.scope_info);
+ if (element.scope_info->scope_type() == FUNCTION_SCOPE) {
+ DCHECK_EQ(context_chain_.length() - 1, i);
+ UpdateContextChainFromMaterializedObject(element.materialized_object,
+ element.original_context);
+ }
}
}
}
@@ -271,41 +308,85 @@ void DebugEvaluate::ContextBuilder::MaterializeArgumentsObject(
}
-Handle<Context> DebugEvaluate::ContextBuilder::MaterializeReceiver(
- Handle<Context> target, Handle<JSFunction> function) {
- Handle<SharedFunctionInfo> shared(function->shared());
- Handle<ScopeInfo> scope_info(shared->scope_info());
- Handle<Object> receiver;
- switch (scope_info->scope_type()) {
- case FUNCTION_SCOPE: {
- VariableMode mode;
- InitializationFlag init_flag;
- MaybeAssignedFlag maybe_assigned_flag;
-
- // Don't bother creating a fake context node if "this" is in the context
- // already.
- if (ScopeInfo::ContextSlotIndex(scope_info,
- isolate_->factory()->this_string(), &mode,
- &init_flag, &maybe_assigned_flag) >= 0) {
- return target;
- }
- receiver = handle(frame_->receiver(), isolate_);
- break;
- }
- case MODULE_SCOPE:
- receiver = isolate_->factory()->undefined_value();
- break;
- case SCRIPT_SCOPE:
- receiver = handle(function->global_proxy(), isolate_);
- break;
- default:
- // For eval code, arrow functions, and the like, there's no "this" binding
- // to materialize.
- return target;
+MaybeHandle<Object> DebugEvaluate::ContextBuilder::LoadFromContext(
+ Handle<Context> context, Handle<String> name) {
+ static const ContextLookupFlags flags = FOLLOW_CONTEXT_CHAIN;
+ int index;
+ PropertyAttributes attributes;
+ BindingFlags binding;
+ Handle<Object> holder =
+ context->Lookup(name, flags, &index, &attributes, &binding);
+ if (holder.is_null()) return MaybeHandle<Object>();
+ Handle<Object> value;
+ if (index != Context::kNotFound) { // Found on context.
+ Handle<Context> context = Handle<Context>::cast(holder);
+ return Handle<Object>(context->get(index), isolate_);
+ } else { // Found on object.
+ Handle<JSReceiver> object = Handle<JSReceiver>::cast(holder);
+ return JSReceiver::GetDataProperty(object, name);
+ }
+}
+
+
+void DebugEvaluate::ContextBuilder::MaterializeContextChain(
+ Handle<JSObject> target, Handle<Context> context) {
+ for (const Handle<String>& name : non_locals_) {
+ HandleScope scope(isolate_);
+ Handle<Object> value;
+ if (!LoadFromContext(context, name).ToHandle(&value)) continue;
+ JSObject::SetOwnPropertyIgnoreAttributes(target, name, value, NONE).Check();
+ }
+}
+
+
+void DebugEvaluate::ContextBuilder::StoreToContext(Handle<Context> context,
+ Handle<String> name,
+ Handle<Object> value) {
+ static const ContextLookupFlags flags = FOLLOW_CONTEXT_CHAIN;
+ int index;
+ PropertyAttributes attributes;
+ BindingFlags binding;
+ Handle<Object> holder =
+ context->Lookup(name, flags, &index, &attributes, &binding);
+ if (holder.is_null()) return;
+ if (attributes & READ_ONLY) return;
+ if (index != Context::kNotFound) { // Found on context.
+ Handle<Context> context = Handle<Context>::cast(holder);
+ context->set(index, *value);
+ } else { // Found on object.
+ Handle<JSReceiver> object = Handle<JSReceiver>::cast(holder);
+ LookupIterator lookup(object, name);
+ if (lookup.state() != LookupIterator::DATA) return;
+ CHECK(JSReceiver::SetDataProperty(&lookup, value).FromJust());
+ }
+}
+
+
+void DebugEvaluate::ContextBuilder::UpdateContextChainFromMaterializedObject(
+ Handle<JSObject> source, Handle<Context> context) {
+ // TODO(yangguo): check whether overwriting context fields is actually safe
+ // wrt fields we consider constant.
+ for (const Handle<String>& name : non_locals_) {
+ HandleScope scope(isolate_);
+ Handle<Object> value = JSReceiver::GetDataProperty(source, name);
+ StoreToContext(context, name, value);
}
+}
- return isolate_->factory()->NewCatchContext(
- function, target, isolate_->factory()->this_string(), receiver);
+
+Handle<Context> DebugEvaluate::ContextBuilder::MaterializeReceiver(
+ Handle<Context> parent_context, Handle<Context> lookup_context,
+ Handle<JSFunction> local_function, Handle<JSFunction> global_function,
+ bool this_is_non_local) {
+ Handle<Object> receiver = isolate_->factory()->undefined_value();
+ Handle<String> this_string = isolate_->factory()->this_string();
+ if (this_is_non_local) {
+ LoadFromContext(lookup_context, this_string).ToHandle(&receiver);
+ } else if (local_function->shared()->scope_info()->HasReceiver()) {
+ receiver = handle(frame_->receiver(), isolate_);
+ }
+ return isolate_->factory()->NewCatchContext(global_function, parent_context,
+ this_string, receiver);
}
} // namespace internal
« src/ast/scopes.h ('K') | « src/debug/debug-evaluate.h ('k') | src/debug/debug-frames.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698