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

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: test case 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..44ce86ad63edff140394095506068c541c831c9a 100644
--- a/src/debug/debug-evaluate.cc
+++ b/src/debug/debug-evaluate.cc
@@ -112,6 +112,16 @@ MaybeHandle<Object> DebugEvaluate::Evaluate(
}
+Handle<Context> FindScriptOrNativeContext(Handle<Context> context) {
+ DisallowHeapAllocation no_gc;
+ Context* raw_context = *context;
+ while (!raw_context->IsScriptContext() && !raw_context->IsNativeContext()) {
+ raw_context = raw_context->previous();
+ }
+ return Handle<Context>(raw_context);
+}
+
+
DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate,
JavaScriptFrame* frame,
int inlined_jsframe_index)
@@ -126,35 +136,57 @@ DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate,
Handle<Context> inner_context;
bool stop = false;
- for (ScopeIterator it(isolate, &frame_inspector);
+ 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();
+ // Duplicate contexts up to the script context for debug evaluate.
+ // The content of the duplicated contexts are written back to the original
+ // at the end. This however means that any changes to the original will be
+ // overwritten.
if (scope_type == ScopeIterator::ScopeTypeLocal) {
- Handle<Context> parent_context =
+ // The context chain at this point looks like this:
+ // <native context> <script context> <outer contexts>
+ // <function context> <inner contexts>
+ // We can only be sure to correctly resolve variables that our function
+ // itself already references. Other variables may be stack-allocated,
+ // and invisible to the context lookup.
+ // To make sure that other variables are not accessible, we collect the
+ // variables used by our function and replace the context chain between
+ // the current function and the script context by a with-context that
+ // materializes accessible context vars and stack vars. We also add a
+ // catch-context to resolve 'this' if necessary.
+ // After the replacement, it looks like this:
+ // <native context> <script context> <receiver context>
+ // <accessible context vars and materialized stack> <inner contexts>
+ DCHECK_EQ(FUNCTION_SCOPE, it.CurrentScopeInfo()->scope_type());
+ it.GetNonLocals(&non_locals_);
+ Handle<Context> base_context = FindScriptOrNativeContext(outer_context);
+ 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(
+ base_context, local_context, function, it.ThisIsNonLocal());
Handle<JSObject> materialized_function = NewJSObjectWithNullProto();
-
frame_inspector.MaterializeStackLocals(materialized_function, function);
-
MaterializeArgumentsObject(materialized_function, function);
+ MaterializeContextChain(materialized_function, local_context);
Handle<Context> with_context = isolate->factory()->NewWithContext(
- function, parent_context, materialized_function);
+ 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(
@@ -207,6 +239,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 +256,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 +309,84 @@ 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);
}
+}
+
- return isolate_->factory()->NewCatchContext(
- function, target, isolate_->factory()->this_string(), receiver);
+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);
+ }
+}
+
+
+Handle<Context> DebugEvaluate::ContextBuilder::MaterializeReceiver(
+ Handle<Context> parent_context, Handle<Context> lookup_context,
+ Handle<JSFunction> 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 (function->shared()->scope_info()->HasReceiver()) {
+ receiver = handle(frame_->receiver(), isolate_);
+ }
+ return isolate_->factory()->NewCatchContext(function, parent_context,
+ this_string, receiver);
}
} // namespace internal
« src/ast/scopes.cc ('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