"src/dateparser-inl.h",
"src/dateparser.cc",
"src/dateparser.h",
+ "src/debug/debug-evaluate.cc",
+ "src/debug/debug-evaluate.h",
+ "src/debug/debug-frames.cc",
+ "src/debug/debug-frames.h",
+ "src/debug/debug-scopes.cc",
+ "src/debug/debug-scopes.h",
"src/debug/debug.cc",
"src/debug/debug.h",
"src/debug/liveedit.cc",
--- /dev/null
+// 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
--- /dev/null
+// 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.
+
+#ifndef V8_DEBUG_DEBUG_EVALUATE_H_
+#define V8_DEBUG_DEBUG_EVALUATE_H_
+
+#include "src/frames.h"
+#include "src/objects.h"
+
+namespace v8 {
+namespace internal {
+
+class DebugEvaluate : public AllStatic {
+ public:
+ static MaybeHandle<Object> Global(Isolate* isolate, Handle<String> source,
+ bool disable_break,
+ Handle<Object> context_extension);
+
+ // Evaluate a piece of JavaScript in the context of a stack frame for
+ // debugging. Things that need special attention are:
+ // - Parameters and stack-allocated locals need to be materialized. Altered
+ // values need to be written back to the stack afterwards.
+ // - The arguments object needs to materialized.
+ static MaybeHandle<Object> Local(Isolate* isolate, StackFrame::Id frame_id,
+ int inlined_jsframe_index,
+ Handle<String> source, bool disable_break,
+ Handle<Object> context_extension);
+
+ private:
+ // This class builds a context chain for evaluation of expressions
+ // in debugger.
+ // The scope chain leading up to a breakpoint where evaluation occurs
+ // looks like:
+ // - [a mix of with, catch and block scopes]
+ // - [function stack + context]
+ // - [outer context]
+ // The builder materializes all stack variables into properties of objects;
+ // the expression is then evaluated as if it is inside a series of 'with'
+ // statements using those objects. To this end, the builder builds a new
+ // context chain, based on a scope chain:
+ // - every With and Catch scope begets a cloned context
+ // - Block scope begets one or two contexts:
+ // - if a block has context-allocated varaibles, its context is cloned
+ // - stack locals are materizalized as a With context
+ // - Local scope begets a With context for materizalized locals, chained to
+ // original function context. Original function context is the end of
+ // the chain.
+ class ContextBuilder {
+ public:
+ ContextBuilder(Isolate* isolate, JavaScriptFrame* frame,
+ int inlined_jsframe_index);
+
+ void UpdateValues();
+
+ Handle<Context> innermost_context() const { return innermost_context_; }
+ Handle<SharedFunctionInfo> outer_info() const { return outer_info_; }
+
+ private:
+ struct ContextChainElement {
+ Handle<Context> original_context;
+ Handle<Context> cloned_context;
+ Handle<JSObject> materialized_object;
+ Handle<ScopeInfo> scope_info;
+ };
+
+ void RecordContextsInChain(Handle<Context>* inner_context,
+ Handle<Context> first, Handle<Context> last);
+
+ Handle<JSObject> NewJSObjectWithNullProto();
+
+ // Helper function to find or create the arguments object for
+ // Runtime_DebugEvaluate.
+ void MaterializeArgumentsObject(Handle<JSObject> target,
+ Handle<JSFunction> function);
+
+ Handle<Context> MaterializeReceiver(Handle<Context> target,
+ Handle<JSFunction> function);
+
+ Handle<SharedFunctionInfo> outer_info_;
+ Handle<Context> innermost_context_;
+ List<ContextChainElement> context_chain_;
+ Isolate* isolate_;
+ JavaScriptFrame* frame_;
+ int inlined_jsframe_index_;
+ };
+
+ static MaybeHandle<Object> Evaluate(Isolate* isolate,
+ Handle<SharedFunctionInfo> outer_info,
+ Handle<Context> context,
+ Handle<Object> context_extension,
+ Handle<Object> receiver,
+ Handle<String> source);
+};
+
+
+} // namespace internal
+} // namespace v8
+
+#endif // V8_DEBUG_DEBUG_EVALUATE_H_
--- /dev/null
+// 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-frames.h"
+
+namespace v8 {
+namespace internal {
+
+FrameInspector::FrameInspector(JavaScriptFrame* frame,
+ int inlined_jsframe_index, Isolate* isolate)
+ : frame_(frame), deoptimized_frame_(NULL), isolate_(isolate) {
+ has_adapted_arguments_ = frame_->has_adapted_arguments();
+ is_bottommost_ = inlined_jsframe_index == 0;
+ is_optimized_ = frame_->is_optimized();
+ // Calculate the deoptimized frame.
+ if (frame->is_optimized()) {
+ // TODO(turbofan): Revisit once we support deoptimization.
+ if (frame->LookupCode()->is_turbofanned() &&
+ frame->function()->shared()->asm_function() &&
+ !FLAG_turbo_asm_deoptimization) {
+ is_optimized_ = false;
+ return;
+ }
+
+ deoptimized_frame_ = Deoptimizer::DebuggerInspectableFrame(
+ frame, inlined_jsframe_index, isolate);
+ }
+}
+
+
+FrameInspector::~FrameInspector() {
+ // Get rid of the calculated deoptimized frame if any.
+ if (deoptimized_frame_ != NULL) {
+ Deoptimizer::DeleteDebuggerInspectableFrame(deoptimized_frame_, isolate_);
+ }
+}
+
+
+int FrameInspector::GetParametersCount() {
+ return is_optimized_ ? deoptimized_frame_->parameters_count()
+ : frame_->ComputeParametersCount();
+}
+
+
+int FrameInspector::expression_count() {
+ return deoptimized_frame_->expression_count();
+}
+
+
+Object* FrameInspector::GetFunction() {
+ return is_optimized_ ? deoptimized_frame_->GetFunction() : frame_->function();
+}
+
+
+Object* FrameInspector::GetParameter(int index) {
+ return is_optimized_ ? deoptimized_frame_->GetParameter(index)
+ : frame_->GetParameter(index);
+}
+
+
+Object* FrameInspector::GetExpression(int index) {
+ // TODO(turbofan): Revisit once we support deoptimization.
+ if (frame_->LookupCode()->is_turbofanned() &&
+ frame_->function()->shared()->asm_function() &&
+ !FLAG_turbo_asm_deoptimization) {
+ return isolate_->heap()->undefined_value();
+ }
+ return is_optimized_ ? deoptimized_frame_->GetExpression(index)
+ : frame_->GetExpression(index);
+}
+
+
+int FrameInspector::GetSourcePosition() {
+ return is_optimized_ ? deoptimized_frame_->GetSourcePosition()
+ : frame_->LookupCode()->SourcePosition(frame_->pc());
+}
+
+
+bool FrameInspector::IsConstructor() {
+ return is_optimized_ && !is_bottommost_
+ ? deoptimized_frame_->HasConstructStub()
+ : frame_->IsConstructor();
+}
+
+
+Object* FrameInspector::GetContext() {
+ return is_optimized_ ? deoptimized_frame_->GetContext() : frame_->context();
+}
+
+
+// To inspect all the provided arguments the frame might need to be
+// replaced with the arguments frame.
+void FrameInspector::SetArgumentsFrame(JavaScriptFrame* frame) {
+ DCHECK(has_adapted_arguments_);
+ frame_ = frame;
+ is_optimized_ = frame_->is_optimized();
+ DCHECK(!is_optimized_);
+}
+
+
+// Create a plain JSObject which materializes the local scope for the specified
+// frame.
+void FrameInspector::MaterializeStackLocals(Handle<JSObject> target,
+ Handle<ScopeInfo> scope_info) {
+ HandleScope scope(isolate_);
+ // First fill all parameters.
+ for (int i = 0; i < scope_info->ParameterCount(); ++i) {
+ // Do not materialize the parameter if it is shadowed by a context local.
+ Handle<String> name(scope_info->ParameterName(i));
+ if (ParameterIsShadowedByContextLocal(scope_info, name)) continue;
+
+ Handle<Object> value(i < GetParametersCount()
+ ? GetParameter(i)
+ : isolate_->heap()->undefined_value(),
+ isolate_);
+ DCHECK(!value->IsTheHole());
+
+ JSObject::SetOwnPropertyIgnoreAttributes(target, name, value, NONE).Check();
+ }
+
+ // Second fill all stack locals.
+ for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
+ if (scope_info->LocalIsSynthetic(i)) continue;
+ Handle<String> name(scope_info->StackLocalName(i));
+ Handle<Object> value(GetExpression(scope_info->StackLocalIndex(i)),
+ isolate_);
+ if (value->IsTheHole()) value = isolate_->factory()->undefined_value();
+
+ JSObject::SetOwnPropertyIgnoreAttributes(target, name, value, NONE).Check();
+ }
+}
+
+
+void FrameInspector::MaterializeStackLocals(Handle<JSObject> target,
+ Handle<JSFunction> function) {
+ Handle<SharedFunctionInfo> shared(function->shared());
+ Handle<ScopeInfo> scope_info(shared->scope_info());
+ MaterializeStackLocals(target, scope_info);
+}
+
+
+void FrameInspector::UpdateStackLocalsFromMaterializedObject(
+ Handle<JSObject> target, Handle<ScopeInfo> scope_info) {
+ if (is_optimized_) {
+ // Optimized frames are not supported. Simply give up.
+ return;
+ }
+
+ HandleScope scope(isolate_);
+
+ // Parameters.
+ for (int i = 0; i < scope_info->ParameterCount(); ++i) {
+ // Shadowed parameters were not materialized.
+ Handle<String> name(scope_info->ParameterName(i));
+ if (ParameterIsShadowedByContextLocal(scope_info, name)) continue;
+
+ DCHECK(!frame_->GetParameter(i)->IsTheHole());
+ Handle<Object> value =
+ Object::GetPropertyOrElement(target, name).ToHandleChecked();
+ frame_->SetParameterValue(i, *value);
+ }
+
+ // Stack locals.
+ for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
+ if (scope_info->LocalIsSynthetic(i)) continue;
+ int index = scope_info->StackLocalIndex(i);
+ if (frame_->GetExpression(index)->IsTheHole()) continue;
+ Handle<Object> value =
+ Object::GetPropertyOrElement(
+ target, handle(scope_info->StackLocalName(i), isolate_))
+ .ToHandleChecked();
+ frame_->SetExpression(index, *value);
+ }
+}
+
+
+bool FrameInspector::ParameterIsShadowedByContextLocal(
+ Handle<ScopeInfo> info, Handle<String> parameter_name) {
+ VariableMode mode;
+ VariableLocation location;
+ InitializationFlag init_flag;
+ MaybeAssignedFlag maybe_assigned_flag;
+ return ScopeInfo::ContextSlotIndex(info, parameter_name, &mode, &location,
+ &init_flag, &maybe_assigned_flag) != -1;
+}
+
+
+SaveContext* DebugFrameHelper::FindSavedContextForFrame(
+ Isolate* isolate, JavaScriptFrame* frame) {
+ SaveContext* save = isolate->save_context();
+ while (save != NULL && !save->IsBelowFrame(frame)) {
+ save = save->prev();
+ }
+ DCHECK(save != NULL);
+ return save;
+}
+
+
+int DebugFrameHelper::FindIndexedNonNativeFrame(JavaScriptFrameIterator* it,
+ int index) {
+ int count = -1;
+ for (; !it->done(); it->Advance()) {
+ List<FrameSummary> frames(FLAG_max_inlining_levels + 1);
+ it->frame()->Summarize(&frames);
+ for (int i = frames.length() - 1; i >= 0; i--) {
+ // Omit functions from native and extension scripts.
+ if (!frames[i].function()->IsSubjectToDebugging()) continue;
+ if (++count == index) return i;
+ }
+ }
+ return -1;
+}
+
+
+} // namespace internal
+} // namespace v8
--- /dev/null
+// 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.
+
+#ifndef V8_DEBUG_DEBUG_FRAMES_H_
+#define V8_DEBUG_DEBUG_FRAMES_H_
+
+#include "src/deoptimizer.h"
+#include "src/frames.h"
+#include "src/isolate.h"
+#include "src/objects.h"
+
+namespace v8 {
+namespace internal {
+
+class FrameInspector {
+ public:
+ FrameInspector(JavaScriptFrame* frame, int inlined_jsframe_index,
+ Isolate* isolate);
+
+ ~FrameInspector();
+
+ int GetParametersCount();
+ int expression_count();
+ Object* GetFunction();
+ Object* GetParameter(int index);
+ Object* GetExpression(int index);
+ int GetSourcePosition();
+ bool IsConstructor();
+ Object* GetContext();
+
+ JavaScriptFrame* GetArgumentsFrame() { return frame_; }
+ void SetArgumentsFrame(JavaScriptFrame* frame);
+
+ void MaterializeStackLocals(Handle<JSObject> target,
+ Handle<ScopeInfo> scope_info);
+
+ void MaterializeStackLocals(Handle<JSObject> target,
+ Handle<JSFunction> function);
+
+ void UpdateStackLocalsFromMaterializedObject(Handle<JSObject> object,
+ Handle<ScopeInfo> scope_info);
+
+ private:
+ bool ParameterIsShadowedByContextLocal(Handle<ScopeInfo> info,
+ Handle<String> parameter_name);
+
+ JavaScriptFrame* frame_;
+ DeoptimizedFrameInfo* deoptimized_frame_;
+ Isolate* isolate_;
+ bool is_optimized_;
+ bool is_bottommost_;
+ bool has_adapted_arguments_;
+
+ DISALLOW_COPY_AND_ASSIGN(FrameInspector);
+};
+
+
+class DebugFrameHelper : public AllStatic {
+ public:
+ static SaveContext* FindSavedContextForFrame(Isolate* isolate,
+ JavaScriptFrame* frame);
+ // Advances the iterator to the frame that matches the index and returns the
+ // inlined frame index, or -1 if not found. Skips native JS functions.
+ static int FindIndexedNonNativeFrame(JavaScriptFrameIterator* it, int index);
+
+ // Helper functions for wrapping and unwrapping stack frame ids.
+ static Smi* WrapFrameId(StackFrame::Id id) {
+ DCHECK(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
+ return Smi::FromInt(id >> 2);
+ }
+
+ static StackFrame::Id UnwrapFrameId(int wrapped) {
+ return static_cast<StackFrame::Id>(wrapped << 2);
+ }
+};
+
+} // namespace internal
+} // namespace v8
+
+#endif // V8_DEBUG_DEBUG_FRAMES_H_
--- /dev/null
+// 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-scopes.h"
+
+#include "src/debug/debug.h"
+#include "src/globals.h"
+#include "src/parser.h"
+#include "src/scopes.h"
+
+namespace v8 {
+namespace internal {
+
+ScopeIterator::ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector,
+ bool ignore_nested_scopes)
+ : isolate_(isolate),
+ frame_inspector_(frame_inspector),
+ nested_scope_chain_(4),
+ seen_script_scope_(false),
+ failed_(false) {
+ if (!frame_inspector->GetContext()->IsContext() ||
+ !frame_inspector->GetFunction()->IsJSFunction()) {
+ // Optimized frame, context or function cannot be materialized. Give up.
+ return;
+ }
+
+ context_ = Handle<Context>(Context::cast(frame_inspector->GetContext()));
+
+ // Catch the case when the debugger stops in an internal function.
+ Handle<JSFunction> function = GetFunction();
+ Handle<SharedFunctionInfo> shared_info(function->shared());
+ Handle<ScopeInfo> scope_info(shared_info->scope_info());
+ if (shared_info->script() == isolate->heap()->undefined_value()) {
+ while (context_->closure() == *function) {
+ context_ = Handle<Context>(context_->previous(), isolate_);
+ }
+ return;
+ }
+
+ // Currently it takes too much time to find nested scopes due to script
+ // parsing. Sometimes we want to run the ScopeIterator as fast as possible
+ // (for example, while collecting async call stacks on every
+ // addEventListener call), even if we drop some nested scopes.
+ // Later we may optimize getting the nested scopes (cache the result?)
+ // and include nested scopes into the "fast" iteration case as well.
+
+ if (!ignore_nested_scopes && shared_info->HasDebugInfo()) {
+ // The source position at return is always the end of the function,
+ // which is not consistent with the current scope chain. Therefore all
+ // nested with, catch and block contexts are skipped, and we can only
+ // inspect the function scope.
+ // This can only happen if we set a break point inside right before the
+ // return, which requires a debug info to be available.
+ Handle<DebugInfo> debug_info(shared_info->GetDebugInfo());
+
+ // PC points to the instruction after the current one, possibly a break
+ // location as well. So the "- 1" to exclude it from the search.
+ Address call_pc = GetFrame()->pc() - 1;
+
+ // Find the break point where execution has stopped.
+ BreakLocation location =
+ BreakLocation::FromAddress(debug_info, ALL_BREAK_LOCATIONS, call_pc);
+
+ ignore_nested_scopes = location.IsReturn();
+ }
+
+ if (ignore_nested_scopes) {
+ if (scope_info->HasContext()) {
+ context_ = Handle<Context>(context_->declaration_context(), isolate_);
+ } else {
+ while (context_->closure() == *function) {
+ context_ = Handle<Context>(context_->previous(), isolate_);
+ }
+ }
+ if (scope_info->scope_type() == FUNCTION_SCOPE ||
+ scope_info->scope_type() == ARROW_SCOPE) {
+ nested_scope_chain_.Add(scope_info);
+ }
+ } else {
+ // Reparse the code and analyze the scopes.
+ Handle<Script> script(Script::cast(shared_info->script()));
+ Scope* scope = NULL;
+
+ // Check whether we are in global, eval or function code.
+ Handle<ScopeInfo> scope_info(shared_info->scope_info());
+ Zone zone;
+ if (scope_info->scope_type() != FUNCTION_SCOPE &&
+ scope_info->scope_type() != ARROW_SCOPE) {
+ // Global or eval code.
+ ParseInfo info(&zone, script);
+ if (scope_info->scope_type() == SCRIPT_SCOPE) {
+ info.set_global();
+ } else {
+ DCHECK(scope_info->scope_type() == EVAL_SCOPE);
+ info.set_eval();
+ info.set_context(Handle<Context>(function->context()));
+ }
+ if (Parser::ParseStatic(&info) && Scope::Analyze(&info)) {
+ scope = info.function()->scope();
+ }
+ RetrieveScopeChain(scope, shared_info);
+ } else {
+ // Function code
+ ParseInfo info(&zone, function);
+ if (Parser::ParseStatic(&info) && Scope::Analyze(&info)) {
+ scope = info.function()->scope();
+ }
+ RetrieveScopeChain(scope, shared_info);
+ }
+ }
+}
+
+
+ScopeIterator::ScopeIterator(Isolate* isolate, Handle<JSFunction> function)
+ : isolate_(isolate),
+ frame_inspector_(NULL),
+ context_(function->context()),
+ seen_script_scope_(false),
+ failed_(false) {
+ if (function->IsBuiltin()) context_ = Handle<Context>();
+}
+
+
+MUST_USE_RESULT MaybeHandle<JSObject> ScopeIterator::MaterializeScopeDetails() {
+ // Calculate the size of the result.
+ Handle<FixedArray> details =
+ isolate_->factory()->NewFixedArray(kScopeDetailsSize);
+ // Fill in scope details.
+ details->set(kScopeDetailsTypeIndex, Smi::FromInt(Type()));
+ Handle<JSObject> scope_object;
+ ASSIGN_RETURN_ON_EXCEPTION(isolate_, scope_object, ScopeObject(), JSObject);
+ details->set(kScopeDetailsObjectIndex, *scope_object);
+ return isolate_->factory()->NewJSArrayWithElements(details);
+}
+
+
+void ScopeIterator::Next() {
+ DCHECK(!failed_);
+ ScopeType scope_type = Type();
+ if (scope_type == ScopeTypeGlobal) {
+ // The global scope is always the last in the chain.
+ DCHECK(context_->IsNativeContext());
+ context_ = Handle<Context>();
+ return;
+ }
+ if (scope_type == ScopeTypeScript) {
+ seen_script_scope_ = true;
+ if (context_->IsScriptContext()) {
+ context_ = Handle<Context>(context_->previous(), isolate_);
+ }
+ if (!nested_scope_chain_.is_empty()) {
+ DCHECK_EQ(nested_scope_chain_.last()->scope_type(), SCRIPT_SCOPE);
+ nested_scope_chain_.RemoveLast();
+ DCHECK(nested_scope_chain_.is_empty());
+ }
+ CHECK(context_->IsNativeContext());
+ return;
+ }
+ if (nested_scope_chain_.is_empty()) {
+ context_ = Handle<Context>(context_->previous(), isolate_);
+ } else {
+ if (nested_scope_chain_.last()->HasContext()) {
+ DCHECK(context_->previous() != NULL);
+ context_ = Handle<Context>(context_->previous(), isolate_);
+ }
+ nested_scope_chain_.RemoveLast();
+ }
+}
+
+
+// Return the type of the current scope.
+ScopeIterator::ScopeType ScopeIterator::Type() {
+ DCHECK(!failed_);
+ if (!nested_scope_chain_.is_empty()) {
+ Handle<ScopeInfo> scope_info = nested_scope_chain_.last();
+ switch (scope_info->scope_type()) {
+ case FUNCTION_SCOPE:
+ case ARROW_SCOPE:
+ DCHECK(context_->IsFunctionContext() || !scope_info->HasContext());
+ return ScopeTypeLocal;
+ case MODULE_SCOPE:
+ DCHECK(context_->IsModuleContext());
+ return ScopeTypeModule;
+ case SCRIPT_SCOPE:
+ DCHECK(context_->IsScriptContext() || context_->IsNativeContext());
+ return ScopeTypeScript;
+ case WITH_SCOPE:
+ DCHECK(context_->IsWithContext());
+ return ScopeTypeWith;
+ case CATCH_SCOPE:
+ DCHECK(context_->IsCatchContext());
+ return ScopeTypeCatch;
+ case BLOCK_SCOPE:
+ DCHECK(!scope_info->HasContext() || context_->IsBlockContext());
+ return ScopeTypeBlock;
+ case EVAL_SCOPE:
+ UNREACHABLE();
+ }
+ }
+ if (context_->IsNativeContext()) {
+ DCHECK(context_->global_object()->IsGlobalObject());
+ // If we are at the native context and have not yet seen script scope,
+ // fake it.
+ return seen_script_scope_ ? ScopeTypeGlobal : ScopeTypeScript;
+ }
+ if (context_->IsFunctionContext()) {
+ return ScopeTypeClosure;
+ }
+ if (context_->IsCatchContext()) {
+ return ScopeTypeCatch;
+ }
+ if (context_->IsBlockContext()) {
+ return ScopeTypeBlock;
+ }
+ if (context_->IsModuleContext()) {
+ return ScopeTypeModule;
+ }
+ if (context_->IsScriptContext()) {
+ return ScopeTypeScript;
+ }
+ DCHECK(context_->IsWithContext());
+ return ScopeTypeWith;
+}
+
+
+MaybeHandle<JSObject> ScopeIterator::ScopeObject() {
+ DCHECK(!failed_);
+ switch (Type()) {
+ case ScopeIterator::ScopeTypeGlobal:
+ return Handle<JSObject>(CurrentContext()->global_object());
+ case ScopeIterator::ScopeTypeScript:
+ return MaterializeScriptScope();
+ case ScopeIterator::ScopeTypeLocal:
+ // Materialize the content of the local scope into a JSObject.
+ DCHECK(nested_scope_chain_.length() == 1);
+ return MaterializeLocalScope();
+ case ScopeIterator::ScopeTypeWith:
+ // Return the with object.
+ return Handle<JSObject>(JSObject::cast(CurrentContext()->extension()));
+ case ScopeIterator::ScopeTypeCatch:
+ return MaterializeCatchScope();
+ case ScopeIterator::ScopeTypeClosure:
+ // Materialize the content of the closure scope into a JSObject.
+ return MaterializeClosure();
+ case ScopeIterator::ScopeTypeBlock:
+ return MaterializeBlockScope();
+ case ScopeIterator::ScopeTypeModule:
+ return MaterializeModuleScope();
+ }
+ UNREACHABLE();
+ return Handle<JSObject>();
+}
+
+
+bool ScopeIterator::HasContext() {
+ ScopeType type = Type();
+ if (type == ScopeTypeBlock || type == ScopeTypeLocal) {
+ if (!nested_scope_chain_.is_empty()) {
+ return nested_scope_chain_.last()->HasContext();
+ }
+ }
+ return true;
+}
+
+
+bool ScopeIterator::SetVariableValue(Handle<String> variable_name,
+ Handle<Object> new_value) {
+ DCHECK(!failed_);
+ switch (Type()) {
+ case ScopeIterator::ScopeTypeGlobal:
+ break;
+ case ScopeIterator::ScopeTypeLocal:
+ return SetLocalVariableValue(variable_name, new_value);
+ case ScopeIterator::ScopeTypeWith:
+ break;
+ case ScopeIterator::ScopeTypeCatch:
+ return SetCatchVariableValue(variable_name, new_value);
+ case ScopeIterator::ScopeTypeClosure:
+ return SetClosureVariableValue(variable_name, new_value);
+ case ScopeIterator::ScopeTypeScript:
+ return SetScriptVariableValue(variable_name, new_value);
+ case ScopeIterator::ScopeTypeBlock:
+ return SetBlockVariableValue(variable_name, new_value);
+ case ScopeIterator::ScopeTypeModule:
+ // TODO(2399): should we implement it?
+ break;
+ }
+ return false;
+}
+
+
+Handle<ScopeInfo> ScopeIterator::CurrentScopeInfo() {
+ DCHECK(!failed_);
+ if (!nested_scope_chain_.is_empty()) {
+ return nested_scope_chain_.last();
+ } else if (context_->IsBlockContext()) {
+ return Handle<ScopeInfo>(ScopeInfo::cast(context_->extension()));
+ } else if (context_->IsFunctionContext()) {
+ return Handle<ScopeInfo>(context_->closure()->shared()->scope_info());
+ }
+ return Handle<ScopeInfo>::null();
+}
+
+
+Handle<Context> ScopeIterator::CurrentContext() {
+ DCHECK(!failed_);
+ if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript ||
+ nested_scope_chain_.is_empty()) {
+ return context_;
+ } else if (nested_scope_chain_.last()->HasContext()) {
+ return context_;
+ } else {
+ return Handle<Context>();
+ }
+}
+
+#ifdef DEBUG
+// Debug print of the content of the current scope.
+void ScopeIterator::DebugPrint() {
+ OFStream os(stdout);
+ DCHECK(!failed_);
+ switch (Type()) {
+ case ScopeIterator::ScopeTypeGlobal:
+ os << "Global:\n";
+ CurrentContext()->Print(os);
+ break;
+
+ case ScopeIterator::ScopeTypeLocal: {
+ os << "Local:\n";
+ GetFunction()->shared()->scope_info()->Print();
+ if (!CurrentContext().is_null()) {
+ CurrentContext()->Print(os);
+ if (CurrentContext()->has_extension()) {
+ Handle<Object> extension(CurrentContext()->extension(), isolate_);
+ if (extension->IsJSContextExtensionObject()) {
+ extension->Print(os);
+ }
+ }
+ }
+ break;
+ }
+
+ case ScopeIterator::ScopeTypeWith:
+ os << "With:\n";
+ CurrentContext()->extension()->Print(os);
+ break;
+
+ case ScopeIterator::ScopeTypeCatch:
+ os << "Catch:\n";
+ CurrentContext()->extension()->Print(os);
+ CurrentContext()->get(Context::THROWN_OBJECT_INDEX)->Print(os);
+ break;
+
+ case ScopeIterator::ScopeTypeClosure:
+ os << "Closure:\n";
+ CurrentContext()->Print(os);
+ if (CurrentContext()->has_extension()) {
+ Handle<Object> extension(CurrentContext()->extension(), isolate_);
+ if (extension->IsJSContextExtensionObject()) {
+ extension->Print(os);
+ }
+ }
+ break;
+
+ case ScopeIterator::ScopeTypeScript:
+ os << "Script:\n";
+ CurrentContext()
+ ->global_object()
+ ->native_context()
+ ->script_context_table()
+ ->Print(os);
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+ PrintF("\n");
+}
+#endif
+
+
+void ScopeIterator::RetrieveScopeChain(Scope* scope,
+ Handle<SharedFunctionInfo> shared_info) {
+ if (scope != NULL) {
+ int source_position = frame_inspector_->GetSourcePosition();
+ scope->GetNestedScopeChain(isolate_, &nested_scope_chain_, source_position);
+ } else {
+ // A failed reparse indicates that the preparser has diverged from the
+ // parser or that the preparse data given to the initial parse has been
+ // faulty. We fail in debug mode but in release mode we only provide the
+ // information we get from the context chain but nothing about
+ // completely stack allocated scopes or stack allocated locals.
+ // Or it could be due to stack overflow.
+ DCHECK(isolate_->has_pending_exception());
+ failed_ = true;
+ }
+}
+
+
+MaybeHandle<JSObject> ScopeIterator::MaterializeScriptScope() {
+ Handle<GlobalObject> global(CurrentContext()->global_object());
+ Handle<ScriptContextTable> script_contexts(
+ global->native_context()->script_context_table());
+
+ Handle<JSObject> script_scope =
+ isolate_->factory()->NewJSObject(isolate_->object_function());
+
+ for (int context_index = 0; context_index < script_contexts->used();
+ context_index++) {
+ Handle<Context> context =
+ ScriptContextTable::GetContext(script_contexts, context_index);
+ Handle<ScopeInfo> scope_info(ScopeInfo::cast(context->extension()));
+ CopyContextLocalsToScopeObject(scope_info, context, script_scope);
+ }
+ return script_scope;
+}
+
+
+MaybeHandle<JSObject> ScopeIterator::MaterializeLocalScope() {
+ Handle<JSFunction> function = GetFunction();
+
+ Handle<JSObject> local_scope =
+ isolate_->factory()->NewJSObject(isolate_->object_function());
+ frame_inspector_->MaterializeStackLocals(local_scope, function);
+
+ Handle<Context> frame_context(Context::cast(frame_inspector_->GetContext()));
+
+ HandleScope scope(isolate_);
+ Handle<SharedFunctionInfo> shared(function->shared());
+ Handle<ScopeInfo> scope_info(shared->scope_info());
+
+ if (!scope_info->HasContext()) return local_scope;
+
+ // Third fill all context locals.
+ Handle<Context> function_context(frame_context->declaration_context());
+ CopyContextLocalsToScopeObject(scope_info, function_context, local_scope);
+
+ // Finally copy any properties from the function context extension.
+ // These will be variables introduced by eval.
+ if (function_context->closure() == *function) {
+ if (function_context->has_extension() &&
+ !function_context->IsNativeContext()) {
+ Handle<JSObject> ext(JSObject::cast(function_context->extension()));
+ Handle<FixedArray> keys;
+ ASSIGN_RETURN_ON_EXCEPTION(
+ isolate_, keys, JSReceiver::GetKeys(ext, JSReceiver::INCLUDE_PROTOS),
+ JSObject);
+
+ for (int i = 0; i < keys->length(); i++) {
+ // Names of variables introduced by eval are strings.
+ DCHECK(keys->get(i)->IsString());
+ Handle<String> key(String::cast(keys->get(i)));
+ Handle<Object> value;
+ ASSIGN_RETURN_ON_EXCEPTION(
+ isolate_, value, Object::GetPropertyOrElement(ext, key), JSObject);
+ RETURN_ON_EXCEPTION(isolate_,
+ Runtime::SetObjectProperty(isolate_, local_scope,
+ key, value, SLOPPY),
+ JSObject);
+ }
+ }
+ }
+
+ return local_scope;
+}
+
+
+// Create a plain JSObject which materializes the closure content for the
+// context.
+Handle<JSObject> ScopeIterator::MaterializeClosure() {
+ Handle<Context> context = CurrentContext();
+ DCHECK(context->IsFunctionContext());
+
+ Handle<SharedFunctionInfo> shared(context->closure()->shared());
+ Handle<ScopeInfo> scope_info(shared->scope_info());
+
+ // Allocate and initialize a JSObject with all the content of this function
+ // closure.
+ Handle<JSObject> closure_scope =
+ isolate_->factory()->NewJSObject(isolate_->object_function());
+
+ // Fill all context locals to the context extension.
+ CopyContextLocalsToScopeObject(scope_info, context, closure_scope);
+
+ // Finally copy any properties from the function context extension. This will
+ // be variables introduced by eval.
+ if (context->has_extension()) {
+ Handle<JSObject> ext(JSObject::cast(context->extension()));
+ DCHECK(ext->IsJSContextExtensionObject());
+ Handle<FixedArray> keys =
+ JSReceiver::GetKeys(ext, JSReceiver::OWN_ONLY).ToHandleChecked();
+
+ for (int i = 0; i < keys->length(); i++) {
+ HandleScope scope(isolate_);
+ // Names of variables introduced by eval are strings.
+ DCHECK(keys->get(i)->IsString());
+ Handle<String> key(String::cast(keys->get(i)));
+ Handle<Object> value = Object::GetProperty(ext, key).ToHandleChecked();
+ JSObject::SetOwnPropertyIgnoreAttributes(closure_scope, key, value, NONE)
+ .Check();
+ }
+ }
+
+ return closure_scope;
+}
+
+
+// Create a plain JSObject which materializes the scope for the specified
+// catch context.
+Handle<JSObject> ScopeIterator::MaterializeCatchScope() {
+ Handle<Context> context = CurrentContext();
+ DCHECK(context->IsCatchContext());
+ Handle<String> name(String::cast(context->extension()));
+ Handle<Object> thrown_object(context->get(Context::THROWN_OBJECT_INDEX),
+ isolate_);
+ Handle<JSObject> catch_scope =
+ isolate_->factory()->NewJSObject(isolate_->object_function());
+ JSObject::SetOwnPropertyIgnoreAttributes(catch_scope, name, thrown_object,
+ NONE)
+ .Check();
+ return catch_scope;
+}
+
+
+// Create a plain JSObject which materializes the block scope for the specified
+// block context.
+Handle<JSObject> ScopeIterator::MaterializeBlockScope() {
+ Handle<JSObject> block_scope =
+ isolate_->factory()->NewJSObject(isolate_->object_function());
+
+ Handle<Context> context = Handle<Context>::null();
+ if (!nested_scope_chain_.is_empty()) {
+ Handle<ScopeInfo> scope_info = nested_scope_chain_.last();
+ frame_inspector_->MaterializeStackLocals(block_scope, scope_info);
+ if (scope_info->HasContext()) context = CurrentContext();
+ } else {
+ context = CurrentContext();
+ }
+
+ if (!context.is_null()) {
+ Handle<ScopeInfo> scope_info_from_context(
+ ScopeInfo::cast(context->extension()));
+ // Fill all context locals.
+ CopyContextLocalsToScopeObject(scope_info_from_context, context,
+ block_scope);
+ }
+ return block_scope;
+}
+
+
+// Create a plain JSObject which materializes the module scope for the specified
+// module context.
+MaybeHandle<JSObject> ScopeIterator::MaterializeModuleScope() {
+ Handle<Context> context = CurrentContext();
+ DCHECK(context->IsModuleContext());
+ Handle<ScopeInfo> scope_info(ScopeInfo::cast(context->extension()));
+
+ // Allocate and initialize a JSObject with all the members of the debugged
+ // module.
+ Handle<JSObject> module_scope =
+ isolate_->factory()->NewJSObject(isolate_->object_function());
+
+ // Fill all context locals.
+ CopyContextLocalsToScopeObject(scope_info, context, module_scope);
+
+ return module_scope;
+}
+
+
+// Set the context local variable value.
+bool ScopeIterator::SetContextLocalValue(Handle<ScopeInfo> scope_info,
+ Handle<Context> context,
+ Handle<String> variable_name,
+ Handle<Object> new_value) {
+ for (int i = 0; i < scope_info->ContextLocalCount(); i++) {
+ Handle<String> next_name(scope_info->ContextLocalName(i));
+ if (String::Equals(variable_name, next_name)) {
+ VariableMode mode;
+ VariableLocation location;
+ InitializationFlag init_flag;
+ MaybeAssignedFlag maybe_assigned_flag;
+ int context_index =
+ ScopeInfo::ContextSlotIndex(scope_info, next_name, &mode, &location,
+ &init_flag, &maybe_assigned_flag);
+ context->set(context_index, *new_value);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+bool ScopeIterator::SetLocalVariableValue(Handle<String> variable_name,
+ Handle<Object> new_value) {
+ JavaScriptFrame* frame = GetFrame();
+ // Optimized frames are not supported.
+ if (frame->is_optimized()) return false;
+
+ Handle<JSFunction> function(frame->function());
+ Handle<SharedFunctionInfo> shared(function->shared());
+ Handle<ScopeInfo> scope_info(shared->scope_info());
+
+ bool default_result = false;
+
+ // Parameters.
+ for (int i = 0; i < scope_info->ParameterCount(); ++i) {
+ HandleScope scope(isolate_);
+ if (String::Equals(handle(scope_info->ParameterName(i)), variable_name)) {
+ frame->SetParameterValue(i, *new_value);
+ // Argument might be shadowed in heap context, don't stop here.
+ default_result = true;
+ }
+ }
+
+ // Stack locals.
+ for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
+ HandleScope scope(isolate_);
+ if (String::Equals(handle(scope_info->StackLocalName(i)), variable_name)) {
+ frame->SetExpression(scope_info->StackLocalIndex(i), *new_value);
+ return true;
+ }
+ }
+
+ if (scope_info->HasContext()) {
+ // Context locals.
+ Handle<Context> frame_context(Context::cast(frame->context()));
+ Handle<Context> function_context(frame_context->declaration_context());
+ if (SetContextLocalValue(scope_info, function_context, variable_name,
+ new_value)) {
+ return true;
+ }
+
+ // Function context extension. These are variables introduced by eval.
+ if (function_context->closure() == *function) {
+ if (function_context->has_extension() &&
+ !function_context->IsNativeContext()) {
+ Handle<JSObject> ext(JSObject::cast(function_context->extension()));
+
+ Maybe<bool> maybe = JSReceiver::HasProperty(ext, variable_name);
+ DCHECK(maybe.IsJust());
+ if (maybe.FromJust()) {
+ // We don't expect this to do anything except replacing
+ // property value.
+ Runtime::SetObjectProperty(isolate_, ext, variable_name, new_value,
+ SLOPPY)
+ .Assert();
+ return true;
+ }
+ }
+ }
+ }
+
+ return default_result;
+}
+
+
+bool ScopeIterator::SetBlockVariableValue(Handle<String> variable_name,
+ Handle<Object> new_value) {
+ Handle<ScopeInfo> scope_info = CurrentScopeInfo();
+ JavaScriptFrame* frame = GetFrame();
+
+ for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
+ HandleScope scope(isolate_);
+ if (String::Equals(handle(scope_info->StackLocalName(i)), variable_name)) {
+ frame->SetExpression(scope_info->StackLocalIndex(i), *new_value);
+ return true;
+ }
+ }
+
+ if (HasContext()) {
+ return SetContextLocalValue(scope_info, CurrentContext(), variable_name,
+ new_value);
+ }
+ return false;
+}
+
+
+// This method copies structure of MaterializeClosure method above.
+bool ScopeIterator::SetClosureVariableValue(Handle<String> variable_name,
+ Handle<Object> new_value) {
+ Handle<Context> context = CurrentContext();
+ DCHECK(context->IsFunctionContext());
+
+ // Context locals to the context extension.
+ Handle<SharedFunctionInfo> shared(context->closure()->shared());
+ Handle<ScopeInfo> scope_info(shared->scope_info());
+ if (SetContextLocalValue(scope_info, context, variable_name, new_value)) {
+ return true;
+ }
+
+ // Properties from the function context extension. This will
+ // be variables introduced by eval.
+ if (context->has_extension()) {
+ Handle<JSObject> ext(JSObject::cast(context->extension()));
+ DCHECK(ext->IsJSContextExtensionObject());
+ Maybe<bool> maybe = JSReceiver::HasOwnProperty(ext, variable_name);
+ DCHECK(maybe.IsJust());
+ if (maybe.FromJust()) {
+ // We don't expect this to do anything except replacing property value.
+ JSObject::SetOwnPropertyIgnoreAttributes(ext, variable_name, new_value,
+ NONE)
+ .Check();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+bool ScopeIterator::SetScriptVariableValue(Handle<String> variable_name,
+ Handle<Object> new_value) {
+ Handle<Context> context = CurrentContext();
+ Handle<ScriptContextTable> script_contexts(
+ context->global_object()->native_context()->script_context_table());
+ ScriptContextTable::LookupResult lookup_result;
+ if (ScriptContextTable::Lookup(script_contexts, variable_name,
+ &lookup_result)) {
+ Handle<Context> script_context = ScriptContextTable::GetContext(
+ script_contexts, lookup_result.context_index);
+ script_context->set(lookup_result.slot_index, *new_value);
+ return true;
+ }
+
+ return false;
+}
+
+
+bool ScopeIterator::SetCatchVariableValue(Handle<String> variable_name,
+ Handle<Object> new_value) {
+ Handle<Context> context = CurrentContext();
+ DCHECK(context->IsCatchContext());
+ Handle<String> name(String::cast(context->extension()));
+ if (!String::Equals(name, variable_name)) {
+ return false;
+ }
+ context->set(Context::THROWN_OBJECT_INDEX, *new_value);
+ return true;
+}
+
+
+void ScopeIterator::CopyContextLocalsToScopeObject(
+ Handle<ScopeInfo> scope_info, Handle<Context> context,
+ Handle<JSObject> scope_object) {
+ Isolate* isolate = scope_info->GetIsolate();
+ int local_count = scope_info->ContextLocalCount();
+ if (local_count == 0) return;
+ // Fill all context locals to the context extension.
+ int first_context_var = scope_info->StackLocalCount();
+ int start = scope_info->ContextLocalNameEntriesIndex();
+ for (int i = 0; i < local_count; ++i) {
+ if (scope_info->LocalIsSynthetic(first_context_var + i)) continue;
+ int context_index = Context::MIN_CONTEXT_SLOTS + i;
+ Handle<Object> value = Handle<Object>(context->get(context_index), isolate);
+ // Reflect variables under TDZ as undefined in scope object.
+ if (value->IsTheHole()) continue;
+ // This should always succeed.
+ // TODO(verwaest): Use AddDataProperty instead.
+ JSObject::SetOwnPropertyIgnoreAttributes(
+ scope_object, handle(String::cast(scope_info->get(i + start))), value,
+ ::NONE)
+ .Check();
+ }
+}
+
+} // namespace internal
+} // namespace v8
--- /dev/null
+// 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.
+
+#ifndef V8_DEBUG_DEBUG_SCOPES_H_
+#define V8_DEBUG_DEBUG_SCOPES_H_
+
+#include "src/debug/debug-frames.h"
+#include "src/frames.h"
+
+namespace v8 {
+namespace internal {
+
+// Iterate over the actual scopes visible from a stack frame or from a closure.
+// The iteration proceeds from the innermost visible nested scope outwards.
+// All scopes are backed by an actual context except the local scope,
+// which is inserted "artificially" in the context chain.
+class ScopeIterator {
+ public:
+ enum ScopeType {
+ ScopeTypeGlobal = 0,
+ ScopeTypeLocal,
+ ScopeTypeWith,
+ ScopeTypeClosure,
+ ScopeTypeCatch,
+ ScopeTypeBlock,
+ ScopeTypeScript,
+ ScopeTypeModule
+ };
+
+ static const int kScopeDetailsTypeIndex = 0;
+ static const int kScopeDetailsObjectIndex = 1;
+ static const int kScopeDetailsSize = 2;
+
+ ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector,
+ bool ignore_nested_scopes = false);
+
+ ScopeIterator(Isolate* isolate, Handle<JSFunction> function);
+
+ MUST_USE_RESULT MaybeHandle<JSObject> MaterializeScopeDetails();
+
+ // More scopes?
+ bool Done() {
+ DCHECK(!failed_);
+ return context_.is_null();
+ }
+
+ bool Failed() { return failed_; }
+
+ // Move to the next scope.
+ void Next();
+
+ // Return the type of the current scope.
+ ScopeType Type();
+
+ // Return the JavaScript object with the content of the current scope.
+ MaybeHandle<JSObject> ScopeObject();
+
+ bool HasContext();
+
+ // Set variable value and return true on success.
+ bool SetVariableValue(Handle<String> variable_name, Handle<Object> new_value);
+
+ Handle<ScopeInfo> CurrentScopeInfo();
+
+ // Return the context for this scope. For the local context there might not
+ // be an actual context.
+ Handle<Context> CurrentContext();
+
+#ifdef DEBUG
+ // Debug print of the content of the current scope.
+ void DebugPrint();
+#endif
+
+ private:
+ Isolate* isolate_;
+ FrameInspector* const frame_inspector_;
+ Handle<Context> context_;
+ List<Handle<ScopeInfo> > nested_scope_chain_;
+ bool seen_script_scope_;
+ bool failed_;
+
+ inline JavaScriptFrame* GetFrame() {
+ return frame_inspector_->GetArgumentsFrame();
+ }
+
+ inline Handle<JSFunction> GetFunction() {
+ return Handle<JSFunction>(
+ JSFunction::cast(frame_inspector_->GetFunction()));
+ }
+
+ void RetrieveScopeChain(Scope* scope, Handle<SharedFunctionInfo> shared_info);
+
+ MUST_USE_RESULT MaybeHandle<JSObject> MaterializeScriptScope();
+ MUST_USE_RESULT MaybeHandle<JSObject> MaterializeLocalScope();
+ MUST_USE_RESULT MaybeHandle<JSObject> MaterializeModuleScope();
+ Handle<JSObject> MaterializeClosure();
+ Handle<JSObject> MaterializeCatchScope();
+ Handle<JSObject> MaterializeBlockScope();
+
+ bool SetLocalVariableValue(Handle<String> variable_name,
+ Handle<Object> new_value);
+ bool SetBlockVariableValue(Handle<String> variable_name,
+ Handle<Object> new_value);
+ bool SetClosureVariableValue(Handle<String> variable_name,
+ Handle<Object> new_value);
+ bool SetScriptVariableValue(Handle<String> variable_name,
+ Handle<Object> new_value);
+ bool SetCatchVariableValue(Handle<String> variable_name,
+ Handle<Object> new_value);
+ bool SetContextLocalValue(Handle<ScopeInfo> scope_info,
+ Handle<Context> context,
+ Handle<String> variable_name,
+ Handle<Object> new_value);
+
+ void CopyContextLocalsToScopeObject(Handle<ScopeInfo> scope_info,
+ Handle<Context> context,
+ Handle<JSObject> scope_object);
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
+};
+
+} // namespace internal
+} // namespace v8
+
+#endif // V8_DEBUG_DEBUG_SCOPES_H_
class CodeStub;
class Context;
class Debug;
-class Debugger;
class DebugInfo;
class Descriptor;
class DescriptorArray;
class Debug;
-class Debugger;
class PromiseOnStack;
class Redirection;
class Simulator;
FunctionKind function_kind();
- // Copies all the context locals into an object used to materialize a scope.
- static void CopyContextLocalsToScopeObject(Handle<ScopeInfo> scope_info,
- Handle<Context> context,
- Handle<JSObject> scope_object);
-
-
static Handle<ScopeInfo> Create(Isolate* isolate, Zone* zone, Scope* scope);
static Handle<ScopeInfo> CreateGlobalThisBinding(Isolate* isolate);
class ContextLocalInitFlag: public BitField<InitializationFlag, 3, 1> {};
class ContextLocalMaybeAssignedFlag
: public BitField<MaybeAssignedFlag, 4, 1> {};
+
+ friend class ScopeIterator;
};
#include "src/v8.h"
-#include "src/accessors.h"
#include "src/arguments.h"
-#include "src/compiler.h"
#include "src/debug/debug.h"
-#include "src/deoptimizer.h"
-#include "src/parser.h"
+#include "src/debug/debug-evaluate.h"
+#include "src/debug/debug-frames.h"
+#include "src/debug/debug-scopes.h"
#include "src/runtime/runtime.h"
#include "src/runtime/runtime-utils.h"
}
-// Helper functions for wrapping and unwrapping stack frame ids.
-static Smi* WrapFrameId(StackFrame::Id id) {
- DCHECK(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
- return Smi::FromInt(id >> 2);
-}
-
-
-static StackFrame::Id UnwrapFrameId(int wrapped) {
- return static_cast<StackFrame::Id>(wrapped << 2);
-}
-
-
// Adds a JavaScript function as a debug event listener.
// args[0]: debug event listener function to set or null or undefined for
// clearing the event listener function
}
-class FrameInspector {
- public:
- FrameInspector(JavaScriptFrame* frame, int inlined_jsframe_index,
- Isolate* isolate)
- : frame_(frame), deoptimized_frame_(NULL), isolate_(isolate) {
- has_adapted_arguments_ = frame_->has_adapted_arguments();
- is_bottommost_ = inlined_jsframe_index == 0;
- is_optimized_ = frame_->is_optimized();
- // Calculate the deoptimized frame.
- if (frame->is_optimized()) {
- // TODO(turbofan): Revisit once we support deoptimization.
- if (frame->LookupCode()->is_turbofanned() &&
- frame->function()->shared()->asm_function() &&
- !FLAG_turbo_asm_deoptimization) {
- is_optimized_ = false;
- return;
- }
-
- deoptimized_frame_ = Deoptimizer::DebuggerInspectableFrame(
- frame, inlined_jsframe_index, isolate);
- }
- }
-
- ~FrameInspector() {
- // Get rid of the calculated deoptimized frame if any.
- if (deoptimized_frame_ != NULL) {
- Deoptimizer::DeleteDebuggerInspectableFrame(deoptimized_frame_, isolate_);
- }
- }
-
- int GetParametersCount() {
- return is_optimized_ ? deoptimized_frame_->parameters_count()
- : frame_->ComputeParametersCount();
- }
- int expression_count() { return deoptimized_frame_->expression_count(); }
- Object* GetFunction() {
- return is_optimized_ ? deoptimized_frame_->GetFunction()
- : frame_->function();
- }
- Object* GetParameter(int index) {
- return is_optimized_ ? deoptimized_frame_->GetParameter(index)
- : frame_->GetParameter(index);
- }
- Object* GetExpression(int index) {
- // TODO(turbofan): Revisit once we support deoptimization.
- if (frame_->LookupCode()->is_turbofanned() &&
- frame_->function()->shared()->asm_function() &&
- !FLAG_turbo_asm_deoptimization) {
- return isolate_->heap()->undefined_value();
- }
- return is_optimized_ ? deoptimized_frame_->GetExpression(index)
- : frame_->GetExpression(index);
- }
- int GetSourcePosition() {
- return is_optimized_ ? deoptimized_frame_->GetSourcePosition()
- : frame_->LookupCode()->SourcePosition(frame_->pc());
- }
- bool IsConstructor() {
- return is_optimized_ && !is_bottommost_
- ? deoptimized_frame_->HasConstructStub()
- : frame_->IsConstructor();
- }
- Object* GetContext() {
- return is_optimized_ ? deoptimized_frame_->GetContext() : frame_->context();
- }
- JavaScriptFrame* GetArgumentsFrame() { return frame_; }
-
- // To inspect all the provided arguments the frame might need to be
- // replaced with the arguments frame.
- void SetArgumentsFrame(JavaScriptFrame* frame) {
- DCHECK(has_adapted_arguments_);
- frame_ = frame;
- is_optimized_ = frame_->is_optimized();
- DCHECK(!is_optimized_);
- }
-
- private:
- JavaScriptFrame* frame_;
- DeoptimizedFrameInfo* deoptimized_frame_;
- Isolate* isolate_;
- bool is_optimized_;
- bool is_bottommost_;
- bool has_adapted_arguments_;
-
- DISALLOW_COPY_AND_ASSIGN(FrameInspector);
-};
-
-
static const int kFrameDetailsFrameIdIndex = 0;
static const int kFrameDetailsReceiverIndex = 1;
static const int kFrameDetailsFunctionIndex = 2;
static const int kFrameDetailsFirstDynamicIndex = 9;
-static SaveContext* FindSavedContextForFrame(Isolate* isolate,
- JavaScriptFrame* frame) {
- SaveContext* save = isolate->save_context();
- while (save != NULL && !save->IsBelowFrame(frame)) {
- save = save->prev();
- }
- DCHECK(save != NULL);
- return save;
-}
-
-
-// Advances the iterator to the frame that matches the index and returns the
-// inlined frame index, or -1 if not found. Skips native JS functions.
-int Runtime::FindIndexedNonNativeFrame(JavaScriptFrameIterator* it, int index) {
- int count = -1;
- for (; !it->done(); it->Advance()) {
- List<FrameSummary> frames(FLAG_max_inlining_levels + 1);
- it->frame()->Summarize(&frames);
- for (int i = frames.length() - 1; i >= 0; i--) {
- // Omit functions from native and extension scripts.
- if (!frames[i].function()->IsSubjectToDebugging()) continue;
- if (++count == index) return i;
- }
- }
- return -1;
-}
-
-
// Return an array with frame details
// args[0]: number: break id
// args[1]: number: frame index
JavaScriptFrameIterator it(isolate, id);
// Inlined frame index in optimized frame, starting from outer function.
- int inlined_jsframe_index = Runtime::FindIndexedNonNativeFrame(&it, index);
+ int inlined_jsframe_index =
+ DebugFrameHelper::FindIndexedNonNativeFrame(&it, index);
if (inlined_jsframe_index == -1) return heap->undefined_value();
FrameInspector frame_inspector(it.frame(), inlined_jsframe_index, isolate);
// Traverse the saved contexts chain to find the active context for the
// selected frame.
- SaveContext* save = FindSavedContextForFrame(isolate, it.frame());
+ SaveContext* save =
+ DebugFrameHelper::FindSavedContextForFrame(isolate, it.frame());
// Get the frame id.
- Handle<Object> frame_id(WrapFrameId(it.frame()->id()), isolate);
+ Handle<Object> frame_id(DebugFrameHelper::WrapFrameId(it.frame()->id()),
+ isolate);
// Find source position in unoptimized code.
int position = frame_inspector.GetSourcePosition();
}
-static bool ParameterIsShadowedByContextLocal(Handle<ScopeInfo> info,
- Handle<String> parameter_name) {
- VariableMode mode;
- VariableLocation location;
- InitializationFlag init_flag;
- MaybeAssignedFlag maybe_assigned_flag;
- return ScopeInfo::ContextSlotIndex(info, parameter_name, &mode, &location,
- &init_flag, &maybe_assigned_flag) != -1;
-}
-
-
-static Handle<Context> MaterializeReceiver(Isolate* isolate,
- Handle<Context> target,
- Handle<JSFunction> function,
- JavaScriptFrame* frame) {
- 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);
-}
-
-
-// Create a plain JSObject which materializes the local scope for the specified
-// frame.
-static void MaterializeStackLocalsWithFrameInspector(
- Isolate* isolate, Handle<JSObject> target, Handle<ScopeInfo> scope_info,
- FrameInspector* frame_inspector) {
- // First fill all parameters.
- for (int i = 0; i < scope_info->ParameterCount(); ++i) {
- // Do not materialize the parameter if it is shadowed by a context local.
- Handle<String> name(scope_info->ParameterName(i));
- if (ParameterIsShadowedByContextLocal(scope_info, name)) continue;
-
- DCHECK_NOT_NULL(frame_inspector);
-
- HandleScope scope(isolate);
- Handle<Object> value(i < frame_inspector->GetParametersCount()
- ? frame_inspector->GetParameter(i)
- : isolate->heap()->undefined_value(),
- isolate);
- DCHECK(!value->IsTheHole());
-
- JSObject::SetOwnPropertyIgnoreAttributes(target, name, value, NONE).Check();
- }
-
- // Second fill all stack locals.
- for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
- if (scope_info->LocalIsSynthetic(i)) continue;
- Handle<String> name(scope_info->StackLocalName(i));
- Handle<Object> value(
- frame_inspector->GetExpression(scope_info->StackLocalIndex(i)),
- isolate);
- if (value->IsTheHole()) {
- value = isolate->factory()->undefined_value();
- }
-
- JSObject::SetOwnPropertyIgnoreAttributes(target, name, value, NONE).Check();
- }
-}
-
-static void MaterializeStackLocalsWithFrameInspector(
- Isolate* isolate, Handle<JSObject> target, Handle<JSFunction> function,
- FrameInspector* frame_inspector) {
- Handle<SharedFunctionInfo> shared(function->shared());
- Handle<ScopeInfo> scope_info(shared->scope_info());
-
- MaterializeStackLocalsWithFrameInspector(isolate, target, scope_info,
- frame_inspector);
-}
-
-
-static void UpdateStackLocalsFromMaterializedObject(
- Isolate* isolate, Handle<JSObject> target, Handle<ScopeInfo> scope_info,
- JavaScriptFrame* frame, int inlined_jsframe_index) {
- if (inlined_jsframe_index != 0 || frame->is_optimized()) {
- // Optimized frames are not supported. Simply give up.
- return;
- }
-
- // Parameters.
- for (int i = 0; i < scope_info->ParameterCount(); ++i) {
- // Shadowed parameters were not materialized.
- Handle<String> name(scope_info->ParameterName(i));
- if (ParameterIsShadowedByContextLocal(scope_info, name)) continue;
-
- DCHECK(!frame->GetParameter(i)->IsTheHole());
- HandleScope scope(isolate);
- Handle<Object> value =
- Object::GetPropertyOrElement(target, name).ToHandleChecked();
- frame->SetParameterValue(i, *value);
- }
-
- // Stack locals.
- for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
- if (scope_info->LocalIsSynthetic(i)) continue;
- int index = scope_info->StackLocalIndex(i);
- if (frame->GetExpression(index)->IsTheHole()) continue;
- HandleScope scope(isolate);
- Handle<Object> value = Object::GetPropertyOrElement(
- target, handle(scope_info->StackLocalName(i),
- isolate)).ToHandleChecked();
- frame->SetExpression(index, *value);
- }
-}
-
-
-MUST_USE_RESULT static MaybeHandle<JSObject> MaterializeLocalContext(
- Isolate* isolate, Handle<JSObject> target, Handle<JSFunction> function,
- Handle<Context> frame_context) {
- HandleScope scope(isolate);
- Handle<SharedFunctionInfo> shared(function->shared());
- Handle<ScopeInfo> scope_info(shared->scope_info());
-
- if (!scope_info->HasContext()) return target;
-
- // Third fill all context locals.
- Handle<Context> function_context(frame_context->declaration_context());
- ScopeInfo::CopyContextLocalsToScopeObject(scope_info, function_context,
- target);
-
- // Finally copy any properties from the function context extension.
- // These will be variables introduced by eval.
- if (function_context->closure() == *function) {
- if (function_context->has_extension() &&
- !function_context->IsNativeContext()) {
- Handle<JSObject> ext(JSObject::cast(function_context->extension()));
- Handle<FixedArray> keys;
- ASSIGN_RETURN_ON_EXCEPTION(
- isolate, keys, JSReceiver::GetKeys(ext, JSReceiver::INCLUDE_PROTOS),
- JSObject);
-
- for (int i = 0; i < keys->length(); i++) {
- // Names of variables introduced by eval are strings.
- DCHECK(keys->get(i)->IsString());
- Handle<String> key(String::cast(keys->get(i)));
- Handle<Object> value;
- ASSIGN_RETURN_ON_EXCEPTION(
- isolate, value, Object::GetPropertyOrElement(ext, key), JSObject);
- RETURN_ON_EXCEPTION(isolate, Runtime::SetObjectProperty(
- isolate, target, key, value, SLOPPY),
- JSObject);
- }
- }
- }
-
- return target;
-}
-
-
-MUST_USE_RESULT static MaybeHandle<JSObject> MaterializeScriptScope(
- Handle<GlobalObject> global) {
- Isolate* isolate = global->GetIsolate();
- Handle<ScriptContextTable> script_contexts(
- global->native_context()->script_context_table());
-
- Handle<JSObject> script_scope =
- isolate->factory()->NewJSObject(isolate->object_function());
-
- for (int context_index = 0; context_index < script_contexts->used();
- context_index++) {
- Handle<Context> context =
- ScriptContextTable::GetContext(script_contexts, context_index);
- Handle<ScopeInfo> scope_info(ScopeInfo::cast(context->extension()));
- ScopeInfo::CopyContextLocalsToScopeObject(scope_info, context,
- script_scope);
- }
- return script_scope;
-}
-
-
-MUST_USE_RESULT static MaybeHandle<JSObject> MaterializeLocalScope(
- Isolate* isolate, FrameInspector* frame_inspector) {
- Handle<JSFunction> function(JSFunction::cast(frame_inspector->GetFunction()));
-
- Handle<JSObject> local_scope =
- isolate->factory()->NewJSObject(isolate->object_function());
- MaterializeStackLocalsWithFrameInspector(isolate, local_scope, function,
- frame_inspector);
-
- Handle<Context> frame_context(Context::cast(frame_inspector->GetContext()));
-
- return MaterializeLocalContext(isolate, local_scope, function, frame_context);
-}
-
-
-// Set the context local variable value.
-static bool SetContextLocalValue(Isolate* isolate, Handle<ScopeInfo> scope_info,
- Handle<Context> context,
- Handle<String> variable_name,
- Handle<Object> new_value) {
- for (int i = 0; i < scope_info->ContextLocalCount(); i++) {
- Handle<String> next_name(scope_info->ContextLocalName(i));
- if (String::Equals(variable_name, next_name)) {
- VariableMode mode;
- VariableLocation location;
- InitializationFlag init_flag;
- MaybeAssignedFlag maybe_assigned_flag;
- int context_index =
- ScopeInfo::ContextSlotIndex(scope_info, next_name, &mode, &location,
- &init_flag, &maybe_assigned_flag);
- context->set(context_index, *new_value);
- return true;
- }
- }
-
- return false;
-}
-
-
-static bool SetLocalVariableValue(Isolate* isolate, JavaScriptFrame* frame,
- Handle<String> variable_name,
- Handle<Object> new_value) {
- // Optimized frames are not supported.
- if (frame->is_optimized()) return false;
-
- Handle<JSFunction> function(frame->function());
- Handle<SharedFunctionInfo> shared(function->shared());
- Handle<ScopeInfo> scope_info(shared->scope_info());
-
- bool default_result = false;
-
- // Parameters.
- for (int i = 0; i < scope_info->ParameterCount(); ++i) {
- HandleScope scope(isolate);
- if (String::Equals(handle(scope_info->ParameterName(i)), variable_name)) {
- frame->SetParameterValue(i, *new_value);
- // Argument might be shadowed in heap context, don't stop here.
- default_result = true;
- }
- }
-
- // Stack locals.
- for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
- HandleScope scope(isolate);
- if (String::Equals(handle(scope_info->StackLocalName(i)), variable_name)) {
- frame->SetExpression(scope_info->StackLocalIndex(i), *new_value);
- return true;
- }
- }
-
- if (scope_info->HasContext()) {
- // Context locals.
- Handle<Context> frame_context(Context::cast(frame->context()));
- Handle<Context> function_context(frame_context->declaration_context());
- if (SetContextLocalValue(isolate, scope_info, function_context,
- variable_name, new_value)) {
- return true;
- }
-
- // Function context extension. These are variables introduced by eval.
- if (function_context->closure() == *function) {
- if (function_context->has_extension() &&
- !function_context->IsNativeContext()) {
- Handle<JSObject> ext(JSObject::cast(function_context->extension()));
-
- Maybe<bool> maybe = JSReceiver::HasProperty(ext, variable_name);
- DCHECK(maybe.IsJust());
- if (maybe.FromJust()) {
- // We don't expect this to do anything except replacing
- // property value.
- Runtime::SetObjectProperty(isolate, ext, variable_name, new_value,
- SLOPPY).Assert();
- return true;
- }
- }
- }
- }
-
- return default_result;
-}
-
-
-static bool SetBlockVariableValue(Isolate* isolate,
- Handle<Context> block_context,
- Handle<ScopeInfo> scope_info,
- JavaScriptFrame* frame,
- Handle<String> variable_name,
- Handle<Object> new_value) {
- if (frame != nullptr) {
- for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
- HandleScope scope(isolate);
- if (String::Equals(handle(scope_info->StackLocalName(i)),
- variable_name)) {
- frame->SetExpression(scope_info->StackLocalIndex(i), *new_value);
- return true;
- }
- }
- }
- if (!block_context.is_null()) {
- return SetContextLocalValue(block_context->GetIsolate(), scope_info,
- block_context, variable_name, new_value);
- }
- return false;
-}
-
-
-// Create a plain JSObject which materializes the closure content for the
-// context.
-static Handle<JSObject> MaterializeClosure(Isolate* isolate,
- Handle<Context> context) {
- DCHECK(context->IsFunctionContext());
-
- Handle<SharedFunctionInfo> shared(context->closure()->shared());
- Handle<ScopeInfo> scope_info(shared->scope_info());
-
- // Allocate and initialize a JSObject with all the content of this function
- // closure.
- Handle<JSObject> closure_scope =
- isolate->factory()->NewJSObject(isolate->object_function());
-
- // Fill all context locals to the context extension.
- ScopeInfo::CopyContextLocalsToScopeObject(scope_info, context, closure_scope);
-
- // Finally copy any properties from the function context extension. This will
- // be variables introduced by eval.
- if (context->has_extension()) {
- Handle<JSObject> ext(JSObject::cast(context->extension()));
- DCHECK(ext->IsJSContextExtensionObject());
- Handle<FixedArray> keys =
- JSReceiver::GetKeys(ext, JSReceiver::OWN_ONLY).ToHandleChecked();
-
- for (int i = 0; i < keys->length(); i++) {
- HandleScope scope(isolate);
- // Names of variables introduced by eval are strings.
- DCHECK(keys->get(i)->IsString());
- Handle<String> key(String::cast(keys->get(i)));
- Handle<Object> value = Object::GetProperty(ext, key).ToHandleChecked();
- JSObject::SetOwnPropertyIgnoreAttributes(closure_scope, key, value, NONE)
- .Check();
- }
- }
-
- return closure_scope;
-}
-
-
-// This method copies structure of MaterializeClosure method above.
-static bool SetClosureVariableValue(Isolate* isolate, Handle<Context> context,
- Handle<String> variable_name,
- Handle<Object> new_value) {
- DCHECK(context->IsFunctionContext());
-
- Handle<SharedFunctionInfo> shared(context->closure()->shared());
- Handle<ScopeInfo> scope_info(shared->scope_info());
-
- // Context locals to the context extension.
- if (SetContextLocalValue(isolate, scope_info, context, variable_name,
- new_value)) {
- return true;
- }
-
- // Properties from the function context extension. This will
- // be variables introduced by eval.
- if (context->has_extension()) {
- Handle<JSObject> ext(JSObject::cast(context->extension()));
- DCHECK(ext->IsJSContextExtensionObject());
- Maybe<bool> maybe = JSReceiver::HasOwnProperty(ext, variable_name);
- DCHECK(maybe.IsJust());
- if (maybe.FromJust()) {
- // We don't expect this to do anything except replacing property value.
- JSObject::SetOwnPropertyIgnoreAttributes(ext, variable_name, new_value,
- NONE).Check();
- return true;
- }
- }
-
- return false;
-}
-
-
-static bool SetScriptVariableValue(Handle<Context> context,
- Handle<String> variable_name,
- Handle<Object> new_value) {
- Handle<ScriptContextTable> script_contexts(
- context->global_object()->native_context()->script_context_table());
- ScriptContextTable::LookupResult lookup_result;
- if (ScriptContextTable::Lookup(script_contexts, variable_name,
- &lookup_result)) {
- Handle<Context> script_context = ScriptContextTable::GetContext(
- script_contexts, lookup_result.context_index);
- script_context->set(lookup_result.slot_index, *new_value);
- return true;
- }
-
- return false;
-}
-
-
-// Create a plain JSObject which materializes the scope for the specified
-// catch context.
-static Handle<JSObject> MaterializeCatchScope(Isolate* isolate,
- Handle<Context> context) {
- DCHECK(context->IsCatchContext());
- Handle<String> name(String::cast(context->extension()));
- Handle<Object> thrown_object(context->get(Context::THROWN_OBJECT_INDEX),
- isolate);
- Handle<JSObject> catch_scope =
- isolate->factory()->NewJSObject(isolate->object_function());
- JSObject::SetOwnPropertyIgnoreAttributes(catch_scope, name, thrown_object,
- NONE).Check();
- return catch_scope;
-}
-
-
-static bool SetCatchVariableValue(Isolate* isolate, Handle<Context> context,
- Handle<String> variable_name,
- Handle<Object> new_value) {
- DCHECK(context->IsCatchContext());
- Handle<String> name(String::cast(context->extension()));
- if (!String::Equals(name, variable_name)) {
- return false;
- }
- context->set(Context::THROWN_OBJECT_INDEX, *new_value);
- return true;
-}
-
-
-// Create a plain JSObject which materializes the block scope for the specified
-// block context.
-static Handle<JSObject> MaterializeBlockScope(Isolate* isolate,
- Handle<ScopeInfo> scope_info,
- Handle<Context> context,
- FrameInspector* frame_inspector) {
- Handle<JSObject> block_scope =
- isolate->factory()->NewJSObject(isolate->object_function());
-
- if (frame_inspector != nullptr) {
- MaterializeStackLocalsWithFrameInspector(isolate, block_scope, scope_info,
- frame_inspector);
- }
-
- if (!context.is_null()) {
- Handle<ScopeInfo> scope_info_from_context(
- ScopeInfo::cast(context->extension()));
- // Fill all context locals.
- ScopeInfo::CopyContextLocalsToScopeObject(scope_info_from_context, context,
- block_scope);
- }
-
- return block_scope;
-}
-
-
-// Create a plain JSObject which materializes the module scope for the specified
-// module context.
-MUST_USE_RESULT static MaybeHandle<JSObject> MaterializeModuleScope(
- Isolate* isolate, Handle<Context> context) {
- DCHECK(context->IsModuleContext());
- Handle<ScopeInfo> scope_info(ScopeInfo::cast(context->extension()));
-
- // Allocate and initialize a JSObject with all the members of the debugged
- // module.
- Handle<JSObject> module_scope =
- isolate->factory()->NewJSObject(isolate->object_function());
-
- // Fill all context locals.
- ScopeInfo::CopyContextLocalsToScopeObject(scope_info, context, module_scope);
-
- return module_scope;
-}
-
-
-// Iterate over the actual scopes visible from a stack frame or from a closure.
-// The iteration proceeds from the innermost visible nested scope outwards.
-// All scopes are backed by an actual context except the local scope,
-// which is inserted "artificially" in the context chain.
-class ScopeIterator {
- public:
- enum ScopeType {
- ScopeTypeGlobal = 0,
- ScopeTypeLocal,
- ScopeTypeWith,
- ScopeTypeClosure,
- ScopeTypeCatch,
- ScopeTypeBlock,
- ScopeTypeScript,
- ScopeTypeModule
- };
-
- ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector,
- bool ignore_nested_scopes = false)
- : isolate_(isolate),
- frame_inspector_(frame_inspector),
- nested_scope_chain_(4),
- seen_script_scope_(false),
- failed_(false) {
- if (!frame_inspector->GetContext()->IsContext() ||
- !frame_inspector->GetFunction()->IsJSFunction()) {
- // Optimized frame, context or function cannot be materialized. Give up.
- return;
- }
-
- context_ = Handle<Context>(Context::cast(frame_inspector->GetContext()));
-
- // Catch the case when the debugger stops in an internal function.
- Handle<SharedFunctionInfo> shared_info(function()->shared());
- Handle<ScopeInfo> scope_info(shared_info->scope_info());
- if (shared_info->script() == isolate->heap()->undefined_value()) {
- while (context_->closure() == function()) {
- context_ = Handle<Context>(context_->previous(), isolate_);
- }
- return;
- }
-
- // Currently it takes too much time to find nested scopes due to script
- // parsing. Sometimes we want to run the ScopeIterator as fast as possible
- // (for example, while collecting async call stacks on every
- // addEventListener call), even if we drop some nested scopes.
- // Later we may optimize getting the nested scopes (cache the result?)
- // and include nested scopes into the "fast" iteration case as well.
-
- if (!ignore_nested_scopes && shared_info->HasDebugInfo()) {
- // The source position at return is always the end of the function,
- // which is not consistent with the current scope chain. Therefore all
- // nested with, catch and block contexts are skipped, and we can only
- // inspect the function scope.
- // This can only happen if we set a break point inside right before the
- // return, which requires a debug info to be available.
- Handle<DebugInfo> debug_info(shared_info->GetDebugInfo());
-
- // PC points to the instruction after the current one, possibly a break
- // location as well. So the "- 1" to exclude it from the search.
- Address call_pc = frame()->pc() - 1;
-
- // Find the break point where execution has stopped.
- BreakLocation location =
- BreakLocation::FromAddress(debug_info, ALL_BREAK_LOCATIONS, call_pc);
-
- ignore_nested_scopes = location.IsReturn();
- }
-
- if (ignore_nested_scopes) {
- if (scope_info->HasContext()) {
- context_ = Handle<Context>(context_->declaration_context(), isolate_);
- } else {
- while (context_->closure() == function()) {
- context_ = Handle<Context>(context_->previous(), isolate_);
- }
- }
- if (scope_info->scope_type() == FUNCTION_SCOPE ||
- scope_info->scope_type() == ARROW_SCOPE) {
- nested_scope_chain_.Add(scope_info);
- }
- } else {
- // Reparse the code and analyze the scopes.
- Handle<Script> script(Script::cast(shared_info->script()));
- Scope* scope = NULL;
-
- // Check whether we are in global, eval or function code.
- Handle<ScopeInfo> scope_info(shared_info->scope_info());
- Zone zone;
- if (scope_info->scope_type() != FUNCTION_SCOPE &&
- scope_info->scope_type() != ARROW_SCOPE) {
- // Global or eval code.
- ParseInfo info(&zone, script);
- if (scope_info->scope_type() == SCRIPT_SCOPE) {
- info.set_global();
- } else {
- DCHECK(scope_info->scope_type() == EVAL_SCOPE);
- info.set_eval();
- info.set_context(Handle<Context>(function()->context()));
- }
- if (Parser::ParseStatic(&info) && Scope::Analyze(&info)) {
- scope = info.function()->scope();
- }
- RetrieveScopeChain(scope, shared_info);
- } else {
- // Function code
- ParseInfo info(&zone, Handle<JSFunction>(function()));
- if (Parser::ParseStatic(&info) && Scope::Analyze(&info)) {
- scope = info.function()->scope();
- }
- RetrieveScopeChain(scope, shared_info);
- }
- }
- }
-
- ScopeIterator(Isolate* isolate, Handle<JSFunction> function)
- : isolate_(isolate),
- frame_inspector_(NULL),
- context_(function->context()),
- seen_script_scope_(false),
- failed_(false) {
- if (function->IsBuiltin()) context_ = Handle<Context>();
- }
-
- // More scopes?
- bool Done() {
- DCHECK(!failed_);
- return context_.is_null();
- }
-
- bool Failed() { return failed_; }
-
- // Move to the next scope.
- void Next() {
- DCHECK(!failed_);
- ScopeType scope_type = Type();
- if (scope_type == ScopeTypeGlobal) {
- // The global scope is always the last in the chain.
- DCHECK(context_->IsNativeContext());
- context_ = Handle<Context>();
- return;
- }
- if (scope_type == ScopeTypeScript) {
- seen_script_scope_ = true;
- if (context_->IsScriptContext()) {
- context_ = Handle<Context>(context_->previous(), isolate_);
- }
- if (!nested_scope_chain_.is_empty()) {
- DCHECK_EQ(nested_scope_chain_.last()->scope_type(), SCRIPT_SCOPE);
- nested_scope_chain_.RemoveLast();
- DCHECK(nested_scope_chain_.is_empty());
- }
- CHECK(context_->IsNativeContext());
- return;
- }
- if (nested_scope_chain_.is_empty()) {
- context_ = Handle<Context>(context_->previous(), isolate_);
- } else {
- if (nested_scope_chain_.last()->HasContext()) {
- DCHECK(context_->previous() != NULL);
- context_ = Handle<Context>(context_->previous(), isolate_);
- }
- nested_scope_chain_.RemoveLast();
- }
- }
-
- // Return the type of the current scope.
- ScopeType Type() {
- DCHECK(!failed_);
- if (!nested_scope_chain_.is_empty()) {
- Handle<ScopeInfo> scope_info = nested_scope_chain_.last();
- switch (scope_info->scope_type()) {
- case FUNCTION_SCOPE:
- case ARROW_SCOPE:
- DCHECK(context_->IsFunctionContext() || !scope_info->HasContext());
- return ScopeTypeLocal;
- case MODULE_SCOPE:
- DCHECK(context_->IsModuleContext());
- return ScopeTypeModule;
- case SCRIPT_SCOPE:
- DCHECK(context_->IsScriptContext() || context_->IsNativeContext());
- return ScopeTypeScript;
- case WITH_SCOPE:
- DCHECK(context_->IsWithContext());
- return ScopeTypeWith;
- case CATCH_SCOPE:
- DCHECK(context_->IsCatchContext());
- return ScopeTypeCatch;
- case BLOCK_SCOPE:
- DCHECK(!scope_info->HasContext() || context_->IsBlockContext());
- return ScopeTypeBlock;
- case EVAL_SCOPE:
- UNREACHABLE();
- }
- }
- if (context_->IsNativeContext()) {
- DCHECK(context_->global_object()->IsGlobalObject());
- // If we are at the native context and have not yet seen script scope,
- // fake it.
- return seen_script_scope_ ? ScopeTypeGlobal : ScopeTypeScript;
- }
- if (context_->IsFunctionContext()) {
- return ScopeTypeClosure;
- }
- if (context_->IsCatchContext()) {
- return ScopeTypeCatch;
- }
- if (context_->IsBlockContext()) {
- return ScopeTypeBlock;
- }
- if (context_->IsModuleContext()) {
- return ScopeTypeModule;
- }
- if (context_->IsScriptContext()) {
- return ScopeTypeScript;
- }
- DCHECK(context_->IsWithContext());
- return ScopeTypeWith;
- }
-
- // Return the JavaScript object with the content of the current scope.
- MaybeHandle<JSObject> ScopeObject() {
- DCHECK(!failed_);
- switch (Type()) {
- case ScopeIterator::ScopeTypeGlobal:
- return Handle<JSObject>(CurrentContext()->global_object());
- case ScopeIterator::ScopeTypeScript:
- return MaterializeScriptScope(
- Handle<GlobalObject>(CurrentContext()->global_object()));
- case ScopeIterator::ScopeTypeLocal:
- // Materialize the content of the local scope into a JSObject.
- DCHECK(nested_scope_chain_.length() == 1);
- return MaterializeLocalScope(isolate_, frame_inspector_);
- case ScopeIterator::ScopeTypeWith:
- // Return the with object.
- return Handle<JSObject>(JSObject::cast(CurrentContext()->extension()));
- case ScopeIterator::ScopeTypeCatch:
- return MaterializeCatchScope(isolate_, CurrentContext());
- case ScopeIterator::ScopeTypeClosure:
- // Materialize the content of the closure scope into a JSObject.
- return MaterializeClosure(isolate_, CurrentContext());
- case ScopeIterator::ScopeTypeBlock: {
- if (!nested_scope_chain_.is_empty()) {
- // this is a block scope on the stack.
- Handle<ScopeInfo> scope_info = nested_scope_chain_.last();
- Handle<Context> context = scope_info->HasContext()
- ? CurrentContext()
- : Handle<Context>::null();
- return MaterializeBlockScope(isolate_, scope_info, context,
- frame_inspector_);
- } else {
- return MaterializeBlockScope(isolate_, Handle<ScopeInfo>::null(),
- CurrentContext(), nullptr);
- }
- }
- case ScopeIterator::ScopeTypeModule:
- return MaterializeModuleScope(isolate_, CurrentContext());
- }
- UNREACHABLE();
- return Handle<JSObject>();
- }
-
- bool HasContext() {
- ScopeType type = Type();
- if (type == ScopeTypeBlock || type == ScopeTypeLocal) {
- if (!nested_scope_chain_.is_empty()) {
- return nested_scope_chain_.last()->HasContext();
- }
- }
- return true;
- }
-
- bool SetVariableValue(Handle<String> variable_name,
- Handle<Object> new_value) {
- DCHECK(!failed_);
- switch (Type()) {
- case ScopeIterator::ScopeTypeGlobal:
- break;
- case ScopeIterator::ScopeTypeLocal:
- return SetLocalVariableValue(isolate_, frame(), variable_name,
- new_value);
- case ScopeIterator::ScopeTypeWith:
- break;
- case ScopeIterator::ScopeTypeCatch:
- return SetCatchVariableValue(isolate_, CurrentContext(), variable_name,
- new_value);
- case ScopeIterator::ScopeTypeClosure:
- return SetClosureVariableValue(isolate_, CurrentContext(),
- variable_name, new_value);
- case ScopeIterator::ScopeTypeScript:
- return SetScriptVariableValue(CurrentContext(), variable_name,
- new_value);
- case ScopeIterator::ScopeTypeBlock:
- return SetBlockVariableValue(
- isolate_, HasContext() ? CurrentContext() : Handle<Context>::null(),
- CurrentScopeInfo(), frame(), variable_name, new_value);
- case ScopeIterator::ScopeTypeModule:
- // TODO(2399): should we implement it?
- break;
- }
- return false;
- }
-
- Handle<ScopeInfo> CurrentScopeInfo() {
- DCHECK(!failed_);
- if (!nested_scope_chain_.is_empty()) {
- return nested_scope_chain_.last();
- } else if (context_->IsBlockContext()) {
- return Handle<ScopeInfo>(ScopeInfo::cast(context_->extension()));
- } else if (context_->IsFunctionContext()) {
- return Handle<ScopeInfo>(context_->closure()->shared()->scope_info());
- }
- return Handle<ScopeInfo>::null();
- }
-
- // Return the context for this scope. For the local context there might not
- // be an actual context.
- Handle<Context> CurrentContext() {
- DCHECK(!failed_);
- if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript ||
- nested_scope_chain_.is_empty()) {
- return context_;
- } else if (nested_scope_chain_.last()->HasContext()) {
- return context_;
- } else {
- return Handle<Context>();
- }
- }
-
-#ifdef DEBUG
- // Debug print of the content of the current scope.
- void DebugPrint() {
- OFStream os(stdout);
- DCHECK(!failed_);
- switch (Type()) {
- case ScopeIterator::ScopeTypeGlobal:
- os << "Global:\n";
- CurrentContext()->Print(os);
- break;
-
- case ScopeIterator::ScopeTypeLocal: {
- os << "Local:\n";
- function()->shared()->scope_info()->Print();
- if (!CurrentContext().is_null()) {
- CurrentContext()->Print(os);
- if (CurrentContext()->has_extension()) {
- Handle<Object> extension(CurrentContext()->extension(), isolate_);
- if (extension->IsJSContextExtensionObject()) {
- extension->Print(os);
- }
- }
- }
- break;
- }
-
- case ScopeIterator::ScopeTypeWith:
- os << "With:\n";
- CurrentContext()->extension()->Print(os);
- break;
-
- case ScopeIterator::ScopeTypeCatch:
- os << "Catch:\n";
- CurrentContext()->extension()->Print(os);
- CurrentContext()->get(Context::THROWN_OBJECT_INDEX)->Print(os);
- break;
-
- case ScopeIterator::ScopeTypeClosure:
- os << "Closure:\n";
- CurrentContext()->Print(os);
- if (CurrentContext()->has_extension()) {
- Handle<Object> extension(CurrentContext()->extension(), isolate_);
- if (extension->IsJSContextExtensionObject()) {
- extension->Print(os);
- }
- }
- break;
-
- case ScopeIterator::ScopeTypeScript:
- os << "Script:\n";
- CurrentContext()
- ->global_object()
- ->native_context()
- ->script_context_table()
- ->Print(os);
- break;
-
- default:
- UNREACHABLE();
- }
- PrintF("\n");
- }
-#endif
-
- private:
- Isolate* isolate_;
- FrameInspector* const frame_inspector_;
- Handle<Context> context_;
- List<Handle<ScopeInfo> > nested_scope_chain_;
- bool seen_script_scope_;
- bool failed_;
-
- inline JavaScriptFrame* frame() {
- return frame_inspector_->GetArgumentsFrame();
- }
-
- inline JSFunction* function() {
- return JSFunction::cast(frame_inspector_->GetFunction());
- }
-
- void RetrieveScopeChain(Scope* scope,
- Handle<SharedFunctionInfo> shared_info) {
- if (scope != NULL) {
- int source_position = frame_inspector_->GetSourcePosition();
- scope->GetNestedScopeChain(isolate_, &nested_scope_chain_,
- source_position);
- } else {
- // A failed reparse indicates that the preparser has diverged from the
- // parser or that the preparse data given to the initial parse has been
- // faulty. We fail in debug mode but in release mode we only provide the
- // information we get from the context chain but nothing about
- // completely stack allocated scopes or stack allocated locals.
- // Or it could be due to stack overflow.
- DCHECK(isolate_->has_pending_exception());
- failed_ = true;
- }
- }
-
- DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
-};
-
-
RUNTIME_FUNCTION(Runtime_GetScopeCount) {
HandleScope scope(isolate);
DCHECK(args.length() == 2);
CONVERT_SMI_ARG_CHECKED(wrapped_id, 1);
// Get the frame where the debugging is performed.
- StackFrame::Id id = UnwrapFrameId(wrapped_id);
+ StackFrame::Id id = DebugFrameHelper::UnwrapFrameId(wrapped_id);
JavaScriptFrameIterator it(isolate, id);
JavaScriptFrame* frame = it.frame();
FrameInspector frame_inspector(frame, 0, isolate);
CONVERT_SMI_ARG_CHECKED(wrapped_id, 1);
// Get the frame where the debugging is performed.
- StackFrame::Id id = UnwrapFrameId(wrapped_id);
+ StackFrame::Id id = DebugFrameHelper::UnwrapFrameId(wrapped_id);
JavaScriptFrameIterator frame_it(isolate, id);
RUNTIME_ASSERT(!frame_it.done());
}
-static const int kScopeDetailsTypeIndex = 0;
-static const int kScopeDetailsObjectIndex = 1;
-static const int kScopeDetailsSize = 2;
-
-
-MUST_USE_RESULT static MaybeHandle<JSObject> MaterializeScopeDetails(
- Isolate* isolate, ScopeIterator* it) {
- // Calculate the size of the result.
- int details_size = kScopeDetailsSize;
- Handle<FixedArray> details = isolate->factory()->NewFixedArray(details_size);
-
- // Fill in scope details.
- details->set(kScopeDetailsTypeIndex, Smi::FromInt(it->Type()));
- Handle<JSObject> scope_object;
- ASSIGN_RETURN_ON_EXCEPTION(isolate, scope_object, it->ScopeObject(),
- JSObject);
- details->set(kScopeDetailsObjectIndex, *scope_object);
-
- return isolate->factory()->NewJSArrayWithElements(details);
-}
-
-
// Return an array with scope details
// args[0]: number: break id
// args[1]: number: frame index
CONVERT_NUMBER_CHECKED(int, index, Int32, args[3]);
// Get the frame where the debugging is performed.
- StackFrame::Id id = UnwrapFrameId(wrapped_id);
+ StackFrame::Id id = DebugFrameHelper::UnwrapFrameId(wrapped_id);
JavaScriptFrameIterator frame_it(isolate, id);
JavaScriptFrame* frame = frame_it.frame();
FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate);
}
Handle<JSObject> details;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, details,
- MaterializeScopeDetails(isolate, &it));
+ it.MaterializeScopeDetails());
return *details;
}
}
// Get the frame where the debugging is performed.
- StackFrame::Id id = UnwrapFrameId(wrapped_id);
+ StackFrame::Id id = DebugFrameHelper::UnwrapFrameId(wrapped_id);
JavaScriptFrameIterator frame_it(isolate, id);
JavaScriptFrame* frame = frame_it.frame();
FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate);
for (; !it.Done(); it.Next()) {
Handle<JSObject> details;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, details,
- MaterializeScopeDetails(isolate, &it));
+ it.MaterializeScopeDetails());
result.Add(details);
}
Handle<JSObject> details;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, details,
- MaterializeScopeDetails(isolate, &it));
+ it.MaterializeScopeDetails());
return *details;
}
CONVERT_NUMBER_CHECKED(int, inlined_jsframe_index, Int32, args[2]);
// Get the frame where the debugging is performed.
- StackFrame::Id id = UnwrapFrameId(wrapped_id);
+ StackFrame::Id id = DebugFrameHelper::UnwrapFrameId(wrapped_id);
JavaScriptFrameIterator frame_it(isolate, id);
JavaScriptFrame* frame = frame_it.frame();
FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate);
if (wrapped_frame_id == 0) {
frame_id = StackFrame::NO_ID;
} else {
- frame_id = UnwrapFrameId(wrapped_frame_id);
+ frame_id = DebugFrameHelper::UnwrapFrameId(wrapped_frame_id);
}
// Get the step action and check validity.
}
-// Helper function to find or create the arguments object for
-// Runtime_DebugEvaluate.
-static void MaterializeArgumentsObject(Isolate* isolate,
- 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();
-}
-
-
-// Compile and evaluate source for the given context.
-static MaybeHandle<Object> DebugEvaluate(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;
-}
-
-
-static Handle<JSObject> NewJSObjectWithNullProto(Isolate* isolate) {
- 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;
-}
-
-
-namespace {
-
-// This class builds a context chain for evaluation of expressions
-// in debugger.
-// The scope chain leading up to a breakpoint where evaluation occurs
-// looks like:
-// - [a mix of with, catch and block scopes]
-// - [function stack + context]
-// - [outer context]
-// The builder materializes all stack variables into properties of objects;
-// the expression is then evaluated as if it is inside a series of 'with'
-// statements using those objects. To this end, the builder builds a new
-// context chain, based on a scope chain:
-// - every With and Catch scope begets a cloned context
-// - Block scope begets one or two contexts:
-// - if a block has context-allocated varaibles, its context is cloned
-// - stack locals are materizalized as a With context
-// - Local scope begets a With context for materizalized locals, chained to
-// original function context. Original function context is the end of
-// the chain.
-class EvaluationContextBuilder {
- public:
- EvaluationContextBuilder(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(isolate, parent_context, function, frame);
-
- Handle<JSObject> materialized_function =
- NewJSObjectWithNullProto(isolate);
-
- MaterializeStackLocalsWithFrameInspector(isolate, materialized_function,
- function, &frame_inspector);
-
- MaterializeArgumentsObject(isolate, 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(isolate);
- MaterializeStackLocalsWithFrameInspector(isolate, materialized_object,
- it.CurrentScopeInfo(),
- &frame_inspector);
- 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 UpdateVariables() {
- 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.
- UpdateStackLocalsFromMaterializedObject(
- isolate_, element.materialized_object, element.scope_info, frame_,
- inlined_jsframe_index_);
- }
- }
- }
-
- Handle<Context> innermost_context() const { return innermost_context_; }
- Handle<SharedFunctionInfo> outer_info() const { return outer_info_; }
-
- private:
- struct ContextChainElement {
- Handle<Context> original_context;
- Handle<Context> cloned_context;
- Handle<JSObject> materialized_object;
- Handle<ScopeInfo> scope_info;
- };
-
- void 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;
- }
-
- Handle<SharedFunctionInfo> outer_info_;
- Handle<Context> innermost_context_;
- List<ContextChainElement> context_chain_;
- Isolate* isolate_;
- JavaScriptFrame* frame_;
- int inlined_jsframe_index_;
-};
-}
-
-
-// Evaluate a piece of JavaScript in the context of a stack frame for
-// debugging. Things that need special attention are:
-// - Parameters and stack-allocated locals need to be materialized. Altered
-// values need to be written back to the stack afterwards.
-// - The arguments object needs to materialized.
RUNTIME_FUNCTION(Runtime_DebugEvaluate) {
HandleScope scope(isolate);
CONVERT_BOOLEAN_ARG_CHECKED(disable_break, 4);
CONVERT_ARG_HANDLE_CHECKED(Object, context_extension, 5);
- // Handle the processing of break.
- DisableBreak disable_break_scope(isolate->debug(), disable_break);
-
- // Get the frame where the debugging is performed.
- StackFrame::Id id = UnwrapFrameId(wrapped_id);
- JavaScriptFrameIterator it(isolate, id);
- JavaScriptFrame* frame = it.frame();
-
- // Traverse the saved contexts chain to find the active context for the
- // selected frame.
- SaveContext* save = FindSavedContextForFrame(isolate, frame);
-
- SaveContext savex(isolate);
- isolate->set_context(*(save->context()));
-
- // Materialize stack locals and the arguments object.
-
- EvaluationContextBuilder context_builder(isolate, frame,
- inlined_jsframe_index);
- if (isolate->has_pending_exception()) {
- return isolate->heap()->exception();
- }
-
-
- Handle<Object> receiver(frame->receiver(), isolate);
- MaybeHandle<Object> maybe_result = DebugEvaluate(
- isolate, context_builder.outer_info(),
- context_builder.innermost_context(), context_extension, receiver, source);
+ StackFrame::Id id = DebugFrameHelper::UnwrapFrameId(wrapped_id);
Handle<Object> result;
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, maybe_result);
- context_builder.UpdateVariables();
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, result,
+ DebugEvaluate::Local(isolate, id, inlined_jsframe_index, source,
+ disable_break, context_extension));
return *result;
}
-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();
-}
-
-
RUNTIME_FUNCTION(Runtime_DebugEvaluateGlobal) {
HandleScope scope(isolate);
CONVERT_BOOLEAN_ARG_CHECKED(disable_break, 2);
CONVERT_ARG_HANDLE_CHECKED(Object, context_extension, 3);
- // 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);
Handle<Object> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
- isolate, result, DebugEvaluate(isolate, outer_info, context,
- context_extension, receiver, source));
+ isolate, result,
+ DebugEvaluate::Global(isolate, source, disable_break, context_extension));
return *result;
}
#include "src/arguments.h"
#include "src/debug/debug.h"
+#include "src/debug/debug-frames.h"
#include "src/debug/liveedit.h"
#include "src/runtime/runtime.h"
#include "src/runtime/runtime-utils.h"
}
JavaScriptFrameIterator it(isolate, id);
- int inlined_jsframe_index = Runtime::FindIndexedNonNativeFrame(&it, index);
+ int inlined_jsframe_index =
+ DebugFrameHelper::FindIndexedNonNativeFrame(&it, index);
if (inlined_jsframe_index == -1) return heap->undefined_value();
// We don't really care what the inlined frame index is, since we are
// throwing away the entire frame anyways.
static void NeuterArrayBuffer(Handle<JSArrayBuffer> array_buffer);
- static int FindIndexedNonNativeFrame(JavaScriptFrameIterator* it, int index);
-
enum TypedArrayId {
// arrayIds below should be synchromized with typedarray.js natives.
ARRAY_ID_UINT8 = 1,
}
-void ScopeInfo::CopyContextLocalsToScopeObject(Handle<ScopeInfo> scope_info,
- Handle<Context> context,
- Handle<JSObject> scope_object) {
- Isolate* isolate = scope_info->GetIsolate();
- int local_count = scope_info->ContextLocalCount();
- if (local_count == 0) return;
- // Fill all context locals to the context extension.
- int first_context_var = scope_info->StackLocalCount();
- int start = scope_info->ContextLocalNameEntriesIndex();
- for (int i = 0; i < local_count; ++i) {
- if (scope_info->LocalIsSynthetic(first_context_var + i)) continue;
- int context_index = Context::MIN_CONTEXT_SLOTS + i;
- Handle<Object> value = Handle<Object>(context->get(context_index), isolate);
- // Reflect variables under TDZ as undefined in scope object.
- if (value->IsTheHole()) continue;
- // This should always succeed.
- // TODO(verwaest): Use AddDataProperty instead.
- JSObject::SetOwnPropertyIgnoreAttributes(
- scope_object, handle(String::cast(scope_info->get(i + start))), value,
- ::NONE).Check();
- }
-}
-
-
int ScopeInfo::ParameterEntriesIndex() {
DCHECK(length() > 0);
return kVariablePartIndex;
using ::v8::internal::JSGlobalProxy;
using ::v8::internal::Code;
using ::v8::internal::Debug;
-using ::v8::internal::Debugger;
using ::v8::internal::CommandMessage;
using ::v8::internal::CommandMessageQueue;
using ::v8::internal::StackFrame;
'../../src/dateparser-inl.h',
'../../src/dateparser.cc',
'../../src/dateparser.h',
+ '../../src/debug/debug-evaluate.cc',
+ '../../src/debug/debug-evaluate.h',
+ '../../src/debug/debug-frames.cc',
+ '../../src/debug/debug-frames.h',
+ '../../src/debug/debug-scopes.cc',
+ '../../src/debug/debug-scopes.h',
'../../src/debug/debug.cc',
'../../src/debug/debug.h',
'../../src/debug/liveedit.cc',