Index: src/runtime/runtime.cc |
diff --git a/src/runtime/runtime.cc b/src/runtime/runtime.cc |
index 373d4b1227971f93a9e4cd34ea9b449456fe3527..379a38f746d7f5bcbb00bd06607f8d69cb1a3fef 100644 |
--- a/src/runtime/runtime.cc |
+++ b/src/runtime/runtime.cc |
@@ -12596,10 +12596,6 @@ RUNTIME_FUNCTION(Runtime_DebugEvaluate) { |
SaveContext savex(isolate); |
isolate->set_context(*(save->context())); |
- // Evaluate on the context of the frame. |
- Handle<Context> context(Context::cast(frame_inspector.GetContext())); |
- DCHECK(!context.is_null()); |
- |
// Materialize stack locals and the arguments object. |
Handle<JSObject> materialized = NewJSObjectWithNullProto(isolate); |
@@ -12612,14 +12608,53 @@ RUNTIME_FUNCTION(Runtime_DebugEvaluate) { |
isolate, materialized, |
MaterializeArgumentsObject(isolate, materialized, function)); |
- // Add the materialized object in a with-scope to shadow the stack locals. |
- context = isolate->factory()->NewWithContext(function, context, materialized); |
+ // At this point, the lookup chain may look like this: |
+ // [inner context] -> [function stack]+[function context] -> [outer context] |
+ // The function stack is not an actual context, it complements the function |
+ // context. In order to have the same lookup chain when debug-evaluating, |
+ // we materialize the stack and insert it into the context chain as a |
+ // with-context before the function context. |
+ // [inner context] -> [with context] -> [function context] -> [outer context] |
+ // Ordering the with-context before the function context forces a dynamic |
+ // lookup instead of a static lookup that could fail as the scope info is |
+ // outdated and may expect variables to still be stack-allocated. |
+ // Afterwards, we write changes to the with-context back to the stack |
+ // and remove it from the context chain. |
+ // This could cause lookup failures if debug-evaluate creates a closure that |
+ // uses this temporary context chain. |
+ |
+ Handle<Context> eval_context(Context::cast(frame_inspector.GetContext())); |
+ DCHECK(!eval_context.is_null()); |
+ Handle<Context> function_context = eval_context; |
+ Handle<Context> outer_context(function->context(), isolate); |
+ Handle<Context> inner_context; |
+ // We iterate to find the function's context. If the function has no |
+ // context-allocated variables, we iterate until we hit the outer context. |
+ while (!function_context->IsFunctionContext() && |
+ !function_context.is_identical_to(outer_context)) { |
+ inner_context = function_context; |
+ function_context = Handle<Context>(function_context->previous(), isolate); |
+ } |
+ |
+ Handle<Context> materialized_context = isolate->factory()->NewWithContext( |
+ function, function_context, materialized); |
+ |
+ if (inner_context.is_null()) { |
+ // No inner context. The with-context is now inner-most. |
+ eval_context = materialized_context; |
+ } else { |
+ inner_context->set_previous(*materialized_context); |
+ } |
Handle<Object> receiver(frame->receiver(), isolate); |
+ MaybeHandle<Object> maybe_result = |
+ DebugEvaluate(isolate, eval_context, context_extension, receiver, source); |
+ |
+ // Remove with-context if it was inserted in between. |
+ if (!inner_context.is_null()) inner_context->set_previous(*function_context); |
+ |
Handle<Object> result; |
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
- isolate, result, |
- DebugEvaluate(isolate, context, context_extension, receiver, source)); |
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, maybe_result); |
// Write back potential changes to materialized stack locals to the stack. |
UpdateStackLocalsFromMaterializedObject(isolate, materialized, function, |