diff options
Diffstat (limited to 'chromium/v8/src/debug/debug-evaluate.cc')
-rw-r--r-- | chromium/v8/src/debug/debug-evaluate.cc | 208 |
1 files changed, 151 insertions, 57 deletions
diff --git a/chromium/v8/src/debug/debug-evaluate.cc b/chromium/v8/src/debug/debug-evaluate.cc index b2495613242..e19b93eebea 100644 --- a/chromium/v8/src/debug/debug-evaluate.cc +++ b/chromium/v8/src/debug/debug-evaluate.cc @@ -20,10 +20,9 @@ static inline bool IsDebugContext(Isolate* isolate, Context* context) { } -MaybeHandle<Object> DebugEvaluate::Global(Isolate* isolate, - Handle<String> source, - bool disable_break, - Handle<Object> context_extension) { +MaybeHandle<Object> DebugEvaluate::Global( + Isolate* isolate, Handle<String> source, bool disable_break, + Handle<HeapObject> context_extension) { // Handle the processing of break. DisableBreak disable_break_scope(isolate->debug(), disable_break); @@ -50,7 +49,7 @@ MaybeHandle<Object> DebugEvaluate::Local(Isolate* isolate, int inlined_jsframe_index, Handle<String> source, bool disable_break, - Handle<Object> context_extension) { + Handle<HeapObject> context_extension) { // Handle the processing of break. DisableBreak disable_break_scope(isolate->debug(), disable_break); @@ -65,15 +64,23 @@ 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. + // Note that the native context is taken from the original context chain, + // which may not be the current native context of the isolate. 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 = context_builder.native_context(); + Handle<JSObject> receiver(context->global_proxy()); MaybeHandle<Object> maybe_result = Evaluate( isolate, context_builder.outer_info(), context_builder.innermost_context(), context_extension, receiver, source); - if (!maybe_result.is_null()) context_builder.UpdateValues(); + if (!maybe_result.is_null() && !FLAG_debug_eval_readonly_locals) { + context_builder.UpdateValues(); + } return maybe_result; } @@ -81,7 +88,7 @@ MaybeHandle<Object> DebugEvaluate::Local(Isolate* isolate, // Compile and evaluate source for the given context. MaybeHandle<Object> DebugEvaluate::Evaluate( Isolate* isolate, Handle<SharedFunctionInfo> outer_info, - Handle<Context> context, Handle<Object> context_extension, + Handle<Context> context, Handle<HeapObject> context_extension, Handle<Object> receiver, Handle<String> source) { if (context_extension->IsJSObject()) { Handle<JSObject> extension = Handle<JSObject>::cast(context_extension); @@ -120,42 +127,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()); + native_context_ = Handle<Context>(outer_context->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( @@ -175,7 +208,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(); @@ -187,7 +220,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; @@ -208,6 +241,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() && @@ -224,6 +258,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); + } } } } @@ -272,41 +311,96 @@ 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; +MaybeHandle<Object> DebugEvaluate::ContextBuilder::LoadFromContext( + Handle<Context> context, Handle<String> name, bool* global) { + 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); + // Do not shadow variables on the script context. + *global = context->IsScriptContext(); + return Handle<Object>(context->get(index), isolate_); + } else { // Found on object. + Handle<JSReceiver> object = Handle<JSReceiver>::cast(holder); + // Do not shadow properties on the global object. + *global = object->IsJSGlobalObject(); + 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; + bool global; + if (!LoadFromContext(context, name, &global).ToHandle(&value) || global) { + // If resolving the variable fails, skip it. If it resolves to a global + // variable, skip it as well since it's not read-only and can be resolved + // within debug-evaluate. + continue; } - 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; + 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()); + } +} + - return isolate_->factory()->NewCatchContext( - function, target, isolate_->factory()->this_string(), receiver); +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> 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) { + bool global; + LoadFromContext(lookup_context, this_string, &global).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 |