| Index: src/debug/debug-evaluate.cc
|
| diff --git a/src/debug/debug-evaluate.cc b/src/debug/debug-evaluate.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..509d97a3903511ec4e269fb7d4605ac7b23d0dc8
|
| --- /dev/null
|
| +++ b/src/debug/debug-evaluate.cc
|
| @@ -0,0 +1,319 @@
|
| +// Copyright 2015 the V8 project authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "src/debug/debug-evaluate.h"
|
| +
|
| +#include "src/accessors.h"
|
| +#include "src/contexts.h"
|
| +#include "src/debug/debug.h"
|
| +#include "src/debug/debug-frames.h"
|
| +#include "src/debug/debug-scopes.h"
|
| +#include "src/isolate.h"
|
| +
|
| +namespace v8 {
|
| +namespace internal {
|
| +
|
| +
|
| +static inline bool IsDebugContext(Isolate* isolate, Context* context) {
|
| + // Try to unwrap script context if it exist.
|
| + if (context->IsScriptContext()) context = context->previous();
|
| + DCHECK_NOT_NULL(context);
|
| + return context == *isolate->debug()->debug_context();
|
| +}
|
| +
|
| +
|
| +MaybeHandle<Object> DebugEvaluate::Global(Isolate* isolate,
|
| + Handle<String> source,
|
| + bool disable_break,
|
| + Handle<Object> context_extension) {
|
| + // Handle the processing of break.
|
| + DisableBreak disable_break_scope(isolate->debug(), disable_break);
|
| +
|
| + // Enter the top context from before the debugger was invoked.
|
| + SaveContext save(isolate);
|
| + SaveContext* top = &save;
|
| + while (top != NULL && IsDebugContext(isolate, *top->context())) {
|
| + top = top->prev();
|
| + }
|
| + if (top != NULL) isolate->set_context(*top->context());
|
| +
|
| + // Get the native context now set to the top context from before the
|
| + // debugger was invoked.
|
| + Handle<Context> context = isolate->native_context();
|
| + Handle<JSObject> receiver(context->global_proxy());
|
| + Handle<SharedFunctionInfo> outer_info(context->closure()->shared(), isolate);
|
| + return Evaluate(isolate, outer_info, context, context_extension, receiver,
|
| + source);
|
| +}
|
| +
|
| +
|
| +MaybeHandle<Object> DebugEvaluate::Local(Isolate* isolate,
|
| + StackFrame::Id frame_id,
|
| + int inlined_jsframe_index,
|
| + Handle<String> source,
|
| + bool disable_break,
|
| + Handle<Object> context_extension) {
|
| + // Handle the processing of break.
|
| + DisableBreak disable_break_scope(isolate->debug(), disable_break);
|
| +
|
| + // Get the frame where the debugging is performed.
|
| + JavaScriptFrameIterator it(isolate, frame_id);
|
| + JavaScriptFrame* frame = it.frame();
|
| +
|
| + // Traverse the saved contexts chain to find the active context for the
|
| + // selected frame.
|
| + SaveContext* save =
|
| + DebugFrameHelper::FindSavedContextForFrame(isolate, frame);
|
| + SaveContext savex(isolate);
|
| + isolate->set_context(*(save->context()));
|
| +
|
| + // Materialize stack locals and the arguments object.
|
| + ContextBuilder context_builder(isolate, frame, inlined_jsframe_index);
|
| + if (isolate->has_pending_exception()) return MaybeHandle<Object>();
|
| +
|
| + Handle<Object> receiver(frame->receiver(), isolate);
|
| + 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();
|
| + return maybe_result;
|
| +}
|
| +
|
| +
|
| +// 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<Object> receiver, Handle<String> source) {
|
| + if (context_extension->IsJSObject()) {
|
| + Handle<JSObject> extension = Handle<JSObject>::cast(context_extension);
|
| + Handle<JSFunction> closure(context->closure(), isolate);
|
| + context = isolate->factory()->NewWithContext(closure, context, extension);
|
| + }
|
| +
|
| + Handle<JSFunction> eval_fun;
|
| + ASSIGN_RETURN_ON_EXCEPTION(isolate, eval_fun,
|
| + Compiler::GetFunctionFromEval(
|
| + source, outer_info, context, SLOPPY,
|
| + NO_PARSE_RESTRICTION, RelocInfo::kNoPosition),
|
| + Object);
|
| +
|
| + Handle<Object> result;
|
| + ASSIGN_RETURN_ON_EXCEPTION(
|
| + isolate, result, Execution::Call(isolate, eval_fun, receiver, 0, NULL),
|
| + Object);
|
| +
|
| + // Skip the global proxy as it has no properties and always delegates to the
|
| + // real global object.
|
| + if (result->IsJSGlobalProxy()) {
|
| + PrototypeIterator iter(isolate, result);
|
| + // TODO(verwaest): This will crash when the global proxy is detached.
|
| + result = Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter));
|
| + }
|
| +
|
| + return result;
|
| +}
|
| +
|
| +
|
| +DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate,
|
| + JavaScriptFrame* frame,
|
| + int inlined_jsframe_index)
|
| + : isolate_(isolate),
|
| + frame_(frame),
|
| + inlined_jsframe_index_(inlined_jsframe_index) {
|
| + FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate);
|
| + Handle<JSFunction> function =
|
| + handle(JSFunction::cast(frame_inspector.GetFunction()));
|
| + Handle<Context> outer_context = handle(function->context(), isolate);
|
| + outer_info_ = handle(function->shared());
|
| + Handle<Context> inner_context;
|
| +
|
| + bool stop = false;
|
| + for (ScopeIterator it(isolate, &frame_inspector);
|
| + !it.Failed() && !it.Done() && !stop; it.Next()) {
|
| + ScopeIterator::ScopeType scope_type = it.Type();
|
| +
|
| + if (scope_type == ScopeIterator::ScopeTypeLocal) {
|
| + Handle<Context> parent_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<JSObject> materialized_function = NewJSObjectWithNullProto();
|
| +
|
| + frame_inspector.MaterializeStackLocals(materialized_function, function);
|
| +
|
| + MaterializeArgumentsObject(materialized_function, function);
|
| +
|
| + Handle<Context> with_context = isolate->factory()->NewWithContext(
|
| + function, parent_context, materialized_function);
|
| +
|
| + ContextChainElement context_chain_element;
|
| + context_chain_element.original_context = it.CurrentContext();
|
| + 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);
|
| + } else if (scope_type == ScopeIterator::ScopeTypeCatch ||
|
| + scope_type == ScopeIterator::ScopeTypeWith) {
|
| + Handle<Context> cloned_context =
|
| + Handle<Context>::cast(FixedArray::CopySize(
|
| + it.CurrentContext(), it.CurrentContext()->length()));
|
| +
|
| + ContextChainElement context_chain_element;
|
| + context_chain_element.original_context = it.CurrentContext();
|
| + context_chain_element.cloned_context = cloned_context;
|
| + context_chain_.Add(context_chain_element);
|
| +
|
| + RecordContextsInChain(&inner_context, cloned_context, cloned_context);
|
| + } else if (scope_type == ScopeIterator::ScopeTypeBlock) {
|
| + Handle<JSObject> materialized_object = NewJSObjectWithNullProto();
|
| + frame_inspector.MaterializeStackLocals(materialized_object,
|
| + it.CurrentScopeInfo());
|
| + if (it.HasContext()) {
|
| + Handle<Context> cloned_context =
|
| + Handle<Context>::cast(FixedArray::CopySize(
|
| + it.CurrentContext(), it.CurrentContext()->length()));
|
| + Handle<Context> with_context = isolate->factory()->NewWithContext(
|
| + function, cloned_context, materialized_object);
|
| +
|
| + ContextChainElement context_chain_element;
|
| + context_chain_element.original_context = it.CurrentContext();
|
| + context_chain_element.cloned_context = cloned_context;
|
| + context_chain_element.materialized_object = materialized_object;
|
| + context_chain_element.scope_info = it.CurrentScopeInfo();
|
| + context_chain_.Add(context_chain_element);
|
| +
|
| + RecordContextsInChain(&inner_context, cloned_context, with_context);
|
| + } else {
|
| + Handle<Context> with_context = isolate->factory()->NewWithContext(
|
| + function, outer_context, materialized_object);
|
| +
|
| + ContextChainElement context_chain_element;
|
| + context_chain_element.materialized_object = materialized_object;
|
| + context_chain_element.scope_info = it.CurrentScopeInfo();
|
| + context_chain_.Add(context_chain_element);
|
| +
|
| + RecordContextsInChain(&inner_context, with_context, with_context);
|
| + }
|
| + } else {
|
| + stop = true;
|
| + }
|
| + }
|
| + if (innermost_context_.is_null()) {
|
| + innermost_context_ = outer_context;
|
| + }
|
| + DCHECK(!innermost_context_.is_null());
|
| +}
|
| +
|
| +
|
| +void DebugEvaluate::ContextBuilder::UpdateValues() {
|
| + for (int i = 0; i < context_chain_.length(); i++) {
|
| + ContextChainElement element = context_chain_[i];
|
| + if (!element.original_context.is_null() &&
|
| + !element.cloned_context.is_null()) {
|
| + Handle<Context> cloned_context = element.cloned_context;
|
| + cloned_context->CopyTo(
|
| + Context::MIN_CONTEXT_SLOTS, *element.original_context,
|
| + Context::MIN_CONTEXT_SLOTS,
|
| + cloned_context->length() - Context::MIN_CONTEXT_SLOTS);
|
| + }
|
| + if (!element.materialized_object.is_null()) {
|
| + // Write back potential changes to materialized stack locals to the
|
| + // stack.
|
| + FrameInspector(frame_, inlined_jsframe_index_, isolate_)
|
| + .UpdateStackLocalsFromMaterializedObject(element.materialized_object,
|
| + element.scope_info);
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +Handle<JSObject> DebugEvaluate::ContextBuilder::NewJSObjectWithNullProto() {
|
| + Handle<JSObject> result =
|
| + isolate_->factory()->NewJSObject(isolate_->object_function());
|
| + Handle<Map> new_map =
|
| + Map::Copy(Handle<Map>(result->map()), "ObjectWithNullProto");
|
| + Map::SetPrototype(new_map, isolate_->factory()->null_value());
|
| + JSObject::MigrateToMap(result, new_map);
|
| + return result;
|
| +}
|
| +
|
| +
|
| +void DebugEvaluate::ContextBuilder::RecordContextsInChain(
|
| + Handle<Context>* inner_context, Handle<Context> first,
|
| + Handle<Context> last) {
|
| + if (!inner_context->is_null()) {
|
| + (*inner_context)->set_previous(*last);
|
| + } else {
|
| + innermost_context_ = last;
|
| + }
|
| + *inner_context = first;
|
| +}
|
| +
|
| +
|
| +void DebugEvaluate::ContextBuilder::MaterializeArgumentsObject(
|
| + Handle<JSObject> target, Handle<JSFunction> function) {
|
| + // Do not materialize the arguments object for eval or top-level code.
|
| + // Skip if "arguments" is already taken.
|
| + if (!function->shared()->is_function()) return;
|
| + Maybe<bool> maybe = JSReceiver::HasOwnProperty(
|
| + target, isolate_->factory()->arguments_string());
|
| + DCHECK(maybe.IsJust());
|
| + if (maybe.FromJust()) return;
|
| +
|
| + // FunctionGetArguments can't throw an exception.
|
| + Handle<JSObject> arguments =
|
| + Handle<JSObject>::cast(Accessors::FunctionGetArguments(function));
|
| + Handle<String> arguments_str = isolate_->factory()->arguments_string();
|
| + JSObject::SetOwnPropertyIgnoreAttributes(target, arguments_str, arguments,
|
| + NONE)
|
| + .Check();
|
| +}
|
| +
|
| +
|
| +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;
|
| + VariableLocation location;
|
| + 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, &location,
|
| + &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;
|
| + }
|
| +
|
| + return isolate_->factory()->NewCatchContext(
|
| + function, target, isolate_->factory()->this_string(), receiver);
|
| +}
|
| +
|
| +} // namespace internal
|
| +} // namespace v8
|
|
|