| 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
|
|
|