Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(317)

Unified Diff: src/runtime/runtime-debug.cc

Issue 612023002: Split even more runtime functions into separate files. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/runtime/runtime-date.cc ('k') | src/runtime/runtime-generator.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/runtime/runtime-debug.cc
diff --git a/src/runtime/runtime-debug.cc b/src/runtime/runtime-debug.cc
new file mode 100644
index 0000000000000000000000000000000000000000..e3d18bc4ffd17c9c264d70e8c936b0b89f353531
--- /dev/null
+++ b/src/runtime/runtime-debug.cc
@@ -0,0 +1,2736 @@
+// Copyright 2014 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/v8.h"
+
+#include "src/accessors.h"
+#include "src/arguments.h"
+#include "src/debug.h"
+#include "src/deoptimizer.h"
+#include "src/isolate-inl.h"
+#include "src/parser.h"
+#include "src/runtime/runtime.h"
+#include "src/runtime/runtime-utils.h"
+
+namespace v8 {
+namespace internal {
+
+RUNTIME_FUNCTION(Runtime_DebugBreak) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 0);
+ isolate->debug()->HandleDebugBreak();
+ return isolate->heap()->undefined_value();
+}
+
+
+// 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
+// args[1]: object supplied during callback
+RUNTIME_FUNCTION(Runtime_SetDebugEventListener) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 2);
+ RUNTIME_ASSERT(args[0]->IsJSFunction() || args[0]->IsUndefined() ||
+ args[0]->IsNull());
+ CONVERT_ARG_HANDLE_CHECKED(Object, callback, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, data, 1);
+ isolate->debug()->SetEventListener(callback, data);
+
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(Runtime_Break) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 0);
+ isolate->stack_guard()->RequestDebugBreak();
+ return isolate->heap()->undefined_value();
+}
+
+
+static Handle<Object> DebugGetProperty(LookupIterator* it,
+ bool* has_caught = NULL) {
+ for (; it->IsFound(); it->Next()) {
+ switch (it->state()) {
+ case LookupIterator::NOT_FOUND:
+ case LookupIterator::TRANSITION:
+ UNREACHABLE();
+ case LookupIterator::ACCESS_CHECK:
+ // Ignore access checks.
+ break;
+ case LookupIterator::INTERCEPTOR:
+ case LookupIterator::JSPROXY:
+ return it->isolate()->factory()->undefined_value();
+ case LookupIterator::ACCESSOR: {
+ Handle<Object> accessors = it->GetAccessors();
+ if (!accessors->IsAccessorInfo()) {
+ return it->isolate()->factory()->undefined_value();
+ }
+ MaybeHandle<Object> maybe_result = JSObject::GetPropertyWithAccessor(
+ it->GetReceiver(), it->name(), it->GetHolder<JSObject>(),
+ accessors);
+ Handle<Object> result;
+ if (!maybe_result.ToHandle(&result)) {
+ result = handle(it->isolate()->pending_exception(), it->isolate());
+ it->isolate()->clear_pending_exception();
+ if (has_caught != NULL) *has_caught = true;
+ }
+ return result;
+ }
+
+ case LookupIterator::DATA:
+ return it->GetDataValue();
+ }
+ }
+
+ return it->isolate()->factory()->undefined_value();
+}
+
+
+// Get debugger related details for an object property, in the following format:
+// 0: Property value
+// 1: Property details
+// 2: Property value is exception
+// 3: Getter function if defined
+// 4: Setter function if defined
+// Items 2-4 are only filled if the property has either a getter or a setter.
+RUNTIME_FUNCTION(Runtime_DebugGetPropertyDetails) {
+ HandleScope scope(isolate);
+
+ DCHECK(args.length() == 2);
+
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Name, name, 1);
+
+ // Make sure to set the current context to the context before the debugger was
+ // entered (if the debugger is entered). The reason for switching context here
+ // is that for some property lookups (accessors and interceptors) callbacks
+ // into the embedding application can occour, and the embedding application
+ // could have the assumption that its own native context is the current
+ // context and not some internal debugger context.
+ SaveContext save(isolate);
+ if (isolate->debug()->in_debug_scope()) {
+ isolate->set_context(*isolate->debug()->debugger_entry()->GetContext());
+ }
+
+ // Check if the name is trivially convertible to an index and get the element
+ // if so.
+ uint32_t index;
+ if (name->AsArrayIndex(&index)) {
+ Handle<FixedArray> details = isolate->factory()->NewFixedArray(2);
+ Handle<Object> element_or_char;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, element_or_char,
+ Runtime::GetElementOrCharAt(isolate, obj, index));
+ details->set(0, *element_or_char);
+ details->set(1,
+ PropertyDetails(NONE, NORMAL, Representation::None()).AsSmi());
+ return *isolate->factory()->NewJSArrayWithElements(details);
+ }
+
+ LookupIterator it(obj, name, LookupIterator::HIDDEN);
+ bool has_caught = false;
+ Handle<Object> value = DebugGetProperty(&it, &has_caught);
+ if (!it.IsFound()) return isolate->heap()->undefined_value();
+
+ Handle<Object> maybe_pair;
+ if (it.state() == LookupIterator::ACCESSOR) {
+ maybe_pair = it.GetAccessors();
+ }
+
+ // If the callback object is a fixed array then it contains JavaScript
+ // getter and/or setter.
+ bool has_js_accessors = !maybe_pair.is_null() && maybe_pair->IsAccessorPair();
+ Handle<FixedArray> details =
+ isolate->factory()->NewFixedArray(has_js_accessors ? 6 : 3);
+ details->set(0, *value);
+ // TODO(verwaest): Get rid of this random way of handling interceptors.
+ PropertyDetails d = it.state() == LookupIterator::INTERCEPTOR
+ ? PropertyDetails(NONE, NORMAL, 0)
+ : it.property_details();
+ details->set(1, d.AsSmi());
+ details->set(
+ 2, isolate->heap()->ToBoolean(it.state() == LookupIterator::INTERCEPTOR));
+ if (has_js_accessors) {
+ AccessorPair* accessors = AccessorPair::cast(*maybe_pair);
+ details->set(3, isolate->heap()->ToBoolean(has_caught));
+ details->set(4, accessors->GetComponent(ACCESSOR_GETTER));
+ details->set(5, accessors->GetComponent(ACCESSOR_SETTER));
+ }
+
+ return *isolate->factory()->NewJSArrayWithElements(details);
+}
+
+
+RUNTIME_FUNCTION(Runtime_DebugGetProperty) {
+ HandleScope scope(isolate);
+
+ DCHECK(args.length() == 2);
+
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Name, name, 1);
+
+ LookupIterator it(obj, name);
+ return *DebugGetProperty(&it);
+}
+
+
+// Return the property type calculated from the property details.
+// args[0]: smi with property details.
+RUNTIME_FUNCTION(Runtime_DebugPropertyTypeFromDetails) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_PROPERTY_DETAILS_CHECKED(details, 0);
+ return Smi::FromInt(static_cast<int>(details.type()));
+}
+
+
+// Return the property attribute calculated from the property details.
+// args[0]: smi with property details.
+RUNTIME_FUNCTION(Runtime_DebugPropertyAttributesFromDetails) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_PROPERTY_DETAILS_CHECKED(details, 0);
+ return Smi::FromInt(static_cast<int>(details.attributes()));
+}
+
+
+// Return the property insertion index calculated from the property details.
+// args[0]: smi with property details.
+RUNTIME_FUNCTION(Runtime_DebugPropertyIndexFromDetails) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_PROPERTY_DETAILS_CHECKED(details, 0);
+ // TODO(verwaest): Depends on the type of details.
+ return Smi::FromInt(details.dictionary_index());
+}
+
+
+// Return property value from named interceptor.
+// args[0]: object
+// args[1]: property name
+RUNTIME_FUNCTION(Runtime_DebugNamedInterceptorPropertyValue) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0);
+ RUNTIME_ASSERT(obj->HasNamedInterceptor());
+ CONVERT_ARG_HANDLE_CHECKED(Name, name, 1);
+
+ Handle<Object> result;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result,
+ JSObject::GetProperty(obj, name));
+ return *result;
+}
+
+
+// Return element value from indexed interceptor.
+// args[0]: object
+// args[1]: index
+RUNTIME_FUNCTION(Runtime_DebugIndexedInterceptorElementValue) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0);
+ RUNTIME_ASSERT(obj->HasIndexedInterceptor());
+ CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
+ Handle<Object> result;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, result, JSObject::GetElementWithInterceptor(obj, obj, index));
+ return *result;
+}
+
+
+RUNTIME_FUNCTION(Runtime_CheckExecutionState) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
+ RUNTIME_ASSERT(isolate->debug()->CheckExecutionState(break_id));
+ return isolate->heap()->true_value();
+}
+
+
+RUNTIME_FUNCTION(Runtime_GetFrameCount) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
+ RUNTIME_ASSERT(isolate->debug()->CheckExecutionState(break_id));
+
+ // Count all frames which are relevant to debugging stack trace.
+ int n = 0;
+ StackFrame::Id id = isolate->debug()->break_frame_id();
+ if (id == StackFrame::NO_ID) {
+ // If there is no JavaScript stack frame count is 0.
+ return Smi::FromInt(0);
+ }
+
+ for (JavaScriptFrameIterator it(isolate, id); !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 scripts.
+ if (!frames[i].function()->IsFromNativeScript()) n++;
+ }
+ }
+ return Smi::FromInt(n);
+}
+
+
+class FrameInspector {
+ public:
+ FrameInspector(JavaScriptFrame* frame, int inlined_jsframe_index,
+ Isolate* isolate)
+ : frame_(frame), deoptimized_frame_(NULL), isolate_(isolate) {
+ // Calculate the deoptimized frame.
+ if (frame->is_optimized()) {
+ deoptimized_frame_ = Deoptimizer::DebuggerInspectableFrame(
+ frame, inlined_jsframe_index, isolate);
+ }
+ has_adapted_arguments_ = frame_->has_adapted_arguments();
+ is_bottommost_ = inlined_jsframe_index == 0;
+ is_optimized_ = frame_->is_optimized();
+ }
+
+ ~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) {
+ 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();
+ }
+
+ // 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 kFrameDetailsArgumentCountIndex = 3;
+static const int kFrameDetailsLocalCountIndex = 4;
+static const int kFrameDetailsSourcePositionIndex = 5;
+static const int kFrameDetailsConstructCallIndex = 6;
+static const int kFrameDetailsAtReturnIndex = 7;
+static const int kFrameDetailsFlagsIndex = 8;
+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 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 scripts.
+ if (frames[i].function()->IsFromNativeScript()) continue;
+ if (++count == index) return i;
+ }
+ }
+ return -1;
+}
+
+
+// Return an array with frame details
+// args[0]: number: break id
+// args[1]: number: frame index
+//
+// The array returned contains the following information:
+// 0: Frame id
+// 1: Receiver
+// 2: Function
+// 3: Argument count
+// 4: Local count
+// 5: Source position
+// 6: Constructor call
+// 7: Is at return
+// 8: Flags
+// Arguments name, value
+// Locals name, value
+// Return value if any
+RUNTIME_FUNCTION(Runtime_GetFrameDetails) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+ CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
+ RUNTIME_ASSERT(isolate->debug()->CheckExecutionState(break_id));
+
+ CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
+ Heap* heap = isolate->heap();
+
+ // Find the relevant frame with the requested index.
+ StackFrame::Id id = isolate->debug()->break_frame_id();
+ if (id == StackFrame::NO_ID) {
+ // If there are no JavaScript stack frames return undefined.
+ return heap->undefined_value();
+ }
+
+ JavaScriptFrameIterator it(isolate, id);
+ // Inlined frame index in optimized frame, starting from outer function.
+ int inlined_jsframe_index = FindIndexedNonNativeFrame(&it, index);
+ if (inlined_jsframe_index == -1) return heap->undefined_value();
+
+ FrameInspector frame_inspector(it.frame(), inlined_jsframe_index, isolate);
+ bool is_optimized = it.frame()->is_optimized();
+
+ // Traverse the saved contexts chain to find the active context for the
+ // selected frame.
+ SaveContext* save = FindSavedContextForFrame(isolate, it.frame());
+
+ // Get the frame id.
+ Handle<Object> frame_id(WrapFrameId(it.frame()->id()), isolate);
+
+ // Find source position in unoptimized code.
+ int position = frame_inspector.GetSourcePosition();
+
+ // Check for constructor frame.
+ bool constructor = frame_inspector.IsConstructor();
+
+ // Get scope info and read from it for local variable information.
+ Handle<JSFunction> function(JSFunction::cast(frame_inspector.GetFunction()));
+ Handle<SharedFunctionInfo> shared(function->shared());
+ Handle<ScopeInfo> scope_info(shared->scope_info());
+ DCHECK(*scope_info != ScopeInfo::Empty(isolate));
+
+ // Get the locals names and values into a temporary array.
+ int local_count = scope_info->LocalCount();
+ for (int slot = 0; slot < scope_info->LocalCount(); ++slot) {
+ // Hide compiler-introduced temporary variables, whether on the stack or on
+ // the context.
+ if (scope_info->LocalIsSynthetic(slot)) local_count--;
+ }
+
+ Handle<FixedArray> locals =
+ isolate->factory()->NewFixedArray(local_count * 2);
+
+ // Fill in the values of the locals.
+ int local = 0;
+ int i = 0;
+ for (; i < scope_info->StackLocalCount(); ++i) {
+ // Use the value from the stack.
+ if (scope_info->LocalIsSynthetic(i)) continue;
+ locals->set(local * 2, scope_info->LocalName(i));
+ locals->set(local * 2 + 1, frame_inspector.GetExpression(i));
+ local++;
+ }
+ if (local < local_count) {
+ // Get the context containing declarations.
+ Handle<Context> context(
+ Context::cast(frame_inspector.GetContext())->declaration_context());
+ for (; i < scope_info->LocalCount(); ++i) {
+ if (scope_info->LocalIsSynthetic(i)) continue;
+ Handle<String> name(scope_info->LocalName(i));
+ VariableMode mode;
+ InitializationFlag init_flag;
+ MaybeAssignedFlag maybe_assigned_flag;
+ locals->set(local * 2, *name);
+ int context_slot_index = ScopeInfo::ContextSlotIndex(
+ scope_info, name, &mode, &init_flag, &maybe_assigned_flag);
+ Object* value = context->get(context_slot_index);
+ locals->set(local * 2 + 1, value);
+ local++;
+ }
+ }
+
+ // Check whether this frame is positioned at return. If not top
+ // frame or if the frame is optimized it cannot be at a return.
+ bool at_return = false;
+ if (!is_optimized && index == 0) {
+ at_return = isolate->debug()->IsBreakAtReturn(it.frame());
+ }
+
+ // If positioned just before return find the value to be returned and add it
+ // to the frame information.
+ Handle<Object> return_value = isolate->factory()->undefined_value();
+ if (at_return) {
+ StackFrameIterator it2(isolate);
+ Address internal_frame_sp = NULL;
+ while (!it2.done()) {
+ if (it2.frame()->is_internal()) {
+ internal_frame_sp = it2.frame()->sp();
+ } else {
+ if (it2.frame()->is_java_script()) {
+ if (it2.frame()->id() == it.frame()->id()) {
+ // The internal frame just before the JavaScript frame contains the
+ // value to return on top. A debug break at return will create an
+ // internal frame to store the return value (eax/rax/r0) before
+ // entering the debug break exit frame.
+ if (internal_frame_sp != NULL) {
+ return_value =
+ Handle<Object>(Memory::Object_at(internal_frame_sp), isolate);
+ break;
+ }
+ }
+ }
+
+ // Indicate that the previous frame was not an internal frame.
+ internal_frame_sp = NULL;
+ }
+ it2.Advance();
+ }
+ }
+
+ // Now advance to the arguments adapter frame (if any). It contains all
+ // the provided parameters whereas the function frame always have the number
+ // of arguments matching the functions parameters. The rest of the
+ // information (except for what is collected above) is the same.
+ if ((inlined_jsframe_index == 0) && it.frame()->has_adapted_arguments()) {
+ it.AdvanceToArgumentsFrame();
+ frame_inspector.SetArgumentsFrame(it.frame());
+ }
+
+ // Find the number of arguments to fill. At least fill the number of
+ // parameters for the function and fill more if more parameters are provided.
+ int argument_count = scope_info->ParameterCount();
+ if (argument_count < frame_inspector.GetParametersCount()) {
+ argument_count = frame_inspector.GetParametersCount();
+ }
+
+ // Calculate the size of the result.
+ int details_size = kFrameDetailsFirstDynamicIndex +
+ 2 * (argument_count + local_count) + (at_return ? 1 : 0);
+ Handle<FixedArray> details = isolate->factory()->NewFixedArray(details_size);
+
+ // Add the frame id.
+ details->set(kFrameDetailsFrameIdIndex, *frame_id);
+
+ // Add the function (same as in function frame).
+ details->set(kFrameDetailsFunctionIndex, frame_inspector.GetFunction());
+
+ // Add the arguments count.
+ details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
+
+ // Add the locals count
+ details->set(kFrameDetailsLocalCountIndex, Smi::FromInt(local_count));
+
+ // Add the source position.
+ if (position != RelocInfo::kNoPosition) {
+ details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
+ } else {
+ details->set(kFrameDetailsSourcePositionIndex, heap->undefined_value());
+ }
+
+ // Add the constructor information.
+ details->set(kFrameDetailsConstructCallIndex, heap->ToBoolean(constructor));
+
+ // Add the at return information.
+ details->set(kFrameDetailsAtReturnIndex, heap->ToBoolean(at_return));
+
+ // Add flags to indicate information on whether this frame is
+ // bit 0: invoked in the debugger context.
+ // bit 1: optimized frame.
+ // bit 2: inlined in optimized frame
+ int flags = 0;
+ if (*save->context() == *isolate->debug()->debug_context()) {
+ flags |= 1 << 0;
+ }
+ if (is_optimized) {
+ flags |= 1 << 1;
+ flags |= inlined_jsframe_index << 2;
+ }
+ details->set(kFrameDetailsFlagsIndex, Smi::FromInt(flags));
+
+ // Fill the dynamic part.
+ int details_index = kFrameDetailsFirstDynamicIndex;
+
+ // Add arguments name and value.
+ for (int i = 0; i < argument_count; i++) {
+ // Name of the argument.
+ if (i < scope_info->ParameterCount()) {
+ details->set(details_index++, scope_info->ParameterName(i));
+ } else {
+ details->set(details_index++, heap->undefined_value());
+ }
+
+ // Parameter value.
+ if (i < frame_inspector.GetParametersCount()) {
+ // Get the value from the stack.
+ details->set(details_index++, frame_inspector.GetParameter(i));
+ } else {
+ details->set(details_index++, heap->undefined_value());
+ }
+ }
+
+ // Add locals name and value from the temporary copy from the function frame.
+ for (int i = 0; i < local_count * 2; i++) {
+ details->set(details_index++, locals->get(i));
+ }
+
+ // Add the value being returned.
+ if (at_return) {
+ details->set(details_index++, *return_value);
+ }
+
+ // Add the receiver (same as in function frame).
+ // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
+ // THE FRAME ITERATOR TO WRAP THE RECEIVER.
+ Handle<Object> receiver(it.frame()->receiver(), isolate);
+ if (!receiver->IsJSObject() && shared->strict_mode() == SLOPPY &&
+ !function->IsBuiltin()) {
+ // If the receiver is not a JSObject and the function is not a
+ // builtin or strict-mode we have hit an optimization where a
+ // value object is not converted into a wrapped JS objects. To
+ // hide this optimization from the debugger, we wrap the receiver
+ // by creating correct wrapper object based on the calling frame's
+ // native context.
+ it.Advance();
+ if (receiver->IsUndefined()) {
+ receiver = handle(function->global_proxy());
+ } else {
+ Context* context = Context::cast(it.frame()->context());
+ Handle<Context> native_context(Context::cast(context->native_context()));
+ if (!Object::ToObject(isolate, receiver, native_context)
+ .ToHandle(&receiver)) {
+ // This only happens if the receiver is forcibly set in %_CallFunction.
+ return heap->undefined_value();
+ }
+ }
+ }
+ details->set(kFrameDetailsReceiverIndex, *receiver);
+
+ DCHECK_EQ(details_size, details_index);
+ return *isolate->factory()->NewJSArrayWithElements(details);
+}
+
+
+static bool ParameterIsShadowedByContextLocal(Handle<ScopeInfo> info,
+ Handle<String> parameter_name) {
+ VariableMode mode;
+ InitializationFlag init_flag;
+ MaybeAssignedFlag maybe_assigned_flag;
+ return ScopeInfo::ContextSlotIndex(info, parameter_name, &mode, &init_flag,
+ &maybe_assigned_flag) != -1;
+}
+
+
+// Create a plain JSObject which materializes the local scope for the specified
+// frame.
+MUST_USE_RESULT
+static MaybeHandle<JSObject> MaterializeStackLocalsWithFrameInspector(
+ Isolate* isolate, Handle<JSObject> target, Handle<JSFunction> function,
+ FrameInspector* frame_inspector) {
+ Handle<SharedFunctionInfo> shared(function->shared());
+ Handle<ScopeInfo> scope_info(shared->scope_info());
+
+ // 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;
+
+ HandleScope scope(isolate);
+ Handle<Object> value(i < frame_inspector->GetParametersCount()
+ ? frame_inspector->GetParameter(i)
+ : isolate->heap()->undefined_value(),
+ isolate);
+ DCHECK(!value->IsTheHole());
+
+ RETURN_ON_EXCEPTION(isolate, Runtime::SetObjectProperty(
+ isolate, target, name, value, SLOPPY),
+ JSObject);
+ }
+
+ // 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(i), isolate);
+ if (value->IsTheHole()) continue;
+
+ RETURN_ON_EXCEPTION(isolate, Runtime::SetObjectProperty(
+ isolate, target, name, value, SLOPPY),
+ JSObject);
+ }
+
+ return target;
+}
+
+
+static void UpdateStackLocalsFromMaterializedObject(Isolate* isolate,
+ Handle<JSObject> target,
+ Handle<JSFunction> function,
+ JavaScriptFrame* frame,
+ int inlined_jsframe_index) {
+ if (inlined_jsframe_index != 0 || frame->is_optimized()) {
+ // Optimized frames are not supported.
+ // TODO(yangguo): make sure all code deoptimized when debugger is active
+ // and assert that this cannot happen.
+ return;
+ }
+
+ Handle<SharedFunctionInfo> shared(function->shared());
+ Handle<ScopeInfo> scope_info(shared->scope_info());
+
+ // 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;
+ if (frame->GetExpression(i)->IsTheHole()) continue;
+ HandleScope scope(isolate);
+ Handle<Object> value = Object::GetPropertyOrElement(
+ target, handle(scope_info->StackLocalName(i),
+ isolate)).ToHandleChecked();
+ frame->SetExpression(i, *value);
+ }
+}
+
+
+MUST_USE_RESULT static MaybeHandle<JSObject> MaterializeLocalContext(
+ Isolate* isolate, Handle<JSObject> target, Handle<JSFunction> function,
+ JavaScriptFrame* frame) {
+ 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> frame_context(Context::cast(frame->context()));
+ Handle<Context> function_context(frame_context->declaration_context());
+ if (!ScopeInfo::CopyContextLocalsToScopeObject(scope_info, function_context,
+ target)) {
+ return MaybeHandle<JSObject>();
+ }
+
+ // 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> MaterializeLocalScope(
+ Isolate* isolate, JavaScriptFrame* frame, int inlined_jsframe_index) {
+ FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate);
+ Handle<JSFunction> function(JSFunction::cast(frame_inspector.GetFunction()));
+
+ Handle<JSObject> local_scope =
+ isolate->factory()->NewJSObject(isolate->object_function());
+ ASSIGN_RETURN_ON_EXCEPTION(
+ isolate, local_scope,
+ MaterializeStackLocalsWithFrameInspector(isolate, local_scope, function,
+ &frame_inspector),
+ JSObject);
+
+ return MaterializeLocalContext(isolate, local_scope, function, frame);
+}
+
+
+// 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;
+ InitializationFlag init_flag;
+ MaybeAssignedFlag maybe_assigned_flag;
+ int context_index = ScopeInfo::ContextSlotIndex(
+ scope_info, next_name, &mode, &init_flag, &maybe_assigned_flag);
+ context->set(context_index, *new_value);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+static bool SetLocalVariableValue(Isolate* isolate, JavaScriptFrame* frame,
+ int inlined_jsframe_index,
+ Handle<String> variable_name,
+ Handle<Object> new_value) {
+ if (inlined_jsframe_index != 0 || frame->is_optimized()) {
+ // Optimized frames are not supported.
+ 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(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.has_value);
+ if (maybe.value) {
+ // 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;
+}
+
+
+// Create a plain JSObject which materializes the closure content for the
+// context.
+MUST_USE_RESULT static MaybeHandle<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.
+ if (!ScopeInfo::CopyContextLocalsToScopeObject(scope_info, context,
+ closure_scope)) {
+ return MaybeHandle<JSObject>();
+ }
+
+ // 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()));
+ Handle<FixedArray> keys;
+ ASSIGN_RETURN_ON_EXCEPTION(
+ isolate, keys, JSReceiver::GetKeys(ext, JSReceiver::INCLUDE_PROTOS),
+ JSObject);
+
+ 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;
+ ASSIGN_RETURN_ON_EXCEPTION(
+ isolate, value, Object::GetPropertyOrElement(ext, key), JSObject);
+ RETURN_ON_EXCEPTION(isolate, Runtime::DefineObjectProperty(
+ closure_scope, key, value, NONE),
+ JSObject);
+ }
+ }
+
+ 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()));
+ Maybe<bool> maybe = JSReceiver::HasProperty(ext, variable_name);
+ DCHECK(maybe.has_value);
+ if (maybe.value) {
+ // We don't expect this to do anything except replacing property value.
+ Runtime::DefineObjectProperty(ext, variable_name, new_value, NONE)
+ .Assert();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+// Create a plain JSObject which materializes the scope for the specified
+// catch context.
+MUST_USE_RESULT static MaybeHandle<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());
+ RETURN_ON_EXCEPTION(isolate, Runtime::DefineObjectProperty(
+ catch_scope, name, thrown_object, NONE),
+ JSObject);
+ 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.
+MUST_USE_RESULT static MaybeHandle<JSObject> MaterializeBlockScope(
+ Isolate* isolate, Handle<Context> context) {
+ DCHECK(context->IsBlockContext());
+ Handle<ScopeInfo> scope_info(ScopeInfo::cast(context->extension()));
+
+ // Allocate and initialize a JSObject with all the arguments, stack locals
+ // heap locals and extension properties of the debugged function.
+ Handle<JSObject> block_scope =
+ isolate->factory()->NewJSObject(isolate->object_function());
+
+ // Fill all context locals.
+ if (!ScopeInfo::CopyContextLocalsToScopeObject(scope_info, context,
+ block_scope)) {
+ return MaybeHandle<JSObject>();
+ }
+
+ 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.
+ if (!ScopeInfo::CopyContextLocalsToScopeObject(scope_info, context,
+ module_scope)) {
+ return MaybeHandle<JSObject>();
+ }
+
+ 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,
+ ScopeTypeModule
+ };
+
+ ScopeIterator(Isolate* isolate, JavaScriptFrame* frame,
+ int inlined_jsframe_index, bool ignore_nested_scopes = false)
+ : isolate_(isolate),
+ frame_(frame),
+ inlined_jsframe_index_(inlined_jsframe_index),
+ function_(frame->function()),
+ context_(Context::cast(frame->context())),
+ nested_scope_chain_(4),
+ failed_(false) {
+ // 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;
+ }
+
+ // Get the debug info (create it if it does not exist).
+ if (!isolate->debug()->EnsureDebugInfo(shared_info, function_)) {
+ // Return if ensuring debug info failed.
+ 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) {
+ Handle<DebugInfo> debug_info = Debug::GetDebugInfo(shared_info);
+
+ // Find the break point where execution has stopped.
+ BreakLocationIterator break_location_iterator(debug_info,
+ ALL_BREAK_LOCATIONS);
+ // pc points to the instruction after the current one, possibly a break
+ // location as well. So the "- 1" to exclude it from the search.
+ break_location_iterator.FindBreakLocationFromAddress(frame->pc() - 1);
+
+ // Within the return sequence at the moment it is not possible to
+ // get a source position which is consistent with the current scope chain.
+ // Thus all nested with, catch and block contexts are skipped and we only
+ // provide the function scope.
+ ignore_nested_scopes = break_location_iterator.IsExit();
+ }
+
+ 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) {
+ 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());
+ if (scope_info->scope_type() != FUNCTION_SCOPE) {
+ // Global or eval code.
+ CompilationInfoWithZone info(script);
+ if (scope_info->scope_type() == GLOBAL_SCOPE) {
+ info.MarkAsGlobal();
+ } else {
+ DCHECK(scope_info->scope_type() == EVAL_SCOPE);
+ info.MarkAsEval();
+ info.SetContext(Handle<Context>(function_->context()));
+ }
+ if (Parser::Parse(&info) && Scope::Analyze(&info)) {
+ scope = info.function()->scope();
+ }
+ RetrieveScopeChain(scope, shared_info);
+ } else {
+ // Function code
+ CompilationInfoWithZone info(shared_info);
+ if (Parser::Parse(&info) && Scope::Analyze(&info)) {
+ scope = info.function()->scope();
+ }
+ RetrieveScopeChain(scope, shared_info);
+ }
+ }
+ }
+
+ ScopeIterator(Isolate* isolate, Handle<JSFunction> function)
+ : isolate_(isolate),
+ frame_(NULL),
+ inlined_jsframe_index_(0),
+ function_(function),
+ context_(function->context()),
+ 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 (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:
+ DCHECK(context_->IsFunctionContext() || !scope_info->HasContext());
+ return ScopeTypeLocal;
+ case MODULE_SCOPE:
+ DCHECK(context_->IsModuleContext());
+ return ScopeTypeModule;
+ case GLOBAL_SCOPE:
+ DCHECK(context_->IsNativeContext());
+ return ScopeTypeGlobal;
+ 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());
+ return ScopeTypeGlobal;
+ }
+ if (context_->IsFunctionContext()) {
+ return ScopeTypeClosure;
+ }
+ if (context_->IsCatchContext()) {
+ return ScopeTypeCatch;
+ }
+ if (context_->IsBlockContext()) {
+ return ScopeTypeBlock;
+ }
+ if (context_->IsModuleContext()) {
+ return ScopeTypeModule;
+ }
+ 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::ScopeTypeLocal:
+ // Materialize the content of the local scope into a JSObject.
+ DCHECK(nested_scope_chain_.length() == 1);
+ return MaterializeLocalScope(isolate_, frame_, inlined_jsframe_index_);
+ 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:
+ return MaterializeBlockScope(isolate_, CurrentContext());
+ case ScopeIterator::ScopeTypeModule:
+ return MaterializeModuleScope(isolate_, CurrentContext());
+ }
+ UNREACHABLE();
+ return Handle<JSObject>();
+ }
+
+ 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_, inlined_jsframe_index_,
+ 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::ScopeTypeBlock:
+ // TODO(2399): should we implement it?
+ break;
+ 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 || 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;
+
+ default:
+ UNREACHABLE();
+ }
+ PrintF("\n");
+ }
+#endif
+
+ private:
+ Isolate* isolate_;
+ JavaScriptFrame* frame_;
+ int inlined_jsframe_index_;
+ Handle<JSFunction> function_;
+ Handle<Context> context_;
+ List<Handle<ScopeInfo> > nested_scope_chain_;
+ bool failed_;
+
+ void RetrieveScopeChain(Scope* scope,
+ Handle<SharedFunctionInfo> shared_info) {
+ if (scope != NULL) {
+ int source_position = shared_info->code()->SourcePosition(frame_->pc());
+ scope->GetNestedScopeChain(&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_NUMBER_CHECKED(int, break_id, Int32, args[0]);
+ RUNTIME_ASSERT(isolate->debug()->CheckExecutionState(break_id));
+
+ CONVERT_SMI_ARG_CHECKED(wrapped_id, 1);
+
+ // Get the frame where the debugging is performed.
+ StackFrame::Id id = UnwrapFrameId(wrapped_id);
+ JavaScriptFrameIterator it(isolate, id);
+ JavaScriptFrame* frame = it.frame();
+
+ // Count the visible scopes.
+ int n = 0;
+ for (ScopeIterator it(isolate, frame, 0); !it.Done(); it.Next()) {
+ n++;
+ }
+
+ return Smi::FromInt(n);
+}
+
+
+// Returns the list of step-in positions (text offset) in a function of the
+// stack frame in a range from the current debug break position to the end
+// of the corresponding statement.
+RUNTIME_FUNCTION(Runtime_GetStepInPositions) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+ CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
+ RUNTIME_ASSERT(isolate->debug()->CheckExecutionState(break_id));
+
+ CONVERT_SMI_ARG_CHECKED(wrapped_id, 1);
+
+ // Get the frame where the debugging is performed.
+ StackFrame::Id id = UnwrapFrameId(wrapped_id);
+ JavaScriptFrameIterator frame_it(isolate, id);
+ RUNTIME_ASSERT(!frame_it.done());
+
+ JavaScriptFrame* frame = frame_it.frame();
+
+ Handle<JSFunction> fun = Handle<JSFunction>(frame->function());
+ Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>(fun->shared());
+
+ if (!isolate->debug()->EnsureDebugInfo(shared, fun)) {
+ return isolate->heap()->undefined_value();
+ }
+
+ Handle<DebugInfo> debug_info = Debug::GetDebugInfo(shared);
+
+ int len = 0;
+ Handle<JSArray> array(isolate->factory()->NewJSArray(10));
+ // Find the break point where execution has stopped.
+ BreakLocationIterator break_location_iterator(debug_info,
+ ALL_BREAK_LOCATIONS);
+
+ break_location_iterator.FindBreakLocationFromAddress(frame->pc() - 1);
+ int current_statement_pos = break_location_iterator.statement_position();
+
+ while (!break_location_iterator.Done()) {
+ bool accept;
+ if (break_location_iterator.pc() > frame->pc()) {
+ accept = true;
+ } else {
+ StackFrame::Id break_frame_id = isolate->debug()->break_frame_id();
+ // The break point is near our pc. Could be a step-in possibility,
+ // that is currently taken by active debugger call.
+ if (break_frame_id == StackFrame::NO_ID) {
+ // We are not stepping.
+ accept = false;
+ } else {
+ JavaScriptFrameIterator additional_frame_it(isolate, break_frame_id);
+ // If our frame is a top frame and we are stepping, we can do step-in
+ // at this place.
+ accept = additional_frame_it.frame()->id() == id;
+ }
+ }
+ if (accept) {
+ if (break_location_iterator.IsStepInLocation(isolate)) {
+ Smi* position_value = Smi::FromInt(break_location_iterator.position());
+ RETURN_FAILURE_ON_EXCEPTION(
+ isolate, JSObject::SetElement(
+ array, len, Handle<Object>(position_value, isolate),
+ NONE, SLOPPY));
+ len++;
+ }
+ }
+ // Advance iterator.
+ break_location_iterator.Next();
+ if (current_statement_pos != break_location_iterator.statement_position()) {
+ break;
+ }
+ }
+ return *array;
+}
+
+
+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
+// args[2]: number: inlined frame index
+// args[3]: number: scope index
+//
+// The array returned contains the following information:
+// 0: Scope type
+// 1: Scope object
+RUNTIME_FUNCTION(Runtime_GetScopeDetails) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 4);
+ CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
+ RUNTIME_ASSERT(isolate->debug()->CheckExecutionState(break_id));
+
+ CONVERT_SMI_ARG_CHECKED(wrapped_id, 1);
+ CONVERT_NUMBER_CHECKED(int, inlined_jsframe_index, Int32, args[2]);
+ CONVERT_NUMBER_CHECKED(int, index, Int32, args[3]);
+
+ // Get the frame where the debugging is performed.
+ StackFrame::Id id = UnwrapFrameId(wrapped_id);
+ JavaScriptFrameIterator frame_it(isolate, id);
+ JavaScriptFrame* frame = frame_it.frame();
+
+ // Find the requested scope.
+ int n = 0;
+ ScopeIterator it(isolate, frame, inlined_jsframe_index);
+ for (; !it.Done() && n < index; it.Next()) {
+ n++;
+ }
+ if (it.Done()) {
+ return isolate->heap()->undefined_value();
+ }
+ Handle<JSObject> details;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, details,
+ MaterializeScopeDetails(isolate, &it));
+ return *details;
+}
+
+
+// Return an array of scope details
+// args[0]: number: break id
+// args[1]: number: frame index
+// args[2]: number: inlined frame index
+// args[3]: boolean: ignore nested scopes
+//
+// The array returned contains arrays with the following information:
+// 0: Scope type
+// 1: Scope object
+RUNTIME_FUNCTION(Runtime_GetAllScopesDetails) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 3 || args.length() == 4);
+ CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
+ RUNTIME_ASSERT(isolate->debug()->CheckExecutionState(break_id));
+
+ CONVERT_SMI_ARG_CHECKED(wrapped_id, 1);
+ CONVERT_NUMBER_CHECKED(int, inlined_jsframe_index, Int32, args[2]);
+
+ bool ignore_nested_scopes = false;
+ if (args.length() == 4) {
+ CONVERT_BOOLEAN_ARG_CHECKED(flag, 3);
+ ignore_nested_scopes = flag;
+ }
+
+ // Get the frame where the debugging is performed.
+ StackFrame::Id id = UnwrapFrameId(wrapped_id);
+ JavaScriptFrameIterator frame_it(isolate, id);
+ JavaScriptFrame* frame = frame_it.frame();
+
+ List<Handle<JSObject> > result(4);
+ ScopeIterator it(isolate, frame, inlined_jsframe_index, ignore_nested_scopes);
+ for (; !it.Done(); it.Next()) {
+ Handle<JSObject> details;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, details,
+ MaterializeScopeDetails(isolate, &it));
+ result.Add(details);
+ }
+
+ Handle<FixedArray> array = isolate->factory()->NewFixedArray(result.length());
+ for (int i = 0; i < result.length(); ++i) {
+ array->set(i, *result[i]);
+ }
+ return *isolate->factory()->NewJSArrayWithElements(array);
+}
+
+
+RUNTIME_FUNCTION(Runtime_GetFunctionScopeCount) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+
+ // Check arguments.
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, fun, 0);
+
+ // Count the visible scopes.
+ int n = 0;
+ for (ScopeIterator it(isolate, fun); !it.Done(); it.Next()) {
+ n++;
+ }
+
+ return Smi::FromInt(n);
+}
+
+
+RUNTIME_FUNCTION(Runtime_GetFunctionScopeDetails) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+
+ // Check arguments.
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, fun, 0);
+ CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
+
+ // Find the requested scope.
+ int n = 0;
+ ScopeIterator it(isolate, fun);
+ for (; !it.Done() && n < index; it.Next()) {
+ n++;
+ }
+ if (it.Done()) {
+ return isolate->heap()->undefined_value();
+ }
+
+ Handle<JSObject> details;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, details,
+ MaterializeScopeDetails(isolate, &it));
+ return *details;
+}
+
+
+static bool SetScopeVariableValue(ScopeIterator* it, int index,
+ Handle<String> variable_name,
+ Handle<Object> new_value) {
+ for (int n = 0; !it->Done() && n < index; it->Next()) {
+ n++;
+ }
+ if (it->Done()) {
+ return false;
+ }
+ return it->SetVariableValue(variable_name, new_value);
+}
+
+
+// Change variable value in closure or local scope
+// args[0]: number or JsFunction: break id or function
+// args[1]: number: frame index (when arg[0] is break id)
+// args[2]: number: inlined frame index (when arg[0] is break id)
+// args[3]: number: scope index
+// args[4]: string: variable name
+// args[5]: object: new value
+//
+// Return true if success and false otherwise
+RUNTIME_FUNCTION(Runtime_SetScopeVariableValue) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 6);
+
+ // Check arguments.
+ CONVERT_NUMBER_CHECKED(int, index, Int32, args[3]);
+ CONVERT_ARG_HANDLE_CHECKED(String, variable_name, 4);
+ CONVERT_ARG_HANDLE_CHECKED(Object, new_value, 5);
+
+ bool res;
+ if (args[0]->IsNumber()) {
+ CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
+ RUNTIME_ASSERT(isolate->debug()->CheckExecutionState(break_id));
+
+ CONVERT_SMI_ARG_CHECKED(wrapped_id, 1);
+ CONVERT_NUMBER_CHECKED(int, inlined_jsframe_index, Int32, args[2]);
+
+ // Get the frame where the debugging is performed.
+ StackFrame::Id id = UnwrapFrameId(wrapped_id);
+ JavaScriptFrameIterator frame_it(isolate, id);
+ JavaScriptFrame* frame = frame_it.frame();
+
+ ScopeIterator it(isolate, frame, inlined_jsframe_index);
+ res = SetScopeVariableValue(&it, index, variable_name, new_value);
+ } else {
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, fun, 0);
+ ScopeIterator it(isolate, fun);
+ res = SetScopeVariableValue(&it, index, variable_name, new_value);
+ }
+
+ return isolate->heap()->ToBoolean(res);
+}
+
+
+RUNTIME_FUNCTION(Runtime_DebugPrintScopes) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 0);
+
+#ifdef DEBUG
+ // Print the scopes for the top frame.
+ StackFrameLocator locator(isolate);
+ JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
+ for (ScopeIterator it(isolate, frame, 0); !it.Done(); it.Next()) {
+ it.DebugPrint();
+ }
+#endif
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(Runtime_GetThreadCount) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
+ RUNTIME_ASSERT(isolate->debug()->CheckExecutionState(break_id));
+
+ // Count all archived V8 threads.
+ int n = 0;
+ for (ThreadState* thread = isolate->thread_manager()->FirstThreadStateInUse();
+ thread != NULL; thread = thread->Next()) {
+ n++;
+ }
+
+ // Total number of threads is current thread and archived threads.
+ return Smi::FromInt(n + 1);
+}
+
+
+static const int kThreadDetailsCurrentThreadIndex = 0;
+static const int kThreadDetailsThreadIdIndex = 1;
+static const int kThreadDetailsSize = 2;
+
+// Return an array with thread details
+// args[0]: number: break id
+// args[1]: number: thread index
+//
+// The array returned contains the following information:
+// 0: Is current thread?
+// 1: Thread id
+RUNTIME_FUNCTION(Runtime_GetThreadDetails) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+ CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
+ RUNTIME_ASSERT(isolate->debug()->CheckExecutionState(break_id));
+
+ CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
+
+ // Allocate array for result.
+ Handle<FixedArray> details =
+ isolate->factory()->NewFixedArray(kThreadDetailsSize);
+
+ // Thread index 0 is current thread.
+ if (index == 0) {
+ // Fill the details.
+ details->set(kThreadDetailsCurrentThreadIndex,
+ isolate->heap()->true_value());
+ details->set(kThreadDetailsThreadIdIndex,
+ Smi::FromInt(ThreadId::Current().ToInteger()));
+ } else {
+ // Find the thread with the requested index.
+ int n = 1;
+ ThreadState* thread = isolate->thread_manager()->FirstThreadStateInUse();
+ while (index != n && thread != NULL) {
+ thread = thread->Next();
+ n++;
+ }
+ if (thread == NULL) {
+ return isolate->heap()->undefined_value();
+ }
+
+ // Fill the details.
+ details->set(kThreadDetailsCurrentThreadIndex,
+ isolate->heap()->false_value());
+ details->set(kThreadDetailsThreadIdIndex,
+ Smi::FromInt(thread->id().ToInteger()));
+ }
+
+ // Convert to JS array and return.
+ return *isolate->factory()->NewJSArrayWithElements(details);
+}
+
+
+// Sets the disable break state
+// args[0]: disable break state
+RUNTIME_FUNCTION(Runtime_SetDisableBreak) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_BOOLEAN_ARG_CHECKED(disable_break, 0);
+ isolate->debug()->set_disable_break(disable_break);
+ return isolate->heap()->undefined_value();
+}
+
+
+static bool IsPositionAlignmentCodeCorrect(int alignment) {
+ return alignment == STATEMENT_ALIGNED || alignment == BREAK_POSITION_ALIGNED;
+}
+
+
+RUNTIME_FUNCTION(Runtime_GetBreakLocations) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, fun, 0);
+ CONVERT_NUMBER_CHECKED(int32_t, statement_aligned_code, Int32, args[1]);
+
+ if (!IsPositionAlignmentCodeCorrect(statement_aligned_code)) {
+ return isolate->ThrowIllegalOperation();
+ }
+ BreakPositionAlignment alignment =
+ static_cast<BreakPositionAlignment>(statement_aligned_code);
+
+ Handle<SharedFunctionInfo> shared(fun->shared());
+ // Find the number of break points
+ Handle<Object> break_locations =
+ Debug::GetSourceBreakLocations(shared, alignment);
+ if (break_locations->IsUndefined()) return isolate->heap()->undefined_value();
+ // Return array as JS array
+ return *isolate->factory()->NewJSArrayWithElements(
+ Handle<FixedArray>::cast(break_locations));
+}
+
+
+// Set a break point in a function.
+// args[0]: function
+// args[1]: number: break source position (within the function source)
+// args[2]: number: break point object
+RUNTIME_FUNCTION(Runtime_SetFunctionBreakPoint) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 3);
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
+ CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
+ RUNTIME_ASSERT(source_position >= function->shared()->start_position() &&
+ source_position <= function->shared()->end_position());
+ CONVERT_ARG_HANDLE_CHECKED(Object, break_point_object_arg, 2);
+
+ // Set break point.
+ RUNTIME_ASSERT(isolate->debug()->SetBreakPoint(
+ function, break_point_object_arg, &source_position));
+
+ return Smi::FromInt(source_position);
+}
+
+
+// Changes the state of a break point in a script and returns source position
+// where break point was set. NOTE: Regarding performance see the NOTE for
+// GetScriptFromScriptData.
+// args[0]: script to set break point in
+// args[1]: number: break source position (within the script source)
+// args[2]: number, breakpoint position alignment
+// args[3]: number: break point object
+RUNTIME_FUNCTION(Runtime_SetScriptBreakPoint) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 4);
+ CONVERT_ARG_HANDLE_CHECKED(JSValue, wrapper, 0);
+ CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
+ RUNTIME_ASSERT(source_position >= 0);
+ CONVERT_NUMBER_CHECKED(int32_t, statement_aligned_code, Int32, args[2]);
+ CONVERT_ARG_HANDLE_CHECKED(Object, break_point_object_arg, 3);
+
+ if (!IsPositionAlignmentCodeCorrect(statement_aligned_code)) {
+ return isolate->ThrowIllegalOperation();
+ }
+ BreakPositionAlignment alignment =
+ static_cast<BreakPositionAlignment>(statement_aligned_code);
+
+ // Get the script from the script wrapper.
+ RUNTIME_ASSERT(wrapper->value()->IsScript());
+ Handle<Script> script(Script::cast(wrapper->value()));
+
+ // Set break point.
+ if (!isolate->debug()->SetBreakPointForScript(script, break_point_object_arg,
+ &source_position, alignment)) {
+ return isolate->heap()->undefined_value();
+ }
+
+ return Smi::FromInt(source_position);
+}
+
+
+// Clear a break point
+// args[0]: number: break point object
+RUNTIME_FUNCTION(Runtime_ClearBreakPoint) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(Object, break_point_object_arg, 0);
+
+ // Clear break point.
+ isolate->debug()->ClearBreakPoint(break_point_object_arg);
+
+ return isolate->heap()->undefined_value();
+}
+
+
+// Change the state of break on exceptions.
+// args[0]: Enum value indicating whether to affect caught/uncaught exceptions.
+// args[1]: Boolean indicating on/off.
+RUNTIME_FUNCTION(Runtime_ChangeBreakOnException) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+ CONVERT_NUMBER_CHECKED(uint32_t, type_arg, Uint32, args[0]);
+ CONVERT_BOOLEAN_ARG_CHECKED(enable, 1);
+
+ // If the number doesn't match an enum value, the ChangeBreakOnException
+ // function will default to affecting caught exceptions.
+ ExceptionBreakType type = static_cast<ExceptionBreakType>(type_arg);
+ // Update break point state.
+ isolate->debug()->ChangeBreakOnException(type, enable);
+ return isolate->heap()->undefined_value();
+}
+
+
+// Returns the state of break on exceptions
+// args[0]: boolean indicating uncaught exceptions
+RUNTIME_FUNCTION(Runtime_IsBreakOnException) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_NUMBER_CHECKED(uint32_t, type_arg, Uint32, args[0]);
+
+ ExceptionBreakType type = static_cast<ExceptionBreakType>(type_arg);
+ bool result = isolate->debug()->IsBreakOnException(type);
+ return Smi::FromInt(result);
+}
+
+
+// Prepare for stepping
+// args[0]: break id for checking execution state
+// args[1]: step action from the enumeration StepAction
+// args[2]: number of times to perform the step, for step out it is the number
+// of frames to step down.
+RUNTIME_FUNCTION(Runtime_PrepareStep) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 4);
+ CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
+ RUNTIME_ASSERT(isolate->debug()->CheckExecutionState(break_id));
+
+ if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
+ return isolate->Throw(isolate->heap()->illegal_argument_string());
+ }
+
+ CONVERT_NUMBER_CHECKED(int, wrapped_frame_id, Int32, args[3]);
+
+ StackFrame::Id frame_id;
+ if (wrapped_frame_id == 0) {
+ frame_id = StackFrame::NO_ID;
+ } else {
+ frame_id = UnwrapFrameId(wrapped_frame_id);
+ }
+
+ // Get the step action and check validity.
+ StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
+ if (step_action != StepIn && step_action != StepNext &&
+ step_action != StepOut && step_action != StepInMin &&
+ step_action != StepMin) {
+ return isolate->Throw(isolate->heap()->illegal_argument_string());
+ }
+
+ if (frame_id != StackFrame::NO_ID && step_action != StepNext &&
+ step_action != StepMin && step_action != StepOut) {
+ return isolate->ThrowIllegalOperation();
+ }
+
+ // Get the number of steps.
+ int step_count = NumberToInt32(args[2]);
+ if (step_count < 1) {
+ return isolate->Throw(isolate->heap()->illegal_argument_string());
+ }
+
+ // Clear all current stepping setup.
+ isolate->debug()->ClearStepping();
+
+ // Prepare step.
+ isolate->debug()->PrepareStep(static_cast<StepAction>(step_action),
+ step_count, frame_id);
+ return isolate->heap()->undefined_value();
+}
+
+
+// Clear all stepping set by PrepareStep.
+RUNTIME_FUNCTION(Runtime_ClearStepping) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 0);
+ isolate->debug()->ClearStepping();
+ return isolate->heap()->undefined_value();
+}
+
+
+// Helper function to find or create the arguments object for
+// Runtime_DebugEvaluate.
+MUST_USE_RESULT static MaybeHandle<JSObject> 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 target;
+ Maybe<bool> maybe = JSReceiver::HasOwnProperty(
+ target, isolate->factory()->arguments_string());
+ if (!maybe.has_value) return MaybeHandle<JSObject>();
+ if (maybe.value) return target;
+
+ // FunctionGetArguments can't throw an exception.
+ Handle<JSObject> arguments =
+ Handle<JSObject>::cast(Accessors::FunctionGetArguments(function));
+ Handle<String> arguments_str = isolate->factory()->arguments_string();
+ RETURN_ON_EXCEPTION(isolate, Runtime::DefineObjectProperty(
+ target, arguments_str, arguments, NONE),
+ JSObject);
+ return target;
+}
+
+
+// Compile and evaluate source for the given context.
+static MaybeHandle<Object> DebugEvaluate(Isolate* isolate,
+ 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, 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));
+ }
+
+ // Clear the oneshot breakpoints so that the debugger does not step further.
+ isolate->debug()->ClearStepping();
+ 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()));
+ new_map->set_prototype(*isolate->factory()->null_value());
+ JSObject::MigrateToMap(result, new_map);
+ return result;
+}
+
+
+// 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);
+
+ // Check the execution state and decode arguments frame and source to be
+ // evaluated.
+ DCHECK(args.length() == 6);
+ CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
+ RUNTIME_ASSERT(isolate->debug()->CheckExecutionState(break_id));
+
+ CONVERT_SMI_ARG_CHECKED(wrapped_id, 1);
+ CONVERT_NUMBER_CHECKED(int, inlined_jsframe_index, Int32, args[2]);
+ CONVERT_ARG_HANDLE_CHECKED(String, source, 3);
+ 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();
+ FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate);
+ Handle<JSFunction> function(JSFunction::cast(frame_inspector.GetFunction()));
+
+ // 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.
+ Handle<JSObject> materialized = NewJSObjectWithNullProto(isolate);
+
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, materialized,
+ MaterializeStackLocalsWithFrameInspector(isolate, materialized, function,
+ &frame_inspector));
+
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, materialized,
+ MaterializeArgumentsObject(isolate, materialized, function));
+
+ // At this point, the lookup chain may look like this:
+ // [inner context] -> [function stack]+[function context] -> [outer context]
+ // The function stack is not an actual context, it complements the function
+ // context. In order to have the same lookup chain when debug-evaluating,
+ // we materialize the stack and insert it into the context chain as a
+ // with-context before the function context.
+ // [inner context] -> [with context] -> [function context] -> [outer context]
+ // Ordering the with-context before the function context forces a dynamic
+ // lookup instead of a static lookup that could fail as the scope info is
+ // outdated and may expect variables to still be stack-allocated.
+ // Afterwards, we write changes to the with-context back to the stack
+ // and remove it from the context chain.
+ // This could cause lookup failures if debug-evaluate creates a closure that
+ // uses this temporary context chain.
+
+ Handle<Context> eval_context(Context::cast(frame_inspector.GetContext()));
+ DCHECK(!eval_context.is_null());
+ Handle<Context> function_context = eval_context;
+ Handle<Context> outer_context(function->context(), isolate);
+ Handle<Context> inner_context;
+ // We iterate to find the function's context. If the function has no
+ // context-allocated variables, we iterate until we hit the outer context.
+ while (!function_context->IsFunctionContext() &&
+ !function_context.is_identical_to(outer_context)) {
+ inner_context = function_context;
+ function_context = Handle<Context>(function_context->previous(), isolate);
+ }
+
+ Handle<Context> materialized_context = isolate->factory()->NewWithContext(
+ function, function_context, materialized);
+
+ if (inner_context.is_null()) {
+ // No inner context. The with-context is now inner-most.
+ eval_context = materialized_context;
+ } else {
+ inner_context->set_previous(*materialized_context);
+ }
+
+ Handle<Object> receiver(frame->receiver(), isolate);
+ MaybeHandle<Object> maybe_result =
+ DebugEvaluate(isolate, eval_context, context_extension, receiver, source);
+
+ // Remove with-context if it was inserted in between.
+ if (!inner_context.is_null()) inner_context->set_previous(*function_context);
+
+ Handle<Object> result;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, maybe_result);
+
+ // Write back potential changes to materialized stack locals to the stack.
+ UpdateStackLocalsFromMaterializedObject(isolate, materialized, function,
+ frame, inlined_jsframe_index);
+
+ return *result;
+}
+
+
+RUNTIME_FUNCTION(Runtime_DebugEvaluateGlobal) {
+ HandleScope scope(isolate);
+
+ // Check the execution state and decode arguments frame and source to be
+ // evaluated.
+ DCHECK(args.length() == 4);
+ CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
+ RUNTIME_ASSERT(isolate->debug()->CheckExecutionState(break_id));
+
+ CONVERT_ARG_HANDLE_CHECKED(String, source, 1);
+ 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 && *top->context() == *isolate->debug()->debug_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<Object> result;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, result,
+ DebugEvaluate(isolate, context, context_extension, receiver, source));
+ return *result;
+}
+
+
+RUNTIME_FUNCTION(Runtime_DebugGetLoadedScripts) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 0);
+
+ // Fill the script objects.
+ Handle<FixedArray> instances = isolate->debug()->GetLoadedScripts();
+
+ // Convert the script objects to proper JS objects.
+ for (int i = 0; i < instances->length(); i++) {
+ Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
+ // Get the script wrapper in a local handle before calling GetScriptWrapper,
+ // because using
+ // instances->set(i, *GetScriptWrapper(script))
+ // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
+ // already have dereferenced the instances handle.
+ Handle<JSObject> wrapper = Script::GetWrapper(script);
+ instances->set(i, *wrapper);
+ }
+
+ // Return result as a JS array.
+ Handle<JSObject> result =
+ isolate->factory()->NewJSObject(isolate->array_function());
+ JSArray::SetContent(Handle<JSArray>::cast(result), instances);
+ return *result;
+}
+
+
+// Helper function used by Runtime_DebugReferencedBy below.
+static int DebugReferencedBy(HeapIterator* iterator, JSObject* target,
+ Object* instance_filter, int max_references,
+ FixedArray* instances, int instances_size,
+ JSFunction* arguments_function) {
+ Isolate* isolate = target->GetIsolate();
+ SealHandleScope shs(isolate);
+ DisallowHeapAllocation no_allocation;
+
+ // Iterate the heap.
+ int count = 0;
+ JSObject* last = NULL;
+ HeapObject* heap_obj = NULL;
+ while (((heap_obj = iterator->next()) != NULL) &&
+ (max_references == 0 || count < max_references)) {
+ // Only look at all JSObjects.
+ if (heap_obj->IsJSObject()) {
+ // Skip context extension objects and argument arrays as these are
+ // checked in the context of functions using them.
+ JSObject* obj = JSObject::cast(heap_obj);
+ if (obj->IsJSContextExtensionObject() ||
+ obj->map()->constructor() == arguments_function) {
+ continue;
+ }
+
+ // Check if the JS object has a reference to the object looked for.
+ if (obj->ReferencesObject(target)) {
+ // Check instance filter if supplied. This is normally used to avoid
+ // references from mirror objects (see Runtime_IsInPrototypeChain).
+ if (!instance_filter->IsUndefined()) {
+ for (PrototypeIterator iter(isolate, obj); !iter.IsAtEnd();
+ iter.Advance()) {
+ if (iter.GetCurrent() == instance_filter) {
+ obj = NULL; // Don't add this object.
+ break;
+ }
+ }
+ }
+
+ if (obj != NULL) {
+ // Valid reference found add to instance array if supplied an update
+ // count.
+ if (instances != NULL && count < instances_size) {
+ instances->set(count, obj);
+ }
+ last = obj;
+ count++;
+ }
+ }
+ }
+ }
+
+ // Check for circular reference only. This can happen when the object is only
+ // referenced from mirrors and has a circular reference in which case the
+ // object is not really alive and would have been garbage collected if not
+ // referenced from the mirror.
+ if (count == 1 && last == target) {
+ count = 0;
+ }
+
+ // Return the number of referencing objects found.
+ return count;
+}
+
+
+// Scan the heap for objects with direct references to an object
+// args[0]: the object to find references to
+// args[1]: constructor function for instances to exclude (Mirror)
+// args[2]: the the maximum number of objects to return
+RUNTIME_FUNCTION(Runtime_DebugReferencedBy) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 3);
+
+ // Check parameters.
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, target, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, instance_filter, 1);
+ RUNTIME_ASSERT(instance_filter->IsUndefined() ||
+ instance_filter->IsJSObject());
+ CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
+ RUNTIME_ASSERT(max_references >= 0);
+
+
+ // Get the constructor function for context extension and arguments array.
+ Handle<JSFunction> arguments_function(
+ JSFunction::cast(isolate->sloppy_arguments_map()->constructor()));
+
+ // Get the number of referencing objects.
+ int count;
+ // First perform a full GC in order to avoid dead objects and to make the heap
+ // iterable.
+ Heap* heap = isolate->heap();
+ heap->CollectAllGarbage(Heap::kMakeHeapIterableMask, "%DebugConstructedBy");
+ {
+ HeapIterator heap_iterator(heap);
+ count = DebugReferencedBy(&heap_iterator, *target, *instance_filter,
+ max_references, NULL, 0, *arguments_function);
+ }
+
+ // Allocate an array to hold the result.
+ Handle<FixedArray> instances = isolate->factory()->NewFixedArray(count);
+
+ // Fill the referencing objects.
+ {
+ HeapIterator heap_iterator(heap);
+ count = DebugReferencedBy(&heap_iterator, *target, *instance_filter,
+ max_references, *instances, count,
+ *arguments_function);
+ }
+
+ // Return result as JS array.
+ Handle<JSFunction> constructor = isolate->array_function();
+
+ Handle<JSObject> result = isolate->factory()->NewJSObject(constructor);
+ JSArray::SetContent(Handle<JSArray>::cast(result), instances);
+ return *result;
+}
+
+
+// Helper function used by Runtime_DebugConstructedBy below.
+static int DebugConstructedBy(HeapIterator* iterator, JSFunction* constructor,
+ int max_references, FixedArray* instances,
+ int instances_size) {
+ DisallowHeapAllocation no_allocation;
+
+ // Iterate the heap.
+ int count = 0;
+ HeapObject* heap_obj = NULL;
+ while (((heap_obj = iterator->next()) != NULL) &&
+ (max_references == 0 || count < max_references)) {
+ // Only look at all JSObjects.
+ if (heap_obj->IsJSObject()) {
+ JSObject* obj = JSObject::cast(heap_obj);
+ if (obj->map()->constructor() == constructor) {
+ // Valid reference found add to instance array if supplied an update
+ // count.
+ if (instances != NULL && count < instances_size) {
+ instances->set(count, obj);
+ }
+ count++;
+ }
+ }
+ }
+
+ // Return the number of referencing objects found.
+ return count;
+}
+
+
+// Scan the heap for objects constructed by a specific function.
+// args[0]: the constructor to find instances of
+// args[1]: the the maximum number of objects to return
+RUNTIME_FUNCTION(Runtime_DebugConstructedBy) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+
+
+ // Check parameters.
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, 0);
+ CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
+ RUNTIME_ASSERT(max_references >= 0);
+
+ // Get the number of referencing objects.
+ int count;
+ // First perform a full GC in order to avoid dead objects and to make the heap
+ // iterable.
+ Heap* heap = isolate->heap();
+ heap->CollectAllGarbage(Heap::kMakeHeapIterableMask, "%DebugConstructedBy");
+ {
+ HeapIterator heap_iterator(heap);
+ count = DebugConstructedBy(&heap_iterator, *constructor, max_references,
+ NULL, 0);
+ }
+
+ // Allocate an array to hold the result.
+ Handle<FixedArray> instances = isolate->factory()->NewFixedArray(count);
+
+ // Fill the referencing objects.
+ {
+ HeapIterator heap_iterator2(heap);
+ count = DebugConstructedBy(&heap_iterator2, *constructor, max_references,
+ *instances, count);
+ }
+
+ // Return result as JS array.
+ Handle<JSFunction> array_function = isolate->array_function();
+ Handle<JSObject> result = isolate->factory()->NewJSObject(array_function);
+ JSArray::SetContent(Handle<JSArray>::cast(result), instances);
+ return *result;
+}
+
+
+// Find the effective prototype object as returned by __proto__.
+// args[0]: the object to find the prototype for.
+RUNTIME_FUNCTION(Runtime_DebugGetPrototype) {
+ HandleScope shs(isolate);
+ DCHECK(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0);
+ return *Object::GetPrototypeSkipHiddenPrototypes(isolate, obj);
+}
+
+
+// Patches script source (should be called upon BeforeCompile event).
+RUNTIME_FUNCTION(Runtime_DebugSetScriptSource) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+
+ CONVERT_ARG_HANDLE_CHECKED(JSValue, script_wrapper, 0);
+ CONVERT_ARG_HANDLE_CHECKED(String, source, 1);
+
+ RUNTIME_ASSERT(script_wrapper->value()->IsScript());
+ Handle<Script> script(Script::cast(script_wrapper->value()));
+
+ int compilation_state = script->compilation_state();
+ RUNTIME_ASSERT(compilation_state == Script::COMPILATION_STATE_INITIAL);
+ script->set_source(*source);
+
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(Runtime_DebugDisassembleFunction) {
+ HandleScope scope(isolate);
+#ifdef DEBUG
+ DCHECK(args.length() == 1);
+ // Get the function and make sure it is compiled.
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, func, 0);
+ if (!Compiler::EnsureCompiled(func, KEEP_EXCEPTION)) {
+ return isolate->heap()->exception();
+ }
+ OFStream os(stdout);
+ func->code()->Print(os);
+ os << endl;
+#endif // DEBUG
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(Runtime_DebugDisassembleConstructor) {
+ HandleScope scope(isolate);
+#ifdef DEBUG
+ DCHECK(args.length() == 1);
+ // Get the function and make sure it is compiled.
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, func, 0);
+ if (!Compiler::EnsureCompiled(func, KEEP_EXCEPTION)) {
+ return isolate->heap()->exception();
+ }
+ OFStream os(stdout);
+ func->shared()->construct_stub()->Print(os);
+ os << endl;
+#endif // DEBUG
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(Runtime_FunctionGetInferredName) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 1);
+
+ CONVERT_ARG_CHECKED(JSFunction, f, 0);
+ return f->shared()->inferred_name();
+}
+
+
+// A testing entry. Returns statement position which is the closest to
+// source_position.
+RUNTIME_FUNCTION(Runtime_GetFunctionCodePositionFromSource) {
+ HandleScope scope(isolate);
+ CHECK(isolate->debug()->live_edit_enabled());
+ DCHECK(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
+ CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
+
+ Handle<Code> code(function->code(), isolate);
+
+ if (code->kind() != Code::FUNCTION &&
+ code->kind() != Code::OPTIMIZED_FUNCTION) {
+ return isolate->heap()->undefined_value();
+ }
+
+ RelocIterator it(*code, RelocInfo::ModeMask(RelocInfo::STATEMENT_POSITION));
+ int closest_pc = 0;
+ int distance = kMaxInt;
+ while (!it.done()) {
+ int statement_position = static_cast<int>(it.rinfo()->data());
+ // Check if this break point is closer that what was previously found.
+ if (source_position <= statement_position &&
+ statement_position - source_position < distance) {
+ closest_pc =
+ static_cast<int>(it.rinfo()->pc() - code->instruction_start());
+ distance = statement_position - source_position;
+ // Check whether we can't get any closer.
+ if (distance == 0) break;
+ }
+ it.next();
+ }
+
+ return Smi::FromInt(closest_pc);
+}
+
+
+// Calls specified function with or without entering the debugger.
+// This is used in unit tests to run code as if debugger is entered or simply
+// to have a stack with C++ frame in the middle.
+RUNTIME_FUNCTION(Runtime_ExecuteInDebugContext) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
+ CONVERT_BOOLEAN_ARG_CHECKED(without_debugger, 1);
+
+ MaybeHandle<Object> maybe_result;
+ if (without_debugger) {
+ maybe_result = Execution::Call(isolate, function,
+ handle(function->global_proxy()), 0, NULL);
+ } else {
+ DebugScope debug_scope(isolate->debug());
+ maybe_result = Execution::Call(isolate, function,
+ handle(function->global_proxy()), 0, NULL);
+ }
+ Handle<Object> result;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, maybe_result);
+ return *result;
+}
+
+
+// Performs a GC.
+// Presently, it only does a full GC.
+RUNTIME_FUNCTION(Runtime_CollectGarbage) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 1);
+ isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags, "%CollectGarbage");
+ return isolate->heap()->undefined_value();
+}
+
+
+// Gets the current heap usage.
+RUNTIME_FUNCTION(Runtime_GetHeapUsage) {
+ SealHandleScope shs(isolate);
+ DCHECK(args.length() == 0);
+ int usage = static_cast<int>(isolate->heap()->SizeOfObjects());
+ if (!Smi::IsValid(usage)) {
+ return *isolate->factory()->NewNumberFromInt(usage);
+ }
+ return Smi::FromInt(usage);
+}
+
+
+// Finds the script object from the script data. NOTE: This operation uses
+// heap traversal to find the function generated for the source position
+// for the requested break point. For lazily compiled functions several heap
+// traversals might be required rendering this operation as a rather slow
+// operation. However for setting break points which is normally done through
+// some kind of user interaction the performance is not crucial.
+static Handle<Object> Runtime_GetScriptFromScriptName(
+ Handle<String> script_name) {
+ // Scan the heap for Script objects to find the script with the requested
+ // script data.
+ Handle<Script> script;
+ Factory* factory = script_name->GetIsolate()->factory();
+ Heap* heap = script_name->GetHeap();
+ HeapIterator iterator(heap);
+ HeapObject* obj = NULL;
+ while (script.is_null() && ((obj = iterator.next()) != NULL)) {
+ // If a script is found check if it has the script data requested.
+ if (obj->IsScript()) {
+ if (Script::cast(obj)->name()->IsString()) {
+ if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
+ script = Handle<Script>(Script::cast(obj));
+ }
+ }
+ }
+ }
+
+ // If no script with the requested script data is found return undefined.
+ if (script.is_null()) return factory->undefined_value();
+
+ // Return the script found.
+ return Script::GetWrapper(script);
+}
+
+
+// Get the script object from script data. NOTE: Regarding performance
+// see the NOTE for GetScriptFromScriptData.
+// args[0]: script data for the script to find the source for
+RUNTIME_FUNCTION(Runtime_GetScript) {
+ HandleScope scope(isolate);
+
+ DCHECK(args.length() == 1);
+
+ CONVERT_ARG_CHECKED(String, script_name, 0);
+
+ // Find the requested script.
+ Handle<Object> result =
+ Runtime_GetScriptFromScriptName(Handle<String>(script_name));
+ return *result;
+}
+
+
+// Check whether debugger and is about to step into the callback that is passed
+// to a built-in function such as Array.forEach.
+RUNTIME_FUNCTION(Runtime_DebugCallbackSupportsStepping) {
+ DCHECK(args.length() == 1);
+ if (!isolate->debug()->is_active() || !isolate->debug()->StepInActive()) {
+ return isolate->heap()->false_value();
+ }
+ CONVERT_ARG_CHECKED(Object, callback, 0);
+ // We do not step into the callback if it's a builtin or not even a function.
+ return isolate->heap()->ToBoolean(callback->IsJSFunction() &&
+ !JSFunction::cast(callback)->IsBuiltin());
+}
+
+
+// Set one shot breakpoints for the callback function that is passed to a
+// built-in function such as Array.forEach to enable stepping into the callback.
+RUNTIME_FUNCTION(Runtime_DebugPrepareStepInIfStepping) {
+ DCHECK(args.length() == 1);
+ Debug* debug = isolate->debug();
+ if (!debug->IsStepping()) return isolate->heap()->undefined_value();
+
+ HandleScope scope(isolate);
+ CONVERT_ARG_HANDLE_CHECKED(Object, object, 0);
+ RUNTIME_ASSERT(object->IsJSFunction() || object->IsJSGeneratorObject());
+ Handle<JSFunction> fun;
+ if (object->IsJSFunction()) {
+ fun = Handle<JSFunction>::cast(object);
+ } else {
+ fun = Handle<JSFunction>(
+ Handle<JSGeneratorObject>::cast(object)->function(), isolate);
+ }
+ // When leaving the function, step out has been activated, but not performed
+ // if we do not leave the builtin. To be able to step into the function
+ // again, we need to clear the step out at this point.
+ debug->ClearStepOut();
+ debug->FloodWithOneShot(fun);
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(Runtime_DebugPushPromise) {
+ DCHECK(args.length() == 1);
+ HandleScope scope(isolate);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, promise, 0);
+ isolate->PushPromise(promise);
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(Runtime_DebugPopPromise) {
+ DCHECK(args.length() == 0);
+ SealHandleScope shs(isolate);
+ isolate->PopPromise();
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(Runtime_DebugPromiseEvent) {
+ DCHECK(args.length() == 1);
+ HandleScope scope(isolate);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, data, 0);
+ isolate->debug()->OnPromiseEvent(data);
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(Runtime_DebugAsyncTaskEvent) {
+ DCHECK(args.length() == 1);
+ HandleScope scope(isolate);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, data, 0);
+ isolate->debug()->OnAsyncTaskEvent(data);
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(RuntimeReference_DebugIsActive) {
+ SealHandleScope shs(isolate);
+ return Smi::FromInt(isolate->debug()->is_active());
+}
+}
+} // namespace v8::internal
« no previous file with comments | « src/runtime/runtime-date.cc ('k') | src/runtime/runtime-generator.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698