"src/json.js",
"src/array-iterator.js",
"src/string-iterator.js",
- "src/debug-debugger.js",
- "src/mirror-debugger.js",
- "src/liveedit-debugger.js",
"src/templates.js",
"src/harmony-array.js",
"src/harmony-typedarray.js",
+ "src/debug/debug.js",
+ "src/debug/mirrors.js",
+ "src/debug/liveedit.js",
]
outputs = [
"src/dateparser-inl.h",
"src/dateparser.cc",
"src/dateparser.h",
- "src/debug.cc",
- "src/debug.h",
+ "src/debug/debug.cc",
+ "src/debug/debug.h",
+ "src/debug/liveedit.cc",
+ "src/debug/liveedit.h",
"src/deoptimizer.cc",
"src/deoptimizer.h",
"src/disasm.h",
"src/lithium-codegen.h",
"src/lithium.cc",
"src/lithium.h",
- "src/liveedit.cc",
- "src/liveedit.h",
"src/log-inl.h",
"src/log-utils.cc",
"src/log-utils.h",
"src/ia32/codegen-ia32.cc",
"src/ia32/codegen-ia32.h",
"src/ia32/cpu-ia32.cc",
- "src/ia32/debug-ia32.cc",
"src/ia32/deoptimizer-ia32.cc",
"src/ia32/disasm-ia32.cc",
"src/ia32/frames-ia32.cc",
"src/compiler/ia32/instruction-codes-ia32.h",
"src/compiler/ia32/instruction-selector-ia32.cc",
"src/compiler/ia32/linkage-ia32.cc",
+ "src/debug/ia32/debug-ia32.cc",
"src/full-codegen/ia32/full-codegen-ia32.cc",
"src/ic/ia32/access-compiler-ia32.cc",
"src/ic/ia32/handler-compiler-ia32.cc",
"src/x64/codegen-x64.cc",
"src/x64/codegen-x64.h",
"src/x64/cpu-x64.cc",
- "src/x64/debug-x64.cc",
"src/x64/deoptimizer-x64.cc",
"src/x64/disasm-x64.cc",
"src/x64/frames-x64.cc",
"src/compiler/x64/instruction-codes-x64.h",
"src/compiler/x64/instruction-selector-x64.cc",
"src/compiler/x64/linkage-x64.cc",
+ "src/debug/x64/debug-x64.cc",
"src/full-codegen/x64/full-codegen-x64.cc",
"src/ic/x64/access-compiler-x64.cc",
"src/ic/x64/handler-compiler-x64.cc",
"src/arm/constants-arm.h",
"src/arm/constants-arm.cc",
"src/arm/cpu-arm.cc",
- "src/arm/debug-arm.cc",
"src/arm/deoptimizer-arm.cc",
"src/arm/disasm-arm.cc",
"src/arm/frames-arm.cc",
"src/compiler/arm/instruction-codes-arm.h",
"src/compiler/arm/instruction-selector-arm.cc",
"src/compiler/arm/linkage-arm.cc",
+ "src/debug/arm/debug-arm.cc",
"src/full-codegen/arm/full-codegen-arm.cc",
"src/ic/arm/access-compiler-arm.cc",
"src/ic/arm/handler-compiler-arm.cc",
"src/arm64/code-stubs-arm64.h",
"src/arm64/constants-arm64.h",
"src/arm64/cpu-arm64.cc",
- "src/arm64/debug-arm64.cc",
"src/arm64/decoder-arm64.cc",
"src/arm64/decoder-arm64.h",
"src/arm64/decoder-arm64-inl.h",
"src/compiler/arm64/instruction-codes-arm64.h",
"src/compiler/arm64/instruction-selector-arm64.cc",
"src/compiler/arm64/linkage-arm64.cc",
+ "src/debug/arm64/debug-arm64.cc",
"src/full-codegen/arm64/full-codegen-arm64.cc",
"src/ic/arm64/access-compiler-arm64.cc",
"src/ic/arm64/handler-compiler-arm64.cc",
"src/mips/constants-mips.cc",
"src/mips/constants-mips.h",
"src/mips/cpu-mips.cc",
- "src/mips/debug-mips.cc",
"src/mips/deoptimizer-mips.cc",
"src/mips/disasm-mips.cc",
"src/mips/frames-mips.cc",
"src/compiler/mips/instruction-codes-mips.h",
"src/compiler/mips/instruction-selector-mips.cc",
"src/compiler/mips/linkage-mips.cc",
+ "src/debug/mips/debug-mips.cc",
"src/full-codegen/mips/full-codegen-mips.cc",
"src/ic/mips/access-compiler-mips.cc",
"src/ic/mips/handler-compiler-mips.cc",
"src/mips64/constants-mips64.cc",
"src/mips64/constants-mips64.h",
"src/mips64/cpu-mips64.cc",
- "src/mips64/debug-mips64.cc",
"src/mips64/deoptimizer-mips64.cc",
"src/mips64/disasm-mips64.cc",
"src/mips64/frames-mips64.cc",
"src/mips64/regexp-macro-assembler-mips64.h",
"src/mips64/simulator-mips64.cc",
"src/mips64/simulator-mips64.h",
+ "src/debug/mips64/debug-mips64.cc",
"src/full-codegen/mips64/full-codegen-mips64.cc",
"src/ic/mips64/access-compiler-mips64.cc",
"src/ic/mips64/handler-compiler-mips64.cc",
'filepath': 'src/snapshot/',
},
'debugger': {
- 'filepath': 'src/debug\.(cc|h)|src/.*-debugger\.js|src/runtime/runtime-debug\.cc',
+ 'filepath': 'src/debug/',
},
},
#include "src/conversions-inl.h"
#include "src/counters.h"
#include "src/cpu-profiler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/deoptimizer.h"
#include "src/execution.h"
#include "src/global-handles.h"
#include "src/arm/assembler-arm.h"
#include "src/assembler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
namespace v8 {
#if V8_TARGET_ARCH_ARM
#include "src/codegen.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/deoptimizer.h"
#include "src/full-codegen/full-codegen.h"
#include "src/interpreter/bytecodes.h"
+++ /dev/null
-// Copyright 2012 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"
-
-#if V8_TARGET_ARCH_ARM
-
-#include "src/codegen.h"
-#include "src/debug.h"
-
-namespace v8 {
-namespace internal {
-
-#define __ ACCESS_MASM(masm)
-
-
-void EmitDebugBreakSlot(MacroAssembler* masm) {
- Label check_size;
- __ bind(&check_size);
- for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
- __ nop(MacroAssembler::DEBUG_BREAK_NOP);
- }
- DCHECK_EQ(Assembler::kDebugBreakSlotInstructions,
- masm->InstructionsGeneratedSince(&check_size));
-}
-
-
-void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode,
- int call_argc) {
- // Generate enough nop's to make space for a call instruction. Avoid emitting
- // the constant pool in the debug break slot code.
- Assembler::BlockConstPoolScope block_const_pool(masm);
- masm->RecordDebugBreakSlot(mode, call_argc);
- EmitDebugBreakSlot(masm);
-}
-
-
-void DebugCodegen::ClearDebugBreakSlot(Address pc) {
- CodePatcher patcher(pc, Assembler::kDebugBreakSlotInstructions);
- EmitDebugBreakSlot(patcher.masm());
-}
-
-
-void DebugCodegen::PatchDebugBreakSlot(Address pc, Handle<Code> code) {
- DCHECK_EQ(Code::BUILTIN, code->kind());
- CodePatcher patcher(pc, Assembler::kDebugBreakSlotInstructions);
- // Patch the code changing the debug break slot code from
- // mov r2, r2
- // mov r2, r2
- // mov r2, r2
- // mov r2, r2
- // to a call to the debug break slot code.
- // ldr ip, [pc, #0]
- // b skip
- // <debug break slot code entry point address>
- // skip:
- // blx ip
- Label skip_constant;
- patcher.masm()->ldr(ip, MemOperand(v8::internal::pc, 0));
- patcher.masm()->b(&skip_constant);
- patcher.Emit(code->entry());
- patcher.masm()->bind(&skip_constant);
- patcher.masm()->blx(ip);
-}
-
-
-void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
- DebugBreakCallHelperMode mode) {
- __ RecordComment("Debug break");
- {
- FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
-
- // Load padding words on stack.
- __ mov(ip, Operand(Smi::FromInt(LiveEdit::kFramePaddingValue)));
- for (int i = 0; i < LiveEdit::kFramePaddingInitialSize; i++) {
- __ push(ip);
- }
- __ mov(ip, Operand(Smi::FromInt(LiveEdit::kFramePaddingInitialSize)));
- __ push(ip);
-
- if (mode == SAVE_RESULT_REGISTER) __ push(r0);
-
- __ mov(r0, Operand::Zero()); // no arguments
- __ mov(r1,
- Operand(ExternalReference(
- Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate())));
-
- CEntryStub ceb(masm->isolate(), 1);
- __ CallStub(&ceb);
-
- if (FLAG_debug_code) {
- for (int i = 0; i < kNumJSCallerSaved; i++) {
- Register reg = {JSCallerSavedCode(i)};
- __ mov(reg, Operand(kDebugZapValue));
- }
- }
-
- if (mode == SAVE_RESULT_REGISTER) __ pop(r0);
-
- // Don't bother removing padding bytes pushed on the stack
- // as the frame is going to be restored right away.
-
- // Leave the internal frame.
- }
-
- // Now that the break point has been handled, resume normal execution by
- // jumping to the target address intended by the caller and that was
- // overwritten by the address of DebugBreakXXX.
- ExternalReference after_break_target =
- ExternalReference::debug_after_break_target_address(masm->isolate());
- __ mov(ip, Operand(after_break_target));
- __ ldr(ip, MemOperand(ip));
- __ Jump(ip);
-}
-
-
-void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
- __ Ret();
-}
-
-
-void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
- ExternalReference restarter_frame_function_slot =
- ExternalReference::debug_restarter_frame_function_pointer_address(
- masm->isolate());
- __ mov(ip, Operand(restarter_frame_function_slot));
- __ mov(r1, Operand::Zero());
- __ str(r1, MemOperand(ip, 0));
-
- // Load the function pointer off of our current stack frame.
- __ ldr(r1, MemOperand(fp,
- StandardFrameConstants::kConstantPoolOffset - kPointerSize));
-
- // Pop return address, frame and constant pool pointer (if
- // FLAG_enable_embedded_constant_pool).
- __ LeaveFrame(StackFrame::INTERNAL);
-
- { ConstantPoolUnavailableScope constant_pool_unavailable(masm);
- // Load context from the function.
- __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
-
- // Get function code.
- __ ldr(ip, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
- __ ldr(ip, FieldMemOperand(ip, SharedFunctionInfo::kCodeOffset));
- __ add(ip, ip, Operand(Code::kHeaderSize - kHeapObjectTag));
-
- // Re-run JSFunction, r1 is function, cp is context.
- __ Jump(ip);
- }
-}
-
-
-const bool LiveEdit::kFrameDropperSupported = true;
-
-#undef __
-
-} // namespace internal
-} // namespace v8
-
-#endif // V8_TARGET_ARCH_ARM
#include "src/bootstrapper.h"
#include "src/codegen.h"
#include "src/cpu-profiler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/runtime/runtime.h"
namespace v8 {
#include "src/arm64/assembler-arm64.h"
#include "src/assembler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
namespace v8 {
#if V8_TARGET_ARCH_ARM64
#include "src/codegen.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/deoptimizer.h"
#include "src/full-codegen/full-codegen.h"
#include "src/runtime/runtime.h"
+++ /dev/null
-// Copyright 2013 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"
-
-#if V8_TARGET_ARCH_ARM64
-
-#include "src/codegen.h"
-#include "src/debug.h"
-
-namespace v8 {
-namespace internal {
-
-#define __ ACCESS_MASM(masm)
-
-
-void EmitDebugBreakSlot(Assembler* masm) {
- Label check_size;
- __ bind(&check_size);
- for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
- __ nop(Assembler::DEBUG_BREAK_NOP);
- }
- DCHECK_EQ(Assembler::kDebugBreakSlotInstructions,
- static_cast<int>(masm->InstructionsGeneratedSince(&check_size)));
-}
-
-
-void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode,
- int call_argc) {
- // Generate enough nop's to make space for a call instruction. Avoid emitting
- // the constant pool in the debug break slot code.
- InstructionAccurateScope scope(masm, Assembler::kDebugBreakSlotInstructions);
- masm->RecordDebugBreakSlot(mode, call_argc);
- EmitDebugBreakSlot(masm);
-}
-
-
-void DebugCodegen::ClearDebugBreakSlot(Address pc) {
- PatchingAssembler patcher(reinterpret_cast<Instruction*>(pc),
- Assembler::kDebugBreakSlotInstructions);
- EmitDebugBreakSlot(&patcher);
-}
-
-
-void DebugCodegen::PatchDebugBreakSlot(Address pc, Handle<Code> code) {
- DCHECK_EQ(Code::BUILTIN, code->kind());
- PatchingAssembler patcher(reinterpret_cast<Instruction*>(pc),
- Assembler::kDebugBreakSlotInstructions);
- // Patch the code emitted by DebugCodegen::GenerateSlots, changing the debug
- // break slot code from
- // mov x0, x0 @ nop DEBUG_BREAK_NOP
- // mov x0, x0 @ nop DEBUG_BREAK_NOP
- // mov x0, x0 @ nop DEBUG_BREAK_NOP
- // mov x0, x0 @ nop DEBUG_BREAK_NOP
- // mov x0, x0 @ nop DEBUG_BREAK_NOP
- // to a call to the debug slot code.
- // ldr ip0, [pc, #(2 * kInstructionSize)]
- // blr ip0
- // b skip
- // <debug break slot code entry point address (64 bits)>
- // skip:
-
- Label skip_constant;
- // The first instruction of a patched debug break slot must be a load literal
- // loading the address of the debug break slot code.
- patcher.ldr_pcrel(ip0, (2 * kInstructionSize) >> kLoadLiteralScaleLog2);
- patcher.b(&skip_constant);
- patcher.dc64(reinterpret_cast<int64_t>(code->entry()));
- patcher.bind(&skip_constant);
- // TODO(all): check the following is correct.
- // The debug break slot code will push a frame and call statically compiled
- // code. By using blr, this call site will be registered in the frame.
- // The debugger can now iterate on the frames to find this call.
- patcher.blr(ip0);
-}
-
-
-void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
- DebugBreakCallHelperMode mode) {
- __ RecordComment("Debug break");
- Register scratch = x10;
- {
- FrameScope scope(masm, StackFrame::INTERNAL);
-
- // Load padding words on stack.
- __ Mov(scratch, Smi::FromInt(LiveEdit::kFramePaddingValue));
- __ PushMultipleTimes(scratch, LiveEdit::kFramePaddingInitialSize);
- __ Mov(scratch, Smi::FromInt(LiveEdit::kFramePaddingInitialSize));
- __ Push(scratch);
-
- if (mode == SAVE_RESULT_REGISTER) __ Push(x0);
-
- __ Mov(x0, 0); // No arguments.
- __ Mov(x1, ExternalReference(Runtime::FunctionForId(Runtime::kDebugBreak),
- masm->isolate()));
-
- CEntryStub stub(masm->isolate(), 1);
- __ CallStub(&stub);
-
- if (FLAG_debug_code) {
- for (int i = 0; i < kNumJSCallerSaved; i++) {
- Register reg = Register::XRegFromCode(JSCallerSavedCode(i));
- __ Mov(reg, Operand(kDebugZapValue));
- }
- }
-
- // Restore the register values from the expression stack.
- if (mode == SAVE_RESULT_REGISTER) __ Pop(x0);
-
- // Don't bother removing padding bytes pushed on the stack
- // as the frame is going to be restored right away.
-
- // Leave the internal frame.
- }
-
- // Now that the break point has been handled, resume normal execution by
- // jumping to the target address intended by the caller and that was
- // overwritten by the address of DebugBreakXXX.
- ExternalReference after_break_target =
- ExternalReference::debug_after_break_target_address(masm->isolate());
- __ Mov(scratch, after_break_target);
- __ Ldr(scratch, MemOperand(scratch));
- __ Br(scratch);
-}
-
-
-void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
- __ Ret();
-}
-
-
-void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
- ExternalReference restarter_frame_function_slot =
- ExternalReference::debug_restarter_frame_function_pointer_address(
- masm->isolate());
- UseScratchRegisterScope temps(masm);
- Register scratch = temps.AcquireX();
-
- __ Mov(scratch, restarter_frame_function_slot);
- __ Str(xzr, MemOperand(scratch));
-
- // We do not know our frame height, but set sp based on fp.
- __ Sub(masm->StackPointer(), fp, kPointerSize);
- __ AssertStackConsistency();
-
- __ Pop(x1, fp, lr); // Function, Frame, Return address.
-
- // Load context from the function.
- __ Ldr(cp, FieldMemOperand(x1, JSFunction::kContextOffset));
-
- // Get function code.
- __ Ldr(scratch, FieldMemOperand(x1, JSFunction::kSharedFunctionInfoOffset));
- __ Ldr(scratch, FieldMemOperand(scratch, SharedFunctionInfo::kCodeOffset));
- __ Add(scratch, scratch, Code::kHeaderSize - kHeapObjectTag);
-
- // Re-run JSFunction, x1 is function, cp is context.
- __ Br(scratch);
-}
-
-
-const bool LiveEdit::kFrameDropperSupported = true;
-
-} // namespace internal
-} // namespace v8
-
-#endif // V8_TARGET_ARCH_ARM64
#include "src/bootstrapper.h"
#include "src/codegen.h"
#include "src/cpu-profiler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/runtime/runtime.h"
namespace v8 {
#include "src/codegen.h"
#include "src/counters.h"
#include "src/cpu-profiler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/deoptimizer.h"
#include "src/execution.h"
#include "src/ic/ic.h"
#include "src/base/lazy-instance.h"
#include "src/base/platform/platform.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/isolate.h"
#include "src/utils.h"
// found in the LICENSE file.
#include "src/background-parsing-task.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
namespace v8 {
namespace internal {
#include "src/codegen.h"
#include "src/compiler.h"
#include "src/cpu-profiler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/parser.h"
#include "src/prettyprinter.h"
#include "src/rewriter.h"
#include "src/compilation-cache.h"
#include "src/compiler/pipeline.h"
#include "src/cpu-profiler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
+#include "src/debug/liveedit.h"
#include "src/deoptimizer.h"
#include "src/full-codegen/full-codegen.h"
#include "src/gdb-jit.h"
#include "src/hydrogen.h"
#include "src/lithium.h"
-#include "src/liveedit.h"
#include "src/messages.h"
#include "src/parser.h"
#include "src/prettyprinter.h"
#include "src/v8.h"
#include "src/bootstrapper.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/scopeinfo.h"
namespace v8 {
+++ /dev/null
-// Copyright 2012 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.
-"use strict";
-
-// Default number of frames to include in the response to backtrace request.
-var kDefaultBacktraceLength = 10;
-
-var Debug = {};
-
-// Regular expression to skip "crud" at the beginning of a source line which is
-// not really code. Currently the regular expression matches whitespace and
-// comments.
-var sourceLineBeginningSkip = /^(?:\s*(?:\/\*.*?\*\/)*)*/;
-
-// Debug events which can occour in the V8 JavaScript engine. These originate
-// from the API include file debug.h.
-Debug.DebugEvent = { Break: 1,
- Exception: 2,
- NewFunction: 3,
- BeforeCompile: 4,
- AfterCompile: 5,
- CompileError: 6,
- PromiseEvent: 7,
- AsyncTaskEvent: 8 };
-
-// Types of exceptions that can be broken upon.
-Debug.ExceptionBreak = { Caught : 0,
- Uncaught: 1 };
-
-// The different types of steps.
-Debug.StepAction = { StepOut: 0,
- StepNext: 1,
- StepIn: 2,
- StepMin: 3,
- StepInMin: 4,
- StepFrame: 5 };
-
-// The different types of scripts matching enum ScriptType in objects.h.
-Debug.ScriptType = { Native: 0,
- Extension: 1,
- Normal: 2 };
-
-// The different types of script compilations matching enum
-// Script::CompilationType in objects.h.
-Debug.ScriptCompilationType = { Host: 0,
- Eval: 1,
- JSON: 2 };
-
-// The different script break point types.
-Debug.ScriptBreakPointType = { ScriptId: 0,
- ScriptName: 1,
- ScriptRegExp: 2 };
-
-// The different types of breakpoint position alignments.
-// Must match BreakPositionAlignment in debug.h.
-Debug.BreakPositionAlignment = {
- Statement: 0,
- BreakPosition: 1
-};
-
-function ScriptTypeFlag(type) {
- return (1 << type);
-}
-
-// Globals.
-var next_response_seq = 0;
-var next_break_point_number = 1;
-var break_points = [];
-var script_break_points = [];
-var debugger_flags = {
- breakPointsActive: {
- value: true,
- getValue: function() { return this.value; },
- setValue: function(value) {
- this.value = !!value;
- %SetDisableBreak(!this.value);
- }
- },
- breakOnCaughtException: {
- getValue: function() { return Debug.isBreakOnException(); },
- setValue: function(value) {
- if (value) {
- Debug.setBreakOnException();
- } else {
- Debug.clearBreakOnException();
- }
- }
- },
- breakOnUncaughtException: {
- getValue: function() { return Debug.isBreakOnUncaughtException(); },
- setValue: function(value) {
- if (value) {
- Debug.setBreakOnUncaughtException();
- } else {
- Debug.clearBreakOnUncaughtException();
- }
- }
- },
-};
-
-
-// Create a new break point object and add it to the list of break points.
-function MakeBreakPoint(source_position, opt_script_break_point) {
- var break_point = new BreakPoint(source_position, opt_script_break_point);
- break_points.push(break_point);
- return break_point;
-}
-
-
-// Object representing a break point.
-// NOTE: This object does not have a reference to the function having break
-// point as this would cause function not to be garbage collected when it is
-// not used any more. We do not want break points to keep functions alive.
-function BreakPoint(source_position, opt_script_break_point) {
- this.source_position_ = source_position;
- if (opt_script_break_point) {
- this.script_break_point_ = opt_script_break_point;
- } else {
- this.number_ = next_break_point_number++;
- }
- this.hit_count_ = 0;
- this.active_ = true;
- this.condition_ = null;
- this.ignoreCount_ = 0;
-}
-
-
-BreakPoint.prototype.number = function() {
- return this.number_;
-};
-
-
-BreakPoint.prototype.func = function() {
- return this.func_;
-};
-
-
-BreakPoint.prototype.source_position = function() {
- return this.source_position_;
-};
-
-
-BreakPoint.prototype.hit_count = function() {
- return this.hit_count_;
-};
-
-
-BreakPoint.prototype.active = function() {
- if (this.script_break_point()) {
- return this.script_break_point().active();
- }
- return this.active_;
-};
-
-
-BreakPoint.prototype.condition = function() {
- if (this.script_break_point() && this.script_break_point().condition()) {
- return this.script_break_point().condition();
- }
- return this.condition_;
-};
-
-
-BreakPoint.prototype.ignoreCount = function() {
- return this.ignoreCount_;
-};
-
-
-BreakPoint.prototype.script_break_point = function() {
- return this.script_break_point_;
-};
-
-
-BreakPoint.prototype.enable = function() {
- this.active_ = true;
-};
-
-
-BreakPoint.prototype.disable = function() {
- this.active_ = false;
-};
-
-
-BreakPoint.prototype.setCondition = function(condition) {
- this.condition_ = condition;
-};
-
-
-BreakPoint.prototype.setIgnoreCount = function(ignoreCount) {
- this.ignoreCount_ = ignoreCount;
-};
-
-
-BreakPoint.prototype.isTriggered = function(exec_state) {
- // Break point not active - not triggered.
- if (!this.active()) return false;
-
- // Check for conditional break point.
- if (this.condition()) {
- // If break point has condition try to evaluate it in the top frame.
- try {
- var mirror = exec_state.frame(0).evaluate(this.condition());
- // If no sensible mirror or non true value break point not triggered.
- if (!(mirror instanceof ValueMirror) ||
- !builtins.$toBoolean(mirror.value_)) {
- return false;
- }
- } catch (e) {
- // Exception evaluating condition counts as not triggered.
- return false;
- }
- }
-
- // Update the hit count.
- this.hit_count_++;
- if (this.script_break_point_) {
- this.script_break_point_.hit_count_++;
- }
-
- // If the break point has an ignore count it is not triggered.
- if (this.ignoreCount_ > 0) {
- this.ignoreCount_--;
- return false;
- }
-
- // Break point triggered.
- return true;
-};
-
-
-// Function called from the runtime when a break point is hit. Returns true if
-// the break point is triggered and supposed to break execution.
-function IsBreakPointTriggered(break_id, break_point) {
- return break_point.isTriggered(MakeExecutionState(break_id));
-}
-
-
-// Object representing a script break point. The script is referenced by its
-// script name or script id and the break point is represented as line and
-// column.
-function ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column,
- opt_groupId, opt_position_alignment) {
- this.type_ = type;
- if (type == Debug.ScriptBreakPointType.ScriptId) {
- this.script_id_ = script_id_or_name;
- } else if (type == Debug.ScriptBreakPointType.ScriptName) {
- this.script_name_ = script_id_or_name;
- } else if (type == Debug.ScriptBreakPointType.ScriptRegExp) {
- this.script_regexp_object_ = new RegExp(script_id_or_name);
- } else {
- throw new Error("Unexpected breakpoint type " + type);
- }
- this.line_ = opt_line || 0;
- this.column_ = opt_column;
- this.groupId_ = opt_groupId;
- this.position_alignment_ = IS_UNDEFINED(opt_position_alignment)
- ? Debug.BreakPositionAlignment.Statement : opt_position_alignment;
- this.hit_count_ = 0;
- this.active_ = true;
- this.condition_ = null;
- this.ignoreCount_ = 0;
- this.break_points_ = [];
-}
-
-
-// Creates a clone of script breakpoint that is linked to another script.
-ScriptBreakPoint.prototype.cloneForOtherScript = function (other_script) {
- var copy = new ScriptBreakPoint(Debug.ScriptBreakPointType.ScriptId,
- other_script.id, this.line_, this.column_, this.groupId_,
- this.position_alignment_);
- copy.number_ = next_break_point_number++;
- script_break_points.push(copy);
-
- copy.hit_count_ = this.hit_count_;
- copy.active_ = this.active_;
- copy.condition_ = this.condition_;
- copy.ignoreCount_ = this.ignoreCount_;
- return copy;
-};
-
-
-ScriptBreakPoint.prototype.number = function() {
- return this.number_;
-};
-
-
-ScriptBreakPoint.prototype.groupId = function() {
- return this.groupId_;
-};
-
-
-ScriptBreakPoint.prototype.type = function() {
- return this.type_;
-};
-
-
-ScriptBreakPoint.prototype.script_id = function() {
- return this.script_id_;
-};
-
-
-ScriptBreakPoint.prototype.script_name = function() {
- return this.script_name_;
-};
-
-
-ScriptBreakPoint.prototype.script_regexp_object = function() {
- return this.script_regexp_object_;
-};
-
-
-ScriptBreakPoint.prototype.line = function() {
- return this.line_;
-};
-
-
-ScriptBreakPoint.prototype.column = function() {
- return this.column_;
-};
-
-
-ScriptBreakPoint.prototype.actual_locations = function() {
- var locations = [];
- for (var i = 0; i < this.break_points_.length; i++) {
- locations.push(this.break_points_[i].actual_location);
- }
- return locations;
-};
-
-
-ScriptBreakPoint.prototype.update_positions = function(line, column) {
- this.line_ = line;
- this.column_ = column;
-};
-
-
-ScriptBreakPoint.prototype.hit_count = function() {
- return this.hit_count_;
-};
-
-
-ScriptBreakPoint.prototype.active = function() {
- return this.active_;
-};
-
-
-ScriptBreakPoint.prototype.condition = function() {
- return this.condition_;
-};
-
-
-ScriptBreakPoint.prototype.ignoreCount = function() {
- return this.ignoreCount_;
-};
-
-
-ScriptBreakPoint.prototype.enable = function() {
- this.active_ = true;
-};
-
-
-ScriptBreakPoint.prototype.disable = function() {
- this.active_ = false;
-};
-
-
-ScriptBreakPoint.prototype.setCondition = function(condition) {
- this.condition_ = condition;
-};
-
-
-ScriptBreakPoint.prototype.setIgnoreCount = function(ignoreCount) {
- this.ignoreCount_ = ignoreCount;
-
- // Set ignore count on all break points created from this script break point.
- for (var i = 0; i < this.break_points_.length; i++) {
- this.break_points_[i].setIgnoreCount(ignoreCount);
- }
-};
-
-
-// Check whether a script matches this script break point. Currently this is
-// only based on script name.
-ScriptBreakPoint.prototype.matchesScript = function(script) {
- if (this.type_ == Debug.ScriptBreakPointType.ScriptId) {
- return this.script_id_ == script.id;
- } else {
- // We might want to account columns here as well.
- if (!(script.line_offset <= this.line_ &&
- this.line_ < script.line_offset + script.lineCount())) {
- return false;
- }
- if (this.type_ == Debug.ScriptBreakPointType.ScriptName) {
- return this.script_name_ == script.nameOrSourceURL();
- } else if (this.type_ == Debug.ScriptBreakPointType.ScriptRegExp) {
- return this.script_regexp_object_.test(script.nameOrSourceURL());
- } else {
- throw new Error("Unexpected breakpoint type " + this.type_);
- }
- }
-};
-
-
-// Set the script break point in a script.
-ScriptBreakPoint.prototype.set = function (script) {
- var column = this.column();
- var line = this.line();
- // If the column is undefined the break is on the line. To help locate the
- // first piece of breakable code on the line try to find the column on the
- // line which contains some source.
- if (IS_UNDEFINED(column)) {
- var source_line = script.sourceLine(this.line());
-
- // Allocate array for caching the columns where the actual source starts.
- if (!script.sourceColumnStart_) {
- script.sourceColumnStart_ = new Array(script.lineCount());
- }
-
- // Fill cache if needed and get column where the actual source starts.
- if (IS_UNDEFINED(script.sourceColumnStart_[line])) {
- script.sourceColumnStart_[line] =
- source_line.match(sourceLineBeginningSkip)[0].length;
- }
- column = script.sourceColumnStart_[line];
- }
-
- // Convert the line and column into an absolute position within the script.
- var position = Debug.findScriptSourcePosition(script, this.line(), column);
-
- // If the position is not found in the script (the script might be shorter
- // than it used to be) just ignore it.
- if (IS_NULL(position)) return;
-
- // Create a break point object and set the break point.
- var break_point = MakeBreakPoint(position, this);
- break_point.setIgnoreCount(this.ignoreCount());
- var actual_position = %SetScriptBreakPoint(script, position,
- this.position_alignment_,
- break_point);
- if (IS_UNDEFINED(actual_position)) {
- actual_position = position;
- }
- var actual_location = script.locationFromPosition(actual_position, true);
- break_point.actual_location = { line: actual_location.line,
- column: actual_location.column,
- script_id: script.id };
- this.break_points_.push(break_point);
- return break_point;
-};
-
-
-// Clear all the break points created from this script break point
-ScriptBreakPoint.prototype.clear = function () {
- var remaining_break_points = [];
- for (var i = 0; i < break_points.length; i++) {
- if (break_points[i].script_break_point() &&
- break_points[i].script_break_point() === this) {
- %ClearBreakPoint(break_points[i]);
- } else {
- remaining_break_points.push(break_points[i]);
- }
- }
- break_points = remaining_break_points;
- this.break_points_ = [];
-};
-
-
-// Function called from runtime when a new script is compiled to set any script
-// break points set in this script.
-function UpdateScriptBreakPoints(script) {
- for (var i = 0; i < script_break_points.length; i++) {
- var break_point = script_break_points[i];
- if ((break_point.type() == Debug.ScriptBreakPointType.ScriptName ||
- break_point.type() == Debug.ScriptBreakPointType.ScriptRegExp) &&
- break_point.matchesScript(script)) {
- break_point.set(script);
- }
- }
-}
-
-
-function GetScriptBreakPoints(script) {
- var result = [];
- for (var i = 0; i < script_break_points.length; i++) {
- if (script_break_points[i].matchesScript(script)) {
- result.push(script_break_points[i]);
- }
- }
- return result;
-}
-
-
-Debug.setListener = function(listener, opt_data) {
- if (!IS_FUNCTION(listener) && !IS_UNDEFINED(listener) && !IS_NULL(listener)) {
- throw new Error('Parameters have wrong types.');
- }
- %SetDebugEventListener(listener, opt_data);
-};
-
-
-Debug.breakLocations = function(f, opt_position_aligment) {
- if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
- var position_aligment = IS_UNDEFINED(opt_position_aligment)
- ? Debug.BreakPositionAlignment.Statement : opt_position_aligment;
- return %GetBreakLocations(f, position_aligment);
-};
-
-// Returns a Script object. If the parameter is a function the return value
-// is the script in which the function is defined. If the parameter is a string
-// the return value is the script for which the script name has that string
-// value. If it is a regexp and there is a unique script whose name matches
-// we return that, otherwise undefined.
-Debug.findScript = function(func_or_script_name) {
- if (IS_FUNCTION(func_or_script_name)) {
- return %FunctionGetScript(func_or_script_name);
- } else if (IS_REGEXP(func_or_script_name)) {
- var scripts = Debug.scripts();
- var last_result = null;
- var result_count = 0;
- for (var i in scripts) {
- var script = scripts[i];
- if (func_or_script_name.test(script.name)) {
- last_result = script;
- result_count++;
- }
- }
- // Return the unique script matching the regexp. If there are more
- // than one we don't return a value since there is no good way to
- // decide which one to return. Returning a "random" one, say the
- // first, would introduce nondeterminism (or something close to it)
- // because the order is the heap iteration order.
- if (result_count == 1) {
- return last_result;
- } else {
- return undefined;
- }
- } else {
- return %GetScript(func_or_script_name);
- }
-};
-
-// Returns the script source. If the parameter is a function the return value
-// is the script source for the script in which the function is defined. If the
-// parameter is a string the return value is the script for which the script
-// name has that string value.
-Debug.scriptSource = function(func_or_script_name) {
- return this.findScript(func_or_script_name).source;
-};
-
-
-Debug.source = function(f) {
- if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
- return %FunctionGetSourceCode(f);
-};
-
-
-Debug.sourcePosition = function(f) {
- if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
- return %FunctionGetScriptSourcePosition(f);
-};
-
-
-Debug.findFunctionSourceLocation = function(func, opt_line, opt_column) {
- var script = %FunctionGetScript(func);
- var script_offset = %FunctionGetScriptSourcePosition(func);
- return script.locationFromLine(opt_line, opt_column, script_offset);
-};
-
-
-// Returns the character position in a script based on a line number and an
-// optional position within that line.
-Debug.findScriptSourcePosition = function(script, opt_line, opt_column) {
- var location = script.locationFromLine(opt_line, opt_column);
- return location ? location.position : null;
-};
-
-
-Debug.findBreakPoint = function(break_point_number, remove) {
- var break_point;
- for (var i = 0; i < break_points.length; i++) {
- if (break_points[i].number() == break_point_number) {
- break_point = break_points[i];
- // Remove the break point from the list if requested.
- if (remove) {
- break_points.splice(i, 1);
- }
- break;
- }
- }
- if (break_point) {
- return break_point;
- } else {
- return this.findScriptBreakPoint(break_point_number, remove);
- }
-};
-
-Debug.findBreakPointActualLocations = function(break_point_number) {
- for (var i = 0; i < script_break_points.length; i++) {
- if (script_break_points[i].number() == break_point_number) {
- return script_break_points[i].actual_locations();
- }
- }
- for (var i = 0; i < break_points.length; i++) {
- if (break_points[i].number() == break_point_number) {
- return [break_points[i].actual_location];
- }
- }
- return [];
-};
-
-Debug.setBreakPoint = function(func, opt_line, opt_column, opt_condition) {
- if (!IS_FUNCTION(func)) throw new Error('Parameters have wrong types.');
- // Break points in API functions are not supported.
- if (%FunctionIsAPIFunction(func)) {
- throw new Error('Cannot set break point in native code.');
- }
- // Find source position relative to start of the function
- var break_position =
- this.findFunctionSourceLocation(func, opt_line, opt_column).position;
- var source_position = break_position - this.sourcePosition(func);
- // Find the script for the function.
- var script = %FunctionGetScript(func);
- // Break in builtin JavaScript code is not supported.
- if (script.type == Debug.ScriptType.Native) {
- throw new Error('Cannot set break point in native code.');
- }
- // If the script for the function has a name convert this to a script break
- // point.
- if (script && script.id) {
- // Adjust the source position to be script relative.
- source_position += %FunctionGetScriptSourcePosition(func);
- // Find line and column for the position in the script and set a script
- // break point from that.
- var location = script.locationFromPosition(source_position, false);
- return this.setScriptBreakPointById(script.id,
- location.line, location.column,
- opt_condition);
- } else {
- // Set a break point directly on the function.
- var break_point = MakeBreakPoint(source_position);
- var actual_position =
- %SetFunctionBreakPoint(func, source_position, break_point);
- actual_position += this.sourcePosition(func);
- var actual_location = script.locationFromPosition(actual_position, true);
- break_point.actual_location = { line: actual_location.line,
- column: actual_location.column,
- script_id: script.id };
- break_point.setCondition(opt_condition);
- return break_point.number();
- }
-};
-
-
-Debug.setBreakPointByScriptIdAndPosition = function(script_id, position,
- condition, enabled,
- opt_position_alignment)
-{
- var break_point = MakeBreakPoint(position);
- break_point.setCondition(condition);
- if (!enabled) {
- break_point.disable();
- }
- var scripts = this.scripts();
- var position_alignment = IS_UNDEFINED(opt_position_alignment)
- ? Debug.BreakPositionAlignment.Statement : opt_position_alignment;
- for (var i = 0; i < scripts.length; i++) {
- if (script_id == scripts[i].id) {
- break_point.actual_position = %SetScriptBreakPoint(scripts[i], position,
- position_alignment, break_point);
- break;
- }
- }
- return break_point;
-};
-
-
-Debug.enableBreakPoint = function(break_point_number) {
- var break_point = this.findBreakPoint(break_point_number, false);
- // Only enable if the breakpoint hasn't been deleted:
- if (break_point) {
- break_point.enable();
- }
-};
-
-
-Debug.disableBreakPoint = function(break_point_number) {
- var break_point = this.findBreakPoint(break_point_number, false);
- // Only enable if the breakpoint hasn't been deleted:
- if (break_point) {
- break_point.disable();
- }
-};
-
-
-Debug.changeBreakPointCondition = function(break_point_number, condition) {
- var break_point = this.findBreakPoint(break_point_number, false);
- break_point.setCondition(condition);
-};
-
-
-Debug.changeBreakPointIgnoreCount = function(break_point_number, ignoreCount) {
- if (ignoreCount < 0) {
- throw new Error('Invalid argument');
- }
- var break_point = this.findBreakPoint(break_point_number, false);
- break_point.setIgnoreCount(ignoreCount);
-};
-
-
-Debug.clearBreakPoint = function(break_point_number) {
- var break_point = this.findBreakPoint(break_point_number, true);
- if (break_point) {
- return %ClearBreakPoint(break_point);
- } else {
- break_point = this.findScriptBreakPoint(break_point_number, true);
- if (!break_point) {
- throw new Error('Invalid breakpoint');
- }
- }
-};
-
-
-Debug.clearAllBreakPoints = function() {
- for (var i = 0; i < break_points.length; i++) {
- var break_point = break_points[i];
- %ClearBreakPoint(break_point);
- }
- break_points = [];
-};
-
-
-Debug.disableAllBreakPoints = function() {
- // Disable all user defined breakpoints:
- for (var i = 1; i < next_break_point_number; i++) {
- Debug.disableBreakPoint(i);
- }
- // Disable all exception breakpoints:
- %ChangeBreakOnException(Debug.ExceptionBreak.Caught, false);
- %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, false);
-};
-
-
-Debug.findScriptBreakPoint = function(break_point_number, remove) {
- var script_break_point;
- for (var i = 0; i < script_break_points.length; i++) {
- if (script_break_points[i].number() == break_point_number) {
- script_break_point = script_break_points[i];
- // Remove the break point from the list if requested.
- if (remove) {
- script_break_point.clear();
- script_break_points.splice(i,1);
- }
- break;
- }
- }
- return script_break_point;
-};
-
-
-// Sets a breakpoint in a script identified through id or name at the
-// specified source line and column within that line.
-Debug.setScriptBreakPoint = function(type, script_id_or_name,
- opt_line, opt_column, opt_condition,
- opt_groupId, opt_position_alignment) {
- // Create script break point object.
- var script_break_point =
- new ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column,
- opt_groupId, opt_position_alignment);
-
- // Assign number to the new script break point and add it.
- script_break_point.number_ = next_break_point_number++;
- script_break_point.setCondition(opt_condition);
- script_break_points.push(script_break_point);
-
- // Run through all scripts to see if this script break point matches any
- // loaded scripts.
- var scripts = this.scripts();
- for (var i = 0; i < scripts.length; i++) {
- if (script_break_point.matchesScript(scripts[i])) {
- script_break_point.set(scripts[i]);
- }
- }
-
- return script_break_point.number();
-};
-
-
-Debug.setScriptBreakPointById = function(script_id,
- opt_line, opt_column,
- opt_condition, opt_groupId,
- opt_position_alignment) {
- return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptId,
- script_id, opt_line, opt_column,
- opt_condition, opt_groupId,
- opt_position_alignment);
-};
-
-
-Debug.setScriptBreakPointByName = function(script_name,
- opt_line, opt_column,
- opt_condition, opt_groupId) {
- return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptName,
- script_name, opt_line, opt_column,
- opt_condition, opt_groupId);
-};
-
-
-Debug.setScriptBreakPointByRegExp = function(script_regexp,
- opt_line, opt_column,
- opt_condition, opt_groupId) {
- return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptRegExp,
- script_regexp, opt_line, opt_column,
- opt_condition, opt_groupId);
-};
-
-
-Debug.enableScriptBreakPoint = function(break_point_number) {
- var script_break_point = this.findScriptBreakPoint(break_point_number, false);
- script_break_point.enable();
-};
-
-
-Debug.disableScriptBreakPoint = function(break_point_number) {
- var script_break_point = this.findScriptBreakPoint(break_point_number, false);
- script_break_point.disable();
-};
-
-
-Debug.changeScriptBreakPointCondition = function(
- break_point_number, condition) {
- var script_break_point = this.findScriptBreakPoint(break_point_number, false);
- script_break_point.setCondition(condition);
-};
-
-
-Debug.changeScriptBreakPointIgnoreCount = function(
- break_point_number, ignoreCount) {
- if (ignoreCount < 0) {
- throw new Error('Invalid argument');
- }
- var script_break_point = this.findScriptBreakPoint(break_point_number, false);
- script_break_point.setIgnoreCount(ignoreCount);
-};
-
-
-Debug.scriptBreakPoints = function() {
- return script_break_points;
-};
-
-
-Debug.clearStepping = function() {
- %ClearStepping();
-};
-
-Debug.setBreakOnException = function() {
- return %ChangeBreakOnException(Debug.ExceptionBreak.Caught, true);
-};
-
-Debug.clearBreakOnException = function() {
- return %ChangeBreakOnException(Debug.ExceptionBreak.Caught, false);
-};
-
-Debug.isBreakOnException = function() {
- return !!%IsBreakOnException(Debug.ExceptionBreak.Caught);
-};
-
-Debug.setBreakOnUncaughtException = function() {
- return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, true);
-};
-
-Debug.clearBreakOnUncaughtException = function() {
- return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, false);
-};
-
-Debug.isBreakOnUncaughtException = function() {
- return !!%IsBreakOnException(Debug.ExceptionBreak.Uncaught);
-};
-
-Debug.showBreakPoints = function(f, full, opt_position_alignment) {
- if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
- var source = full ? this.scriptSource(f) : this.source(f);
- var offset = full ? this.sourcePosition(f) : 0;
- var locations = this.breakLocations(f, opt_position_alignment);
- if (!locations) return source;
- locations.sort(function(x, y) { return x - y; });
- var result = "";
- var prev_pos = 0;
- var pos;
- for (var i = 0; i < locations.length; i++) {
- pos = locations[i] - offset;
- result += source.slice(prev_pos, pos);
- result += "[B" + i + "]";
- prev_pos = pos;
- }
- pos = source.length;
- result += source.substring(prev_pos, pos);
- return result;
-};
-
-
-// Get all the scripts currently loaded. Locating all the scripts is based on
-// scanning the heap.
-Debug.scripts = function() {
- // Collect all scripts in the heap.
- return %DebugGetLoadedScripts();
-};
-
-
-Debug.debuggerFlags = function() {
- return debugger_flags;
-};
-
-Debug.MakeMirror = MakeMirror;
-
-function MakeExecutionState(break_id) {
- return new ExecutionState(break_id);
-}
-
-function ExecutionState(break_id) {
- this.break_id = break_id;
- this.selected_frame = 0;
-}
-
-ExecutionState.prototype.prepareStep = function(opt_action, opt_count,
- opt_callframe) {
- var action = Debug.StepAction.StepIn;
- if (!IS_UNDEFINED(opt_action)) action = builtins.$toNumber(opt_action);
- var count = opt_count ? builtins.$toNumber(opt_count) : 1;
- var callFrameId = 0;
- if (!IS_UNDEFINED(opt_callframe)) {
- callFrameId = opt_callframe.details_.frameId();
- }
-
- return %PrepareStep(this.break_id, action, count, callFrameId);
-};
-
-ExecutionState.prototype.evaluateGlobal = function(source, disable_break,
- opt_additional_context) {
- return MakeMirror(%DebugEvaluateGlobal(this.break_id, source,
- Boolean(disable_break),
- opt_additional_context));
-};
-
-ExecutionState.prototype.frameCount = function() {
- return %GetFrameCount(this.break_id);
-};
-
-ExecutionState.prototype.threadCount = function() {
- return %GetThreadCount(this.break_id);
-};
-
-ExecutionState.prototype.frame = function(opt_index) {
- // If no index supplied return the selected frame.
- if (opt_index == null) opt_index = this.selected_frame;
- if (opt_index < 0 || opt_index >= this.frameCount()) {
- throw new Error('Illegal frame index.');
- }
- return new FrameMirror(this.break_id, opt_index);
-};
-
-ExecutionState.prototype.setSelectedFrame = function(index) {
- var i = builtins.$toNumber(index);
- if (i < 0 || i >= this.frameCount()) throw new Error('Illegal frame index.');
- this.selected_frame = i;
-};
-
-ExecutionState.prototype.selectedFrame = function() {
- return this.selected_frame;
-};
-
-ExecutionState.prototype.debugCommandProcessor = function(opt_is_running) {
- return new DebugCommandProcessor(this, opt_is_running);
-};
-
-
-function MakeBreakEvent(break_id, break_points_hit) {
- return new BreakEvent(break_id, break_points_hit);
-}
-
-
-function BreakEvent(break_id, break_points_hit) {
- this.frame_ = new FrameMirror(break_id, 0);
- this.break_points_hit_ = break_points_hit;
-}
-
-
-BreakEvent.prototype.eventType = function() {
- return Debug.DebugEvent.Break;
-};
-
-
-BreakEvent.prototype.func = function() {
- return this.frame_.func();
-};
-
-
-BreakEvent.prototype.sourceLine = function() {
- return this.frame_.sourceLine();
-};
-
-
-BreakEvent.prototype.sourceColumn = function() {
- return this.frame_.sourceColumn();
-};
-
-
-BreakEvent.prototype.sourceLineText = function() {
- return this.frame_.sourceLineText();
-};
-
-
-BreakEvent.prototype.breakPointsHit = function() {
- return this.break_points_hit_;
-};
-
-
-BreakEvent.prototype.toJSONProtocol = function() {
- var o = { seq: next_response_seq++,
- type: "event",
- event: "break",
- body: { invocationText: this.frame_.invocationText() }
- };
-
- // Add script related information to the event if available.
- var script = this.func().script();
- if (script) {
- o.body.sourceLine = this.sourceLine(),
- o.body.sourceColumn = this.sourceColumn(),
- o.body.sourceLineText = this.sourceLineText(),
- o.body.script = MakeScriptObject_(script, false);
- }
-
- // Add an Array of break points hit if any.
- if (this.breakPointsHit()) {
- o.body.breakpoints = [];
- for (var i = 0; i < this.breakPointsHit().length; i++) {
- // Find the break point number. For break points originating from a
- // script break point supply the script break point number.
- var breakpoint = this.breakPointsHit()[i];
- var script_break_point = breakpoint.script_break_point();
- var number;
- if (script_break_point) {
- number = script_break_point.number();
- } else {
- number = breakpoint.number();
- }
- o.body.breakpoints.push(number);
- }
- }
- return JSON.stringify(ObjectToProtocolObject_(o));
-};
-
-
-function MakeExceptionEvent(break_id, exception, uncaught, promise) {
- return new ExceptionEvent(break_id, exception, uncaught, promise);
-}
-
-
-function ExceptionEvent(break_id, exception, uncaught, promise) {
- this.exec_state_ = new ExecutionState(break_id);
- this.exception_ = exception;
- this.uncaught_ = uncaught;
- this.promise_ = promise;
-}
-
-
-ExceptionEvent.prototype.eventType = function() {
- return Debug.DebugEvent.Exception;
-};
-
-
-ExceptionEvent.prototype.exception = function() {
- return this.exception_;
-};
-
-
-ExceptionEvent.prototype.uncaught = function() {
- return this.uncaught_;
-};
-
-
-ExceptionEvent.prototype.promise = function() {
- return this.promise_;
-};
-
-
-ExceptionEvent.prototype.func = function() {
- return this.exec_state_.frame(0).func();
-};
-
-
-ExceptionEvent.prototype.sourceLine = function() {
- return this.exec_state_.frame(0).sourceLine();
-};
-
-
-ExceptionEvent.prototype.sourceColumn = function() {
- return this.exec_state_.frame(0).sourceColumn();
-};
-
-
-ExceptionEvent.prototype.sourceLineText = function() {
- return this.exec_state_.frame(0).sourceLineText();
-};
-
-
-ExceptionEvent.prototype.toJSONProtocol = function() {
- var o = new ProtocolMessage();
- o.event = "exception";
- o.body = { uncaught: this.uncaught_,
- exception: MakeMirror(this.exception_)
- };
-
- // Exceptions might happen whithout any JavaScript frames.
- if (this.exec_state_.frameCount() > 0) {
- o.body.sourceLine = this.sourceLine();
- o.body.sourceColumn = this.sourceColumn();
- o.body.sourceLineText = this.sourceLineText();
-
- // Add script information to the event if available.
- var script = this.func().script();
- if (script) {
- o.body.script = MakeScriptObject_(script, false);
- }
- } else {
- o.body.sourceLine = -1;
- }
-
- return o.toJSONProtocol();
-};
-
-
-function MakeCompileEvent(script, type) {
- return new CompileEvent(script, type);
-}
-
-
-function CompileEvent(script, type) {
- this.script_ = MakeMirror(script);
- this.type_ = type;
-}
-
-
-CompileEvent.prototype.eventType = function() {
- return this.type_;
-};
-
-
-CompileEvent.prototype.script = function() {
- return this.script_;
-};
-
-
-CompileEvent.prototype.toJSONProtocol = function() {
- var o = new ProtocolMessage();
- o.running = true;
- switch (this.type_) {
- case Debug.DebugEvent.BeforeCompile:
- o.event = "beforeCompile";
- break;
- case Debug.DebugEvent.AfterCompile:
- o.event = "afterCompile";
- break;
- case Debug.DebugEvent.CompileError:
- o.event = "compileError";
- break;
- }
- o.body = {};
- o.body.script = this.script_;
-
- return o.toJSONProtocol();
-};
-
-
-function MakeScriptObject_(script, include_source) {
- var o = { id: script.id(),
- name: script.name(),
- lineOffset: script.lineOffset(),
- columnOffset: script.columnOffset(),
- lineCount: script.lineCount(),
- };
- if (!IS_UNDEFINED(script.data())) {
- o.data = script.data();
- }
- if (include_source) {
- o.source = script.source();
- }
- return o;
-}
-
-
-function MakePromiseEvent(event_data) {
- return new PromiseEvent(event_data);
-}
-
-
-function PromiseEvent(event_data) {
- this.promise_ = event_data.promise;
- this.parentPromise_ = event_data.parentPromise;
- this.status_ = event_data.status;
- this.value_ = event_data.value;
-}
-
-
-PromiseEvent.prototype.promise = function() {
- return MakeMirror(this.promise_);
-}
-
-
-PromiseEvent.prototype.parentPromise = function() {
- return MakeMirror(this.parentPromise_);
-}
-
-
-PromiseEvent.prototype.status = function() {
- return this.status_;
-}
-
-
-PromiseEvent.prototype.value = function() {
- return MakeMirror(this.value_);
-}
-
-
-function MakeAsyncTaskEvent(event_data) {
- return new AsyncTaskEvent(event_data);
-}
-
-
-function AsyncTaskEvent(event_data) {
- this.type_ = event_data.type;
- this.name_ = event_data.name;
- this.id_ = event_data.id;
-}
-
-
-AsyncTaskEvent.prototype.type = function() {
- return this.type_;
-}
-
-
-AsyncTaskEvent.prototype.name = function() {
- return this.name_;
-}
-
-
-AsyncTaskEvent.prototype.id = function() {
- return this.id_;
-}
-
-
-function DebugCommandProcessor(exec_state, opt_is_running) {
- this.exec_state_ = exec_state;
- this.running_ = opt_is_running || false;
-}
-
-
-DebugCommandProcessor.prototype.processDebugRequest = function (request) {
- return this.processDebugJSONRequest(request);
-};
-
-
-function ProtocolMessage(request) {
- // Update sequence number.
- this.seq = next_response_seq++;
-
- if (request) {
- // If message is based on a request this is a response. Fill the initial
- // response from the request.
- this.type = 'response';
- this.request_seq = request.seq;
- this.command = request.command;
- } else {
- // If message is not based on a request it is a dabugger generated event.
- this.type = 'event';
- }
- this.success = true;
- // Handler may set this field to control debugger state.
- this.running = undefined;
-}
-
-
-ProtocolMessage.prototype.setOption = function(name, value) {
- if (!this.options_) {
- this.options_ = {};
- }
- this.options_[name] = value;
-};
-
-
-ProtocolMessage.prototype.failed = function(message, opt_details) {
- this.success = false;
- this.message = message;
- if (IS_OBJECT(opt_details)) {
- this.error_details = opt_details;
- }
-};
-
-
-ProtocolMessage.prototype.toJSONProtocol = function() {
- // Encode the protocol header.
- var json = {};
- json.seq= this.seq;
- if (this.request_seq) {
- json.request_seq = this.request_seq;
- }
- json.type = this.type;
- if (this.event) {
- json.event = this.event;
- }
- if (this.command) {
- json.command = this.command;
- }
- if (this.success) {
- json.success = this.success;
- } else {
- json.success = false;
- }
- if (this.body) {
- // Encode the body part.
- var bodyJson;
- var serializer = MakeMirrorSerializer(true, this.options_);
- if (this.body instanceof Mirror) {
- bodyJson = serializer.serializeValue(this.body);
- } else if (this.body instanceof Array) {
- bodyJson = [];
- for (var i = 0; i < this.body.length; i++) {
- if (this.body[i] instanceof Mirror) {
- bodyJson.push(serializer.serializeValue(this.body[i]));
- } else {
- bodyJson.push(ObjectToProtocolObject_(this.body[i], serializer));
- }
- }
- } else {
- bodyJson = ObjectToProtocolObject_(this.body, serializer);
- }
- json.body = bodyJson;
- json.refs = serializer.serializeReferencedObjects();
- }
- if (this.message) {
- json.message = this.message;
- }
- if (this.error_details) {
- json.error_details = this.error_details;
- }
- json.running = this.running;
- return JSON.stringify(json);
-};
-
-
-DebugCommandProcessor.prototype.createResponse = function(request) {
- return new ProtocolMessage(request);
-};
-
-
-DebugCommandProcessor.prototype.processDebugJSONRequest = function(
- json_request) {
- var request; // Current request.
- var response; // Generated response.
- try {
- try {
- // Convert the JSON string to an object.
- request = JSON.parse(json_request);
-
- // Create an initial response.
- response = this.createResponse(request);
-
- if (!request.type) {
- throw new Error('Type not specified');
- }
-
- if (request.type != 'request') {
- throw new Error("Illegal type '" + request.type + "' in request");
- }
-
- if (!request.command) {
- throw new Error('Command not specified');
- }
-
- if (request.arguments) {
- var args = request.arguments;
- // TODO(yurys): remove request.arguments.compactFormat check once
- // ChromeDevTools are switched to 'inlineRefs'
- if (args.inlineRefs || args.compactFormat) {
- response.setOption('inlineRefs', true);
- }
- if (!IS_UNDEFINED(args.maxStringLength)) {
- response.setOption('maxStringLength', args.maxStringLength);
- }
- }
-
- var key = request.command.toLowerCase();
- var handler = DebugCommandProcessor.prototype.dispatch_[key];
- if (IS_FUNCTION(handler)) {
- %_CallFunction(this, request, response, handler);
- } else {
- throw new Error('Unknown command "' + request.command + '" in request');
- }
- } catch (e) {
- // If there is no response object created one (without command).
- if (!response) {
- response = this.createResponse();
- }
- response.success = false;
- response.message = builtins.$toString(e);
- }
-
- // Return the response as a JSON encoded string.
- try {
- if (!IS_UNDEFINED(response.running)) {
- // Response controls running state.
- this.running_ = response.running;
- }
- response.running = this.running_;
- return response.toJSONProtocol();
- } catch (e) {
- // Failed to generate response - return generic error.
- return '{"seq":' + response.seq + ',' +
- '"request_seq":' + request.seq + ',' +
- '"type":"response",' +
- '"success":false,' +
- '"message":"Internal error: ' + builtins.$toString(e) + '"}';
- }
- } catch (e) {
- // Failed in one of the catch blocks above - most generic error.
- return '{"seq":0,"type":"response","success":false,"message":"Internal error"}';
- }
-};
-
-
-DebugCommandProcessor.prototype.continueRequest_ = function(request, response) {
- // Check for arguments for continue.
- if (request.arguments) {
- var count = 1;
- var action = Debug.StepAction.StepIn;
-
- // Pull out arguments.
- var stepaction = request.arguments.stepaction;
- var stepcount = request.arguments.stepcount;
-
- // Get the stepcount argument if any.
- if (stepcount) {
- count = builtins.$toNumber(stepcount);
- if (count < 0) {
- throw new Error('Invalid stepcount argument "' + stepcount + '".');
- }
- }
-
- // Get the stepaction argument.
- if (stepaction) {
- if (stepaction == 'in') {
- action = Debug.StepAction.StepIn;
- } else if (stepaction == 'min') {
- action = Debug.StepAction.StepMin;
- } else if (stepaction == 'next') {
- action = Debug.StepAction.StepNext;
- } else if (stepaction == 'out') {
- action = Debug.StepAction.StepOut;
- } else {
- throw new Error('Invalid stepaction argument "' + stepaction + '".');
- }
- }
-
- // Set up the VM for stepping.
- this.exec_state_.prepareStep(action, count);
- }
-
- // VM should be running after executing this request.
- response.running = true;
-};
-
-
-DebugCommandProcessor.prototype.breakRequest_ = function(request, response) {
- // Ignore as break command does not do anything when broken.
-};
-
-
-DebugCommandProcessor.prototype.setBreakPointRequest_ =
- function(request, response) {
- // Check for legal request.
- if (!request.arguments) {
- response.failed('Missing arguments');
- return;
- }
-
- // Pull out arguments.
- var type = request.arguments.type;
- var target = request.arguments.target;
- var line = request.arguments.line;
- var column = request.arguments.column;
- var enabled = IS_UNDEFINED(request.arguments.enabled) ?
- true : request.arguments.enabled;
- var condition = request.arguments.condition;
- var ignoreCount = request.arguments.ignoreCount;
- var groupId = request.arguments.groupId;
-
- // Check for legal arguments.
- if (!type || IS_UNDEFINED(target)) {
- response.failed('Missing argument "type" or "target"');
- return;
- }
-
- // Either function or script break point.
- var break_point_number;
- if (type == 'function') {
- // Handle function break point.
- if (!IS_STRING(target)) {
- response.failed('Argument "target" is not a string value');
- return;
- }
- var f;
- try {
- // Find the function through a global evaluate.
- f = this.exec_state_.evaluateGlobal(target).value();
- } catch (e) {
- response.failed('Error: "' + builtins.$toString(e) +
- '" evaluating "' + target + '"');
- return;
- }
- if (!IS_FUNCTION(f)) {
- response.failed('"' + target + '" does not evaluate to a function');
- return;
- }
-
- // Set function break point.
- break_point_number = Debug.setBreakPoint(f, line, column, condition);
- } else if (type == 'handle') {
- // Find the object pointed by the specified handle.
- var handle = parseInt(target, 10);
- var mirror = LookupMirror(handle);
- if (!mirror) {
- return response.failed('Object #' + handle + '# not found');
- }
- if (!mirror.isFunction()) {
- return response.failed('Object #' + handle + '# is not a function');
- }
-
- // Set function break point.
- break_point_number = Debug.setBreakPoint(mirror.value(),
- line, column, condition);
- } else if (type == 'script') {
- // set script break point.
- break_point_number =
- Debug.setScriptBreakPointByName(target, line, column, condition,
- groupId);
- } else if (type == 'scriptId') {
- break_point_number =
- Debug.setScriptBreakPointById(target, line, column, condition, groupId);
- } else if (type == 'scriptRegExp') {
- break_point_number =
- Debug.setScriptBreakPointByRegExp(target, line, column, condition,
- groupId);
- } else {
- response.failed('Illegal type "' + type + '"');
- return;
- }
-
- // Set additional break point properties.
- var break_point = Debug.findBreakPoint(break_point_number);
- if (ignoreCount) {
- Debug.changeBreakPointIgnoreCount(break_point_number, ignoreCount);
- }
- if (!enabled) {
- Debug.disableBreakPoint(break_point_number);
- }
-
- // Add the break point number to the response.
- response.body = { type: type,
- breakpoint: break_point_number };
-
- // Add break point information to the response.
- if (break_point instanceof ScriptBreakPoint) {
- if (break_point.type() == Debug.ScriptBreakPointType.ScriptId) {
- response.body.type = 'scriptId';
- response.body.script_id = break_point.script_id();
- } else if (break_point.type() == Debug.ScriptBreakPointType.ScriptName) {
- response.body.type = 'scriptName';
- response.body.script_name = break_point.script_name();
- } else if (break_point.type() == Debug.ScriptBreakPointType.ScriptRegExp) {
- response.body.type = 'scriptRegExp';
- response.body.script_regexp = break_point.script_regexp_object().source;
- } else {
- throw new Error("Internal error: Unexpected breakpoint type: " +
- break_point.type());
- }
- response.body.line = break_point.line();
- response.body.column = break_point.column();
- response.body.actual_locations = break_point.actual_locations();
- } else {
- response.body.type = 'function';
- response.body.actual_locations = [break_point.actual_location];
- }
-};
-
-
-DebugCommandProcessor.prototype.changeBreakPointRequest_ = function(
- request, response) {
- // Check for legal request.
- if (!request.arguments) {
- response.failed('Missing arguments');
- return;
- }
-
- // Pull out arguments.
- var break_point = builtins.$toNumber(request.arguments.breakpoint);
- var enabled = request.arguments.enabled;
- var condition = request.arguments.condition;
- var ignoreCount = request.arguments.ignoreCount;
-
- // Check for legal arguments.
- if (!break_point) {
- response.failed('Missing argument "breakpoint"');
- return;
- }
-
- // Change enabled state if supplied.
- if (!IS_UNDEFINED(enabled)) {
- if (enabled) {
- Debug.enableBreakPoint(break_point);
- } else {
- Debug.disableBreakPoint(break_point);
- }
- }
-
- // Change condition if supplied
- if (!IS_UNDEFINED(condition)) {
- Debug.changeBreakPointCondition(break_point, condition);
- }
-
- // Change ignore count if supplied
- if (!IS_UNDEFINED(ignoreCount)) {
- Debug.changeBreakPointIgnoreCount(break_point, ignoreCount);
- }
-};
-
-
-DebugCommandProcessor.prototype.clearBreakPointGroupRequest_ = function(
- request, response) {
- // Check for legal request.
- if (!request.arguments) {
- response.failed('Missing arguments');
- return;
- }
-
- // Pull out arguments.
- var group_id = request.arguments.groupId;
-
- // Check for legal arguments.
- if (!group_id) {
- response.failed('Missing argument "groupId"');
- return;
- }
-
- var cleared_break_points = [];
- var new_script_break_points = [];
- for (var i = 0; i < script_break_points.length; i++) {
- var next_break_point = script_break_points[i];
- if (next_break_point.groupId() == group_id) {
- cleared_break_points.push(next_break_point.number());
- next_break_point.clear();
- } else {
- new_script_break_points.push(next_break_point);
- }
- }
- script_break_points = new_script_break_points;
-
- // Add the cleared break point numbers to the response.
- response.body = { breakpoints: cleared_break_points };
-};
-
-
-DebugCommandProcessor.prototype.clearBreakPointRequest_ = function(
- request, response) {
- // Check for legal request.
- if (!request.arguments) {
- response.failed('Missing arguments');
- return;
- }
-
- // Pull out arguments.
- var break_point = builtins.$toNumber(request.arguments.breakpoint);
-
- // Check for legal arguments.
- if (!break_point) {
- response.failed('Missing argument "breakpoint"');
- return;
- }
-
- // Clear break point.
- Debug.clearBreakPoint(break_point);
-
- // Add the cleared break point number to the response.
- response.body = { breakpoint: break_point };
-};
-
-
-DebugCommandProcessor.prototype.listBreakpointsRequest_ = function(
- request, response) {
- var array = [];
- for (var i = 0; i < script_break_points.length; i++) {
- var break_point = script_break_points[i];
-
- var description = {
- number: break_point.number(),
- line: break_point.line(),
- column: break_point.column(),
- groupId: break_point.groupId(),
- hit_count: break_point.hit_count(),
- active: break_point.active(),
- condition: break_point.condition(),
- ignoreCount: break_point.ignoreCount(),
- actual_locations: break_point.actual_locations()
- };
-
- if (break_point.type() == Debug.ScriptBreakPointType.ScriptId) {
- description.type = 'scriptId';
- description.script_id = break_point.script_id();
- } else if (break_point.type() == Debug.ScriptBreakPointType.ScriptName) {
- description.type = 'scriptName';
- description.script_name = break_point.script_name();
- } else if (break_point.type() == Debug.ScriptBreakPointType.ScriptRegExp) {
- description.type = 'scriptRegExp';
- description.script_regexp = break_point.script_regexp_object().source;
- } else {
- throw new Error("Internal error: Unexpected breakpoint type: " +
- break_point.type());
- }
- array.push(description);
- }
-
- response.body = {
- breakpoints: array,
- breakOnExceptions: Debug.isBreakOnException(),
- breakOnUncaughtExceptions: Debug.isBreakOnUncaughtException()
- };
-};
-
-
-DebugCommandProcessor.prototype.disconnectRequest_ =
- function(request, response) {
- Debug.disableAllBreakPoints();
- this.continueRequest_(request, response);
-};
-
-
-DebugCommandProcessor.prototype.setExceptionBreakRequest_ =
- function(request, response) {
- // Check for legal request.
- if (!request.arguments) {
- response.failed('Missing arguments');
- return;
- }
-
- // Pull out and check the 'type' argument:
- var type = request.arguments.type;
- if (!type) {
- response.failed('Missing argument "type"');
- return;
- }
-
- // Initialize the default value of enable:
- var enabled;
- if (type == 'all') {
- enabled = !Debug.isBreakOnException();
- } else if (type == 'uncaught') {
- enabled = !Debug.isBreakOnUncaughtException();
- }
-
- // Pull out and check the 'enabled' argument if present:
- if (!IS_UNDEFINED(request.arguments.enabled)) {
- enabled = request.arguments.enabled;
- if ((enabled != true) && (enabled != false)) {
- response.failed('Illegal value for "enabled":"' + enabled + '"');
- }
- }
-
- // Now set the exception break state:
- if (type == 'all') {
- %ChangeBreakOnException(Debug.ExceptionBreak.Caught, enabled);
- } else if (type == 'uncaught') {
- %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, enabled);
- } else {
- response.failed('Unknown "type":"' + type + '"');
- }
-
- // Add the cleared break point number to the response.
- response.body = { 'type': type, 'enabled': enabled };
-};
-
-
-DebugCommandProcessor.prototype.backtraceRequest_ = function(
- request, response) {
- // Get the number of frames.
- var total_frames = this.exec_state_.frameCount();
-
- // Create simple response if there are no frames.
- if (total_frames == 0) {
- response.body = {
- totalFrames: total_frames
- };
- return;
- }
-
- // Default frame range to include in backtrace.
- var from_index = 0;
- var to_index = kDefaultBacktraceLength;
-
- // Get the range from the arguments.
- if (request.arguments) {
- if (request.arguments.fromFrame) {
- from_index = request.arguments.fromFrame;
- }
- if (request.arguments.toFrame) {
- to_index = request.arguments.toFrame;
- }
- if (request.arguments.bottom) {
- var tmp_index = total_frames - from_index;
- from_index = total_frames - to_index;
- to_index = tmp_index;
- }
- if (from_index < 0 || to_index < 0) {
- return response.failed('Invalid frame number');
- }
- }
-
- // Adjust the index.
- to_index = Math.min(total_frames, to_index);
-
- if (to_index <= from_index) {
- var error = 'Invalid frame range';
- return response.failed(error);
- }
-
- // Create the response body.
- var frames = [];
- for (var i = from_index; i < to_index; i++) {
- frames.push(this.exec_state_.frame(i));
- }
- response.body = {
- fromFrame: from_index,
- toFrame: to_index,
- totalFrames: total_frames,
- frames: frames
- };
-};
-
-
-DebugCommandProcessor.prototype.frameRequest_ = function(request, response) {
- // No frames no source.
- if (this.exec_state_.frameCount() == 0) {
- return response.failed('No frames');
- }
-
- // With no arguments just keep the selected frame.
- if (request.arguments) {
- var index = request.arguments.number;
- if (index < 0 || this.exec_state_.frameCount() <= index) {
- return response.failed('Invalid frame number');
- }
-
- this.exec_state_.setSelectedFrame(request.arguments.number);
- }
- response.body = this.exec_state_.frame();
-};
-
-
-DebugCommandProcessor.prototype.resolveFrameFromScopeDescription_ =
- function(scope_description) {
- // Get the frame for which the scope or scopes are requested.
- // With no frameNumber argument use the currently selected frame.
- if (scope_description && !IS_UNDEFINED(scope_description.frameNumber)) {
- var frame_index = scope_description.frameNumber;
- if (frame_index < 0 || this.exec_state_.frameCount() <= frame_index) {
- throw new Error('Invalid frame number');
- }
- return this.exec_state_.frame(frame_index);
- } else {
- return this.exec_state_.frame();
- }
-};
-
-
-// Gets scope host object from request. It is either a function
-// ('functionHandle' argument must be specified) or a stack frame
-// ('frameNumber' may be specified and the current frame is taken by default).
-DebugCommandProcessor.prototype.resolveScopeHolder_ =
- function(scope_description) {
- if (scope_description && "functionHandle" in scope_description) {
- if (!IS_NUMBER(scope_description.functionHandle)) {
- throw new Error('Function handle must be a number');
- }
- var function_mirror = LookupMirror(scope_description.functionHandle);
- if (!function_mirror) {
- throw new Error('Failed to find function object by handle');
- }
- if (!function_mirror.isFunction()) {
- throw new Error('Value of non-function type is found by handle');
- }
- return function_mirror;
- } else {
- // No frames no scopes.
- if (this.exec_state_.frameCount() == 0) {
- throw new Error('No scopes');
- }
-
- // Get the frame for which the scopes are requested.
- var frame = this.resolveFrameFromScopeDescription_(scope_description);
- return frame;
- }
-}
-
-
-DebugCommandProcessor.prototype.scopesRequest_ = function(request, response) {
- var scope_holder = this.resolveScopeHolder_(request.arguments);
-
- // Fill all scopes for this frame or function.
- var total_scopes = scope_holder.scopeCount();
- var scopes = [];
- for (var i = 0; i < total_scopes; i++) {
- scopes.push(scope_holder.scope(i));
- }
- response.body = {
- fromScope: 0,
- toScope: total_scopes,
- totalScopes: total_scopes,
- scopes: scopes
- };
-};
-
-
-DebugCommandProcessor.prototype.scopeRequest_ = function(request, response) {
- // Get the frame or function for which the scope is requested.
- var scope_holder = this.resolveScopeHolder_(request.arguments);
-
- // With no scope argument just return top scope.
- var scope_index = 0;
- if (request.arguments && !IS_UNDEFINED(request.arguments.number)) {
- scope_index = builtins.$toNumber(request.arguments.number);
- if (scope_index < 0 || scope_holder.scopeCount() <= scope_index) {
- return response.failed('Invalid scope number');
- }
- }
-
- response.body = scope_holder.scope(scope_index);
-};
-
-
-// Reads value from protocol description. Description may be in form of type
-// (for singletons), raw value (primitive types supported in JSON),
-// string value description plus type (for primitive values) or handle id.
-// Returns raw value or throws exception.
-DebugCommandProcessor.resolveValue_ = function(value_description) {
- if ("handle" in value_description) {
- var value_mirror = LookupMirror(value_description.handle);
- if (!value_mirror) {
- throw new Error("Failed to resolve value by handle, ' #" +
- value_description.handle + "# not found");
- }
- return value_mirror.value();
- } else if ("stringDescription" in value_description) {
- if (value_description.type == BOOLEAN_TYPE) {
- return Boolean(value_description.stringDescription);
- } else if (value_description.type == NUMBER_TYPE) {
- return Number(value_description.stringDescription);
- } if (value_description.type == STRING_TYPE) {
- return String(value_description.stringDescription);
- } else {
- throw new Error("Unknown type");
- }
- } else if ("value" in value_description) {
- return value_description.value;
- } else if (value_description.type == UNDEFINED_TYPE) {
- return UNDEFINED;
- } else if (value_description.type == NULL_TYPE) {
- return null;
- } else {
- throw new Error("Failed to parse value description");
- }
-};
-
-
-DebugCommandProcessor.prototype.setVariableValueRequest_ =
- function(request, response) {
- if (!request.arguments) {
- response.failed('Missing arguments');
- return;
- }
-
- if (IS_UNDEFINED(request.arguments.name)) {
- response.failed('Missing variable name');
- }
- var variable_name = request.arguments.name;
-
- var scope_description = request.arguments.scope;
-
- // Get the frame or function for which the scope is requested.
- var scope_holder = this.resolveScopeHolder_(scope_description);
-
- if (IS_UNDEFINED(scope_description.number)) {
- response.failed('Missing scope number');
- }
- var scope_index = builtins.$toNumber(scope_description.number);
-
- var scope = scope_holder.scope(scope_index);
-
- var new_value =
- DebugCommandProcessor.resolveValue_(request.arguments.newValue);
-
- scope.setVariableValue(variable_name, new_value);
-
- var new_value_mirror = MakeMirror(new_value);
-
- response.body = {
- newValue: new_value_mirror
- };
-};
-
-
-DebugCommandProcessor.prototype.evaluateRequest_ = function(request, response) {
- if (!request.arguments) {
- return response.failed('Missing arguments');
- }
-
- // Pull out arguments.
- var expression = request.arguments.expression;
- var frame = request.arguments.frame;
- var global = request.arguments.global;
- var disable_break = request.arguments.disable_break;
- var additional_context = request.arguments.additional_context;
-
- // The expression argument could be an integer so we convert it to a
- // string.
- try {
- expression = String(expression);
- } catch(e) {
- return response.failed('Failed to convert expression argument to string');
- }
-
- // Check for legal arguments.
- if (!IS_UNDEFINED(frame) && global) {
- return response.failed('Arguments "frame" and "global" are exclusive');
- }
-
- var additional_context_object;
- if (additional_context) {
- additional_context_object = {};
- for (var i = 0; i < additional_context.length; i++) {
- var mapping = additional_context[i];
-
- if (!IS_STRING(mapping.name)) {
- return response.failed("Context element #" + i +
- " doesn't contain name:string property");
- }
-
- var raw_value = DebugCommandProcessor.resolveValue_(mapping);
- additional_context_object[mapping.name] = raw_value;
- }
- }
-
- // Global evaluate.
- if (global) {
- // Evaluate in the native context.
- response.body = this.exec_state_.evaluateGlobal(
- expression, Boolean(disable_break), additional_context_object);
- return;
- }
-
- // Default value for disable_break is true.
- if (IS_UNDEFINED(disable_break)) {
- disable_break = true;
- }
-
- // No frames no evaluate in frame.
- if (this.exec_state_.frameCount() == 0) {
- return response.failed('No frames');
- }
-
- // Check whether a frame was specified.
- if (!IS_UNDEFINED(frame)) {
- var frame_number = builtins.$toNumber(frame);
- if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) {
- return response.failed('Invalid frame "' + frame + '"');
- }
- // Evaluate in the specified frame.
- response.body = this.exec_state_.frame(frame_number).evaluate(
- expression, Boolean(disable_break), additional_context_object);
- return;
- } else {
- // Evaluate in the selected frame.
- response.body = this.exec_state_.frame().evaluate(
- expression, Boolean(disable_break), additional_context_object);
- return;
- }
-};
-
-
-DebugCommandProcessor.prototype.lookupRequest_ = function(request, response) {
- if (!request.arguments) {
- return response.failed('Missing arguments');
- }
-
- // Pull out arguments.
- var handles = request.arguments.handles;
-
- // Check for legal arguments.
- if (IS_UNDEFINED(handles)) {
- return response.failed('Argument "handles" missing');
- }
-
- // Set 'includeSource' option for script lookup.
- if (!IS_UNDEFINED(request.arguments.includeSource)) {
- var includeSource = builtins.$toBoolean(request.arguments.includeSource);
- response.setOption('includeSource', includeSource);
- }
-
- // Lookup handles.
- var mirrors = {};
- for (var i = 0; i < handles.length; i++) {
- var handle = handles[i];
- var mirror = LookupMirror(handle);
- if (!mirror) {
- return response.failed('Object #' + handle + '# not found');
- }
- mirrors[handle] = mirror;
- }
- response.body = mirrors;
-};
-
-
-DebugCommandProcessor.prototype.referencesRequest_ =
- function(request, response) {
- if (!request.arguments) {
- return response.failed('Missing arguments');
- }
-
- // Pull out arguments.
- var type = request.arguments.type;
- var handle = request.arguments.handle;
-
- // Check for legal arguments.
- if (IS_UNDEFINED(type)) {
- return response.failed('Argument "type" missing');
- }
- if (IS_UNDEFINED(handle)) {
- return response.failed('Argument "handle" missing');
- }
- if (type != 'referencedBy' && type != 'constructedBy') {
- return response.failed('Invalid type "' + type + '"');
- }
-
- // Lookup handle and return objects with references the object.
- var mirror = LookupMirror(handle);
- if (mirror) {
- if (type == 'referencedBy') {
- response.body = mirror.referencedBy();
- } else {
- response.body = mirror.constructedBy();
- }
- } else {
- return response.failed('Object #' + handle + '# not found');
- }
-};
-
-
-DebugCommandProcessor.prototype.sourceRequest_ = function(request, response) {
- // No frames no source.
- if (this.exec_state_.frameCount() == 0) {
- return response.failed('No source');
- }
-
- var from_line;
- var to_line;
- var frame = this.exec_state_.frame();
- if (request.arguments) {
- // Pull out arguments.
- from_line = request.arguments.fromLine;
- to_line = request.arguments.toLine;
-
- if (!IS_UNDEFINED(request.arguments.frame)) {
- var frame_number = builtins.$toNumber(request.arguments.frame);
- if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) {
- return response.failed('Invalid frame "' + frame + '"');
- }
- frame = this.exec_state_.frame(frame_number);
- }
- }
-
- // Get the script selected.
- var script = frame.func().script();
- if (!script) {
- return response.failed('No source');
- }
-
- // Get the source slice and fill it into the response.
- var slice = script.sourceSlice(from_line, to_line);
- if (!slice) {
- return response.failed('Invalid line interval');
- }
- response.body = {};
- response.body.source = slice.sourceText();
- response.body.fromLine = slice.from_line;
- response.body.toLine = slice.to_line;
- response.body.fromPosition = slice.from_position;
- response.body.toPosition = slice.to_position;
- response.body.totalLines = script.lineCount();
-};
-
-
-DebugCommandProcessor.prototype.scriptsRequest_ = function(request, response) {
- var types = ScriptTypeFlag(Debug.ScriptType.Normal);
- var includeSource = false;
- var idsToInclude = null;
- if (request.arguments) {
- // Pull out arguments.
- if (!IS_UNDEFINED(request.arguments.types)) {
- types = builtins.$toNumber(request.arguments.types);
- if (isNaN(types) || types < 0) {
- return response.failed('Invalid types "' +
- request.arguments.types + '"');
- }
- }
-
- if (!IS_UNDEFINED(request.arguments.includeSource)) {
- includeSource = builtins.$toBoolean(request.arguments.includeSource);
- response.setOption('includeSource', includeSource);
- }
-
- if (IS_ARRAY(request.arguments.ids)) {
- idsToInclude = {};
- var ids = request.arguments.ids;
- for (var i = 0; i < ids.length; i++) {
- idsToInclude[ids[i]] = true;
- }
- }
-
- var filterStr = null;
- var filterNum = null;
- if (!IS_UNDEFINED(request.arguments.filter)) {
- var num = builtins.$toNumber(request.arguments.filter);
- if (!isNaN(num)) {
- filterNum = num;
- }
- filterStr = request.arguments.filter;
- }
- }
-
- // Collect all scripts in the heap.
- var scripts = %DebugGetLoadedScripts();
-
- response.body = [];
-
- for (var i = 0; i < scripts.length; i++) {
- if (idsToInclude && !idsToInclude[scripts[i].id]) {
- continue;
- }
- if (filterStr || filterNum) {
- var script = scripts[i];
- var found = false;
- if (filterNum && !found) {
- if (script.id && script.id === filterNum) {
- found = true;
- }
- }
- if (filterStr && !found) {
- if (script.name && script.name.indexOf(filterStr) >= 0) {
- found = true;
- }
- }
- if (!found) continue;
- }
- if (types & ScriptTypeFlag(scripts[i].type)) {
- response.body.push(MakeMirror(scripts[i]));
- }
- }
-};
-
-
-DebugCommandProcessor.prototype.threadsRequest_ = function(request, response) {
- // Get the number of threads.
- var total_threads = this.exec_state_.threadCount();
-
- // Get information for all threads.
- var threads = [];
- for (var i = 0; i < total_threads; i++) {
- var details = %GetThreadDetails(this.exec_state_.break_id, i);
- var thread_info = { current: details[0],
- id: details[1]
- };
- threads.push(thread_info);
- }
-
- // Create the response body.
- response.body = {
- totalThreads: total_threads,
- threads: threads
- };
-};
-
-
-DebugCommandProcessor.prototype.suspendRequest_ = function(request, response) {
- response.running = false;
-};
-
-
-DebugCommandProcessor.prototype.versionRequest_ = function(request, response) {
- response.body = {
- V8Version: %GetV8Version()
- };
-};
-
-
-DebugCommandProcessor.prototype.changeLiveRequest_ = function(
- request, response) {
- if (!request.arguments) {
- return response.failed('Missing arguments');
- }
- var script_id = request.arguments.script_id;
- var preview_only = !!request.arguments.preview_only;
-
- var scripts = %DebugGetLoadedScripts();
-
- var the_script = null;
- for (var i = 0; i < scripts.length; i++) {
- if (scripts[i].id == script_id) {
- the_script = scripts[i];
- }
- }
- if (!the_script) {
- response.failed('Script not found');
- return;
- }
-
- var change_log = new Array();
-
- if (!IS_STRING(request.arguments.new_source)) {
- throw "new_source argument expected";
- }
-
- var new_source = request.arguments.new_source;
-
- var result_description;
- try {
- result_description = Debug.LiveEdit.SetScriptSource(the_script,
- new_source, preview_only, change_log);
- } catch (e) {
- if (e instanceof Debug.LiveEdit.Failure && "details" in e) {
- response.failed(e.message, e.details);
- return;
- }
- throw e;
- }
- response.body = {change_log: change_log, result: result_description};
-
- if (!preview_only && !this.running_ && result_description.stack_modified) {
- response.body.stepin_recommended = true;
- }
-};
-
-
-DebugCommandProcessor.prototype.restartFrameRequest_ = function(
- request, response) {
- if (!request.arguments) {
- return response.failed('Missing arguments');
- }
- var frame = request.arguments.frame;
-
- // No frames to evaluate in frame.
- if (this.exec_state_.frameCount() == 0) {
- return response.failed('No frames');
- }
-
- var frame_mirror;
- // Check whether a frame was specified.
- if (!IS_UNDEFINED(frame)) {
- var frame_number = builtins.$toNumber(frame);
- if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) {
- return response.failed('Invalid frame "' + frame + '"');
- }
- // Restart specified frame.
- frame_mirror = this.exec_state_.frame(frame_number);
- } else {
- // Restart selected frame.
- frame_mirror = this.exec_state_.frame();
- }
-
- var result_description = Debug.LiveEdit.RestartFrame(frame_mirror);
- response.body = {result: result_description};
-};
-
-
-DebugCommandProcessor.prototype.debuggerFlagsRequest_ = function(request,
- response) {
- // Check for legal request.
- if (!request.arguments) {
- response.failed('Missing arguments');
- return;
- }
-
- // Pull out arguments.
- var flags = request.arguments.flags;
-
- response.body = { flags: [] };
- if (!IS_UNDEFINED(flags)) {
- for (var i = 0; i < flags.length; i++) {
- var name = flags[i].name;
- var debugger_flag = debugger_flags[name];
- if (!debugger_flag) {
- continue;
- }
- if ('value' in flags[i]) {
- debugger_flag.setValue(flags[i].value);
- }
- response.body.flags.push({ name: name, value: debugger_flag.getValue() });
- }
- } else {
- for (var name in debugger_flags) {
- var value = debugger_flags[name].getValue();
- response.body.flags.push({ name: name, value: value });
- }
- }
-};
-
-
-DebugCommandProcessor.prototype.v8FlagsRequest_ = function(request, response) {
- var flags = request.arguments.flags;
- if (!flags) flags = '';
- %SetFlags(flags);
-};
-
-
-DebugCommandProcessor.prototype.gcRequest_ = function(request, response) {
- var type = request.arguments.type;
- if (!type) type = 'all';
-
- var before = %GetHeapUsage();
- %CollectGarbage(type);
- var after = %GetHeapUsage();
-
- response.body = { "before": before, "after": after };
-};
-
-
-DebugCommandProcessor.prototype.dispatch_ = (function() {
- var proto = DebugCommandProcessor.prototype;
- return {
- "continue": proto.continueRequest_,
- "break" : proto.breakRequest_,
- "setbreakpoint" : proto.setBreakPointRequest_,
- "changebreakpoint": proto.changeBreakPointRequest_,
- "clearbreakpoint": proto.clearBreakPointRequest_,
- "clearbreakpointgroup": proto.clearBreakPointGroupRequest_,
- "disconnect": proto.disconnectRequest_,
- "setexceptionbreak": proto.setExceptionBreakRequest_,
- "listbreakpoints": proto.listBreakpointsRequest_,
- "backtrace": proto.backtraceRequest_,
- "frame": proto.frameRequest_,
- "scopes": proto.scopesRequest_,
- "scope": proto.scopeRequest_,
- "setvariablevalue": proto.setVariableValueRequest_,
- "evaluate": proto.evaluateRequest_,
- "lookup": proto.lookupRequest_,
- "references": proto.referencesRequest_,
- "source": proto.sourceRequest_,
- "scripts": proto.scriptsRequest_,
- "threads": proto.threadsRequest_,
- "suspend": proto.suspendRequest_,
- "version": proto.versionRequest_,
- "changelive": proto.changeLiveRequest_,
- "restartframe": proto.restartFrameRequest_,
- "flags": proto.debuggerFlagsRequest_,
- "v8flag": proto.v8FlagsRequest_,
- "gc": proto.gcRequest_,
- };
-})();
-
-
-// Check whether the previously processed command caused the VM to become
-// running.
-DebugCommandProcessor.prototype.isRunning = function() {
- return this.running_;
-};
-
-
-DebugCommandProcessor.prototype.systemBreak = function(cmd, args) {
- return %SystemBreak();
-};
-
-
-/**
- * Convert an Object to its debugger protocol representation. The representation
- * may be serilized to a JSON object using JSON.stringify().
- * This implementation simply runs through all string property names, converts
- * each property value to a protocol value and adds the property to the result
- * object. For type "object" the function will be called recursively. Note that
- * circular structures will cause infinite recursion.
- * @param {Object} object The object to format as protocol object.
- * @param {MirrorSerializer} mirror_serializer The serializer to use if any
- * mirror objects are encountered.
- * @return {Object} Protocol object value.
- */
-function ObjectToProtocolObject_(object, mirror_serializer) {
- var content = {};
- for (var key in object) {
- // Only consider string keys.
- if (typeof key == 'string') {
- // Format the value based on its type.
- var property_value_json = ValueToProtocolValue_(object[key],
- mirror_serializer);
- // Add the property if relevant.
- if (!IS_UNDEFINED(property_value_json)) {
- content[key] = property_value_json;
- }
- }
- }
-
- return content;
-}
-
-
-/**
- * Convert an array to its debugger protocol representation. It will convert
- * each array element to a protocol value.
- * @param {Array} array The array to format as protocol array.
- * @param {MirrorSerializer} mirror_serializer The serializer to use if any
- * mirror objects are encountered.
- * @return {Array} Protocol array value.
- */
-function ArrayToProtocolArray_(array, mirror_serializer) {
- var json = [];
- for (var i = 0; i < array.length; i++) {
- json.push(ValueToProtocolValue_(array[i], mirror_serializer));
- }
- return json;
-}
-
-
-/**
- * Convert a value to its debugger protocol representation.
- * @param {*} value The value to format as protocol value.
- * @param {MirrorSerializer} mirror_serializer The serializer to use if any
- * mirror objects are encountered.
- * @return {*} Protocol value.
- */
-function ValueToProtocolValue_(value, mirror_serializer) {
- // Format the value based on its type.
- var json;
- switch (typeof value) {
- case 'object':
- if (value instanceof Mirror) {
- json = mirror_serializer.serializeValue(value);
- } else if (IS_ARRAY(value)){
- json = ArrayToProtocolArray_(value, mirror_serializer);
- } else {
- json = ObjectToProtocolObject_(value, mirror_serializer);
- }
- break;
-
- case 'boolean':
- case 'string':
- case 'number':
- json = value;
- break;
-
- default:
- json = null;
- }
- return json;
-}
+++ /dev/null
-// Copyright 2012 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/api.h"
-#include "src/arguments.h"
-#include "src/bootstrapper.h"
-#include "src/code-stubs.h"
-#include "src/codegen.h"
-#include "src/compilation-cache.h"
-#include "src/compiler.h"
-#include "src/debug.h"
-#include "src/deoptimizer.h"
-#include "src/execution.h"
-#include "src/full-codegen/full-codegen.h"
-#include "src/global-handles.h"
-#include "src/list.h"
-#include "src/log.h"
-#include "src/messages.h"
-#include "src/snapshot/natives.h"
-
-#include "include/v8-debug.h"
-
-namespace v8 {
-namespace internal {
-
-Debug::Debug(Isolate* isolate)
- : debug_context_(Handle<Context>()),
- event_listener_(Handle<Object>()),
- event_listener_data_(Handle<Object>()),
- message_handler_(NULL),
- command_received_(0),
- command_queue_(isolate->logger(), kQueueInitialSize),
- is_active_(false),
- is_suppressed_(false),
- live_edit_enabled_(true), // TODO(yangguo): set to false by default.
- break_disabled_(false),
- in_debug_event_listener_(false),
- break_on_exception_(false),
- break_on_uncaught_exception_(false),
- script_cache_(NULL),
- debug_info_list_(NULL),
- isolate_(isolate) {
- ThreadInit();
-}
-
-
-static v8::Local<v8::Context> GetDebugEventContext(Isolate* isolate) {
- Handle<Context> context = isolate->debug()->debugger_entry()->GetContext();
- // Isolate::context() may have been NULL when "script collected" event
- // occured.
- if (context.is_null()) return v8::Local<v8::Context>();
- Handle<Context> native_context(context->native_context());
- return v8::Utils::ToLocal(native_context);
-}
-
-
-BreakLocation::BreakLocation(Handle<DebugInfo> debug_info, RelocInfo* rinfo,
- int position, int statement_position)
- : debug_info_(debug_info),
- pc_offset_(static_cast<int>(rinfo->pc() - debug_info->code()->entry())),
- rmode_(rinfo->rmode()),
- data_(rinfo->data()),
- position_(position),
- statement_position_(statement_position) {}
-
-
-BreakLocation::Iterator::Iterator(Handle<DebugInfo> debug_info,
- BreakLocatorType type)
- : debug_info_(debug_info),
- reloc_iterator_(debug_info->code(), GetModeMask(type)),
- break_index_(-1),
- position_(1),
- statement_position_(1) {
- if (!Done()) Next();
-}
-
-
-int BreakLocation::Iterator::GetModeMask(BreakLocatorType type) {
- int mask = 0;
- mask |= RelocInfo::ModeMask(RelocInfo::POSITION);
- mask |= RelocInfo::ModeMask(RelocInfo::STATEMENT_POSITION);
- mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_RETURN);
- mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_CALL);
- mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_CONSTRUCT_CALL);
- if (type == ALL_BREAK_LOCATIONS) {
- mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_POSITION);
- mask |= RelocInfo::ModeMask(RelocInfo::DEBUGGER_STATEMENT);
- }
- return mask;
-}
-
-
-void BreakLocation::Iterator::Next() {
- DisallowHeapAllocation no_gc;
- DCHECK(!Done());
-
- // Iterate through reloc info for code and original code stopping at each
- // breakable code target.
- bool first = break_index_ == -1;
- while (!Done()) {
- if (!first) reloc_iterator_.next();
- first = false;
- if (Done()) return;
-
- // Whenever a statement position or (plain) position is passed update the
- // current value of these.
- if (RelocInfo::IsPosition(rmode())) {
- if (RelocInfo::IsStatementPosition(rmode())) {
- statement_position_ = static_cast<int>(
- rinfo()->data() - debug_info_->shared()->start_position());
- }
- // Always update the position as we don't want that to be before the
- // statement position.
- position_ = static_cast<int>(rinfo()->data() -
- debug_info_->shared()->start_position());
- DCHECK(position_ >= 0);
- DCHECK(statement_position_ >= 0);
- continue;
- }
-
- DCHECK(RelocInfo::IsDebugBreakSlot(rmode()) ||
- RelocInfo::IsDebuggerStatement(rmode()));
-
- if (RelocInfo::IsDebugBreakSlotAtReturn(rmode())) {
- // Set the positions to the end of the function.
- if (debug_info_->shared()->HasSourceCode()) {
- position_ = debug_info_->shared()->end_position() -
- debug_info_->shared()->start_position() - 1;
- } else {
- position_ = 0;
- }
- statement_position_ = position_;
- }
-
- break;
- }
- break_index_++;
-}
-
-
-// Find the break point at the supplied address, or the closest one before
-// the address.
-BreakLocation BreakLocation::FromAddress(Handle<DebugInfo> debug_info,
- BreakLocatorType type, Address pc) {
- Iterator it(debug_info, type);
- it.SkipTo(BreakIndexFromAddress(debug_info, type, pc));
- return it.GetBreakLocation();
-}
-
-
-// Find the break point at the supplied address, or the closest one before
-// the address.
-void BreakLocation::FromAddressSameStatement(Handle<DebugInfo> debug_info,
- BreakLocatorType type, Address pc,
- List<BreakLocation>* result_out) {
- int break_index = BreakIndexFromAddress(debug_info, type, pc);
- Iterator it(debug_info, type);
- it.SkipTo(break_index);
- int statement_position = it.statement_position();
- while (!it.Done() && it.statement_position() == statement_position) {
- result_out->Add(it.GetBreakLocation());
- it.Next();
- }
-}
-
-
-int BreakLocation::BreakIndexFromAddress(Handle<DebugInfo> debug_info,
- BreakLocatorType type, Address pc) {
- // Run through all break points to locate the one closest to the address.
- int closest_break = 0;
- int distance = kMaxInt;
- for (Iterator it(debug_info, type); !it.Done(); it.Next()) {
- // Check if this break point is closer that what was previously found.
- if (it.pc() <= pc && pc - it.pc() < distance) {
- closest_break = it.break_index();
- distance = static_cast<int>(pc - it.pc());
- // Check whether we can't get any closer.
- if (distance == 0) break;
- }
- }
- return closest_break;
-}
-
-
-BreakLocation BreakLocation::FromPosition(Handle<DebugInfo> debug_info,
- BreakLocatorType type, int position,
- BreakPositionAlignment alignment) {
- // Run through all break points to locate the one closest to the source
- // position.
- int closest_break = 0;
- int distance = kMaxInt;
-
- for (Iterator it(debug_info, type); !it.Done(); it.Next()) {
- int next_position;
- if (alignment == STATEMENT_ALIGNED) {
- next_position = it.statement_position();
- } else {
- DCHECK(alignment == BREAK_POSITION_ALIGNED);
- next_position = it.position();
- }
- if (position <= next_position && next_position - position < distance) {
- closest_break = it.break_index();
- distance = next_position - position;
- // Check whether we can't get any closer.
- if (distance == 0) break;
- }
- }
-
- Iterator it(debug_info, type);
- it.SkipTo(closest_break);
- return it.GetBreakLocation();
-}
-
-
-void BreakLocation::SetBreakPoint(Handle<Object> break_point_object) {
- // If there is not already a real break point here patch code with debug
- // break.
- if (!HasBreakPoint()) SetDebugBreak();
- DCHECK(IsDebugBreak() || IsDebuggerStatement());
- // Set the break point information.
- DebugInfo::SetBreakPoint(debug_info_, pc_offset_, position_,
- statement_position_, break_point_object);
-}
-
-
-void BreakLocation::ClearBreakPoint(Handle<Object> break_point_object) {
- // Clear the break point information.
- DebugInfo::ClearBreakPoint(debug_info_, pc_offset_, break_point_object);
- // If there are no more break points here remove the debug break.
- if (!HasBreakPoint()) {
- ClearDebugBreak();
- DCHECK(!IsDebugBreak());
- }
-}
-
-
-void BreakLocation::SetOneShot() {
- // Debugger statement always calls debugger. No need to modify it.
- if (IsDebuggerStatement()) return;
-
- // If there is a real break point here no more to do.
- if (HasBreakPoint()) {
- DCHECK(IsDebugBreak());
- return;
- }
-
- // Patch code with debug break.
- SetDebugBreak();
-}
-
-
-void BreakLocation::ClearOneShot() {
- // Debugger statement always calls debugger. No need to modify it.
- if (IsDebuggerStatement()) return;
-
- // If there is a real break point here no more to do.
- if (HasBreakPoint()) {
- DCHECK(IsDebugBreak());
- return;
- }
-
- // Patch code removing debug break.
- ClearDebugBreak();
- DCHECK(!IsDebugBreak());
-}
-
-
-void BreakLocation::SetDebugBreak() {
- // Debugger statement always calls debugger. No need to modify it.
- if (IsDebuggerStatement()) return;
-
- // If there is already a break point here just return. This might happen if
- // the same code is flooded with break points twice. Flooding the same
- // function twice might happen when stepping in a function with an exception
- // handler as the handler and the function is the same.
- if (IsDebugBreak()) return;
-
- DCHECK(IsDebugBreakSlot());
- Builtins* builtins = debug_info_->GetIsolate()->builtins();
- Handle<Code> target =
- IsReturn() ? builtins->Return_DebugBreak() : builtins->Slot_DebugBreak();
- DebugCodegen::PatchDebugBreakSlot(pc(), target);
- DCHECK(IsDebugBreak());
-}
-
-
-void BreakLocation::ClearDebugBreak() {
- // Debugger statement always calls debugger. No need to modify it.
- if (IsDebuggerStatement()) return;
-
- DCHECK(IsDebugBreakSlot());
- DebugCodegen::ClearDebugBreakSlot(pc());
- DCHECK(!IsDebugBreak());
-}
-
-
-bool BreakLocation::IsStepInLocation() const {
- return IsConstructCall() || IsCall();
-}
-
-
-bool BreakLocation::IsDebugBreak() const {
- if (IsDebugBreakSlot()) {
- return rinfo().IsPatchedDebugBreakSlotSequence();
- }
- return false;
-}
-
-
-Handle<Object> BreakLocation::BreakPointObjects() const {
- return debug_info_->GetBreakPointObjects(pc_offset_);
-}
-
-
-// Threading support.
-void Debug::ThreadInit() {
- thread_local_.break_count_ = 0;
- thread_local_.break_id_ = 0;
- thread_local_.break_frame_id_ = StackFrame::NO_ID;
- thread_local_.last_step_action_ = StepNone;
- thread_local_.last_statement_position_ = RelocInfo::kNoPosition;
- thread_local_.step_count_ = 0;
- thread_local_.last_fp_ = 0;
- thread_local_.queued_step_count_ = 0;
- thread_local_.step_into_fp_ = 0;
- thread_local_.step_out_fp_ = 0;
- // TODO(isolates): frames_are_dropped_?
- base::NoBarrier_Store(&thread_local_.current_debug_scope_,
- static_cast<base::AtomicWord>(0));
- thread_local_.restarter_frame_function_pointer_ = NULL;
-}
-
-
-char* Debug::ArchiveDebug(char* storage) {
- char* to = storage;
- MemCopy(to, reinterpret_cast<char*>(&thread_local_), sizeof(ThreadLocal));
- ThreadInit();
- return storage + ArchiveSpacePerThread();
-}
-
-
-char* Debug::RestoreDebug(char* storage) {
- char* from = storage;
- MemCopy(reinterpret_cast<char*>(&thread_local_), from, sizeof(ThreadLocal));
- return storage + ArchiveSpacePerThread();
-}
-
-
-int Debug::ArchiveSpacePerThread() {
- return sizeof(ThreadLocal);
-}
-
-
-ScriptCache::ScriptCache(Isolate* isolate) : isolate_(isolate) {
- Heap* heap = isolate_->heap();
- HandleScope scope(isolate_);
-
- DCHECK(isolate_->debug()->is_active());
-
- // Perform a GC to get rid of all unreferenced scripts.
- heap->CollectAllGarbage(Heap::kMakeHeapIterableMask, "ScriptCache");
-
- // Scan heap for Script objects.
- List<Handle<Script> > scripts;
- {
- HeapIterator iterator(heap, HeapIterator::kFilterUnreachable);
- DisallowHeapAllocation no_allocation;
- for (HeapObject* obj = iterator.next(); obj != NULL;
- obj = iterator.next()) {
- if (obj->IsScript() && Script::cast(obj)->HasValidSource()) {
- scripts.Add(Handle<Script>(Script::cast(obj)));
- }
- }
- }
-
- GlobalHandles* global_handles = isolate_->global_handles();
- table_ = Handle<WeakValueHashTable>::cast(global_handles->Create(
- Object::cast(*WeakValueHashTable::New(isolate_, scripts.length()))));
- for (int i = 0; i < scripts.length(); i++) Add(scripts[i]);
-}
-
-
-void ScriptCache::Add(Handle<Script> script) {
- HandleScope scope(isolate_);
- Handle<Smi> id(script->id(), isolate_);
-
-#ifdef DEBUG
- Handle<Object> lookup(table_->LookupWeak(id), isolate_);
- if (!lookup->IsTheHole()) {
- Handle<Script> found = Handle<Script>::cast(lookup);
- DCHECK(script->id() == found->id());
- DCHECK(!script->name()->IsString() ||
- String::cast(script->name())->Equals(String::cast(found->name())));
- }
-#endif
-
- Handle<WeakValueHashTable> new_table =
- WeakValueHashTable::PutWeak(table_, id, script);
-
- if (new_table.is_identical_to(table_)) return;
- GlobalHandles* global_handles = isolate_->global_handles();
- global_handles->Destroy(Handle<Object>::cast(table_).location());
- table_ = Handle<WeakValueHashTable>::cast(
- global_handles->Create(Object::cast(*new_table)));
-}
-
-
-ScriptCache::~ScriptCache() {
- isolate_->global_handles()->Destroy(Handle<Object>::cast(table_).location());
- table_ = Handle<WeakValueHashTable>();
-}
-
-
-DebugInfoListNode::DebugInfoListNode(DebugInfo* debug_info): next_(NULL) {
- // Globalize the request debug info object and make it weak.
- GlobalHandles* global_handles = debug_info->GetIsolate()->global_handles();
- debug_info_ =
- Handle<DebugInfo>::cast(global_handles->Create(debug_info)).location();
-}
-
-
-DebugInfoListNode::~DebugInfoListNode() {
- if (debug_info_ == nullptr) return;
- GlobalHandles::Destroy(reinterpret_cast<Object**>(debug_info_));
- debug_info_ = nullptr;
-}
-
-
-bool Debug::CompileDebuggerScript(Isolate* isolate, int index) {
- Factory* factory = isolate->factory();
- HandleScope scope(isolate);
-
- // Bail out if the index is invalid.
- if (index == -1) return false;
-
- // Find source and name for the requested script.
- Handle<String> source_code =
- isolate->bootstrapper()->SourceLookup<Natives>(index);
- Vector<const char> name = Natives::GetScriptName(index);
- Handle<String> script_name =
- factory->NewStringFromAscii(name).ToHandleChecked();
- Handle<Context> context = isolate->native_context();
-
- // Compile the script.
- Handle<SharedFunctionInfo> function_info;
- function_info = Compiler::CompileScript(
- source_code, script_name, 0, 0, ScriptOriginOptions(), Handle<Object>(),
- context, NULL, NULL, ScriptCompiler::kNoCompileOptions, NATIVES_CODE,
- false);
- if (function_info.is_null()) return false;
-
- // Execute the shared function in the debugger context.
- Handle<JSFunction> function =
- factory->NewFunctionFromSharedFunctionInfo(function_info, context);
-
- MaybeHandle<Object> maybe_exception;
- MaybeHandle<Object> result = Execution::TryCall(
- function, handle(context->global_proxy()), 0, NULL, &maybe_exception);
-
- // Check for caught exceptions.
- if (result.is_null()) {
- DCHECK(!isolate->has_pending_exception());
- MessageLocation computed_location;
- isolate->ComputeLocation(&computed_location);
- Handle<JSMessageObject> message = MessageHandler::MakeMessageObject(
- isolate, MessageTemplate::kDebuggerLoading, &computed_location,
- isolate->factory()->undefined_value(), Handle<JSArray>());
- DCHECK(!isolate->has_pending_exception());
- Handle<Object> exception;
- if (maybe_exception.ToHandle(&exception)) {
- isolate->set_pending_exception(*exception);
- MessageHandler::ReportMessage(isolate, NULL, message);
- }
- DCHECK(!maybe_exception.is_null());
- return false;
- }
-
- // Mark this script as native and return successfully.
- Handle<Script> script(Script::cast(function->shared()->script()));
- script->set_type(Smi::FromInt(Script::TYPE_NATIVE));
- return true;
-}
-
-
-bool Debug::Load() {
- // Return if debugger is already loaded.
- if (is_loaded()) return true;
-
- // Bail out if we're already in the process of compiling the native
- // JavaScript source code for the debugger.
- if (is_suppressed_) return false;
- SuppressDebug while_loading(this);
-
- // Disable breakpoints and interrupts while compiling and running the
- // debugger scripts including the context creation code.
- DisableBreak disable(this, true);
- PostponeInterruptsScope postpone(isolate_);
-
- // Create the debugger context.
- HandleScope scope(isolate_);
- ExtensionConfiguration no_extensions;
- Handle<Context> context = isolate_->bootstrapper()->CreateEnvironment(
- MaybeHandle<JSGlobalProxy>(), v8::Local<ObjectTemplate>(),
- &no_extensions);
-
- // Fail if no context could be created.
- if (context.is_null()) return false;
-
- // Use the debugger context.
- SaveContext save(isolate_);
- isolate_->set_context(*context);
-
- // Expose the builtins object in the debugger context.
- Handle<String> key = isolate_->factory()->InternalizeOneByteString(
- STATIC_CHAR_VECTOR("builtins"));
- Handle<GlobalObject> global =
- Handle<GlobalObject>(context->global_object(), isolate_);
- Handle<JSBuiltinsObject> builtin =
- Handle<JSBuiltinsObject>(global->builtins(), isolate_);
- RETURN_ON_EXCEPTION_VALUE(
- isolate_, Object::SetProperty(global, key, builtin, SLOPPY), false);
-
- // Compile the JavaScript for the debugger in the debugger context.
- bool caught_exception =
- !CompileDebuggerScript(isolate_, Natives::GetIndex("mirror")) ||
- !CompileDebuggerScript(isolate_, Natives::GetIndex("debug"));
-
- if (FLAG_enable_liveedit) {
- caught_exception = caught_exception ||
- !CompileDebuggerScript(isolate_, Natives::GetIndex("liveedit"));
- }
- // Check for caught exceptions.
- if (caught_exception) return false;
-
- debug_context_ = Handle<Context>::cast(
- isolate_->global_handles()->Create(*context));
- return true;
-}
-
-
-void Debug::Unload() {
- ClearAllBreakPoints();
- ClearStepping();
-
- // Return debugger is not loaded.
- if (!is_loaded()) return;
-
- // Clear the script cache.
- if (script_cache_ != NULL) {
- delete script_cache_;
- script_cache_ = NULL;
- }
-
- // Clear debugger context global handle.
- GlobalHandles::Destroy(Handle<Object>::cast(debug_context_).location());
- debug_context_ = Handle<Context>();
-}
-
-
-void Debug::Break(Arguments args, JavaScriptFrame* frame) {
- Heap* heap = isolate_->heap();
- HandleScope scope(isolate_);
- DCHECK(args.length() == 0);
-
- // Initialize LiveEdit.
- LiveEdit::InitializeThreadLocal(this);
-
- // Just continue if breaks are disabled or debugger cannot be loaded.
- if (break_disabled()) return;
-
- // Enter the debugger.
- DebugScope debug_scope(this);
- if (debug_scope.failed()) return;
-
- // Postpone interrupt during breakpoint processing.
- PostponeInterruptsScope postpone(isolate_);
-
- // Get the debug info (create it if it does not exist).
- Handle<SharedFunctionInfo> shared =
- Handle<SharedFunctionInfo>(frame->function()->shared());
- Handle<DebugInfo> debug_info(shared->GetDebugInfo());
-
- // Find the break point where execution has stopped.
- // PC points to the instruction after the current one, possibly a break
- // location as well. So the "- 1" to exclude it from the search.
- Address call_pc = frame->pc() - 1;
- BreakLocation break_location =
- BreakLocation::FromAddress(debug_info, ALL_BREAK_LOCATIONS, call_pc);
-
- // Check whether step next reached a new statement.
- if (!StepNextContinue(&break_location, frame)) {
- // Decrease steps left if performing multiple steps.
- if (thread_local_.step_count_ > 0) {
- thread_local_.step_count_--;
- }
- }
-
- // If there is one or more real break points check whether any of these are
- // triggered.
- Handle<Object> break_points_hit(heap->undefined_value(), isolate_);
- if (break_location.HasBreakPoint()) {
- Handle<Object> break_point_objects = break_location.BreakPointObjects();
- break_points_hit = CheckBreakPoints(break_point_objects);
- }
-
- // If step out is active skip everything until the frame where we need to step
- // out to is reached, unless real breakpoint is hit.
- if (StepOutActive() &&
- frame->fp() != thread_local_.step_out_fp_ &&
- break_points_hit->IsUndefined() ) {
- // Step count should always be 0 for StepOut.
- DCHECK(thread_local_.step_count_ == 0);
- } else if (!break_points_hit->IsUndefined() ||
- (thread_local_.last_step_action_ != StepNone &&
- thread_local_.step_count_ == 0)) {
- // Notify debugger if a real break point is triggered or if performing
- // single stepping with no more steps to perform. Otherwise do another step.
-
- // Clear all current stepping setup.
- ClearStepping();
-
- if (thread_local_.queued_step_count_ > 0) {
- // Perform queued steps
- int step_count = thread_local_.queued_step_count_;
-
- // Clear queue
- thread_local_.queued_step_count_ = 0;
-
- PrepareStep(StepNext, step_count, StackFrame::NO_ID);
- } else {
- // Notify the debug event listeners.
- OnDebugBreak(break_points_hit, false);
- }
- } else if (thread_local_.last_step_action_ != StepNone) {
- // Hold on to last step action as it is cleared by the call to
- // ClearStepping.
- StepAction step_action = thread_local_.last_step_action_;
- int step_count = thread_local_.step_count_;
-
- // If StepNext goes deeper in code, StepOut until original frame
- // and keep step count queued up in the meantime.
- if (step_action == StepNext && frame->fp() < thread_local_.last_fp_) {
- // Count frames until target frame
- int count = 0;
- JavaScriptFrameIterator it(isolate_);
- while (!it.done() && it.frame()->fp() < thread_local_.last_fp_) {
- count++;
- it.Advance();
- }
-
- // Check that we indeed found the frame we are looking for.
- CHECK(!it.done() && (it.frame()->fp() == thread_local_.last_fp_));
- if (step_count > 1) {
- // Save old count and action to continue stepping after StepOut.
- thread_local_.queued_step_count_ = step_count - 1;
- }
-
- // Set up for StepOut to reach target frame.
- step_action = StepOut;
- step_count = count;
- }
-
- // Clear all current stepping setup.
- ClearStepping();
-
- // Set up for the remaining steps.
- PrepareStep(step_action, step_count, StackFrame::NO_ID);
- }
-}
-
-
-// Check the break point objects for whether one or more are actually
-// triggered. This function returns a JSArray with the break point objects
-// which is triggered.
-Handle<Object> Debug::CheckBreakPoints(Handle<Object> break_point_objects) {
- Factory* factory = isolate_->factory();
-
- // Count the number of break points hit. If there are multiple break points
- // they are in a FixedArray.
- Handle<FixedArray> break_points_hit;
- int break_points_hit_count = 0;
- DCHECK(!break_point_objects->IsUndefined());
- if (break_point_objects->IsFixedArray()) {
- Handle<FixedArray> array(FixedArray::cast(*break_point_objects));
- break_points_hit = factory->NewFixedArray(array->length());
- for (int i = 0; i < array->length(); i++) {
- Handle<Object> o(array->get(i), isolate_);
- if (CheckBreakPoint(o)) {
- break_points_hit->set(break_points_hit_count++, *o);
- }
- }
- } else {
- break_points_hit = factory->NewFixedArray(1);
- if (CheckBreakPoint(break_point_objects)) {
- break_points_hit->set(break_points_hit_count++, *break_point_objects);
- }
- }
-
- // Return undefined if no break points were triggered.
- if (break_points_hit_count == 0) {
- return factory->undefined_value();
- }
- // Return break points hit as a JSArray.
- Handle<JSArray> result = factory->NewJSArrayWithElements(break_points_hit);
- result->set_length(Smi::FromInt(break_points_hit_count));
- return result;
-}
-
-
-// Check whether a single break point object is triggered.
-bool Debug::CheckBreakPoint(Handle<Object> break_point_object) {
- Factory* factory = isolate_->factory();
- HandleScope scope(isolate_);
-
- // Ignore check if break point object is not a JSObject.
- if (!break_point_object->IsJSObject()) return true;
-
- // Get the function IsBreakPointTriggered (defined in debug-debugger.js).
- Handle<String> is_break_point_triggered_string =
- factory->InternalizeOneByteString(
- STATIC_CHAR_VECTOR("IsBreakPointTriggered"));
- Handle<GlobalObject> debug_global(debug_context()->global_object());
- Handle<JSFunction> check_break_point =
- Handle<JSFunction>::cast(Object::GetProperty(
- debug_global, is_break_point_triggered_string).ToHandleChecked());
-
- // Get the break id as an object.
- Handle<Object> break_id = factory->NewNumberFromInt(Debug::break_id());
-
- // Call HandleBreakPointx.
- Handle<Object> argv[] = { break_id, break_point_object };
- Handle<Object> result;
- if (!Execution::TryCall(check_break_point,
- isolate_->js_builtins_object(),
- arraysize(argv),
- argv).ToHandle(&result)) {
- return false;
- }
-
- // Return whether the break point is triggered.
- return result->IsTrue();
-}
-
-
-bool Debug::SetBreakPoint(Handle<JSFunction> function,
- Handle<Object> break_point_object,
- int* source_position) {
- HandleScope scope(isolate_);
-
- // Make sure the function is compiled and has set up the debug info.
- Handle<SharedFunctionInfo> shared(function->shared());
- if (!EnsureDebugInfo(shared, function)) {
- // Return if retrieving debug info failed.
- return true;
- }
-
- Handle<DebugInfo> debug_info(shared->GetDebugInfo());
- // Source positions starts with zero.
- DCHECK(*source_position >= 0);
-
- // Find the break point and change it.
- BreakLocation location = BreakLocation::FromPosition(
- debug_info, ALL_BREAK_LOCATIONS, *source_position, STATEMENT_ALIGNED);
- *source_position = location.statement_position();
- location.SetBreakPoint(break_point_object);
-
- // At least one active break point now.
- return debug_info->GetBreakPointCount() > 0;
-}
-
-
-bool Debug::SetBreakPointForScript(Handle<Script> script,
- Handle<Object> break_point_object,
- int* source_position,
- BreakPositionAlignment alignment) {
- HandleScope scope(isolate_);
-
- // Obtain shared function info for the function.
- Handle<Object> result =
- FindSharedFunctionInfoInScript(script, *source_position);
- if (result->IsUndefined()) return false;
-
- // Make sure the function has set up the debug info.
- Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>::cast(result);
- if (!EnsureDebugInfo(shared, Handle<JSFunction>::null())) {
- // Return if retrieving debug info failed.
- return false;
- }
-
- // Find position within function. The script position might be before the
- // source position of the first function.
- int position;
- if (shared->start_position() > *source_position) {
- position = 0;
- } else {
- position = *source_position - shared->start_position();
- }
-
- Handle<DebugInfo> debug_info(shared->GetDebugInfo());
- // Source positions starts with zero.
- DCHECK(position >= 0);
-
- // Find the break point and change it.
- BreakLocation location = BreakLocation::FromPosition(
- debug_info, ALL_BREAK_LOCATIONS, position, alignment);
- location.SetBreakPoint(break_point_object);
-
- position = (alignment == STATEMENT_ALIGNED) ? location.statement_position()
- : location.position();
-
- *source_position = position + shared->start_position();
-
- // At least one active break point now.
- DCHECK(debug_info->GetBreakPointCount() > 0);
- return true;
-}
-
-
-void Debug::ClearBreakPoint(Handle<Object> break_point_object) {
- HandleScope scope(isolate_);
-
- DebugInfoListNode* node = debug_info_list_;
- while (node != NULL) {
- Handle<Object> result =
- DebugInfo::FindBreakPointInfo(node->debug_info(), break_point_object);
- if (!result->IsUndefined()) {
- // Get information in the break point.
- Handle<BreakPointInfo> break_point_info =
- Handle<BreakPointInfo>::cast(result);
- Handle<DebugInfo> debug_info = node->debug_info();
-
- // Find the break point and clear it.
- Address pc = debug_info->code()->entry() +
- break_point_info->code_position()->value();
-
- BreakLocation location =
- BreakLocation::FromAddress(debug_info, ALL_BREAK_LOCATIONS, pc);
- location.ClearBreakPoint(break_point_object);
-
- // If there are no more break points left remove the debug info for this
- // function.
- if (debug_info->GetBreakPointCount() == 0) {
- RemoveDebugInfoAndClearFromShared(debug_info);
- }
-
- return;
- }
- node = node->next();
- }
-}
-
-
-// Clear out all the debug break code. This is ONLY supposed to be used when
-// shutting down the debugger as it will leave the break point information in
-// DebugInfo even though the code is patched back to the non break point state.
-void Debug::ClearAllBreakPoints() {
- for (DebugInfoListNode* node = debug_info_list_; node != NULL;
- node = node->next()) {
- for (BreakLocation::Iterator it(node->debug_info(), ALL_BREAK_LOCATIONS);
- !it.Done(); it.Next()) {
- it.GetBreakLocation().ClearDebugBreak();
- }
- }
- // Remove all debug info.
- while (debug_info_list_ != NULL) {
- RemoveDebugInfoAndClearFromShared(debug_info_list_->debug_info());
- }
-}
-
-
-void Debug::FloodWithOneShot(Handle<JSFunction> function,
- BreakLocatorType type) {
- // Make sure the function is compiled and has set up the debug info.
- Handle<SharedFunctionInfo> shared(function->shared());
- if (!EnsureDebugInfo(shared, function)) {
- // Return if we failed to retrieve the debug info.
- return;
- }
-
- // Flood the function with break points.
- Handle<DebugInfo> debug_info(shared->GetDebugInfo());
- for (BreakLocation::Iterator it(debug_info, type); !it.Done(); it.Next()) {
- it.GetBreakLocation().SetOneShot();
- }
-}
-
-
-void Debug::FloodBoundFunctionWithOneShot(Handle<JSFunction> function) {
- Handle<FixedArray> new_bindings(function->function_bindings());
- Handle<Object> bindee(new_bindings->get(JSFunction::kBoundFunctionIndex),
- isolate_);
-
- if (!bindee.is_null() && bindee->IsJSFunction()) {
- Handle<JSFunction> bindee_function(JSFunction::cast(*bindee));
- FloodWithOneShotGeneric(bindee_function);
- }
-}
-
-
-void Debug::FloodDefaultConstructorWithOneShot(Handle<JSFunction> function) {
- DCHECK(function->shared()->is_default_constructor());
- // Instead of stepping into the function we directly step into the super class
- // constructor.
- Isolate* isolate = function->GetIsolate();
- PrototypeIterator iter(isolate, function);
- Handle<Object> proto = PrototypeIterator::GetCurrent(iter);
- if (!proto->IsJSFunction()) return; // Object.prototype
- Handle<JSFunction> function_proto = Handle<JSFunction>::cast(proto);
- FloodWithOneShotGeneric(function_proto);
-}
-
-
-void Debug::FloodWithOneShotGeneric(Handle<JSFunction> function,
- Handle<Object> holder) {
- if (function->shared()->bound()) {
- FloodBoundFunctionWithOneShot(function);
- } else if (function->shared()->is_default_constructor()) {
- FloodDefaultConstructorWithOneShot(function);
- } else {
- Isolate* isolate = function->GetIsolate();
- // Don't allow step into functions in the native context.
- if (function->shared()->code() ==
- isolate->builtins()->builtin(Builtins::kFunctionApply) ||
- function->shared()->code() ==
- isolate->builtins()->builtin(Builtins::kFunctionCall)) {
- // Handle function.apply and function.call separately to flood the
- // function to be called and not the code for Builtins::FunctionApply or
- // Builtins::FunctionCall. The receiver of call/apply is the target
- // function.
- if (!holder.is_null() && holder->IsJSFunction()) {
- Handle<JSFunction> js_function = Handle<JSFunction>::cast(holder);
- FloodWithOneShotGeneric(js_function);
- }
- } else {
- FloodWithOneShot(function);
- }
- }
-}
-
-
-void Debug::FloodHandlerWithOneShot() {
- // Iterate through the JavaScript stack looking for handlers.
- StackFrame::Id id = break_frame_id();
- if (id == StackFrame::NO_ID) {
- // If there is no JavaScript stack don't do anything.
- return;
- }
- for (JavaScriptFrameIterator it(isolate_, id); !it.done(); it.Advance()) {
- JavaScriptFrame* frame = it.frame();
- int stack_slots = 0; // The computed stack slot count is not used.
- if (frame->LookupExceptionHandlerInTable(&stack_slots, NULL) > 0) {
- // Flood the function with the catch/finally block with break points.
- FloodWithOneShot(Handle<JSFunction>(frame->function()));
- return;
- }
- }
-}
-
-
-void Debug::ChangeBreakOnException(ExceptionBreakType type, bool enable) {
- if (type == BreakUncaughtException) {
- break_on_uncaught_exception_ = enable;
- } else {
- break_on_exception_ = enable;
- }
-}
-
-
-bool Debug::IsBreakOnException(ExceptionBreakType type) {
- if (type == BreakUncaughtException) {
- return break_on_uncaught_exception_;
- } else {
- return break_on_exception_;
- }
-}
-
-
-FrameSummary GetFirstFrameSummary(JavaScriptFrame* frame) {
- List<FrameSummary> frames(FLAG_max_inlining_levels + 1);
- frame->Summarize(&frames);
- return frames.first();
-}
-
-
-void Debug::PrepareStep(StepAction step_action,
- int step_count,
- StackFrame::Id frame_id) {
- HandleScope scope(isolate_);
-
- DCHECK(in_debug_scope());
-
- // Remember this step action and count.
- thread_local_.last_step_action_ = step_action;
- if (step_action == StepOut) {
- // For step out target frame will be found on the stack so there is no need
- // to set step counter for it. It's expected to always be 0 for StepOut.
- thread_local_.step_count_ = 0;
- } else {
- thread_local_.step_count_ = step_count;
- }
-
- // Get the frame where the execution has stopped and skip the debug frame if
- // any. The debug frame will only be present if execution was stopped due to
- // hitting a break point. In other situations (e.g. unhandled exception) the
- // debug frame is not present.
- StackFrame::Id id = break_frame_id();
- if (id == StackFrame::NO_ID) {
- // If there is no JavaScript stack don't do anything.
- return;
- }
- if (frame_id != StackFrame::NO_ID) {
- id = frame_id;
- }
- JavaScriptFrameIterator frames_it(isolate_, id);
- JavaScriptFrame* frame = frames_it.frame();
-
- // First of all ensure there is one-shot break points in the top handler
- // if any.
- FloodHandlerWithOneShot();
-
- // If the function on the top frame is unresolved perform step out. This will
- // be the case when calling unknown function and having the debugger stopped
- // in an unhandled exception.
- if (!frame->function()->IsJSFunction()) {
- // Step out: Find the calling JavaScript frame and flood it with
- // breakpoints.
- frames_it.Advance();
- // Fill the function to return to with one-shot break points.
- JSFunction* function = frames_it.frame()->function();
- FloodWithOneShot(Handle<JSFunction>(function));
- return;
- }
-
- // Get the debug info (create it if it does not exist).
- FrameSummary summary = GetFirstFrameSummary(frame);
- Handle<JSFunction> function(summary.function());
- Handle<SharedFunctionInfo> shared(function->shared());
- if (!EnsureDebugInfo(shared, function)) {
- // Return if ensuring debug info failed.
- return;
- }
-
- Handle<DebugInfo> debug_info(shared->GetDebugInfo());
- // Refresh frame summary if the code has been recompiled for debugging.
- if (shared->code() != *summary.code()) summary = GetFirstFrameSummary(frame);
-
- // PC points to the instruction after the current one, possibly a break
- // location as well. So the "- 1" to exclude it from the search.
- Address call_pc = summary.pc() - 1;
- BreakLocation location =
- BreakLocation::FromAddress(debug_info, ALL_BREAK_LOCATIONS, call_pc);
-
- // If this is the last break code target step out is the only possibility.
- if (location.IsReturn() || step_action == StepOut) {
- if (step_action == StepOut) {
- // Skip step_count frames starting with the current one.
- while (step_count-- > 0 && !frames_it.done()) {
- frames_it.Advance();
- }
- } else {
- DCHECK(location.IsReturn());
- frames_it.Advance();
- }
- // Skip native and extension functions on the stack.
- while (!frames_it.done() &&
- !frames_it.frame()->function()->IsSubjectToDebugging()) {
- frames_it.Advance();
- }
- // Step out: If there is a JavaScript caller frame, we need to
- // flood it with breakpoints.
- if (!frames_it.done()) {
- // Fill the function to return to with one-shot break points.
- JSFunction* function = frames_it.frame()->function();
- FloodWithOneShot(Handle<JSFunction>(function));
- // Set target frame pointer.
- ActivateStepOut(frames_it.frame());
- }
- return;
- }
-
- if (step_action != StepNext && step_action != StepMin) {
- // If there's restarter frame on top of the stack, just get the pointer
- // to function which is going to be restarted.
- if (thread_local_.restarter_frame_function_pointer_ != NULL) {
- Handle<JSFunction> restarted_function(
- JSFunction::cast(*thread_local_.restarter_frame_function_pointer_));
- FloodWithOneShot(restarted_function);
- } else if (location.IsCall()) {
- // Find target function on the expression stack.
- // Expression stack looks like this (top to bottom):
- // argN
- // ...
- // arg0
- // Receiver
- // Function to call
- int num_expressions_without_args =
- frame->ComputeExpressionsCount() - location.CallArgumentsCount();
- DCHECK(num_expressions_without_args >= 2);
- Object* fun = frame->GetExpression(num_expressions_without_args - 2);
-
- // Flood the actual target of call/apply.
- if (fun->IsJSFunction()) {
- Isolate* isolate = JSFunction::cast(fun)->GetIsolate();
- Code* apply = isolate->builtins()->builtin(Builtins::kFunctionApply);
- Code* call = isolate->builtins()->builtin(Builtins::kFunctionCall);
- // Find target function on the expression stack for expression like
- // Function.call.call...apply(...)
- int i = 1;
- while (fun->IsJSFunction()) {
- Code* code = JSFunction::cast(fun)->shared()->code();
- if (code != apply && code != call) break;
- DCHECK(num_expressions_without_args >= i);
- fun = frame->GetExpression(num_expressions_without_args - i);
- i--;
- }
- }
-
- if (fun->IsJSFunction()) {
- Handle<JSFunction> js_function(JSFunction::cast(fun));
- FloodWithOneShotGeneric(js_function);
- }
- }
-
- ActivateStepIn(frame);
- }
-
- // Fill the current function with one-shot break points even for step in on
- // a call target as the function called might be a native function for
- // which step in will not stop. It also prepares for stepping in
- // getters/setters.
- // If we are stepping into another frame, only fill calls and returns.
- FloodWithOneShot(function, step_action == StepFrame ? CALLS_AND_RETURNS
- : ALL_BREAK_LOCATIONS);
-
- // Remember source position and frame to handle step next.
- thread_local_.last_statement_position_ =
- debug_info->code()->SourceStatementPosition(summary.pc());
- thread_local_.last_fp_ = frame->UnpaddedFP();
-}
-
-
-// Check whether the current debug break should be reported to the debugger. It
-// is used to have step next and step in only report break back to the debugger
-// if on a different frame or in a different statement. In some situations
-// there will be several break points in the same statement when the code is
-// flooded with one-shot break points. This function helps to perform several
-// steps before reporting break back to the debugger.
-bool Debug::StepNextContinue(BreakLocation* break_location,
- JavaScriptFrame* frame) {
- // StepNext and StepOut shouldn't bring us deeper in code, so last frame
- // shouldn't be a parent of current frame.
- StepAction step_action = thread_local_.last_step_action_;
-
- if (step_action == StepNext || step_action == StepOut) {
- if (frame->fp() < thread_local_.last_fp_) return true;
- }
-
- // We stepped into a new frame if the frame pointer changed.
- if (step_action == StepFrame) {
- return frame->UnpaddedFP() == thread_local_.last_fp_;
- }
-
- // If the step last action was step next or step in make sure that a new
- // statement is hit.
- if (step_action == StepNext || step_action == StepIn) {
- // Never continue if returning from function.
- if (break_location->IsReturn()) return false;
-
- // Continue if we are still on the same frame and in the same statement.
- int current_statement_position =
- break_location->code()->SourceStatementPosition(frame->pc());
- return thread_local_.last_fp_ == frame->UnpaddedFP() &&
- thread_local_.last_statement_position_ == current_statement_position;
- }
-
- // No step next action - don't continue.
- return false;
-}
-
-
-// Check whether the code object at the specified address is a debug break code
-// object.
-bool Debug::IsDebugBreak(Address addr) {
- Code* code = Code::GetCodeFromTargetAddress(addr);
- return code->is_debug_stub();
-}
-
-
-// Simple function for returning the source positions for active break points.
-Handle<Object> Debug::GetSourceBreakLocations(
- Handle<SharedFunctionInfo> shared,
- BreakPositionAlignment position_alignment) {
- Isolate* isolate = shared->GetIsolate();
- Heap* heap = isolate->heap();
- if (!shared->HasDebugInfo()) {
- return Handle<Object>(heap->undefined_value(), isolate);
- }
- Handle<DebugInfo> debug_info(shared->GetDebugInfo());
- if (debug_info->GetBreakPointCount() == 0) {
- return Handle<Object>(heap->undefined_value(), isolate);
- }
- Handle<FixedArray> locations =
- isolate->factory()->NewFixedArray(debug_info->GetBreakPointCount());
- int count = 0;
- for (int i = 0; i < debug_info->break_points()->length(); ++i) {
- if (!debug_info->break_points()->get(i)->IsUndefined()) {
- BreakPointInfo* break_point_info =
- BreakPointInfo::cast(debug_info->break_points()->get(i));
- int break_points = break_point_info->GetBreakPointCount();
- if (break_points == 0) continue;
- Smi* position = NULL;
- switch (position_alignment) {
- case STATEMENT_ALIGNED:
- position = break_point_info->statement_position();
- break;
- case BREAK_POSITION_ALIGNED:
- position = break_point_info->source_position();
- break;
- }
- for (int j = 0; j < break_points; ++j) locations->set(count++, position);
- }
- }
- return locations;
-}
-
-
-// Handle stepping into a function.
-void Debug::HandleStepIn(Handle<Object> function_obj, bool is_constructor) {
- // Flood getter/setter if we either step in or step to another frame.
- bool step_frame = thread_local_.last_step_action_ == StepFrame;
- if (!StepInActive() && !step_frame) return;
- if (!function_obj->IsJSFunction()) return;
- Handle<JSFunction> function = Handle<JSFunction>::cast(function_obj);
- Isolate* isolate = function->GetIsolate();
-
- StackFrameIterator it(isolate);
- it.Advance();
- // For constructor functions skip another frame.
- if (is_constructor) {
- DCHECK(it.frame()->is_construct());
- it.Advance();
- }
- Address fp = it.frame()->fp();
-
- // Flood the function with one-shot break points if it is called from where
- // step into was requested, or when stepping into a new frame.
- if (fp == thread_local_.step_into_fp_ || step_frame) {
- FloodWithOneShotGeneric(function, Handle<Object>());
- }
-}
-
-
-void Debug::ClearStepping() {
- // Clear the various stepping setup.
- ClearOneShot();
- ClearStepIn();
- ClearStepOut();
- ClearStepNext();
-
- // Clear multiple step counter.
- thread_local_.step_count_ = 0;
-}
-
-
-// Clears all the one-shot break points that are currently set. Normally this
-// function is called each time a break point is hit as one shot break points
-// are used to support stepping.
-void Debug::ClearOneShot() {
- // The current implementation just runs through all the breakpoints. When the
- // last break point for a function is removed that function is automatically
- // removed from the list.
- for (DebugInfoListNode* node = debug_info_list_; node != NULL;
- node = node->next()) {
- for (BreakLocation::Iterator it(node->debug_info(), ALL_BREAK_LOCATIONS);
- !it.Done(); it.Next()) {
- it.GetBreakLocation().ClearOneShot();
- }
- }
-}
-
-
-void Debug::ActivateStepIn(StackFrame* frame) {
- DCHECK(!StepOutActive());
- thread_local_.step_into_fp_ = frame->UnpaddedFP();
-}
-
-
-void Debug::ClearStepIn() {
- thread_local_.step_into_fp_ = 0;
-}
-
-
-void Debug::ActivateStepOut(StackFrame* frame) {
- DCHECK(!StepInActive());
- thread_local_.step_out_fp_ = frame->UnpaddedFP();
-}
-
-
-void Debug::ClearStepOut() {
- thread_local_.step_out_fp_ = 0;
-}
-
-
-void Debug::ClearStepNext() {
- thread_local_.last_step_action_ = StepNone;
- thread_local_.last_statement_position_ = RelocInfo::kNoPosition;
- thread_local_.last_fp_ = 0;
-}
-
-
-bool MatchingCodeTargets(Code* target1, Code* target2) {
- if (target1 == target2) return true;
- if (target1->kind() != target2->kind()) return false;
- return target1->is_handler() || target1->is_inline_cache_stub();
-}
-
-
-// Count the number of calls before the current frame PC to find the
-// corresponding PC in the newly recompiled code.
-static Address ComputeNewPcForRedirect(Code* new_code, Code* old_code,
- Address old_pc) {
- DCHECK_EQ(old_code->kind(), Code::FUNCTION);
- DCHECK_EQ(new_code->kind(), Code::FUNCTION);
- DCHECK(new_code->has_debug_break_slots());
- static const int mask = RelocInfo::kCodeTargetMask;
-
- // Find the target of the current call.
- Code* target = NULL;
- intptr_t delta = 0;
- for (RelocIterator it(old_code, mask); !it.done(); it.next()) {
- RelocInfo* rinfo = it.rinfo();
- Address current_pc = rinfo->pc();
- // The frame PC is behind the call instruction by the call instruction size.
- if (current_pc > old_pc) break;
- delta = old_pc - current_pc;
- target = Code::GetCodeFromTargetAddress(rinfo->target_address());
- }
-
- // Count the number of calls to the same target before the current call.
- int index = 0;
- for (RelocIterator it(old_code, mask); !it.done(); it.next()) {
- RelocInfo* rinfo = it.rinfo();
- Address current_pc = rinfo->pc();
- if (current_pc > old_pc) break;
- Code* current = Code::GetCodeFromTargetAddress(rinfo->target_address());
- if (MatchingCodeTargets(target, current)) index++;
- }
-
- DCHECK(index > 0);
-
- // Repeat the count on the new code to find corresponding call.
- for (RelocIterator it(new_code, mask); !it.done(); it.next()) {
- RelocInfo* rinfo = it.rinfo();
- Code* current = Code::GetCodeFromTargetAddress(rinfo->target_address());
- if (MatchingCodeTargets(target, current)) index--;
- if (index == 0) return rinfo->pc() + delta;
- }
-
- UNREACHABLE();
- return NULL;
-}
-
-
-// Count the number of continuations at which the current pc offset is at.
-static int ComputeContinuationIndexFromPcOffset(Code* code, int pc_offset) {
- DCHECK_EQ(code->kind(), Code::FUNCTION);
- Address pc = code->instruction_start() + pc_offset;
- int mask = RelocInfo::ModeMask(RelocInfo::GENERATOR_CONTINUATION);
- int index = 0;
- for (RelocIterator it(code, mask); !it.done(); it.next()) {
- index++;
- RelocInfo* rinfo = it.rinfo();
- Address current_pc = rinfo->pc();
- if (current_pc == pc) break;
- DCHECK(current_pc < pc);
- }
- return index;
-}
-
-
-// Find the pc offset for the given continuation index.
-static int ComputePcOffsetFromContinuationIndex(Code* code, int index) {
- DCHECK_EQ(code->kind(), Code::FUNCTION);
- DCHECK(code->has_debug_break_slots());
- int mask = RelocInfo::ModeMask(RelocInfo::GENERATOR_CONTINUATION);
- RelocIterator it(code, mask);
- for (int i = 1; i < index; i++) it.next();
- return static_cast<int>(it.rinfo()->pc() - code->instruction_start());
-}
-
-
-class RedirectActiveFunctions : public ThreadVisitor {
- public:
- explicit RedirectActiveFunctions(SharedFunctionInfo* shared)
- : shared_(shared) {
- DCHECK(shared->HasDebugCode());
- }
-
- void VisitThread(Isolate* isolate, ThreadLocalTop* top) {
- for (JavaScriptFrameIterator it(isolate, top); !it.done(); it.Advance()) {
- JavaScriptFrame* frame = it.frame();
- JSFunction* function = frame->function();
- if (frame->is_optimized()) continue;
- if (!function->Inlines(shared_)) continue;
-
- Code* frame_code = frame->LookupCode();
- DCHECK(frame_code->kind() == Code::FUNCTION);
- if (frame_code->has_debug_break_slots()) continue;
-
- Code* new_code = function->shared()->code();
- Address old_pc = frame->pc();
- Address new_pc = ComputeNewPcForRedirect(new_code, frame_code, old_pc);
-
- if (FLAG_trace_deopt) {
- PrintF("Replacing pc for debugging: %08" V8PRIxPTR " => %08" V8PRIxPTR
- "\n",
- reinterpret_cast<intptr_t>(old_pc),
- reinterpret_cast<intptr_t>(new_pc));
- }
-
- if (FLAG_enable_embedded_constant_pool) {
- // Update constant pool pointer for new code.
- frame->set_constant_pool(new_code->constant_pool());
- }
-
- // Patch the return address to return into the code with
- // debug break slots.
- frame->set_pc(new_pc);
- }
- }
-
- private:
- SharedFunctionInfo* shared_;
- DisallowHeapAllocation no_gc_;
-};
-
-
-bool Debug::PrepareFunctionForBreakPoints(Handle<SharedFunctionInfo> shared) {
- DCHECK(shared->is_compiled());
-
- if (isolate_->concurrent_recompilation_enabled()) {
- isolate_->optimizing_compile_dispatcher()->Flush();
- }
-
- List<Handle<JSFunction> > functions;
- List<Handle<JSGeneratorObject> > suspended_generators;
-
- if (!shared->optimized_code_map()->IsSmi()) {
- shared->ClearOptimizedCodeMap();
- }
-
- // Make sure we abort incremental marking.
- isolate_->heap()->CollectAllGarbage(Heap::kMakeHeapIterableMask,
- "prepare for break points");
-
- {
- HeapIterator iterator(isolate_->heap());
- HeapObject* obj;
- bool include_generators = shared->is_generator();
-
- while ((obj = iterator.next())) {
- if (obj->IsJSFunction()) {
- JSFunction* function = JSFunction::cast(obj);
- if (!function->Inlines(*shared)) continue;
- if (function->code()->kind() == Code::OPTIMIZED_FUNCTION) {
- Deoptimizer::DeoptimizeFunction(function);
- }
- if (function->shared() == *shared) functions.Add(handle(function));
- } else if (include_generators && obj->IsJSGeneratorObject()) {
- JSGeneratorObject* generator_obj = JSGeneratorObject::cast(obj);
- if (!generator_obj->is_suspended()) continue;
- JSFunction* function = generator_obj->function();
- if (!function->Inlines(*shared)) continue;
- int pc_offset = generator_obj->continuation();
- int index =
- ComputeContinuationIndexFromPcOffset(function->code(), pc_offset);
- generator_obj->set_continuation(index);
- suspended_generators.Add(handle(generator_obj));
- }
- }
- }
-
- if (!shared->HasDebugCode()) {
- DCHECK(functions.length() > 0);
- if (!Compiler::CompileDebugCode(functions.first())) return false;
- }
-
- for (Handle<JSFunction> const function : functions) {
- function->ReplaceCode(shared->code());
- }
-
- for (Handle<JSGeneratorObject> const generator_obj : suspended_generators) {
- int index = generator_obj->continuation();
- int pc_offset = ComputePcOffsetFromContinuationIndex(shared->code(), index);
- generator_obj->set_continuation(pc_offset);
- }
-
- // Update PCs on the stack to point to recompiled code.
- RedirectActiveFunctions redirect_visitor(*shared);
- redirect_visitor.VisitThread(isolate_, isolate_->thread_local_top());
- isolate_->thread_manager()->IterateArchivedThreads(&redirect_visitor);
-
- return true;
-}
-
-
-class SharedFunctionInfoFinder {
- public:
- explicit SharedFunctionInfoFinder(int target_position)
- : current_candidate_(NULL),
- current_candidate_closure_(NULL),
- current_start_position_(RelocInfo::kNoPosition),
- target_position_(target_position) {}
-
- void NewCandidate(SharedFunctionInfo* shared, JSFunction* closure = NULL) {
- int start_position = shared->function_token_position();
- if (start_position == RelocInfo::kNoPosition) {
- start_position = shared->start_position();
- }
-
- if (start_position > target_position_) return;
- if (target_position_ > shared->end_position()) return;
-
- if (current_candidate_ != NULL) {
- if (current_start_position_ == start_position &&
- shared->end_position() == current_candidate_->end_position()) {
- // If a top-level function contains only one function
- // declaration the source for the top-level and the function
- // is the same. In that case prefer the non top-level function.
- if (shared->is_toplevel()) return;
- } else if (start_position < current_start_position_ ||
- current_candidate_->end_position() < shared->end_position()) {
- return;
- }
- }
-
- current_start_position_ = start_position;
- current_candidate_ = shared;
- current_candidate_closure_ = closure;
- }
-
- SharedFunctionInfo* Result() { return current_candidate_; }
-
- JSFunction* ResultClosure() { return current_candidate_closure_; }
-
- private:
- SharedFunctionInfo* current_candidate_;
- JSFunction* current_candidate_closure_;
- int current_start_position_;
- int target_position_;
- DisallowHeapAllocation no_gc_;
-};
-
-
-// We need to find a SFI for a literal that may not yet have been compiled yet,
-// and there may not be a JSFunction referencing it. Find the SFI closest to
-// the given position, compile it to reveal possible inner SFIs and repeat.
-// While we are at this, also ensure code with debug break slots so that we do
-// not have to compile a SFI without JSFunction, which is paifu for those that
-// cannot be compiled without context (need to find outer compilable SFI etc.)
-Handle<Object> Debug::FindSharedFunctionInfoInScript(Handle<Script> script,
- int position) {
- while (true) {
- // Go through all shared function infos associated with this script to
- // find the inner most function containing this position.
- if (!script->shared_function_infos()->IsWeakFixedArray()) break;
- WeakFixedArray* array =
- WeakFixedArray::cast(script->shared_function_infos());
-
- SharedFunctionInfo* shared;
- {
- SharedFunctionInfoFinder finder(position);
- for (int i = 0; i < array->Length(); i++) {
- Object* item = array->Get(i);
- if (!item->IsSharedFunctionInfo()) continue;
- finder.NewCandidate(SharedFunctionInfo::cast(item));
- }
- shared = finder.Result();
- if (shared == NULL) break;
- // We found it if it's already compiled and has debug code.
- if (shared->HasDebugCode()) return handle(shared);
- }
- // If not, compile to reveal inner functions, if possible.
- if (shared->allows_lazy_compilation_without_context()) {
- HandleScope scope(isolate_);
- if (!Compiler::CompileDebugCode(handle(shared))) break;
- continue;
- }
-
- // If not possible, comb the heap for the best suitable compile target.
- JSFunction* closure;
- {
- HeapIterator it(isolate_->heap());
- SharedFunctionInfoFinder finder(position);
- while (HeapObject* object = it.next()) {
- JSFunction* candidate_closure = NULL;
- SharedFunctionInfo* candidate = NULL;
- if (object->IsJSFunction()) {
- candidate_closure = JSFunction::cast(object);
- candidate = candidate_closure->shared();
- } else if (object->IsSharedFunctionInfo()) {
- candidate = SharedFunctionInfo::cast(object);
- if (!candidate->allows_lazy_compilation_without_context()) continue;
- } else {
- continue;
- }
- if (candidate->script() == *script) {
- finder.NewCandidate(candidate, candidate_closure);
- }
- }
- closure = finder.ResultClosure();
- shared = finder.Result();
- }
- HandleScope scope(isolate_);
- if (closure == NULL) {
- if (!Compiler::CompileDebugCode(handle(shared))) break;
- } else {
- if (!Compiler::CompileDebugCode(handle(closure))) break;
- }
- }
- return isolate_->factory()->undefined_value();
-}
-
-
-// Ensures the debug information is present for shared.
-bool Debug::EnsureDebugInfo(Handle<SharedFunctionInfo> shared,
- Handle<JSFunction> function) {
- if (!shared->IsSubjectToDebugging()) return false;
-
- // Return if we already have the debug info for shared.
- if (shared->HasDebugInfo()) return true;
-
- if (function.is_null()) {
- DCHECK(shared->HasDebugCode());
- } else if (!Compiler::EnsureCompiled(function, CLEAR_EXCEPTION)) {
- return false;
- }
-
- if (!PrepareFunctionForBreakPoints(shared)) return false;
-
- // Make sure IC state is clean. This is so that we correctly flood
- // accessor pairs when stepping in.
- shared->code()->ClearInlineCaches();
- shared->feedback_vector()->ClearICSlots(*shared);
-
- // Create the debug info object.
- DCHECK(shared->HasDebugCode());
- Handle<DebugInfo> debug_info = isolate_->factory()->NewDebugInfo(shared);
-
- // Add debug info to the list.
- DebugInfoListNode* node = new DebugInfoListNode(*debug_info);
- node->set_next(debug_info_list_);
- debug_info_list_ = node;
-
- return true;
-}
-
-
-void Debug::RemoveDebugInfoAndClearFromShared(Handle<DebugInfo> debug_info) {
- HandleScope scope(isolate_);
- Handle<SharedFunctionInfo> shared(debug_info->shared());
-
- DCHECK_NOT_NULL(debug_info_list_);
- // Run through the debug info objects to find this one and remove it.
- DebugInfoListNode* prev = NULL;
- DebugInfoListNode* current = debug_info_list_;
- while (current != NULL) {
- if (current->debug_info().is_identical_to(debug_info)) {
- // Unlink from list. If prev is NULL we are looking at the first element.
- if (prev == NULL) {
- debug_info_list_ = current->next();
- } else {
- prev->set_next(current->next());
- }
- delete current;
- shared->set_debug_info(isolate_->heap()->undefined_value());
- return;
- }
- // Move to next in list.
- prev = current;
- current = current->next();
- }
-
- UNREACHABLE();
-}
-
-
-void Debug::SetAfterBreakTarget(JavaScriptFrame* frame) {
- after_break_target_ = NULL;
-
- if (LiveEdit::SetAfterBreakTarget(this)) return; // LiveEdit did the job.
-
- // Continue just after the slot.
- after_break_target_ = frame->pc();
-}
-
-
-bool Debug::IsBreakAtReturn(JavaScriptFrame* frame) {
- HandleScope scope(isolate_);
-
- // Get the executing function in which the debug break occurred.
- Handle<JSFunction> function(JSFunction::cast(frame->function()));
- Handle<SharedFunctionInfo> shared(function->shared());
-
- // With no debug info there are no break points, so we can't be at a return.
- if (!shared->HasDebugInfo()) return false;
- Handle<DebugInfo> debug_info(shared->GetDebugInfo());
- Handle<Code> code(debug_info->code());
-#ifdef DEBUG
- // Get the code which is actually executing.
- Handle<Code> frame_code(frame->LookupCode());
- DCHECK(frame_code.is_identical_to(code));
-#endif
-
- // Find the reloc info matching the start of the debug break slot.
- Address slot_pc = frame->pc() - Assembler::kDebugBreakSlotLength;
- int mask = RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_RETURN);
- for (RelocIterator it(*code, mask); !it.done(); it.next()) {
- if (it.rinfo()->pc() == slot_pc) return true;
- }
- return false;
-}
-
-
-void Debug::FramesHaveBeenDropped(StackFrame::Id new_break_frame_id,
- LiveEdit::FrameDropMode mode,
- Object** restarter_frame_function_pointer) {
- if (mode != LiveEdit::CURRENTLY_SET_MODE) {
- thread_local_.frame_drop_mode_ = mode;
- }
- thread_local_.break_frame_id_ = new_break_frame_id;
- thread_local_.restarter_frame_function_pointer_ =
- restarter_frame_function_pointer;
-}
-
-
-bool Debug::IsDebugGlobal(GlobalObject* global) {
- return is_loaded() && global == debug_context()->global_object();
-}
-
-
-void Debug::ClearMirrorCache() {
- PostponeInterruptsScope postpone(isolate_);
- HandleScope scope(isolate_);
- AssertDebugContext();
- Factory* factory = isolate_->factory();
- Handle<GlobalObject> global(isolate_->global_object());
- JSObject::SetProperty(global,
- factory->NewStringFromAsciiChecked("next_handle_"),
- handle(Smi::FromInt(0), isolate_), SLOPPY).Check();
- JSObject::SetProperty(global,
- factory->NewStringFromAsciiChecked("mirror_cache_"),
- factory->NewJSArray(0, FAST_ELEMENTS), SLOPPY).Check();
-}
-
-
-Handle<FixedArray> Debug::GetLoadedScripts() {
- // Create and fill the script cache when the loaded scripts is requested for
- // the first time.
- if (script_cache_ == NULL) script_cache_ = new ScriptCache(isolate_);
-
- // Perform GC to get unreferenced scripts evicted from the cache before
- // returning the content.
- isolate_->heap()->CollectAllGarbage(Heap::kNoGCFlags,
- "Debug::GetLoadedScripts");
-
- // Get the scripts from the cache.
- return script_cache_->GetScripts();
-}
-
-
-void Debug::GetStepinPositions(JavaScriptFrame* frame, StackFrame::Id frame_id,
- List<int>* results_out) {
- FrameSummary summary = GetFirstFrameSummary(frame);
-
- Handle<JSFunction> fun = Handle<JSFunction>(summary.function());
- Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>(fun->shared());
-
- if (!EnsureDebugInfo(shared, fun)) return;
-
- Handle<DebugInfo> debug_info(shared->GetDebugInfo());
- // Refresh frame summary if the code has been recompiled for debugging.
- if (shared->code() != *summary.code()) summary = GetFirstFrameSummary(frame);
-
- // Find range of break points starting from the break point where execution
- // has stopped.
- Address call_pc = summary.pc() - 1;
- List<BreakLocation> locations;
- BreakLocation::FromAddressSameStatement(debug_info, ALL_BREAK_LOCATIONS,
- call_pc, &locations);
-
- for (BreakLocation location : locations) {
- if (location.pc() <= summary.pc()) {
- // 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) {
- continue; // We are not stepping.
- } else {
- JavaScriptFrameIterator 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.
- if (frame_it.frame()->id() != frame_id) continue;
- }
- }
- if (location.IsStepInLocation()) results_out->Add(location.position());
- }
-}
-
-
-void Debug::RecordEvalCaller(Handle<Script> script) {
- script->set_compilation_type(Script::COMPILATION_TYPE_EVAL);
- // For eval scripts add information on the function from which eval was
- // called.
- StackTraceFrameIterator it(script->GetIsolate());
- if (!it.done()) {
- script->set_eval_from_shared(it.frame()->function()->shared());
- Code* code = it.frame()->LookupCode();
- int offset = static_cast<int>(
- it.frame()->pc() - code->instruction_start());
- script->set_eval_from_instructions_offset(Smi::FromInt(offset));
- }
-}
-
-
-MaybeHandle<Object> Debug::MakeJSObject(const char* constructor_name,
- int argc,
- Handle<Object> argv[]) {
- AssertDebugContext();
- // Create the execution state object.
- Handle<GlobalObject> global(isolate_->global_object());
- Handle<Object> constructor = Object::GetProperty(
- isolate_, global, constructor_name).ToHandleChecked();
- DCHECK(constructor->IsJSFunction());
- if (!constructor->IsJSFunction()) return MaybeHandle<Object>();
- // We do not handle interrupts here. In particular, termination interrupts.
- PostponeInterruptsScope no_interrupts(isolate_);
- return Execution::TryCall(Handle<JSFunction>::cast(constructor),
- handle(debug_context()->global_proxy()),
- argc,
- argv);
-}
-
-
-MaybeHandle<Object> Debug::MakeExecutionState() {
- // Create the execution state object.
- Handle<Object> argv[] = { isolate_->factory()->NewNumberFromInt(break_id()) };
- return MakeJSObject("MakeExecutionState", arraysize(argv), argv);
-}
-
-
-MaybeHandle<Object> Debug::MakeBreakEvent(Handle<Object> break_points_hit) {
- // Create the new break event object.
- Handle<Object> argv[] = { isolate_->factory()->NewNumberFromInt(break_id()),
- break_points_hit };
- return MakeJSObject("MakeBreakEvent", arraysize(argv), argv);
-}
-
-
-MaybeHandle<Object> Debug::MakeExceptionEvent(Handle<Object> exception,
- bool uncaught,
- Handle<Object> promise) {
- // Create the new exception event object.
- Handle<Object> argv[] = { isolate_->factory()->NewNumberFromInt(break_id()),
- exception,
- isolate_->factory()->ToBoolean(uncaught),
- promise };
- return MakeJSObject("MakeExceptionEvent", arraysize(argv), argv);
-}
-
-
-MaybeHandle<Object> Debug::MakeCompileEvent(Handle<Script> script,
- v8::DebugEvent type) {
- // Create the compile event object.
- Handle<Object> script_wrapper = Script::GetWrapper(script);
- Handle<Object> argv[] = { script_wrapper,
- isolate_->factory()->NewNumberFromInt(type) };
- return MakeJSObject("MakeCompileEvent", arraysize(argv), argv);
-}
-
-
-MaybeHandle<Object> Debug::MakePromiseEvent(Handle<JSObject> event_data) {
- // Create the promise event object.
- Handle<Object> argv[] = { event_data };
- return MakeJSObject("MakePromiseEvent", arraysize(argv), argv);
-}
-
-
-MaybeHandle<Object> Debug::MakeAsyncTaskEvent(Handle<JSObject> task_event) {
- // Create the async task event object.
- Handle<Object> argv[] = { task_event };
- return MakeJSObject("MakeAsyncTaskEvent", arraysize(argv), argv);
-}
-
-
-void Debug::OnThrow(Handle<Object> exception) {
- if (in_debug_scope() || ignore_events()) return;
- // Temporarily clear any scheduled_exception to allow evaluating
- // JavaScript from the debug event handler.
- HandleScope scope(isolate_);
- Handle<Object> scheduled_exception;
- if (isolate_->has_scheduled_exception()) {
- scheduled_exception = handle(isolate_->scheduled_exception(), isolate_);
- isolate_->clear_scheduled_exception();
- }
- OnException(exception, isolate_->GetPromiseOnStackOnThrow());
- if (!scheduled_exception.is_null()) {
- isolate_->thread_local_top()->scheduled_exception_ = *scheduled_exception;
- }
-}
-
-
-void Debug::OnPromiseReject(Handle<JSObject> promise, Handle<Object> value) {
- if (in_debug_scope() || ignore_events()) return;
- HandleScope scope(isolate_);
- // Check whether the promise has been marked as having triggered a message.
- Handle<Symbol> key = isolate_->factory()->promise_debug_marker_symbol();
- if (JSReceiver::GetDataProperty(promise, key)->IsUndefined()) {
- OnException(value, promise);
- }
-}
-
-
-MaybeHandle<Object> Debug::PromiseHasUserDefinedRejectHandler(
- Handle<JSObject> promise) {
- Handle<JSFunction> fun = Handle<JSFunction>::cast(
- JSReceiver::GetDataProperty(isolate_->js_builtins_object(),
- isolate_->factory()->NewStringFromStaticChars(
- "$promiseHasUserDefinedRejectHandler")));
- return Execution::Call(isolate_, fun, promise, 0, NULL);
-}
-
-
-void Debug::OnException(Handle<Object> exception, Handle<Object> promise) {
- // In our prediction, try-finally is not considered to catch.
- Isolate::CatchType catch_type = isolate_->PredictExceptionCatcher();
- bool uncaught = (catch_type == Isolate::NOT_CAUGHT);
- if (promise->IsJSObject()) {
- Handle<JSObject> jspromise = Handle<JSObject>::cast(promise);
- // Mark the promise as already having triggered a message.
- Handle<Symbol> key = isolate_->factory()->promise_debug_marker_symbol();
- JSObject::SetProperty(jspromise, key, key, STRICT).Assert();
- // Check whether the promise reject is considered an uncaught exception.
- Handle<Object> has_reject_handler;
- ASSIGN_RETURN_ON_EXCEPTION_VALUE(
- isolate_, has_reject_handler,
- PromiseHasUserDefinedRejectHandler(jspromise), /* void */);
- uncaught = has_reject_handler->IsFalse();
- }
- // Bail out if exception breaks are not active
- if (uncaught) {
- // Uncaught exceptions are reported by either flags.
- if (!(break_on_uncaught_exception_ || break_on_exception_)) return;
- } else {
- // Caught exceptions are reported is activated.
- if (!break_on_exception_) return;
- }
-
- DebugScope debug_scope(this);
- if (debug_scope.failed()) return;
-
- // Clear all current stepping setup.
- ClearStepping();
-
- // Create the event data object.
- Handle<Object> event_data;
- // Bail out and don't call debugger if exception.
- if (!MakeExceptionEvent(
- exception, uncaught, promise).ToHandle(&event_data)) {
- return;
- }
-
- // Process debug event.
- ProcessDebugEvent(v8::Exception, Handle<JSObject>::cast(event_data), false);
- // Return to continue execution from where the exception was thrown.
-}
-
-
-void Debug::OnCompileError(Handle<Script> script) {
- if (ignore_events()) return;
-
- if (in_debug_scope()) {
- ProcessCompileEventInDebugScope(v8::CompileError, script);
- return;
- }
-
- HandleScope scope(isolate_);
- DebugScope debug_scope(this);
- if (debug_scope.failed()) return;
-
- // Create the compile state object.
- Handle<Object> event_data;
- // Bail out and don't call debugger if exception.
- if (!MakeCompileEvent(script, v8::CompileError).ToHandle(&event_data)) return;
-
- // Process debug event.
- ProcessDebugEvent(v8::CompileError, Handle<JSObject>::cast(event_data), true);
-}
-
-
-void Debug::OnDebugBreak(Handle<Object> break_points_hit,
- bool auto_continue) {
- // The caller provided for DebugScope.
- AssertDebugContext();
- // Bail out if there is no listener for this event
- if (ignore_events()) return;
-
- HandleScope scope(isolate_);
- // Create the event data object.
- Handle<Object> event_data;
- // Bail out and don't call debugger if exception.
- if (!MakeBreakEvent(break_points_hit).ToHandle(&event_data)) return;
-
- // Process debug event.
- ProcessDebugEvent(v8::Break,
- Handle<JSObject>::cast(event_data),
- auto_continue);
-}
-
-
-void Debug::OnBeforeCompile(Handle<Script> script) {
- if (in_debug_scope() || ignore_events()) return;
-
- HandleScope scope(isolate_);
- DebugScope debug_scope(this);
- if (debug_scope.failed()) return;
-
- // Create the event data object.
- Handle<Object> event_data;
- // Bail out and don't call debugger if exception.
- if (!MakeCompileEvent(script, v8::BeforeCompile).ToHandle(&event_data))
- return;
-
- // Process debug event.
- ProcessDebugEvent(v8::BeforeCompile,
- Handle<JSObject>::cast(event_data),
- true);
-}
-
-
-// Handle debugger actions when a new script is compiled.
-void Debug::OnAfterCompile(Handle<Script> script) {
- // Add the newly compiled script to the script cache.
- if (script_cache_ != NULL) script_cache_->Add(script);
-
- if (ignore_events()) return;
-
- if (in_debug_scope()) {
- ProcessCompileEventInDebugScope(v8::AfterCompile, script);
- return;
- }
-
- HandleScope scope(isolate_);
- DebugScope debug_scope(this);
- if (debug_scope.failed()) return;
-
- // If debugging there might be script break points registered for this
- // script. Make sure that these break points are set.
-
- // Get the function UpdateScriptBreakPoints (defined in debug-debugger.js).
- Handle<String> update_script_break_points_string =
- isolate_->factory()->InternalizeOneByteString(
- STATIC_CHAR_VECTOR("UpdateScriptBreakPoints"));
- Handle<GlobalObject> debug_global(debug_context()->global_object());
- Handle<Object> update_script_break_points =
- Object::GetProperty(
- debug_global, update_script_break_points_string).ToHandleChecked();
- if (!update_script_break_points->IsJSFunction()) {
- return;
- }
- DCHECK(update_script_break_points->IsJSFunction());
-
- // Wrap the script object in a proper JS object before passing it
- // to JavaScript.
- Handle<Object> wrapper = Script::GetWrapper(script);
-
- // Call UpdateScriptBreakPoints expect no exceptions.
- Handle<Object> argv[] = { wrapper };
- if (Execution::TryCall(Handle<JSFunction>::cast(update_script_break_points),
- isolate_->js_builtins_object(),
- arraysize(argv),
- argv).is_null()) {
- return;
- }
-
- // Create the compile state object.
- Handle<Object> event_data;
- // Bail out and don't call debugger if exception.
- if (!MakeCompileEvent(script, v8::AfterCompile).ToHandle(&event_data)) return;
-
- // Process debug event.
- ProcessDebugEvent(v8::AfterCompile, Handle<JSObject>::cast(event_data), true);
-}
-
-
-void Debug::OnPromiseEvent(Handle<JSObject> data) {
- if (in_debug_scope() || ignore_events()) return;
-
- HandleScope scope(isolate_);
- DebugScope debug_scope(this);
- if (debug_scope.failed()) return;
-
- // Create the script collected state object.
- Handle<Object> event_data;
- // Bail out and don't call debugger if exception.
- if (!MakePromiseEvent(data).ToHandle(&event_data)) return;
-
- // Process debug event.
- ProcessDebugEvent(v8::PromiseEvent,
- Handle<JSObject>::cast(event_data),
- true);
-}
-
-
-void Debug::OnAsyncTaskEvent(Handle<JSObject> data) {
- if (in_debug_scope() || ignore_events()) return;
-
- HandleScope scope(isolate_);
- DebugScope debug_scope(this);
- if (debug_scope.failed()) return;
-
- // Create the script collected state object.
- Handle<Object> event_data;
- // Bail out and don't call debugger if exception.
- if (!MakeAsyncTaskEvent(data).ToHandle(&event_data)) return;
-
- // Process debug event.
- ProcessDebugEvent(v8::AsyncTaskEvent,
- Handle<JSObject>::cast(event_data),
- true);
-}
-
-
-void Debug::ProcessDebugEvent(v8::DebugEvent event,
- Handle<JSObject> event_data,
- bool auto_continue) {
- HandleScope scope(isolate_);
-
- // Create the execution state.
- Handle<Object> exec_state;
- // Bail out and don't call debugger if exception.
- if (!MakeExecutionState().ToHandle(&exec_state)) return;
-
- // First notify the message handler if any.
- if (message_handler_ != NULL) {
- NotifyMessageHandler(event,
- Handle<JSObject>::cast(exec_state),
- event_data,
- auto_continue);
- }
- // Notify registered debug event listener. This can be either a C or
- // a JavaScript function. Don't call event listener for v8::Break
- // here, if it's only a debug command -- they will be processed later.
- if ((event != v8::Break || !auto_continue) && !event_listener_.is_null()) {
- CallEventCallback(event, exec_state, event_data, NULL);
- }
-}
-
-
-void Debug::CallEventCallback(v8::DebugEvent event,
- Handle<Object> exec_state,
- Handle<Object> event_data,
- v8::Debug::ClientData* client_data) {
- bool previous = in_debug_event_listener_;
- in_debug_event_listener_ = true;
- if (event_listener_->IsForeign()) {
- // Invoke the C debug event listener.
- v8::Debug::EventCallback callback =
- FUNCTION_CAST<v8::Debug::EventCallback>(
- Handle<Foreign>::cast(event_listener_)->foreign_address());
- EventDetailsImpl event_details(event,
- Handle<JSObject>::cast(exec_state),
- Handle<JSObject>::cast(event_data),
- event_listener_data_,
- client_data);
- callback(event_details);
- DCHECK(!isolate_->has_scheduled_exception());
- } else {
- // Invoke the JavaScript debug event listener.
- DCHECK(event_listener_->IsJSFunction());
- Handle<Object> argv[] = { Handle<Object>(Smi::FromInt(event), isolate_),
- exec_state,
- event_data,
- event_listener_data_ };
- Handle<JSReceiver> global(isolate_->global_proxy());
- Execution::TryCall(Handle<JSFunction>::cast(event_listener_),
- global, arraysize(argv), argv);
- }
- in_debug_event_listener_ = previous;
-}
-
-
-void Debug::ProcessCompileEventInDebugScope(v8::DebugEvent event,
- Handle<Script> script) {
- if (event_listener_.is_null()) return;
-
- SuppressDebug while_processing(this);
- DebugScope debug_scope(this);
- if (debug_scope.failed()) return;
-
- Handle<Object> event_data;
- // Bail out and don't call debugger if exception.
- if (!MakeCompileEvent(script, event).ToHandle(&event_data)) return;
-
- // Create the execution state.
- Handle<Object> exec_state;
- // Bail out and don't call debugger if exception.
- if (!MakeExecutionState().ToHandle(&exec_state)) return;
-
- CallEventCallback(event, exec_state, event_data, NULL);
-}
-
-
-Handle<Context> Debug::GetDebugContext() {
- if (!is_loaded()) return Handle<Context>();
- DebugScope debug_scope(this);
- if (debug_scope.failed()) return Handle<Context>();
- // The global handle may be destroyed soon after. Return it reboxed.
- return handle(*debug_context(), isolate_);
-}
-
-
-void Debug::NotifyMessageHandler(v8::DebugEvent event,
- Handle<JSObject> exec_state,
- Handle<JSObject> event_data,
- bool auto_continue) {
- // Prevent other interrupts from triggering, for example API callbacks,
- // while dispatching message handler callbacks.
- PostponeInterruptsScope no_interrupts(isolate_);
- DCHECK(is_active_);
- HandleScope scope(isolate_);
- // Process the individual events.
- bool sendEventMessage = false;
- switch (event) {
- case v8::Break:
- sendEventMessage = !auto_continue;
- break;
- case v8::NewFunction:
- case v8::BeforeCompile:
- case v8::CompileError:
- case v8::PromiseEvent:
- case v8::AsyncTaskEvent:
- break;
- case v8::Exception:
- case v8::AfterCompile:
- sendEventMessage = true;
- break;
- }
-
- // The debug command interrupt flag might have been set when the command was
- // added. It should be enough to clear the flag only once while we are in the
- // debugger.
- DCHECK(in_debug_scope());
- isolate_->stack_guard()->ClearDebugCommand();
-
- // Notify the debugger that a debug event has occurred unless auto continue is
- // active in which case no event is send.
- if (sendEventMessage) {
- MessageImpl message = MessageImpl::NewEvent(
- event,
- auto_continue,
- Handle<JSObject>::cast(exec_state),
- Handle<JSObject>::cast(event_data));
- InvokeMessageHandler(message);
- }
-
- // If auto continue don't make the event cause a break, but process messages
- // in the queue if any. For script collected events don't even process
- // messages in the queue as the execution state might not be what is expected
- // by the client.
- if (auto_continue && !has_commands()) return;
-
- // DebugCommandProcessor goes here.
- bool running = auto_continue;
-
- Handle<Object> cmd_processor_ctor = Object::GetProperty(
- isolate_, exec_state, "debugCommandProcessor").ToHandleChecked();
- Handle<Object> ctor_args[] = { isolate_->factory()->ToBoolean(running) };
- Handle<Object> cmd_processor = Execution::Call(
- isolate_, cmd_processor_ctor, exec_state, 1, ctor_args).ToHandleChecked();
- Handle<JSFunction> process_debug_request = Handle<JSFunction>::cast(
- Object::GetProperty(
- isolate_, cmd_processor, "processDebugRequest").ToHandleChecked());
- Handle<Object> is_running = Object::GetProperty(
- isolate_, cmd_processor, "isRunning").ToHandleChecked();
-
- // Process requests from the debugger.
- do {
- // Wait for new command in the queue.
- command_received_.Wait();
-
- // Get the command from the queue.
- CommandMessage command = command_queue_.Get();
- isolate_->logger()->DebugTag(
- "Got request from command queue, in interactive loop.");
- if (!is_active()) {
- // Delete command text and user data.
- command.Dispose();
- return;
- }
-
- Vector<const uc16> command_text(
- const_cast<const uc16*>(command.text().start()),
- command.text().length());
- Handle<String> request_text = isolate_->factory()->NewStringFromTwoByte(
- command_text).ToHandleChecked();
- Handle<Object> request_args[] = { request_text };
- Handle<Object> answer_value;
- Handle<String> answer;
- MaybeHandle<Object> maybe_exception;
- MaybeHandle<Object> maybe_result =
- Execution::TryCall(process_debug_request, cmd_processor, 1,
- request_args, &maybe_exception);
-
- if (maybe_result.ToHandle(&answer_value)) {
- if (answer_value->IsUndefined()) {
- answer = isolate_->factory()->empty_string();
- } else {
- answer = Handle<String>::cast(answer_value);
- }
-
- // Log the JSON request/response.
- if (FLAG_trace_debug_json) {
- PrintF("%s\n", request_text->ToCString().get());
- PrintF("%s\n", answer->ToCString().get());
- }
-
- Handle<Object> is_running_args[] = { answer };
- maybe_result = Execution::Call(
- isolate_, is_running, cmd_processor, 1, is_running_args);
- Handle<Object> result;
- if (!maybe_result.ToHandle(&result)) break;
- running = result->IsTrue();
- } else {
- Handle<Object> exception;
- if (!maybe_exception.ToHandle(&exception)) break;
- Handle<Object> result;
- if (!Execution::ToString(isolate_, exception).ToHandle(&result)) break;
- answer = Handle<String>::cast(result);
- }
-
- // Return the result.
- MessageImpl message = MessageImpl::NewResponse(
- event, running, exec_state, event_data, answer, command.client_data());
- InvokeMessageHandler(message);
- command.Dispose();
-
- // Return from debug event processing if either the VM is put into the
- // running state (through a continue command) or auto continue is active
- // and there are no more commands queued.
- } while (!running || has_commands());
- command_queue_.Clear();
-}
-
-
-void Debug::SetEventListener(Handle<Object> callback,
- Handle<Object> data) {
- GlobalHandles* global_handles = isolate_->global_handles();
-
- // Remove existing entry.
- GlobalHandles::Destroy(event_listener_.location());
- event_listener_ = Handle<Object>();
- GlobalHandles::Destroy(event_listener_data_.location());
- event_listener_data_ = Handle<Object>();
-
- // Set new entry.
- if (!callback->IsUndefined() && !callback->IsNull()) {
- event_listener_ = global_handles->Create(*callback);
- if (data.is_null()) data = isolate_->factory()->undefined_value();
- event_listener_data_ = global_handles->Create(*data);
- }
-
- UpdateState();
-}
-
-
-void Debug::SetMessageHandler(v8::Debug::MessageHandler handler) {
- message_handler_ = handler;
- UpdateState();
- if (handler == NULL && in_debug_scope()) {
- // Send an empty command to the debugger if in a break to make JavaScript
- // run again if the debugger is closed.
- EnqueueCommandMessage(Vector<const uint16_t>::empty());
- }
-}
-
-
-
-void Debug::UpdateState() {
- bool is_active = message_handler_ != NULL || !event_listener_.is_null();
- if (is_active || in_debug_scope()) {
- // Note that the debug context could have already been loaded to
- // bootstrap test cases.
- isolate_->compilation_cache()->Disable();
- is_active = Load();
- } else if (is_loaded()) {
- isolate_->compilation_cache()->Enable();
- Unload();
- }
- is_active_ = is_active;
-}
-
-
-// Calls the registered debug message handler. This callback is part of the
-// public API.
-void Debug::InvokeMessageHandler(MessageImpl message) {
- if (message_handler_ != NULL) message_handler_(message);
-}
-
-
-// Puts a command coming from the public API on the queue. Creates
-// a copy of the command string managed by the debugger. Up to this
-// point, the command data was managed by the API client. Called
-// by the API client thread.
-void Debug::EnqueueCommandMessage(Vector<const uint16_t> command,
- v8::Debug::ClientData* client_data) {
- // Need to cast away const.
- CommandMessage message = CommandMessage::New(
- Vector<uint16_t>(const_cast<uint16_t*>(command.start()),
- command.length()),
- client_data);
- isolate_->logger()->DebugTag("Put command on command_queue.");
- command_queue_.Put(message);
- command_received_.Signal();
-
- // Set the debug command break flag to have the command processed.
- if (!in_debug_scope()) isolate_->stack_guard()->RequestDebugCommand();
-}
-
-
-MaybeHandle<Object> Debug::Call(Handle<JSFunction> fun, Handle<Object> data) {
- DebugScope debug_scope(this);
- if (debug_scope.failed()) return isolate_->factory()->undefined_value();
-
- // Create the execution state.
- Handle<Object> exec_state;
- if (!MakeExecutionState().ToHandle(&exec_state)) {
- return isolate_->factory()->undefined_value();
- }
-
- Handle<Object> argv[] = { exec_state, data };
- return Execution::Call(
- isolate_,
- fun,
- Handle<Object>(debug_context()->global_proxy(), isolate_),
- arraysize(argv),
- argv);
-}
-
-
-void Debug::HandleDebugBreak() {
- // Ignore debug break during bootstrapping.
- if (isolate_->bootstrapper()->IsActive()) return;
- // Just continue if breaks are disabled.
- if (break_disabled()) return;
- // Ignore debug break if debugger is not active.
- if (!is_active()) return;
-
- StackLimitCheck check(isolate_);
- if (check.HasOverflowed()) return;
-
- { JavaScriptFrameIterator it(isolate_);
- DCHECK(!it.done());
- Object* fun = it.frame()->function();
- if (fun && fun->IsJSFunction()) {
- // Don't stop in builtin functions.
- if (JSFunction::cast(fun)->IsBuiltin()) return;
- GlobalObject* global = JSFunction::cast(fun)->context()->global_object();
- // Don't stop in debugger functions.
- if (IsDebugGlobal(global)) return;
- }
- }
-
- // Collect the break state before clearing the flags.
- bool debug_command_only = isolate_->stack_guard()->CheckDebugCommand() &&
- !isolate_->stack_guard()->CheckDebugBreak();
-
- isolate_->stack_guard()->ClearDebugBreak();
-
- ProcessDebugMessages(debug_command_only);
-}
-
-
-void Debug::ProcessDebugMessages(bool debug_command_only) {
- isolate_->stack_guard()->ClearDebugCommand();
-
- StackLimitCheck check(isolate_);
- if (check.HasOverflowed()) return;
-
- HandleScope scope(isolate_);
- DebugScope debug_scope(this);
- if (debug_scope.failed()) return;
-
- // Notify the debug event listeners. Indicate auto continue if the break was
- // a debug command break.
- OnDebugBreak(isolate_->factory()->undefined_value(), debug_command_only);
-}
-
-
-DebugScope::DebugScope(Debug* debug)
- : debug_(debug),
- prev_(debug->debugger_entry()),
- save_(debug_->isolate_),
- no_termination_exceptons_(debug_->isolate_,
- StackGuard::TERMINATE_EXECUTION) {
- // Link recursive debugger entry.
- base::NoBarrier_Store(&debug_->thread_local_.current_debug_scope_,
- reinterpret_cast<base::AtomicWord>(this));
-
- // Store the previous break id and frame id.
- break_id_ = debug_->break_id();
- break_frame_id_ = debug_->break_frame_id();
-
- // Create the new break info. If there is no JavaScript frames there is no
- // break frame id.
- JavaScriptFrameIterator it(isolate());
- bool has_js_frames = !it.done();
- debug_->thread_local_.break_frame_id_ = has_js_frames ? it.frame()->id()
- : StackFrame::NO_ID;
- debug_->SetNextBreakId();
-
- debug_->UpdateState();
- // Make sure that debugger is loaded and enter the debugger context.
- // The previous context is kept in save_.
- failed_ = !debug_->is_loaded();
- if (!failed_) isolate()->set_context(*debug->debug_context());
-}
-
-
-
-DebugScope::~DebugScope() {
- if (!failed_ && prev_ == NULL) {
- // Clear mirror cache when leaving the debugger. Skip this if there is a
- // pending exception as clearing the mirror cache calls back into
- // JavaScript. This can happen if the v8::Debug::Call is used in which
- // case the exception should end up in the calling code.
- if (!isolate()->has_pending_exception()) debug_->ClearMirrorCache();
-
- // If there are commands in the queue when leaving the debugger request
- // that these commands are processed.
- if (debug_->has_commands()) isolate()->stack_guard()->RequestDebugCommand();
- }
-
- // Leaving this debugger entry.
- base::NoBarrier_Store(&debug_->thread_local_.current_debug_scope_,
- reinterpret_cast<base::AtomicWord>(prev_));
-
- // Restore to the previous break state.
- debug_->thread_local_.break_frame_id_ = break_frame_id_;
- debug_->thread_local_.break_id_ = break_id_;
-
- debug_->UpdateState();
-}
-
-
-MessageImpl MessageImpl::NewEvent(DebugEvent event,
- bool running,
- Handle<JSObject> exec_state,
- Handle<JSObject> event_data) {
- MessageImpl message(true, event, running,
- exec_state, event_data, Handle<String>(), NULL);
- return message;
-}
-
-
-MessageImpl MessageImpl::NewResponse(DebugEvent event,
- bool running,
- Handle<JSObject> exec_state,
- Handle<JSObject> event_data,
- Handle<String> response_json,
- v8::Debug::ClientData* client_data) {
- MessageImpl message(false, event, running,
- exec_state, event_data, response_json, client_data);
- return message;
-}
-
-
-MessageImpl::MessageImpl(bool is_event,
- DebugEvent event,
- bool running,
- Handle<JSObject> exec_state,
- Handle<JSObject> event_data,
- Handle<String> response_json,
- v8::Debug::ClientData* client_data)
- : is_event_(is_event),
- event_(event),
- running_(running),
- exec_state_(exec_state),
- event_data_(event_data),
- response_json_(response_json),
- client_data_(client_data) {}
-
-
-bool MessageImpl::IsEvent() const {
- return is_event_;
-}
-
-
-bool MessageImpl::IsResponse() const {
- return !is_event_;
-}
-
-
-DebugEvent MessageImpl::GetEvent() const {
- return event_;
-}
-
-
-bool MessageImpl::WillStartRunning() const {
- return running_;
-}
-
-
-v8::Local<v8::Object> MessageImpl::GetExecutionState() const {
- return v8::Utils::ToLocal(exec_state_);
-}
-
-
-v8::Isolate* MessageImpl::GetIsolate() const {
- return reinterpret_cast<v8::Isolate*>(exec_state_->GetIsolate());
-}
-
-
-v8::Local<v8::Object> MessageImpl::GetEventData() const {
- return v8::Utils::ToLocal(event_data_);
-}
-
-
-v8::Local<v8::String> MessageImpl::GetJSON() const {
- Isolate* isolate = event_data_->GetIsolate();
- v8::EscapableHandleScope scope(reinterpret_cast<v8::Isolate*>(isolate));
-
- if (IsEvent()) {
- // Call toJSONProtocol on the debug event object.
- Handle<Object> fun = Object::GetProperty(
- isolate, event_data_, "toJSONProtocol").ToHandleChecked();
- if (!fun->IsJSFunction()) {
- return v8::Local<v8::String>();
- }
-
- MaybeHandle<Object> maybe_json =
- Execution::TryCall(Handle<JSFunction>::cast(fun), event_data_, 0, NULL);
- Handle<Object> json;
- if (!maybe_json.ToHandle(&json) || !json->IsString()) {
- return v8::Local<v8::String>();
- }
- return scope.Escape(v8::Utils::ToLocal(Handle<String>::cast(json)));
- } else {
- return v8::Utils::ToLocal(response_json_);
- }
-}
-
-
-v8::Local<v8::Context> MessageImpl::GetEventContext() const {
- Isolate* isolate = event_data_->GetIsolate();
- v8::Local<v8::Context> context = GetDebugEventContext(isolate);
- // Isolate::context() may be NULL when "script collected" event occurs.
- DCHECK(!context.IsEmpty());
- return context;
-}
-
-
-v8::Debug::ClientData* MessageImpl::GetClientData() const {
- return client_data_;
-}
-
-
-EventDetailsImpl::EventDetailsImpl(DebugEvent event,
- Handle<JSObject> exec_state,
- Handle<JSObject> event_data,
- Handle<Object> callback_data,
- v8::Debug::ClientData* client_data)
- : event_(event),
- exec_state_(exec_state),
- event_data_(event_data),
- callback_data_(callback_data),
- client_data_(client_data) {}
-
-
-DebugEvent EventDetailsImpl::GetEvent() const {
- return event_;
-}
-
-
-v8::Local<v8::Object> EventDetailsImpl::GetExecutionState() const {
- return v8::Utils::ToLocal(exec_state_);
-}
-
-
-v8::Local<v8::Object> EventDetailsImpl::GetEventData() const {
- return v8::Utils::ToLocal(event_data_);
-}
-
-
-v8::Local<v8::Context> EventDetailsImpl::GetEventContext() const {
- return GetDebugEventContext(exec_state_->GetIsolate());
-}
-
-
-v8::Local<v8::Value> EventDetailsImpl::GetCallbackData() const {
- return v8::Utils::ToLocal(callback_data_);
-}
-
-
-v8::Debug::ClientData* EventDetailsImpl::GetClientData() const {
- return client_data_;
-}
-
-
-CommandMessage::CommandMessage() : text_(Vector<uint16_t>::empty()),
- client_data_(NULL) {
-}
-
-
-CommandMessage::CommandMessage(const Vector<uint16_t>& text,
- v8::Debug::ClientData* data)
- : text_(text),
- client_data_(data) {
-}
-
-
-void CommandMessage::Dispose() {
- text_.Dispose();
- delete client_data_;
- client_data_ = NULL;
-}
-
-
-CommandMessage CommandMessage::New(const Vector<uint16_t>& command,
- v8::Debug::ClientData* data) {
- return CommandMessage(command.Clone(), data);
-}
-
-
-CommandMessageQueue::CommandMessageQueue(int size) : start_(0), end_(0),
- size_(size) {
- messages_ = NewArray<CommandMessage>(size);
-}
-
-
-CommandMessageQueue::~CommandMessageQueue() {
- while (!IsEmpty()) Get().Dispose();
- DeleteArray(messages_);
-}
-
-
-CommandMessage CommandMessageQueue::Get() {
- DCHECK(!IsEmpty());
- int result = start_;
- start_ = (start_ + 1) % size_;
- return messages_[result];
-}
-
-
-void CommandMessageQueue::Put(const CommandMessage& message) {
- if ((end_ + 1) % size_ == start_) {
- Expand();
- }
- messages_[end_] = message;
- end_ = (end_ + 1) % size_;
-}
-
-
-void CommandMessageQueue::Expand() {
- CommandMessageQueue new_queue(size_ * 2);
- while (!IsEmpty()) {
- new_queue.Put(Get());
- }
- CommandMessage* array_to_free = messages_;
- *this = new_queue;
- new_queue.messages_ = array_to_free;
- // Make the new_queue empty so that it doesn't call Dispose on any messages.
- new_queue.start_ = new_queue.end_;
- // Automatic destructor called on new_queue, freeing array_to_free.
-}
-
-
-LockingCommandMessageQueue::LockingCommandMessageQueue(Logger* logger, int size)
- : logger_(logger), queue_(size) {}
-
-
-bool LockingCommandMessageQueue::IsEmpty() const {
- base::LockGuard<base::Mutex> lock_guard(&mutex_);
- return queue_.IsEmpty();
-}
-
-
-CommandMessage LockingCommandMessageQueue::Get() {
- base::LockGuard<base::Mutex> lock_guard(&mutex_);
- CommandMessage result = queue_.Get();
- logger_->DebugEvent("Get", result.text());
- return result;
-}
-
-
-void LockingCommandMessageQueue::Put(const CommandMessage& message) {
- base::LockGuard<base::Mutex> lock_guard(&mutex_);
- queue_.Put(message);
- logger_->DebugEvent("Put", message.text());
-}
-
-
-void LockingCommandMessageQueue::Clear() {
- base::LockGuard<base::Mutex> lock_guard(&mutex_);
- queue_.Clear();
-}
-
-} // namespace internal
-} // namespace v8
+++ /dev/null
-// Copyright 2012 the V8 project authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef V8_DEBUG_H_
-#define V8_DEBUG_H_
-
-#include "src/allocation.h"
-#include "src/arguments.h"
-#include "src/assembler.h"
-#include "src/base/atomicops.h"
-#include "src/base/platform/platform.h"
-#include "src/execution.h"
-#include "src/factory.h"
-#include "src/flags.h"
-#include "src/frames-inl.h"
-#include "src/hashmap.h"
-#include "src/liveedit.h"
-#include "src/runtime/runtime.h"
-#include "src/string-stream.h"
-#include "src/v8threads.h"
-
-#include "include/v8-debug.h"
-
-namespace v8 {
-namespace internal {
-
-
-// Forward declarations.
-class DebugScope;
-
-
-// Step actions. NOTE: These values are in macros.py as well.
-enum StepAction {
- StepNone = -1, // Stepping not prepared.
- StepOut = 0, // Step out of the current function.
- StepNext = 1, // Step to the next statement in the current function.
- StepIn = 2, // Step into new functions invoked or the next statement
- // in the current function.
- StepMin = 3, // Perform a minimum step in the current function.
- StepInMin = 4, // Step into new functions invoked or perform a minimum step
- // in the current function.
- StepFrame = 5 // Step into a new frame or return to previous frame.
-};
-
-
-// Type of exception break. NOTE: These values are in macros.py as well.
-enum ExceptionBreakType {
- BreakException = 0,
- BreakUncaughtException = 1
-};
-
-
-// Type of exception break.
-enum BreakLocatorType { ALL_BREAK_LOCATIONS, CALLS_AND_RETURNS };
-
-
-// The different types of breakpoint position alignments.
-// Must match Debug.BreakPositionAlignment in debug-debugger.js
-enum BreakPositionAlignment {
- STATEMENT_ALIGNED = 0,
- BREAK_POSITION_ALIGNED = 1
-};
-
-
-class BreakLocation {
- public:
- // Find the break point at the supplied address, or the closest one before
- // the address.
- static BreakLocation FromAddress(Handle<DebugInfo> debug_info,
- BreakLocatorType type, Address pc);
-
- static void FromAddressSameStatement(Handle<DebugInfo> debug_info,
- BreakLocatorType type, Address pc,
- List<BreakLocation>* result_out);
-
- static BreakLocation FromPosition(Handle<DebugInfo> debug_info,
- BreakLocatorType type, int position,
- BreakPositionAlignment alignment);
-
- bool IsDebugBreak() const;
-
- inline bool IsReturn() const {
- return RelocInfo::IsDebugBreakSlotAtReturn(rmode_);
- }
- inline bool IsCall() const {
- return RelocInfo::IsDebugBreakSlotAtCall(rmode_);
- }
- inline bool IsConstructCall() const {
- return RelocInfo::IsDebugBreakSlotAtConstructCall(rmode_);
- }
- inline int CallArgumentsCount() const {
- DCHECK(IsCall());
- return RelocInfo::DebugBreakCallArgumentsCount(data_);
- }
-
- bool IsStepInLocation() const;
- inline bool HasBreakPoint() const {
- return debug_info_->HasBreakPoint(pc_offset_);
- }
-
- Handle<Object> BreakPointObjects() const;
-
- void SetBreakPoint(Handle<Object> break_point_object);
- void ClearBreakPoint(Handle<Object> break_point_object);
-
- void SetOneShot();
- void ClearOneShot();
-
-
- inline RelocInfo rinfo() const {
- return RelocInfo(pc(), rmode(), data_, code());
- }
-
- inline int position() const { return position_; }
- inline int statement_position() const { return statement_position_; }
-
- inline Address pc() const { return code()->entry() + pc_offset_; }
-
- inline RelocInfo::Mode rmode() const { return rmode_; }
-
- inline Code* code() const { return debug_info_->code(); }
-
- private:
- BreakLocation(Handle<DebugInfo> debug_info, RelocInfo* rinfo, int position,
- int statement_position);
-
- class Iterator {
- public:
- Iterator(Handle<DebugInfo> debug_info, BreakLocatorType type);
-
- BreakLocation GetBreakLocation() {
- return BreakLocation(debug_info_, rinfo(), position(),
- statement_position());
- }
-
- inline bool Done() const { return reloc_iterator_.done(); }
- void Next();
-
- void SkipTo(int count) {
- while (count-- > 0) Next();
- }
-
- inline RelocInfo::Mode rmode() { return reloc_iterator_.rinfo()->rmode(); }
- inline RelocInfo* rinfo() { return reloc_iterator_.rinfo(); }
- inline Address pc() { return rinfo()->pc(); }
- int break_index() const { return break_index_; }
- inline int position() const { return position_; }
- inline int statement_position() const { return statement_position_; }
-
- private:
- static int GetModeMask(BreakLocatorType type);
-
- Handle<DebugInfo> debug_info_;
- RelocIterator reloc_iterator_;
- int break_index_;
- int position_;
- int statement_position_;
-
- DisallowHeapAllocation no_gc_;
-
- DISALLOW_COPY_AND_ASSIGN(Iterator);
- };
-
- friend class Debug;
-
- static int BreakIndexFromAddress(Handle<DebugInfo> debug_info,
- BreakLocatorType type, Address pc);
-
- void SetDebugBreak();
- void ClearDebugBreak();
-
- inline bool IsDebuggerStatement() const {
- return RelocInfo::IsDebuggerStatement(rmode_);
- }
- inline bool IsDebugBreakSlot() const {
- return RelocInfo::IsDebugBreakSlot(rmode_);
- }
-
- Handle<DebugInfo> debug_info_;
- int pc_offset_;
- RelocInfo::Mode rmode_;
- intptr_t data_;
- int position_;
- int statement_position_;
-};
-
-
-// Cache of all script objects in the heap. When a script is added a weak handle
-// to it is created and that weak handle is stored in the cache. The weak handle
-// callback takes care of removing the script from the cache. The key used in
-// the cache is the script id.
-class ScriptCache {
- public:
- explicit ScriptCache(Isolate* isolate);
- ~ScriptCache();
-
- // Add script to the cache.
- void Add(Handle<Script> script);
-
- // Return the scripts in the cache.
- Handle<FixedArray> GetScripts() {
- return WeakValueHashTable::GetWeakValues(table_);
- }
-
- private:
- Isolate* isolate_;
- Handle<WeakValueHashTable> table_;
-};
-
-
-// Linked list holding debug info objects. The debug info objects are kept as
-// weak handles to avoid a debug info object to keep a function alive.
-class DebugInfoListNode {
- public:
- explicit DebugInfoListNode(DebugInfo* debug_info);
- ~DebugInfoListNode();
-
- DebugInfoListNode* next() { return next_; }
- void set_next(DebugInfoListNode* next) { next_ = next; }
- Handle<DebugInfo> debug_info() { return Handle<DebugInfo>(debug_info_); }
-
- private:
- // Global (weak) handle to the debug info object.
- DebugInfo** debug_info_;
-
- // Next pointer for linked list.
- DebugInfoListNode* next_;
-};
-
-
-
-// Message delivered to the message handler callback. This is either a debugger
-// event or the response to a command.
-class MessageImpl: public v8::Debug::Message {
- public:
- // Create a message object for a debug event.
- static MessageImpl NewEvent(DebugEvent event,
- bool running,
- Handle<JSObject> exec_state,
- Handle<JSObject> event_data);
-
- // Create a message object for the response to a debug command.
- static MessageImpl NewResponse(DebugEvent event,
- bool running,
- Handle<JSObject> exec_state,
- Handle<JSObject> event_data,
- Handle<String> response_json,
- v8::Debug::ClientData* client_data);
-
- // Implementation of interface v8::Debug::Message.
- virtual bool IsEvent() const;
- virtual bool IsResponse() const;
- virtual DebugEvent GetEvent() const;
- virtual bool WillStartRunning() const;
- virtual v8::Local<v8::Object> GetExecutionState() const;
- virtual v8::Local<v8::Object> GetEventData() const;
- virtual v8::Local<v8::String> GetJSON() const;
- virtual v8::Local<v8::Context> GetEventContext() const;
- virtual v8::Debug::ClientData* GetClientData() const;
- virtual v8::Isolate* GetIsolate() const;
-
- private:
- MessageImpl(bool is_event,
- DebugEvent event,
- bool running,
- Handle<JSObject> exec_state,
- Handle<JSObject> event_data,
- Handle<String> response_json,
- v8::Debug::ClientData* client_data);
-
- bool is_event_; // Does this message represent a debug event?
- DebugEvent event_; // Debug event causing the break.
- bool running_; // Will the VM start running after this event?
- Handle<JSObject> exec_state_; // Current execution state.
- Handle<JSObject> event_data_; // Data associated with the event.
- Handle<String> response_json_; // Response JSON if message holds a response.
- v8::Debug::ClientData* client_data_; // Client data passed with the request.
-};
-
-
-// Details of the debug event delivered to the debug event listener.
-class EventDetailsImpl : public v8::Debug::EventDetails {
- public:
- EventDetailsImpl(DebugEvent event,
- Handle<JSObject> exec_state,
- Handle<JSObject> event_data,
- Handle<Object> callback_data,
- v8::Debug::ClientData* client_data);
- virtual DebugEvent GetEvent() const;
- virtual v8::Local<v8::Object> GetExecutionState() const;
- virtual v8::Local<v8::Object> GetEventData() const;
- virtual v8::Local<v8::Context> GetEventContext() const;
- virtual v8::Local<v8::Value> GetCallbackData() const;
- virtual v8::Debug::ClientData* GetClientData() const;
- private:
- DebugEvent event_; // Debug event causing the break.
- Handle<JSObject> exec_state_; // Current execution state.
- Handle<JSObject> event_data_; // Data associated with the event.
- Handle<Object> callback_data_; // User data passed with the callback
- // when it was registered.
- v8::Debug::ClientData* client_data_; // Data passed to DebugBreakForCommand.
-};
-
-
-// Message send by user to v8 debugger or debugger output message.
-// In addition to command text it may contain a pointer to some user data
-// which are expected to be passed along with the command reponse to message
-// handler.
-class CommandMessage {
- public:
- static CommandMessage New(const Vector<uint16_t>& command,
- v8::Debug::ClientData* data);
- CommandMessage();
-
- // Deletes user data and disposes of the text.
- void Dispose();
- Vector<uint16_t> text() const { return text_; }
- v8::Debug::ClientData* client_data() const { return client_data_; }
- private:
- CommandMessage(const Vector<uint16_t>& text,
- v8::Debug::ClientData* data);
-
- Vector<uint16_t> text_;
- v8::Debug::ClientData* client_data_;
-};
-
-
-// A Queue of CommandMessage objects. A thread-safe version is
-// LockingCommandMessageQueue, based on this class.
-class CommandMessageQueue BASE_EMBEDDED {
- public:
- explicit CommandMessageQueue(int size);
- ~CommandMessageQueue();
- bool IsEmpty() const { return start_ == end_; }
- CommandMessage Get();
- void Put(const CommandMessage& message);
- void Clear() { start_ = end_ = 0; } // Queue is empty after Clear().
- private:
- // Doubles the size of the message queue, and copies the messages.
- void Expand();
-
- CommandMessage* messages_;
- int start_;
- int end_;
- int size_; // The size of the queue buffer. Queue can hold size-1 messages.
-};
-
-
-// LockingCommandMessageQueue is a thread-safe circular buffer of CommandMessage
-// messages. The message data is not managed by LockingCommandMessageQueue.
-// Pointers to the data are passed in and out. Implemented by adding a
-// Mutex to CommandMessageQueue. Includes logging of all puts and gets.
-class LockingCommandMessageQueue BASE_EMBEDDED {
- public:
- LockingCommandMessageQueue(Logger* logger, int size);
- bool IsEmpty() const;
- CommandMessage Get();
- void Put(const CommandMessage& message);
- void Clear();
- private:
- Logger* logger_;
- CommandMessageQueue queue_;
- mutable base::Mutex mutex_;
- DISALLOW_COPY_AND_ASSIGN(LockingCommandMessageQueue);
-};
-
-
-// This class contains the debugger support. The main purpose is to handle
-// setting break points in the code.
-//
-// This class controls the debug info for all functions which currently have
-// active breakpoints in them. This debug info is held in the heap root object
-// debug_info which is a FixedArray. Each entry in this list is of class
-// DebugInfo.
-class Debug {
- public:
- // Debug event triggers.
- void OnDebugBreak(Handle<Object> break_points_hit, bool auto_continue);
-
- void OnThrow(Handle<Object> exception);
- void OnPromiseReject(Handle<JSObject> promise, Handle<Object> value);
- void OnCompileError(Handle<Script> script);
- void OnBeforeCompile(Handle<Script> script);
- void OnAfterCompile(Handle<Script> script);
- void OnPromiseEvent(Handle<JSObject> data);
- void OnAsyncTaskEvent(Handle<JSObject> data);
-
- // API facing.
- void SetEventListener(Handle<Object> callback, Handle<Object> data);
- void SetMessageHandler(v8::Debug::MessageHandler handler);
- void EnqueueCommandMessage(Vector<const uint16_t> command,
- v8::Debug::ClientData* client_data = NULL);
- MUST_USE_RESULT MaybeHandle<Object> Call(Handle<JSFunction> fun,
- Handle<Object> data);
- Handle<Context> GetDebugContext();
- void HandleDebugBreak();
- void ProcessDebugMessages(bool debug_command_only);
-
- // Internal logic
- bool Load();
- void Break(Arguments args, JavaScriptFrame*);
- void SetAfterBreakTarget(JavaScriptFrame* frame);
-
- // Scripts handling.
- Handle<FixedArray> GetLoadedScripts();
-
- // Break point handling.
- bool SetBreakPoint(Handle<JSFunction> function,
- Handle<Object> break_point_object,
- int* source_position);
- bool SetBreakPointForScript(Handle<Script> script,
- Handle<Object> break_point_object,
- int* source_position,
- BreakPositionAlignment alignment);
- void ClearBreakPoint(Handle<Object> break_point_object);
- void ClearAllBreakPoints();
- void FloodWithOneShot(Handle<JSFunction> function,
- BreakLocatorType type = ALL_BREAK_LOCATIONS);
- void FloodBoundFunctionWithOneShot(Handle<JSFunction> function);
- void FloodDefaultConstructorWithOneShot(Handle<JSFunction> function);
- void FloodWithOneShotGeneric(Handle<JSFunction> function,
- Handle<Object> holder = Handle<Object>());
- void FloodHandlerWithOneShot();
- void ChangeBreakOnException(ExceptionBreakType type, bool enable);
- bool IsBreakOnException(ExceptionBreakType type);
-
- // Stepping handling.
- void PrepareStep(StepAction step_action,
- int step_count,
- StackFrame::Id frame_id);
- void ClearStepping();
- void ClearStepOut();
- bool IsStepping() { return thread_local_.step_count_ > 0; }
- bool StepNextContinue(BreakLocation* location, JavaScriptFrame* frame);
- bool StepInActive() { return thread_local_.step_into_fp_ != 0; }
- void HandleStepIn(Handle<Object> function_obj, bool is_constructor);
- bool StepOutActive() { return thread_local_.step_out_fp_ != 0; }
-
- void GetStepinPositions(JavaScriptFrame* frame, StackFrame::Id frame_id,
- List<int>* results_out);
-
- bool PrepareFunctionForBreakPoints(Handle<SharedFunctionInfo> shared);
-
- // Returns whether the operation succeeded. Compilation can only be triggered
- // if a valid closure is passed as the second argument, otherwise the shared
- // function needs to be compiled already.
- bool EnsureDebugInfo(Handle<SharedFunctionInfo> shared,
- Handle<JSFunction> function);
- static Handle<DebugInfo> GetDebugInfo(Handle<SharedFunctionInfo> shared);
-
- template <typename C>
- bool CompileToRevealInnerFunctions(C* compilable);
-
- // This function is used in FunctionNameUsing* tests.
- Handle<Object> FindSharedFunctionInfoInScript(Handle<Script> script,
- int position);
-
- // Returns true if the current stub call is patched to call the debugger.
- static bool IsDebugBreak(Address addr);
-
- static Handle<Object> GetSourceBreakLocations(
- Handle<SharedFunctionInfo> shared,
- BreakPositionAlignment position_aligment);
-
- // Check whether a global object is the debug global object.
- bool IsDebugGlobal(GlobalObject* global);
-
- // Check whether this frame is just about to return.
- bool IsBreakAtReturn(JavaScriptFrame* frame);
-
- // Support for LiveEdit
- void FramesHaveBeenDropped(StackFrame::Id new_break_frame_id,
- LiveEdit::FrameDropMode mode,
- Object** restarter_frame_function_pointer);
-
- // Threading support.
- char* ArchiveDebug(char* to);
- char* RestoreDebug(char* from);
- static int ArchiveSpacePerThread();
- void FreeThreadResources() { }
-
- // Record function from which eval was called.
- static void RecordEvalCaller(Handle<Script> script);
-
- bool CheckExecutionState(int id) {
- return is_active() && !debug_context().is_null() && break_id() != 0 &&
- break_id() == id;
- }
-
- // Flags and states.
- DebugScope* debugger_entry() {
- return reinterpret_cast<DebugScope*>(
- base::NoBarrier_Load(&thread_local_.current_debug_scope_));
- }
- inline Handle<Context> debug_context() { return debug_context_; }
- void set_live_edit_enabled(bool v) { live_edit_enabled_ = v; }
- bool live_edit_enabled() const {
- return FLAG_enable_liveedit && live_edit_enabled_ ;
- }
-
- inline bool is_active() const { return is_active_; }
- inline bool is_loaded() const { return !debug_context_.is_null(); }
- inline bool in_debug_scope() const {
- return !!base::NoBarrier_Load(&thread_local_.current_debug_scope_);
- }
- void set_disable_break(bool v) { break_disabled_ = v; }
-
- StackFrame::Id break_frame_id() { return thread_local_.break_frame_id_; }
- int break_id() { return thread_local_.break_id_; }
-
- // Support for embedding into generated code.
- Address is_active_address() {
- return reinterpret_cast<Address>(&is_active_);
- }
-
- Address after_break_target_address() {
- return reinterpret_cast<Address>(&after_break_target_);
- }
-
- Address restarter_frame_function_pointer_address() {
- Object*** address = &thread_local_.restarter_frame_function_pointer_;
- return reinterpret_cast<Address>(address);
- }
-
- Address step_in_fp_addr() {
- return reinterpret_cast<Address>(&thread_local_.step_into_fp_);
- }
-
- StepAction last_step_action() { return thread_local_.last_step_action_; }
-
- private:
- explicit Debug(Isolate* isolate);
-
- void UpdateState();
- void Unload();
- void SetNextBreakId() {
- thread_local_.break_id_ = ++thread_local_.break_count_;
- }
-
- // Check whether there are commands in the command queue.
- inline bool has_commands() const { return !command_queue_.IsEmpty(); }
- inline bool ignore_events() const { return is_suppressed_ || !is_active_; }
- inline bool break_disabled() const {
- return break_disabled_ || in_debug_event_listener_;
- }
-
- void OnException(Handle<Object> exception, Handle<Object> promise);
-
- // Constructors for debug event objects.
- MUST_USE_RESULT MaybeHandle<Object> MakeJSObject(
- const char* constructor_name,
- int argc,
- Handle<Object> argv[]);
- MUST_USE_RESULT MaybeHandle<Object> MakeExecutionState();
- MUST_USE_RESULT MaybeHandle<Object> MakeBreakEvent(
- Handle<Object> break_points_hit);
- MUST_USE_RESULT MaybeHandle<Object> MakeExceptionEvent(
- Handle<Object> exception,
- bool uncaught,
- Handle<Object> promise);
- MUST_USE_RESULT MaybeHandle<Object> MakeCompileEvent(
- Handle<Script> script, v8::DebugEvent type);
- MUST_USE_RESULT MaybeHandle<Object> MakePromiseEvent(
- Handle<JSObject> promise_event);
- MUST_USE_RESULT MaybeHandle<Object> MakeAsyncTaskEvent(
- Handle<JSObject> task_event);
-
- // Mirror cache handling.
- void ClearMirrorCache();
-
- MaybeHandle<Object> PromiseHasUserDefinedRejectHandler(
- Handle<JSObject> promise);
-
- void CallEventCallback(v8::DebugEvent event,
- Handle<Object> exec_state,
- Handle<Object> event_data,
- v8::Debug::ClientData* client_data);
- void ProcessCompileEventInDebugScope(v8::DebugEvent event,
- Handle<Script> script);
- void ProcessDebugEvent(v8::DebugEvent event,
- Handle<JSObject> event_data,
- bool auto_continue);
- void NotifyMessageHandler(v8::DebugEvent event,
- Handle<JSObject> exec_state,
- Handle<JSObject> event_data,
- bool auto_continue);
- void InvokeMessageHandler(MessageImpl message);
-
- static bool CompileDebuggerScript(Isolate* isolate, int index);
- void ClearOneShot();
- void ActivateStepIn(StackFrame* frame);
- void ClearStepIn();
- void ActivateStepOut(StackFrame* frame);
- void ClearStepNext();
- void RemoveDebugInfoAndClearFromShared(Handle<DebugInfo> debug_info);
- Handle<Object> CheckBreakPoints(Handle<Object> break_point);
- bool CheckBreakPoint(Handle<Object> break_point_object);
-
- inline void AssertDebugContext() {
- DCHECK(isolate_->context() == *debug_context());
- DCHECK(in_debug_scope());
- }
-
- void ThreadInit();
-
- // Global handles.
- Handle<Context> debug_context_;
- Handle<Object> event_listener_;
- Handle<Object> event_listener_data_;
-
- v8::Debug::MessageHandler message_handler_;
-
- static const int kQueueInitialSize = 4;
- base::Semaphore command_received_; // Signaled for each command received.
- LockingCommandMessageQueue command_queue_;
-
- bool is_active_;
- bool is_suppressed_;
- bool live_edit_enabled_;
- bool has_break_points_;
- bool break_disabled_;
- bool in_debug_event_listener_;
- bool break_on_exception_;
- bool break_on_uncaught_exception_;
-
- ScriptCache* script_cache_; // Cache of all scripts in the heap.
- DebugInfoListNode* debug_info_list_; // List of active debug info objects.
-
- // Storage location for jump when exiting debug break calls.
- // Note that this address is not GC safe. It should be computed immediately
- // before returning to the DebugBreakCallHelper.
- Address after_break_target_;
-
- // Per-thread data.
- class ThreadLocal {
- public:
- // Top debugger entry.
- base::AtomicWord current_debug_scope_;
-
- // Counter for generating next break id.
- int break_count_;
-
- // Current break id.
- int break_id_;
-
- // Frame id for the frame of the current break.
- StackFrame::Id break_frame_id_;
-
- // Step action for last step performed.
- StepAction last_step_action_;
-
- // Source statement position from last step next action.
- int last_statement_position_;
-
- // Number of steps left to perform before debug event.
- int step_count_;
-
- // Frame pointer from last step next or step frame action.
- Address last_fp_;
-
- // Number of queued steps left to perform before debug event.
- int queued_step_count_;
-
- // Frame pointer for frame from which step in was performed.
- Address step_into_fp_;
-
- // Frame pointer for the frame where debugger should be called when current
- // step out action is completed.
- Address step_out_fp_;
-
- // Stores the way how LiveEdit has patched the stack. It is used when
- // debugger returns control back to user script.
- LiveEdit::FrameDropMode frame_drop_mode_;
-
- // When restarter frame is on stack, stores the address
- // of the pointer to function being restarted. Otherwise (most of the time)
- // stores NULL. This pointer is used with 'step in' implementation.
- Object** restarter_frame_function_pointer_;
- };
-
- // Storage location for registers when handling debug break calls
- ThreadLocal thread_local_;
-
- Isolate* isolate_;
-
- friend class Isolate;
- friend class DebugScope;
- friend class DisableBreak;
- friend class LiveEdit;
- friend class SuppressDebug;
-
- friend Handle<FixedArray> GetDebuggedFunctions(); // In test-debug.cc
- friend void CheckDebuggerUnloaded(bool check_functions); // In test-debug.cc
-
- DISALLOW_COPY_AND_ASSIGN(Debug);
-};
-
-
-// This scope is used to load and enter the debug context and create a new
-// break state. Leaving the scope will restore the previous state.
-// On failure to load, FailedToEnter returns true.
-class DebugScope BASE_EMBEDDED {
- public:
- explicit DebugScope(Debug* debug);
- ~DebugScope();
-
- // Check whether loading was successful.
- inline bool failed() { return failed_; }
-
- // Get the active context from before entering the debugger.
- inline Handle<Context> GetContext() { return save_.context(); }
-
- private:
- Isolate* isolate() { return debug_->isolate_; }
-
- Debug* debug_;
- DebugScope* prev_; // Previous scope if entered recursively.
- StackFrame::Id break_frame_id_; // Previous break frame id.
- int break_id_; // Previous break id.
- bool failed_; // Did the debug context fail to load?
- SaveContext save_; // Saves previous context.
- PostponeInterruptsScope no_termination_exceptons_;
-};
-
-
-// Stack allocated class for disabling break.
-class DisableBreak BASE_EMBEDDED {
- public:
- explicit DisableBreak(Debug* debug, bool disable_break)
- : debug_(debug),
- previous_break_disabled_(debug->break_disabled_),
- previous_in_debug_event_listener_(debug->in_debug_event_listener_) {
- debug_->break_disabled_ = disable_break;
- debug_->in_debug_event_listener_ = disable_break;
- }
- ~DisableBreak() {
- debug_->break_disabled_ = previous_break_disabled_;
- debug_->in_debug_event_listener_ = previous_in_debug_event_listener_;
- }
-
- private:
- Debug* debug_;
- bool previous_break_disabled_;
- bool previous_in_debug_event_listener_;
- DISALLOW_COPY_AND_ASSIGN(DisableBreak);
-};
-
-
-class SuppressDebug BASE_EMBEDDED {
- public:
- explicit SuppressDebug(Debug* debug)
- : debug_(debug), old_state_(debug->is_suppressed_) {
- debug_->is_suppressed_ = true;
- }
- ~SuppressDebug() { debug_->is_suppressed_ = old_state_; }
-
- private:
- Debug* debug_;
- bool old_state_;
- DISALLOW_COPY_AND_ASSIGN(SuppressDebug);
-};
-
-
-// Code generator routines.
-class DebugCodegen : public AllStatic {
- public:
- enum DebugBreakCallHelperMode {
- SAVE_RESULT_REGISTER,
- IGNORE_RESULT_REGISTER
- };
-
- static void GenerateDebugBreakStub(MacroAssembler* masm,
- DebugBreakCallHelperMode mode);
-
- static void GeneratePlainReturnLiveEdit(MacroAssembler* masm);
-
- // FrameDropper is a code replacement for a JavaScript frame with possibly
- // several frames above.
- // There is no calling conventions here, because it never actually gets
- // called, it only gets returned to.
- static void GenerateFrameDropperLiveEdit(MacroAssembler* masm);
-
-
- static void GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode,
- int call_argc = -1);
-
- static void PatchDebugBreakSlot(Address pc, Handle<Code> code);
- static void ClearDebugBreakSlot(Address pc);
-};
-
-
-} } // namespace v8::internal
-
-#endif // V8_DEBUG_H_
--- /dev/null
+mvstanton@chromium.org
+ulan@chromium.org
+verwaest@chromium.org
+yangguo@chromium.org
--- /dev/null
+rmcilroy@chromium.org
--- /dev/null
+// Copyright 2012 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"
+
+#if V8_TARGET_ARCH_ARM
+
+#include "src/codegen.h"
+#include "src/debug/debug.h"
+
+namespace v8 {
+namespace internal {
+
+#define __ ACCESS_MASM(masm)
+
+
+void EmitDebugBreakSlot(MacroAssembler* masm) {
+ Label check_size;
+ __ bind(&check_size);
+ for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
+ __ nop(MacroAssembler::DEBUG_BREAK_NOP);
+ }
+ DCHECK_EQ(Assembler::kDebugBreakSlotInstructions,
+ masm->InstructionsGeneratedSince(&check_size));
+}
+
+
+void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode,
+ int call_argc) {
+ // Generate enough nop's to make space for a call instruction. Avoid emitting
+ // the constant pool in the debug break slot code.
+ Assembler::BlockConstPoolScope block_const_pool(masm);
+ masm->RecordDebugBreakSlot(mode, call_argc);
+ EmitDebugBreakSlot(masm);
+}
+
+
+void DebugCodegen::ClearDebugBreakSlot(Address pc) {
+ CodePatcher patcher(pc, Assembler::kDebugBreakSlotInstructions);
+ EmitDebugBreakSlot(patcher.masm());
+}
+
+
+void DebugCodegen::PatchDebugBreakSlot(Address pc, Handle<Code> code) {
+ DCHECK_EQ(Code::BUILTIN, code->kind());
+ CodePatcher patcher(pc, Assembler::kDebugBreakSlotInstructions);
+ // Patch the code changing the debug break slot code from
+ // mov r2, r2
+ // mov r2, r2
+ // mov r2, r2
+ // mov r2, r2
+ // to a call to the debug break slot code.
+ // ldr ip, [pc, #0]
+ // b skip
+ // <debug break slot code entry point address>
+ // skip:
+ // blx ip
+ Label skip_constant;
+ patcher.masm()->ldr(ip, MemOperand(v8::internal::pc, 0));
+ patcher.masm()->b(&skip_constant);
+ patcher.Emit(code->entry());
+ patcher.masm()->bind(&skip_constant);
+ patcher.masm()->blx(ip);
+}
+
+
+void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
+ DebugBreakCallHelperMode mode) {
+ __ RecordComment("Debug break");
+ {
+ FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
+
+ // Load padding words on stack.
+ __ mov(ip, Operand(Smi::FromInt(LiveEdit::kFramePaddingValue)));
+ for (int i = 0; i < LiveEdit::kFramePaddingInitialSize; i++) {
+ __ push(ip);
+ }
+ __ mov(ip, Operand(Smi::FromInt(LiveEdit::kFramePaddingInitialSize)));
+ __ push(ip);
+
+ if (mode == SAVE_RESULT_REGISTER) __ push(r0);
+
+ __ mov(r0, Operand::Zero()); // no arguments
+ __ mov(r1,
+ Operand(ExternalReference(
+ Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate())));
+
+ CEntryStub ceb(masm->isolate(), 1);
+ __ CallStub(&ceb);
+
+ if (FLAG_debug_code) {
+ for (int i = 0; i < kNumJSCallerSaved; i++) {
+ Register reg = {JSCallerSavedCode(i)};
+ __ mov(reg, Operand(kDebugZapValue));
+ }
+ }
+
+ if (mode == SAVE_RESULT_REGISTER) __ pop(r0);
+
+ // Don't bother removing padding bytes pushed on the stack
+ // as the frame is going to be restored right away.
+
+ // Leave the internal frame.
+ }
+
+ // Now that the break point has been handled, resume normal execution by
+ // jumping to the target address intended by the caller and that was
+ // overwritten by the address of DebugBreakXXX.
+ ExternalReference after_break_target =
+ ExternalReference::debug_after_break_target_address(masm->isolate());
+ __ mov(ip, Operand(after_break_target));
+ __ ldr(ip, MemOperand(ip));
+ __ Jump(ip);
+}
+
+
+void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
+ __ Ret();
+}
+
+
+void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
+ ExternalReference restarter_frame_function_slot =
+ ExternalReference::debug_restarter_frame_function_pointer_address(
+ masm->isolate());
+ __ mov(ip, Operand(restarter_frame_function_slot));
+ __ mov(r1, Operand::Zero());
+ __ str(r1, MemOperand(ip, 0));
+
+ // Load the function pointer off of our current stack frame.
+ __ ldr(r1, MemOperand(fp,
+ StandardFrameConstants::kConstantPoolOffset - kPointerSize));
+
+ // Pop return address, frame and constant pool pointer (if
+ // FLAG_enable_embedded_constant_pool).
+ __ LeaveFrame(StackFrame::INTERNAL);
+
+ { ConstantPoolUnavailableScope constant_pool_unavailable(masm);
+ // Load context from the function.
+ __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
+
+ // Get function code.
+ __ ldr(ip, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
+ __ ldr(ip, FieldMemOperand(ip, SharedFunctionInfo::kCodeOffset));
+ __ add(ip, ip, Operand(Code::kHeaderSize - kHeapObjectTag));
+
+ // Re-run JSFunction, r1 is function, cp is context.
+ __ Jump(ip);
+ }
+}
+
+
+const bool LiveEdit::kFrameDropperSupported = true;
+
+#undef __
+
+} // namespace internal
+} // namespace v8
+
+#endif // V8_TARGET_ARCH_ARM
--- /dev/null
+rmcilroy@chromium.org
--- /dev/null
+// Copyright 2013 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"
+
+#if V8_TARGET_ARCH_ARM64
+
+#include "src/codegen.h"
+#include "src/debug/debug.h"
+
+namespace v8 {
+namespace internal {
+
+#define __ ACCESS_MASM(masm)
+
+
+void EmitDebugBreakSlot(Assembler* masm) {
+ Label check_size;
+ __ bind(&check_size);
+ for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
+ __ nop(Assembler::DEBUG_BREAK_NOP);
+ }
+ DCHECK_EQ(Assembler::kDebugBreakSlotInstructions,
+ static_cast<int>(masm->InstructionsGeneratedSince(&check_size)));
+}
+
+
+void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode,
+ int call_argc) {
+ // Generate enough nop's to make space for a call instruction. Avoid emitting
+ // the constant pool in the debug break slot code.
+ InstructionAccurateScope scope(masm, Assembler::kDebugBreakSlotInstructions);
+ masm->RecordDebugBreakSlot(mode, call_argc);
+ EmitDebugBreakSlot(masm);
+}
+
+
+void DebugCodegen::ClearDebugBreakSlot(Address pc) {
+ PatchingAssembler patcher(reinterpret_cast<Instruction*>(pc),
+ Assembler::kDebugBreakSlotInstructions);
+ EmitDebugBreakSlot(&patcher);
+}
+
+
+void DebugCodegen::PatchDebugBreakSlot(Address pc, Handle<Code> code) {
+ DCHECK_EQ(Code::BUILTIN, code->kind());
+ PatchingAssembler patcher(reinterpret_cast<Instruction*>(pc),
+ Assembler::kDebugBreakSlotInstructions);
+ // Patch the code emitted by DebugCodegen::GenerateSlots, changing the debug
+ // break slot code from
+ // mov x0, x0 @ nop DEBUG_BREAK_NOP
+ // mov x0, x0 @ nop DEBUG_BREAK_NOP
+ // mov x0, x0 @ nop DEBUG_BREAK_NOP
+ // mov x0, x0 @ nop DEBUG_BREAK_NOP
+ // mov x0, x0 @ nop DEBUG_BREAK_NOP
+ // to a call to the debug slot code.
+ // ldr ip0, [pc, #(2 * kInstructionSize)]
+ // blr ip0
+ // b skip
+ // <debug break slot code entry point address (64 bits)>
+ // skip:
+
+ Label skip_constant;
+ // The first instruction of a patched debug break slot must be a load literal
+ // loading the address of the debug break slot code.
+ patcher.ldr_pcrel(ip0, (2 * kInstructionSize) >> kLoadLiteralScaleLog2);
+ patcher.b(&skip_constant);
+ patcher.dc64(reinterpret_cast<int64_t>(code->entry()));
+ patcher.bind(&skip_constant);
+ // TODO(all): check the following is correct.
+ // The debug break slot code will push a frame and call statically compiled
+ // code. By using blr, this call site will be registered in the frame.
+ // The debugger can now iterate on the frames to find this call.
+ patcher.blr(ip0);
+}
+
+
+void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
+ DebugBreakCallHelperMode mode) {
+ __ RecordComment("Debug break");
+ Register scratch = x10;
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // Load padding words on stack.
+ __ Mov(scratch, Smi::FromInt(LiveEdit::kFramePaddingValue));
+ __ PushMultipleTimes(scratch, LiveEdit::kFramePaddingInitialSize);
+ __ Mov(scratch, Smi::FromInt(LiveEdit::kFramePaddingInitialSize));
+ __ Push(scratch);
+
+ if (mode == SAVE_RESULT_REGISTER) __ Push(x0);
+
+ __ Mov(x0, 0); // No arguments.
+ __ Mov(x1, ExternalReference(Runtime::FunctionForId(Runtime::kDebugBreak),
+ masm->isolate()));
+
+ CEntryStub stub(masm->isolate(), 1);
+ __ CallStub(&stub);
+
+ if (FLAG_debug_code) {
+ for (int i = 0; i < kNumJSCallerSaved; i++) {
+ Register reg = Register::XRegFromCode(JSCallerSavedCode(i));
+ __ Mov(reg, Operand(kDebugZapValue));
+ }
+ }
+
+ // Restore the register values from the expression stack.
+ if (mode == SAVE_RESULT_REGISTER) __ Pop(x0);
+
+ // Don't bother removing padding bytes pushed on the stack
+ // as the frame is going to be restored right away.
+
+ // Leave the internal frame.
+ }
+
+ // Now that the break point has been handled, resume normal execution by
+ // jumping to the target address intended by the caller and that was
+ // overwritten by the address of DebugBreakXXX.
+ ExternalReference after_break_target =
+ ExternalReference::debug_after_break_target_address(masm->isolate());
+ __ Mov(scratch, after_break_target);
+ __ Ldr(scratch, MemOperand(scratch));
+ __ Br(scratch);
+}
+
+
+void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
+ __ Ret();
+}
+
+
+void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
+ ExternalReference restarter_frame_function_slot =
+ ExternalReference::debug_restarter_frame_function_pointer_address(
+ masm->isolate());
+ UseScratchRegisterScope temps(masm);
+ Register scratch = temps.AcquireX();
+
+ __ Mov(scratch, restarter_frame_function_slot);
+ __ Str(xzr, MemOperand(scratch));
+
+ // We do not know our frame height, but set sp based on fp.
+ __ Sub(masm->StackPointer(), fp, kPointerSize);
+ __ AssertStackConsistency();
+
+ __ Pop(x1, fp, lr); // Function, Frame, Return address.
+
+ // Load context from the function.
+ __ Ldr(cp, FieldMemOperand(x1, JSFunction::kContextOffset));
+
+ // Get function code.
+ __ Ldr(scratch, FieldMemOperand(x1, JSFunction::kSharedFunctionInfoOffset));
+ __ Ldr(scratch, FieldMemOperand(scratch, SharedFunctionInfo::kCodeOffset));
+ __ Add(scratch, scratch, Code::kHeaderSize - kHeapObjectTag);
+
+ // Re-run JSFunction, x1 is function, cp is context.
+ __ Br(scratch);
+}
+
+
+const bool LiveEdit::kFrameDropperSupported = true;
+
+} // namespace internal
+} // namespace v8
+
+#endif // V8_TARGET_ARCH_ARM64
--- /dev/null
+// Copyright 2012 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/debug/debug.h"
+
+#include "src/api.h"
+#include "src/arguments.h"
+#include "src/bootstrapper.h"
+#include "src/code-stubs.h"
+#include "src/codegen.h"
+#include "src/compilation-cache.h"
+#include "src/compiler.h"
+#include "src/deoptimizer.h"
+#include "src/execution.h"
+#include "src/full-codegen/full-codegen.h"
+#include "src/global-handles.h"
+#include "src/list.h"
+#include "src/log.h"
+#include "src/messages.h"
+#include "src/snapshot/natives.h"
+
+#include "include/v8-debug.h"
+
+namespace v8 {
+namespace internal {
+
+Debug::Debug(Isolate* isolate)
+ : debug_context_(Handle<Context>()),
+ event_listener_(Handle<Object>()),
+ event_listener_data_(Handle<Object>()),
+ message_handler_(NULL),
+ command_received_(0),
+ command_queue_(isolate->logger(), kQueueInitialSize),
+ is_active_(false),
+ is_suppressed_(false),
+ live_edit_enabled_(true), // TODO(yangguo): set to false by default.
+ break_disabled_(false),
+ in_debug_event_listener_(false),
+ break_on_exception_(false),
+ break_on_uncaught_exception_(false),
+ script_cache_(NULL),
+ debug_info_list_(NULL),
+ isolate_(isolate) {
+ ThreadInit();
+}
+
+
+static v8::Local<v8::Context> GetDebugEventContext(Isolate* isolate) {
+ Handle<Context> context = isolate->debug()->debugger_entry()->GetContext();
+ // Isolate::context() may have been NULL when "script collected" event
+ // occured.
+ if (context.is_null()) return v8::Local<v8::Context>();
+ Handle<Context> native_context(context->native_context());
+ return v8::Utils::ToLocal(native_context);
+}
+
+
+BreakLocation::BreakLocation(Handle<DebugInfo> debug_info, RelocInfo* rinfo,
+ int position, int statement_position)
+ : debug_info_(debug_info),
+ pc_offset_(static_cast<int>(rinfo->pc() - debug_info->code()->entry())),
+ rmode_(rinfo->rmode()),
+ data_(rinfo->data()),
+ position_(position),
+ statement_position_(statement_position) {}
+
+
+BreakLocation::Iterator::Iterator(Handle<DebugInfo> debug_info,
+ BreakLocatorType type)
+ : debug_info_(debug_info),
+ reloc_iterator_(debug_info->code(), GetModeMask(type)),
+ break_index_(-1),
+ position_(1),
+ statement_position_(1) {
+ if (!Done()) Next();
+}
+
+
+int BreakLocation::Iterator::GetModeMask(BreakLocatorType type) {
+ int mask = 0;
+ mask |= RelocInfo::ModeMask(RelocInfo::POSITION);
+ mask |= RelocInfo::ModeMask(RelocInfo::STATEMENT_POSITION);
+ mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_RETURN);
+ mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_CALL);
+ mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_CONSTRUCT_CALL);
+ if (type == ALL_BREAK_LOCATIONS) {
+ mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_POSITION);
+ mask |= RelocInfo::ModeMask(RelocInfo::DEBUGGER_STATEMENT);
+ }
+ return mask;
+}
+
+
+void BreakLocation::Iterator::Next() {
+ DisallowHeapAllocation no_gc;
+ DCHECK(!Done());
+
+ // Iterate through reloc info for code and original code stopping at each
+ // breakable code target.
+ bool first = break_index_ == -1;
+ while (!Done()) {
+ if (!first) reloc_iterator_.next();
+ first = false;
+ if (Done()) return;
+
+ // Whenever a statement position or (plain) position is passed update the
+ // current value of these.
+ if (RelocInfo::IsPosition(rmode())) {
+ if (RelocInfo::IsStatementPosition(rmode())) {
+ statement_position_ = static_cast<int>(
+ rinfo()->data() - debug_info_->shared()->start_position());
+ }
+ // Always update the position as we don't want that to be before the
+ // statement position.
+ position_ = static_cast<int>(rinfo()->data() -
+ debug_info_->shared()->start_position());
+ DCHECK(position_ >= 0);
+ DCHECK(statement_position_ >= 0);
+ continue;
+ }
+
+ DCHECK(RelocInfo::IsDebugBreakSlot(rmode()) ||
+ RelocInfo::IsDebuggerStatement(rmode()));
+
+ if (RelocInfo::IsDebugBreakSlotAtReturn(rmode())) {
+ // Set the positions to the end of the function.
+ if (debug_info_->shared()->HasSourceCode()) {
+ position_ = debug_info_->shared()->end_position() -
+ debug_info_->shared()->start_position() - 1;
+ } else {
+ position_ = 0;
+ }
+ statement_position_ = position_;
+ }
+
+ break;
+ }
+ break_index_++;
+}
+
+
+// Find the break point at the supplied address, or the closest one before
+// the address.
+BreakLocation BreakLocation::FromAddress(Handle<DebugInfo> debug_info,
+ BreakLocatorType type, Address pc) {
+ Iterator it(debug_info, type);
+ it.SkipTo(BreakIndexFromAddress(debug_info, type, pc));
+ return it.GetBreakLocation();
+}
+
+
+// Find the break point at the supplied address, or the closest one before
+// the address.
+void BreakLocation::FromAddressSameStatement(Handle<DebugInfo> debug_info,
+ BreakLocatorType type, Address pc,
+ List<BreakLocation>* result_out) {
+ int break_index = BreakIndexFromAddress(debug_info, type, pc);
+ Iterator it(debug_info, type);
+ it.SkipTo(break_index);
+ int statement_position = it.statement_position();
+ while (!it.Done() && it.statement_position() == statement_position) {
+ result_out->Add(it.GetBreakLocation());
+ it.Next();
+ }
+}
+
+
+int BreakLocation::BreakIndexFromAddress(Handle<DebugInfo> debug_info,
+ BreakLocatorType type, Address pc) {
+ // Run through all break points to locate the one closest to the address.
+ int closest_break = 0;
+ int distance = kMaxInt;
+ for (Iterator it(debug_info, type); !it.Done(); it.Next()) {
+ // Check if this break point is closer that what was previously found.
+ if (it.pc() <= pc && pc - it.pc() < distance) {
+ closest_break = it.break_index();
+ distance = static_cast<int>(pc - it.pc());
+ // Check whether we can't get any closer.
+ if (distance == 0) break;
+ }
+ }
+ return closest_break;
+}
+
+
+BreakLocation BreakLocation::FromPosition(Handle<DebugInfo> debug_info,
+ BreakLocatorType type, int position,
+ BreakPositionAlignment alignment) {
+ // Run through all break points to locate the one closest to the source
+ // position.
+ int closest_break = 0;
+ int distance = kMaxInt;
+
+ for (Iterator it(debug_info, type); !it.Done(); it.Next()) {
+ int next_position;
+ if (alignment == STATEMENT_ALIGNED) {
+ next_position = it.statement_position();
+ } else {
+ DCHECK(alignment == BREAK_POSITION_ALIGNED);
+ next_position = it.position();
+ }
+ if (position <= next_position && next_position - position < distance) {
+ closest_break = it.break_index();
+ distance = next_position - position;
+ // Check whether we can't get any closer.
+ if (distance == 0) break;
+ }
+ }
+
+ Iterator it(debug_info, type);
+ it.SkipTo(closest_break);
+ return it.GetBreakLocation();
+}
+
+
+void BreakLocation::SetBreakPoint(Handle<Object> break_point_object) {
+ // If there is not already a real break point here patch code with debug
+ // break.
+ if (!HasBreakPoint()) SetDebugBreak();
+ DCHECK(IsDebugBreak() || IsDebuggerStatement());
+ // Set the break point information.
+ DebugInfo::SetBreakPoint(debug_info_, pc_offset_, position_,
+ statement_position_, break_point_object);
+}
+
+
+void BreakLocation::ClearBreakPoint(Handle<Object> break_point_object) {
+ // Clear the break point information.
+ DebugInfo::ClearBreakPoint(debug_info_, pc_offset_, break_point_object);
+ // If there are no more break points here remove the debug break.
+ if (!HasBreakPoint()) {
+ ClearDebugBreak();
+ DCHECK(!IsDebugBreak());
+ }
+}
+
+
+void BreakLocation::SetOneShot() {
+ // Debugger statement always calls debugger. No need to modify it.
+ if (IsDebuggerStatement()) return;
+
+ // If there is a real break point here no more to do.
+ if (HasBreakPoint()) {
+ DCHECK(IsDebugBreak());
+ return;
+ }
+
+ // Patch code with debug break.
+ SetDebugBreak();
+}
+
+
+void BreakLocation::ClearOneShot() {
+ // Debugger statement always calls debugger. No need to modify it.
+ if (IsDebuggerStatement()) return;
+
+ // If there is a real break point here no more to do.
+ if (HasBreakPoint()) {
+ DCHECK(IsDebugBreak());
+ return;
+ }
+
+ // Patch code removing debug break.
+ ClearDebugBreak();
+ DCHECK(!IsDebugBreak());
+}
+
+
+void BreakLocation::SetDebugBreak() {
+ // Debugger statement always calls debugger. No need to modify it.
+ if (IsDebuggerStatement()) return;
+
+ // If there is already a break point here just return. This might happen if
+ // the same code is flooded with break points twice. Flooding the same
+ // function twice might happen when stepping in a function with an exception
+ // handler as the handler and the function is the same.
+ if (IsDebugBreak()) return;
+
+ DCHECK(IsDebugBreakSlot());
+ Builtins* builtins = debug_info_->GetIsolate()->builtins();
+ Handle<Code> target =
+ IsReturn() ? builtins->Return_DebugBreak() : builtins->Slot_DebugBreak();
+ DebugCodegen::PatchDebugBreakSlot(pc(), target);
+ DCHECK(IsDebugBreak());
+}
+
+
+void BreakLocation::ClearDebugBreak() {
+ // Debugger statement always calls debugger. No need to modify it.
+ if (IsDebuggerStatement()) return;
+
+ DCHECK(IsDebugBreakSlot());
+ DebugCodegen::ClearDebugBreakSlot(pc());
+ DCHECK(!IsDebugBreak());
+}
+
+
+bool BreakLocation::IsStepInLocation() const {
+ return IsConstructCall() || IsCall();
+}
+
+
+bool BreakLocation::IsDebugBreak() const {
+ if (IsDebugBreakSlot()) {
+ return rinfo().IsPatchedDebugBreakSlotSequence();
+ }
+ return false;
+}
+
+
+Handle<Object> BreakLocation::BreakPointObjects() const {
+ return debug_info_->GetBreakPointObjects(pc_offset_);
+}
+
+
+// Threading support.
+void Debug::ThreadInit() {
+ thread_local_.break_count_ = 0;
+ thread_local_.break_id_ = 0;
+ thread_local_.break_frame_id_ = StackFrame::NO_ID;
+ thread_local_.last_step_action_ = StepNone;
+ thread_local_.last_statement_position_ = RelocInfo::kNoPosition;
+ thread_local_.step_count_ = 0;
+ thread_local_.last_fp_ = 0;
+ thread_local_.queued_step_count_ = 0;
+ thread_local_.step_into_fp_ = 0;
+ thread_local_.step_out_fp_ = 0;
+ // TODO(isolates): frames_are_dropped_?
+ base::NoBarrier_Store(&thread_local_.current_debug_scope_,
+ static_cast<base::AtomicWord>(0));
+ thread_local_.restarter_frame_function_pointer_ = NULL;
+}
+
+
+char* Debug::ArchiveDebug(char* storage) {
+ char* to = storage;
+ MemCopy(to, reinterpret_cast<char*>(&thread_local_), sizeof(ThreadLocal));
+ ThreadInit();
+ return storage + ArchiveSpacePerThread();
+}
+
+
+char* Debug::RestoreDebug(char* storage) {
+ char* from = storage;
+ MemCopy(reinterpret_cast<char*>(&thread_local_), from, sizeof(ThreadLocal));
+ return storage + ArchiveSpacePerThread();
+}
+
+
+int Debug::ArchiveSpacePerThread() {
+ return sizeof(ThreadLocal);
+}
+
+
+ScriptCache::ScriptCache(Isolate* isolate) : isolate_(isolate) {
+ Heap* heap = isolate_->heap();
+ HandleScope scope(isolate_);
+
+ DCHECK(isolate_->debug()->is_active());
+
+ // Perform a GC to get rid of all unreferenced scripts.
+ heap->CollectAllGarbage(Heap::kMakeHeapIterableMask, "ScriptCache");
+
+ // Scan heap for Script objects.
+ List<Handle<Script> > scripts;
+ {
+ HeapIterator iterator(heap, HeapIterator::kFilterUnreachable);
+ DisallowHeapAllocation no_allocation;
+ for (HeapObject* obj = iterator.next(); obj != NULL;
+ obj = iterator.next()) {
+ if (obj->IsScript() && Script::cast(obj)->HasValidSource()) {
+ scripts.Add(Handle<Script>(Script::cast(obj)));
+ }
+ }
+ }
+
+ GlobalHandles* global_handles = isolate_->global_handles();
+ table_ = Handle<WeakValueHashTable>::cast(global_handles->Create(
+ Object::cast(*WeakValueHashTable::New(isolate_, scripts.length()))));
+ for (int i = 0; i < scripts.length(); i++) Add(scripts[i]);
+}
+
+
+void ScriptCache::Add(Handle<Script> script) {
+ HandleScope scope(isolate_);
+ Handle<Smi> id(script->id(), isolate_);
+
+#ifdef DEBUG
+ Handle<Object> lookup(table_->LookupWeak(id), isolate_);
+ if (!lookup->IsTheHole()) {
+ Handle<Script> found = Handle<Script>::cast(lookup);
+ DCHECK(script->id() == found->id());
+ DCHECK(!script->name()->IsString() ||
+ String::cast(script->name())->Equals(String::cast(found->name())));
+ }
+#endif
+
+ Handle<WeakValueHashTable> new_table =
+ WeakValueHashTable::PutWeak(table_, id, script);
+
+ if (new_table.is_identical_to(table_)) return;
+ GlobalHandles* global_handles = isolate_->global_handles();
+ global_handles->Destroy(Handle<Object>::cast(table_).location());
+ table_ = Handle<WeakValueHashTable>::cast(
+ global_handles->Create(Object::cast(*new_table)));
+}
+
+
+ScriptCache::~ScriptCache() {
+ isolate_->global_handles()->Destroy(Handle<Object>::cast(table_).location());
+ table_ = Handle<WeakValueHashTable>();
+}
+
+
+DebugInfoListNode::DebugInfoListNode(DebugInfo* debug_info): next_(NULL) {
+ // Globalize the request debug info object and make it weak.
+ GlobalHandles* global_handles = debug_info->GetIsolate()->global_handles();
+ debug_info_ =
+ Handle<DebugInfo>::cast(global_handles->Create(debug_info)).location();
+}
+
+
+DebugInfoListNode::~DebugInfoListNode() {
+ if (debug_info_ == nullptr) return;
+ GlobalHandles::Destroy(reinterpret_cast<Object**>(debug_info_));
+ debug_info_ = nullptr;
+}
+
+
+bool Debug::CompileDebuggerScript(Isolate* isolate, int index) {
+ Factory* factory = isolate->factory();
+ HandleScope scope(isolate);
+
+ // Bail out if the index is invalid.
+ if (index == -1) return false;
+
+ // Find source and name for the requested script.
+ Handle<String> source_code =
+ isolate->bootstrapper()->SourceLookup<Natives>(index);
+ Vector<const char> name = Natives::GetScriptName(index);
+ Handle<String> script_name =
+ factory->NewStringFromAscii(name).ToHandleChecked();
+ Handle<Context> context = isolate->native_context();
+
+ // Compile the script.
+ Handle<SharedFunctionInfo> function_info;
+ function_info = Compiler::CompileScript(
+ source_code, script_name, 0, 0, ScriptOriginOptions(), Handle<Object>(),
+ context, NULL, NULL, ScriptCompiler::kNoCompileOptions, NATIVES_CODE,
+ false);
+ if (function_info.is_null()) return false;
+
+ // Execute the shared function in the debugger context.
+ Handle<JSFunction> function =
+ factory->NewFunctionFromSharedFunctionInfo(function_info, context);
+
+ MaybeHandle<Object> maybe_exception;
+ MaybeHandle<Object> result = Execution::TryCall(
+ function, handle(context->global_proxy()), 0, NULL, &maybe_exception);
+
+ // Check for caught exceptions.
+ if (result.is_null()) {
+ DCHECK(!isolate->has_pending_exception());
+ MessageLocation computed_location;
+ isolate->ComputeLocation(&computed_location);
+ Handle<JSMessageObject> message = MessageHandler::MakeMessageObject(
+ isolate, MessageTemplate::kDebuggerLoading, &computed_location,
+ isolate->factory()->undefined_value(), Handle<JSArray>());
+ DCHECK(!isolate->has_pending_exception());
+ Handle<Object> exception;
+ if (maybe_exception.ToHandle(&exception)) {
+ isolate->set_pending_exception(*exception);
+ MessageHandler::ReportMessage(isolate, NULL, message);
+ }
+ DCHECK(!maybe_exception.is_null());
+ return false;
+ }
+
+ // Mark this script as native and return successfully.
+ Handle<Script> script(Script::cast(function->shared()->script()));
+ script->set_type(Smi::FromInt(Script::TYPE_NATIVE));
+ return true;
+}
+
+
+bool Debug::Load() {
+ // Return if debugger is already loaded.
+ if (is_loaded()) return true;
+
+ // Bail out if we're already in the process of compiling the native
+ // JavaScript source code for the debugger.
+ if (is_suppressed_) return false;
+ SuppressDebug while_loading(this);
+
+ // Disable breakpoints and interrupts while compiling and running the
+ // debugger scripts including the context creation code.
+ DisableBreak disable(this, true);
+ PostponeInterruptsScope postpone(isolate_);
+
+ // Create the debugger context.
+ HandleScope scope(isolate_);
+ ExtensionConfiguration no_extensions;
+ Handle<Context> context = isolate_->bootstrapper()->CreateEnvironment(
+ MaybeHandle<JSGlobalProxy>(), v8::Local<ObjectTemplate>(),
+ &no_extensions);
+
+ // Fail if no context could be created.
+ if (context.is_null()) return false;
+
+ // Use the debugger context.
+ SaveContext save(isolate_);
+ isolate_->set_context(*context);
+
+ // Expose the builtins object in the debugger context.
+ Handle<String> key = isolate_->factory()->InternalizeOneByteString(
+ STATIC_CHAR_VECTOR("builtins"));
+ Handle<GlobalObject> global =
+ Handle<GlobalObject>(context->global_object(), isolate_);
+ Handle<JSBuiltinsObject> builtin =
+ Handle<JSBuiltinsObject>(global->builtins(), isolate_);
+ RETURN_ON_EXCEPTION_VALUE(
+ isolate_, Object::SetProperty(global, key, builtin, SLOPPY), false);
+
+ // Compile the JavaScript for the debugger in the debugger context.
+ bool caught_exception =
+ !CompileDebuggerScript(isolate_, Natives::GetIndex("mirrors")) ||
+ !CompileDebuggerScript(isolate_, Natives::GetIndex("debug"));
+
+ if (FLAG_enable_liveedit) {
+ caught_exception = caught_exception ||
+ !CompileDebuggerScript(isolate_, Natives::GetIndex("liveedit"));
+ }
+ // Check for caught exceptions.
+ if (caught_exception) return false;
+
+ debug_context_ = Handle<Context>::cast(
+ isolate_->global_handles()->Create(*context));
+ return true;
+}
+
+
+void Debug::Unload() {
+ ClearAllBreakPoints();
+ ClearStepping();
+
+ // Return debugger is not loaded.
+ if (!is_loaded()) return;
+
+ // Clear the script cache.
+ if (script_cache_ != NULL) {
+ delete script_cache_;
+ script_cache_ = NULL;
+ }
+
+ // Clear debugger context global handle.
+ GlobalHandles::Destroy(Handle<Object>::cast(debug_context_).location());
+ debug_context_ = Handle<Context>();
+}
+
+
+void Debug::Break(Arguments args, JavaScriptFrame* frame) {
+ Heap* heap = isolate_->heap();
+ HandleScope scope(isolate_);
+ DCHECK(args.length() == 0);
+
+ // Initialize LiveEdit.
+ LiveEdit::InitializeThreadLocal(this);
+
+ // Just continue if breaks are disabled or debugger cannot be loaded.
+ if (break_disabled()) return;
+
+ // Enter the debugger.
+ DebugScope debug_scope(this);
+ if (debug_scope.failed()) return;
+
+ // Postpone interrupt during breakpoint processing.
+ PostponeInterruptsScope postpone(isolate_);
+
+ // Get the debug info (create it if it does not exist).
+ Handle<SharedFunctionInfo> shared =
+ Handle<SharedFunctionInfo>(frame->function()->shared());
+ Handle<DebugInfo> debug_info(shared->GetDebugInfo());
+
+ // Find the break point where execution has stopped.
+ // PC points to the instruction after the current one, possibly a break
+ // location as well. So the "- 1" to exclude it from the search.
+ Address call_pc = frame->pc() - 1;
+ BreakLocation break_location =
+ BreakLocation::FromAddress(debug_info, ALL_BREAK_LOCATIONS, call_pc);
+
+ // Check whether step next reached a new statement.
+ if (!StepNextContinue(&break_location, frame)) {
+ // Decrease steps left if performing multiple steps.
+ if (thread_local_.step_count_ > 0) {
+ thread_local_.step_count_--;
+ }
+ }
+
+ // If there is one or more real break points check whether any of these are
+ // triggered.
+ Handle<Object> break_points_hit(heap->undefined_value(), isolate_);
+ if (break_location.HasBreakPoint()) {
+ Handle<Object> break_point_objects = break_location.BreakPointObjects();
+ break_points_hit = CheckBreakPoints(break_point_objects);
+ }
+
+ // If step out is active skip everything until the frame where we need to step
+ // out to is reached, unless real breakpoint is hit.
+ if (StepOutActive() &&
+ frame->fp() != thread_local_.step_out_fp_ &&
+ break_points_hit->IsUndefined() ) {
+ // Step count should always be 0 for StepOut.
+ DCHECK(thread_local_.step_count_ == 0);
+ } else if (!break_points_hit->IsUndefined() ||
+ (thread_local_.last_step_action_ != StepNone &&
+ thread_local_.step_count_ == 0)) {
+ // Notify debugger if a real break point is triggered or if performing
+ // single stepping with no more steps to perform. Otherwise do another step.
+
+ // Clear all current stepping setup.
+ ClearStepping();
+
+ if (thread_local_.queued_step_count_ > 0) {
+ // Perform queued steps
+ int step_count = thread_local_.queued_step_count_;
+
+ // Clear queue
+ thread_local_.queued_step_count_ = 0;
+
+ PrepareStep(StepNext, step_count, StackFrame::NO_ID);
+ } else {
+ // Notify the debug event listeners.
+ OnDebugBreak(break_points_hit, false);
+ }
+ } else if (thread_local_.last_step_action_ != StepNone) {
+ // Hold on to last step action as it is cleared by the call to
+ // ClearStepping.
+ StepAction step_action = thread_local_.last_step_action_;
+ int step_count = thread_local_.step_count_;
+
+ // If StepNext goes deeper in code, StepOut until original frame
+ // and keep step count queued up in the meantime.
+ if (step_action == StepNext && frame->fp() < thread_local_.last_fp_) {
+ // Count frames until target frame
+ int count = 0;
+ JavaScriptFrameIterator it(isolate_);
+ while (!it.done() && it.frame()->fp() < thread_local_.last_fp_) {
+ count++;
+ it.Advance();
+ }
+
+ // Check that we indeed found the frame we are looking for.
+ CHECK(!it.done() && (it.frame()->fp() == thread_local_.last_fp_));
+ if (step_count > 1) {
+ // Save old count and action to continue stepping after StepOut.
+ thread_local_.queued_step_count_ = step_count - 1;
+ }
+
+ // Set up for StepOut to reach target frame.
+ step_action = StepOut;
+ step_count = count;
+ }
+
+ // Clear all current stepping setup.
+ ClearStepping();
+
+ // Set up for the remaining steps.
+ PrepareStep(step_action, step_count, StackFrame::NO_ID);
+ }
+}
+
+
+// Check the break point objects for whether one or more are actually
+// triggered. This function returns a JSArray with the break point objects
+// which is triggered.
+Handle<Object> Debug::CheckBreakPoints(Handle<Object> break_point_objects) {
+ Factory* factory = isolate_->factory();
+
+ // Count the number of break points hit. If there are multiple break points
+ // they are in a FixedArray.
+ Handle<FixedArray> break_points_hit;
+ int break_points_hit_count = 0;
+ DCHECK(!break_point_objects->IsUndefined());
+ if (break_point_objects->IsFixedArray()) {
+ Handle<FixedArray> array(FixedArray::cast(*break_point_objects));
+ break_points_hit = factory->NewFixedArray(array->length());
+ for (int i = 0; i < array->length(); i++) {
+ Handle<Object> o(array->get(i), isolate_);
+ if (CheckBreakPoint(o)) {
+ break_points_hit->set(break_points_hit_count++, *o);
+ }
+ }
+ } else {
+ break_points_hit = factory->NewFixedArray(1);
+ if (CheckBreakPoint(break_point_objects)) {
+ break_points_hit->set(break_points_hit_count++, *break_point_objects);
+ }
+ }
+
+ // Return undefined if no break points were triggered.
+ if (break_points_hit_count == 0) {
+ return factory->undefined_value();
+ }
+ // Return break points hit as a JSArray.
+ Handle<JSArray> result = factory->NewJSArrayWithElements(break_points_hit);
+ result->set_length(Smi::FromInt(break_points_hit_count));
+ return result;
+}
+
+
+// Check whether a single break point object is triggered.
+bool Debug::CheckBreakPoint(Handle<Object> break_point_object) {
+ Factory* factory = isolate_->factory();
+ HandleScope scope(isolate_);
+
+ // Ignore check if break point object is not a JSObject.
+ if (!break_point_object->IsJSObject()) return true;
+
+ // Get the function IsBreakPointTriggered (defined in debug.js).
+ Handle<String> is_break_point_triggered_string =
+ factory->InternalizeOneByteString(
+ STATIC_CHAR_VECTOR("IsBreakPointTriggered"));
+ Handle<GlobalObject> debug_global(debug_context()->global_object());
+ Handle<JSFunction> check_break_point =
+ Handle<JSFunction>::cast(Object::GetProperty(
+ debug_global, is_break_point_triggered_string).ToHandleChecked());
+
+ // Get the break id as an object.
+ Handle<Object> break_id = factory->NewNumberFromInt(Debug::break_id());
+
+ // Call HandleBreakPointx.
+ Handle<Object> argv[] = { break_id, break_point_object };
+ Handle<Object> result;
+ if (!Execution::TryCall(check_break_point,
+ isolate_->js_builtins_object(),
+ arraysize(argv),
+ argv).ToHandle(&result)) {
+ return false;
+ }
+
+ // Return whether the break point is triggered.
+ return result->IsTrue();
+}
+
+
+bool Debug::SetBreakPoint(Handle<JSFunction> function,
+ Handle<Object> break_point_object,
+ int* source_position) {
+ HandleScope scope(isolate_);
+
+ // Make sure the function is compiled and has set up the debug info.
+ Handle<SharedFunctionInfo> shared(function->shared());
+ if (!EnsureDebugInfo(shared, function)) {
+ // Return if retrieving debug info failed.
+ return true;
+ }
+
+ Handle<DebugInfo> debug_info(shared->GetDebugInfo());
+ // Source positions starts with zero.
+ DCHECK(*source_position >= 0);
+
+ // Find the break point and change it.
+ BreakLocation location = BreakLocation::FromPosition(
+ debug_info, ALL_BREAK_LOCATIONS, *source_position, STATEMENT_ALIGNED);
+ *source_position = location.statement_position();
+ location.SetBreakPoint(break_point_object);
+
+ // At least one active break point now.
+ return debug_info->GetBreakPointCount() > 0;
+}
+
+
+bool Debug::SetBreakPointForScript(Handle<Script> script,
+ Handle<Object> break_point_object,
+ int* source_position,
+ BreakPositionAlignment alignment) {
+ HandleScope scope(isolate_);
+
+ // Obtain shared function info for the function.
+ Handle<Object> result =
+ FindSharedFunctionInfoInScript(script, *source_position);
+ if (result->IsUndefined()) return false;
+
+ // Make sure the function has set up the debug info.
+ Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>::cast(result);
+ if (!EnsureDebugInfo(shared, Handle<JSFunction>::null())) {
+ // Return if retrieving debug info failed.
+ return false;
+ }
+
+ // Find position within function. The script position might be before the
+ // source position of the first function.
+ int position;
+ if (shared->start_position() > *source_position) {
+ position = 0;
+ } else {
+ position = *source_position - shared->start_position();
+ }
+
+ Handle<DebugInfo> debug_info(shared->GetDebugInfo());
+ // Source positions starts with zero.
+ DCHECK(position >= 0);
+
+ // Find the break point and change it.
+ BreakLocation location = BreakLocation::FromPosition(
+ debug_info, ALL_BREAK_LOCATIONS, position, alignment);
+ location.SetBreakPoint(break_point_object);
+
+ position = (alignment == STATEMENT_ALIGNED) ? location.statement_position()
+ : location.position();
+
+ *source_position = position + shared->start_position();
+
+ // At least one active break point now.
+ DCHECK(debug_info->GetBreakPointCount() > 0);
+ return true;
+}
+
+
+void Debug::ClearBreakPoint(Handle<Object> break_point_object) {
+ HandleScope scope(isolate_);
+
+ DebugInfoListNode* node = debug_info_list_;
+ while (node != NULL) {
+ Handle<Object> result =
+ DebugInfo::FindBreakPointInfo(node->debug_info(), break_point_object);
+ if (!result->IsUndefined()) {
+ // Get information in the break point.
+ Handle<BreakPointInfo> break_point_info =
+ Handle<BreakPointInfo>::cast(result);
+ Handle<DebugInfo> debug_info = node->debug_info();
+
+ // Find the break point and clear it.
+ Address pc = debug_info->code()->entry() +
+ break_point_info->code_position()->value();
+
+ BreakLocation location =
+ BreakLocation::FromAddress(debug_info, ALL_BREAK_LOCATIONS, pc);
+ location.ClearBreakPoint(break_point_object);
+
+ // If there are no more break points left remove the debug info for this
+ // function.
+ if (debug_info->GetBreakPointCount() == 0) {
+ RemoveDebugInfoAndClearFromShared(debug_info);
+ }
+
+ return;
+ }
+ node = node->next();
+ }
+}
+
+
+// Clear out all the debug break code. This is ONLY supposed to be used when
+// shutting down the debugger as it will leave the break point information in
+// DebugInfo even though the code is patched back to the non break point state.
+void Debug::ClearAllBreakPoints() {
+ for (DebugInfoListNode* node = debug_info_list_; node != NULL;
+ node = node->next()) {
+ for (BreakLocation::Iterator it(node->debug_info(), ALL_BREAK_LOCATIONS);
+ !it.Done(); it.Next()) {
+ it.GetBreakLocation().ClearDebugBreak();
+ }
+ }
+ // Remove all debug info.
+ while (debug_info_list_ != NULL) {
+ RemoveDebugInfoAndClearFromShared(debug_info_list_->debug_info());
+ }
+}
+
+
+void Debug::FloodWithOneShot(Handle<JSFunction> function,
+ BreakLocatorType type) {
+ // Make sure the function is compiled and has set up the debug info.
+ Handle<SharedFunctionInfo> shared(function->shared());
+ if (!EnsureDebugInfo(shared, function)) {
+ // Return if we failed to retrieve the debug info.
+ return;
+ }
+
+ // Flood the function with break points.
+ Handle<DebugInfo> debug_info(shared->GetDebugInfo());
+ for (BreakLocation::Iterator it(debug_info, type); !it.Done(); it.Next()) {
+ it.GetBreakLocation().SetOneShot();
+ }
+}
+
+
+void Debug::FloodBoundFunctionWithOneShot(Handle<JSFunction> function) {
+ Handle<FixedArray> new_bindings(function->function_bindings());
+ Handle<Object> bindee(new_bindings->get(JSFunction::kBoundFunctionIndex),
+ isolate_);
+
+ if (!bindee.is_null() && bindee->IsJSFunction()) {
+ Handle<JSFunction> bindee_function(JSFunction::cast(*bindee));
+ FloodWithOneShotGeneric(bindee_function);
+ }
+}
+
+
+void Debug::FloodDefaultConstructorWithOneShot(Handle<JSFunction> function) {
+ DCHECK(function->shared()->is_default_constructor());
+ // Instead of stepping into the function we directly step into the super class
+ // constructor.
+ Isolate* isolate = function->GetIsolate();
+ PrototypeIterator iter(isolate, function);
+ Handle<Object> proto = PrototypeIterator::GetCurrent(iter);
+ if (!proto->IsJSFunction()) return; // Object.prototype
+ Handle<JSFunction> function_proto = Handle<JSFunction>::cast(proto);
+ FloodWithOneShotGeneric(function_proto);
+}
+
+
+void Debug::FloodWithOneShotGeneric(Handle<JSFunction> function,
+ Handle<Object> holder) {
+ if (function->shared()->bound()) {
+ FloodBoundFunctionWithOneShot(function);
+ } else if (function->shared()->is_default_constructor()) {
+ FloodDefaultConstructorWithOneShot(function);
+ } else {
+ Isolate* isolate = function->GetIsolate();
+ // Don't allow step into functions in the native context.
+ if (function->shared()->code() ==
+ isolate->builtins()->builtin(Builtins::kFunctionApply) ||
+ function->shared()->code() ==
+ isolate->builtins()->builtin(Builtins::kFunctionCall)) {
+ // Handle function.apply and function.call separately to flood the
+ // function to be called and not the code for Builtins::FunctionApply or
+ // Builtins::FunctionCall. The receiver of call/apply is the target
+ // function.
+ if (!holder.is_null() && holder->IsJSFunction()) {
+ Handle<JSFunction> js_function = Handle<JSFunction>::cast(holder);
+ FloodWithOneShotGeneric(js_function);
+ }
+ } else {
+ FloodWithOneShot(function);
+ }
+ }
+}
+
+
+void Debug::FloodHandlerWithOneShot() {
+ // Iterate through the JavaScript stack looking for handlers.
+ StackFrame::Id id = break_frame_id();
+ if (id == StackFrame::NO_ID) {
+ // If there is no JavaScript stack don't do anything.
+ return;
+ }
+ for (JavaScriptFrameIterator it(isolate_, id); !it.done(); it.Advance()) {
+ JavaScriptFrame* frame = it.frame();
+ int stack_slots = 0; // The computed stack slot count is not used.
+ if (frame->LookupExceptionHandlerInTable(&stack_slots, NULL) > 0) {
+ // Flood the function with the catch/finally block with break points.
+ FloodWithOneShot(Handle<JSFunction>(frame->function()));
+ return;
+ }
+ }
+}
+
+
+void Debug::ChangeBreakOnException(ExceptionBreakType type, bool enable) {
+ if (type == BreakUncaughtException) {
+ break_on_uncaught_exception_ = enable;
+ } else {
+ break_on_exception_ = enable;
+ }
+}
+
+
+bool Debug::IsBreakOnException(ExceptionBreakType type) {
+ if (type == BreakUncaughtException) {
+ return break_on_uncaught_exception_;
+ } else {
+ return break_on_exception_;
+ }
+}
+
+
+FrameSummary GetFirstFrameSummary(JavaScriptFrame* frame) {
+ List<FrameSummary> frames(FLAG_max_inlining_levels + 1);
+ frame->Summarize(&frames);
+ return frames.first();
+}
+
+
+void Debug::PrepareStep(StepAction step_action,
+ int step_count,
+ StackFrame::Id frame_id) {
+ HandleScope scope(isolate_);
+
+ DCHECK(in_debug_scope());
+
+ // Remember this step action and count.
+ thread_local_.last_step_action_ = step_action;
+ if (step_action == StepOut) {
+ // For step out target frame will be found on the stack so there is no need
+ // to set step counter for it. It's expected to always be 0 for StepOut.
+ thread_local_.step_count_ = 0;
+ } else {
+ thread_local_.step_count_ = step_count;
+ }
+
+ // Get the frame where the execution has stopped and skip the debug frame if
+ // any. The debug frame will only be present if execution was stopped due to
+ // hitting a break point. In other situations (e.g. unhandled exception) the
+ // debug frame is not present.
+ StackFrame::Id id = break_frame_id();
+ if (id == StackFrame::NO_ID) {
+ // If there is no JavaScript stack don't do anything.
+ return;
+ }
+ if (frame_id != StackFrame::NO_ID) {
+ id = frame_id;
+ }
+ JavaScriptFrameIterator frames_it(isolate_, id);
+ JavaScriptFrame* frame = frames_it.frame();
+
+ // First of all ensure there is one-shot break points in the top handler
+ // if any.
+ FloodHandlerWithOneShot();
+
+ // If the function on the top frame is unresolved perform step out. This will
+ // be the case when calling unknown function and having the debugger stopped
+ // in an unhandled exception.
+ if (!frame->function()->IsJSFunction()) {
+ // Step out: Find the calling JavaScript frame and flood it with
+ // breakpoints.
+ frames_it.Advance();
+ // Fill the function to return to with one-shot break points.
+ JSFunction* function = frames_it.frame()->function();
+ FloodWithOneShot(Handle<JSFunction>(function));
+ return;
+ }
+
+ // Get the debug info (create it if it does not exist).
+ FrameSummary summary = GetFirstFrameSummary(frame);
+ Handle<JSFunction> function(summary.function());
+ Handle<SharedFunctionInfo> shared(function->shared());
+ if (!EnsureDebugInfo(shared, function)) {
+ // Return if ensuring debug info failed.
+ return;
+ }
+
+ Handle<DebugInfo> debug_info(shared->GetDebugInfo());
+ // Refresh frame summary if the code has been recompiled for debugging.
+ if (shared->code() != *summary.code()) summary = GetFirstFrameSummary(frame);
+
+ // PC points to the instruction after the current one, possibly a break
+ // location as well. So the "- 1" to exclude it from the search.
+ Address call_pc = summary.pc() - 1;
+ BreakLocation location =
+ BreakLocation::FromAddress(debug_info, ALL_BREAK_LOCATIONS, call_pc);
+
+ // If this is the last break code target step out is the only possibility.
+ if (location.IsReturn() || step_action == StepOut) {
+ if (step_action == StepOut) {
+ // Skip step_count frames starting with the current one.
+ while (step_count-- > 0 && !frames_it.done()) {
+ frames_it.Advance();
+ }
+ } else {
+ DCHECK(location.IsReturn());
+ frames_it.Advance();
+ }
+ // Skip native and extension functions on the stack.
+ while (!frames_it.done() &&
+ !frames_it.frame()->function()->IsSubjectToDebugging()) {
+ frames_it.Advance();
+ }
+ // Step out: If there is a JavaScript caller frame, we need to
+ // flood it with breakpoints.
+ if (!frames_it.done()) {
+ // Fill the function to return to with one-shot break points.
+ JSFunction* function = frames_it.frame()->function();
+ FloodWithOneShot(Handle<JSFunction>(function));
+ // Set target frame pointer.
+ ActivateStepOut(frames_it.frame());
+ }
+ return;
+ }
+
+ if (step_action != StepNext && step_action != StepMin) {
+ // If there's restarter frame on top of the stack, just get the pointer
+ // to function which is going to be restarted.
+ if (thread_local_.restarter_frame_function_pointer_ != NULL) {
+ Handle<JSFunction> restarted_function(
+ JSFunction::cast(*thread_local_.restarter_frame_function_pointer_));
+ FloodWithOneShot(restarted_function);
+ } else if (location.IsCall()) {
+ // Find target function on the expression stack.
+ // Expression stack looks like this (top to bottom):
+ // argN
+ // ...
+ // arg0
+ // Receiver
+ // Function to call
+ int num_expressions_without_args =
+ frame->ComputeExpressionsCount() - location.CallArgumentsCount();
+ DCHECK(num_expressions_without_args >= 2);
+ Object* fun = frame->GetExpression(num_expressions_without_args - 2);
+
+ // Flood the actual target of call/apply.
+ if (fun->IsJSFunction()) {
+ Isolate* isolate = JSFunction::cast(fun)->GetIsolate();
+ Code* apply = isolate->builtins()->builtin(Builtins::kFunctionApply);
+ Code* call = isolate->builtins()->builtin(Builtins::kFunctionCall);
+ // Find target function on the expression stack for expression like
+ // Function.call.call...apply(...)
+ int i = 1;
+ while (fun->IsJSFunction()) {
+ Code* code = JSFunction::cast(fun)->shared()->code();
+ if (code != apply && code != call) break;
+ DCHECK(num_expressions_without_args >= i);
+ fun = frame->GetExpression(num_expressions_without_args - i);
+ i--;
+ }
+ }
+
+ if (fun->IsJSFunction()) {
+ Handle<JSFunction> js_function(JSFunction::cast(fun));
+ FloodWithOneShotGeneric(js_function);
+ }
+ }
+
+ ActivateStepIn(frame);
+ }
+
+ // Fill the current function with one-shot break points even for step in on
+ // a call target as the function called might be a native function for
+ // which step in will not stop. It also prepares for stepping in
+ // getters/setters.
+ // If we are stepping into another frame, only fill calls and returns.
+ FloodWithOneShot(function, step_action == StepFrame ? CALLS_AND_RETURNS
+ : ALL_BREAK_LOCATIONS);
+
+ // Remember source position and frame to handle step next.
+ thread_local_.last_statement_position_ =
+ debug_info->code()->SourceStatementPosition(summary.pc());
+ thread_local_.last_fp_ = frame->UnpaddedFP();
+}
+
+
+// Check whether the current debug break should be reported to the debugger. It
+// is used to have step next and step in only report break back to the debugger
+// if on a different frame or in a different statement. In some situations
+// there will be several break points in the same statement when the code is
+// flooded with one-shot break points. This function helps to perform several
+// steps before reporting break back to the debugger.
+bool Debug::StepNextContinue(BreakLocation* break_location,
+ JavaScriptFrame* frame) {
+ // StepNext and StepOut shouldn't bring us deeper in code, so last frame
+ // shouldn't be a parent of current frame.
+ StepAction step_action = thread_local_.last_step_action_;
+
+ if (step_action == StepNext || step_action == StepOut) {
+ if (frame->fp() < thread_local_.last_fp_) return true;
+ }
+
+ // We stepped into a new frame if the frame pointer changed.
+ if (step_action == StepFrame) {
+ return frame->UnpaddedFP() == thread_local_.last_fp_;
+ }
+
+ // If the step last action was step next or step in make sure that a new
+ // statement is hit.
+ if (step_action == StepNext || step_action == StepIn) {
+ // Never continue if returning from function.
+ if (break_location->IsReturn()) return false;
+
+ // Continue if we are still on the same frame and in the same statement.
+ int current_statement_position =
+ break_location->code()->SourceStatementPosition(frame->pc());
+ return thread_local_.last_fp_ == frame->UnpaddedFP() &&
+ thread_local_.last_statement_position_ == current_statement_position;
+ }
+
+ // No step next action - don't continue.
+ return false;
+}
+
+
+// Check whether the code object at the specified address is a debug break code
+// object.
+bool Debug::IsDebugBreak(Address addr) {
+ Code* code = Code::GetCodeFromTargetAddress(addr);
+ return code->is_debug_stub();
+}
+
+
+// Simple function for returning the source positions for active break points.
+Handle<Object> Debug::GetSourceBreakLocations(
+ Handle<SharedFunctionInfo> shared,
+ BreakPositionAlignment position_alignment) {
+ Isolate* isolate = shared->GetIsolate();
+ Heap* heap = isolate->heap();
+ if (!shared->HasDebugInfo()) {
+ return Handle<Object>(heap->undefined_value(), isolate);
+ }
+ Handle<DebugInfo> debug_info(shared->GetDebugInfo());
+ if (debug_info->GetBreakPointCount() == 0) {
+ return Handle<Object>(heap->undefined_value(), isolate);
+ }
+ Handle<FixedArray> locations =
+ isolate->factory()->NewFixedArray(debug_info->GetBreakPointCount());
+ int count = 0;
+ for (int i = 0; i < debug_info->break_points()->length(); ++i) {
+ if (!debug_info->break_points()->get(i)->IsUndefined()) {
+ BreakPointInfo* break_point_info =
+ BreakPointInfo::cast(debug_info->break_points()->get(i));
+ int break_points = break_point_info->GetBreakPointCount();
+ if (break_points == 0) continue;
+ Smi* position = NULL;
+ switch (position_alignment) {
+ case STATEMENT_ALIGNED:
+ position = break_point_info->statement_position();
+ break;
+ case BREAK_POSITION_ALIGNED:
+ position = break_point_info->source_position();
+ break;
+ }
+ for (int j = 0; j < break_points; ++j) locations->set(count++, position);
+ }
+ }
+ return locations;
+}
+
+
+// Handle stepping into a function.
+void Debug::HandleStepIn(Handle<Object> function_obj, bool is_constructor) {
+ // Flood getter/setter if we either step in or step to another frame.
+ bool step_frame = thread_local_.last_step_action_ == StepFrame;
+ if (!StepInActive() && !step_frame) return;
+ if (!function_obj->IsJSFunction()) return;
+ Handle<JSFunction> function = Handle<JSFunction>::cast(function_obj);
+ Isolate* isolate = function->GetIsolate();
+
+ StackFrameIterator it(isolate);
+ it.Advance();
+ // For constructor functions skip another frame.
+ if (is_constructor) {
+ DCHECK(it.frame()->is_construct());
+ it.Advance();
+ }
+ Address fp = it.frame()->fp();
+
+ // Flood the function with one-shot break points if it is called from where
+ // step into was requested, or when stepping into a new frame.
+ if (fp == thread_local_.step_into_fp_ || step_frame) {
+ FloodWithOneShotGeneric(function, Handle<Object>());
+ }
+}
+
+
+void Debug::ClearStepping() {
+ // Clear the various stepping setup.
+ ClearOneShot();
+ ClearStepIn();
+ ClearStepOut();
+ ClearStepNext();
+
+ // Clear multiple step counter.
+ thread_local_.step_count_ = 0;
+}
+
+
+// Clears all the one-shot break points that are currently set. Normally this
+// function is called each time a break point is hit as one shot break points
+// are used to support stepping.
+void Debug::ClearOneShot() {
+ // The current implementation just runs through all the breakpoints. When the
+ // last break point for a function is removed that function is automatically
+ // removed from the list.
+ for (DebugInfoListNode* node = debug_info_list_; node != NULL;
+ node = node->next()) {
+ for (BreakLocation::Iterator it(node->debug_info(), ALL_BREAK_LOCATIONS);
+ !it.Done(); it.Next()) {
+ it.GetBreakLocation().ClearOneShot();
+ }
+ }
+}
+
+
+void Debug::ActivateStepIn(StackFrame* frame) {
+ DCHECK(!StepOutActive());
+ thread_local_.step_into_fp_ = frame->UnpaddedFP();
+}
+
+
+void Debug::ClearStepIn() {
+ thread_local_.step_into_fp_ = 0;
+}
+
+
+void Debug::ActivateStepOut(StackFrame* frame) {
+ DCHECK(!StepInActive());
+ thread_local_.step_out_fp_ = frame->UnpaddedFP();
+}
+
+
+void Debug::ClearStepOut() {
+ thread_local_.step_out_fp_ = 0;
+}
+
+
+void Debug::ClearStepNext() {
+ thread_local_.last_step_action_ = StepNone;
+ thread_local_.last_statement_position_ = RelocInfo::kNoPosition;
+ thread_local_.last_fp_ = 0;
+}
+
+
+bool MatchingCodeTargets(Code* target1, Code* target2) {
+ if (target1 == target2) return true;
+ if (target1->kind() != target2->kind()) return false;
+ return target1->is_handler() || target1->is_inline_cache_stub();
+}
+
+
+// Count the number of calls before the current frame PC to find the
+// corresponding PC in the newly recompiled code.
+static Address ComputeNewPcForRedirect(Code* new_code, Code* old_code,
+ Address old_pc) {
+ DCHECK_EQ(old_code->kind(), Code::FUNCTION);
+ DCHECK_EQ(new_code->kind(), Code::FUNCTION);
+ DCHECK(new_code->has_debug_break_slots());
+ static const int mask = RelocInfo::kCodeTargetMask;
+
+ // Find the target of the current call.
+ Code* target = NULL;
+ intptr_t delta = 0;
+ for (RelocIterator it(old_code, mask); !it.done(); it.next()) {
+ RelocInfo* rinfo = it.rinfo();
+ Address current_pc = rinfo->pc();
+ // The frame PC is behind the call instruction by the call instruction size.
+ if (current_pc > old_pc) break;
+ delta = old_pc - current_pc;
+ target = Code::GetCodeFromTargetAddress(rinfo->target_address());
+ }
+
+ // Count the number of calls to the same target before the current call.
+ int index = 0;
+ for (RelocIterator it(old_code, mask); !it.done(); it.next()) {
+ RelocInfo* rinfo = it.rinfo();
+ Address current_pc = rinfo->pc();
+ if (current_pc > old_pc) break;
+ Code* current = Code::GetCodeFromTargetAddress(rinfo->target_address());
+ if (MatchingCodeTargets(target, current)) index++;
+ }
+
+ DCHECK(index > 0);
+
+ // Repeat the count on the new code to find corresponding call.
+ for (RelocIterator it(new_code, mask); !it.done(); it.next()) {
+ RelocInfo* rinfo = it.rinfo();
+ Code* current = Code::GetCodeFromTargetAddress(rinfo->target_address());
+ if (MatchingCodeTargets(target, current)) index--;
+ if (index == 0) return rinfo->pc() + delta;
+ }
+
+ UNREACHABLE();
+ return NULL;
+}
+
+
+// Count the number of continuations at which the current pc offset is at.
+static int ComputeContinuationIndexFromPcOffset(Code* code, int pc_offset) {
+ DCHECK_EQ(code->kind(), Code::FUNCTION);
+ Address pc = code->instruction_start() + pc_offset;
+ int mask = RelocInfo::ModeMask(RelocInfo::GENERATOR_CONTINUATION);
+ int index = 0;
+ for (RelocIterator it(code, mask); !it.done(); it.next()) {
+ index++;
+ RelocInfo* rinfo = it.rinfo();
+ Address current_pc = rinfo->pc();
+ if (current_pc == pc) break;
+ DCHECK(current_pc < pc);
+ }
+ return index;
+}
+
+
+// Find the pc offset for the given continuation index.
+static int ComputePcOffsetFromContinuationIndex(Code* code, int index) {
+ DCHECK_EQ(code->kind(), Code::FUNCTION);
+ DCHECK(code->has_debug_break_slots());
+ int mask = RelocInfo::ModeMask(RelocInfo::GENERATOR_CONTINUATION);
+ RelocIterator it(code, mask);
+ for (int i = 1; i < index; i++) it.next();
+ return static_cast<int>(it.rinfo()->pc() - code->instruction_start());
+}
+
+
+class RedirectActiveFunctions : public ThreadVisitor {
+ public:
+ explicit RedirectActiveFunctions(SharedFunctionInfo* shared)
+ : shared_(shared) {
+ DCHECK(shared->HasDebugCode());
+ }
+
+ void VisitThread(Isolate* isolate, ThreadLocalTop* top) {
+ for (JavaScriptFrameIterator it(isolate, top); !it.done(); it.Advance()) {
+ JavaScriptFrame* frame = it.frame();
+ JSFunction* function = frame->function();
+ if (frame->is_optimized()) continue;
+ if (!function->Inlines(shared_)) continue;
+
+ Code* frame_code = frame->LookupCode();
+ DCHECK(frame_code->kind() == Code::FUNCTION);
+ if (frame_code->has_debug_break_slots()) continue;
+
+ Code* new_code = function->shared()->code();
+ Address old_pc = frame->pc();
+ Address new_pc = ComputeNewPcForRedirect(new_code, frame_code, old_pc);
+
+ if (FLAG_trace_deopt) {
+ PrintF("Replacing pc for debugging: %08" V8PRIxPTR " => %08" V8PRIxPTR
+ "\n",
+ reinterpret_cast<intptr_t>(old_pc),
+ reinterpret_cast<intptr_t>(new_pc));
+ }
+
+ if (FLAG_enable_embedded_constant_pool) {
+ // Update constant pool pointer for new code.
+ frame->set_constant_pool(new_code->constant_pool());
+ }
+
+ // Patch the return address to return into the code with
+ // debug break slots.
+ frame->set_pc(new_pc);
+ }
+ }
+
+ private:
+ SharedFunctionInfo* shared_;
+ DisallowHeapAllocation no_gc_;
+};
+
+
+bool Debug::PrepareFunctionForBreakPoints(Handle<SharedFunctionInfo> shared) {
+ DCHECK(shared->is_compiled());
+
+ if (isolate_->concurrent_recompilation_enabled()) {
+ isolate_->optimizing_compile_dispatcher()->Flush();
+ }
+
+ List<Handle<JSFunction> > functions;
+ List<Handle<JSGeneratorObject> > suspended_generators;
+
+ if (!shared->optimized_code_map()->IsSmi()) {
+ shared->ClearOptimizedCodeMap();
+ }
+
+ // Make sure we abort incremental marking.
+ isolate_->heap()->CollectAllGarbage(Heap::kMakeHeapIterableMask,
+ "prepare for break points");
+
+ {
+ HeapIterator iterator(isolate_->heap());
+ HeapObject* obj;
+ bool include_generators = shared->is_generator();
+
+ while ((obj = iterator.next())) {
+ if (obj->IsJSFunction()) {
+ JSFunction* function = JSFunction::cast(obj);
+ if (!function->Inlines(*shared)) continue;
+ if (function->code()->kind() == Code::OPTIMIZED_FUNCTION) {
+ Deoptimizer::DeoptimizeFunction(function);
+ }
+ if (function->shared() == *shared) functions.Add(handle(function));
+ } else if (include_generators && obj->IsJSGeneratorObject()) {
+ JSGeneratorObject* generator_obj = JSGeneratorObject::cast(obj);
+ if (!generator_obj->is_suspended()) continue;
+ JSFunction* function = generator_obj->function();
+ if (!function->Inlines(*shared)) continue;
+ int pc_offset = generator_obj->continuation();
+ int index =
+ ComputeContinuationIndexFromPcOffset(function->code(), pc_offset);
+ generator_obj->set_continuation(index);
+ suspended_generators.Add(handle(generator_obj));
+ }
+ }
+ }
+
+ if (!shared->HasDebugCode()) {
+ DCHECK(functions.length() > 0);
+ if (!Compiler::CompileDebugCode(functions.first())) return false;
+ }
+
+ for (Handle<JSFunction> const function : functions) {
+ function->ReplaceCode(shared->code());
+ }
+
+ for (Handle<JSGeneratorObject> const generator_obj : suspended_generators) {
+ int index = generator_obj->continuation();
+ int pc_offset = ComputePcOffsetFromContinuationIndex(shared->code(), index);
+ generator_obj->set_continuation(pc_offset);
+ }
+
+ // Update PCs on the stack to point to recompiled code.
+ RedirectActiveFunctions redirect_visitor(*shared);
+ redirect_visitor.VisitThread(isolate_, isolate_->thread_local_top());
+ isolate_->thread_manager()->IterateArchivedThreads(&redirect_visitor);
+
+ return true;
+}
+
+
+class SharedFunctionInfoFinder {
+ public:
+ explicit SharedFunctionInfoFinder(int target_position)
+ : current_candidate_(NULL),
+ current_candidate_closure_(NULL),
+ current_start_position_(RelocInfo::kNoPosition),
+ target_position_(target_position) {}
+
+ void NewCandidate(SharedFunctionInfo* shared, JSFunction* closure = NULL) {
+ int start_position = shared->function_token_position();
+ if (start_position == RelocInfo::kNoPosition) {
+ start_position = shared->start_position();
+ }
+
+ if (start_position > target_position_) return;
+ if (target_position_ > shared->end_position()) return;
+
+ if (current_candidate_ != NULL) {
+ if (current_start_position_ == start_position &&
+ shared->end_position() == current_candidate_->end_position()) {
+ // If a top-level function contains only one function
+ // declaration the source for the top-level and the function
+ // is the same. In that case prefer the non top-level function.
+ if (shared->is_toplevel()) return;
+ } else if (start_position < current_start_position_ ||
+ current_candidate_->end_position() < shared->end_position()) {
+ return;
+ }
+ }
+
+ current_start_position_ = start_position;
+ current_candidate_ = shared;
+ current_candidate_closure_ = closure;
+ }
+
+ SharedFunctionInfo* Result() { return current_candidate_; }
+
+ JSFunction* ResultClosure() { return current_candidate_closure_; }
+
+ private:
+ SharedFunctionInfo* current_candidate_;
+ JSFunction* current_candidate_closure_;
+ int current_start_position_;
+ int target_position_;
+ DisallowHeapAllocation no_gc_;
+};
+
+
+// We need to find a SFI for a literal that may not yet have been compiled yet,
+// and there may not be a JSFunction referencing it. Find the SFI closest to
+// the given position, compile it to reveal possible inner SFIs and repeat.
+// While we are at this, also ensure code with debug break slots so that we do
+// not have to compile a SFI without JSFunction, which is paifu for those that
+// cannot be compiled without context (need to find outer compilable SFI etc.)
+Handle<Object> Debug::FindSharedFunctionInfoInScript(Handle<Script> script,
+ int position) {
+ while (true) {
+ // Go through all shared function infos associated with this script to
+ // find the inner most function containing this position.
+ if (!script->shared_function_infos()->IsWeakFixedArray()) break;
+ WeakFixedArray* array =
+ WeakFixedArray::cast(script->shared_function_infos());
+
+ SharedFunctionInfo* shared;
+ {
+ SharedFunctionInfoFinder finder(position);
+ for (int i = 0; i < array->Length(); i++) {
+ Object* item = array->Get(i);
+ if (!item->IsSharedFunctionInfo()) continue;
+ finder.NewCandidate(SharedFunctionInfo::cast(item));
+ }
+ shared = finder.Result();
+ if (shared == NULL) break;
+ // We found it if it's already compiled and has debug code.
+ if (shared->HasDebugCode()) return handle(shared);
+ }
+ // If not, compile to reveal inner functions, if possible.
+ if (shared->allows_lazy_compilation_without_context()) {
+ HandleScope scope(isolate_);
+ if (!Compiler::CompileDebugCode(handle(shared))) break;
+ continue;
+ }
+
+ // If not possible, comb the heap for the best suitable compile target.
+ JSFunction* closure;
+ {
+ HeapIterator it(isolate_->heap());
+ SharedFunctionInfoFinder finder(position);
+ while (HeapObject* object = it.next()) {
+ JSFunction* candidate_closure = NULL;
+ SharedFunctionInfo* candidate = NULL;
+ if (object->IsJSFunction()) {
+ candidate_closure = JSFunction::cast(object);
+ candidate = candidate_closure->shared();
+ } else if (object->IsSharedFunctionInfo()) {
+ candidate = SharedFunctionInfo::cast(object);
+ if (!candidate->allows_lazy_compilation_without_context()) continue;
+ } else {
+ continue;
+ }
+ if (candidate->script() == *script) {
+ finder.NewCandidate(candidate, candidate_closure);
+ }
+ }
+ closure = finder.ResultClosure();
+ shared = finder.Result();
+ }
+ HandleScope scope(isolate_);
+ if (closure == NULL) {
+ if (!Compiler::CompileDebugCode(handle(shared))) break;
+ } else {
+ if (!Compiler::CompileDebugCode(handle(closure))) break;
+ }
+ }
+ return isolate_->factory()->undefined_value();
+}
+
+
+// Ensures the debug information is present for shared.
+bool Debug::EnsureDebugInfo(Handle<SharedFunctionInfo> shared,
+ Handle<JSFunction> function) {
+ if (!shared->IsSubjectToDebugging()) return false;
+
+ // Return if we already have the debug info for shared.
+ if (shared->HasDebugInfo()) return true;
+
+ if (function.is_null()) {
+ DCHECK(shared->HasDebugCode());
+ } else if (!Compiler::EnsureCompiled(function, CLEAR_EXCEPTION)) {
+ return false;
+ }
+
+ if (!PrepareFunctionForBreakPoints(shared)) return false;
+
+ // Make sure IC state is clean. This is so that we correctly flood
+ // accessor pairs when stepping in.
+ shared->code()->ClearInlineCaches();
+ shared->feedback_vector()->ClearICSlots(*shared);
+
+ // Create the debug info object.
+ DCHECK(shared->HasDebugCode());
+ Handle<DebugInfo> debug_info = isolate_->factory()->NewDebugInfo(shared);
+
+ // Add debug info to the list.
+ DebugInfoListNode* node = new DebugInfoListNode(*debug_info);
+ node->set_next(debug_info_list_);
+ debug_info_list_ = node;
+
+ return true;
+}
+
+
+void Debug::RemoveDebugInfoAndClearFromShared(Handle<DebugInfo> debug_info) {
+ HandleScope scope(isolate_);
+ Handle<SharedFunctionInfo> shared(debug_info->shared());
+
+ DCHECK_NOT_NULL(debug_info_list_);
+ // Run through the debug info objects to find this one and remove it.
+ DebugInfoListNode* prev = NULL;
+ DebugInfoListNode* current = debug_info_list_;
+ while (current != NULL) {
+ if (current->debug_info().is_identical_to(debug_info)) {
+ // Unlink from list. If prev is NULL we are looking at the first element.
+ if (prev == NULL) {
+ debug_info_list_ = current->next();
+ } else {
+ prev->set_next(current->next());
+ }
+ delete current;
+ shared->set_debug_info(isolate_->heap()->undefined_value());
+ return;
+ }
+ // Move to next in list.
+ prev = current;
+ current = current->next();
+ }
+
+ UNREACHABLE();
+}
+
+
+void Debug::SetAfterBreakTarget(JavaScriptFrame* frame) {
+ after_break_target_ = NULL;
+
+ if (LiveEdit::SetAfterBreakTarget(this)) return; // LiveEdit did the job.
+
+ // Continue just after the slot.
+ after_break_target_ = frame->pc();
+}
+
+
+bool Debug::IsBreakAtReturn(JavaScriptFrame* frame) {
+ HandleScope scope(isolate_);
+
+ // Get the executing function in which the debug break occurred.
+ Handle<JSFunction> function(JSFunction::cast(frame->function()));
+ Handle<SharedFunctionInfo> shared(function->shared());
+
+ // With no debug info there are no break points, so we can't be at a return.
+ if (!shared->HasDebugInfo()) return false;
+ Handle<DebugInfo> debug_info(shared->GetDebugInfo());
+ Handle<Code> code(debug_info->code());
+#ifdef DEBUG
+ // Get the code which is actually executing.
+ Handle<Code> frame_code(frame->LookupCode());
+ DCHECK(frame_code.is_identical_to(code));
+#endif
+
+ // Find the reloc info matching the start of the debug break slot.
+ Address slot_pc = frame->pc() - Assembler::kDebugBreakSlotLength;
+ int mask = RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_RETURN);
+ for (RelocIterator it(*code, mask); !it.done(); it.next()) {
+ if (it.rinfo()->pc() == slot_pc) return true;
+ }
+ return false;
+}
+
+
+void Debug::FramesHaveBeenDropped(StackFrame::Id new_break_frame_id,
+ LiveEdit::FrameDropMode mode,
+ Object** restarter_frame_function_pointer) {
+ if (mode != LiveEdit::CURRENTLY_SET_MODE) {
+ thread_local_.frame_drop_mode_ = mode;
+ }
+ thread_local_.break_frame_id_ = new_break_frame_id;
+ thread_local_.restarter_frame_function_pointer_ =
+ restarter_frame_function_pointer;
+}
+
+
+bool Debug::IsDebugGlobal(GlobalObject* global) {
+ return is_loaded() && global == debug_context()->global_object();
+}
+
+
+void Debug::ClearMirrorCache() {
+ PostponeInterruptsScope postpone(isolate_);
+ HandleScope scope(isolate_);
+ AssertDebugContext();
+ Factory* factory = isolate_->factory();
+ Handle<GlobalObject> global(isolate_->global_object());
+ JSObject::SetProperty(global,
+ factory->NewStringFromAsciiChecked("next_handle_"),
+ handle(Smi::FromInt(0), isolate_), SLOPPY).Check();
+ JSObject::SetProperty(global,
+ factory->NewStringFromAsciiChecked("mirror_cache_"),
+ factory->NewJSArray(0, FAST_ELEMENTS), SLOPPY).Check();
+}
+
+
+Handle<FixedArray> Debug::GetLoadedScripts() {
+ // Create and fill the script cache when the loaded scripts is requested for
+ // the first time.
+ if (script_cache_ == NULL) script_cache_ = new ScriptCache(isolate_);
+
+ // Perform GC to get unreferenced scripts evicted from the cache before
+ // returning the content.
+ isolate_->heap()->CollectAllGarbage(Heap::kNoGCFlags,
+ "Debug::GetLoadedScripts");
+
+ // Get the scripts from the cache.
+ return script_cache_->GetScripts();
+}
+
+
+void Debug::GetStepinPositions(JavaScriptFrame* frame, StackFrame::Id frame_id,
+ List<int>* results_out) {
+ FrameSummary summary = GetFirstFrameSummary(frame);
+
+ Handle<JSFunction> fun = Handle<JSFunction>(summary.function());
+ Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>(fun->shared());
+
+ if (!EnsureDebugInfo(shared, fun)) return;
+
+ Handle<DebugInfo> debug_info(shared->GetDebugInfo());
+ // Refresh frame summary if the code has been recompiled for debugging.
+ if (shared->code() != *summary.code()) summary = GetFirstFrameSummary(frame);
+
+ // Find range of break points starting from the break point where execution
+ // has stopped.
+ Address call_pc = summary.pc() - 1;
+ List<BreakLocation> locations;
+ BreakLocation::FromAddressSameStatement(debug_info, ALL_BREAK_LOCATIONS,
+ call_pc, &locations);
+
+ for (BreakLocation location : locations) {
+ if (location.pc() <= summary.pc()) {
+ // 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) {
+ continue; // We are not stepping.
+ } else {
+ JavaScriptFrameIterator 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.
+ if (frame_it.frame()->id() != frame_id) continue;
+ }
+ }
+ if (location.IsStepInLocation()) results_out->Add(location.position());
+ }
+}
+
+
+void Debug::RecordEvalCaller(Handle<Script> script) {
+ script->set_compilation_type(Script::COMPILATION_TYPE_EVAL);
+ // For eval scripts add information on the function from which eval was
+ // called.
+ StackTraceFrameIterator it(script->GetIsolate());
+ if (!it.done()) {
+ script->set_eval_from_shared(it.frame()->function()->shared());
+ Code* code = it.frame()->LookupCode();
+ int offset = static_cast<int>(
+ it.frame()->pc() - code->instruction_start());
+ script->set_eval_from_instructions_offset(Smi::FromInt(offset));
+ }
+}
+
+
+MaybeHandle<Object> Debug::MakeJSObject(const char* constructor_name,
+ int argc,
+ Handle<Object> argv[]) {
+ AssertDebugContext();
+ // Create the execution state object.
+ Handle<GlobalObject> global(isolate_->global_object());
+ Handle<Object> constructor = Object::GetProperty(
+ isolate_, global, constructor_name).ToHandleChecked();
+ DCHECK(constructor->IsJSFunction());
+ if (!constructor->IsJSFunction()) return MaybeHandle<Object>();
+ // We do not handle interrupts here. In particular, termination interrupts.
+ PostponeInterruptsScope no_interrupts(isolate_);
+ return Execution::TryCall(Handle<JSFunction>::cast(constructor),
+ handle(debug_context()->global_proxy()),
+ argc,
+ argv);
+}
+
+
+MaybeHandle<Object> Debug::MakeExecutionState() {
+ // Create the execution state object.
+ Handle<Object> argv[] = { isolate_->factory()->NewNumberFromInt(break_id()) };
+ return MakeJSObject("MakeExecutionState", arraysize(argv), argv);
+}
+
+
+MaybeHandle<Object> Debug::MakeBreakEvent(Handle<Object> break_points_hit) {
+ // Create the new break event object.
+ Handle<Object> argv[] = { isolate_->factory()->NewNumberFromInt(break_id()),
+ break_points_hit };
+ return MakeJSObject("MakeBreakEvent", arraysize(argv), argv);
+}
+
+
+MaybeHandle<Object> Debug::MakeExceptionEvent(Handle<Object> exception,
+ bool uncaught,
+ Handle<Object> promise) {
+ // Create the new exception event object.
+ Handle<Object> argv[] = { isolate_->factory()->NewNumberFromInt(break_id()),
+ exception,
+ isolate_->factory()->ToBoolean(uncaught),
+ promise };
+ return MakeJSObject("MakeExceptionEvent", arraysize(argv), argv);
+}
+
+
+MaybeHandle<Object> Debug::MakeCompileEvent(Handle<Script> script,
+ v8::DebugEvent type) {
+ // Create the compile event object.
+ Handle<Object> script_wrapper = Script::GetWrapper(script);
+ Handle<Object> argv[] = { script_wrapper,
+ isolate_->factory()->NewNumberFromInt(type) };
+ return MakeJSObject("MakeCompileEvent", arraysize(argv), argv);
+}
+
+
+MaybeHandle<Object> Debug::MakePromiseEvent(Handle<JSObject> event_data) {
+ // Create the promise event object.
+ Handle<Object> argv[] = { event_data };
+ return MakeJSObject("MakePromiseEvent", arraysize(argv), argv);
+}
+
+
+MaybeHandle<Object> Debug::MakeAsyncTaskEvent(Handle<JSObject> task_event) {
+ // Create the async task event object.
+ Handle<Object> argv[] = { task_event };
+ return MakeJSObject("MakeAsyncTaskEvent", arraysize(argv), argv);
+}
+
+
+void Debug::OnThrow(Handle<Object> exception) {
+ if (in_debug_scope() || ignore_events()) return;
+ // Temporarily clear any scheduled_exception to allow evaluating
+ // JavaScript from the debug event handler.
+ HandleScope scope(isolate_);
+ Handle<Object> scheduled_exception;
+ if (isolate_->has_scheduled_exception()) {
+ scheduled_exception = handle(isolate_->scheduled_exception(), isolate_);
+ isolate_->clear_scheduled_exception();
+ }
+ OnException(exception, isolate_->GetPromiseOnStackOnThrow());
+ if (!scheduled_exception.is_null()) {
+ isolate_->thread_local_top()->scheduled_exception_ = *scheduled_exception;
+ }
+}
+
+
+void Debug::OnPromiseReject(Handle<JSObject> promise, Handle<Object> value) {
+ if (in_debug_scope() || ignore_events()) return;
+ HandleScope scope(isolate_);
+ // Check whether the promise has been marked as having triggered a message.
+ Handle<Symbol> key = isolate_->factory()->promise_debug_marker_symbol();
+ if (JSReceiver::GetDataProperty(promise, key)->IsUndefined()) {
+ OnException(value, promise);
+ }
+}
+
+
+MaybeHandle<Object> Debug::PromiseHasUserDefinedRejectHandler(
+ Handle<JSObject> promise) {
+ Handle<JSFunction> fun = Handle<JSFunction>::cast(
+ JSReceiver::GetDataProperty(isolate_->js_builtins_object(),
+ isolate_->factory()->NewStringFromStaticChars(
+ "$promiseHasUserDefinedRejectHandler")));
+ return Execution::Call(isolate_, fun, promise, 0, NULL);
+}
+
+
+void Debug::OnException(Handle<Object> exception, Handle<Object> promise) {
+ // In our prediction, try-finally is not considered to catch.
+ Isolate::CatchType catch_type = isolate_->PredictExceptionCatcher();
+ bool uncaught = (catch_type == Isolate::NOT_CAUGHT);
+ if (promise->IsJSObject()) {
+ Handle<JSObject> jspromise = Handle<JSObject>::cast(promise);
+ // Mark the promise as already having triggered a message.
+ Handle<Symbol> key = isolate_->factory()->promise_debug_marker_symbol();
+ JSObject::SetProperty(jspromise, key, key, STRICT).Assert();
+ // Check whether the promise reject is considered an uncaught exception.
+ Handle<Object> has_reject_handler;
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate_, has_reject_handler,
+ PromiseHasUserDefinedRejectHandler(jspromise), /* void */);
+ uncaught = has_reject_handler->IsFalse();
+ }
+ // Bail out if exception breaks are not active
+ if (uncaught) {
+ // Uncaught exceptions are reported by either flags.
+ if (!(break_on_uncaught_exception_ || break_on_exception_)) return;
+ } else {
+ // Caught exceptions are reported is activated.
+ if (!break_on_exception_) return;
+ }
+
+ DebugScope debug_scope(this);
+ if (debug_scope.failed()) return;
+
+ // Clear all current stepping setup.
+ ClearStepping();
+
+ // Create the event data object.
+ Handle<Object> event_data;
+ // Bail out and don't call debugger if exception.
+ if (!MakeExceptionEvent(
+ exception, uncaught, promise).ToHandle(&event_data)) {
+ return;
+ }
+
+ // Process debug event.
+ ProcessDebugEvent(v8::Exception, Handle<JSObject>::cast(event_data), false);
+ // Return to continue execution from where the exception was thrown.
+}
+
+
+void Debug::OnCompileError(Handle<Script> script) {
+ if (ignore_events()) return;
+
+ if (in_debug_scope()) {
+ ProcessCompileEventInDebugScope(v8::CompileError, script);
+ return;
+ }
+
+ HandleScope scope(isolate_);
+ DebugScope debug_scope(this);
+ if (debug_scope.failed()) return;
+
+ // Create the compile state object.
+ Handle<Object> event_data;
+ // Bail out and don't call debugger if exception.
+ if (!MakeCompileEvent(script, v8::CompileError).ToHandle(&event_data)) return;
+
+ // Process debug event.
+ ProcessDebugEvent(v8::CompileError, Handle<JSObject>::cast(event_data), true);
+}
+
+
+void Debug::OnDebugBreak(Handle<Object> break_points_hit,
+ bool auto_continue) {
+ // The caller provided for DebugScope.
+ AssertDebugContext();
+ // Bail out if there is no listener for this event
+ if (ignore_events()) return;
+
+ HandleScope scope(isolate_);
+ // Create the event data object.
+ Handle<Object> event_data;
+ // Bail out and don't call debugger if exception.
+ if (!MakeBreakEvent(break_points_hit).ToHandle(&event_data)) return;
+
+ // Process debug event.
+ ProcessDebugEvent(v8::Break,
+ Handle<JSObject>::cast(event_data),
+ auto_continue);
+}
+
+
+void Debug::OnBeforeCompile(Handle<Script> script) {
+ if (in_debug_scope() || ignore_events()) return;
+
+ HandleScope scope(isolate_);
+ DebugScope debug_scope(this);
+ if (debug_scope.failed()) return;
+
+ // Create the event data object.
+ Handle<Object> event_data;
+ // Bail out and don't call debugger if exception.
+ if (!MakeCompileEvent(script, v8::BeforeCompile).ToHandle(&event_data))
+ return;
+
+ // Process debug event.
+ ProcessDebugEvent(v8::BeforeCompile,
+ Handle<JSObject>::cast(event_data),
+ true);
+}
+
+
+// Handle debugger actions when a new script is compiled.
+void Debug::OnAfterCompile(Handle<Script> script) {
+ // Add the newly compiled script to the script cache.
+ if (script_cache_ != NULL) script_cache_->Add(script);
+
+ if (ignore_events()) return;
+
+ if (in_debug_scope()) {
+ ProcessCompileEventInDebugScope(v8::AfterCompile, script);
+ return;
+ }
+
+ HandleScope scope(isolate_);
+ DebugScope debug_scope(this);
+ if (debug_scope.failed()) return;
+
+ // If debugging there might be script break points registered for this
+ // script. Make sure that these break points are set.
+
+ // Get the function UpdateScriptBreakPoints (defined in debug.js).
+ Handle<String> update_script_break_points_string =
+ isolate_->factory()->InternalizeOneByteString(
+ STATIC_CHAR_VECTOR("UpdateScriptBreakPoints"));
+ Handle<GlobalObject> debug_global(debug_context()->global_object());
+ Handle<Object> update_script_break_points =
+ Object::GetProperty(
+ debug_global, update_script_break_points_string).ToHandleChecked();
+ if (!update_script_break_points->IsJSFunction()) {
+ return;
+ }
+ DCHECK(update_script_break_points->IsJSFunction());
+
+ // Wrap the script object in a proper JS object before passing it
+ // to JavaScript.
+ Handle<Object> wrapper = Script::GetWrapper(script);
+
+ // Call UpdateScriptBreakPoints expect no exceptions.
+ Handle<Object> argv[] = { wrapper };
+ if (Execution::TryCall(Handle<JSFunction>::cast(update_script_break_points),
+ isolate_->js_builtins_object(),
+ arraysize(argv),
+ argv).is_null()) {
+ return;
+ }
+
+ // Create the compile state object.
+ Handle<Object> event_data;
+ // Bail out and don't call debugger if exception.
+ if (!MakeCompileEvent(script, v8::AfterCompile).ToHandle(&event_data)) return;
+
+ // Process debug event.
+ ProcessDebugEvent(v8::AfterCompile, Handle<JSObject>::cast(event_data), true);
+}
+
+
+void Debug::OnPromiseEvent(Handle<JSObject> data) {
+ if (in_debug_scope() || ignore_events()) return;
+
+ HandleScope scope(isolate_);
+ DebugScope debug_scope(this);
+ if (debug_scope.failed()) return;
+
+ // Create the script collected state object.
+ Handle<Object> event_data;
+ // Bail out and don't call debugger if exception.
+ if (!MakePromiseEvent(data).ToHandle(&event_data)) return;
+
+ // Process debug event.
+ ProcessDebugEvent(v8::PromiseEvent,
+ Handle<JSObject>::cast(event_data),
+ true);
+}
+
+
+void Debug::OnAsyncTaskEvent(Handle<JSObject> data) {
+ if (in_debug_scope() || ignore_events()) return;
+
+ HandleScope scope(isolate_);
+ DebugScope debug_scope(this);
+ if (debug_scope.failed()) return;
+
+ // Create the script collected state object.
+ Handle<Object> event_data;
+ // Bail out and don't call debugger if exception.
+ if (!MakeAsyncTaskEvent(data).ToHandle(&event_data)) return;
+
+ // Process debug event.
+ ProcessDebugEvent(v8::AsyncTaskEvent,
+ Handle<JSObject>::cast(event_data),
+ true);
+}
+
+
+void Debug::ProcessDebugEvent(v8::DebugEvent event,
+ Handle<JSObject> event_data,
+ bool auto_continue) {
+ HandleScope scope(isolate_);
+
+ // Create the execution state.
+ Handle<Object> exec_state;
+ // Bail out and don't call debugger if exception.
+ if (!MakeExecutionState().ToHandle(&exec_state)) return;
+
+ // First notify the message handler if any.
+ if (message_handler_ != NULL) {
+ NotifyMessageHandler(event,
+ Handle<JSObject>::cast(exec_state),
+ event_data,
+ auto_continue);
+ }
+ // Notify registered debug event listener. This can be either a C or
+ // a JavaScript function. Don't call event listener for v8::Break
+ // here, if it's only a debug command -- they will be processed later.
+ if ((event != v8::Break || !auto_continue) && !event_listener_.is_null()) {
+ CallEventCallback(event, exec_state, event_data, NULL);
+ }
+}
+
+
+void Debug::CallEventCallback(v8::DebugEvent event,
+ Handle<Object> exec_state,
+ Handle<Object> event_data,
+ v8::Debug::ClientData* client_data) {
+ bool previous = in_debug_event_listener_;
+ in_debug_event_listener_ = true;
+ if (event_listener_->IsForeign()) {
+ // Invoke the C debug event listener.
+ v8::Debug::EventCallback callback =
+ FUNCTION_CAST<v8::Debug::EventCallback>(
+ Handle<Foreign>::cast(event_listener_)->foreign_address());
+ EventDetailsImpl event_details(event,
+ Handle<JSObject>::cast(exec_state),
+ Handle<JSObject>::cast(event_data),
+ event_listener_data_,
+ client_data);
+ callback(event_details);
+ DCHECK(!isolate_->has_scheduled_exception());
+ } else {
+ // Invoke the JavaScript debug event listener.
+ DCHECK(event_listener_->IsJSFunction());
+ Handle<Object> argv[] = { Handle<Object>(Smi::FromInt(event), isolate_),
+ exec_state,
+ event_data,
+ event_listener_data_ };
+ Handle<JSReceiver> global(isolate_->global_proxy());
+ Execution::TryCall(Handle<JSFunction>::cast(event_listener_),
+ global, arraysize(argv), argv);
+ }
+ in_debug_event_listener_ = previous;
+}
+
+
+void Debug::ProcessCompileEventInDebugScope(v8::DebugEvent event,
+ Handle<Script> script) {
+ if (event_listener_.is_null()) return;
+
+ SuppressDebug while_processing(this);
+ DebugScope debug_scope(this);
+ if (debug_scope.failed()) return;
+
+ Handle<Object> event_data;
+ // Bail out and don't call debugger if exception.
+ if (!MakeCompileEvent(script, event).ToHandle(&event_data)) return;
+
+ // Create the execution state.
+ Handle<Object> exec_state;
+ // Bail out and don't call debugger if exception.
+ if (!MakeExecutionState().ToHandle(&exec_state)) return;
+
+ CallEventCallback(event, exec_state, event_data, NULL);
+}
+
+
+Handle<Context> Debug::GetDebugContext() {
+ if (!is_loaded()) return Handle<Context>();
+ DebugScope debug_scope(this);
+ if (debug_scope.failed()) return Handle<Context>();
+ // The global handle may be destroyed soon after. Return it reboxed.
+ return handle(*debug_context(), isolate_);
+}
+
+
+void Debug::NotifyMessageHandler(v8::DebugEvent event,
+ Handle<JSObject> exec_state,
+ Handle<JSObject> event_data,
+ bool auto_continue) {
+ // Prevent other interrupts from triggering, for example API callbacks,
+ // while dispatching message handler callbacks.
+ PostponeInterruptsScope no_interrupts(isolate_);
+ DCHECK(is_active_);
+ HandleScope scope(isolate_);
+ // Process the individual events.
+ bool sendEventMessage = false;
+ switch (event) {
+ case v8::Break:
+ sendEventMessage = !auto_continue;
+ break;
+ case v8::NewFunction:
+ case v8::BeforeCompile:
+ case v8::CompileError:
+ case v8::PromiseEvent:
+ case v8::AsyncTaskEvent:
+ break;
+ case v8::Exception:
+ case v8::AfterCompile:
+ sendEventMessage = true;
+ break;
+ }
+
+ // The debug command interrupt flag might have been set when the command was
+ // added. It should be enough to clear the flag only once while we are in the
+ // debugger.
+ DCHECK(in_debug_scope());
+ isolate_->stack_guard()->ClearDebugCommand();
+
+ // Notify the debugger that a debug event has occurred unless auto continue is
+ // active in which case no event is send.
+ if (sendEventMessage) {
+ MessageImpl message = MessageImpl::NewEvent(
+ event,
+ auto_continue,
+ Handle<JSObject>::cast(exec_state),
+ Handle<JSObject>::cast(event_data));
+ InvokeMessageHandler(message);
+ }
+
+ // If auto continue don't make the event cause a break, but process messages
+ // in the queue if any. For script collected events don't even process
+ // messages in the queue as the execution state might not be what is expected
+ // by the client.
+ if (auto_continue && !has_commands()) return;
+
+ // DebugCommandProcessor goes here.
+ bool running = auto_continue;
+
+ Handle<Object> cmd_processor_ctor = Object::GetProperty(
+ isolate_, exec_state, "debugCommandProcessor").ToHandleChecked();
+ Handle<Object> ctor_args[] = { isolate_->factory()->ToBoolean(running) };
+ Handle<Object> cmd_processor = Execution::Call(
+ isolate_, cmd_processor_ctor, exec_state, 1, ctor_args).ToHandleChecked();
+ Handle<JSFunction> process_debug_request = Handle<JSFunction>::cast(
+ Object::GetProperty(
+ isolate_, cmd_processor, "processDebugRequest").ToHandleChecked());
+ Handle<Object> is_running = Object::GetProperty(
+ isolate_, cmd_processor, "isRunning").ToHandleChecked();
+
+ // Process requests from the debugger.
+ do {
+ // Wait for new command in the queue.
+ command_received_.Wait();
+
+ // Get the command from the queue.
+ CommandMessage command = command_queue_.Get();
+ isolate_->logger()->DebugTag(
+ "Got request from command queue, in interactive loop.");
+ if (!is_active()) {
+ // Delete command text and user data.
+ command.Dispose();
+ return;
+ }
+
+ Vector<const uc16> command_text(
+ const_cast<const uc16*>(command.text().start()),
+ command.text().length());
+ Handle<String> request_text = isolate_->factory()->NewStringFromTwoByte(
+ command_text).ToHandleChecked();
+ Handle<Object> request_args[] = { request_text };
+ Handle<Object> answer_value;
+ Handle<String> answer;
+ MaybeHandle<Object> maybe_exception;
+ MaybeHandle<Object> maybe_result =
+ Execution::TryCall(process_debug_request, cmd_processor, 1,
+ request_args, &maybe_exception);
+
+ if (maybe_result.ToHandle(&answer_value)) {
+ if (answer_value->IsUndefined()) {
+ answer = isolate_->factory()->empty_string();
+ } else {
+ answer = Handle<String>::cast(answer_value);
+ }
+
+ // Log the JSON request/response.
+ if (FLAG_trace_debug_json) {
+ PrintF("%s\n", request_text->ToCString().get());
+ PrintF("%s\n", answer->ToCString().get());
+ }
+
+ Handle<Object> is_running_args[] = { answer };
+ maybe_result = Execution::Call(
+ isolate_, is_running, cmd_processor, 1, is_running_args);
+ Handle<Object> result;
+ if (!maybe_result.ToHandle(&result)) break;
+ running = result->IsTrue();
+ } else {
+ Handle<Object> exception;
+ if (!maybe_exception.ToHandle(&exception)) break;
+ Handle<Object> result;
+ if (!Execution::ToString(isolate_, exception).ToHandle(&result)) break;
+ answer = Handle<String>::cast(result);
+ }
+
+ // Return the result.
+ MessageImpl message = MessageImpl::NewResponse(
+ event, running, exec_state, event_data, answer, command.client_data());
+ InvokeMessageHandler(message);
+ command.Dispose();
+
+ // Return from debug event processing if either the VM is put into the
+ // running state (through a continue command) or auto continue is active
+ // and there are no more commands queued.
+ } while (!running || has_commands());
+ command_queue_.Clear();
+}
+
+
+void Debug::SetEventListener(Handle<Object> callback,
+ Handle<Object> data) {
+ GlobalHandles* global_handles = isolate_->global_handles();
+
+ // Remove existing entry.
+ GlobalHandles::Destroy(event_listener_.location());
+ event_listener_ = Handle<Object>();
+ GlobalHandles::Destroy(event_listener_data_.location());
+ event_listener_data_ = Handle<Object>();
+
+ // Set new entry.
+ if (!callback->IsUndefined() && !callback->IsNull()) {
+ event_listener_ = global_handles->Create(*callback);
+ if (data.is_null()) data = isolate_->factory()->undefined_value();
+ event_listener_data_ = global_handles->Create(*data);
+ }
+
+ UpdateState();
+}
+
+
+void Debug::SetMessageHandler(v8::Debug::MessageHandler handler) {
+ message_handler_ = handler;
+ UpdateState();
+ if (handler == NULL && in_debug_scope()) {
+ // Send an empty command to the debugger if in a break to make JavaScript
+ // run again if the debugger is closed.
+ EnqueueCommandMessage(Vector<const uint16_t>::empty());
+ }
+}
+
+
+
+void Debug::UpdateState() {
+ bool is_active = message_handler_ != NULL || !event_listener_.is_null();
+ if (is_active || in_debug_scope()) {
+ // Note that the debug context could have already been loaded to
+ // bootstrap test cases.
+ isolate_->compilation_cache()->Disable();
+ is_active = Load();
+ } else if (is_loaded()) {
+ isolate_->compilation_cache()->Enable();
+ Unload();
+ }
+ is_active_ = is_active;
+}
+
+
+// Calls the registered debug message handler. This callback is part of the
+// public API.
+void Debug::InvokeMessageHandler(MessageImpl message) {
+ if (message_handler_ != NULL) message_handler_(message);
+}
+
+
+// Puts a command coming from the public API on the queue. Creates
+// a copy of the command string managed by the debugger. Up to this
+// point, the command data was managed by the API client. Called
+// by the API client thread.
+void Debug::EnqueueCommandMessage(Vector<const uint16_t> command,
+ v8::Debug::ClientData* client_data) {
+ // Need to cast away const.
+ CommandMessage message = CommandMessage::New(
+ Vector<uint16_t>(const_cast<uint16_t*>(command.start()),
+ command.length()),
+ client_data);
+ isolate_->logger()->DebugTag("Put command on command_queue.");
+ command_queue_.Put(message);
+ command_received_.Signal();
+
+ // Set the debug command break flag to have the command processed.
+ if (!in_debug_scope()) isolate_->stack_guard()->RequestDebugCommand();
+}
+
+
+MaybeHandle<Object> Debug::Call(Handle<JSFunction> fun, Handle<Object> data) {
+ DebugScope debug_scope(this);
+ if (debug_scope.failed()) return isolate_->factory()->undefined_value();
+
+ // Create the execution state.
+ Handle<Object> exec_state;
+ if (!MakeExecutionState().ToHandle(&exec_state)) {
+ return isolate_->factory()->undefined_value();
+ }
+
+ Handle<Object> argv[] = { exec_state, data };
+ return Execution::Call(
+ isolate_,
+ fun,
+ Handle<Object>(debug_context()->global_proxy(), isolate_),
+ arraysize(argv),
+ argv);
+}
+
+
+void Debug::HandleDebugBreak() {
+ // Ignore debug break during bootstrapping.
+ if (isolate_->bootstrapper()->IsActive()) return;
+ // Just continue if breaks are disabled.
+ if (break_disabled()) return;
+ // Ignore debug break if debugger is not active.
+ if (!is_active()) return;
+
+ StackLimitCheck check(isolate_);
+ if (check.HasOverflowed()) return;
+
+ { JavaScriptFrameIterator it(isolate_);
+ DCHECK(!it.done());
+ Object* fun = it.frame()->function();
+ if (fun && fun->IsJSFunction()) {
+ // Don't stop in builtin functions.
+ if (JSFunction::cast(fun)->IsBuiltin()) return;
+ GlobalObject* global = JSFunction::cast(fun)->context()->global_object();
+ // Don't stop in debugger functions.
+ if (IsDebugGlobal(global)) return;
+ }
+ }
+
+ // Collect the break state before clearing the flags.
+ bool debug_command_only = isolate_->stack_guard()->CheckDebugCommand() &&
+ !isolate_->stack_guard()->CheckDebugBreak();
+
+ isolate_->stack_guard()->ClearDebugBreak();
+
+ ProcessDebugMessages(debug_command_only);
+}
+
+
+void Debug::ProcessDebugMessages(bool debug_command_only) {
+ isolate_->stack_guard()->ClearDebugCommand();
+
+ StackLimitCheck check(isolate_);
+ if (check.HasOverflowed()) return;
+
+ HandleScope scope(isolate_);
+ DebugScope debug_scope(this);
+ if (debug_scope.failed()) return;
+
+ // Notify the debug event listeners. Indicate auto continue if the break was
+ // a debug command break.
+ OnDebugBreak(isolate_->factory()->undefined_value(), debug_command_only);
+}
+
+
+DebugScope::DebugScope(Debug* debug)
+ : debug_(debug),
+ prev_(debug->debugger_entry()),
+ save_(debug_->isolate_),
+ no_termination_exceptons_(debug_->isolate_,
+ StackGuard::TERMINATE_EXECUTION) {
+ // Link recursive debugger entry.
+ base::NoBarrier_Store(&debug_->thread_local_.current_debug_scope_,
+ reinterpret_cast<base::AtomicWord>(this));
+
+ // Store the previous break id and frame id.
+ break_id_ = debug_->break_id();
+ break_frame_id_ = debug_->break_frame_id();
+
+ // Create the new break info. If there is no JavaScript frames there is no
+ // break frame id.
+ JavaScriptFrameIterator it(isolate());
+ bool has_js_frames = !it.done();
+ debug_->thread_local_.break_frame_id_ = has_js_frames ? it.frame()->id()
+ : StackFrame::NO_ID;
+ debug_->SetNextBreakId();
+
+ debug_->UpdateState();
+ // Make sure that debugger is loaded and enter the debugger context.
+ // The previous context is kept in save_.
+ failed_ = !debug_->is_loaded();
+ if (!failed_) isolate()->set_context(*debug->debug_context());
+}
+
+
+
+DebugScope::~DebugScope() {
+ if (!failed_ && prev_ == NULL) {
+ // Clear mirror cache when leaving the debugger. Skip this if there is a
+ // pending exception as clearing the mirror cache calls back into
+ // JavaScript. This can happen if the v8::Debug::Call is used in which
+ // case the exception should end up in the calling code.
+ if (!isolate()->has_pending_exception()) debug_->ClearMirrorCache();
+
+ // If there are commands in the queue when leaving the debugger request
+ // that these commands are processed.
+ if (debug_->has_commands()) isolate()->stack_guard()->RequestDebugCommand();
+ }
+
+ // Leaving this debugger entry.
+ base::NoBarrier_Store(&debug_->thread_local_.current_debug_scope_,
+ reinterpret_cast<base::AtomicWord>(prev_));
+
+ // Restore to the previous break state.
+ debug_->thread_local_.break_frame_id_ = break_frame_id_;
+ debug_->thread_local_.break_id_ = break_id_;
+
+ debug_->UpdateState();
+}
+
+
+MessageImpl MessageImpl::NewEvent(DebugEvent event,
+ bool running,
+ Handle<JSObject> exec_state,
+ Handle<JSObject> event_data) {
+ MessageImpl message(true, event, running,
+ exec_state, event_data, Handle<String>(), NULL);
+ return message;
+}
+
+
+MessageImpl MessageImpl::NewResponse(DebugEvent event,
+ bool running,
+ Handle<JSObject> exec_state,
+ Handle<JSObject> event_data,
+ Handle<String> response_json,
+ v8::Debug::ClientData* client_data) {
+ MessageImpl message(false, event, running,
+ exec_state, event_data, response_json, client_data);
+ return message;
+}
+
+
+MessageImpl::MessageImpl(bool is_event,
+ DebugEvent event,
+ bool running,
+ Handle<JSObject> exec_state,
+ Handle<JSObject> event_data,
+ Handle<String> response_json,
+ v8::Debug::ClientData* client_data)
+ : is_event_(is_event),
+ event_(event),
+ running_(running),
+ exec_state_(exec_state),
+ event_data_(event_data),
+ response_json_(response_json),
+ client_data_(client_data) {}
+
+
+bool MessageImpl::IsEvent() const {
+ return is_event_;
+}
+
+
+bool MessageImpl::IsResponse() const {
+ return !is_event_;
+}
+
+
+DebugEvent MessageImpl::GetEvent() const {
+ return event_;
+}
+
+
+bool MessageImpl::WillStartRunning() const {
+ return running_;
+}
+
+
+v8::Local<v8::Object> MessageImpl::GetExecutionState() const {
+ return v8::Utils::ToLocal(exec_state_);
+}
+
+
+v8::Isolate* MessageImpl::GetIsolate() const {
+ return reinterpret_cast<v8::Isolate*>(exec_state_->GetIsolate());
+}
+
+
+v8::Local<v8::Object> MessageImpl::GetEventData() const {
+ return v8::Utils::ToLocal(event_data_);
+}
+
+
+v8::Local<v8::String> MessageImpl::GetJSON() const {
+ Isolate* isolate = event_data_->GetIsolate();
+ v8::EscapableHandleScope scope(reinterpret_cast<v8::Isolate*>(isolate));
+
+ if (IsEvent()) {
+ // Call toJSONProtocol on the debug event object.
+ Handle<Object> fun = Object::GetProperty(
+ isolate, event_data_, "toJSONProtocol").ToHandleChecked();
+ if (!fun->IsJSFunction()) {
+ return v8::Local<v8::String>();
+ }
+
+ MaybeHandle<Object> maybe_json =
+ Execution::TryCall(Handle<JSFunction>::cast(fun), event_data_, 0, NULL);
+ Handle<Object> json;
+ if (!maybe_json.ToHandle(&json) || !json->IsString()) {
+ return v8::Local<v8::String>();
+ }
+ return scope.Escape(v8::Utils::ToLocal(Handle<String>::cast(json)));
+ } else {
+ return v8::Utils::ToLocal(response_json_);
+ }
+}
+
+
+v8::Local<v8::Context> MessageImpl::GetEventContext() const {
+ Isolate* isolate = event_data_->GetIsolate();
+ v8::Local<v8::Context> context = GetDebugEventContext(isolate);
+ // Isolate::context() may be NULL when "script collected" event occurs.
+ DCHECK(!context.IsEmpty());
+ return context;
+}
+
+
+v8::Debug::ClientData* MessageImpl::GetClientData() const {
+ return client_data_;
+}
+
+
+EventDetailsImpl::EventDetailsImpl(DebugEvent event,
+ Handle<JSObject> exec_state,
+ Handle<JSObject> event_data,
+ Handle<Object> callback_data,
+ v8::Debug::ClientData* client_data)
+ : event_(event),
+ exec_state_(exec_state),
+ event_data_(event_data),
+ callback_data_(callback_data),
+ client_data_(client_data) {}
+
+
+DebugEvent EventDetailsImpl::GetEvent() const {
+ return event_;
+}
+
+
+v8::Local<v8::Object> EventDetailsImpl::GetExecutionState() const {
+ return v8::Utils::ToLocal(exec_state_);
+}
+
+
+v8::Local<v8::Object> EventDetailsImpl::GetEventData() const {
+ return v8::Utils::ToLocal(event_data_);
+}
+
+
+v8::Local<v8::Context> EventDetailsImpl::GetEventContext() const {
+ return GetDebugEventContext(exec_state_->GetIsolate());
+}
+
+
+v8::Local<v8::Value> EventDetailsImpl::GetCallbackData() const {
+ return v8::Utils::ToLocal(callback_data_);
+}
+
+
+v8::Debug::ClientData* EventDetailsImpl::GetClientData() const {
+ return client_data_;
+}
+
+
+CommandMessage::CommandMessage() : text_(Vector<uint16_t>::empty()),
+ client_data_(NULL) {
+}
+
+
+CommandMessage::CommandMessage(const Vector<uint16_t>& text,
+ v8::Debug::ClientData* data)
+ : text_(text),
+ client_data_(data) {
+}
+
+
+void CommandMessage::Dispose() {
+ text_.Dispose();
+ delete client_data_;
+ client_data_ = NULL;
+}
+
+
+CommandMessage CommandMessage::New(const Vector<uint16_t>& command,
+ v8::Debug::ClientData* data) {
+ return CommandMessage(command.Clone(), data);
+}
+
+
+CommandMessageQueue::CommandMessageQueue(int size) : start_(0), end_(0),
+ size_(size) {
+ messages_ = NewArray<CommandMessage>(size);
+}
+
+
+CommandMessageQueue::~CommandMessageQueue() {
+ while (!IsEmpty()) Get().Dispose();
+ DeleteArray(messages_);
+}
+
+
+CommandMessage CommandMessageQueue::Get() {
+ DCHECK(!IsEmpty());
+ int result = start_;
+ start_ = (start_ + 1) % size_;
+ return messages_[result];
+}
+
+
+void CommandMessageQueue::Put(const CommandMessage& message) {
+ if ((end_ + 1) % size_ == start_) {
+ Expand();
+ }
+ messages_[end_] = message;
+ end_ = (end_ + 1) % size_;
+}
+
+
+void CommandMessageQueue::Expand() {
+ CommandMessageQueue new_queue(size_ * 2);
+ while (!IsEmpty()) {
+ new_queue.Put(Get());
+ }
+ CommandMessage* array_to_free = messages_;
+ *this = new_queue;
+ new_queue.messages_ = array_to_free;
+ // Make the new_queue empty so that it doesn't call Dispose on any messages.
+ new_queue.start_ = new_queue.end_;
+ // Automatic destructor called on new_queue, freeing array_to_free.
+}
+
+
+LockingCommandMessageQueue::LockingCommandMessageQueue(Logger* logger, int size)
+ : logger_(logger), queue_(size) {}
+
+
+bool LockingCommandMessageQueue::IsEmpty() const {
+ base::LockGuard<base::Mutex> lock_guard(&mutex_);
+ return queue_.IsEmpty();
+}
+
+
+CommandMessage LockingCommandMessageQueue::Get() {
+ base::LockGuard<base::Mutex> lock_guard(&mutex_);
+ CommandMessage result = queue_.Get();
+ logger_->DebugEvent("Get", result.text());
+ return result;
+}
+
+
+void LockingCommandMessageQueue::Put(const CommandMessage& message) {
+ base::LockGuard<base::Mutex> lock_guard(&mutex_);
+ queue_.Put(message);
+ logger_->DebugEvent("Put", message.text());
+}
+
+
+void LockingCommandMessageQueue::Clear() {
+ base::LockGuard<base::Mutex> lock_guard(&mutex_);
+ queue_.Clear();
+}
+
+} // namespace internal
+} // namespace v8
--- /dev/null
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_DEBUG_DEBUG_H_
+#define V8_DEBUG_DEBUG_H_
+
+#include "src/allocation.h"
+#include "src/arguments.h"
+#include "src/assembler.h"
+#include "src/base/atomicops.h"
+#include "src/base/platform/platform.h"
+#include "src/debug/liveedit.h"
+#include "src/execution.h"
+#include "src/factory.h"
+#include "src/flags.h"
+#include "src/frames-inl.h"
+#include "src/hashmap.h"
+#include "src/runtime/runtime.h"
+#include "src/string-stream.h"
+#include "src/v8threads.h"
+
+#include "include/v8-debug.h"
+
+namespace v8 {
+namespace internal {
+
+
+// Forward declarations.
+class DebugScope;
+
+
+// Step actions. NOTE: These values are in macros.py as well.
+enum StepAction {
+ StepNone = -1, // Stepping not prepared.
+ StepOut = 0, // Step out of the current function.
+ StepNext = 1, // Step to the next statement in the current function.
+ StepIn = 2, // Step into new functions invoked or the next statement
+ // in the current function.
+ StepMin = 3, // Perform a minimum step in the current function.
+ StepInMin = 4, // Step into new functions invoked or perform a minimum step
+ // in the current function.
+ StepFrame = 5 // Step into a new frame or return to previous frame.
+};
+
+
+// Type of exception break. NOTE: These values are in macros.py as well.
+enum ExceptionBreakType {
+ BreakException = 0,
+ BreakUncaughtException = 1
+};
+
+
+// Type of exception break.
+enum BreakLocatorType { ALL_BREAK_LOCATIONS, CALLS_AND_RETURNS };
+
+
+// The different types of breakpoint position alignments.
+// Must match Debug.BreakPositionAlignment in debug.js
+enum BreakPositionAlignment {
+ STATEMENT_ALIGNED = 0,
+ BREAK_POSITION_ALIGNED = 1
+};
+
+
+class BreakLocation {
+ public:
+ // Find the break point at the supplied address, or the closest one before
+ // the address.
+ static BreakLocation FromAddress(Handle<DebugInfo> debug_info,
+ BreakLocatorType type, Address pc);
+
+ static void FromAddressSameStatement(Handle<DebugInfo> debug_info,
+ BreakLocatorType type, Address pc,
+ List<BreakLocation>* result_out);
+
+ static BreakLocation FromPosition(Handle<DebugInfo> debug_info,
+ BreakLocatorType type, int position,
+ BreakPositionAlignment alignment);
+
+ bool IsDebugBreak() const;
+
+ inline bool IsReturn() const {
+ return RelocInfo::IsDebugBreakSlotAtReturn(rmode_);
+ }
+ inline bool IsCall() const {
+ return RelocInfo::IsDebugBreakSlotAtCall(rmode_);
+ }
+ inline bool IsConstructCall() const {
+ return RelocInfo::IsDebugBreakSlotAtConstructCall(rmode_);
+ }
+ inline int CallArgumentsCount() const {
+ DCHECK(IsCall());
+ return RelocInfo::DebugBreakCallArgumentsCount(data_);
+ }
+
+ bool IsStepInLocation() const;
+ inline bool HasBreakPoint() const {
+ return debug_info_->HasBreakPoint(pc_offset_);
+ }
+
+ Handle<Object> BreakPointObjects() const;
+
+ void SetBreakPoint(Handle<Object> break_point_object);
+ void ClearBreakPoint(Handle<Object> break_point_object);
+
+ void SetOneShot();
+ void ClearOneShot();
+
+
+ inline RelocInfo rinfo() const {
+ return RelocInfo(pc(), rmode(), data_, code());
+ }
+
+ inline int position() const { return position_; }
+ inline int statement_position() const { return statement_position_; }
+
+ inline Address pc() const { return code()->entry() + pc_offset_; }
+
+ inline RelocInfo::Mode rmode() const { return rmode_; }
+
+ inline Code* code() const { return debug_info_->code(); }
+
+ private:
+ BreakLocation(Handle<DebugInfo> debug_info, RelocInfo* rinfo, int position,
+ int statement_position);
+
+ class Iterator {
+ public:
+ Iterator(Handle<DebugInfo> debug_info, BreakLocatorType type);
+
+ BreakLocation GetBreakLocation() {
+ return BreakLocation(debug_info_, rinfo(), position(),
+ statement_position());
+ }
+
+ inline bool Done() const { return reloc_iterator_.done(); }
+ void Next();
+
+ void SkipTo(int count) {
+ while (count-- > 0) Next();
+ }
+
+ inline RelocInfo::Mode rmode() { return reloc_iterator_.rinfo()->rmode(); }
+ inline RelocInfo* rinfo() { return reloc_iterator_.rinfo(); }
+ inline Address pc() { return rinfo()->pc(); }
+ int break_index() const { return break_index_; }
+ inline int position() const { return position_; }
+ inline int statement_position() const { return statement_position_; }
+
+ private:
+ static int GetModeMask(BreakLocatorType type);
+
+ Handle<DebugInfo> debug_info_;
+ RelocIterator reloc_iterator_;
+ int break_index_;
+ int position_;
+ int statement_position_;
+
+ DisallowHeapAllocation no_gc_;
+
+ DISALLOW_COPY_AND_ASSIGN(Iterator);
+ };
+
+ friend class Debug;
+
+ static int BreakIndexFromAddress(Handle<DebugInfo> debug_info,
+ BreakLocatorType type, Address pc);
+
+ void SetDebugBreak();
+ void ClearDebugBreak();
+
+ inline bool IsDebuggerStatement() const {
+ return RelocInfo::IsDebuggerStatement(rmode_);
+ }
+ inline bool IsDebugBreakSlot() const {
+ return RelocInfo::IsDebugBreakSlot(rmode_);
+ }
+
+ Handle<DebugInfo> debug_info_;
+ int pc_offset_;
+ RelocInfo::Mode rmode_;
+ intptr_t data_;
+ int position_;
+ int statement_position_;
+};
+
+
+// Cache of all script objects in the heap. When a script is added a weak handle
+// to it is created and that weak handle is stored in the cache. The weak handle
+// callback takes care of removing the script from the cache. The key used in
+// the cache is the script id.
+class ScriptCache {
+ public:
+ explicit ScriptCache(Isolate* isolate);
+ ~ScriptCache();
+
+ // Add script to the cache.
+ void Add(Handle<Script> script);
+
+ // Return the scripts in the cache.
+ Handle<FixedArray> GetScripts() {
+ return WeakValueHashTable::GetWeakValues(table_);
+ }
+
+ private:
+ Isolate* isolate_;
+ Handle<WeakValueHashTable> table_;
+};
+
+
+// Linked list holding debug info objects. The debug info objects are kept as
+// weak handles to avoid a debug info object to keep a function alive.
+class DebugInfoListNode {
+ public:
+ explicit DebugInfoListNode(DebugInfo* debug_info);
+ ~DebugInfoListNode();
+
+ DebugInfoListNode* next() { return next_; }
+ void set_next(DebugInfoListNode* next) { next_ = next; }
+ Handle<DebugInfo> debug_info() { return Handle<DebugInfo>(debug_info_); }
+
+ private:
+ // Global (weak) handle to the debug info object.
+ DebugInfo** debug_info_;
+
+ // Next pointer for linked list.
+ DebugInfoListNode* next_;
+};
+
+
+
+// Message delivered to the message handler callback. This is either a debugger
+// event or the response to a command.
+class MessageImpl: public v8::Debug::Message {
+ public:
+ // Create a message object for a debug event.
+ static MessageImpl NewEvent(DebugEvent event,
+ bool running,
+ Handle<JSObject> exec_state,
+ Handle<JSObject> event_data);
+
+ // Create a message object for the response to a debug command.
+ static MessageImpl NewResponse(DebugEvent event,
+ bool running,
+ Handle<JSObject> exec_state,
+ Handle<JSObject> event_data,
+ Handle<String> response_json,
+ v8::Debug::ClientData* client_data);
+
+ // Implementation of interface v8::Debug::Message.
+ virtual bool IsEvent() const;
+ virtual bool IsResponse() const;
+ virtual DebugEvent GetEvent() const;
+ virtual bool WillStartRunning() const;
+ virtual v8::Local<v8::Object> GetExecutionState() const;
+ virtual v8::Local<v8::Object> GetEventData() const;
+ virtual v8::Local<v8::String> GetJSON() const;
+ virtual v8::Local<v8::Context> GetEventContext() const;
+ virtual v8::Debug::ClientData* GetClientData() const;
+ virtual v8::Isolate* GetIsolate() const;
+
+ private:
+ MessageImpl(bool is_event,
+ DebugEvent event,
+ bool running,
+ Handle<JSObject> exec_state,
+ Handle<JSObject> event_data,
+ Handle<String> response_json,
+ v8::Debug::ClientData* client_data);
+
+ bool is_event_; // Does this message represent a debug event?
+ DebugEvent event_; // Debug event causing the break.
+ bool running_; // Will the VM start running after this event?
+ Handle<JSObject> exec_state_; // Current execution state.
+ Handle<JSObject> event_data_; // Data associated with the event.
+ Handle<String> response_json_; // Response JSON if message holds a response.
+ v8::Debug::ClientData* client_data_; // Client data passed with the request.
+};
+
+
+// Details of the debug event delivered to the debug event listener.
+class EventDetailsImpl : public v8::Debug::EventDetails {
+ public:
+ EventDetailsImpl(DebugEvent event,
+ Handle<JSObject> exec_state,
+ Handle<JSObject> event_data,
+ Handle<Object> callback_data,
+ v8::Debug::ClientData* client_data);
+ virtual DebugEvent GetEvent() const;
+ virtual v8::Local<v8::Object> GetExecutionState() const;
+ virtual v8::Local<v8::Object> GetEventData() const;
+ virtual v8::Local<v8::Context> GetEventContext() const;
+ virtual v8::Local<v8::Value> GetCallbackData() const;
+ virtual v8::Debug::ClientData* GetClientData() const;
+ private:
+ DebugEvent event_; // Debug event causing the break.
+ Handle<JSObject> exec_state_; // Current execution state.
+ Handle<JSObject> event_data_; // Data associated with the event.
+ Handle<Object> callback_data_; // User data passed with the callback
+ // when it was registered.
+ v8::Debug::ClientData* client_data_; // Data passed to DebugBreakForCommand.
+};
+
+
+// Message send by user to v8 debugger or debugger output message.
+// In addition to command text it may contain a pointer to some user data
+// which are expected to be passed along with the command reponse to message
+// handler.
+class CommandMessage {
+ public:
+ static CommandMessage New(const Vector<uint16_t>& command,
+ v8::Debug::ClientData* data);
+ CommandMessage();
+
+ // Deletes user data and disposes of the text.
+ void Dispose();
+ Vector<uint16_t> text() const { return text_; }
+ v8::Debug::ClientData* client_data() const { return client_data_; }
+ private:
+ CommandMessage(const Vector<uint16_t>& text,
+ v8::Debug::ClientData* data);
+
+ Vector<uint16_t> text_;
+ v8::Debug::ClientData* client_data_;
+};
+
+
+// A Queue of CommandMessage objects. A thread-safe version is
+// LockingCommandMessageQueue, based on this class.
+class CommandMessageQueue BASE_EMBEDDED {
+ public:
+ explicit CommandMessageQueue(int size);
+ ~CommandMessageQueue();
+ bool IsEmpty() const { return start_ == end_; }
+ CommandMessage Get();
+ void Put(const CommandMessage& message);
+ void Clear() { start_ = end_ = 0; } // Queue is empty after Clear().
+ private:
+ // Doubles the size of the message queue, and copies the messages.
+ void Expand();
+
+ CommandMessage* messages_;
+ int start_;
+ int end_;
+ int size_; // The size of the queue buffer. Queue can hold size-1 messages.
+};
+
+
+// LockingCommandMessageQueue is a thread-safe circular buffer of CommandMessage
+// messages. The message data is not managed by LockingCommandMessageQueue.
+// Pointers to the data are passed in and out. Implemented by adding a
+// Mutex to CommandMessageQueue. Includes logging of all puts and gets.
+class LockingCommandMessageQueue BASE_EMBEDDED {
+ public:
+ LockingCommandMessageQueue(Logger* logger, int size);
+ bool IsEmpty() const;
+ CommandMessage Get();
+ void Put(const CommandMessage& message);
+ void Clear();
+ private:
+ Logger* logger_;
+ CommandMessageQueue queue_;
+ mutable base::Mutex mutex_;
+ DISALLOW_COPY_AND_ASSIGN(LockingCommandMessageQueue);
+};
+
+
+// This class contains the debugger support. The main purpose is to handle
+// setting break points in the code.
+//
+// This class controls the debug info for all functions which currently have
+// active breakpoints in them. This debug info is held in the heap root object
+// debug_info which is a FixedArray. Each entry in this list is of class
+// DebugInfo.
+class Debug {
+ public:
+ // Debug event triggers.
+ void OnDebugBreak(Handle<Object> break_points_hit, bool auto_continue);
+
+ void OnThrow(Handle<Object> exception);
+ void OnPromiseReject(Handle<JSObject> promise, Handle<Object> value);
+ void OnCompileError(Handle<Script> script);
+ void OnBeforeCompile(Handle<Script> script);
+ void OnAfterCompile(Handle<Script> script);
+ void OnPromiseEvent(Handle<JSObject> data);
+ void OnAsyncTaskEvent(Handle<JSObject> data);
+
+ // API facing.
+ void SetEventListener(Handle<Object> callback, Handle<Object> data);
+ void SetMessageHandler(v8::Debug::MessageHandler handler);
+ void EnqueueCommandMessage(Vector<const uint16_t> command,
+ v8::Debug::ClientData* client_data = NULL);
+ MUST_USE_RESULT MaybeHandle<Object> Call(Handle<JSFunction> fun,
+ Handle<Object> data);
+ Handle<Context> GetDebugContext();
+ void HandleDebugBreak();
+ void ProcessDebugMessages(bool debug_command_only);
+
+ // Internal logic
+ bool Load();
+ void Break(Arguments args, JavaScriptFrame*);
+ void SetAfterBreakTarget(JavaScriptFrame* frame);
+
+ // Scripts handling.
+ Handle<FixedArray> GetLoadedScripts();
+
+ // Break point handling.
+ bool SetBreakPoint(Handle<JSFunction> function,
+ Handle<Object> break_point_object,
+ int* source_position);
+ bool SetBreakPointForScript(Handle<Script> script,
+ Handle<Object> break_point_object,
+ int* source_position,
+ BreakPositionAlignment alignment);
+ void ClearBreakPoint(Handle<Object> break_point_object);
+ void ClearAllBreakPoints();
+ void FloodWithOneShot(Handle<JSFunction> function,
+ BreakLocatorType type = ALL_BREAK_LOCATIONS);
+ void FloodBoundFunctionWithOneShot(Handle<JSFunction> function);
+ void FloodDefaultConstructorWithOneShot(Handle<JSFunction> function);
+ void FloodWithOneShotGeneric(Handle<JSFunction> function,
+ Handle<Object> holder = Handle<Object>());
+ void FloodHandlerWithOneShot();
+ void ChangeBreakOnException(ExceptionBreakType type, bool enable);
+ bool IsBreakOnException(ExceptionBreakType type);
+
+ // Stepping handling.
+ void PrepareStep(StepAction step_action,
+ int step_count,
+ StackFrame::Id frame_id);
+ void ClearStepping();
+ void ClearStepOut();
+ bool IsStepping() { return thread_local_.step_count_ > 0; }
+ bool StepNextContinue(BreakLocation* location, JavaScriptFrame* frame);
+ bool StepInActive() { return thread_local_.step_into_fp_ != 0; }
+ void HandleStepIn(Handle<Object> function_obj, bool is_constructor);
+ bool StepOutActive() { return thread_local_.step_out_fp_ != 0; }
+
+ void GetStepinPositions(JavaScriptFrame* frame, StackFrame::Id frame_id,
+ List<int>* results_out);
+
+ bool PrepareFunctionForBreakPoints(Handle<SharedFunctionInfo> shared);
+
+ // Returns whether the operation succeeded. Compilation can only be triggered
+ // if a valid closure is passed as the second argument, otherwise the shared
+ // function needs to be compiled already.
+ bool EnsureDebugInfo(Handle<SharedFunctionInfo> shared,
+ Handle<JSFunction> function);
+ static Handle<DebugInfo> GetDebugInfo(Handle<SharedFunctionInfo> shared);
+
+ template <typename C>
+ bool CompileToRevealInnerFunctions(C* compilable);
+
+ // This function is used in FunctionNameUsing* tests.
+ Handle<Object> FindSharedFunctionInfoInScript(Handle<Script> script,
+ int position);
+
+ // Returns true if the current stub call is patched to call the debugger.
+ static bool IsDebugBreak(Address addr);
+
+ static Handle<Object> GetSourceBreakLocations(
+ Handle<SharedFunctionInfo> shared,
+ BreakPositionAlignment position_aligment);
+
+ // Check whether a global object is the debug global object.
+ bool IsDebugGlobal(GlobalObject* global);
+
+ // Check whether this frame is just about to return.
+ bool IsBreakAtReturn(JavaScriptFrame* frame);
+
+ // Support for LiveEdit
+ void FramesHaveBeenDropped(StackFrame::Id new_break_frame_id,
+ LiveEdit::FrameDropMode mode,
+ Object** restarter_frame_function_pointer);
+
+ // Threading support.
+ char* ArchiveDebug(char* to);
+ char* RestoreDebug(char* from);
+ static int ArchiveSpacePerThread();
+ void FreeThreadResources() { }
+
+ // Record function from which eval was called.
+ static void RecordEvalCaller(Handle<Script> script);
+
+ bool CheckExecutionState(int id) {
+ return is_active() && !debug_context().is_null() && break_id() != 0 &&
+ break_id() == id;
+ }
+
+ // Flags and states.
+ DebugScope* debugger_entry() {
+ return reinterpret_cast<DebugScope*>(
+ base::NoBarrier_Load(&thread_local_.current_debug_scope_));
+ }
+ inline Handle<Context> debug_context() { return debug_context_; }
+ void set_live_edit_enabled(bool v) { live_edit_enabled_ = v; }
+ bool live_edit_enabled() const {
+ return FLAG_enable_liveedit && live_edit_enabled_ ;
+ }
+
+ inline bool is_active() const { return is_active_; }
+ inline bool is_loaded() const { return !debug_context_.is_null(); }
+ inline bool in_debug_scope() const {
+ return !!base::NoBarrier_Load(&thread_local_.current_debug_scope_);
+ }
+ void set_disable_break(bool v) { break_disabled_ = v; }
+
+ StackFrame::Id break_frame_id() { return thread_local_.break_frame_id_; }
+ int break_id() { return thread_local_.break_id_; }
+
+ // Support for embedding into generated code.
+ Address is_active_address() {
+ return reinterpret_cast<Address>(&is_active_);
+ }
+
+ Address after_break_target_address() {
+ return reinterpret_cast<Address>(&after_break_target_);
+ }
+
+ Address restarter_frame_function_pointer_address() {
+ Object*** address = &thread_local_.restarter_frame_function_pointer_;
+ return reinterpret_cast<Address>(address);
+ }
+
+ Address step_in_fp_addr() {
+ return reinterpret_cast<Address>(&thread_local_.step_into_fp_);
+ }
+
+ StepAction last_step_action() { return thread_local_.last_step_action_; }
+
+ private:
+ explicit Debug(Isolate* isolate);
+
+ void UpdateState();
+ void Unload();
+ void SetNextBreakId() {
+ thread_local_.break_id_ = ++thread_local_.break_count_;
+ }
+
+ // Check whether there are commands in the command queue.
+ inline bool has_commands() const { return !command_queue_.IsEmpty(); }
+ inline bool ignore_events() const { return is_suppressed_ || !is_active_; }
+ inline bool break_disabled() const {
+ return break_disabled_ || in_debug_event_listener_;
+ }
+
+ void OnException(Handle<Object> exception, Handle<Object> promise);
+
+ // Constructors for debug event objects.
+ MUST_USE_RESULT MaybeHandle<Object> MakeJSObject(
+ const char* constructor_name,
+ int argc,
+ Handle<Object> argv[]);
+ MUST_USE_RESULT MaybeHandle<Object> MakeExecutionState();
+ MUST_USE_RESULT MaybeHandle<Object> MakeBreakEvent(
+ Handle<Object> break_points_hit);
+ MUST_USE_RESULT MaybeHandle<Object> MakeExceptionEvent(
+ Handle<Object> exception,
+ bool uncaught,
+ Handle<Object> promise);
+ MUST_USE_RESULT MaybeHandle<Object> MakeCompileEvent(
+ Handle<Script> script, v8::DebugEvent type);
+ MUST_USE_RESULT MaybeHandle<Object> MakePromiseEvent(
+ Handle<JSObject> promise_event);
+ MUST_USE_RESULT MaybeHandle<Object> MakeAsyncTaskEvent(
+ Handle<JSObject> task_event);
+
+ // Mirror cache handling.
+ void ClearMirrorCache();
+
+ MaybeHandle<Object> PromiseHasUserDefinedRejectHandler(
+ Handle<JSObject> promise);
+
+ void CallEventCallback(v8::DebugEvent event,
+ Handle<Object> exec_state,
+ Handle<Object> event_data,
+ v8::Debug::ClientData* client_data);
+ void ProcessCompileEventInDebugScope(v8::DebugEvent event,
+ Handle<Script> script);
+ void ProcessDebugEvent(v8::DebugEvent event,
+ Handle<JSObject> event_data,
+ bool auto_continue);
+ void NotifyMessageHandler(v8::DebugEvent event,
+ Handle<JSObject> exec_state,
+ Handle<JSObject> event_data,
+ bool auto_continue);
+ void InvokeMessageHandler(MessageImpl message);
+
+ static bool CompileDebuggerScript(Isolate* isolate, int index);
+ void ClearOneShot();
+ void ActivateStepIn(StackFrame* frame);
+ void ClearStepIn();
+ void ActivateStepOut(StackFrame* frame);
+ void ClearStepNext();
+ void RemoveDebugInfoAndClearFromShared(Handle<DebugInfo> debug_info);
+ Handle<Object> CheckBreakPoints(Handle<Object> break_point);
+ bool CheckBreakPoint(Handle<Object> break_point_object);
+
+ inline void AssertDebugContext() {
+ DCHECK(isolate_->context() == *debug_context());
+ DCHECK(in_debug_scope());
+ }
+
+ void ThreadInit();
+
+ // Global handles.
+ Handle<Context> debug_context_;
+ Handle<Object> event_listener_;
+ Handle<Object> event_listener_data_;
+
+ v8::Debug::MessageHandler message_handler_;
+
+ static const int kQueueInitialSize = 4;
+ base::Semaphore command_received_; // Signaled for each command received.
+ LockingCommandMessageQueue command_queue_;
+
+ bool is_active_;
+ bool is_suppressed_;
+ bool live_edit_enabled_;
+ bool has_break_points_;
+ bool break_disabled_;
+ bool in_debug_event_listener_;
+ bool break_on_exception_;
+ bool break_on_uncaught_exception_;
+
+ ScriptCache* script_cache_; // Cache of all scripts in the heap.
+ DebugInfoListNode* debug_info_list_; // List of active debug info objects.
+
+ // Storage location for jump when exiting debug break calls.
+ // Note that this address is not GC safe. It should be computed immediately
+ // before returning to the DebugBreakCallHelper.
+ Address after_break_target_;
+
+ // Per-thread data.
+ class ThreadLocal {
+ public:
+ // Top debugger entry.
+ base::AtomicWord current_debug_scope_;
+
+ // Counter for generating next break id.
+ int break_count_;
+
+ // Current break id.
+ int break_id_;
+
+ // Frame id for the frame of the current break.
+ StackFrame::Id break_frame_id_;
+
+ // Step action for last step performed.
+ StepAction last_step_action_;
+
+ // Source statement position from last step next action.
+ int last_statement_position_;
+
+ // Number of steps left to perform before debug event.
+ int step_count_;
+
+ // Frame pointer from last step next or step frame action.
+ Address last_fp_;
+
+ // Number of queued steps left to perform before debug event.
+ int queued_step_count_;
+
+ // Frame pointer for frame from which step in was performed.
+ Address step_into_fp_;
+
+ // Frame pointer for the frame where debugger should be called when current
+ // step out action is completed.
+ Address step_out_fp_;
+
+ // Stores the way how LiveEdit has patched the stack. It is used when
+ // debugger returns control back to user script.
+ LiveEdit::FrameDropMode frame_drop_mode_;
+
+ // When restarter frame is on stack, stores the address
+ // of the pointer to function being restarted. Otherwise (most of the time)
+ // stores NULL. This pointer is used with 'step in' implementation.
+ Object** restarter_frame_function_pointer_;
+ };
+
+ // Storage location for registers when handling debug break calls
+ ThreadLocal thread_local_;
+
+ Isolate* isolate_;
+
+ friend class Isolate;
+ friend class DebugScope;
+ friend class DisableBreak;
+ friend class LiveEdit;
+ friend class SuppressDebug;
+
+ friend Handle<FixedArray> GetDebuggedFunctions(); // In test-debug.cc
+ friend void CheckDebuggerUnloaded(bool check_functions); // In test-debug.cc
+
+ DISALLOW_COPY_AND_ASSIGN(Debug);
+};
+
+
+// This scope is used to load and enter the debug context and create a new
+// break state. Leaving the scope will restore the previous state.
+// On failure to load, FailedToEnter returns true.
+class DebugScope BASE_EMBEDDED {
+ public:
+ explicit DebugScope(Debug* debug);
+ ~DebugScope();
+
+ // Check whether loading was successful.
+ inline bool failed() { return failed_; }
+
+ // Get the active context from before entering the debugger.
+ inline Handle<Context> GetContext() { return save_.context(); }
+
+ private:
+ Isolate* isolate() { return debug_->isolate_; }
+
+ Debug* debug_;
+ DebugScope* prev_; // Previous scope if entered recursively.
+ StackFrame::Id break_frame_id_; // Previous break frame id.
+ int break_id_; // Previous break id.
+ bool failed_; // Did the debug context fail to load?
+ SaveContext save_; // Saves previous context.
+ PostponeInterruptsScope no_termination_exceptons_;
+};
+
+
+// Stack allocated class for disabling break.
+class DisableBreak BASE_EMBEDDED {
+ public:
+ explicit DisableBreak(Debug* debug, bool disable_break)
+ : debug_(debug),
+ previous_break_disabled_(debug->break_disabled_),
+ previous_in_debug_event_listener_(debug->in_debug_event_listener_) {
+ debug_->break_disabled_ = disable_break;
+ debug_->in_debug_event_listener_ = disable_break;
+ }
+ ~DisableBreak() {
+ debug_->break_disabled_ = previous_break_disabled_;
+ debug_->in_debug_event_listener_ = previous_in_debug_event_listener_;
+ }
+
+ private:
+ Debug* debug_;
+ bool previous_break_disabled_;
+ bool previous_in_debug_event_listener_;
+ DISALLOW_COPY_AND_ASSIGN(DisableBreak);
+};
+
+
+class SuppressDebug BASE_EMBEDDED {
+ public:
+ explicit SuppressDebug(Debug* debug)
+ : debug_(debug), old_state_(debug->is_suppressed_) {
+ debug_->is_suppressed_ = true;
+ }
+ ~SuppressDebug() { debug_->is_suppressed_ = old_state_; }
+
+ private:
+ Debug* debug_;
+ bool old_state_;
+ DISALLOW_COPY_AND_ASSIGN(SuppressDebug);
+};
+
+
+// Code generator routines.
+class DebugCodegen : public AllStatic {
+ public:
+ enum DebugBreakCallHelperMode {
+ SAVE_RESULT_REGISTER,
+ IGNORE_RESULT_REGISTER
+ };
+
+ static void GenerateDebugBreakStub(MacroAssembler* masm,
+ DebugBreakCallHelperMode mode);
+
+ static void GeneratePlainReturnLiveEdit(MacroAssembler* masm);
+
+ // FrameDropper is a code replacement for a JavaScript frame with possibly
+ // several frames above.
+ // There is no calling conventions here, because it never actually gets
+ // called, it only gets returned to.
+ static void GenerateFrameDropperLiveEdit(MacroAssembler* masm);
+
+
+ static void GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode,
+ int call_argc = -1);
+
+ static void PatchDebugBreakSlot(Address pc, Handle<Code> code);
+ static void ClearDebugBreakSlot(Address pc);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_DEBUG_DEBUG_H_
--- /dev/null
+// Copyright 2012 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.
+"use strict";
+
+// Default number of frames to include in the response to backtrace request.
+var kDefaultBacktraceLength = 10;
+
+var Debug = {};
+
+// Regular expression to skip "crud" at the beginning of a source line which is
+// not really code. Currently the regular expression matches whitespace and
+// comments.
+var sourceLineBeginningSkip = /^(?:\s*(?:\/\*.*?\*\/)*)*/;
+
+// Debug events which can occour in the V8 JavaScript engine. These originate
+// from the API include file debug.h.
+Debug.DebugEvent = { Break: 1,
+ Exception: 2,
+ NewFunction: 3,
+ BeforeCompile: 4,
+ AfterCompile: 5,
+ CompileError: 6,
+ PromiseEvent: 7,
+ AsyncTaskEvent: 8 };
+
+// Types of exceptions that can be broken upon.
+Debug.ExceptionBreak = { Caught : 0,
+ Uncaught: 1 };
+
+// The different types of steps.
+Debug.StepAction = { StepOut: 0,
+ StepNext: 1,
+ StepIn: 2,
+ StepMin: 3,
+ StepInMin: 4,
+ StepFrame: 5 };
+
+// The different types of scripts matching enum ScriptType in objects.h.
+Debug.ScriptType = { Native: 0,
+ Extension: 1,
+ Normal: 2 };
+
+// The different types of script compilations matching enum
+// Script::CompilationType in objects.h.
+Debug.ScriptCompilationType = { Host: 0,
+ Eval: 1,
+ JSON: 2 };
+
+// The different script break point types.
+Debug.ScriptBreakPointType = { ScriptId: 0,
+ ScriptName: 1,
+ ScriptRegExp: 2 };
+
+// The different types of breakpoint position alignments.
+// Must match BreakPositionAlignment in debug.h.
+Debug.BreakPositionAlignment = {
+ Statement: 0,
+ BreakPosition: 1
+};
+
+function ScriptTypeFlag(type) {
+ return (1 << type);
+}
+
+// Globals.
+var next_response_seq = 0;
+var next_break_point_number = 1;
+var break_points = [];
+var script_break_points = [];
+var debugger_flags = {
+ breakPointsActive: {
+ value: true,
+ getValue: function() { return this.value; },
+ setValue: function(value) {
+ this.value = !!value;
+ %SetDisableBreak(!this.value);
+ }
+ },
+ breakOnCaughtException: {
+ getValue: function() { return Debug.isBreakOnException(); },
+ setValue: function(value) {
+ if (value) {
+ Debug.setBreakOnException();
+ } else {
+ Debug.clearBreakOnException();
+ }
+ }
+ },
+ breakOnUncaughtException: {
+ getValue: function() { return Debug.isBreakOnUncaughtException(); },
+ setValue: function(value) {
+ if (value) {
+ Debug.setBreakOnUncaughtException();
+ } else {
+ Debug.clearBreakOnUncaughtException();
+ }
+ }
+ },
+};
+
+
+// Create a new break point object and add it to the list of break points.
+function MakeBreakPoint(source_position, opt_script_break_point) {
+ var break_point = new BreakPoint(source_position, opt_script_break_point);
+ break_points.push(break_point);
+ return break_point;
+}
+
+
+// Object representing a break point.
+// NOTE: This object does not have a reference to the function having break
+// point as this would cause function not to be garbage collected when it is
+// not used any more. We do not want break points to keep functions alive.
+function BreakPoint(source_position, opt_script_break_point) {
+ this.source_position_ = source_position;
+ if (opt_script_break_point) {
+ this.script_break_point_ = opt_script_break_point;
+ } else {
+ this.number_ = next_break_point_number++;
+ }
+ this.hit_count_ = 0;
+ this.active_ = true;
+ this.condition_ = null;
+ this.ignoreCount_ = 0;
+}
+
+
+BreakPoint.prototype.number = function() {
+ return this.number_;
+};
+
+
+BreakPoint.prototype.func = function() {
+ return this.func_;
+};
+
+
+BreakPoint.prototype.source_position = function() {
+ return this.source_position_;
+};
+
+
+BreakPoint.prototype.hit_count = function() {
+ return this.hit_count_;
+};
+
+
+BreakPoint.prototype.active = function() {
+ if (this.script_break_point()) {
+ return this.script_break_point().active();
+ }
+ return this.active_;
+};
+
+
+BreakPoint.prototype.condition = function() {
+ if (this.script_break_point() && this.script_break_point().condition()) {
+ return this.script_break_point().condition();
+ }
+ return this.condition_;
+};
+
+
+BreakPoint.prototype.ignoreCount = function() {
+ return this.ignoreCount_;
+};
+
+
+BreakPoint.prototype.script_break_point = function() {
+ return this.script_break_point_;
+};
+
+
+BreakPoint.prototype.enable = function() {
+ this.active_ = true;
+};
+
+
+BreakPoint.prototype.disable = function() {
+ this.active_ = false;
+};
+
+
+BreakPoint.prototype.setCondition = function(condition) {
+ this.condition_ = condition;
+};
+
+
+BreakPoint.prototype.setIgnoreCount = function(ignoreCount) {
+ this.ignoreCount_ = ignoreCount;
+};
+
+
+BreakPoint.prototype.isTriggered = function(exec_state) {
+ // Break point not active - not triggered.
+ if (!this.active()) return false;
+
+ // Check for conditional break point.
+ if (this.condition()) {
+ // If break point has condition try to evaluate it in the top frame.
+ try {
+ var mirror = exec_state.frame(0).evaluate(this.condition());
+ // If no sensible mirror or non true value break point not triggered.
+ if (!(mirror instanceof ValueMirror) ||
+ !builtins.$toBoolean(mirror.value_)) {
+ return false;
+ }
+ } catch (e) {
+ // Exception evaluating condition counts as not triggered.
+ return false;
+ }
+ }
+
+ // Update the hit count.
+ this.hit_count_++;
+ if (this.script_break_point_) {
+ this.script_break_point_.hit_count_++;
+ }
+
+ // If the break point has an ignore count it is not triggered.
+ if (this.ignoreCount_ > 0) {
+ this.ignoreCount_--;
+ return false;
+ }
+
+ // Break point triggered.
+ return true;
+};
+
+
+// Function called from the runtime when a break point is hit. Returns true if
+// the break point is triggered and supposed to break execution.
+function IsBreakPointTriggered(break_id, break_point) {
+ return break_point.isTriggered(MakeExecutionState(break_id));
+}
+
+
+// Object representing a script break point. The script is referenced by its
+// script name or script id and the break point is represented as line and
+// column.
+function ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column,
+ opt_groupId, opt_position_alignment) {
+ this.type_ = type;
+ if (type == Debug.ScriptBreakPointType.ScriptId) {
+ this.script_id_ = script_id_or_name;
+ } else if (type == Debug.ScriptBreakPointType.ScriptName) {
+ this.script_name_ = script_id_or_name;
+ } else if (type == Debug.ScriptBreakPointType.ScriptRegExp) {
+ this.script_regexp_object_ = new RegExp(script_id_or_name);
+ } else {
+ throw new Error("Unexpected breakpoint type " + type);
+ }
+ this.line_ = opt_line || 0;
+ this.column_ = opt_column;
+ this.groupId_ = opt_groupId;
+ this.position_alignment_ = IS_UNDEFINED(opt_position_alignment)
+ ? Debug.BreakPositionAlignment.Statement : opt_position_alignment;
+ this.hit_count_ = 0;
+ this.active_ = true;
+ this.condition_ = null;
+ this.ignoreCount_ = 0;
+ this.break_points_ = [];
+}
+
+
+// Creates a clone of script breakpoint that is linked to another script.
+ScriptBreakPoint.prototype.cloneForOtherScript = function (other_script) {
+ var copy = new ScriptBreakPoint(Debug.ScriptBreakPointType.ScriptId,
+ other_script.id, this.line_, this.column_, this.groupId_,
+ this.position_alignment_);
+ copy.number_ = next_break_point_number++;
+ script_break_points.push(copy);
+
+ copy.hit_count_ = this.hit_count_;
+ copy.active_ = this.active_;
+ copy.condition_ = this.condition_;
+ copy.ignoreCount_ = this.ignoreCount_;
+ return copy;
+};
+
+
+ScriptBreakPoint.prototype.number = function() {
+ return this.number_;
+};
+
+
+ScriptBreakPoint.prototype.groupId = function() {
+ return this.groupId_;
+};
+
+
+ScriptBreakPoint.prototype.type = function() {
+ return this.type_;
+};
+
+
+ScriptBreakPoint.prototype.script_id = function() {
+ return this.script_id_;
+};
+
+
+ScriptBreakPoint.prototype.script_name = function() {
+ return this.script_name_;
+};
+
+
+ScriptBreakPoint.prototype.script_regexp_object = function() {
+ return this.script_regexp_object_;
+};
+
+
+ScriptBreakPoint.prototype.line = function() {
+ return this.line_;
+};
+
+
+ScriptBreakPoint.prototype.column = function() {
+ return this.column_;
+};
+
+
+ScriptBreakPoint.prototype.actual_locations = function() {
+ var locations = [];
+ for (var i = 0; i < this.break_points_.length; i++) {
+ locations.push(this.break_points_[i].actual_location);
+ }
+ return locations;
+};
+
+
+ScriptBreakPoint.prototype.update_positions = function(line, column) {
+ this.line_ = line;
+ this.column_ = column;
+};
+
+
+ScriptBreakPoint.prototype.hit_count = function() {
+ return this.hit_count_;
+};
+
+
+ScriptBreakPoint.prototype.active = function() {
+ return this.active_;
+};
+
+
+ScriptBreakPoint.prototype.condition = function() {
+ return this.condition_;
+};
+
+
+ScriptBreakPoint.prototype.ignoreCount = function() {
+ return this.ignoreCount_;
+};
+
+
+ScriptBreakPoint.prototype.enable = function() {
+ this.active_ = true;
+};
+
+
+ScriptBreakPoint.prototype.disable = function() {
+ this.active_ = false;
+};
+
+
+ScriptBreakPoint.prototype.setCondition = function(condition) {
+ this.condition_ = condition;
+};
+
+
+ScriptBreakPoint.prototype.setIgnoreCount = function(ignoreCount) {
+ this.ignoreCount_ = ignoreCount;
+
+ // Set ignore count on all break points created from this script break point.
+ for (var i = 0; i < this.break_points_.length; i++) {
+ this.break_points_[i].setIgnoreCount(ignoreCount);
+ }
+};
+
+
+// Check whether a script matches this script break point. Currently this is
+// only based on script name.
+ScriptBreakPoint.prototype.matchesScript = function(script) {
+ if (this.type_ == Debug.ScriptBreakPointType.ScriptId) {
+ return this.script_id_ == script.id;
+ } else {
+ // We might want to account columns here as well.
+ if (!(script.line_offset <= this.line_ &&
+ this.line_ < script.line_offset + script.lineCount())) {
+ return false;
+ }
+ if (this.type_ == Debug.ScriptBreakPointType.ScriptName) {
+ return this.script_name_ == script.nameOrSourceURL();
+ } else if (this.type_ == Debug.ScriptBreakPointType.ScriptRegExp) {
+ return this.script_regexp_object_.test(script.nameOrSourceURL());
+ } else {
+ throw new Error("Unexpected breakpoint type " + this.type_);
+ }
+ }
+};
+
+
+// Set the script break point in a script.
+ScriptBreakPoint.prototype.set = function (script) {
+ var column = this.column();
+ var line = this.line();
+ // If the column is undefined the break is on the line. To help locate the
+ // first piece of breakable code on the line try to find the column on the
+ // line which contains some source.
+ if (IS_UNDEFINED(column)) {
+ var source_line = script.sourceLine(this.line());
+
+ // Allocate array for caching the columns where the actual source starts.
+ if (!script.sourceColumnStart_) {
+ script.sourceColumnStart_ = new Array(script.lineCount());
+ }
+
+ // Fill cache if needed and get column where the actual source starts.
+ if (IS_UNDEFINED(script.sourceColumnStart_[line])) {
+ script.sourceColumnStart_[line] =
+ source_line.match(sourceLineBeginningSkip)[0].length;
+ }
+ column = script.sourceColumnStart_[line];
+ }
+
+ // Convert the line and column into an absolute position within the script.
+ var position = Debug.findScriptSourcePosition(script, this.line(), column);
+
+ // If the position is not found in the script (the script might be shorter
+ // than it used to be) just ignore it.
+ if (IS_NULL(position)) return;
+
+ // Create a break point object and set the break point.
+ var break_point = MakeBreakPoint(position, this);
+ break_point.setIgnoreCount(this.ignoreCount());
+ var actual_position = %SetScriptBreakPoint(script, position,
+ this.position_alignment_,
+ break_point);
+ if (IS_UNDEFINED(actual_position)) {
+ actual_position = position;
+ }
+ var actual_location = script.locationFromPosition(actual_position, true);
+ break_point.actual_location = { line: actual_location.line,
+ column: actual_location.column,
+ script_id: script.id };
+ this.break_points_.push(break_point);
+ return break_point;
+};
+
+
+// Clear all the break points created from this script break point
+ScriptBreakPoint.prototype.clear = function () {
+ var remaining_break_points = [];
+ for (var i = 0; i < break_points.length; i++) {
+ if (break_points[i].script_break_point() &&
+ break_points[i].script_break_point() === this) {
+ %ClearBreakPoint(break_points[i]);
+ } else {
+ remaining_break_points.push(break_points[i]);
+ }
+ }
+ break_points = remaining_break_points;
+ this.break_points_ = [];
+};
+
+
+// Function called from runtime when a new script is compiled to set any script
+// break points set in this script.
+function UpdateScriptBreakPoints(script) {
+ for (var i = 0; i < script_break_points.length; i++) {
+ var break_point = script_break_points[i];
+ if ((break_point.type() == Debug.ScriptBreakPointType.ScriptName ||
+ break_point.type() == Debug.ScriptBreakPointType.ScriptRegExp) &&
+ break_point.matchesScript(script)) {
+ break_point.set(script);
+ }
+ }
+}
+
+
+function GetScriptBreakPoints(script) {
+ var result = [];
+ for (var i = 0; i < script_break_points.length; i++) {
+ if (script_break_points[i].matchesScript(script)) {
+ result.push(script_break_points[i]);
+ }
+ }
+ return result;
+}
+
+
+Debug.setListener = function(listener, opt_data) {
+ if (!IS_FUNCTION(listener) && !IS_UNDEFINED(listener) && !IS_NULL(listener)) {
+ throw new Error('Parameters have wrong types.');
+ }
+ %SetDebugEventListener(listener, opt_data);
+};
+
+
+Debug.breakLocations = function(f, opt_position_aligment) {
+ if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
+ var position_aligment = IS_UNDEFINED(opt_position_aligment)
+ ? Debug.BreakPositionAlignment.Statement : opt_position_aligment;
+ return %GetBreakLocations(f, position_aligment);
+};
+
+// Returns a Script object. If the parameter is a function the return value
+// is the script in which the function is defined. If the parameter is a string
+// the return value is the script for which the script name has that string
+// value. If it is a regexp and there is a unique script whose name matches
+// we return that, otherwise undefined.
+Debug.findScript = function(func_or_script_name) {
+ if (IS_FUNCTION(func_or_script_name)) {
+ return %FunctionGetScript(func_or_script_name);
+ } else if (IS_REGEXP(func_or_script_name)) {
+ var scripts = Debug.scripts();
+ var last_result = null;
+ var result_count = 0;
+ for (var i in scripts) {
+ var script = scripts[i];
+ if (func_or_script_name.test(script.name)) {
+ last_result = script;
+ result_count++;
+ }
+ }
+ // Return the unique script matching the regexp. If there are more
+ // than one we don't return a value since there is no good way to
+ // decide which one to return. Returning a "random" one, say the
+ // first, would introduce nondeterminism (or something close to it)
+ // because the order is the heap iteration order.
+ if (result_count == 1) {
+ return last_result;
+ } else {
+ return undefined;
+ }
+ } else {
+ return %GetScript(func_or_script_name);
+ }
+};
+
+// Returns the script source. If the parameter is a function the return value
+// is the script source for the script in which the function is defined. If the
+// parameter is a string the return value is the script for which the script
+// name has that string value.
+Debug.scriptSource = function(func_or_script_name) {
+ return this.findScript(func_or_script_name).source;
+};
+
+
+Debug.source = function(f) {
+ if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
+ return %FunctionGetSourceCode(f);
+};
+
+
+Debug.sourcePosition = function(f) {
+ if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
+ return %FunctionGetScriptSourcePosition(f);
+};
+
+
+Debug.findFunctionSourceLocation = function(func, opt_line, opt_column) {
+ var script = %FunctionGetScript(func);
+ var script_offset = %FunctionGetScriptSourcePosition(func);
+ return script.locationFromLine(opt_line, opt_column, script_offset);
+};
+
+
+// Returns the character position in a script based on a line number and an
+// optional position within that line.
+Debug.findScriptSourcePosition = function(script, opt_line, opt_column) {
+ var location = script.locationFromLine(opt_line, opt_column);
+ return location ? location.position : null;
+};
+
+
+Debug.findBreakPoint = function(break_point_number, remove) {
+ var break_point;
+ for (var i = 0; i < break_points.length; i++) {
+ if (break_points[i].number() == break_point_number) {
+ break_point = break_points[i];
+ // Remove the break point from the list if requested.
+ if (remove) {
+ break_points.splice(i, 1);
+ }
+ break;
+ }
+ }
+ if (break_point) {
+ return break_point;
+ } else {
+ return this.findScriptBreakPoint(break_point_number, remove);
+ }
+};
+
+Debug.findBreakPointActualLocations = function(break_point_number) {
+ for (var i = 0; i < script_break_points.length; i++) {
+ if (script_break_points[i].number() == break_point_number) {
+ return script_break_points[i].actual_locations();
+ }
+ }
+ for (var i = 0; i < break_points.length; i++) {
+ if (break_points[i].number() == break_point_number) {
+ return [break_points[i].actual_location];
+ }
+ }
+ return [];
+};
+
+Debug.setBreakPoint = function(func, opt_line, opt_column, opt_condition) {
+ if (!IS_FUNCTION(func)) throw new Error('Parameters have wrong types.');
+ // Break points in API functions are not supported.
+ if (%FunctionIsAPIFunction(func)) {
+ throw new Error('Cannot set break point in native code.');
+ }
+ // Find source position relative to start of the function
+ var break_position =
+ this.findFunctionSourceLocation(func, opt_line, opt_column).position;
+ var source_position = break_position - this.sourcePosition(func);
+ // Find the script for the function.
+ var script = %FunctionGetScript(func);
+ // Break in builtin JavaScript code is not supported.
+ if (script.type == Debug.ScriptType.Native) {
+ throw new Error('Cannot set break point in native code.');
+ }
+ // If the script for the function has a name convert this to a script break
+ // point.
+ if (script && script.id) {
+ // Adjust the source position to be script relative.
+ source_position += %FunctionGetScriptSourcePosition(func);
+ // Find line and column for the position in the script and set a script
+ // break point from that.
+ var location = script.locationFromPosition(source_position, false);
+ return this.setScriptBreakPointById(script.id,
+ location.line, location.column,
+ opt_condition);
+ } else {
+ // Set a break point directly on the function.
+ var break_point = MakeBreakPoint(source_position);
+ var actual_position =
+ %SetFunctionBreakPoint(func, source_position, break_point);
+ actual_position += this.sourcePosition(func);
+ var actual_location = script.locationFromPosition(actual_position, true);
+ break_point.actual_location = { line: actual_location.line,
+ column: actual_location.column,
+ script_id: script.id };
+ break_point.setCondition(opt_condition);
+ return break_point.number();
+ }
+};
+
+
+Debug.setBreakPointByScriptIdAndPosition = function(script_id, position,
+ condition, enabled,
+ opt_position_alignment)
+{
+ var break_point = MakeBreakPoint(position);
+ break_point.setCondition(condition);
+ if (!enabled) {
+ break_point.disable();
+ }
+ var scripts = this.scripts();
+ var position_alignment = IS_UNDEFINED(opt_position_alignment)
+ ? Debug.BreakPositionAlignment.Statement : opt_position_alignment;
+ for (var i = 0; i < scripts.length; i++) {
+ if (script_id == scripts[i].id) {
+ break_point.actual_position = %SetScriptBreakPoint(scripts[i], position,
+ position_alignment, break_point);
+ break;
+ }
+ }
+ return break_point;
+};
+
+
+Debug.enableBreakPoint = function(break_point_number) {
+ var break_point = this.findBreakPoint(break_point_number, false);
+ // Only enable if the breakpoint hasn't been deleted:
+ if (break_point) {
+ break_point.enable();
+ }
+};
+
+
+Debug.disableBreakPoint = function(break_point_number) {
+ var break_point = this.findBreakPoint(break_point_number, false);
+ // Only enable if the breakpoint hasn't been deleted:
+ if (break_point) {
+ break_point.disable();
+ }
+};
+
+
+Debug.changeBreakPointCondition = function(break_point_number, condition) {
+ var break_point = this.findBreakPoint(break_point_number, false);
+ break_point.setCondition(condition);
+};
+
+
+Debug.changeBreakPointIgnoreCount = function(break_point_number, ignoreCount) {
+ if (ignoreCount < 0) {
+ throw new Error('Invalid argument');
+ }
+ var break_point = this.findBreakPoint(break_point_number, false);
+ break_point.setIgnoreCount(ignoreCount);
+};
+
+
+Debug.clearBreakPoint = function(break_point_number) {
+ var break_point = this.findBreakPoint(break_point_number, true);
+ if (break_point) {
+ return %ClearBreakPoint(break_point);
+ } else {
+ break_point = this.findScriptBreakPoint(break_point_number, true);
+ if (!break_point) {
+ throw new Error('Invalid breakpoint');
+ }
+ }
+};
+
+
+Debug.clearAllBreakPoints = function() {
+ for (var i = 0; i < break_points.length; i++) {
+ var break_point = break_points[i];
+ %ClearBreakPoint(break_point);
+ }
+ break_points = [];
+};
+
+
+Debug.disableAllBreakPoints = function() {
+ // Disable all user defined breakpoints:
+ for (var i = 1; i < next_break_point_number; i++) {
+ Debug.disableBreakPoint(i);
+ }
+ // Disable all exception breakpoints:
+ %ChangeBreakOnException(Debug.ExceptionBreak.Caught, false);
+ %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, false);
+};
+
+
+Debug.findScriptBreakPoint = function(break_point_number, remove) {
+ var script_break_point;
+ for (var i = 0; i < script_break_points.length; i++) {
+ if (script_break_points[i].number() == break_point_number) {
+ script_break_point = script_break_points[i];
+ // Remove the break point from the list if requested.
+ if (remove) {
+ script_break_point.clear();
+ script_break_points.splice(i,1);
+ }
+ break;
+ }
+ }
+ return script_break_point;
+};
+
+
+// Sets a breakpoint in a script identified through id or name at the
+// specified source line and column within that line.
+Debug.setScriptBreakPoint = function(type, script_id_or_name,
+ opt_line, opt_column, opt_condition,
+ opt_groupId, opt_position_alignment) {
+ // Create script break point object.
+ var script_break_point =
+ new ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column,
+ opt_groupId, opt_position_alignment);
+
+ // Assign number to the new script break point and add it.
+ script_break_point.number_ = next_break_point_number++;
+ script_break_point.setCondition(opt_condition);
+ script_break_points.push(script_break_point);
+
+ // Run through all scripts to see if this script break point matches any
+ // loaded scripts.
+ var scripts = this.scripts();
+ for (var i = 0; i < scripts.length; i++) {
+ if (script_break_point.matchesScript(scripts[i])) {
+ script_break_point.set(scripts[i]);
+ }
+ }
+
+ return script_break_point.number();
+};
+
+
+Debug.setScriptBreakPointById = function(script_id,
+ opt_line, opt_column,
+ opt_condition, opt_groupId,
+ opt_position_alignment) {
+ return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptId,
+ script_id, opt_line, opt_column,
+ opt_condition, opt_groupId,
+ opt_position_alignment);
+};
+
+
+Debug.setScriptBreakPointByName = function(script_name,
+ opt_line, opt_column,
+ opt_condition, opt_groupId) {
+ return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptName,
+ script_name, opt_line, opt_column,
+ opt_condition, opt_groupId);
+};
+
+
+Debug.setScriptBreakPointByRegExp = function(script_regexp,
+ opt_line, opt_column,
+ opt_condition, opt_groupId) {
+ return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptRegExp,
+ script_regexp, opt_line, opt_column,
+ opt_condition, opt_groupId);
+};
+
+
+Debug.enableScriptBreakPoint = function(break_point_number) {
+ var script_break_point = this.findScriptBreakPoint(break_point_number, false);
+ script_break_point.enable();
+};
+
+
+Debug.disableScriptBreakPoint = function(break_point_number) {
+ var script_break_point = this.findScriptBreakPoint(break_point_number, false);
+ script_break_point.disable();
+};
+
+
+Debug.changeScriptBreakPointCondition = function(
+ break_point_number, condition) {
+ var script_break_point = this.findScriptBreakPoint(break_point_number, false);
+ script_break_point.setCondition(condition);
+};
+
+
+Debug.changeScriptBreakPointIgnoreCount = function(
+ break_point_number, ignoreCount) {
+ if (ignoreCount < 0) {
+ throw new Error('Invalid argument');
+ }
+ var script_break_point = this.findScriptBreakPoint(break_point_number, false);
+ script_break_point.setIgnoreCount(ignoreCount);
+};
+
+
+Debug.scriptBreakPoints = function() {
+ return script_break_points;
+};
+
+
+Debug.clearStepping = function() {
+ %ClearStepping();
+};
+
+Debug.setBreakOnException = function() {
+ return %ChangeBreakOnException(Debug.ExceptionBreak.Caught, true);
+};
+
+Debug.clearBreakOnException = function() {
+ return %ChangeBreakOnException(Debug.ExceptionBreak.Caught, false);
+};
+
+Debug.isBreakOnException = function() {
+ return !!%IsBreakOnException(Debug.ExceptionBreak.Caught);
+};
+
+Debug.setBreakOnUncaughtException = function() {
+ return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, true);
+};
+
+Debug.clearBreakOnUncaughtException = function() {
+ return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, false);
+};
+
+Debug.isBreakOnUncaughtException = function() {
+ return !!%IsBreakOnException(Debug.ExceptionBreak.Uncaught);
+};
+
+Debug.showBreakPoints = function(f, full, opt_position_alignment) {
+ if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
+ var source = full ? this.scriptSource(f) : this.source(f);
+ var offset = full ? this.sourcePosition(f) : 0;
+ var locations = this.breakLocations(f, opt_position_alignment);
+ if (!locations) return source;
+ locations.sort(function(x, y) { return x - y; });
+ var result = "";
+ var prev_pos = 0;
+ var pos;
+ for (var i = 0; i < locations.length; i++) {
+ pos = locations[i] - offset;
+ result += source.slice(prev_pos, pos);
+ result += "[B" + i + "]";
+ prev_pos = pos;
+ }
+ pos = source.length;
+ result += source.substring(prev_pos, pos);
+ return result;
+};
+
+
+// Get all the scripts currently loaded. Locating all the scripts is based on
+// scanning the heap.
+Debug.scripts = function() {
+ // Collect all scripts in the heap.
+ return %DebugGetLoadedScripts();
+};
+
+
+Debug.debuggerFlags = function() {
+ return debugger_flags;
+};
+
+Debug.MakeMirror = MakeMirror;
+
+function MakeExecutionState(break_id) {
+ return new ExecutionState(break_id);
+}
+
+function ExecutionState(break_id) {
+ this.break_id = break_id;
+ this.selected_frame = 0;
+}
+
+ExecutionState.prototype.prepareStep = function(opt_action, opt_count,
+ opt_callframe) {
+ var action = Debug.StepAction.StepIn;
+ if (!IS_UNDEFINED(opt_action)) action = builtins.$toNumber(opt_action);
+ var count = opt_count ? builtins.$toNumber(opt_count) : 1;
+ var callFrameId = 0;
+ if (!IS_UNDEFINED(opt_callframe)) {
+ callFrameId = opt_callframe.details_.frameId();
+ }
+
+ return %PrepareStep(this.break_id, action, count, callFrameId);
+};
+
+ExecutionState.prototype.evaluateGlobal = function(source, disable_break,
+ opt_additional_context) {
+ return MakeMirror(%DebugEvaluateGlobal(this.break_id, source,
+ Boolean(disable_break),
+ opt_additional_context));
+};
+
+ExecutionState.prototype.frameCount = function() {
+ return %GetFrameCount(this.break_id);
+};
+
+ExecutionState.prototype.threadCount = function() {
+ return %GetThreadCount(this.break_id);
+};
+
+ExecutionState.prototype.frame = function(opt_index) {
+ // If no index supplied return the selected frame.
+ if (opt_index == null) opt_index = this.selected_frame;
+ if (opt_index < 0 || opt_index >= this.frameCount()) {
+ throw new Error('Illegal frame index.');
+ }
+ return new FrameMirror(this.break_id, opt_index);
+};
+
+ExecutionState.prototype.setSelectedFrame = function(index) {
+ var i = builtins.$toNumber(index);
+ if (i < 0 || i >= this.frameCount()) throw new Error('Illegal frame index.');
+ this.selected_frame = i;
+};
+
+ExecutionState.prototype.selectedFrame = function() {
+ return this.selected_frame;
+};
+
+ExecutionState.prototype.debugCommandProcessor = function(opt_is_running) {
+ return new DebugCommandProcessor(this, opt_is_running);
+};
+
+
+function MakeBreakEvent(break_id, break_points_hit) {
+ return new BreakEvent(break_id, break_points_hit);
+}
+
+
+function BreakEvent(break_id, break_points_hit) {
+ this.frame_ = new FrameMirror(break_id, 0);
+ this.break_points_hit_ = break_points_hit;
+}
+
+
+BreakEvent.prototype.eventType = function() {
+ return Debug.DebugEvent.Break;
+};
+
+
+BreakEvent.prototype.func = function() {
+ return this.frame_.func();
+};
+
+
+BreakEvent.prototype.sourceLine = function() {
+ return this.frame_.sourceLine();
+};
+
+
+BreakEvent.prototype.sourceColumn = function() {
+ return this.frame_.sourceColumn();
+};
+
+
+BreakEvent.prototype.sourceLineText = function() {
+ return this.frame_.sourceLineText();
+};
+
+
+BreakEvent.prototype.breakPointsHit = function() {
+ return this.break_points_hit_;
+};
+
+
+BreakEvent.prototype.toJSONProtocol = function() {
+ var o = { seq: next_response_seq++,
+ type: "event",
+ event: "break",
+ body: { invocationText: this.frame_.invocationText() }
+ };
+
+ // Add script related information to the event if available.
+ var script = this.func().script();
+ if (script) {
+ o.body.sourceLine = this.sourceLine(),
+ o.body.sourceColumn = this.sourceColumn(),
+ o.body.sourceLineText = this.sourceLineText(),
+ o.body.script = MakeScriptObject_(script, false);
+ }
+
+ // Add an Array of break points hit if any.
+ if (this.breakPointsHit()) {
+ o.body.breakpoints = [];
+ for (var i = 0; i < this.breakPointsHit().length; i++) {
+ // Find the break point number. For break points originating from a
+ // script break point supply the script break point number.
+ var breakpoint = this.breakPointsHit()[i];
+ var script_break_point = breakpoint.script_break_point();
+ var number;
+ if (script_break_point) {
+ number = script_break_point.number();
+ } else {
+ number = breakpoint.number();
+ }
+ o.body.breakpoints.push(number);
+ }
+ }
+ return JSON.stringify(ObjectToProtocolObject_(o));
+};
+
+
+function MakeExceptionEvent(break_id, exception, uncaught, promise) {
+ return new ExceptionEvent(break_id, exception, uncaught, promise);
+}
+
+
+function ExceptionEvent(break_id, exception, uncaught, promise) {
+ this.exec_state_ = new ExecutionState(break_id);
+ this.exception_ = exception;
+ this.uncaught_ = uncaught;
+ this.promise_ = promise;
+}
+
+
+ExceptionEvent.prototype.eventType = function() {
+ return Debug.DebugEvent.Exception;
+};
+
+
+ExceptionEvent.prototype.exception = function() {
+ return this.exception_;
+};
+
+
+ExceptionEvent.prototype.uncaught = function() {
+ return this.uncaught_;
+};
+
+
+ExceptionEvent.prototype.promise = function() {
+ return this.promise_;
+};
+
+
+ExceptionEvent.prototype.func = function() {
+ return this.exec_state_.frame(0).func();
+};
+
+
+ExceptionEvent.prototype.sourceLine = function() {
+ return this.exec_state_.frame(0).sourceLine();
+};
+
+
+ExceptionEvent.prototype.sourceColumn = function() {
+ return this.exec_state_.frame(0).sourceColumn();
+};
+
+
+ExceptionEvent.prototype.sourceLineText = function() {
+ return this.exec_state_.frame(0).sourceLineText();
+};
+
+
+ExceptionEvent.prototype.toJSONProtocol = function() {
+ var o = new ProtocolMessage();
+ o.event = "exception";
+ o.body = { uncaught: this.uncaught_,
+ exception: MakeMirror(this.exception_)
+ };
+
+ // Exceptions might happen whithout any JavaScript frames.
+ if (this.exec_state_.frameCount() > 0) {
+ o.body.sourceLine = this.sourceLine();
+ o.body.sourceColumn = this.sourceColumn();
+ o.body.sourceLineText = this.sourceLineText();
+
+ // Add script information to the event if available.
+ var script = this.func().script();
+ if (script) {
+ o.body.script = MakeScriptObject_(script, false);
+ }
+ } else {
+ o.body.sourceLine = -1;
+ }
+
+ return o.toJSONProtocol();
+};
+
+
+function MakeCompileEvent(script, type) {
+ return new CompileEvent(script, type);
+}
+
+
+function CompileEvent(script, type) {
+ this.script_ = MakeMirror(script);
+ this.type_ = type;
+}
+
+
+CompileEvent.prototype.eventType = function() {
+ return this.type_;
+};
+
+
+CompileEvent.prototype.script = function() {
+ return this.script_;
+};
+
+
+CompileEvent.prototype.toJSONProtocol = function() {
+ var o = new ProtocolMessage();
+ o.running = true;
+ switch (this.type_) {
+ case Debug.DebugEvent.BeforeCompile:
+ o.event = "beforeCompile";
+ break;
+ case Debug.DebugEvent.AfterCompile:
+ o.event = "afterCompile";
+ break;
+ case Debug.DebugEvent.CompileError:
+ o.event = "compileError";
+ break;
+ }
+ o.body = {};
+ o.body.script = this.script_;
+
+ return o.toJSONProtocol();
+};
+
+
+function MakeScriptObject_(script, include_source) {
+ var o = { id: script.id(),
+ name: script.name(),
+ lineOffset: script.lineOffset(),
+ columnOffset: script.columnOffset(),
+ lineCount: script.lineCount(),
+ };
+ if (!IS_UNDEFINED(script.data())) {
+ o.data = script.data();
+ }
+ if (include_source) {
+ o.source = script.source();
+ }
+ return o;
+}
+
+
+function MakePromiseEvent(event_data) {
+ return new PromiseEvent(event_data);
+}
+
+
+function PromiseEvent(event_data) {
+ this.promise_ = event_data.promise;
+ this.parentPromise_ = event_data.parentPromise;
+ this.status_ = event_data.status;
+ this.value_ = event_data.value;
+}
+
+
+PromiseEvent.prototype.promise = function() {
+ return MakeMirror(this.promise_);
+}
+
+
+PromiseEvent.prototype.parentPromise = function() {
+ return MakeMirror(this.parentPromise_);
+}
+
+
+PromiseEvent.prototype.status = function() {
+ return this.status_;
+}
+
+
+PromiseEvent.prototype.value = function() {
+ return MakeMirror(this.value_);
+}
+
+
+function MakeAsyncTaskEvent(event_data) {
+ return new AsyncTaskEvent(event_data);
+}
+
+
+function AsyncTaskEvent(event_data) {
+ this.type_ = event_data.type;
+ this.name_ = event_data.name;
+ this.id_ = event_data.id;
+}
+
+
+AsyncTaskEvent.prototype.type = function() {
+ return this.type_;
+}
+
+
+AsyncTaskEvent.prototype.name = function() {
+ return this.name_;
+}
+
+
+AsyncTaskEvent.prototype.id = function() {
+ return this.id_;
+}
+
+
+function DebugCommandProcessor(exec_state, opt_is_running) {
+ this.exec_state_ = exec_state;
+ this.running_ = opt_is_running || false;
+}
+
+
+DebugCommandProcessor.prototype.processDebugRequest = function (request) {
+ return this.processDebugJSONRequest(request);
+};
+
+
+function ProtocolMessage(request) {
+ // Update sequence number.
+ this.seq = next_response_seq++;
+
+ if (request) {
+ // If message is based on a request this is a response. Fill the initial
+ // response from the request.
+ this.type = 'response';
+ this.request_seq = request.seq;
+ this.command = request.command;
+ } else {
+ // If message is not based on a request it is a dabugger generated event.
+ this.type = 'event';
+ }
+ this.success = true;
+ // Handler may set this field to control debugger state.
+ this.running = undefined;
+}
+
+
+ProtocolMessage.prototype.setOption = function(name, value) {
+ if (!this.options_) {
+ this.options_ = {};
+ }
+ this.options_[name] = value;
+};
+
+
+ProtocolMessage.prototype.failed = function(message, opt_details) {
+ this.success = false;
+ this.message = message;
+ if (IS_OBJECT(opt_details)) {
+ this.error_details = opt_details;
+ }
+};
+
+
+ProtocolMessage.prototype.toJSONProtocol = function() {
+ // Encode the protocol header.
+ var json = {};
+ json.seq= this.seq;
+ if (this.request_seq) {
+ json.request_seq = this.request_seq;
+ }
+ json.type = this.type;
+ if (this.event) {
+ json.event = this.event;
+ }
+ if (this.command) {
+ json.command = this.command;
+ }
+ if (this.success) {
+ json.success = this.success;
+ } else {
+ json.success = false;
+ }
+ if (this.body) {
+ // Encode the body part.
+ var bodyJson;
+ var serializer = MakeMirrorSerializer(true, this.options_);
+ if (this.body instanceof Mirror) {
+ bodyJson = serializer.serializeValue(this.body);
+ } else if (this.body instanceof Array) {
+ bodyJson = [];
+ for (var i = 0; i < this.body.length; i++) {
+ if (this.body[i] instanceof Mirror) {
+ bodyJson.push(serializer.serializeValue(this.body[i]));
+ } else {
+ bodyJson.push(ObjectToProtocolObject_(this.body[i], serializer));
+ }
+ }
+ } else {
+ bodyJson = ObjectToProtocolObject_(this.body, serializer);
+ }
+ json.body = bodyJson;
+ json.refs = serializer.serializeReferencedObjects();
+ }
+ if (this.message) {
+ json.message = this.message;
+ }
+ if (this.error_details) {
+ json.error_details = this.error_details;
+ }
+ json.running = this.running;
+ return JSON.stringify(json);
+};
+
+
+DebugCommandProcessor.prototype.createResponse = function(request) {
+ return new ProtocolMessage(request);
+};
+
+
+DebugCommandProcessor.prototype.processDebugJSONRequest = function(
+ json_request) {
+ var request; // Current request.
+ var response; // Generated response.
+ try {
+ try {
+ // Convert the JSON string to an object.
+ request = JSON.parse(json_request);
+
+ // Create an initial response.
+ response = this.createResponse(request);
+
+ if (!request.type) {
+ throw new Error('Type not specified');
+ }
+
+ if (request.type != 'request') {
+ throw new Error("Illegal type '" + request.type + "' in request");
+ }
+
+ if (!request.command) {
+ throw new Error('Command not specified');
+ }
+
+ if (request.arguments) {
+ var args = request.arguments;
+ // TODO(yurys): remove request.arguments.compactFormat check once
+ // ChromeDevTools are switched to 'inlineRefs'
+ if (args.inlineRefs || args.compactFormat) {
+ response.setOption('inlineRefs', true);
+ }
+ if (!IS_UNDEFINED(args.maxStringLength)) {
+ response.setOption('maxStringLength', args.maxStringLength);
+ }
+ }
+
+ var key = request.command.toLowerCase();
+ var handler = DebugCommandProcessor.prototype.dispatch_[key];
+ if (IS_FUNCTION(handler)) {
+ %_CallFunction(this, request, response, handler);
+ } else {
+ throw new Error('Unknown command "' + request.command + '" in request');
+ }
+ } catch (e) {
+ // If there is no response object created one (without command).
+ if (!response) {
+ response = this.createResponse();
+ }
+ response.success = false;
+ response.message = builtins.$toString(e);
+ }
+
+ // Return the response as a JSON encoded string.
+ try {
+ if (!IS_UNDEFINED(response.running)) {
+ // Response controls running state.
+ this.running_ = response.running;
+ }
+ response.running = this.running_;
+ return response.toJSONProtocol();
+ } catch (e) {
+ // Failed to generate response - return generic error.
+ return '{"seq":' + response.seq + ',' +
+ '"request_seq":' + request.seq + ',' +
+ '"type":"response",' +
+ '"success":false,' +
+ '"message":"Internal error: ' + builtins.$toString(e) + '"}';
+ }
+ } catch (e) {
+ // Failed in one of the catch blocks above - most generic error.
+ return '{"seq":0,"type":"response","success":false,"message":"Internal error"}';
+ }
+};
+
+
+DebugCommandProcessor.prototype.continueRequest_ = function(request, response) {
+ // Check for arguments for continue.
+ if (request.arguments) {
+ var count = 1;
+ var action = Debug.StepAction.StepIn;
+
+ // Pull out arguments.
+ var stepaction = request.arguments.stepaction;
+ var stepcount = request.arguments.stepcount;
+
+ // Get the stepcount argument if any.
+ if (stepcount) {
+ count = builtins.$toNumber(stepcount);
+ if (count < 0) {
+ throw new Error('Invalid stepcount argument "' + stepcount + '".');
+ }
+ }
+
+ // Get the stepaction argument.
+ if (stepaction) {
+ if (stepaction == 'in') {
+ action = Debug.StepAction.StepIn;
+ } else if (stepaction == 'min') {
+ action = Debug.StepAction.StepMin;
+ } else if (stepaction == 'next') {
+ action = Debug.StepAction.StepNext;
+ } else if (stepaction == 'out') {
+ action = Debug.StepAction.StepOut;
+ } else {
+ throw new Error('Invalid stepaction argument "' + stepaction + '".');
+ }
+ }
+
+ // Set up the VM for stepping.
+ this.exec_state_.prepareStep(action, count);
+ }
+
+ // VM should be running after executing this request.
+ response.running = true;
+};
+
+
+DebugCommandProcessor.prototype.breakRequest_ = function(request, response) {
+ // Ignore as break command does not do anything when broken.
+};
+
+
+DebugCommandProcessor.prototype.setBreakPointRequest_ =
+ function(request, response) {
+ // Check for legal request.
+ if (!request.arguments) {
+ response.failed('Missing arguments');
+ return;
+ }
+
+ // Pull out arguments.
+ var type = request.arguments.type;
+ var target = request.arguments.target;
+ var line = request.arguments.line;
+ var column = request.arguments.column;
+ var enabled = IS_UNDEFINED(request.arguments.enabled) ?
+ true : request.arguments.enabled;
+ var condition = request.arguments.condition;
+ var ignoreCount = request.arguments.ignoreCount;
+ var groupId = request.arguments.groupId;
+
+ // Check for legal arguments.
+ if (!type || IS_UNDEFINED(target)) {
+ response.failed('Missing argument "type" or "target"');
+ return;
+ }
+
+ // Either function or script break point.
+ var break_point_number;
+ if (type == 'function') {
+ // Handle function break point.
+ if (!IS_STRING(target)) {
+ response.failed('Argument "target" is not a string value');
+ return;
+ }
+ var f;
+ try {
+ // Find the function through a global evaluate.
+ f = this.exec_state_.evaluateGlobal(target).value();
+ } catch (e) {
+ response.failed('Error: "' + builtins.$toString(e) +
+ '" evaluating "' + target + '"');
+ return;
+ }
+ if (!IS_FUNCTION(f)) {
+ response.failed('"' + target + '" does not evaluate to a function');
+ return;
+ }
+
+ // Set function break point.
+ break_point_number = Debug.setBreakPoint(f, line, column, condition);
+ } else if (type == 'handle') {
+ // Find the object pointed by the specified handle.
+ var handle = parseInt(target, 10);
+ var mirror = LookupMirror(handle);
+ if (!mirror) {
+ return response.failed('Object #' + handle + '# not found');
+ }
+ if (!mirror.isFunction()) {
+ return response.failed('Object #' + handle + '# is not a function');
+ }
+
+ // Set function break point.
+ break_point_number = Debug.setBreakPoint(mirror.value(),
+ line, column, condition);
+ } else if (type == 'script') {
+ // set script break point.
+ break_point_number =
+ Debug.setScriptBreakPointByName(target, line, column, condition,
+ groupId);
+ } else if (type == 'scriptId') {
+ break_point_number =
+ Debug.setScriptBreakPointById(target, line, column, condition, groupId);
+ } else if (type == 'scriptRegExp') {
+ break_point_number =
+ Debug.setScriptBreakPointByRegExp(target, line, column, condition,
+ groupId);
+ } else {
+ response.failed('Illegal type "' + type + '"');
+ return;
+ }
+
+ // Set additional break point properties.
+ var break_point = Debug.findBreakPoint(break_point_number);
+ if (ignoreCount) {
+ Debug.changeBreakPointIgnoreCount(break_point_number, ignoreCount);
+ }
+ if (!enabled) {
+ Debug.disableBreakPoint(break_point_number);
+ }
+
+ // Add the break point number to the response.
+ response.body = { type: type,
+ breakpoint: break_point_number };
+
+ // Add break point information to the response.
+ if (break_point instanceof ScriptBreakPoint) {
+ if (break_point.type() == Debug.ScriptBreakPointType.ScriptId) {
+ response.body.type = 'scriptId';
+ response.body.script_id = break_point.script_id();
+ } else if (break_point.type() == Debug.ScriptBreakPointType.ScriptName) {
+ response.body.type = 'scriptName';
+ response.body.script_name = break_point.script_name();
+ } else if (break_point.type() == Debug.ScriptBreakPointType.ScriptRegExp) {
+ response.body.type = 'scriptRegExp';
+ response.body.script_regexp = break_point.script_regexp_object().source;
+ } else {
+ throw new Error("Internal error: Unexpected breakpoint type: " +
+ break_point.type());
+ }
+ response.body.line = break_point.line();
+ response.body.column = break_point.column();
+ response.body.actual_locations = break_point.actual_locations();
+ } else {
+ response.body.type = 'function';
+ response.body.actual_locations = [break_point.actual_location];
+ }
+};
+
+
+DebugCommandProcessor.prototype.changeBreakPointRequest_ = function(
+ request, response) {
+ // Check for legal request.
+ if (!request.arguments) {
+ response.failed('Missing arguments');
+ return;
+ }
+
+ // Pull out arguments.
+ var break_point = builtins.$toNumber(request.arguments.breakpoint);
+ var enabled = request.arguments.enabled;
+ var condition = request.arguments.condition;
+ var ignoreCount = request.arguments.ignoreCount;
+
+ // Check for legal arguments.
+ if (!break_point) {
+ response.failed('Missing argument "breakpoint"');
+ return;
+ }
+
+ // Change enabled state if supplied.
+ if (!IS_UNDEFINED(enabled)) {
+ if (enabled) {
+ Debug.enableBreakPoint(break_point);
+ } else {
+ Debug.disableBreakPoint(break_point);
+ }
+ }
+
+ // Change condition if supplied
+ if (!IS_UNDEFINED(condition)) {
+ Debug.changeBreakPointCondition(break_point, condition);
+ }
+
+ // Change ignore count if supplied
+ if (!IS_UNDEFINED(ignoreCount)) {
+ Debug.changeBreakPointIgnoreCount(break_point, ignoreCount);
+ }
+};
+
+
+DebugCommandProcessor.prototype.clearBreakPointGroupRequest_ = function(
+ request, response) {
+ // Check for legal request.
+ if (!request.arguments) {
+ response.failed('Missing arguments');
+ return;
+ }
+
+ // Pull out arguments.
+ var group_id = request.arguments.groupId;
+
+ // Check for legal arguments.
+ if (!group_id) {
+ response.failed('Missing argument "groupId"');
+ return;
+ }
+
+ var cleared_break_points = [];
+ var new_script_break_points = [];
+ for (var i = 0; i < script_break_points.length; i++) {
+ var next_break_point = script_break_points[i];
+ if (next_break_point.groupId() == group_id) {
+ cleared_break_points.push(next_break_point.number());
+ next_break_point.clear();
+ } else {
+ new_script_break_points.push(next_break_point);
+ }
+ }
+ script_break_points = new_script_break_points;
+
+ // Add the cleared break point numbers to the response.
+ response.body = { breakpoints: cleared_break_points };
+};
+
+
+DebugCommandProcessor.prototype.clearBreakPointRequest_ = function(
+ request, response) {
+ // Check for legal request.
+ if (!request.arguments) {
+ response.failed('Missing arguments');
+ return;
+ }
+
+ // Pull out arguments.
+ var break_point = builtins.$toNumber(request.arguments.breakpoint);
+
+ // Check for legal arguments.
+ if (!break_point) {
+ response.failed('Missing argument "breakpoint"');
+ return;
+ }
+
+ // Clear break point.
+ Debug.clearBreakPoint(break_point);
+
+ // Add the cleared break point number to the response.
+ response.body = { breakpoint: break_point };
+};
+
+
+DebugCommandProcessor.prototype.listBreakpointsRequest_ = function(
+ request, response) {
+ var array = [];
+ for (var i = 0; i < script_break_points.length; i++) {
+ var break_point = script_break_points[i];
+
+ var description = {
+ number: break_point.number(),
+ line: break_point.line(),
+ column: break_point.column(),
+ groupId: break_point.groupId(),
+ hit_count: break_point.hit_count(),
+ active: break_point.active(),
+ condition: break_point.condition(),
+ ignoreCount: break_point.ignoreCount(),
+ actual_locations: break_point.actual_locations()
+ };
+
+ if (break_point.type() == Debug.ScriptBreakPointType.ScriptId) {
+ description.type = 'scriptId';
+ description.script_id = break_point.script_id();
+ } else if (break_point.type() == Debug.ScriptBreakPointType.ScriptName) {
+ description.type = 'scriptName';
+ description.script_name = break_point.script_name();
+ } else if (break_point.type() == Debug.ScriptBreakPointType.ScriptRegExp) {
+ description.type = 'scriptRegExp';
+ description.script_regexp = break_point.script_regexp_object().source;
+ } else {
+ throw new Error("Internal error: Unexpected breakpoint type: " +
+ break_point.type());
+ }
+ array.push(description);
+ }
+
+ response.body = {
+ breakpoints: array,
+ breakOnExceptions: Debug.isBreakOnException(),
+ breakOnUncaughtExceptions: Debug.isBreakOnUncaughtException()
+ };
+};
+
+
+DebugCommandProcessor.prototype.disconnectRequest_ =
+ function(request, response) {
+ Debug.disableAllBreakPoints();
+ this.continueRequest_(request, response);
+};
+
+
+DebugCommandProcessor.prototype.setExceptionBreakRequest_ =
+ function(request, response) {
+ // Check for legal request.
+ if (!request.arguments) {
+ response.failed('Missing arguments');
+ return;
+ }
+
+ // Pull out and check the 'type' argument:
+ var type = request.arguments.type;
+ if (!type) {
+ response.failed('Missing argument "type"');
+ return;
+ }
+
+ // Initialize the default value of enable:
+ var enabled;
+ if (type == 'all') {
+ enabled = !Debug.isBreakOnException();
+ } else if (type == 'uncaught') {
+ enabled = !Debug.isBreakOnUncaughtException();
+ }
+
+ // Pull out and check the 'enabled' argument if present:
+ if (!IS_UNDEFINED(request.arguments.enabled)) {
+ enabled = request.arguments.enabled;
+ if ((enabled != true) && (enabled != false)) {
+ response.failed('Illegal value for "enabled":"' + enabled + '"');
+ }
+ }
+
+ // Now set the exception break state:
+ if (type == 'all') {
+ %ChangeBreakOnException(Debug.ExceptionBreak.Caught, enabled);
+ } else if (type == 'uncaught') {
+ %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, enabled);
+ } else {
+ response.failed('Unknown "type":"' + type + '"');
+ }
+
+ // Add the cleared break point number to the response.
+ response.body = { 'type': type, 'enabled': enabled };
+};
+
+
+DebugCommandProcessor.prototype.backtraceRequest_ = function(
+ request, response) {
+ // Get the number of frames.
+ var total_frames = this.exec_state_.frameCount();
+
+ // Create simple response if there are no frames.
+ if (total_frames == 0) {
+ response.body = {
+ totalFrames: total_frames
+ };
+ return;
+ }
+
+ // Default frame range to include in backtrace.
+ var from_index = 0;
+ var to_index = kDefaultBacktraceLength;
+
+ // Get the range from the arguments.
+ if (request.arguments) {
+ if (request.arguments.fromFrame) {
+ from_index = request.arguments.fromFrame;
+ }
+ if (request.arguments.toFrame) {
+ to_index = request.arguments.toFrame;
+ }
+ if (request.arguments.bottom) {
+ var tmp_index = total_frames - from_index;
+ from_index = total_frames - to_index;
+ to_index = tmp_index;
+ }
+ if (from_index < 0 || to_index < 0) {
+ return response.failed('Invalid frame number');
+ }
+ }
+
+ // Adjust the index.
+ to_index = Math.min(total_frames, to_index);
+
+ if (to_index <= from_index) {
+ var error = 'Invalid frame range';
+ return response.failed(error);
+ }
+
+ // Create the response body.
+ var frames = [];
+ for (var i = from_index; i < to_index; i++) {
+ frames.push(this.exec_state_.frame(i));
+ }
+ response.body = {
+ fromFrame: from_index,
+ toFrame: to_index,
+ totalFrames: total_frames,
+ frames: frames
+ };
+};
+
+
+DebugCommandProcessor.prototype.frameRequest_ = function(request, response) {
+ // No frames no source.
+ if (this.exec_state_.frameCount() == 0) {
+ return response.failed('No frames');
+ }
+
+ // With no arguments just keep the selected frame.
+ if (request.arguments) {
+ var index = request.arguments.number;
+ if (index < 0 || this.exec_state_.frameCount() <= index) {
+ return response.failed('Invalid frame number');
+ }
+
+ this.exec_state_.setSelectedFrame(request.arguments.number);
+ }
+ response.body = this.exec_state_.frame();
+};
+
+
+DebugCommandProcessor.prototype.resolveFrameFromScopeDescription_ =
+ function(scope_description) {
+ // Get the frame for which the scope or scopes are requested.
+ // With no frameNumber argument use the currently selected frame.
+ if (scope_description && !IS_UNDEFINED(scope_description.frameNumber)) {
+ var frame_index = scope_description.frameNumber;
+ if (frame_index < 0 || this.exec_state_.frameCount() <= frame_index) {
+ throw new Error('Invalid frame number');
+ }
+ return this.exec_state_.frame(frame_index);
+ } else {
+ return this.exec_state_.frame();
+ }
+};
+
+
+// Gets scope host object from request. It is either a function
+// ('functionHandle' argument must be specified) or a stack frame
+// ('frameNumber' may be specified and the current frame is taken by default).
+DebugCommandProcessor.prototype.resolveScopeHolder_ =
+ function(scope_description) {
+ if (scope_description && "functionHandle" in scope_description) {
+ if (!IS_NUMBER(scope_description.functionHandle)) {
+ throw new Error('Function handle must be a number');
+ }
+ var function_mirror = LookupMirror(scope_description.functionHandle);
+ if (!function_mirror) {
+ throw new Error('Failed to find function object by handle');
+ }
+ if (!function_mirror.isFunction()) {
+ throw new Error('Value of non-function type is found by handle');
+ }
+ return function_mirror;
+ } else {
+ // No frames no scopes.
+ if (this.exec_state_.frameCount() == 0) {
+ throw new Error('No scopes');
+ }
+
+ // Get the frame for which the scopes are requested.
+ var frame = this.resolveFrameFromScopeDescription_(scope_description);
+ return frame;
+ }
+}
+
+
+DebugCommandProcessor.prototype.scopesRequest_ = function(request, response) {
+ var scope_holder = this.resolveScopeHolder_(request.arguments);
+
+ // Fill all scopes for this frame or function.
+ var total_scopes = scope_holder.scopeCount();
+ var scopes = [];
+ for (var i = 0; i < total_scopes; i++) {
+ scopes.push(scope_holder.scope(i));
+ }
+ response.body = {
+ fromScope: 0,
+ toScope: total_scopes,
+ totalScopes: total_scopes,
+ scopes: scopes
+ };
+};
+
+
+DebugCommandProcessor.prototype.scopeRequest_ = function(request, response) {
+ // Get the frame or function for which the scope is requested.
+ var scope_holder = this.resolveScopeHolder_(request.arguments);
+
+ // With no scope argument just return top scope.
+ var scope_index = 0;
+ if (request.arguments && !IS_UNDEFINED(request.arguments.number)) {
+ scope_index = builtins.$toNumber(request.arguments.number);
+ if (scope_index < 0 || scope_holder.scopeCount() <= scope_index) {
+ return response.failed('Invalid scope number');
+ }
+ }
+
+ response.body = scope_holder.scope(scope_index);
+};
+
+
+// Reads value from protocol description. Description may be in form of type
+// (for singletons), raw value (primitive types supported in JSON),
+// string value description plus type (for primitive values) or handle id.
+// Returns raw value or throws exception.
+DebugCommandProcessor.resolveValue_ = function(value_description) {
+ if ("handle" in value_description) {
+ var value_mirror = LookupMirror(value_description.handle);
+ if (!value_mirror) {
+ throw new Error("Failed to resolve value by handle, ' #" +
+ value_description.handle + "# not found");
+ }
+ return value_mirror.value();
+ } else if ("stringDescription" in value_description) {
+ if (value_description.type == BOOLEAN_TYPE) {
+ return Boolean(value_description.stringDescription);
+ } else if (value_description.type == NUMBER_TYPE) {
+ return Number(value_description.stringDescription);
+ } if (value_description.type == STRING_TYPE) {
+ return String(value_description.stringDescription);
+ } else {
+ throw new Error("Unknown type");
+ }
+ } else if ("value" in value_description) {
+ return value_description.value;
+ } else if (value_description.type == UNDEFINED_TYPE) {
+ return UNDEFINED;
+ } else if (value_description.type == NULL_TYPE) {
+ return null;
+ } else {
+ throw new Error("Failed to parse value description");
+ }
+};
+
+
+DebugCommandProcessor.prototype.setVariableValueRequest_ =
+ function(request, response) {
+ if (!request.arguments) {
+ response.failed('Missing arguments');
+ return;
+ }
+
+ if (IS_UNDEFINED(request.arguments.name)) {
+ response.failed('Missing variable name');
+ }
+ var variable_name = request.arguments.name;
+
+ var scope_description = request.arguments.scope;
+
+ // Get the frame or function for which the scope is requested.
+ var scope_holder = this.resolveScopeHolder_(scope_description);
+
+ if (IS_UNDEFINED(scope_description.number)) {
+ response.failed('Missing scope number');
+ }
+ var scope_index = builtins.$toNumber(scope_description.number);
+
+ var scope = scope_holder.scope(scope_index);
+
+ var new_value =
+ DebugCommandProcessor.resolveValue_(request.arguments.newValue);
+
+ scope.setVariableValue(variable_name, new_value);
+
+ var new_value_mirror = MakeMirror(new_value);
+
+ response.body = {
+ newValue: new_value_mirror
+ };
+};
+
+
+DebugCommandProcessor.prototype.evaluateRequest_ = function(request, response) {
+ if (!request.arguments) {
+ return response.failed('Missing arguments');
+ }
+
+ // Pull out arguments.
+ var expression = request.arguments.expression;
+ var frame = request.arguments.frame;
+ var global = request.arguments.global;
+ var disable_break = request.arguments.disable_break;
+ var additional_context = request.arguments.additional_context;
+
+ // The expression argument could be an integer so we convert it to a
+ // string.
+ try {
+ expression = String(expression);
+ } catch(e) {
+ return response.failed('Failed to convert expression argument to string');
+ }
+
+ // Check for legal arguments.
+ if (!IS_UNDEFINED(frame) && global) {
+ return response.failed('Arguments "frame" and "global" are exclusive');
+ }
+
+ var additional_context_object;
+ if (additional_context) {
+ additional_context_object = {};
+ for (var i = 0; i < additional_context.length; i++) {
+ var mapping = additional_context[i];
+
+ if (!IS_STRING(mapping.name)) {
+ return response.failed("Context element #" + i +
+ " doesn't contain name:string property");
+ }
+
+ var raw_value = DebugCommandProcessor.resolveValue_(mapping);
+ additional_context_object[mapping.name] = raw_value;
+ }
+ }
+
+ // Global evaluate.
+ if (global) {
+ // Evaluate in the native context.
+ response.body = this.exec_state_.evaluateGlobal(
+ expression, Boolean(disable_break), additional_context_object);
+ return;
+ }
+
+ // Default value for disable_break is true.
+ if (IS_UNDEFINED(disable_break)) {
+ disable_break = true;
+ }
+
+ // No frames no evaluate in frame.
+ if (this.exec_state_.frameCount() == 0) {
+ return response.failed('No frames');
+ }
+
+ // Check whether a frame was specified.
+ if (!IS_UNDEFINED(frame)) {
+ var frame_number = builtins.$toNumber(frame);
+ if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) {
+ return response.failed('Invalid frame "' + frame + '"');
+ }
+ // Evaluate in the specified frame.
+ response.body = this.exec_state_.frame(frame_number).evaluate(
+ expression, Boolean(disable_break), additional_context_object);
+ return;
+ } else {
+ // Evaluate in the selected frame.
+ response.body = this.exec_state_.frame().evaluate(
+ expression, Boolean(disable_break), additional_context_object);
+ return;
+ }
+};
+
+
+DebugCommandProcessor.prototype.lookupRequest_ = function(request, response) {
+ if (!request.arguments) {
+ return response.failed('Missing arguments');
+ }
+
+ // Pull out arguments.
+ var handles = request.arguments.handles;
+
+ // Check for legal arguments.
+ if (IS_UNDEFINED(handles)) {
+ return response.failed('Argument "handles" missing');
+ }
+
+ // Set 'includeSource' option for script lookup.
+ if (!IS_UNDEFINED(request.arguments.includeSource)) {
+ var includeSource = builtins.$toBoolean(request.arguments.includeSource);
+ response.setOption('includeSource', includeSource);
+ }
+
+ // Lookup handles.
+ var mirrors = {};
+ for (var i = 0; i < handles.length; i++) {
+ var handle = handles[i];
+ var mirror = LookupMirror(handle);
+ if (!mirror) {
+ return response.failed('Object #' + handle + '# not found');
+ }
+ mirrors[handle] = mirror;
+ }
+ response.body = mirrors;
+};
+
+
+DebugCommandProcessor.prototype.referencesRequest_ =
+ function(request, response) {
+ if (!request.arguments) {
+ return response.failed('Missing arguments');
+ }
+
+ // Pull out arguments.
+ var type = request.arguments.type;
+ var handle = request.arguments.handle;
+
+ // Check for legal arguments.
+ if (IS_UNDEFINED(type)) {
+ return response.failed('Argument "type" missing');
+ }
+ if (IS_UNDEFINED(handle)) {
+ return response.failed('Argument "handle" missing');
+ }
+ if (type != 'referencedBy' && type != 'constructedBy') {
+ return response.failed('Invalid type "' + type + '"');
+ }
+
+ // Lookup handle and return objects with references the object.
+ var mirror = LookupMirror(handle);
+ if (mirror) {
+ if (type == 'referencedBy') {
+ response.body = mirror.referencedBy();
+ } else {
+ response.body = mirror.constructedBy();
+ }
+ } else {
+ return response.failed('Object #' + handle + '# not found');
+ }
+};
+
+
+DebugCommandProcessor.prototype.sourceRequest_ = function(request, response) {
+ // No frames no source.
+ if (this.exec_state_.frameCount() == 0) {
+ return response.failed('No source');
+ }
+
+ var from_line;
+ var to_line;
+ var frame = this.exec_state_.frame();
+ if (request.arguments) {
+ // Pull out arguments.
+ from_line = request.arguments.fromLine;
+ to_line = request.arguments.toLine;
+
+ if (!IS_UNDEFINED(request.arguments.frame)) {
+ var frame_number = builtins.$toNumber(request.arguments.frame);
+ if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) {
+ return response.failed('Invalid frame "' + frame + '"');
+ }
+ frame = this.exec_state_.frame(frame_number);
+ }
+ }
+
+ // Get the script selected.
+ var script = frame.func().script();
+ if (!script) {
+ return response.failed('No source');
+ }
+
+ // Get the source slice and fill it into the response.
+ var slice = script.sourceSlice(from_line, to_line);
+ if (!slice) {
+ return response.failed('Invalid line interval');
+ }
+ response.body = {};
+ response.body.source = slice.sourceText();
+ response.body.fromLine = slice.from_line;
+ response.body.toLine = slice.to_line;
+ response.body.fromPosition = slice.from_position;
+ response.body.toPosition = slice.to_position;
+ response.body.totalLines = script.lineCount();
+};
+
+
+DebugCommandProcessor.prototype.scriptsRequest_ = function(request, response) {
+ var types = ScriptTypeFlag(Debug.ScriptType.Normal);
+ var includeSource = false;
+ var idsToInclude = null;
+ if (request.arguments) {
+ // Pull out arguments.
+ if (!IS_UNDEFINED(request.arguments.types)) {
+ types = builtins.$toNumber(request.arguments.types);
+ if (isNaN(types) || types < 0) {
+ return response.failed('Invalid types "' +
+ request.arguments.types + '"');
+ }
+ }
+
+ if (!IS_UNDEFINED(request.arguments.includeSource)) {
+ includeSource = builtins.$toBoolean(request.arguments.includeSource);
+ response.setOption('includeSource', includeSource);
+ }
+
+ if (IS_ARRAY(request.arguments.ids)) {
+ idsToInclude = {};
+ var ids = request.arguments.ids;
+ for (var i = 0; i < ids.length; i++) {
+ idsToInclude[ids[i]] = true;
+ }
+ }
+
+ var filterStr = null;
+ var filterNum = null;
+ if (!IS_UNDEFINED(request.arguments.filter)) {
+ var num = builtins.$toNumber(request.arguments.filter);
+ if (!isNaN(num)) {
+ filterNum = num;
+ }
+ filterStr = request.arguments.filter;
+ }
+ }
+
+ // Collect all scripts in the heap.
+ var scripts = %DebugGetLoadedScripts();
+
+ response.body = [];
+
+ for (var i = 0; i < scripts.length; i++) {
+ if (idsToInclude && !idsToInclude[scripts[i].id]) {
+ continue;
+ }
+ if (filterStr || filterNum) {
+ var script = scripts[i];
+ var found = false;
+ if (filterNum && !found) {
+ if (script.id && script.id === filterNum) {
+ found = true;
+ }
+ }
+ if (filterStr && !found) {
+ if (script.name && script.name.indexOf(filterStr) >= 0) {
+ found = true;
+ }
+ }
+ if (!found) continue;
+ }
+ if (types & ScriptTypeFlag(scripts[i].type)) {
+ response.body.push(MakeMirror(scripts[i]));
+ }
+ }
+};
+
+
+DebugCommandProcessor.prototype.threadsRequest_ = function(request, response) {
+ // Get the number of threads.
+ var total_threads = this.exec_state_.threadCount();
+
+ // Get information for all threads.
+ var threads = [];
+ for (var i = 0; i < total_threads; i++) {
+ var details = %GetThreadDetails(this.exec_state_.break_id, i);
+ var thread_info = { current: details[0],
+ id: details[1]
+ };
+ threads.push(thread_info);
+ }
+
+ // Create the response body.
+ response.body = {
+ totalThreads: total_threads,
+ threads: threads
+ };
+};
+
+
+DebugCommandProcessor.prototype.suspendRequest_ = function(request, response) {
+ response.running = false;
+};
+
+
+DebugCommandProcessor.prototype.versionRequest_ = function(request, response) {
+ response.body = {
+ V8Version: %GetV8Version()
+ };
+};
+
+
+DebugCommandProcessor.prototype.changeLiveRequest_ = function(
+ request, response) {
+ if (!request.arguments) {
+ return response.failed('Missing arguments');
+ }
+ var script_id = request.arguments.script_id;
+ var preview_only = !!request.arguments.preview_only;
+
+ var scripts = %DebugGetLoadedScripts();
+
+ var the_script = null;
+ for (var i = 0; i < scripts.length; i++) {
+ if (scripts[i].id == script_id) {
+ the_script = scripts[i];
+ }
+ }
+ if (!the_script) {
+ response.failed('Script not found');
+ return;
+ }
+
+ var change_log = new Array();
+
+ if (!IS_STRING(request.arguments.new_source)) {
+ throw "new_source argument expected";
+ }
+
+ var new_source = request.arguments.new_source;
+
+ var result_description;
+ try {
+ result_description = Debug.LiveEdit.SetScriptSource(the_script,
+ new_source, preview_only, change_log);
+ } catch (e) {
+ if (e instanceof Debug.LiveEdit.Failure && "details" in e) {
+ response.failed(e.message, e.details);
+ return;
+ }
+ throw e;
+ }
+ response.body = {change_log: change_log, result: result_description};
+
+ if (!preview_only && !this.running_ && result_description.stack_modified) {
+ response.body.stepin_recommended = true;
+ }
+};
+
+
+DebugCommandProcessor.prototype.restartFrameRequest_ = function(
+ request, response) {
+ if (!request.arguments) {
+ return response.failed('Missing arguments');
+ }
+ var frame = request.arguments.frame;
+
+ // No frames to evaluate in frame.
+ if (this.exec_state_.frameCount() == 0) {
+ return response.failed('No frames');
+ }
+
+ var frame_mirror;
+ // Check whether a frame was specified.
+ if (!IS_UNDEFINED(frame)) {
+ var frame_number = builtins.$toNumber(frame);
+ if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) {
+ return response.failed('Invalid frame "' + frame + '"');
+ }
+ // Restart specified frame.
+ frame_mirror = this.exec_state_.frame(frame_number);
+ } else {
+ // Restart selected frame.
+ frame_mirror = this.exec_state_.frame();
+ }
+
+ var result_description = Debug.LiveEdit.RestartFrame(frame_mirror);
+ response.body = {result: result_description};
+};
+
+
+DebugCommandProcessor.prototype.debuggerFlagsRequest_ = function(request,
+ response) {
+ // Check for legal request.
+ if (!request.arguments) {
+ response.failed('Missing arguments');
+ return;
+ }
+
+ // Pull out arguments.
+ var flags = request.arguments.flags;
+
+ response.body = { flags: [] };
+ if (!IS_UNDEFINED(flags)) {
+ for (var i = 0; i < flags.length; i++) {
+ var name = flags[i].name;
+ var debugger_flag = debugger_flags[name];
+ if (!debugger_flag) {
+ continue;
+ }
+ if ('value' in flags[i]) {
+ debugger_flag.setValue(flags[i].value);
+ }
+ response.body.flags.push({ name: name, value: debugger_flag.getValue() });
+ }
+ } else {
+ for (var name in debugger_flags) {
+ var value = debugger_flags[name].getValue();
+ response.body.flags.push({ name: name, value: value });
+ }
+ }
+};
+
+
+DebugCommandProcessor.prototype.v8FlagsRequest_ = function(request, response) {
+ var flags = request.arguments.flags;
+ if (!flags) flags = '';
+ %SetFlags(flags);
+};
+
+
+DebugCommandProcessor.prototype.gcRequest_ = function(request, response) {
+ var type = request.arguments.type;
+ if (!type) type = 'all';
+
+ var before = %GetHeapUsage();
+ %CollectGarbage(type);
+ var after = %GetHeapUsage();
+
+ response.body = { "before": before, "after": after };
+};
+
+
+DebugCommandProcessor.prototype.dispatch_ = (function() {
+ var proto = DebugCommandProcessor.prototype;
+ return {
+ "continue": proto.continueRequest_,
+ "break" : proto.breakRequest_,
+ "setbreakpoint" : proto.setBreakPointRequest_,
+ "changebreakpoint": proto.changeBreakPointRequest_,
+ "clearbreakpoint": proto.clearBreakPointRequest_,
+ "clearbreakpointgroup": proto.clearBreakPointGroupRequest_,
+ "disconnect": proto.disconnectRequest_,
+ "setexceptionbreak": proto.setExceptionBreakRequest_,
+ "listbreakpoints": proto.listBreakpointsRequest_,
+ "backtrace": proto.backtraceRequest_,
+ "frame": proto.frameRequest_,
+ "scopes": proto.scopesRequest_,
+ "scope": proto.scopeRequest_,
+ "setvariablevalue": proto.setVariableValueRequest_,
+ "evaluate": proto.evaluateRequest_,
+ "lookup": proto.lookupRequest_,
+ "references": proto.referencesRequest_,
+ "source": proto.sourceRequest_,
+ "scripts": proto.scriptsRequest_,
+ "threads": proto.threadsRequest_,
+ "suspend": proto.suspendRequest_,
+ "version": proto.versionRequest_,
+ "changelive": proto.changeLiveRequest_,
+ "restartframe": proto.restartFrameRequest_,
+ "flags": proto.debuggerFlagsRequest_,
+ "v8flag": proto.v8FlagsRequest_,
+ "gc": proto.gcRequest_,
+ };
+})();
+
+
+// Check whether the previously processed command caused the VM to become
+// running.
+DebugCommandProcessor.prototype.isRunning = function() {
+ return this.running_;
+};
+
+
+DebugCommandProcessor.prototype.systemBreak = function(cmd, args) {
+ return %SystemBreak();
+};
+
+
+/**
+ * Convert an Object to its debugger protocol representation. The representation
+ * may be serilized to a JSON object using JSON.stringify().
+ * This implementation simply runs through all string property names, converts
+ * each property value to a protocol value and adds the property to the result
+ * object. For type "object" the function will be called recursively. Note that
+ * circular structures will cause infinite recursion.
+ * @param {Object} object The object to format as protocol object.
+ * @param {MirrorSerializer} mirror_serializer The serializer to use if any
+ * mirror objects are encountered.
+ * @return {Object} Protocol object value.
+ */
+function ObjectToProtocolObject_(object, mirror_serializer) {
+ var content = {};
+ for (var key in object) {
+ // Only consider string keys.
+ if (typeof key == 'string') {
+ // Format the value based on its type.
+ var property_value_json = ValueToProtocolValue_(object[key],
+ mirror_serializer);
+ // Add the property if relevant.
+ if (!IS_UNDEFINED(property_value_json)) {
+ content[key] = property_value_json;
+ }
+ }
+ }
+
+ return content;
+}
+
+
+/**
+ * Convert an array to its debugger protocol representation. It will convert
+ * each array element to a protocol value.
+ * @param {Array} array The array to format as protocol array.
+ * @param {MirrorSerializer} mirror_serializer The serializer to use if any
+ * mirror objects are encountered.
+ * @return {Array} Protocol array value.
+ */
+function ArrayToProtocolArray_(array, mirror_serializer) {
+ var json = [];
+ for (var i = 0; i < array.length; i++) {
+ json.push(ValueToProtocolValue_(array[i], mirror_serializer));
+ }
+ return json;
+}
+
+
+/**
+ * Convert a value to its debugger protocol representation.
+ * @param {*} value The value to format as protocol value.
+ * @param {MirrorSerializer} mirror_serializer The serializer to use if any
+ * mirror objects are encountered.
+ * @return {*} Protocol value.
+ */
+function ValueToProtocolValue_(value, mirror_serializer) {
+ // Format the value based on its type.
+ var json;
+ switch (typeof value) {
+ case 'object':
+ if (value instanceof Mirror) {
+ json = mirror_serializer.serializeValue(value);
+ } else if (IS_ARRAY(value)){
+ json = ArrayToProtocolArray_(value, mirror_serializer);
+ } else {
+ json = ObjectToProtocolObject_(value, mirror_serializer);
+ }
+ break;
+
+ case 'boolean':
+ case 'string':
+ case 'number':
+ json = value;
+ break;
+
+ default:
+ json = null;
+ }
+ return json;
+}
--- /dev/null
+// Copyright 2012 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"
+
+#if V8_TARGET_ARCH_IA32
+
+#include "src/codegen.h"
+#include "src/debug/debug.h"
+
+
+namespace v8 {
+namespace internal {
+
+#define __ ACCESS_MASM(masm)
+
+
+void EmitDebugBreakSlot(MacroAssembler* masm) {
+ Label check_codesize;
+ __ bind(&check_codesize);
+ __ Nop(Assembler::kDebugBreakSlotLength);
+ DCHECK_EQ(Assembler::kDebugBreakSlotLength,
+ masm->SizeOfCodeGeneratedSince(&check_codesize));
+}
+
+
+void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode,
+ int call_argc) {
+ // Generate enough nop's to make space for a call instruction.
+ masm->RecordDebugBreakSlot(mode, call_argc);
+ EmitDebugBreakSlot(masm);
+}
+
+
+void DebugCodegen::ClearDebugBreakSlot(Address pc) {
+ CodePatcher patcher(pc, Assembler::kDebugBreakSlotLength);
+ EmitDebugBreakSlot(patcher.masm());
+}
+
+
+void DebugCodegen::PatchDebugBreakSlot(Address pc, Handle<Code> code) {
+ DCHECK_EQ(Code::BUILTIN, code->kind());
+ static const int kSize = Assembler::kDebugBreakSlotLength;
+ CodePatcher patcher(pc, kSize);
+
+ // Add a label for checking the size of the code used for returning.
+ Label check_codesize;
+ patcher.masm()->bind(&check_codesize);
+ patcher.masm()->call(code->entry(), RelocInfo::NONE32);
+ // Check that the size of the code generated is as expected.
+ DCHECK_EQ(kSize, patcher.masm()->SizeOfCodeGeneratedSince(&check_codesize));
+}
+
+
+void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
+ DebugBreakCallHelperMode mode) {
+ __ RecordComment("Debug break");
+
+ // Enter an internal frame.
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // Load padding words on stack.
+ for (int i = 0; i < LiveEdit::kFramePaddingInitialSize; i++) {
+ __ push(Immediate(Smi::FromInt(LiveEdit::kFramePaddingValue)));
+ }
+ __ push(Immediate(Smi::FromInt(LiveEdit::kFramePaddingInitialSize)));
+
+ if (mode == SAVE_RESULT_REGISTER) __ push(eax);
+
+ __ Move(eax, Immediate(0)); // No arguments.
+ __ mov(ebx,
+ Immediate(ExternalReference(
+ Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate())));
+
+ CEntryStub ceb(masm->isolate(), 1);
+ __ CallStub(&ceb);
+
+ if (FLAG_debug_code) {
+ for (int i = 0; i < kNumJSCallerSaved; ++i) {
+ Register reg = {JSCallerSavedCode(i)};
+ __ Move(reg, Immediate(kDebugZapValue));
+ }
+ }
+
+ if (mode == SAVE_RESULT_REGISTER) __ pop(eax);
+
+ __ pop(ebx);
+ // We divide stored value by 2 (untagging) and multiply it by word's size.
+ STATIC_ASSERT(kSmiTagSize == 1 && kSmiShiftSize == 0);
+ __ lea(esp, Operand(esp, ebx, times_half_pointer_size, 0));
+
+ // Get rid of the internal frame.
+ }
+
+ // This call did not replace a call , so there will be an unwanted
+ // return address left on the stack. Here we get rid of that.
+ __ add(esp, Immediate(kPointerSize));
+
+ // Now that the break point has been handled, resume normal execution by
+ // jumping to the target address intended by the caller and that was
+ // overwritten by the address of DebugBreakXXX.
+ ExternalReference after_break_target =
+ ExternalReference::debug_after_break_target_address(masm->isolate());
+ __ jmp(Operand::StaticVariable(after_break_target));
+}
+
+
+void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
+ masm->ret(0);
+}
+
+
+void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
+ ExternalReference restarter_frame_function_slot =
+ ExternalReference::debug_restarter_frame_function_pointer_address(
+ masm->isolate());
+ __ mov(Operand::StaticVariable(restarter_frame_function_slot), Immediate(0));
+
+ // We do not know our frame height, but set esp based on ebp.
+ __ lea(esp, Operand(ebp, -1 * kPointerSize));
+
+ __ pop(edi); // Function.
+ __ pop(ebp);
+
+ // Load context from the function.
+ __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
+
+ // Get function code.
+ __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
+ __ mov(edx, FieldOperand(edx, SharedFunctionInfo::kCodeOffset));
+ __ lea(edx, FieldOperand(edx, Code::kHeaderSize));
+
+ // Re-run JSFunction, edi is function, esi is context.
+ __ jmp(edx);
+}
+
+
+const bool LiveEdit::kFrameDropperSupported = true;
+
+#undef __
+
+} // namespace internal
+} // namespace v8
+
+#endif // V8_TARGET_ARCH_IA32
--- /dev/null
+// Copyright 2012 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/debug/liveedit.h"
+
+#include "src/code-stubs.h"
+#include "src/compilation-cache.h"
+#include "src/compiler.h"
+#include "src/debug/debug.h"
+#include "src/deoptimizer.h"
+#include "src/global-handles.h"
+#include "src/messages.h"
+#include "src/parser.h"
+#include "src/scopeinfo.h"
+#include "src/scopes.h"
+#include "src/v8memory.h"
+
+namespace v8 {
+namespace internal {
+
+void SetElementSloppy(Handle<JSObject> object,
+ uint32_t index,
+ Handle<Object> value) {
+ // Ignore return value from SetElement. It can only be a failure if there
+ // are element setters causing exceptions and the debugger context has none
+ // of these.
+ Object::SetElement(object->GetIsolate(), object, index, value, SLOPPY)
+ .Assert();
+}
+
+
+// A simple implementation of dynamic programming algorithm. It solves
+// the problem of finding the difference of 2 arrays. It uses a table of results
+// of subproblems. Each cell contains a number together with 2-bit flag
+// that helps building the chunk list.
+class Differencer {
+ public:
+ explicit Differencer(Comparator::Input* input)
+ : input_(input), len1_(input->GetLength1()), len2_(input->GetLength2()) {
+ buffer_ = NewArray<int>(len1_ * len2_);
+ }
+ ~Differencer() {
+ DeleteArray(buffer_);
+ }
+
+ void Initialize() {
+ int array_size = len1_ * len2_;
+ for (int i = 0; i < array_size; i++) {
+ buffer_[i] = kEmptyCellValue;
+ }
+ }
+
+ // Makes sure that result for the full problem is calculated and stored
+ // in the table together with flags showing a path through subproblems.
+ void FillTable() {
+ CompareUpToTail(0, 0);
+ }
+
+ void SaveResult(Comparator::Output* chunk_writer) {
+ ResultWriter writer(chunk_writer);
+
+ int pos1 = 0;
+ int pos2 = 0;
+ while (true) {
+ if (pos1 < len1_) {
+ if (pos2 < len2_) {
+ Direction dir = get_direction(pos1, pos2);
+ switch (dir) {
+ case EQ:
+ writer.eq();
+ pos1++;
+ pos2++;
+ break;
+ case SKIP1:
+ writer.skip1(1);
+ pos1++;
+ break;
+ case SKIP2:
+ case SKIP_ANY:
+ writer.skip2(1);
+ pos2++;
+ break;
+ default:
+ UNREACHABLE();
+ }
+ } else {
+ writer.skip1(len1_ - pos1);
+ break;
+ }
+ } else {
+ if (len2_ != pos2) {
+ writer.skip2(len2_ - pos2);
+ }
+ break;
+ }
+ }
+ writer.close();
+ }
+
+ private:
+ Comparator::Input* input_;
+ int* buffer_;
+ int len1_;
+ int len2_;
+
+ enum Direction {
+ EQ = 0,
+ SKIP1,
+ SKIP2,
+ SKIP_ANY,
+
+ MAX_DIRECTION_FLAG_VALUE = SKIP_ANY
+ };
+
+ // Computes result for a subtask and optionally caches it in the buffer table.
+ // All results values are shifted to make space for flags in the lower bits.
+ int CompareUpToTail(int pos1, int pos2) {
+ if (pos1 < len1_) {
+ if (pos2 < len2_) {
+ int cached_res = get_value4(pos1, pos2);
+ if (cached_res == kEmptyCellValue) {
+ Direction dir;
+ int res;
+ if (input_->Equals(pos1, pos2)) {
+ res = CompareUpToTail(pos1 + 1, pos2 + 1);
+ dir = EQ;
+ } else {
+ int res1 = CompareUpToTail(pos1 + 1, pos2) +
+ (1 << kDirectionSizeBits);
+ int res2 = CompareUpToTail(pos1, pos2 + 1) +
+ (1 << kDirectionSizeBits);
+ if (res1 == res2) {
+ res = res1;
+ dir = SKIP_ANY;
+ } else if (res1 < res2) {
+ res = res1;
+ dir = SKIP1;
+ } else {
+ res = res2;
+ dir = SKIP2;
+ }
+ }
+ set_value4_and_dir(pos1, pos2, res, dir);
+ cached_res = res;
+ }
+ return cached_res;
+ } else {
+ return (len1_ - pos1) << kDirectionSizeBits;
+ }
+ } else {
+ return (len2_ - pos2) << kDirectionSizeBits;
+ }
+ }
+
+ inline int& get_cell(int i1, int i2) {
+ return buffer_[i1 + i2 * len1_];
+ }
+
+ // Each cell keeps a value plus direction. Value is multiplied by 4.
+ void set_value4_and_dir(int i1, int i2, int value4, Direction dir) {
+ DCHECK((value4 & kDirectionMask) == 0);
+ get_cell(i1, i2) = value4 | dir;
+ }
+
+ int get_value4(int i1, int i2) {
+ return get_cell(i1, i2) & (kMaxUInt32 ^ kDirectionMask);
+ }
+ Direction get_direction(int i1, int i2) {
+ return static_cast<Direction>(get_cell(i1, i2) & kDirectionMask);
+ }
+
+ static const int kDirectionSizeBits = 2;
+ static const int kDirectionMask = (1 << kDirectionSizeBits) - 1;
+ static const int kEmptyCellValue = ~0u << kDirectionSizeBits;
+
+ // This method only holds static assert statement (unfortunately you cannot
+ // place one in class scope).
+ void StaticAssertHolder() {
+ STATIC_ASSERT(MAX_DIRECTION_FLAG_VALUE < (1 << kDirectionSizeBits));
+ }
+
+ class ResultWriter {
+ public:
+ explicit ResultWriter(Comparator::Output* chunk_writer)
+ : chunk_writer_(chunk_writer), pos1_(0), pos2_(0),
+ pos1_begin_(-1), pos2_begin_(-1), has_open_chunk_(false) {
+ }
+ void eq() {
+ FlushChunk();
+ pos1_++;
+ pos2_++;
+ }
+ void skip1(int len1) {
+ StartChunk();
+ pos1_ += len1;
+ }
+ void skip2(int len2) {
+ StartChunk();
+ pos2_ += len2;
+ }
+ void close() {
+ FlushChunk();
+ }
+
+ private:
+ Comparator::Output* chunk_writer_;
+ int pos1_;
+ int pos2_;
+ int pos1_begin_;
+ int pos2_begin_;
+ bool has_open_chunk_;
+
+ void StartChunk() {
+ if (!has_open_chunk_) {
+ pos1_begin_ = pos1_;
+ pos2_begin_ = pos2_;
+ has_open_chunk_ = true;
+ }
+ }
+
+ void FlushChunk() {
+ if (has_open_chunk_) {
+ chunk_writer_->AddChunk(pos1_begin_, pos2_begin_,
+ pos1_ - pos1_begin_, pos2_ - pos2_begin_);
+ has_open_chunk_ = false;
+ }
+ }
+ };
+};
+
+
+void Comparator::CalculateDifference(Comparator::Input* input,
+ Comparator::Output* result_writer) {
+ Differencer differencer(input);
+ differencer.Initialize();
+ differencer.FillTable();
+ differencer.SaveResult(result_writer);
+}
+
+
+static bool CompareSubstrings(Handle<String> s1, int pos1,
+ Handle<String> s2, int pos2, int len) {
+ for (int i = 0; i < len; i++) {
+ if (s1->Get(i + pos1) != s2->Get(i + pos2)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+// Additional to Input interface. Lets switch Input range to subrange.
+// More elegant way would be to wrap one Input as another Input object
+// and translate positions there, but that would cost us additional virtual
+// call per comparison.
+class SubrangableInput : public Comparator::Input {
+ public:
+ virtual void SetSubrange1(int offset, int len) = 0;
+ virtual void SetSubrange2(int offset, int len) = 0;
+};
+
+
+class SubrangableOutput : public Comparator::Output {
+ public:
+ virtual void SetSubrange1(int offset, int len) = 0;
+ virtual void SetSubrange2(int offset, int len) = 0;
+};
+
+
+static int min(int a, int b) {
+ return a < b ? a : b;
+}
+
+
+// Finds common prefix and suffix in input. This parts shouldn't take space in
+// linear programming table. Enable subranging in input and output.
+static void NarrowDownInput(SubrangableInput* input,
+ SubrangableOutput* output) {
+ const int len1 = input->GetLength1();
+ const int len2 = input->GetLength2();
+
+ int common_prefix_len;
+ int common_suffix_len;
+
+ {
+ common_prefix_len = 0;
+ int prefix_limit = min(len1, len2);
+ while (common_prefix_len < prefix_limit &&
+ input->Equals(common_prefix_len, common_prefix_len)) {
+ common_prefix_len++;
+ }
+
+ common_suffix_len = 0;
+ int suffix_limit = min(len1 - common_prefix_len, len2 - common_prefix_len);
+
+ while (common_suffix_len < suffix_limit &&
+ input->Equals(len1 - common_suffix_len - 1,
+ len2 - common_suffix_len - 1)) {
+ common_suffix_len++;
+ }
+ }
+
+ if (common_prefix_len > 0 || common_suffix_len > 0) {
+ int new_len1 = len1 - common_suffix_len - common_prefix_len;
+ int new_len2 = len2 - common_suffix_len - common_prefix_len;
+
+ input->SetSubrange1(common_prefix_len, new_len1);
+ input->SetSubrange2(common_prefix_len, new_len2);
+
+ output->SetSubrange1(common_prefix_len, new_len1);
+ output->SetSubrange2(common_prefix_len, new_len2);
+ }
+}
+
+
+// A helper class that writes chunk numbers into JSArray.
+// Each chunk is stored as 3 array elements: (pos1_begin, pos1_end, pos2_end).
+class CompareOutputArrayWriter {
+ public:
+ explicit CompareOutputArrayWriter(Isolate* isolate)
+ : array_(isolate->factory()->NewJSArray(10)), current_size_(0) {}
+
+ Handle<JSArray> GetResult() {
+ return array_;
+ }
+
+ void WriteChunk(int char_pos1, int char_pos2, int char_len1, int char_len2) {
+ Isolate* isolate = array_->GetIsolate();
+ SetElementSloppy(array_,
+ current_size_,
+ Handle<Object>(Smi::FromInt(char_pos1), isolate));
+ SetElementSloppy(array_,
+ current_size_ + 1,
+ Handle<Object>(Smi::FromInt(char_pos1 + char_len1),
+ isolate));
+ SetElementSloppy(array_,
+ current_size_ + 2,
+ Handle<Object>(Smi::FromInt(char_pos2 + char_len2),
+ isolate));
+ current_size_ += 3;
+ }
+
+ private:
+ Handle<JSArray> array_;
+ int current_size_;
+};
+
+
+// Represents 2 strings as 2 arrays of tokens.
+// TODO(LiveEdit): Currently it's actually an array of charactres.
+// Make array of tokens instead.
+class TokensCompareInput : public Comparator::Input {
+ public:
+ TokensCompareInput(Handle<String> s1, int offset1, int len1,
+ Handle<String> s2, int offset2, int len2)
+ : s1_(s1), offset1_(offset1), len1_(len1),
+ s2_(s2), offset2_(offset2), len2_(len2) {
+ }
+ virtual int GetLength1() {
+ return len1_;
+ }
+ virtual int GetLength2() {
+ return len2_;
+ }
+ bool Equals(int index1, int index2) {
+ return s1_->Get(offset1_ + index1) == s2_->Get(offset2_ + index2);
+ }
+
+ private:
+ Handle<String> s1_;
+ int offset1_;
+ int len1_;
+ Handle<String> s2_;
+ int offset2_;
+ int len2_;
+};
+
+
+// Stores compare result in JSArray. Converts substring positions
+// to absolute positions.
+class TokensCompareOutput : public Comparator::Output {
+ public:
+ TokensCompareOutput(CompareOutputArrayWriter* array_writer,
+ int offset1, int offset2)
+ : array_writer_(array_writer), offset1_(offset1), offset2_(offset2) {
+ }
+
+ void AddChunk(int pos1, int pos2, int len1, int len2) {
+ array_writer_->WriteChunk(pos1 + offset1_, pos2 + offset2_, len1, len2);
+ }
+
+ private:
+ CompareOutputArrayWriter* array_writer_;
+ int offset1_;
+ int offset2_;
+};
+
+
+// Wraps raw n-elements line_ends array as a list of n+1 lines. The last line
+// never has terminating new line character.
+class LineEndsWrapper {
+ public:
+ explicit LineEndsWrapper(Handle<String> string)
+ : ends_array_(String::CalculateLineEnds(string, false)),
+ string_len_(string->length()) {
+ }
+ int length() {
+ return ends_array_->length() + 1;
+ }
+ // Returns start for any line including start of the imaginary line after
+ // the last line.
+ int GetLineStart(int index) {
+ if (index == 0) {
+ return 0;
+ } else {
+ return GetLineEnd(index - 1);
+ }
+ }
+ int GetLineEnd(int index) {
+ if (index == ends_array_->length()) {
+ // End of the last line is always an end of the whole string.
+ // If the string ends with a new line character, the last line is an
+ // empty string after this character.
+ return string_len_;
+ } else {
+ return GetPosAfterNewLine(index);
+ }
+ }
+
+ private:
+ Handle<FixedArray> ends_array_;
+ int string_len_;
+
+ int GetPosAfterNewLine(int index) {
+ return Smi::cast(ends_array_->get(index))->value() + 1;
+ }
+};
+
+
+// Represents 2 strings as 2 arrays of lines.
+class LineArrayCompareInput : public SubrangableInput {
+ public:
+ LineArrayCompareInput(Handle<String> s1, Handle<String> s2,
+ LineEndsWrapper line_ends1, LineEndsWrapper line_ends2)
+ : s1_(s1), s2_(s2), line_ends1_(line_ends1),
+ line_ends2_(line_ends2),
+ subrange_offset1_(0), subrange_offset2_(0),
+ subrange_len1_(line_ends1_.length()),
+ subrange_len2_(line_ends2_.length()) {
+ }
+ int GetLength1() {
+ return subrange_len1_;
+ }
+ int GetLength2() {
+ return subrange_len2_;
+ }
+ bool Equals(int index1, int index2) {
+ index1 += subrange_offset1_;
+ index2 += subrange_offset2_;
+
+ int line_start1 = line_ends1_.GetLineStart(index1);
+ int line_start2 = line_ends2_.GetLineStart(index2);
+ int line_end1 = line_ends1_.GetLineEnd(index1);
+ int line_end2 = line_ends2_.GetLineEnd(index2);
+ int len1 = line_end1 - line_start1;
+ int len2 = line_end2 - line_start2;
+ if (len1 != len2) {
+ return false;
+ }
+ return CompareSubstrings(s1_, line_start1, s2_, line_start2,
+ len1);
+ }
+ void SetSubrange1(int offset, int len) {
+ subrange_offset1_ = offset;
+ subrange_len1_ = len;
+ }
+ void SetSubrange2(int offset, int len) {
+ subrange_offset2_ = offset;
+ subrange_len2_ = len;
+ }
+
+ private:
+ Handle<String> s1_;
+ Handle<String> s2_;
+ LineEndsWrapper line_ends1_;
+ LineEndsWrapper line_ends2_;
+ int subrange_offset1_;
+ int subrange_offset2_;
+ int subrange_len1_;
+ int subrange_len2_;
+};
+
+
+// Stores compare result in JSArray. For each chunk tries to conduct
+// a fine-grained nested diff token-wise.
+class TokenizingLineArrayCompareOutput : public SubrangableOutput {
+ public:
+ TokenizingLineArrayCompareOutput(LineEndsWrapper line_ends1,
+ LineEndsWrapper line_ends2,
+ Handle<String> s1, Handle<String> s2)
+ : array_writer_(s1->GetIsolate()),
+ line_ends1_(line_ends1), line_ends2_(line_ends2), s1_(s1), s2_(s2),
+ subrange_offset1_(0), subrange_offset2_(0) {
+ }
+
+ void AddChunk(int line_pos1, int line_pos2, int line_len1, int line_len2) {
+ line_pos1 += subrange_offset1_;
+ line_pos2 += subrange_offset2_;
+
+ int char_pos1 = line_ends1_.GetLineStart(line_pos1);
+ int char_pos2 = line_ends2_.GetLineStart(line_pos2);
+ int char_len1 = line_ends1_.GetLineStart(line_pos1 + line_len1) - char_pos1;
+ int char_len2 = line_ends2_.GetLineStart(line_pos2 + line_len2) - char_pos2;
+
+ if (char_len1 < CHUNK_LEN_LIMIT && char_len2 < CHUNK_LEN_LIMIT) {
+ // Chunk is small enough to conduct a nested token-level diff.
+ HandleScope subTaskScope(s1_->GetIsolate());
+
+ TokensCompareInput tokens_input(s1_, char_pos1, char_len1,
+ s2_, char_pos2, char_len2);
+ TokensCompareOutput tokens_output(&array_writer_, char_pos1,
+ char_pos2);
+
+ Comparator::CalculateDifference(&tokens_input, &tokens_output);
+ } else {
+ array_writer_.WriteChunk(char_pos1, char_pos2, char_len1, char_len2);
+ }
+ }
+ void SetSubrange1(int offset, int len) {
+ subrange_offset1_ = offset;
+ }
+ void SetSubrange2(int offset, int len) {
+ subrange_offset2_ = offset;
+ }
+
+ Handle<JSArray> GetResult() {
+ return array_writer_.GetResult();
+ }
+
+ private:
+ static const int CHUNK_LEN_LIMIT = 800;
+
+ CompareOutputArrayWriter array_writer_;
+ LineEndsWrapper line_ends1_;
+ LineEndsWrapper line_ends2_;
+ Handle<String> s1_;
+ Handle<String> s2_;
+ int subrange_offset1_;
+ int subrange_offset2_;
+};
+
+
+Handle<JSArray> LiveEdit::CompareStrings(Handle<String> s1,
+ Handle<String> s2) {
+ s1 = String::Flatten(s1);
+ s2 = String::Flatten(s2);
+
+ LineEndsWrapper line_ends1(s1);
+ LineEndsWrapper line_ends2(s2);
+
+ LineArrayCompareInput input(s1, s2, line_ends1, line_ends2);
+ TokenizingLineArrayCompareOutput output(line_ends1, line_ends2, s1, s2);
+
+ NarrowDownInput(&input, &output);
+
+ Comparator::CalculateDifference(&input, &output);
+
+ return output.GetResult();
+}
+
+
+// Unwraps JSValue object, returning its field "value"
+static Handle<Object> UnwrapJSValue(Handle<JSValue> jsValue) {
+ return Handle<Object>(jsValue->value(), jsValue->GetIsolate());
+}
+
+
+// Wraps any object into a OpaqueReference, that will hide the object
+// from JavaScript.
+static Handle<JSValue> WrapInJSValue(Handle<HeapObject> object) {
+ Isolate* isolate = object->GetIsolate();
+ Handle<JSFunction> constructor = isolate->opaque_reference_function();
+ Handle<JSValue> result =
+ Handle<JSValue>::cast(isolate->factory()->NewJSObject(constructor));
+ result->set_value(*object);
+ return result;
+}
+
+
+static Handle<SharedFunctionInfo> UnwrapSharedFunctionInfoFromJSValue(
+ Handle<JSValue> jsValue) {
+ Object* shared = jsValue->value();
+ CHECK(shared->IsSharedFunctionInfo());
+ return Handle<SharedFunctionInfo>(SharedFunctionInfo::cast(shared));
+}
+
+
+static int GetArrayLength(Handle<JSArray> array) {
+ Object* length = array->length();
+ CHECK(length->IsSmi());
+ return Smi::cast(length)->value();
+}
+
+
+void FunctionInfoWrapper::SetInitialProperties(Handle<String> name,
+ int start_position,
+ int end_position, int param_num,
+ int literal_count,
+ int parent_index) {
+ HandleScope scope(isolate());
+ this->SetField(kFunctionNameOffset_, name);
+ this->SetSmiValueField(kStartPositionOffset_, start_position);
+ this->SetSmiValueField(kEndPositionOffset_, end_position);
+ this->SetSmiValueField(kParamNumOffset_, param_num);
+ this->SetSmiValueField(kLiteralNumOffset_, literal_count);
+ this->SetSmiValueField(kParentIndexOffset_, parent_index);
+}
+
+
+void FunctionInfoWrapper::SetFunctionCode(Handle<Code> function_code,
+ Handle<HeapObject> code_scope_info) {
+ Handle<JSValue> code_wrapper = WrapInJSValue(function_code);
+ this->SetField(kCodeOffset_, code_wrapper);
+
+ Handle<JSValue> scope_wrapper = WrapInJSValue(code_scope_info);
+ this->SetField(kCodeScopeInfoOffset_, scope_wrapper);
+}
+
+
+void FunctionInfoWrapper::SetSharedFunctionInfo(
+ Handle<SharedFunctionInfo> info) {
+ Handle<JSValue> info_holder = WrapInJSValue(info);
+ this->SetField(kSharedFunctionInfoOffset_, info_holder);
+}
+
+
+Handle<Code> FunctionInfoWrapper::GetFunctionCode() {
+ Handle<Object> element = this->GetField(kCodeOffset_);
+ Handle<JSValue> value_wrapper = Handle<JSValue>::cast(element);
+ Handle<Object> raw_result = UnwrapJSValue(value_wrapper);
+ CHECK(raw_result->IsCode());
+ return Handle<Code>::cast(raw_result);
+}
+
+
+MaybeHandle<TypeFeedbackVector> FunctionInfoWrapper::GetFeedbackVector() {
+ Handle<Object> element = this->GetField(kSharedFunctionInfoOffset_);
+ if (element->IsJSValue()) {
+ Handle<JSValue> value_wrapper = Handle<JSValue>::cast(element);
+ Handle<Object> raw_result = UnwrapJSValue(value_wrapper);
+ Handle<SharedFunctionInfo> shared =
+ Handle<SharedFunctionInfo>::cast(raw_result);
+ return Handle<TypeFeedbackVector>(shared->feedback_vector(), isolate());
+ } else {
+ // Scripts may never have a SharedFunctionInfo created.
+ return MaybeHandle<TypeFeedbackVector>();
+ }
+}
+
+
+Handle<Object> FunctionInfoWrapper::GetCodeScopeInfo() {
+ Handle<Object> element = this->GetField(kCodeScopeInfoOffset_);
+ return UnwrapJSValue(Handle<JSValue>::cast(element));
+}
+
+
+void SharedInfoWrapper::SetProperties(Handle<String> name,
+ int start_position,
+ int end_position,
+ Handle<SharedFunctionInfo> info) {
+ HandleScope scope(isolate());
+ this->SetField(kFunctionNameOffset_, name);
+ Handle<JSValue> info_holder = WrapInJSValue(info);
+ this->SetField(kSharedInfoOffset_, info_holder);
+ this->SetSmiValueField(kStartPositionOffset_, start_position);
+ this->SetSmiValueField(kEndPositionOffset_, end_position);
+}
+
+
+Handle<SharedFunctionInfo> SharedInfoWrapper::GetInfo() {
+ Handle<Object> element = this->GetField(kSharedInfoOffset_);
+ Handle<JSValue> value_wrapper = Handle<JSValue>::cast(element);
+ return UnwrapSharedFunctionInfoFromJSValue(value_wrapper);
+}
+
+
+class FunctionInfoListener {
+ public:
+ explicit FunctionInfoListener(Isolate* isolate) {
+ current_parent_index_ = -1;
+ len_ = 0;
+ result_ = isolate->factory()->NewJSArray(10);
+ }
+
+ void FunctionStarted(FunctionLiteral* fun) {
+ HandleScope scope(isolate());
+ FunctionInfoWrapper info = FunctionInfoWrapper::Create(isolate());
+ info.SetInitialProperties(fun->name(), fun->start_position(),
+ fun->end_position(), fun->parameter_count(),
+ fun->materialized_literal_count(),
+ current_parent_index_);
+ current_parent_index_ = len_;
+ SetElementSloppy(result_, len_, info.GetJSArray());
+ len_++;
+ }
+
+ void FunctionDone() {
+ HandleScope scope(isolate());
+ FunctionInfoWrapper info =
+ FunctionInfoWrapper::cast(
+ *Object::GetElement(
+ isolate(), result_, current_parent_index_).ToHandleChecked());
+ current_parent_index_ = info.GetParentIndex();
+ }
+
+ // Saves only function code, because for a script function we
+ // may never create a SharedFunctionInfo object.
+ void FunctionCode(Handle<Code> function_code) {
+ FunctionInfoWrapper info =
+ FunctionInfoWrapper::cast(
+ *Object::GetElement(
+ isolate(), result_, current_parent_index_).ToHandleChecked());
+ info.SetFunctionCode(function_code,
+ Handle<HeapObject>(isolate()->heap()->null_value()));
+ }
+
+ // Saves full information about a function: its code, its scope info
+ // and a SharedFunctionInfo object.
+ void FunctionInfo(Handle<SharedFunctionInfo> shared, Scope* scope,
+ Zone* zone) {
+ if (!shared->IsSharedFunctionInfo()) {
+ return;
+ }
+ FunctionInfoWrapper info =
+ FunctionInfoWrapper::cast(
+ *Object::GetElement(
+ isolate(), result_, current_parent_index_).ToHandleChecked());
+ info.SetFunctionCode(Handle<Code>(shared->code()),
+ Handle<HeapObject>(shared->scope_info()));
+ info.SetSharedFunctionInfo(shared);
+
+ Handle<Object> scope_info_list = SerializeFunctionScope(scope, zone);
+ info.SetFunctionScopeInfo(scope_info_list);
+ }
+
+ Handle<JSArray> GetResult() { return result_; }
+
+ private:
+ Isolate* isolate() const { return result_->GetIsolate(); }
+
+ Handle<Object> SerializeFunctionScope(Scope* scope, Zone* zone) {
+ Handle<JSArray> scope_info_list = isolate()->factory()->NewJSArray(10);
+ int scope_info_length = 0;
+
+ // Saves some description of scope. It stores name and indexes of
+ // variables in the whole scope chain. Null-named slots delimit
+ // scopes of this chain.
+ Scope* current_scope = scope;
+ while (current_scope != NULL) {
+ HandleScope handle_scope(isolate());
+ ZoneList<Variable*> stack_list(current_scope->StackLocalCount(), zone);
+ ZoneList<Variable*> context_list(
+ current_scope->ContextLocalCount(), zone);
+ ZoneList<Variable*> globals_list(current_scope->ContextGlobalCount(),
+ zone);
+ current_scope->CollectStackAndContextLocals(&stack_list, &context_list,
+ &globals_list);
+ context_list.Sort(&Variable::CompareIndex);
+
+ for (int i = 0; i < context_list.length(); i++) {
+ SetElementSloppy(scope_info_list,
+ scope_info_length,
+ context_list[i]->name());
+ scope_info_length++;
+ SetElementSloppy(
+ scope_info_list,
+ scope_info_length,
+ Handle<Smi>(Smi::FromInt(context_list[i]->index()), isolate()));
+ scope_info_length++;
+ }
+ SetElementSloppy(scope_info_list,
+ scope_info_length,
+ Handle<Object>(isolate()->heap()->null_value(),
+ isolate()));
+ scope_info_length++;
+
+ current_scope = current_scope->outer_scope();
+ }
+
+ return scope_info_list;
+ }
+
+ Handle<JSArray> result_;
+ int len_;
+ int current_parent_index_;
+};
+
+
+void LiveEdit::InitializeThreadLocal(Debug* debug) {
+ debug->thread_local_.frame_drop_mode_ = LiveEdit::FRAMES_UNTOUCHED;
+}
+
+
+bool LiveEdit::SetAfterBreakTarget(Debug* debug) {
+ Code* code = NULL;
+ Isolate* isolate = debug->isolate_;
+ switch (debug->thread_local_.frame_drop_mode_) {
+ case FRAMES_UNTOUCHED:
+ return false;
+ case FRAME_DROPPED_IN_IC_CALL:
+ // We must have been calling IC stub. Do not go there anymore.
+ code = isolate->builtins()->builtin(Builtins::kPlainReturn_LiveEdit);
+ break;
+ case FRAME_DROPPED_IN_DEBUG_SLOT_CALL:
+ // Debug break slot stub does not return normally, instead it manually
+ // cleans the stack and jumps. We should patch the jump address.
+ code = isolate->builtins()->builtin(Builtins::kFrameDropper_LiveEdit);
+ break;
+ case FRAME_DROPPED_IN_DIRECT_CALL:
+ // Nothing to do, after_break_target is not used here.
+ return true;
+ case FRAME_DROPPED_IN_RETURN_CALL:
+ code = isolate->builtins()->builtin(Builtins::kFrameDropper_LiveEdit);
+ break;
+ case CURRENTLY_SET_MODE:
+ UNREACHABLE();
+ break;
+ }
+ debug->after_break_target_ = code->entry();
+ return true;
+}
+
+
+MaybeHandle<JSArray> LiveEdit::GatherCompileInfo(Handle<Script> script,
+ Handle<String> source) {
+ Isolate* isolate = script->GetIsolate();
+
+ FunctionInfoListener listener(isolate);
+ Handle<Object> original_source =
+ Handle<Object>(script->source(), isolate);
+ script->set_source(*source);
+ isolate->set_active_function_info_listener(&listener);
+
+ {
+ // Creating verbose TryCatch from public API is currently the only way to
+ // force code save location. We do not use this the object directly.
+ v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
+ try_catch.SetVerbose(true);
+
+ // A logical 'try' section.
+ Compiler::CompileForLiveEdit(script);
+ }
+
+ // A logical 'catch' section.
+ Handle<JSObject> rethrow_exception;
+ if (isolate->has_pending_exception()) {
+ Handle<Object> exception(isolate->pending_exception(), isolate);
+ MessageLocation message_location = isolate->GetMessageLocation();
+
+ isolate->clear_pending_message();
+ isolate->clear_pending_exception();
+
+ // If possible, copy positions from message object to exception object.
+ if (exception->IsJSObject() && !message_location.script().is_null()) {
+ rethrow_exception = Handle<JSObject>::cast(exception);
+
+ Factory* factory = isolate->factory();
+ Handle<String> start_pos_key = factory->InternalizeOneByteString(
+ STATIC_CHAR_VECTOR("startPosition"));
+ Handle<String> end_pos_key =
+ factory->InternalizeOneByteString(STATIC_CHAR_VECTOR("endPosition"));
+ Handle<String> script_obj_key =
+ factory->InternalizeOneByteString(STATIC_CHAR_VECTOR("scriptObject"));
+ Handle<Smi> start_pos(
+ Smi::FromInt(message_location.start_pos()), isolate);
+ Handle<Smi> end_pos(Smi::FromInt(message_location.end_pos()), isolate);
+ Handle<JSObject> script_obj =
+ Script::GetWrapper(message_location.script());
+ Object::SetProperty(rethrow_exception, start_pos_key, start_pos, SLOPPY)
+ .Assert();
+ Object::SetProperty(rethrow_exception, end_pos_key, end_pos, SLOPPY)
+ .Assert();
+ Object::SetProperty(rethrow_exception, script_obj_key, script_obj, SLOPPY)
+ .Assert();
+ }
+ }
+
+ // A logical 'finally' section.
+ isolate->set_active_function_info_listener(NULL);
+ script->set_source(*original_source);
+
+ if (rethrow_exception.is_null()) {
+ return listener.GetResult();
+ } else {
+ return isolate->Throw<JSArray>(rethrow_exception);
+ }
+}
+
+
+void LiveEdit::WrapSharedFunctionInfos(Handle<JSArray> array) {
+ Isolate* isolate = array->GetIsolate();
+ HandleScope scope(isolate);
+ int len = GetArrayLength(array);
+ for (int i = 0; i < len; i++) {
+ Handle<SharedFunctionInfo> info(
+ SharedFunctionInfo::cast(
+ *Object::GetElement(isolate, array, i).ToHandleChecked()));
+ SharedInfoWrapper info_wrapper = SharedInfoWrapper::Create(isolate);
+ Handle<String> name_handle(String::cast(info->name()));
+ info_wrapper.SetProperties(name_handle, info->start_position(),
+ info->end_position(), info);
+ SetElementSloppy(array, i, info_wrapper.GetJSArray());
+ }
+}
+
+
+// Visitor that finds all references to a particular code object,
+// including "CODE_TARGET" references in other code objects and replaces
+// them on the fly.
+class ReplacingVisitor : public ObjectVisitor {
+ public:
+ explicit ReplacingVisitor(Code* original, Code* substitution)
+ : original_(original), substitution_(substitution) {
+ }
+
+ virtual void VisitPointers(Object** start, Object** end) {
+ for (Object** p = start; p < end; p++) {
+ if (*p == original_) {
+ *p = substitution_;
+ }
+ }
+ }
+
+ virtual void VisitCodeEntry(Address entry) {
+ if (Code::GetObjectFromEntryAddress(entry) == original_) {
+ Address substitution_entry = substitution_->instruction_start();
+ Memory::Address_at(entry) = substitution_entry;
+ }
+ }
+
+ virtual void VisitCodeTarget(RelocInfo* rinfo) {
+ if (RelocInfo::IsCodeTarget(rinfo->rmode()) &&
+ Code::GetCodeFromTargetAddress(rinfo->target_address()) == original_) {
+ Address substitution_entry = substitution_->instruction_start();
+ rinfo->set_target_address(substitution_entry);
+ }
+ }
+
+ virtual void VisitDebugTarget(RelocInfo* rinfo) {
+ VisitCodeTarget(rinfo);
+ }
+
+ private:
+ Code* original_;
+ Code* substitution_;
+};
+
+
+// Finds all references to original and replaces them with substitution.
+static void ReplaceCodeObject(Handle<Code> original,
+ Handle<Code> substitution) {
+ // Perform a full GC in order to ensure that we are not in the middle of an
+ // incremental marking phase when we are replacing the code object.
+ // Since we are not in an incremental marking phase we can write pointers
+ // to code objects (that are never in new space) without worrying about
+ // write barriers.
+ Heap* heap = original->GetHeap();
+ HeapIterator iterator(heap);
+
+ DCHECK(!heap->InNewSpace(*substitution));
+
+ ReplacingVisitor visitor(*original, *substitution);
+
+ // Iterate over all roots. Stack frames may have pointer into original code,
+ // so temporary replace the pointers with offset numbers
+ // in prologue/epilogue.
+ heap->IterateRoots(&visitor, VISIT_ALL);
+
+ // Now iterate over all pointers of all objects, including code_target
+ // implicit pointers.
+ for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
+ obj->Iterate(&visitor);
+ }
+}
+
+
+// Patch function literals.
+// Name 'literals' is a misnomer. Rather it's a cache for complex object
+// boilerplates and for a native context. We must clean cached values.
+// Additionally we may need to allocate a new array if number of literals
+// changed.
+class LiteralFixer {
+ public:
+ static void PatchLiterals(FunctionInfoWrapper* compile_info_wrapper,
+ Handle<SharedFunctionInfo> shared_info,
+ Isolate* isolate) {
+ int new_literal_count = compile_info_wrapper->GetLiteralCount();
+ int old_literal_count = shared_info->num_literals();
+
+ if (old_literal_count == new_literal_count) {
+ // If literal count didn't change, simply go over all functions
+ // and clear literal arrays.
+ ClearValuesVisitor visitor;
+ IterateJSFunctions(shared_info, &visitor);
+ } else {
+ // When literal count changes, we have to create new array instances.
+ // Since we cannot create instances when iterating heap, we should first
+ // collect all functions and fix their literal arrays.
+ Handle<FixedArray> function_instances =
+ CollectJSFunctions(shared_info, isolate);
+ for (int i = 0; i < function_instances->length(); i++) {
+ Handle<JSFunction> fun(JSFunction::cast(function_instances->get(i)));
+ Handle<FixedArray> new_literals =
+ isolate->factory()->NewFixedArray(new_literal_count);
+ fun->set_literals(*new_literals);
+ }
+
+ shared_info->set_num_literals(new_literal_count);
+ }
+ }
+
+ private:
+ // Iterates all function instances in the HEAP that refers to the
+ // provided shared_info.
+ template<typename Visitor>
+ static void IterateJSFunctions(Handle<SharedFunctionInfo> shared_info,
+ Visitor* visitor) {
+ HeapIterator iterator(shared_info->GetHeap());
+ for (HeapObject* obj = iterator.next(); obj != NULL;
+ obj = iterator.next()) {
+ if (obj->IsJSFunction()) {
+ JSFunction* function = JSFunction::cast(obj);
+ if (function->shared() == *shared_info) {
+ visitor->visit(function);
+ }
+ }
+ }
+ }
+
+ // Finds all instances of JSFunction that refers to the provided shared_info
+ // and returns array with them.
+ static Handle<FixedArray> CollectJSFunctions(
+ Handle<SharedFunctionInfo> shared_info, Isolate* isolate) {
+ CountVisitor count_visitor;
+ count_visitor.count = 0;
+ IterateJSFunctions(shared_info, &count_visitor);
+ int size = count_visitor.count;
+
+ Handle<FixedArray> result = isolate->factory()->NewFixedArray(size);
+ if (size > 0) {
+ CollectVisitor collect_visitor(result);
+ IterateJSFunctions(shared_info, &collect_visitor);
+ }
+ return result;
+ }
+
+ class ClearValuesVisitor {
+ public:
+ void visit(JSFunction* fun) {
+ FixedArray* literals = fun->literals();
+ int len = literals->length();
+ for (int j = 0; j < len; j++) {
+ literals->set_undefined(j);
+ }
+ }
+ };
+
+ class CountVisitor {
+ public:
+ void visit(JSFunction* fun) {
+ count++;
+ }
+ int count;
+ };
+
+ class CollectVisitor {
+ public:
+ explicit CollectVisitor(Handle<FixedArray> output)
+ : m_output(output), m_pos(0) {}
+
+ void visit(JSFunction* fun) {
+ m_output->set(m_pos, fun);
+ m_pos++;
+ }
+ private:
+ Handle<FixedArray> m_output;
+ int m_pos;
+ };
+};
+
+
+// Marks code that shares the same shared function info or has inlined
+// code that shares the same function info.
+class DependentFunctionMarker: public OptimizedFunctionVisitor {
+ public:
+ SharedFunctionInfo* shared_info_;
+ bool found_;
+
+ explicit DependentFunctionMarker(SharedFunctionInfo* shared_info)
+ : shared_info_(shared_info), found_(false) { }
+
+ virtual void EnterContext(Context* context) { } // Don't care.
+ virtual void LeaveContext(Context* context) { } // Don't care.
+ virtual void VisitFunction(JSFunction* function) {
+ // It should be guaranteed by the iterator that everything is optimized.
+ DCHECK(function->code()->kind() == Code::OPTIMIZED_FUNCTION);
+ if (function->Inlines(shared_info_)) {
+ // Mark the code for deoptimization.
+ function->code()->set_marked_for_deoptimization(true);
+ found_ = true;
+ }
+ }
+};
+
+
+static void DeoptimizeDependentFunctions(SharedFunctionInfo* function_info) {
+ DisallowHeapAllocation no_allocation;
+ DependentFunctionMarker marker(function_info);
+ // TODO(titzer): need to traverse all optimized code to find OSR code here.
+ Deoptimizer::VisitAllOptimizedFunctions(function_info->GetIsolate(), &marker);
+
+ if (marker.found_) {
+ // Only go through with the deoptimization if something was found.
+ Deoptimizer::DeoptimizeMarkedCode(function_info->GetIsolate());
+ }
+}
+
+
+void LiveEdit::ReplaceFunctionCode(
+ Handle<JSArray> new_compile_info_array,
+ Handle<JSArray> shared_info_array) {
+ Isolate* isolate = new_compile_info_array->GetIsolate();
+
+ FunctionInfoWrapper compile_info_wrapper(new_compile_info_array);
+ SharedInfoWrapper shared_info_wrapper(shared_info_array);
+
+ Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo();
+
+ if (shared_info->code()->kind() == Code::FUNCTION) {
+ Handle<Code> code = compile_info_wrapper.GetFunctionCode();
+ ReplaceCodeObject(Handle<Code>(shared_info->code()), code);
+ Handle<Object> code_scope_info = compile_info_wrapper.GetCodeScopeInfo();
+ if (code_scope_info->IsFixedArray()) {
+ shared_info->set_scope_info(ScopeInfo::cast(*code_scope_info));
+ }
+ shared_info->DisableOptimization(kLiveEdit);
+ // Update the type feedback vector, if needed.
+ MaybeHandle<TypeFeedbackVector> feedback_vector =
+ compile_info_wrapper.GetFeedbackVector();
+ if (!feedback_vector.is_null()) {
+ shared_info->set_feedback_vector(*feedback_vector.ToHandleChecked());
+ }
+ }
+
+ int start_position = compile_info_wrapper.GetStartPosition();
+ int end_position = compile_info_wrapper.GetEndPosition();
+ shared_info->set_start_position(start_position);
+ shared_info->set_end_position(end_position);
+
+ LiteralFixer::PatchLiterals(&compile_info_wrapper, shared_info, isolate);
+
+ shared_info->set_construct_stub(
+ isolate->builtins()->builtin(Builtins::kJSConstructStubGeneric));
+
+ DeoptimizeDependentFunctions(*shared_info);
+ isolate->compilation_cache()->Remove(shared_info);
+}
+
+
+void LiveEdit::FunctionSourceUpdated(Handle<JSArray> shared_info_array) {
+ SharedInfoWrapper shared_info_wrapper(shared_info_array);
+ Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo();
+
+ DeoptimizeDependentFunctions(*shared_info);
+ shared_info_array->GetIsolate()->compilation_cache()->Remove(shared_info);
+}
+
+
+void LiveEdit::SetFunctionScript(Handle<JSValue> function_wrapper,
+ Handle<Object> script_handle) {
+ Handle<SharedFunctionInfo> shared_info =
+ UnwrapSharedFunctionInfoFromJSValue(function_wrapper);
+ CHECK(script_handle->IsScript() || script_handle->IsUndefined());
+ SharedFunctionInfo::SetScript(shared_info, script_handle);
+ shared_info->DisableOptimization(kLiveEdit);
+
+ function_wrapper->GetIsolate()->compilation_cache()->Remove(shared_info);
+}
+
+
+// For a script text change (defined as position_change_array), translates
+// position in unchanged text to position in changed text.
+// Text change is a set of non-overlapping regions in text, that have changed
+// their contents and length. It is specified as array of groups of 3 numbers:
+// (change_begin, change_end, change_end_new_position).
+// Each group describes a change in text; groups are sorted by change_begin.
+// Only position in text beyond any changes may be successfully translated.
+// If a positions is inside some region that changed, result is currently
+// undefined.
+static int TranslatePosition(int original_position,
+ Handle<JSArray> position_change_array) {
+ int position_diff = 0;
+ int array_len = GetArrayLength(position_change_array);
+ Isolate* isolate = position_change_array->GetIsolate();
+ // TODO(635): binary search may be used here
+ for (int i = 0; i < array_len; i += 3) {
+ HandleScope scope(isolate);
+ Handle<Object> element = Object::GetElement(
+ isolate, position_change_array, i).ToHandleChecked();
+ CHECK(element->IsSmi());
+ int chunk_start = Handle<Smi>::cast(element)->value();
+ if (original_position < chunk_start) {
+ break;
+ }
+ element = Object::GetElement(
+ isolate, position_change_array, i + 1).ToHandleChecked();
+ CHECK(element->IsSmi());
+ int chunk_end = Handle<Smi>::cast(element)->value();
+ // Position mustn't be inside a chunk.
+ DCHECK(original_position >= chunk_end);
+ element = Object::GetElement(
+ isolate, position_change_array, i + 2).ToHandleChecked();
+ CHECK(element->IsSmi());
+ int chunk_changed_end = Handle<Smi>::cast(element)->value();
+ position_diff = chunk_changed_end - chunk_end;
+ }
+
+ return original_position + position_diff;
+}
+
+
+// Auto-growing buffer for writing relocation info code section. This buffer
+// is a simplified version of buffer from Assembler. Unlike Assembler, this
+// class is platform-independent and it works without dealing with instructions.
+// As specified by RelocInfo format, the buffer is filled in reversed order:
+// from upper to lower addresses.
+// It uses NewArray/DeleteArray for memory management.
+class RelocInfoBuffer {
+ public:
+ RelocInfoBuffer(int buffer_initial_capicity, byte* pc) {
+ buffer_size_ = buffer_initial_capicity + kBufferGap;
+ buffer_ = NewArray<byte>(buffer_size_);
+
+ reloc_info_writer_.Reposition(buffer_ + buffer_size_, pc);
+ }
+ ~RelocInfoBuffer() {
+ DeleteArray(buffer_);
+ }
+
+ // As specified by RelocInfo format, the buffer is filled in reversed order:
+ // from upper to lower addresses.
+ void Write(const RelocInfo* rinfo) {
+ if (buffer_ + kBufferGap >= reloc_info_writer_.pos()) {
+ Grow();
+ }
+ reloc_info_writer_.Write(rinfo);
+ }
+
+ Vector<byte> GetResult() {
+ // Return the bytes from pos up to end of buffer.
+ int result_size =
+ static_cast<int>((buffer_ + buffer_size_) - reloc_info_writer_.pos());
+ return Vector<byte>(reloc_info_writer_.pos(), result_size);
+ }
+
+ private:
+ void Grow() {
+ // Compute new buffer size.
+ int new_buffer_size;
+ if (buffer_size_ < 2 * KB) {
+ new_buffer_size = 4 * KB;
+ } else {
+ new_buffer_size = 2 * buffer_size_;
+ }
+ // Some internal data structures overflow for very large buffers,
+ // they must ensure that kMaximalBufferSize is not too large.
+ if (new_buffer_size > kMaximalBufferSize) {
+ V8::FatalProcessOutOfMemory("RelocInfoBuffer::GrowBuffer");
+ }
+
+ // Set up new buffer.
+ byte* new_buffer = NewArray<byte>(new_buffer_size);
+
+ // Copy the data.
+ int curently_used_size =
+ static_cast<int>(buffer_ + buffer_size_ - reloc_info_writer_.pos());
+ MemMove(new_buffer + new_buffer_size - curently_used_size,
+ reloc_info_writer_.pos(), curently_used_size);
+
+ reloc_info_writer_.Reposition(
+ new_buffer + new_buffer_size - curently_used_size,
+ reloc_info_writer_.last_pc());
+
+ DeleteArray(buffer_);
+ buffer_ = new_buffer;
+ buffer_size_ = new_buffer_size;
+ }
+
+ RelocInfoWriter reloc_info_writer_;
+ byte* buffer_;
+ int buffer_size_;
+
+ static const int kBufferGap = RelocInfoWriter::kMaxSize;
+ static const int kMaximalBufferSize = 512*MB;
+};
+
+
+// Patch positions in code (changes relocation info section) and possibly
+// returns new instance of code.
+static Handle<Code> PatchPositionsInCode(
+ Handle<Code> code,
+ Handle<JSArray> position_change_array) {
+ Isolate* isolate = code->GetIsolate();
+
+ RelocInfoBuffer buffer_writer(code->relocation_size(),
+ code->instruction_start());
+
+ {
+ for (RelocIterator it(*code); !it.done(); it.next()) {
+ RelocInfo* rinfo = it.rinfo();
+ if (RelocInfo::IsPosition(rinfo->rmode())) {
+ int position = static_cast<int>(rinfo->data());
+ int new_position = TranslatePosition(position,
+ position_change_array);
+ if (position != new_position) {
+ RelocInfo info_copy(rinfo->pc(), rinfo->rmode(), new_position, NULL);
+ buffer_writer.Write(&info_copy);
+ continue;
+ }
+ }
+ if (RelocInfo::IsRealRelocMode(rinfo->rmode())) {
+ buffer_writer.Write(it.rinfo());
+ }
+ }
+ }
+
+ Vector<byte> buffer = buffer_writer.GetResult();
+
+ if (buffer.length() == code->relocation_size()) {
+ // Simply patch relocation area of code.
+ MemCopy(code->relocation_start(), buffer.start(), buffer.length());
+ return code;
+ } else {
+ // Relocation info section now has different size. We cannot simply
+ // rewrite it inside code object. Instead we have to create a new
+ // code object.
+ Handle<Code> result(isolate->factory()->CopyCode(code, buffer));
+ return result;
+ }
+}
+
+
+void LiveEdit::PatchFunctionPositions(Handle<JSArray> shared_info_array,
+ Handle<JSArray> position_change_array) {
+ SharedInfoWrapper shared_info_wrapper(shared_info_array);
+ Handle<SharedFunctionInfo> info = shared_info_wrapper.GetInfo();
+
+ int old_function_start = info->start_position();
+ int new_function_start = TranslatePosition(old_function_start,
+ position_change_array);
+ int new_function_end = TranslatePosition(info->end_position(),
+ position_change_array);
+ int new_function_token_pos =
+ TranslatePosition(info->function_token_position(), position_change_array);
+
+ info->set_start_position(new_function_start);
+ info->set_end_position(new_function_end);
+ info->set_function_token_position(new_function_token_pos);
+
+ if (info->code()->kind() == Code::FUNCTION) {
+ // Patch relocation info section of the code.
+ Handle<Code> patched_code = PatchPositionsInCode(Handle<Code>(info->code()),
+ position_change_array);
+ if (*patched_code != info->code()) {
+ // Replace all references to the code across the heap. In particular,
+ // some stubs may refer to this code and this code may be being executed
+ // on stack (it is safe to substitute the code object on stack, because
+ // we only change the structure of rinfo and leave instructions
+ // untouched).
+ ReplaceCodeObject(Handle<Code>(info->code()), patched_code);
+ }
+ }
+}
+
+
+static Handle<Script> CreateScriptCopy(Handle<Script> original) {
+ Isolate* isolate = original->GetIsolate();
+
+ Handle<String> original_source(String::cast(original->source()));
+ Handle<Script> copy = isolate->factory()->NewScript(original_source);
+
+ copy->set_name(original->name());
+ copy->set_line_offset(original->line_offset());
+ copy->set_column_offset(original->column_offset());
+ copy->set_type(original->type());
+ copy->set_context_data(original->context_data());
+ copy->set_eval_from_shared(original->eval_from_shared());
+ copy->set_eval_from_instructions_offset(
+ original->eval_from_instructions_offset());
+
+ // Copy all the flags, but clear compilation state.
+ copy->set_flags(original->flags());
+ copy->set_compilation_state(Script::COMPILATION_STATE_INITIAL);
+
+ return copy;
+}
+
+
+Handle<Object> LiveEdit::ChangeScriptSource(Handle<Script> original_script,
+ Handle<String> new_source,
+ Handle<Object> old_script_name) {
+ Isolate* isolate = original_script->GetIsolate();
+ Handle<Object> old_script_object;
+ if (old_script_name->IsString()) {
+ Handle<Script> old_script = CreateScriptCopy(original_script);
+ old_script->set_name(String::cast(*old_script_name));
+ old_script_object = old_script;
+ isolate->debug()->OnAfterCompile(old_script);
+ } else {
+ old_script_object = isolate->factory()->null_value();
+ }
+
+ original_script->set_source(*new_source);
+
+ // Drop line ends so that they will be recalculated.
+ original_script->set_line_ends(isolate->heap()->undefined_value());
+
+ return old_script_object;
+}
+
+
+
+void LiveEdit::ReplaceRefToNestedFunction(
+ Handle<JSValue> parent_function_wrapper,
+ Handle<JSValue> orig_function_wrapper,
+ Handle<JSValue> subst_function_wrapper) {
+
+ Handle<SharedFunctionInfo> parent_shared =
+ UnwrapSharedFunctionInfoFromJSValue(parent_function_wrapper);
+ Handle<SharedFunctionInfo> orig_shared =
+ UnwrapSharedFunctionInfoFromJSValue(orig_function_wrapper);
+ Handle<SharedFunctionInfo> subst_shared =
+ UnwrapSharedFunctionInfoFromJSValue(subst_function_wrapper);
+
+ for (RelocIterator it(parent_shared->code()); !it.done(); it.next()) {
+ if (it.rinfo()->rmode() == RelocInfo::EMBEDDED_OBJECT) {
+ if (it.rinfo()->target_object() == *orig_shared) {
+ it.rinfo()->set_target_object(*subst_shared);
+ }
+ }
+ }
+}
+
+
+// Check an activation against list of functions. If there is a function
+// that matches, its status in result array is changed to status argument value.
+static bool CheckActivation(Handle<JSArray> shared_info_array,
+ Handle<JSArray> result,
+ StackFrame* frame,
+ LiveEdit::FunctionPatchabilityStatus status) {
+ if (!frame->is_java_script()) return false;
+
+ Handle<JSFunction> function(JavaScriptFrame::cast(frame)->function());
+
+ Isolate* isolate = shared_info_array->GetIsolate();
+ int len = GetArrayLength(shared_info_array);
+ for (int i = 0; i < len; i++) {
+ HandleScope scope(isolate);
+ Handle<Object> element =
+ Object::GetElement(isolate, shared_info_array, i).ToHandleChecked();
+ Handle<JSValue> jsvalue = Handle<JSValue>::cast(element);
+ Handle<SharedFunctionInfo> shared =
+ UnwrapSharedFunctionInfoFromJSValue(jsvalue);
+
+ if (function->Inlines(*shared)) {
+ SetElementSloppy(result, i, Handle<Smi>(Smi::FromInt(status), isolate));
+ return true;
+ }
+ }
+ return false;
+}
+
+
+// Iterates over handler chain and removes all elements that are inside
+// frames being dropped.
+static bool FixTryCatchHandler(StackFrame* top_frame,
+ StackFrame* bottom_frame) {
+ Address* pointer_address =
+ &Memory::Address_at(top_frame->isolate()->get_address_from_id(
+ Isolate::kHandlerAddress));
+
+ while (*pointer_address < top_frame->sp()) {
+ pointer_address = &Memory::Address_at(*pointer_address);
+ }
+ Address* above_frame_address = pointer_address;
+ while (*pointer_address < bottom_frame->fp()) {
+ pointer_address = &Memory::Address_at(*pointer_address);
+ }
+ bool change = *above_frame_address != *pointer_address;
+ *above_frame_address = *pointer_address;
+ return change;
+}
+
+
+// Initializes an artificial stack frame. The data it contains is used for:
+// a. successful work of frame dropper code which eventually gets control,
+// b. being compatible with regular stack structure for various stack
+// iterators.
+// Returns address of stack allocated pointer to restarted function,
+// the value that is called 'restarter_frame_function_pointer'. The value
+// at this address (possibly updated by GC) may be used later when preparing
+// 'step in' operation.
+// Frame structure (conforms InternalFrame structure):
+// -- code
+// -- SMI maker
+// -- function (slot is called "context")
+// -- frame base
+static Object** SetUpFrameDropperFrame(StackFrame* bottom_js_frame,
+ Handle<Code> code) {
+ DCHECK(bottom_js_frame->is_java_script());
+
+ Address fp = bottom_js_frame->fp();
+
+ // Move function pointer into "context" slot.
+ Memory::Object_at(fp + StandardFrameConstants::kContextOffset) =
+ Memory::Object_at(fp + JavaScriptFrameConstants::kFunctionOffset);
+
+ Memory::Object_at(fp + InternalFrameConstants::kCodeOffset) = *code;
+ Memory::Object_at(fp + StandardFrameConstants::kMarkerOffset) =
+ Smi::FromInt(StackFrame::INTERNAL);
+
+ return reinterpret_cast<Object**>(&Memory::Object_at(
+ fp + StandardFrameConstants::kContextOffset));
+}
+
+
+// Removes specified range of frames from stack. There may be 1 or more
+// frames in range. Anyway the bottom frame is restarted rather than dropped,
+// and therefore has to be a JavaScript frame.
+// Returns error message or NULL.
+static const char* DropFrames(Vector<StackFrame*> frames,
+ int top_frame_index,
+ int bottom_js_frame_index,
+ LiveEdit::FrameDropMode* mode,
+ Object*** restarter_frame_function_pointer) {
+ if (!LiveEdit::kFrameDropperSupported) {
+ return "Stack manipulations are not supported in this architecture.";
+ }
+
+ StackFrame* pre_top_frame = frames[top_frame_index - 1];
+ StackFrame* top_frame = frames[top_frame_index];
+ StackFrame* bottom_js_frame = frames[bottom_js_frame_index];
+
+ DCHECK(bottom_js_frame->is_java_script());
+
+ // Check the nature of the top frame.
+ Isolate* isolate = bottom_js_frame->isolate();
+ Code* pre_top_frame_code = pre_top_frame->LookupCode();
+ bool frame_has_padding = true;
+ if (pre_top_frame_code->is_inline_cache_stub() &&
+ pre_top_frame_code->is_debug_stub()) {
+ // OK, we can drop inline cache calls.
+ *mode = LiveEdit::FRAME_DROPPED_IN_IC_CALL;
+ } else if (pre_top_frame_code ==
+ isolate->builtins()->builtin(Builtins::kSlot_DebugBreak)) {
+ // OK, we can drop debug break slot.
+ *mode = LiveEdit::FRAME_DROPPED_IN_DEBUG_SLOT_CALL;
+ } else if (pre_top_frame_code ==
+ isolate->builtins()->builtin(Builtins::kFrameDropper_LiveEdit)) {
+ // OK, we can drop our own code.
+ pre_top_frame = frames[top_frame_index - 2];
+ top_frame = frames[top_frame_index - 1];
+ *mode = LiveEdit::CURRENTLY_SET_MODE;
+ frame_has_padding = false;
+ } else if (pre_top_frame_code ==
+ isolate->builtins()->builtin(Builtins::kReturn_DebugBreak)) {
+ *mode = LiveEdit::FRAME_DROPPED_IN_RETURN_CALL;
+ } else if (pre_top_frame_code->kind() == Code::STUB &&
+ CodeStub::GetMajorKey(pre_top_frame_code) == CodeStub::CEntry) {
+ // Entry from our unit tests on 'debugger' statement.
+ // It's fine, we support this case.
+ *mode = LiveEdit::FRAME_DROPPED_IN_DIRECT_CALL;
+ // We don't have a padding from 'debugger' statement call.
+ // Here the stub is CEntry, it's not debug-only and can't be padded.
+ // If anyone would complain, a proxy padded stub could be added.
+ frame_has_padding = false;
+ } else if (pre_top_frame->type() == StackFrame::ARGUMENTS_ADAPTOR) {
+ // This must be adaptor that remain from the frame dropping that
+ // is still on stack. A frame dropper frame must be above it.
+ DCHECK(frames[top_frame_index - 2]->LookupCode() ==
+ isolate->builtins()->builtin(Builtins::kFrameDropper_LiveEdit));
+ pre_top_frame = frames[top_frame_index - 3];
+ top_frame = frames[top_frame_index - 2];
+ *mode = LiveEdit::CURRENTLY_SET_MODE;
+ frame_has_padding = false;
+ } else {
+ return "Unknown structure of stack above changing function";
+ }
+
+ Address unused_stack_top = top_frame->sp();
+ int new_frame_size = LiveEdit::kFrameDropperFrameSize * kPointerSize;
+ Address unused_stack_bottom = bottom_js_frame->fp()
+ - new_frame_size + kPointerSize; // Bigger address end is exclusive.
+
+ Address* top_frame_pc_address = top_frame->pc_address();
+
+ // top_frame may be damaged below this point. Do not used it.
+ DCHECK(!(top_frame = NULL));
+
+ if (unused_stack_top > unused_stack_bottom) {
+ if (frame_has_padding) {
+ int shortage_bytes =
+ static_cast<int>(unused_stack_top - unused_stack_bottom);
+
+ Address padding_start = pre_top_frame->fp() -
+ LiveEdit::kFrameDropperFrameSize * kPointerSize;
+
+ Address padding_pointer = padding_start;
+ Smi* padding_object = Smi::FromInt(LiveEdit::kFramePaddingValue);
+ while (Memory::Object_at(padding_pointer) == padding_object) {
+ padding_pointer -= kPointerSize;
+ }
+ int padding_counter =
+ Smi::cast(Memory::Object_at(padding_pointer))->value();
+ if (padding_counter * kPointerSize < shortage_bytes) {
+ return "Not enough space for frame dropper frame "
+ "(even with padding frame)";
+ }
+ Memory::Object_at(padding_pointer) =
+ Smi::FromInt(padding_counter - shortage_bytes / kPointerSize);
+
+ StackFrame* pre_pre_frame = frames[top_frame_index - 2];
+
+ MemMove(padding_start + kPointerSize - shortage_bytes,
+ padding_start + kPointerSize,
+ LiveEdit::kFrameDropperFrameSize * kPointerSize);
+
+ pre_top_frame->UpdateFp(pre_top_frame->fp() - shortage_bytes);
+ pre_pre_frame->SetCallerFp(pre_top_frame->fp());
+ unused_stack_top -= shortage_bytes;
+
+ STATIC_ASSERT(sizeof(Address) == kPointerSize);
+ top_frame_pc_address -= shortage_bytes / kPointerSize;
+ } else {
+ return "Not enough space for frame dropper frame";
+ }
+ }
+
+ // Committing now. After this point we should return only NULL value.
+
+ FixTryCatchHandler(pre_top_frame, bottom_js_frame);
+ // Make sure FixTryCatchHandler is idempotent.
+ DCHECK(!FixTryCatchHandler(pre_top_frame, bottom_js_frame));
+
+ Handle<Code> code = isolate->builtins()->FrameDropper_LiveEdit();
+ *top_frame_pc_address = code->entry();
+ pre_top_frame->SetCallerFp(bottom_js_frame->fp());
+
+ *restarter_frame_function_pointer =
+ SetUpFrameDropperFrame(bottom_js_frame, code);
+
+ DCHECK((**restarter_frame_function_pointer)->IsJSFunction());
+
+ for (Address a = unused_stack_top;
+ a < unused_stack_bottom;
+ a += kPointerSize) {
+ Memory::Object_at(a) = Smi::FromInt(0);
+ }
+
+ return NULL;
+}
+
+
+// Describes a set of call frames that execute any of listed functions.
+// Finding no such frames does not mean error.
+class MultipleFunctionTarget {
+ public:
+ MultipleFunctionTarget(Handle<JSArray> shared_info_array,
+ Handle<JSArray> result)
+ : m_shared_info_array(shared_info_array),
+ m_result(result) {}
+ bool MatchActivation(StackFrame* frame,
+ LiveEdit::FunctionPatchabilityStatus status) {
+ return CheckActivation(m_shared_info_array, m_result, frame, status);
+ }
+ const char* GetNotFoundMessage() const {
+ return NULL;
+ }
+ private:
+ Handle<JSArray> m_shared_info_array;
+ Handle<JSArray> m_result;
+};
+
+
+// Drops all call frame matched by target and all frames above them.
+template <typename TARGET>
+static const char* DropActivationsInActiveThreadImpl(Isolate* isolate,
+ TARGET& target, // NOLINT
+ bool do_drop) {
+ Debug* debug = isolate->debug();
+ Zone zone;
+ Vector<StackFrame*> frames = CreateStackMap(isolate, &zone);
+
+
+ int top_frame_index = -1;
+ int frame_index = 0;
+ for (; frame_index < frames.length(); frame_index++) {
+ StackFrame* frame = frames[frame_index];
+ if (frame->id() == debug->break_frame_id()) {
+ top_frame_index = frame_index;
+ break;
+ }
+ if (target.MatchActivation(
+ frame, LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE)) {
+ // We are still above break_frame. It is not a target frame,
+ // it is a problem.
+ return "Debugger mark-up on stack is not found";
+ }
+ }
+
+ if (top_frame_index == -1) {
+ // We haven't found break frame, but no function is blocking us anyway.
+ return target.GetNotFoundMessage();
+ }
+
+ bool target_frame_found = false;
+ int bottom_js_frame_index = top_frame_index;
+ bool non_droppable_frame_found = false;
+ LiveEdit::FunctionPatchabilityStatus non_droppable_reason;
+
+ for (; frame_index < frames.length(); frame_index++) {
+ StackFrame* frame = frames[frame_index];
+ if (frame->is_exit()) {
+ non_droppable_frame_found = true;
+ non_droppable_reason = LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE;
+ break;
+ }
+ if (frame->is_java_script() &&
+ JavaScriptFrame::cast(frame)->function()->shared()->is_generator()) {
+ non_droppable_frame_found = true;
+ non_droppable_reason = LiveEdit::FUNCTION_BLOCKED_UNDER_GENERATOR;
+ break;
+ }
+ if (target.MatchActivation(
+ frame, LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) {
+ target_frame_found = true;
+ bottom_js_frame_index = frame_index;
+ }
+ }
+
+ if (non_droppable_frame_found) {
+ // There is a C or generator frame on stack. We can't drop C frames, and we
+ // can't restart generators. Check that there are no target frames below
+ // them.
+ for (; frame_index < frames.length(); frame_index++) {
+ StackFrame* frame = frames[frame_index];
+ if (frame->is_java_script()) {
+ if (target.MatchActivation(frame, non_droppable_reason)) {
+ // Fail.
+ return NULL;
+ }
+ }
+ }
+ }
+
+ if (!do_drop) {
+ // We are in check-only mode.
+ return NULL;
+ }
+
+ if (!target_frame_found) {
+ // Nothing to drop.
+ return target.GetNotFoundMessage();
+ }
+
+ LiveEdit::FrameDropMode drop_mode = LiveEdit::FRAMES_UNTOUCHED;
+ Object** restarter_frame_function_pointer = NULL;
+ const char* error_message = DropFrames(frames, top_frame_index,
+ bottom_js_frame_index, &drop_mode,
+ &restarter_frame_function_pointer);
+
+ if (error_message != NULL) {
+ return error_message;
+ }
+
+ // Adjust break_frame after some frames has been dropped.
+ StackFrame::Id new_id = StackFrame::NO_ID;
+ for (int i = bottom_js_frame_index + 1; i < frames.length(); i++) {
+ if (frames[i]->type() == StackFrame::JAVA_SCRIPT) {
+ new_id = frames[i]->id();
+ break;
+ }
+ }
+ debug->FramesHaveBeenDropped(
+ new_id, drop_mode, restarter_frame_function_pointer);
+ return NULL;
+}
+
+
+// Fills result array with statuses of functions. Modifies the stack
+// removing all listed function if possible and if do_drop is true.
+static const char* DropActivationsInActiveThread(
+ Handle<JSArray> shared_info_array, Handle<JSArray> result, bool do_drop) {
+ MultipleFunctionTarget target(shared_info_array, result);
+ Isolate* isolate = shared_info_array->GetIsolate();
+
+ const char* message =
+ DropActivationsInActiveThreadImpl(isolate, target, do_drop);
+ if (message) {
+ return message;
+ }
+
+ int array_len = GetArrayLength(shared_info_array);
+
+ // Replace "blocked on active" with "replaced on active" status.
+ for (int i = 0; i < array_len; i++) {
+ Handle<Object> obj =
+ Object::GetElement(isolate, result, i).ToHandleChecked();
+ if (*obj == Smi::FromInt(LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) {
+ Handle<Object> replaced(
+ Smi::FromInt(LiveEdit::FUNCTION_REPLACED_ON_ACTIVE_STACK), isolate);
+ SetElementSloppy(result, i, replaced);
+ }
+ }
+ return NULL;
+}
+
+
+bool LiveEdit::FindActiveGenerators(Handle<FixedArray> shared_info_array,
+ Handle<FixedArray> result,
+ int len) {
+ Isolate* isolate = shared_info_array->GetIsolate();
+ bool found_suspended_activations = false;
+
+ DCHECK_LE(len, result->length());
+
+ FunctionPatchabilityStatus active = FUNCTION_BLOCKED_ACTIVE_GENERATOR;
+
+ Heap* heap = isolate->heap();
+ HeapIterator iterator(heap);
+ HeapObject* obj = NULL;
+ while ((obj = iterator.next()) != NULL) {
+ if (!obj->IsJSGeneratorObject()) continue;
+
+ JSGeneratorObject* gen = JSGeneratorObject::cast(obj);
+ if (gen->is_closed()) continue;
+
+ HandleScope scope(isolate);
+
+ for (int i = 0; i < len; i++) {
+ Handle<JSValue> jsvalue =
+ Handle<JSValue>::cast(FixedArray::get(shared_info_array, i));
+ Handle<SharedFunctionInfo> shared =
+ UnwrapSharedFunctionInfoFromJSValue(jsvalue);
+
+ if (gen->function()->shared() == *shared) {
+ result->set(i, Smi::FromInt(active));
+ found_suspended_activations = true;
+ }
+ }
+ }
+
+ return found_suspended_activations;
+}
+
+
+class InactiveThreadActivationsChecker : public ThreadVisitor {
+ public:
+ InactiveThreadActivationsChecker(Handle<JSArray> shared_info_array,
+ Handle<JSArray> result)
+ : shared_info_array_(shared_info_array), result_(result),
+ has_blocked_functions_(false) {
+ }
+ void VisitThread(Isolate* isolate, ThreadLocalTop* top) {
+ for (StackFrameIterator it(isolate, top); !it.done(); it.Advance()) {
+ has_blocked_functions_ |= CheckActivation(
+ shared_info_array_, result_, it.frame(),
+ LiveEdit::FUNCTION_BLOCKED_ON_OTHER_STACK);
+ }
+ }
+ bool HasBlockedFunctions() {
+ return has_blocked_functions_;
+ }
+
+ private:
+ Handle<JSArray> shared_info_array_;
+ Handle<JSArray> result_;
+ bool has_blocked_functions_;
+};
+
+
+Handle<JSArray> LiveEdit::CheckAndDropActivations(
+ Handle<JSArray> shared_info_array, bool do_drop) {
+ Isolate* isolate = shared_info_array->GetIsolate();
+ int len = GetArrayLength(shared_info_array);
+
+ DCHECK(shared_info_array->HasFastElements());
+ Handle<FixedArray> shared_info_array_elements(
+ FixedArray::cast(shared_info_array->elements()));
+
+ Handle<JSArray> result = isolate->factory()->NewJSArray(len);
+ Handle<FixedArray> result_elements =
+ JSObject::EnsureWritableFastElements(result);
+
+ // Fill the default values.
+ for (int i = 0; i < len; i++) {
+ FunctionPatchabilityStatus status = FUNCTION_AVAILABLE_FOR_PATCH;
+ result_elements->set(i, Smi::FromInt(status));
+ }
+
+ // Scan the heap for active generators -- those that are either currently
+ // running (as we wouldn't want to restart them, because we don't know where
+ // to restart them from) or suspended. Fail if any one corresponds to the set
+ // of functions being edited.
+ if (FindActiveGenerators(shared_info_array_elements, result_elements, len)) {
+ return result;
+ }
+
+ // Check inactive threads. Fail if some functions are blocked there.
+ InactiveThreadActivationsChecker inactive_threads_checker(shared_info_array,
+ result);
+ isolate->thread_manager()->IterateArchivedThreads(
+ &inactive_threads_checker);
+ if (inactive_threads_checker.HasBlockedFunctions()) {
+ return result;
+ }
+
+ // Try to drop activations from the current stack.
+ const char* error_message =
+ DropActivationsInActiveThread(shared_info_array, result, do_drop);
+ if (error_message != NULL) {
+ // Add error message as an array extra element.
+ Handle<String> str =
+ isolate->factory()->NewStringFromAsciiChecked(error_message);
+ SetElementSloppy(result, len, str);
+ }
+ return result;
+}
+
+
+// Describes a single callframe a target. Not finding this frame
+// means an error.
+class SingleFrameTarget {
+ public:
+ explicit SingleFrameTarget(JavaScriptFrame* frame)
+ : m_frame(frame),
+ m_saved_status(LiveEdit::FUNCTION_AVAILABLE_FOR_PATCH) {}
+
+ bool MatchActivation(StackFrame* frame,
+ LiveEdit::FunctionPatchabilityStatus status) {
+ if (frame->fp() == m_frame->fp()) {
+ m_saved_status = status;
+ return true;
+ }
+ return false;
+ }
+ const char* GetNotFoundMessage() const {
+ return "Failed to found requested frame";
+ }
+ LiveEdit::FunctionPatchabilityStatus saved_status() {
+ return m_saved_status;
+ }
+ private:
+ JavaScriptFrame* m_frame;
+ LiveEdit::FunctionPatchabilityStatus m_saved_status;
+};
+
+
+// Finds a drops required frame and all frames above.
+// Returns error message or NULL.
+const char* LiveEdit::RestartFrame(JavaScriptFrame* frame) {
+ SingleFrameTarget target(frame);
+
+ const char* result =
+ DropActivationsInActiveThreadImpl(frame->isolate(), target, true);
+ if (result != NULL) {
+ return result;
+ }
+ if (target.saved_status() == LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE) {
+ return "Function is blocked under native code";
+ }
+ if (target.saved_status() == LiveEdit::FUNCTION_BLOCKED_UNDER_GENERATOR) {
+ return "Function is blocked under a generator activation";
+ }
+ return NULL;
+}
+
+
+LiveEditFunctionTracker::LiveEditFunctionTracker(Isolate* isolate,
+ FunctionLiteral* fun)
+ : isolate_(isolate) {
+ if (isolate_->active_function_info_listener() != NULL) {
+ isolate_->active_function_info_listener()->FunctionStarted(fun);
+ }
+}
+
+
+LiveEditFunctionTracker::~LiveEditFunctionTracker() {
+ if (isolate_->active_function_info_listener() != NULL) {
+ isolate_->active_function_info_listener()->FunctionDone();
+ }
+}
+
+
+void LiveEditFunctionTracker::RecordFunctionInfo(
+ Handle<SharedFunctionInfo> info, FunctionLiteral* lit,
+ Zone* zone) {
+ if (isolate_->active_function_info_listener() != NULL) {
+ isolate_->active_function_info_listener()->FunctionInfo(info, lit->scope(),
+ zone);
+ }
+}
+
+
+void LiveEditFunctionTracker::RecordRootFunctionInfo(Handle<Code> code) {
+ isolate_->active_function_info_listener()->FunctionCode(code);
+}
+
+
+bool LiveEditFunctionTracker::IsActive(Isolate* isolate) {
+ return isolate->active_function_info_listener() != NULL;
+}
+
+} // namespace internal
+} // namespace v8
--- /dev/null
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_DEBUG_LIVEEDIT_H_
+#define V8_DEBUG_LIVEEDIT_H_
+
+
+// Live Edit feature implementation.
+// User should be able to change script on already running VM. This feature
+// matches hot swap features in other frameworks.
+//
+// The basic use-case is when user spots some mistake in function body
+// from debugger and wishes to change the algorithm without restart.
+//
+// A single change always has a form of a simple replacement (in pseudo-code):
+// script.source[positions, positions+length] = new_string;
+// Implementation first determines, which function's body includes this
+// change area. Then both old and new versions of script are fully compiled
+// in order to analyze, whether the function changed its outer scope
+// expectations (or number of parameters). If it didn't, function's code is
+// patched with a newly compiled code. If it did change, enclosing function
+// gets patched. All inner functions are left untouched, whatever happened
+// to them in a new script version. However, new version of code will
+// instantiate newly compiled functions.
+
+
+#include "src/allocation.h"
+#include "src/compiler.h"
+
+namespace v8 {
+namespace internal {
+
+// This class collects some specific information on structure of functions
+// in a particular script. It gets called from compiler all the time, but
+// actually records any data only when liveedit operation is in process;
+// in any other time this class is very cheap.
+//
+// The primary interest of the Tracker is to record function scope structures
+// in order to analyze whether function code maybe safely patched (with new
+// code successfully reading existing data from function scopes). The Tracker
+// also collects compiled function codes.
+class LiveEditFunctionTracker {
+ public:
+ explicit LiveEditFunctionTracker(Isolate* isolate, FunctionLiteral* fun);
+ ~LiveEditFunctionTracker();
+ void RecordFunctionInfo(Handle<SharedFunctionInfo> info,
+ FunctionLiteral* lit, Zone* zone);
+ void RecordRootFunctionInfo(Handle<Code> code);
+
+ static bool IsActive(Isolate* isolate);
+
+ private:
+ Isolate* isolate_;
+};
+
+
+class LiveEdit : AllStatic {
+ public:
+ // Describes how exactly a frame has been dropped from stack.
+ enum FrameDropMode {
+ // No frame has been dropped.
+ FRAMES_UNTOUCHED,
+ // The top JS frame had been calling IC stub. IC stub mustn't be called now.
+ FRAME_DROPPED_IN_IC_CALL,
+ // The top JS frame had been calling debug break slot stub. Patch the
+ // address this stub jumps to in the end.
+ FRAME_DROPPED_IN_DEBUG_SLOT_CALL,
+ // The top JS frame had been calling some C++ function. The return address
+ // gets patched automatically.
+ FRAME_DROPPED_IN_DIRECT_CALL,
+ FRAME_DROPPED_IN_RETURN_CALL,
+ CURRENTLY_SET_MODE
+ };
+
+ static void InitializeThreadLocal(Debug* debug);
+
+ static bool SetAfterBreakTarget(Debug* debug);
+
+ MUST_USE_RESULT static MaybeHandle<JSArray> GatherCompileInfo(
+ Handle<Script> script,
+ Handle<String> source);
+
+ static void WrapSharedFunctionInfos(Handle<JSArray> array);
+
+ static void ReplaceFunctionCode(Handle<JSArray> new_compile_info_array,
+ Handle<JSArray> shared_info_array);
+
+ static void FunctionSourceUpdated(Handle<JSArray> shared_info_array);
+
+ // Updates script field in FunctionSharedInfo.
+ static void SetFunctionScript(Handle<JSValue> function_wrapper,
+ Handle<Object> script_handle);
+
+ static void PatchFunctionPositions(Handle<JSArray> shared_info_array,
+ Handle<JSArray> position_change_array);
+
+ // For a script updates its source field. If old_script_name is provided
+ // (i.e. is a String), also creates a copy of the script with its original
+ // source and sends notification to debugger.
+ static Handle<Object> ChangeScriptSource(Handle<Script> original_script,
+ Handle<String> new_source,
+ Handle<Object> old_script_name);
+
+ // In a code of a parent function replaces original function as embedded
+ // object with a substitution one.
+ static void ReplaceRefToNestedFunction(Handle<JSValue> parent_function_shared,
+ Handle<JSValue> orig_function_shared,
+ Handle<JSValue> subst_function_shared);
+
+ // Find open generator activations, and set corresponding "result" elements to
+ // FUNCTION_BLOCKED_ACTIVE_GENERATOR.
+ static bool FindActiveGenerators(Handle<FixedArray> shared_info_array,
+ Handle<FixedArray> result, int len);
+
+ // Checks listed functions on stack and return array with corresponding
+ // FunctionPatchabilityStatus statuses; extra array element may
+ // contain general error message. Modifies the current stack and
+ // has restart the lowest found frames and drops all other frames above
+ // if possible and if do_drop is true.
+ static Handle<JSArray> CheckAndDropActivations(
+ Handle<JSArray> shared_info_array, bool do_drop);
+
+ // Restarts the call frame and completely drops all frames above it.
+ // Return error message or NULL.
+ static const char* RestartFrame(JavaScriptFrame* frame);
+
+ // A copy of this is in liveedit.js.
+ enum FunctionPatchabilityStatus {
+ FUNCTION_AVAILABLE_FOR_PATCH = 1,
+ FUNCTION_BLOCKED_ON_ACTIVE_STACK = 2,
+ FUNCTION_BLOCKED_ON_OTHER_STACK = 3,
+ FUNCTION_BLOCKED_UNDER_NATIVE_CODE = 4,
+ FUNCTION_REPLACED_ON_ACTIVE_STACK = 5,
+ FUNCTION_BLOCKED_UNDER_GENERATOR = 6,
+ FUNCTION_BLOCKED_ACTIVE_GENERATOR = 7
+ };
+
+ // Compares 2 strings line-by-line, then token-wise and returns diff in form
+ // of array of triplets (pos1, pos1_end, pos2_end) describing list
+ // of diff chunks.
+ static Handle<JSArray> CompareStrings(Handle<String> s1,
+ Handle<String> s2);
+
+ // Architecture-specific constant.
+ static const bool kFrameDropperSupported;
+
+ /**
+ * Defines layout of a stack frame that supports padding. This is a regular
+ * internal frame that has a flexible stack structure. LiveEdit can shift
+ * its lower part up the stack, taking up the 'padding' space when additional
+ * stack memory is required.
+ * Such frame is expected immediately above the topmost JavaScript frame.
+ *
+ * Stack Layout:
+ * --- Top
+ * LiveEdit routine frames
+ * ---
+ * C frames of debug handler
+ * ---
+ * ...
+ * ---
+ * An internal frame that has n padding words:
+ * - any number of words as needed by code -- upper part of frame
+ * - padding size: a Smi storing n -- current size of padding
+ * - padding: n words filled with kPaddingValue in form of Smi
+ * - 3 context/type words of a regular InternalFrame
+ * - fp
+ * ---
+ * Topmost JavaScript frame
+ * ---
+ * ...
+ * --- Bottom
+ */
+ // A size of frame base including fp. Padding words starts right above
+ // the base.
+ static const int kFrameDropperFrameSize = 4;
+ // A number of words that should be reserved on stack for the LiveEdit use.
+ // Stored on stack in form of Smi.
+ static const int kFramePaddingInitialSize = 1;
+ // A value that padding words are filled with (in form of Smi). Going
+ // bottom-top, the first word not having this value is a counter word.
+ static const int kFramePaddingValue = kFramePaddingInitialSize + 1;
+};
+
+
+// A general-purpose comparator between 2 arrays.
+class Comparator {
+ public:
+ // Holds 2 arrays of some elements allowing to compare any pair of
+ // element from the first array and element from the second array.
+ class Input {
+ public:
+ virtual int GetLength1() = 0;
+ virtual int GetLength2() = 0;
+ virtual bool Equals(int index1, int index2) = 0;
+
+ protected:
+ virtual ~Input() {}
+ };
+
+ // Receives compare result as a series of chunks.
+ class Output {
+ public:
+ // Puts another chunk in result list. Note that technically speaking
+ // only 3 arguments actually needed with 4th being derivable.
+ virtual void AddChunk(int pos1, int pos2, int len1, int len2) = 0;
+
+ protected:
+ virtual ~Output() {}
+ };
+
+ // Finds the difference between 2 arrays of elements.
+ static void CalculateDifference(Input* input,
+ Output* result_writer);
+};
+
+
+
+// Simple helper class that creates more or less typed structures over
+// JSArray object. This is an adhoc method of passing structures from C++
+// to JavaScript.
+template<typename S>
+class JSArrayBasedStruct {
+ public:
+ static S Create(Isolate* isolate) {
+ Factory* factory = isolate->factory();
+ Handle<JSArray> array = factory->NewJSArray(S::kSize_);
+ return S(array);
+ }
+
+ static S cast(Object* object) {
+ JSArray* array = JSArray::cast(object);
+ Handle<JSArray> array_handle(array);
+ return S(array_handle);
+ }
+
+ explicit JSArrayBasedStruct(Handle<JSArray> array) : array_(array) {
+ }
+
+ Handle<JSArray> GetJSArray() {
+ return array_;
+ }
+
+ Isolate* isolate() const {
+ return array_->GetIsolate();
+ }
+
+ protected:
+ void SetField(int field_position, Handle<Object> value) {
+ Object::SetElement(isolate(), array_, field_position, value, SLOPPY)
+ .Assert();
+ }
+
+ void SetSmiValueField(int field_position, int value) {
+ SetField(field_position, Handle<Smi>(Smi::FromInt(value), isolate()));
+ }
+
+ Handle<Object> GetField(int field_position) {
+ return Object::GetElement(
+ isolate(), array_, field_position).ToHandleChecked();
+ }
+
+ int GetSmiValueField(int field_position) {
+ Handle<Object> res = GetField(field_position);
+ return Handle<Smi>::cast(res)->value();
+ }
+
+ private:
+ Handle<JSArray> array_;
+};
+
+
+// Represents some function compilation details. This structure will be used
+// from JavaScript. It contains Code object, which is kept wrapped
+// into a BlindReference for sanitizing reasons.
+class FunctionInfoWrapper : public JSArrayBasedStruct<FunctionInfoWrapper> {
+ public:
+ explicit FunctionInfoWrapper(Handle<JSArray> array)
+ : JSArrayBasedStruct<FunctionInfoWrapper>(array) {
+ }
+
+ void SetInitialProperties(Handle<String> name, int start_position,
+ int end_position, int param_num, int literal_count,
+ int parent_index);
+
+ void SetFunctionCode(Handle<Code> function_code,
+ Handle<HeapObject> code_scope_info);
+
+ void SetFunctionScopeInfo(Handle<Object> scope_info_array) {
+ this->SetField(kFunctionScopeInfoOffset_, scope_info_array);
+ }
+
+ void SetSharedFunctionInfo(Handle<SharedFunctionInfo> info);
+
+ int GetLiteralCount() {
+ return this->GetSmiValueField(kLiteralNumOffset_);
+ }
+
+ int GetParentIndex() {
+ return this->GetSmiValueField(kParentIndexOffset_);
+ }
+
+ Handle<Code> GetFunctionCode();
+
+ MaybeHandle<TypeFeedbackVector> GetFeedbackVector();
+
+ Handle<Object> GetCodeScopeInfo();
+
+ int GetStartPosition() {
+ return this->GetSmiValueField(kStartPositionOffset_);
+ }
+
+ int GetEndPosition() { return this->GetSmiValueField(kEndPositionOffset_); }
+
+ private:
+ static const int kFunctionNameOffset_ = 0;
+ static const int kStartPositionOffset_ = 1;
+ static const int kEndPositionOffset_ = 2;
+ static const int kParamNumOffset_ = 3;
+ static const int kCodeOffset_ = 4;
+ static const int kCodeScopeInfoOffset_ = 5;
+ static const int kFunctionScopeInfoOffset_ = 6;
+ static const int kParentIndexOffset_ = 7;
+ static const int kSharedFunctionInfoOffset_ = 8;
+ static const int kLiteralNumOffset_ = 9;
+ static const int kSize_ = 10;
+
+ friend class JSArrayBasedStruct<FunctionInfoWrapper>;
+};
+
+
+// Wraps SharedFunctionInfo along with some of its fields for passing it
+// back to JavaScript. SharedFunctionInfo object itself is additionally
+// wrapped into BlindReference for sanitizing reasons.
+class SharedInfoWrapper : public JSArrayBasedStruct<SharedInfoWrapper> {
+ public:
+ static bool IsInstance(Handle<JSArray> array) {
+ if (array->length() != Smi::FromInt(kSize_)) return false;
+ Handle<Object> element(
+ Object::GetElement(array->GetIsolate(),
+ array,
+ kSharedInfoOffset_).ToHandleChecked());
+ if (!element->IsJSValue()) return false;
+ return Handle<JSValue>::cast(element)->value()->IsSharedFunctionInfo();
+ }
+
+ explicit SharedInfoWrapper(Handle<JSArray> array)
+ : JSArrayBasedStruct<SharedInfoWrapper>(array) {
+ }
+
+ void SetProperties(Handle<String> name,
+ int start_position,
+ int end_position,
+ Handle<SharedFunctionInfo> info);
+
+ Handle<SharedFunctionInfo> GetInfo();
+
+ private:
+ static const int kFunctionNameOffset_ = 0;
+ static const int kStartPositionOffset_ = 1;
+ static const int kEndPositionOffset_ = 2;
+ static const int kSharedInfoOffset_ = 3;
+ static const int kSize_ = 4;
+
+ friend class JSArrayBasedStruct<SharedInfoWrapper>;
+};
+
+} } // namespace v8::internal
+
+#endif /* V8_DEBUG_LIVEEDIT_H_ */
--- /dev/null
+// Copyright 2012 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.
+
+// LiveEdit feature implementation. The script should be executed after
+// debug.js.
+
+// A LiveEdit namespace. It contains functions that modifies JavaScript code
+// according to changes of script source (if possible).
+//
+// When new script source is put in, the difference is calculated textually,
+// in form of list of delete/add/change chunks. The functions that include
+// change chunk(s) get recompiled, or their enclosing functions are
+// recompiled instead.
+// If the function may not be recompiled (e.g. it was completely erased in new
+// version of the script) it remains unchanged, but the code that could
+// create a new instance of this function goes away. An old version of script
+// is created to back up this obsolete function.
+// All unchanged functions have their positions updated accordingly.
+//
+// LiveEdit namespace is declared inside a single function constructor.
+
+"use strict";
+
+Debug.LiveEdit = new function() {
+
+ // Forward declaration for minifier.
+ var FunctionStatus;
+
+ // Applies the change to the script.
+ // The change is in form of list of chunks encoded in a single array as
+ // a series of triplets (pos1_start, pos1_end, pos2_end)
+ function ApplyPatchMultiChunk(script, diff_array, new_source, preview_only,
+ change_log) {
+
+ var old_source = script.source;
+
+ // Gather compile information about old version of script.
+ var old_compile_info = GatherCompileInfo(old_source, script);
+
+ // Build tree structures for old and new versions of the script.
+ var root_old_node = BuildCodeInfoTree(old_compile_info);
+
+ var pos_translator = new PosTranslator(diff_array);
+
+ // Analyze changes.
+ MarkChangedFunctions(root_old_node, pos_translator.GetChunks());
+
+ // Find all SharedFunctionInfo's that were compiled from this script.
+ FindLiveSharedInfos(root_old_node, script);
+
+ // Gather compile information about new version of script.
+ var new_compile_info;
+ try {
+ new_compile_info = GatherCompileInfo(new_source, script);
+ } catch (e) {
+ var failure =
+ new Failure("Failed to compile new version of script: " + e);
+ if (e instanceof SyntaxError) {
+ var details = {
+ type: "liveedit_compile_error",
+ syntaxErrorMessage: e.message
+ };
+ CopyErrorPositionToDetails(e, details);
+ failure.details = details;
+ }
+ throw failure;
+ }
+ var root_new_node = BuildCodeInfoTree(new_compile_info);
+
+ // Link recompiled script data with other data.
+ FindCorrespondingFunctions(root_old_node, root_new_node);
+
+ // Prepare to-do lists.
+ var replace_code_list = new Array();
+ var link_to_old_script_list = new Array();
+ var link_to_original_script_list = new Array();
+ var update_positions_list = new Array();
+
+ function HarvestTodo(old_node) {
+ function CollectDamaged(node) {
+ link_to_old_script_list.push(node);
+ for (var i = 0; i < node.children.length; i++) {
+ CollectDamaged(node.children[i]);
+ }
+ }
+
+ // Recursively collects all newly compiled functions that are going into
+ // business and should have link to the actual script updated.
+ function CollectNew(node_list) {
+ for (var i = 0; i < node_list.length; i++) {
+ link_to_original_script_list.push(node_list[i]);
+ CollectNew(node_list[i].children);
+ }
+ }
+
+ if (old_node.status == FunctionStatus.DAMAGED) {
+ CollectDamaged(old_node);
+ return;
+ }
+ if (old_node.status == FunctionStatus.UNCHANGED) {
+ update_positions_list.push(old_node);
+ } else if (old_node.status == FunctionStatus.SOURCE_CHANGED) {
+ update_positions_list.push(old_node);
+ } else if (old_node.status == FunctionStatus.CHANGED) {
+ replace_code_list.push(old_node);
+ CollectNew(old_node.unmatched_new_nodes);
+ }
+ for (var i = 0; i < old_node.children.length; i++) {
+ HarvestTodo(old_node.children[i]);
+ }
+ }
+
+ var preview_description = {
+ change_tree: DescribeChangeTree(root_old_node),
+ textual_diff: {
+ old_len: old_source.length,
+ new_len: new_source.length,
+ chunks: diff_array
+ },
+ updated: false
+ };
+
+ if (preview_only) {
+ return preview_description;
+ }
+
+ HarvestTodo(root_old_node);
+
+ // Collect shared infos for functions whose code need to be patched.
+ var replaced_function_infos = new Array();
+ for (var i = 0; i < replace_code_list.length; i++) {
+ var live_shared_function_infos =
+ replace_code_list[i].live_shared_function_infos;
+
+ if (live_shared_function_infos) {
+ for (var j = 0; j < live_shared_function_infos.length; j++) {
+ replaced_function_infos.push(live_shared_function_infos[j]);
+ }
+ }
+ }
+
+ // We haven't changed anything before this line yet.
+ // Committing all changes.
+
+ // Check that function being patched is not currently on stack or drop them.
+ var dropped_functions_number =
+ CheckStackActivations(replaced_function_infos, change_log);
+
+ // Our current implementation requires client to manually issue "step in"
+ // command for correct stack state if the stack was modified.
+ preview_description.stack_modified = dropped_functions_number != 0;
+
+ // Start with breakpoints. Convert their line/column positions and
+ // temporary remove.
+ var break_points_restorer = TemporaryRemoveBreakPoints(script, change_log);
+
+ var old_script;
+
+ // Create an old script only if there are function that should be linked
+ // to old version.
+ if (link_to_old_script_list.length == 0) {
+ %LiveEditReplaceScript(script, new_source, null);
+ old_script = UNDEFINED;
+ } else {
+ var old_script_name = CreateNameForOldScript(script);
+
+ // Update the script text and create a new script representing an old
+ // version of the script.
+ old_script = %LiveEditReplaceScript(script, new_source,
+ old_script_name);
+
+ var link_to_old_script_report = new Array();
+ change_log.push( { linked_to_old_script: link_to_old_script_report } );
+
+ // We need to link to old script all former nested functions.
+ for (var i = 0; i < link_to_old_script_list.length; i++) {
+ LinkToOldScript(link_to_old_script_list[i], old_script,
+ link_to_old_script_report);
+ }
+
+ preview_description.created_script_name = old_script_name;
+ }
+
+ // Link to an actual script all the functions that we are going to use.
+ for (var i = 0; i < link_to_original_script_list.length; i++) {
+ %LiveEditFunctionSetScript(
+ link_to_original_script_list[i].info.shared_function_info, script);
+ }
+
+ for (var i = 0; i < replace_code_list.length; i++) {
+ PatchFunctionCode(replace_code_list[i], change_log);
+ }
+
+ var position_patch_report = new Array();
+ change_log.push( {position_patched: position_patch_report} );
+
+ for (var i = 0; i < update_positions_list.length; i++) {
+ // TODO(LiveEdit): take into account whether it's source_changed or
+ // unchanged and whether positions changed at all.
+ PatchPositions(update_positions_list[i], diff_array,
+ position_patch_report);
+
+ if (update_positions_list[i].live_shared_function_infos) {
+ update_positions_list[i].live_shared_function_infos.
+ forEach(function (info) {
+ %LiveEditFunctionSourceUpdated(info.raw_array);
+ });
+ }
+ }
+
+ break_points_restorer(pos_translator, old_script);
+
+ preview_description.updated = true;
+ return preview_description;
+ }
+ // Function is public.
+ this.ApplyPatchMultiChunk = ApplyPatchMultiChunk;
+
+
+ // Fully compiles source string as a script. Returns Array of
+ // FunctionCompileInfo -- a descriptions of all functions of the script.
+ // Elements of array are ordered by start positions of functions (from top
+ // to bottom) in the source. Fields outer_index and next_sibling_index help
+ // to navigate the nesting structure of functions.
+ //
+ // All functions get compiled linked to script provided as parameter script.
+ // TODO(LiveEdit): consider not using actual scripts as script, because
+ // we have to manually erase all links right after compile.
+ function GatherCompileInfo(source, script) {
+ // Get function info, elements are partially sorted (it is a tree of
+ // nested functions serialized as parent followed by serialized children.
+ var raw_compile_info = %LiveEditGatherCompileInfo(script, source);
+
+ // Sort function infos by start position field.
+ var compile_info = new Array();
+ var old_index_map = new Array();
+ for (var i = 0; i < raw_compile_info.length; i++) {
+ var info = new FunctionCompileInfo(raw_compile_info[i]);
+ // Remove all links to the actual script. Breakpoints system and
+ // LiveEdit itself believe that any function in heap that points to a
+ // particular script is a regular function.
+ // For some functions we will restore this link later.
+ %LiveEditFunctionSetScript(info.shared_function_info, UNDEFINED);
+ compile_info.push(info);
+ old_index_map.push(i);
+ }
+
+ for (var i = 0; i < compile_info.length; i++) {
+ var k = i;
+ for (var j = i + 1; j < compile_info.length; j++) {
+ if (compile_info[k].start_position > compile_info[j].start_position) {
+ k = j;
+ }
+ }
+ if (k != i) {
+ var temp_info = compile_info[k];
+ var temp_index = old_index_map[k];
+ compile_info[k] = compile_info[i];
+ old_index_map[k] = old_index_map[i];
+ compile_info[i] = temp_info;
+ old_index_map[i] = temp_index;
+ }
+ }
+
+ // After sorting update outer_index field using old_index_map. Also
+ // set next_sibling_index field.
+ var current_index = 0;
+
+ // The recursive function, that goes over all children of a particular
+ // node (i.e. function info).
+ function ResetIndexes(new_parent_index, old_parent_index) {
+ var previous_sibling = -1;
+ while (current_index < compile_info.length &&
+ compile_info[current_index].outer_index == old_parent_index) {
+ var saved_index = current_index;
+ compile_info[saved_index].outer_index = new_parent_index;
+ if (previous_sibling != -1) {
+ compile_info[previous_sibling].next_sibling_index = saved_index;
+ }
+ previous_sibling = saved_index;
+ current_index++;
+ ResetIndexes(saved_index, old_index_map[saved_index]);
+ }
+ if (previous_sibling != -1) {
+ compile_info[previous_sibling].next_sibling_index = -1;
+ }
+ }
+
+ ResetIndexes(-1, -1);
+ Assert(current_index == compile_info.length);
+
+ return compile_info;
+ }
+
+
+ // Replaces function's Code.
+ function PatchFunctionCode(old_node, change_log) {
+ var new_info = old_node.corresponding_node.info;
+ if (old_node.live_shared_function_infos) {
+ old_node.live_shared_function_infos.forEach(function (old_info) {
+ %LiveEditReplaceFunctionCode(new_info.raw_array,
+ old_info.raw_array);
+
+ // The function got a new code. However, this new code brings all new
+ // instances of SharedFunctionInfo for nested functions. However,
+ // we want the original instances to be used wherever possible.
+ // (This is because old instances and new instances will be both
+ // linked to a script and breakpoints subsystem does not really
+ // expects this; neither does LiveEdit subsystem on next call).
+ for (var i = 0; i < old_node.children.length; i++) {
+ if (old_node.children[i].corresponding_node) {
+ var corresponding_child_info =
+ old_node.children[i].corresponding_node.info.
+ shared_function_info;
+
+ if (old_node.children[i].live_shared_function_infos) {
+ old_node.children[i].live_shared_function_infos.
+ forEach(function (old_child_info) {
+ %LiveEditReplaceRefToNestedFunction(
+ old_info.info,
+ corresponding_child_info,
+ old_child_info.info);
+ });
+ }
+ }
+ }
+ });
+
+ change_log.push( {function_patched: new_info.function_name} );
+ } else {
+ change_log.push( {function_patched: new_info.function_name,
+ function_info_not_found: true} );
+ }
+ }
+
+
+ // Makes a function associated with another instance of a script (the
+ // one representing its old version). This way the function still
+ // may access its own text.
+ function LinkToOldScript(old_info_node, old_script, report_array) {
+ if (old_info_node.live_shared_function_infos) {
+ old_info_node.live_shared_function_infos.
+ forEach(function (info) {
+ %LiveEditFunctionSetScript(info.info, old_script);
+ });
+
+ report_array.push( { name: old_info_node.info.function_name } );
+ } else {
+ report_array.push(
+ { name: old_info_node.info.function_name, not_found: true } );
+ }
+ }
+
+
+ // Returns function that restores breakpoints.
+ function TemporaryRemoveBreakPoints(original_script, change_log) {
+ var script_break_points = GetScriptBreakPoints(original_script);
+
+ var break_points_update_report = [];
+ change_log.push( { break_points_update: break_points_update_report } );
+
+ var break_point_old_positions = [];
+ for (var i = 0; i < script_break_points.length; i++) {
+ var break_point = script_break_points[i];
+
+ break_point.clear();
+
+ // TODO(LiveEdit): be careful with resource offset here.
+ var break_point_position = Debug.findScriptSourcePosition(original_script,
+ break_point.line(), break_point.column());
+
+ var old_position_description = {
+ position: break_point_position,
+ line: break_point.line(),
+ column: break_point.column()
+ };
+ break_point_old_positions.push(old_position_description);
+ }
+
+
+ // Restores breakpoints and creates their copies in the "old" copy of
+ // the script.
+ return function (pos_translator, old_script_copy_opt) {
+ // Update breakpoints (change positions and restore them in old version
+ // of script.
+ for (var i = 0; i < script_break_points.length; i++) {
+ var break_point = script_break_points[i];
+ if (old_script_copy_opt) {
+ var clone = break_point.cloneForOtherScript(old_script_copy_opt);
+ clone.set(old_script_copy_opt);
+
+ break_points_update_report.push( {
+ type: "copied_to_old",
+ id: break_point.number(),
+ new_id: clone.number(),
+ positions: break_point_old_positions[i]
+ } );
+ }
+
+ var updated_position = pos_translator.Translate(
+ break_point_old_positions[i].position,
+ PosTranslator.ShiftWithTopInsideChunkHandler);
+
+ var new_location =
+ original_script.locationFromPosition(updated_position, false);
+
+ break_point.update_positions(new_location.line, new_location.column);
+
+ var new_position_description = {
+ position: updated_position,
+ line: new_location.line,
+ column: new_location.column
+ };
+
+ break_point.set(original_script);
+
+ break_points_update_report.push( { type: "position_changed",
+ id: break_point.number(),
+ old_positions: break_point_old_positions[i],
+ new_positions: new_position_description
+ } );
+ }
+ };
+ }
+
+
+ function Assert(condition, message) {
+ if (!condition) {
+ if (message) {
+ throw "Assert " + message;
+ } else {
+ throw "Assert";
+ }
+ }
+ }
+
+ function DiffChunk(pos1, pos2, len1, len2) {
+ this.pos1 = pos1;
+ this.pos2 = pos2;
+ this.len1 = len1;
+ this.len2 = len2;
+ }
+
+ function PosTranslator(diff_array) {
+ var chunks = new Array();
+ var current_diff = 0;
+ for (var i = 0; i < diff_array.length; i += 3) {
+ var pos1_begin = diff_array[i];
+ var pos2_begin = pos1_begin + current_diff;
+ var pos1_end = diff_array[i + 1];
+ var pos2_end = diff_array[i + 2];
+ chunks.push(new DiffChunk(pos1_begin, pos2_begin, pos1_end - pos1_begin,
+ pos2_end - pos2_begin));
+ current_diff = pos2_end - pos1_end;
+ }
+ this.chunks = chunks;
+ }
+ PosTranslator.prototype.GetChunks = function() {
+ return this.chunks;
+ };
+
+ PosTranslator.prototype.Translate = function(pos, inside_chunk_handler) {
+ var array = this.chunks;
+ if (array.length == 0 || pos < array[0].pos1) {
+ return pos;
+ }
+ var chunk_index1 = 0;
+ var chunk_index2 = array.length - 1;
+
+ while (chunk_index1 < chunk_index2) {
+ var middle_index = Math.floor((chunk_index1 + chunk_index2) / 2);
+ if (pos < array[middle_index + 1].pos1) {
+ chunk_index2 = middle_index;
+ } else {
+ chunk_index1 = middle_index + 1;
+ }
+ }
+ var chunk = array[chunk_index1];
+ if (pos >= chunk.pos1 + chunk.len1) {
+ return pos + chunk.pos2 + chunk.len2 - chunk.pos1 - chunk.len1;
+ }
+
+ if (!inside_chunk_handler) {
+ inside_chunk_handler = PosTranslator.DefaultInsideChunkHandler;
+ }
+ return inside_chunk_handler(pos, chunk);
+ };
+
+ PosTranslator.DefaultInsideChunkHandler = function(pos, diff_chunk) {
+ Assert(false, "Cannot translate position in changed area");
+ };
+
+ PosTranslator.ShiftWithTopInsideChunkHandler =
+ function(pos, diff_chunk) {
+ // We carelessly do not check whether we stay inside the chunk after
+ // translation.
+ return pos - diff_chunk.pos1 + diff_chunk.pos2;
+ };
+
+ var FunctionStatus = {
+ // No change to function or its inner functions; however its positions
+ // in script may have been shifted.
+ UNCHANGED: "unchanged",
+ // The code of a function remains unchanged, but something happened inside
+ // some inner functions.
+ SOURCE_CHANGED: "source changed",
+ // The code of a function is changed or some nested function cannot be
+ // properly patched so this function must be recompiled.
+ CHANGED: "changed",
+ // Function is changed but cannot be patched.
+ DAMAGED: "damaged"
+ };
+
+ function CodeInfoTreeNode(code_info, children, array_index) {
+ this.info = code_info;
+ this.children = children;
+ // an index in array of compile_info
+ this.array_index = array_index;
+ this.parent = UNDEFINED;
+
+ this.status = FunctionStatus.UNCHANGED;
+ // Status explanation is used for debugging purposes and will be shown
+ // in user UI if some explanations are needed.
+ this.status_explanation = UNDEFINED;
+ this.new_start_pos = UNDEFINED;
+ this.new_end_pos = UNDEFINED;
+ this.corresponding_node = UNDEFINED;
+ this.unmatched_new_nodes = UNDEFINED;
+
+ // 'Textual' correspondence/matching is weaker than 'pure'
+ // correspondence/matching. We need 'textual' level for visual presentation
+ // in UI, we use 'pure' level for actual code manipulation.
+ // Sometimes only function body is changed (functions in old and new script
+ // textually correspond), but we cannot patch the code, so we see them
+ // as an old function deleted and new function created.
+ this.textual_corresponding_node = UNDEFINED;
+ this.textually_unmatched_new_nodes = UNDEFINED;
+
+ this.live_shared_function_infos = UNDEFINED;
+ }
+
+ // From array of function infos that is implicitly a tree creates
+ // an actual tree of functions in script.
+ function BuildCodeInfoTree(code_info_array) {
+ // Throughtout all function we iterate over input array.
+ var index = 0;
+
+ // Recursive function that builds a branch of tree.
+ function BuildNode() {
+ var my_index = index;
+ index++;
+ var child_array = new Array();
+ while (index < code_info_array.length &&
+ code_info_array[index].outer_index == my_index) {
+ child_array.push(BuildNode());
+ }
+ var node = new CodeInfoTreeNode(code_info_array[my_index], child_array,
+ my_index);
+ for (var i = 0; i < child_array.length; i++) {
+ child_array[i].parent = node;
+ }
+ return node;
+ }
+
+ var root = BuildNode();
+ Assert(index == code_info_array.length);
+ return root;
+ }
+
+ // Applies a list of the textual diff chunks onto the tree of functions.
+ // Determines status of each function (from unchanged to damaged). However
+ // children of unchanged functions are ignored.
+ function MarkChangedFunctions(code_info_tree, chunks) {
+
+ // A convenient iterator over diff chunks that also translates
+ // positions from old to new in a current non-changed part of script.
+ var chunk_it = new function() {
+ var chunk_index = 0;
+ var pos_diff = 0;
+ this.current = function() { return chunks[chunk_index]; };
+ this.next = function() {
+ var chunk = chunks[chunk_index];
+ pos_diff = chunk.pos2 + chunk.len2 - (chunk.pos1 + chunk.len1);
+ chunk_index++;
+ };
+ this.done = function() { return chunk_index >= chunks.length; };
+ this.TranslatePos = function(pos) { return pos + pos_diff; };
+ };
+
+ // A recursive function that processes internals of a function and all its
+ // inner functions. Iterator chunk_it initially points to a chunk that is
+ // below function start.
+ function ProcessInternals(info_node) {
+ info_node.new_start_pos = chunk_it.TranslatePos(
+ info_node.info.start_position);
+ var child_index = 0;
+ var code_changed = false;
+ var source_changed = false;
+ // Simultaneously iterates over child functions and over chunks.
+ while (!chunk_it.done() &&
+ chunk_it.current().pos1 < info_node.info.end_position) {
+ if (child_index < info_node.children.length) {
+ var child = info_node.children[child_index];
+
+ if (child.info.end_position <= chunk_it.current().pos1) {
+ ProcessUnchangedChild(child);
+ child_index++;
+ continue;
+ } else if (child.info.start_position >=
+ chunk_it.current().pos1 + chunk_it.current().len1) {
+ code_changed = true;
+ chunk_it.next();
+ continue;
+ } else if (child.info.start_position <= chunk_it.current().pos1 &&
+ child.info.end_position >= chunk_it.current().pos1 +
+ chunk_it.current().len1) {
+ ProcessInternals(child);
+ source_changed = source_changed ||
+ ( child.status != FunctionStatus.UNCHANGED );
+ code_changed = code_changed ||
+ ( child.status == FunctionStatus.DAMAGED );
+ child_index++;
+ continue;
+ } else {
+ code_changed = true;
+ child.status = FunctionStatus.DAMAGED;
+ child.status_explanation =
+ "Text diff overlaps with function boundary";
+ child_index++;
+ continue;
+ }
+ } else {
+ if (chunk_it.current().pos1 + chunk_it.current().len1 <=
+ info_node.info.end_position) {
+ info_node.status = FunctionStatus.CHANGED;
+ chunk_it.next();
+ continue;
+ } else {
+ info_node.status = FunctionStatus.DAMAGED;
+ info_node.status_explanation =
+ "Text diff overlaps with function boundary";
+ return;
+ }
+ }
+ Assert("Unreachable", false);
+ }
+ while (child_index < info_node.children.length) {
+ var child = info_node.children[child_index];
+ ProcessUnchangedChild(child);
+ child_index++;
+ }
+ if (code_changed) {
+ info_node.status = FunctionStatus.CHANGED;
+ } else if (source_changed) {
+ info_node.status = FunctionStatus.SOURCE_CHANGED;
+ }
+ info_node.new_end_pos =
+ chunk_it.TranslatePos(info_node.info.end_position);
+ }
+
+ function ProcessUnchangedChild(node) {
+ node.new_start_pos = chunk_it.TranslatePos(node.info.start_position);
+ node.new_end_pos = chunk_it.TranslatePos(node.info.end_position);
+ }
+
+ ProcessInternals(code_info_tree);
+ }
+
+ // For each old function (if it is not damaged) tries to find a corresponding
+ // function in new script. Typically it should succeed (non-damaged functions
+ // by definition may only have changes inside their bodies). However there are
+ // reasons for correspondence not to be found; function with unmodified text
+ // in new script may become enclosed into other function; the innocent change
+ // inside function body may in fact be something like "} function B() {" that
+ // splits a function into 2 functions.
+ function FindCorrespondingFunctions(old_code_tree, new_code_tree) {
+
+ // A recursive function that tries to find a correspondence for all
+ // child functions and for their inner functions.
+ function ProcessNode(old_node, new_node) {
+ var scope_change_description =
+ IsFunctionContextLocalsChanged(old_node.info, new_node.info);
+ if (scope_change_description) {
+ old_node.status = FunctionStatus.CHANGED;
+ }
+
+ var old_children = old_node.children;
+ var new_children = new_node.children;
+
+ var unmatched_new_nodes_list = [];
+ var textually_unmatched_new_nodes_list = [];
+
+ var old_index = 0;
+ var new_index = 0;
+ while (old_index < old_children.length) {
+ if (old_children[old_index].status == FunctionStatus.DAMAGED) {
+ old_index++;
+ } else if (new_index < new_children.length) {
+ if (new_children[new_index].info.start_position <
+ old_children[old_index].new_start_pos) {
+ unmatched_new_nodes_list.push(new_children[new_index]);
+ textually_unmatched_new_nodes_list.push(new_children[new_index]);
+ new_index++;
+ } else if (new_children[new_index].info.start_position ==
+ old_children[old_index].new_start_pos) {
+ if (new_children[new_index].info.end_position ==
+ old_children[old_index].new_end_pos) {
+ old_children[old_index].corresponding_node =
+ new_children[new_index];
+ old_children[old_index].textual_corresponding_node =
+ new_children[new_index];
+ if (scope_change_description) {
+ old_children[old_index].status = FunctionStatus.DAMAGED;
+ old_children[old_index].status_explanation =
+ "Enclosing function is now incompatible. " +
+ scope_change_description;
+ old_children[old_index].corresponding_node = UNDEFINED;
+ } else if (old_children[old_index].status !=
+ FunctionStatus.UNCHANGED) {
+ ProcessNode(old_children[old_index],
+ new_children[new_index]);
+ if (old_children[old_index].status == FunctionStatus.DAMAGED) {
+ unmatched_new_nodes_list.push(
+ old_children[old_index].corresponding_node);
+ old_children[old_index].corresponding_node = UNDEFINED;
+ old_node.status = FunctionStatus.CHANGED;
+ }
+ }
+ } else {
+ old_children[old_index].status = FunctionStatus.DAMAGED;
+ old_children[old_index].status_explanation =
+ "No corresponding function in new script found";
+ old_node.status = FunctionStatus.CHANGED;
+ unmatched_new_nodes_list.push(new_children[new_index]);
+ textually_unmatched_new_nodes_list.push(new_children[new_index]);
+ }
+ new_index++;
+ old_index++;
+ } else {
+ old_children[old_index].status = FunctionStatus.DAMAGED;
+ old_children[old_index].status_explanation =
+ "No corresponding function in new script found";
+ old_node.status = FunctionStatus.CHANGED;
+ old_index++;
+ }
+ } else {
+ old_children[old_index].status = FunctionStatus.DAMAGED;
+ old_children[old_index].status_explanation =
+ "No corresponding function in new script found";
+ old_node.status = FunctionStatus.CHANGED;
+ old_index++;
+ }
+ }
+
+ while (new_index < new_children.length) {
+ unmatched_new_nodes_list.push(new_children[new_index]);
+ textually_unmatched_new_nodes_list.push(new_children[new_index]);
+ new_index++;
+ }
+
+ if (old_node.status == FunctionStatus.CHANGED) {
+ if (old_node.info.param_num != new_node.info.param_num) {
+ old_node.status = FunctionStatus.DAMAGED;
+ old_node.status_explanation = "Changed parameter number: " +
+ old_node.info.param_num + " and " + new_node.info.param_num;
+ }
+ }
+ old_node.unmatched_new_nodes = unmatched_new_nodes_list;
+ old_node.textually_unmatched_new_nodes =
+ textually_unmatched_new_nodes_list;
+ }
+
+ ProcessNode(old_code_tree, new_code_tree);
+
+ old_code_tree.corresponding_node = new_code_tree;
+ old_code_tree.textual_corresponding_node = new_code_tree;
+
+ Assert(old_code_tree.status != FunctionStatus.DAMAGED,
+ "Script became damaged");
+ }
+
+ function FindLiveSharedInfos(old_code_tree, script) {
+ var shared_raw_list = %LiveEditFindSharedFunctionInfosForScript(script);
+
+ var shared_infos = new Array();
+
+ for (var i = 0; i < shared_raw_list.length; i++) {
+ shared_infos.push(new SharedInfoWrapper(shared_raw_list[i]));
+ }
+
+ // Finds all SharedFunctionInfos that corresponds to compile info
+ // in old version of the script.
+ function FindFunctionInfos(compile_info) {
+ var wrappers = [];
+
+ for (var i = 0; i < shared_infos.length; i++) {
+ var wrapper = shared_infos[i];
+ if (wrapper.start_position == compile_info.start_position &&
+ wrapper.end_position == compile_info.end_position) {
+ wrappers.push(wrapper);
+ }
+ }
+
+ if (wrappers.length > 0) {
+ return wrappers;
+ }
+ }
+
+ function TraverseTree(node) {
+ node.live_shared_function_infos = FindFunctionInfos(node.info);
+
+ for (var i = 0; i < node.children.length; i++) {
+ TraverseTree(node.children[i]);
+ }
+ }
+
+ TraverseTree(old_code_tree);
+ }
+
+
+ // An object describing function compilation details. Its index fields
+ // apply to indexes inside array that stores these objects.
+ function FunctionCompileInfo(raw_array) {
+ this.function_name = raw_array[0];
+ this.start_position = raw_array[1];
+ this.end_position = raw_array[2];
+ this.param_num = raw_array[3];
+ this.code = raw_array[4];
+ this.code_scope_info = raw_array[5];
+ this.scope_info = raw_array[6];
+ this.outer_index = raw_array[7];
+ this.shared_function_info = raw_array[8];
+ this.next_sibling_index = null;
+ this.raw_array = raw_array;
+ }
+
+ function SharedInfoWrapper(raw_array) {
+ this.function_name = raw_array[0];
+ this.start_position = raw_array[1];
+ this.end_position = raw_array[2];
+ this.info = raw_array[3];
+ this.raw_array = raw_array;
+ }
+
+ // Changes positions (including all statements) in function.
+ function PatchPositions(old_info_node, diff_array, report_array) {
+ if (old_info_node.live_shared_function_infos) {
+ old_info_node.live_shared_function_infos.forEach(function (info) {
+ %LiveEditPatchFunctionPositions(info.raw_array,
+ diff_array);
+ });
+
+ report_array.push( { name: old_info_node.info.function_name } );
+ } else {
+ // TODO(LiveEdit): function is not compiled yet or is already collected.
+ report_array.push(
+ { name: old_info_node.info.function_name, info_not_found: true } );
+ }
+ }
+
+ // Adds a suffix to script name to mark that it is old version.
+ function CreateNameForOldScript(script) {
+ // TODO(635): try better than this; support several changes.
+ return script.name + " (old)";
+ }
+
+ // Compares a function scope heap structure, old and new version, whether it
+ // changed or not. Returns explanation if they differ.
+ function IsFunctionContextLocalsChanged(function_info1, function_info2) {
+ var scope_info1 = function_info1.scope_info;
+ var scope_info2 = function_info2.scope_info;
+
+ var scope_info1_text;
+ var scope_info2_text;
+
+ if (scope_info1) {
+ scope_info1_text = scope_info1.toString();
+ } else {
+ scope_info1_text = "";
+ }
+ if (scope_info2) {
+ scope_info2_text = scope_info2.toString();
+ } else {
+ scope_info2_text = "";
+ }
+
+ if (scope_info1_text != scope_info2_text) {
+ return "Variable map changed: [" + scope_info1_text +
+ "] => [" + scope_info2_text + "]";
+ }
+ // No differences. Return undefined.
+ return;
+ }
+
+ // Minifier forward declaration.
+ var FunctionPatchabilityStatus;
+
+ // For array of wrapped shared function infos checks that none of them
+ // have activations on stack (of any thread). Throws a Failure exception
+ // if this proves to be false.
+ function CheckStackActivations(shared_wrapper_list, change_log) {
+ var shared_list = new Array();
+ for (var i = 0; i < shared_wrapper_list.length; i++) {
+ shared_list[i] = shared_wrapper_list[i].info;
+ }
+ var result = %LiveEditCheckAndDropActivations(shared_list, true);
+ if (result[shared_list.length]) {
+ // Extra array element may contain error message.
+ throw new Failure(result[shared_list.length]);
+ }
+
+ var problems = new Array();
+ var dropped = new Array();
+ for (var i = 0; i < shared_list.length; i++) {
+ var shared = shared_wrapper_list[i];
+ if (result[i] == FunctionPatchabilityStatus.REPLACED_ON_ACTIVE_STACK) {
+ dropped.push({ name: shared.function_name } );
+ } else if (result[i] != FunctionPatchabilityStatus.AVAILABLE_FOR_PATCH) {
+ var description = {
+ name: shared.function_name,
+ start_pos: shared.start_position,
+ end_pos: shared.end_position,
+ replace_problem:
+ FunctionPatchabilityStatus.SymbolName(result[i])
+ };
+ problems.push(description);
+ }
+ }
+ if (dropped.length > 0) {
+ change_log.push({ dropped_from_stack: dropped });
+ }
+ if (problems.length > 0) {
+ change_log.push( { functions_on_stack: problems } );
+ throw new Failure("Blocked by functions on stack");
+ }
+
+ return dropped.length;
+ }
+
+ // A copy of the FunctionPatchabilityStatus enum from liveedit.h
+ var FunctionPatchabilityStatus = {
+ AVAILABLE_FOR_PATCH: 1,
+ BLOCKED_ON_ACTIVE_STACK: 2,
+ BLOCKED_ON_OTHER_STACK: 3,
+ BLOCKED_UNDER_NATIVE_CODE: 4,
+ REPLACED_ON_ACTIVE_STACK: 5,
+ BLOCKED_UNDER_GENERATOR: 6,
+ BLOCKED_ACTIVE_GENERATOR: 7
+ };
+
+ FunctionPatchabilityStatus.SymbolName = function(code) {
+ var enumeration = FunctionPatchabilityStatus;
+ for (var name in enumeration) {
+ if (enumeration[name] == code) {
+ return name;
+ }
+ }
+ };
+
+
+ // A logical failure in liveedit process. This means that change_log
+ // is valid and consistent description of what happened.
+ function Failure(message) {
+ this.message = message;
+ }
+ // Function (constructor) is public.
+ this.Failure = Failure;
+
+ Failure.prototype.toString = function() {
+ return "LiveEdit Failure: " + this.message;
+ };
+
+ function CopyErrorPositionToDetails(e, details) {
+ function createPositionStruct(script, position) {
+ if (position == -1) return;
+ var location = script.locationFromPosition(position, true);
+ if (location == null) return;
+ return {
+ line: location.line + 1,
+ column: location.column + 1,
+ position: position
+ };
+ }
+
+ if (!("scriptObject" in e) || !("startPosition" in e)) {
+ return;
+ }
+
+ var script = e.scriptObject;
+
+ var position_struct = {
+ start: createPositionStruct(script, e.startPosition),
+ end: createPositionStruct(script, e.endPosition)
+ };
+ details.position = position_struct;
+ }
+
+ // A testing entry.
+ function GetPcFromSourcePos(func, source_pos) {
+ return %GetFunctionCodePositionFromSource(func, source_pos);
+ }
+ // Function is public.
+ this.GetPcFromSourcePos = GetPcFromSourcePos;
+
+ // LiveEdit main entry point: changes a script text to a new string.
+ function SetScriptSource(script, new_source, preview_only, change_log) {
+ var old_source = script.source;
+ var diff = CompareStrings(old_source, new_source);
+ return ApplyPatchMultiChunk(script, diff, new_source, preview_only,
+ change_log);
+ }
+ // Function is public.
+ this.SetScriptSource = SetScriptSource;
+
+ function CompareStrings(s1, s2) {
+ return %LiveEditCompareStrings(s1, s2);
+ }
+
+ // Applies the change to the script.
+ // The change is always a substring (change_pos, change_pos + change_len)
+ // being replaced with a completely different string new_str.
+ // This API is a legacy and is obsolete.
+ //
+ // @param {Script} script that is being changed
+ // @param {Array} change_log a list that collects engineer-readable
+ // description of what happened.
+ function ApplySingleChunkPatch(script, change_pos, change_len, new_str,
+ change_log) {
+ var old_source = script.source;
+
+ // Prepare new source string.
+ var new_source = old_source.substring(0, change_pos) +
+ new_str + old_source.substring(change_pos + change_len);
+
+ return ApplyPatchMultiChunk(script,
+ [ change_pos, change_pos + change_len, change_pos + new_str.length],
+ new_source, false, change_log);
+ }
+
+ // Creates JSON description for a change tree.
+ function DescribeChangeTree(old_code_tree) {
+
+ function ProcessOldNode(node) {
+ var child_infos = [];
+ for (var i = 0; i < node.children.length; i++) {
+ var child = node.children[i];
+ if (child.status != FunctionStatus.UNCHANGED) {
+ child_infos.push(ProcessOldNode(child));
+ }
+ }
+ var new_child_infos = [];
+ if (node.textually_unmatched_new_nodes) {
+ for (var i = 0; i < node.textually_unmatched_new_nodes.length; i++) {
+ var child = node.textually_unmatched_new_nodes[i];
+ new_child_infos.push(ProcessNewNode(child));
+ }
+ }
+ var res = {
+ name: node.info.function_name,
+ positions: DescribePositions(node),
+ status: node.status,
+ children: child_infos,
+ new_children: new_child_infos
+ };
+ if (node.status_explanation) {
+ res.status_explanation = node.status_explanation;
+ }
+ if (node.textual_corresponding_node) {
+ res.new_positions = DescribePositions(node.textual_corresponding_node);
+ }
+ return res;
+ }
+
+ function ProcessNewNode(node) {
+ var child_infos = [];
+ // Do not list ancestors.
+ if (false) {
+ for (var i = 0; i < node.children.length; i++) {
+ child_infos.push(ProcessNewNode(node.children[i]));
+ }
+ }
+ var res = {
+ name: node.info.function_name,
+ positions: DescribePositions(node),
+ children: child_infos,
+ };
+ return res;
+ }
+
+ function DescribePositions(node) {
+ return {
+ start_position: node.info.start_position,
+ end_position: node.info.end_position
+ };
+ }
+
+ return ProcessOldNode(old_code_tree);
+ }
+
+ // Functions are public for tests.
+ this.TestApi = {
+ PosTranslator: PosTranslator,
+ CompareStrings: CompareStrings,
+ ApplySingleChunkPatch: ApplySingleChunkPatch
+ };
+};
--- /dev/null
+paul.lind@imgtec.com
+gergely.kis@imgtec.com
+akos.palfi@imgtec.com
+balazs.kilvady@imgtec.com
+dusan.milosavljevic@imgtec.com
--- /dev/null
+// Copyright 2012 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"
+
+#if V8_TARGET_ARCH_MIPS
+
+#include "src/codegen.h"
+#include "src/debug/debug.h"
+
+namespace v8 {
+namespace internal {
+
+#define __ ACCESS_MASM(masm)
+
+
+void EmitDebugBreakSlot(MacroAssembler* masm) {
+ Label check_size;
+ __ bind(&check_size);
+ for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
+ __ nop(MacroAssembler::DEBUG_BREAK_NOP);
+ }
+ DCHECK_EQ(Assembler::kDebugBreakSlotInstructions,
+ masm->InstructionsGeneratedSince(&check_size));
+}
+
+
+void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode,
+ int call_argc) {
+ // Generate enough nop's to make space for a call instruction. Avoid emitting
+ // the trampoline pool in the debug break slot code.
+ Assembler::BlockTrampolinePoolScope block_pool(masm);
+ masm->RecordDebugBreakSlot(mode, call_argc);
+ EmitDebugBreakSlot(masm);
+}
+
+
+void DebugCodegen::ClearDebugBreakSlot(Address pc) {
+ CodePatcher patcher(pc, Assembler::kDebugBreakSlotInstructions);
+ EmitDebugBreakSlot(patcher.masm());
+}
+
+
+void DebugCodegen::PatchDebugBreakSlot(Address pc, Handle<Code> code) {
+ DCHECK_EQ(Code::BUILTIN, code->kind());
+ CodePatcher patcher(pc, Assembler::kDebugBreakSlotInstructions);
+ // Patch the code changing the debug break slot code from:
+ // nop(DEBUG_BREAK_NOP) - nop(1) is sll(zero_reg, zero_reg, 1)
+ // nop(DEBUG_BREAK_NOP)
+ // nop(DEBUG_BREAK_NOP)
+ // nop(DEBUG_BREAK_NOP)
+ // to a call to the debug break slot code.
+ // li t9, address (lui t9 / ori t9 instruction pair)
+ // call t9 (jalr t9 / nop instruction pair)
+ patcher.masm()->li(v8::internal::t9,
+ Operand(reinterpret_cast<int32_t>(code->entry())));
+ patcher.masm()->Call(v8::internal::t9);
+}
+
+
+void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
+ DebugBreakCallHelperMode mode) {
+ __ RecordComment("Debug break");
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // Load padding words on stack.
+ __ li(at, Operand(Smi::FromInt(LiveEdit::kFramePaddingValue)));
+ __ Subu(sp, sp,
+ Operand(kPointerSize * LiveEdit::kFramePaddingInitialSize));
+ for (int i = LiveEdit::kFramePaddingInitialSize - 1; i >= 0; i--) {
+ __ sw(at, MemOperand(sp, kPointerSize * i));
+ }
+ __ li(at, Operand(Smi::FromInt(LiveEdit::kFramePaddingInitialSize)));
+ __ push(at);
+
+ if (mode == SAVE_RESULT_REGISTER) __ push(v0);
+
+ __ PrepareCEntryArgs(0); // No arguments.
+ __ PrepareCEntryFunction(ExternalReference(
+ Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate()));
+
+ CEntryStub ceb(masm->isolate(), 1);
+ __ CallStub(&ceb);
+
+ if (FLAG_debug_code) {
+ for (int i = 0; i < kNumJSCallerSaved; i++) {
+ Register reg = {JSCallerSavedCode(i)};
+ __ li(reg, kDebugZapValue);
+ }
+ }
+
+ if (mode == SAVE_RESULT_REGISTER) __ pop(v0);
+
+ // Don't bother removing padding bytes pushed on the stack
+ // as the frame is going to be restored right away.
+
+ // Leave the internal frame.
+ }
+
+ // Now that the break point has been handled, resume normal execution by
+ // jumping to the target address intended by the caller and that was
+ // overwritten by the address of DebugBreakXXX.
+ ExternalReference after_break_target =
+ ExternalReference::debug_after_break_target_address(masm->isolate());
+ __ li(t9, Operand(after_break_target));
+ __ lw(t9, MemOperand(t9));
+ __ Jump(t9);
+}
+
+
+void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
+ __ Ret();
+}
+
+
+void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
+ ExternalReference restarter_frame_function_slot =
+ ExternalReference::debug_restarter_frame_function_pointer_address(
+ masm->isolate());
+ __ li(at, Operand(restarter_frame_function_slot));
+ __ sw(zero_reg, MemOperand(at, 0));
+
+ // We do not know our frame height, but set sp based on fp.
+ __ Subu(sp, fp, Operand(kPointerSize));
+
+ __ Pop(ra, fp, a1); // Return address, Frame, Function.
+
+ // Load context from the function.
+ __ lw(cp, FieldMemOperand(a1, JSFunction::kContextOffset));
+
+ // Get function code.
+ __ lw(at, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
+ __ lw(at, FieldMemOperand(at, SharedFunctionInfo::kCodeOffset));
+ __ Addu(t9, at, Operand(Code::kHeaderSize - kHeapObjectTag));
+
+ // Re-run JSFunction, a1 is function, cp is context.
+ __ Jump(t9);
+}
+
+
+const bool LiveEdit::kFrameDropperSupported = true;
+
+#undef __
+
+} // namespace internal
+} // namespace v8
+
+#endif // V8_TARGET_ARCH_MIPS
--- /dev/null
+paul.lind@imgtec.com
+gergely.kis@imgtec.com
+akos.palfi@imgtec.com
+balazs.kilvady@imgtec.com
+dusan.milosavljevic@imgtec.com
--- /dev/null
+// Copyright 2012 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"
+
+#if V8_TARGET_ARCH_MIPS64
+
+#include "src/codegen.h"
+#include "src/debug/debug.h"
+
+namespace v8 {
+namespace internal {
+
+#define __ ACCESS_MASM(masm)
+
+void EmitDebugBreakSlot(MacroAssembler* masm) {
+ Label check_size;
+ __ bind(&check_size);
+ for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
+ __ nop(MacroAssembler::DEBUG_BREAK_NOP);
+ }
+ DCHECK_EQ(Assembler::kDebugBreakSlotInstructions,
+ masm->InstructionsGeneratedSince(&check_size));
+}
+
+
+void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode,
+ int call_argc) {
+ // Generate enough nop's to make space for a call instruction. Avoid emitting
+ // the trampoline pool in the debug break slot code.
+ Assembler::BlockTrampolinePoolScope block_pool(masm);
+ masm->RecordDebugBreakSlot(mode, call_argc);
+ EmitDebugBreakSlot(masm);
+}
+
+
+void DebugCodegen::ClearDebugBreakSlot(Address pc) {
+ CodePatcher patcher(pc, Assembler::kDebugBreakSlotInstructions);
+ EmitDebugBreakSlot(patcher.masm());
+}
+
+
+void DebugCodegen::PatchDebugBreakSlot(Address pc, Handle<Code> code) {
+ DCHECK_EQ(Code::BUILTIN, code->kind());
+ CodePatcher patcher(pc, Assembler::kDebugBreakSlotInstructions);
+ // Patch the code changing the debug break slot code from:
+ // nop(DEBUG_BREAK_NOP) - nop(1) is sll(zero_reg, zero_reg, 1)
+ // nop(DEBUG_BREAK_NOP)
+ // nop(DEBUG_BREAK_NOP)
+ // nop(DEBUG_BREAK_NOP)
+ // nop(DEBUG_BREAK_NOP)
+ // nop(DEBUG_BREAK_NOP)
+ // to a call to the debug break slot code.
+ // li t9, address (4-instruction sequence on mips64)
+ // call t9 (jalr t9 / nop instruction pair)
+ patcher.masm()->li(v8::internal::t9,
+ Operand(reinterpret_cast<int64_t>(code->entry())),
+ ADDRESS_LOAD);
+ patcher.masm()->Call(v8::internal::t9);
+}
+
+
+void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
+ DebugBreakCallHelperMode mode) {
+ __ RecordComment("Debug break");
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // Load padding words on stack.
+ __ li(at, Operand(Smi::FromInt(LiveEdit::kFramePaddingValue)));
+ __ Dsubu(sp, sp,
+ Operand(kPointerSize * LiveEdit::kFramePaddingInitialSize));
+ for (int i = LiveEdit::kFramePaddingInitialSize - 1; i >= 0; i--) {
+ __ sd(at, MemOperand(sp, kPointerSize * i));
+ }
+ __ li(at, Operand(Smi::FromInt(LiveEdit::kFramePaddingInitialSize)));
+ __ push(at);
+
+ if (mode == SAVE_RESULT_REGISTER) __ push(v0);
+
+ __ PrepareCEntryArgs(0); // No arguments.
+ __ PrepareCEntryFunction(ExternalReference(
+ Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate()));
+
+ CEntryStub ceb(masm->isolate(), 1);
+ __ CallStub(&ceb);
+
+ if (FLAG_debug_code) {
+ for (int i = 0; i < kNumJSCallerSaved; i++) {
+ Register reg = {JSCallerSavedCode(i)};
+ __ li(reg, kDebugZapValue);
+ }
+ }
+
+ if (mode == SAVE_RESULT_REGISTER) __ pop(v0);
+
+ // Don't bother removing padding bytes pushed on the stack
+ // as the frame is going to be restored right away.
+
+ // Leave the internal frame.
+ }
+
+ // Now that the break point has been handled, resume normal execution by
+ // jumping to the target address intended by the caller and that was
+ // overwritten by the address of DebugBreakXXX.
+ ExternalReference after_break_target =
+ ExternalReference::debug_after_break_target_address(masm->isolate());
+ __ li(t9, Operand(after_break_target));
+ __ ld(t9, MemOperand(t9));
+ __ Jump(t9);
+}
+
+
+void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
+ __ Ret();
+}
+
+
+void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
+ ExternalReference restarter_frame_function_slot =
+ ExternalReference::debug_restarter_frame_function_pointer_address(
+ masm->isolate());
+ __ li(at, Operand(restarter_frame_function_slot));
+ __ sw(zero_reg, MemOperand(at, 0));
+
+ // We do not know our frame height, but set sp based on fp.
+ __ Dsubu(sp, fp, Operand(kPointerSize));
+
+ __ Pop(ra, fp, a1); // Return address, Frame, Function.
+
+ // Load context from the function.
+ __ ld(cp, FieldMemOperand(a1, JSFunction::kContextOffset));
+
+ // Get function code.
+ __ ld(at, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
+ __ ld(at, FieldMemOperand(at, SharedFunctionInfo::kCodeOffset));
+ __ Daddu(t9, at, Operand(Code::kHeaderSize - kHeapObjectTag));
+
+ // Re-run JSFunction, a1 is function, cp is context.
+ __ Jump(t9);
+}
+
+
+const bool LiveEdit::kFrameDropperSupported = true;
+
+#undef __
+
+} // namespace internal
+} // namespace v8
+
+#endif // V8_TARGET_ARCH_MIPS64
--- /dev/null
+// Copyright 2006-2012 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.
+"use strict";
+
+// Handle id counters.
+var next_handle_ = 0;
+var next_transient_handle_ = -1;
+
+// Mirror cache.
+var mirror_cache_ = [];
+var mirror_cache_enabled_ = true;
+
+
+function ToggleMirrorCache(value) {
+ mirror_cache_enabled_ = value;
+ next_handle_ = 0;
+ mirror_cache_ = [];
+}
+
+
+// Wrapper to check whether an object is a Promise. The call may not work
+// if promises are not enabled.
+// TODO(yangguo): remove try-catch once promises are enabled by default.
+function ObjectIsPromise(value) {
+ try {
+ return IS_SPEC_OBJECT(value) &&
+ !IS_UNDEFINED(%DebugGetProperty(value, builtins.$promiseStatus));
+ } catch (e) {
+ return false;
+ }
+}
+
+
+/**
+ * Returns the mirror for a specified value or object.
+ *
+ * @param {value or Object} value the value or object to retreive the mirror for
+ * @param {boolean} transient indicate whether this object is transient and
+ * should not be added to the mirror cache. The default is not transient.
+ * @returns {Mirror} the mirror reflects the passed value or object
+ */
+function MakeMirror(value, opt_transient) {
+ var mirror;
+
+ // Look for non transient mirrors in the mirror cache.
+ if (!opt_transient && mirror_cache_enabled_) {
+ for (var id in mirror_cache_) {
+ mirror = mirror_cache_[id];
+ if (mirror.value() === value) {
+ return mirror;
+ }
+ // Special check for NaN as NaN == NaN is false.
+ if (mirror.isNumber() && isNaN(mirror.value()) &&
+ typeof value == 'number' && isNaN(value)) {
+ return mirror;
+ }
+ }
+ }
+
+ if (IS_UNDEFINED(value)) {
+ mirror = new UndefinedMirror();
+ } else if (IS_NULL(value)) {
+ mirror = new NullMirror();
+ } else if (IS_BOOLEAN(value)) {
+ mirror = new BooleanMirror(value);
+ } else if (IS_NUMBER(value)) {
+ mirror = new NumberMirror(value);
+ } else if (IS_STRING(value)) {
+ mirror = new StringMirror(value);
+ } else if (IS_SYMBOL(value)) {
+ mirror = new SymbolMirror(value);
+ } else if (IS_ARRAY(value)) {
+ mirror = new ArrayMirror(value);
+ } else if (IS_DATE(value)) {
+ mirror = new DateMirror(value);
+ } else if (IS_FUNCTION(value)) {
+ mirror = new FunctionMirror(value);
+ } else if (IS_REGEXP(value)) {
+ mirror = new RegExpMirror(value);
+ } else if (IS_ERROR(value)) {
+ mirror = new ErrorMirror(value);
+ } else if (IS_SCRIPT(value)) {
+ mirror = new ScriptMirror(value);
+ } else if (IS_MAP(value) || IS_WEAKMAP(value)) {
+ mirror = new MapMirror(value);
+ } else if (IS_SET(value) || IS_WEAKSET(value)) {
+ mirror = new SetMirror(value);
+ } else if (IS_MAP_ITERATOR(value) || IS_SET_ITERATOR(value)) {
+ mirror = new IteratorMirror(value);
+ } else if (ObjectIsPromise(value)) {
+ mirror = new PromiseMirror(value);
+ } else if (IS_GENERATOR(value)) {
+ mirror = new GeneratorMirror(value);
+ } else {
+ mirror = new ObjectMirror(value, OBJECT_TYPE, opt_transient);
+ }
+
+ if (mirror_cache_enabled_) mirror_cache_[mirror.handle()] = mirror;
+ return mirror;
+}
+
+
+/**
+ * Returns the mirror for a specified mirror handle.
+ *
+ * @param {number} handle the handle to find the mirror for
+ * @returns {Mirror or undefiend} the mirror with the requested handle or
+ * undefined if no mirror with the requested handle was found
+ */
+function LookupMirror(handle) {
+ if (!mirror_cache_enabled_) throw new Error("Mirror cache is disabled");
+ return mirror_cache_[handle];
+}
+
+
+/**
+ * Returns the mirror for the undefined value.
+ *
+ * @returns {Mirror} the mirror reflects the undefined value
+ */
+function GetUndefinedMirror() {
+ return MakeMirror(UNDEFINED);
+}
+
+
+/**
+ * Inherit the prototype methods from one constructor into another.
+ *
+ * The Function.prototype.inherits from lang.js rewritten as a standalone
+ * function (not on Function.prototype). NOTE: If this file is to be loaded
+ * during bootstrapping this function needs to be revritten using some native
+ * functions as prototype setup using normal JavaScript does not work as
+ * expected during bootstrapping (see mirror.js in r114903).
+ *
+ * @param {function} ctor Constructor function which needs to inherit the
+ * prototype
+ * @param {function} superCtor Constructor function to inherit prototype from
+ */
+function inherits(ctor, superCtor) {
+ var tempCtor = function(){};
+ tempCtor.prototype = superCtor.prototype;
+ ctor.super_ = superCtor.prototype;
+ ctor.prototype = new tempCtor();
+ ctor.prototype.constructor = ctor;
+}
+
+
+// Type names of the different mirrors.
+var UNDEFINED_TYPE = 'undefined';
+var NULL_TYPE = 'null';
+var BOOLEAN_TYPE = 'boolean';
+var NUMBER_TYPE = 'number';
+var STRING_TYPE = 'string';
+var SYMBOL_TYPE = 'symbol';
+var OBJECT_TYPE = 'object';
+var FUNCTION_TYPE = 'function';
+var REGEXP_TYPE = 'regexp';
+var ERROR_TYPE = 'error';
+var PROPERTY_TYPE = 'property';
+var INTERNAL_PROPERTY_TYPE = 'internalProperty';
+var FRAME_TYPE = 'frame';
+var SCRIPT_TYPE = 'script';
+var CONTEXT_TYPE = 'context';
+var SCOPE_TYPE = 'scope';
+var PROMISE_TYPE = 'promise';
+var MAP_TYPE = 'map';
+var SET_TYPE = 'set';
+var ITERATOR_TYPE = 'iterator';
+var GENERATOR_TYPE = 'generator';
+
+// Maximum length when sending strings through the JSON protocol.
+var kMaxProtocolStringLength = 80;
+
+// Different kind of properties.
+var PropertyKind = {};
+PropertyKind.Named = 1;
+PropertyKind.Indexed = 2;
+
+
+// A copy of the PropertyType enum from property-details.h
+var PropertyType = {};
+PropertyType.Data = 0;
+PropertyType.DataConstant = 2;
+PropertyType.AccessorConstant = 3;
+
+
+// Different attributes for a property.
+var PropertyAttribute = {};
+PropertyAttribute.None = NONE;
+PropertyAttribute.ReadOnly = READ_ONLY;
+PropertyAttribute.DontEnum = DONT_ENUM;
+PropertyAttribute.DontDelete = DONT_DELETE;
+
+
+// A copy of the scope types from runtime-debug.cc.
+// NOTE: these constants should be backward-compatible, so
+// add new ones to the end of this list.
+var ScopeType = { Global: 0,
+ Local: 1,
+ With: 2,
+ Closure: 3,
+ Catch: 4,
+ Block: 5,
+ Script: 6 };
+
+
+// Mirror hierarchy:
+// - Mirror
+// - ValueMirror
+// - UndefinedMirror
+// - NullMirror
+// - NumberMirror
+// - StringMirror
+// - SymbolMirror
+// - ObjectMirror
+// - FunctionMirror
+// - UnresolvedFunctionMirror
+// - ArrayMirror
+// - DateMirror
+// - RegExpMirror
+// - ErrorMirror
+// - PromiseMirror
+// - MapMirror
+// - SetMirror
+// - IteratorMirror
+// - GeneratorMirror
+// - PropertyMirror
+// - InternalPropertyMirror
+// - FrameMirror
+// - ScriptMirror
+
+
+/**
+ * Base class for all mirror objects.
+ * @param {string} type The type of the mirror
+ * @constructor
+ */
+function Mirror(type) {
+ this.type_ = type;
+}
+
+
+Mirror.prototype.type = function() {
+ return this.type_;
+};
+
+
+/**
+ * Check whether the mirror reflects a value.
+ * @returns {boolean} True if the mirror reflects a value.
+ */
+Mirror.prototype.isValue = function() {
+ return this instanceof ValueMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects the undefined value.
+ * @returns {boolean} True if the mirror reflects the undefined value.
+ */
+Mirror.prototype.isUndefined = function() {
+ return this instanceof UndefinedMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects the null value.
+ * @returns {boolean} True if the mirror reflects the null value
+ */
+Mirror.prototype.isNull = function() {
+ return this instanceof NullMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects a boolean value.
+ * @returns {boolean} True if the mirror reflects a boolean value
+ */
+Mirror.prototype.isBoolean = function() {
+ return this instanceof BooleanMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects a number value.
+ * @returns {boolean} True if the mirror reflects a number value
+ */
+Mirror.prototype.isNumber = function() {
+ return this instanceof NumberMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects a string value.
+ * @returns {boolean} True if the mirror reflects a string value
+ */
+Mirror.prototype.isString = function() {
+ return this instanceof StringMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects a symbol.
+ * @returns {boolean} True if the mirror reflects a symbol
+ */
+Mirror.prototype.isSymbol = function() {
+ return this instanceof SymbolMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects an object.
+ * @returns {boolean} True if the mirror reflects an object
+ */
+Mirror.prototype.isObject = function() {
+ return this instanceof ObjectMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects a function.
+ * @returns {boolean} True if the mirror reflects a function
+ */
+Mirror.prototype.isFunction = function() {
+ return this instanceof FunctionMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects an unresolved function.
+ * @returns {boolean} True if the mirror reflects an unresolved function
+ */
+Mirror.prototype.isUnresolvedFunction = function() {
+ return this instanceof UnresolvedFunctionMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects an array.
+ * @returns {boolean} True if the mirror reflects an array
+ */
+Mirror.prototype.isArray = function() {
+ return this instanceof ArrayMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects a date.
+ * @returns {boolean} True if the mirror reflects a date
+ */
+Mirror.prototype.isDate = function() {
+ return this instanceof DateMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects a regular expression.
+ * @returns {boolean} True if the mirror reflects a regular expression
+ */
+Mirror.prototype.isRegExp = function() {
+ return this instanceof RegExpMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects an error.
+ * @returns {boolean} True if the mirror reflects an error
+ */
+Mirror.prototype.isError = function() {
+ return this instanceof ErrorMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects a promise.
+ * @returns {boolean} True if the mirror reflects a promise
+ */
+Mirror.prototype.isPromise = function() {
+ return this instanceof PromiseMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects a generator object.
+ * @returns {boolean} True if the mirror reflects a generator object
+ */
+Mirror.prototype.isGenerator = function() {
+ return this instanceof GeneratorMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects a property.
+ * @returns {boolean} True if the mirror reflects a property
+ */
+Mirror.prototype.isProperty = function() {
+ return this instanceof PropertyMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects an internal property.
+ * @returns {boolean} True if the mirror reflects an internal property
+ */
+Mirror.prototype.isInternalProperty = function() {
+ return this instanceof InternalPropertyMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects a stack frame.
+ * @returns {boolean} True if the mirror reflects a stack frame
+ */
+Mirror.prototype.isFrame = function() {
+ return this instanceof FrameMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects a script.
+ * @returns {boolean} True if the mirror reflects a script
+ */
+Mirror.prototype.isScript = function() {
+ return this instanceof ScriptMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects a context.
+ * @returns {boolean} True if the mirror reflects a context
+ */
+Mirror.prototype.isContext = function() {
+ return this instanceof ContextMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects a scope.
+ * @returns {boolean} True if the mirror reflects a scope
+ */
+Mirror.prototype.isScope = function() {
+ return this instanceof ScopeMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects a map.
+ * @returns {boolean} True if the mirror reflects a map
+ */
+Mirror.prototype.isMap = function() {
+ return this instanceof MapMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects a set.
+ * @returns {boolean} True if the mirror reflects a set
+ */
+Mirror.prototype.isSet = function() {
+ return this instanceof SetMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects an iterator.
+ * @returns {boolean} True if the mirror reflects an iterator
+ */
+Mirror.prototype.isIterator = function() {
+ return this instanceof IteratorMirror;
+};
+
+
+/**
+ * Allocate a handle id for this object.
+ */
+Mirror.prototype.allocateHandle_ = function() {
+ if (mirror_cache_enabled_) this.handle_ = next_handle_++;
+};
+
+
+/**
+ * Allocate a transient handle id for this object. Transient handles are
+ * negative.
+ */
+Mirror.prototype.allocateTransientHandle_ = function() {
+ this.handle_ = next_transient_handle_--;
+};
+
+
+Mirror.prototype.toText = function() {
+ // Simpel to text which is used when on specialization in subclass.
+ return "#<" + this.constructor.name + ">";
+};
+
+
+/**
+ * Base class for all value mirror objects.
+ * @param {string} type The type of the mirror
+ * @param {value} value The value reflected by this mirror
+ * @param {boolean} transient indicate whether this object is transient with a
+ * transient handle
+ * @constructor
+ * @extends Mirror
+ */
+function ValueMirror(type, value, transient) {
+ %_CallFunction(this, type, Mirror);
+ this.value_ = value;
+ if (!transient) {
+ this.allocateHandle_();
+ } else {
+ this.allocateTransientHandle_();
+ }
+}
+inherits(ValueMirror, Mirror);
+
+
+Mirror.prototype.handle = function() {
+ return this.handle_;
+};
+
+
+/**
+ * Check whether this is a primitive value.
+ * @return {boolean} True if the mirror reflects a primitive value
+ */
+ValueMirror.prototype.isPrimitive = function() {
+ var type = this.type();
+ return type === 'undefined' ||
+ type === 'null' ||
+ type === 'boolean' ||
+ type === 'number' ||
+ type === 'string' ||
+ type === 'symbol';
+};
+
+
+/**
+ * Get the actual value reflected by this mirror.
+ * @return {value} The value reflected by this mirror
+ */
+ValueMirror.prototype.value = function() {
+ return this.value_;
+};
+
+
+/**
+ * Mirror object for Undefined.
+ * @constructor
+ * @extends ValueMirror
+ */
+function UndefinedMirror() {
+ %_CallFunction(this, UNDEFINED_TYPE, UNDEFINED, ValueMirror);
+}
+inherits(UndefinedMirror, ValueMirror);
+
+
+UndefinedMirror.prototype.toText = function() {
+ return 'undefined';
+};
+
+
+/**
+ * Mirror object for null.
+ * @constructor
+ * @extends ValueMirror
+ */
+function NullMirror() {
+ %_CallFunction(this, NULL_TYPE, null, ValueMirror);
+}
+inherits(NullMirror, ValueMirror);
+
+
+NullMirror.prototype.toText = function() {
+ return 'null';
+};
+
+
+/**
+ * Mirror object for boolean values.
+ * @param {boolean} value The boolean value reflected by this mirror
+ * @constructor
+ * @extends ValueMirror
+ */
+function BooleanMirror(value) {
+ %_CallFunction(this, BOOLEAN_TYPE, value, ValueMirror);
+}
+inherits(BooleanMirror, ValueMirror);
+
+
+BooleanMirror.prototype.toText = function() {
+ return this.value_ ? 'true' : 'false';
+};
+
+
+/**
+ * Mirror object for number values.
+ * @param {number} value The number value reflected by this mirror
+ * @constructor
+ * @extends ValueMirror
+ */
+function NumberMirror(value) {
+ %_CallFunction(this, NUMBER_TYPE, value, ValueMirror);
+}
+inherits(NumberMirror, ValueMirror);
+
+
+NumberMirror.prototype.toText = function() {
+ return %_NumberToString(this.value_);
+};
+
+
+/**
+ * Mirror object for string values.
+ * @param {string} value The string value reflected by this mirror
+ * @constructor
+ * @extends ValueMirror
+ */
+function StringMirror(value) {
+ %_CallFunction(this, STRING_TYPE, value, ValueMirror);
+}
+inherits(StringMirror, ValueMirror);
+
+
+StringMirror.prototype.length = function() {
+ return this.value_.length;
+};
+
+StringMirror.prototype.getTruncatedValue = function(maxLength) {
+ if (maxLength != -1 && this.length() > maxLength) {
+ return this.value_.substring(0, maxLength) +
+ '... (length: ' + this.length() + ')';
+ }
+ return this.value_;
+};
+
+StringMirror.prototype.toText = function() {
+ return this.getTruncatedValue(kMaxProtocolStringLength);
+};
+
+
+/**
+ * Mirror object for a Symbol
+ * @param {Object} value The Symbol
+ * @constructor
+ * @extends Mirror
+ */
+function SymbolMirror(value) {
+ %_CallFunction(this, SYMBOL_TYPE, value, ValueMirror);
+}
+inherits(SymbolMirror, ValueMirror);
+
+
+SymbolMirror.prototype.description = function() {
+ return %SymbolDescription(%_ValueOf(this.value_));
+}
+
+
+SymbolMirror.prototype.toText = function() {
+ return %_CallFunction(this.value_, builtins.$symbolToString);
+}
+
+
+/**
+ * Mirror object for objects.
+ * @param {object} value The object reflected by this mirror
+ * @param {boolean} transient indicate whether this object is transient with a
+ * transient handle
+ * @constructor
+ * @extends ValueMirror
+ */
+function ObjectMirror(value, type, transient) {
+ %_CallFunction(this, type || OBJECT_TYPE, value, transient, ValueMirror);
+}
+inherits(ObjectMirror, ValueMirror);
+
+
+ObjectMirror.prototype.className = function() {
+ return %_ClassOf(this.value_);
+};
+
+
+ObjectMirror.prototype.constructorFunction = function() {
+ return MakeMirror(%DebugGetProperty(this.value_, 'constructor'));
+};
+
+
+ObjectMirror.prototype.prototypeObject = function() {
+ return MakeMirror(%DebugGetProperty(this.value_, 'prototype'));
+};
+
+
+ObjectMirror.prototype.protoObject = function() {
+ return MakeMirror(%DebugGetPrototype(this.value_));
+};
+
+
+ObjectMirror.prototype.hasNamedInterceptor = function() {
+ // Get information on interceptors for this object.
+ var x = %GetInterceptorInfo(this.value_);
+ return (x & 2) != 0;
+};
+
+
+ObjectMirror.prototype.hasIndexedInterceptor = function() {
+ // Get information on interceptors for this object.
+ var x = %GetInterceptorInfo(this.value_);
+ return (x & 1) != 0;
+};
+
+
+// Get all own property names except for private symbols.
+function TryGetPropertyNames(object) {
+ try {
+ // TODO(yangguo): Should there be a special debugger implementation of
+ // %GetOwnPropertyNames that doesn't perform access checks?
+ return %GetOwnPropertyNames(object, PROPERTY_ATTRIBUTES_PRIVATE_SYMBOL);
+ } catch (e) {
+ // Might have hit a failed access check.
+ return [];
+ }
+}
+
+
+/**
+ * Return the property names for this object.
+ * @param {number} kind Indicate whether named, indexed or both kinds of
+ * properties are requested
+ * @param {number} limit Limit the number of names returend to the specified
+ value
+ * @return {Array} Property names for this object
+ */
+ObjectMirror.prototype.propertyNames = function(kind, limit) {
+ // Find kind and limit and allocate array for the result
+ kind = kind || PropertyKind.Named | PropertyKind.Indexed;
+
+ var propertyNames;
+ var elementNames;
+ var total = 0;
+
+ // Find all the named properties.
+ if (kind & PropertyKind.Named) {
+ propertyNames = TryGetPropertyNames(this.value_);
+ total += propertyNames.length;
+
+ // Get names for named interceptor properties if any.
+ if (this.hasNamedInterceptor() && (kind & PropertyKind.Named)) {
+ var namedInterceptorNames =
+ %GetNamedInterceptorPropertyNames(this.value_);
+ if (namedInterceptorNames) {
+ propertyNames = propertyNames.concat(namedInterceptorNames);
+ total += namedInterceptorNames.length;
+ }
+ }
+ }
+
+ // Find all the indexed properties.
+ if (kind & PropertyKind.Indexed) {
+ // Get own element names.
+ elementNames = %GetOwnElementNames(this.value_);
+ total += elementNames.length;
+
+ // Get names for indexed interceptor properties.
+ if (this.hasIndexedInterceptor() && (kind & PropertyKind.Indexed)) {
+ var indexedInterceptorNames =
+ %GetIndexedInterceptorElementNames(this.value_);
+ if (indexedInterceptorNames) {
+ elementNames = elementNames.concat(indexedInterceptorNames);
+ total += indexedInterceptorNames.length;
+ }
+ }
+ }
+ limit = Math.min(limit || total, total);
+
+ var names = new Array(limit);
+ var index = 0;
+
+ // Copy names for named properties.
+ if (kind & PropertyKind.Named) {
+ for (var i = 0; index < limit && i < propertyNames.length; i++) {
+ names[index++] = propertyNames[i];
+ }
+ }
+
+ // Copy names for indexed properties.
+ if (kind & PropertyKind.Indexed) {
+ for (var i = 0; index < limit && i < elementNames.length; i++) {
+ names[index++] = elementNames[i];
+ }
+ }
+
+ return names;
+};
+
+
+/**
+ * Return the properties for this object as an array of PropertyMirror objects.
+ * @param {number} kind Indicate whether named, indexed or both kinds of
+ * properties are requested
+ * @param {number} limit Limit the number of properties returned to the
+ specified value
+ * @return {Array} Property mirrors for this object
+ */
+ObjectMirror.prototype.properties = function(kind, limit) {
+ var names = this.propertyNames(kind, limit);
+ var properties = new Array(names.length);
+ for (var i = 0; i < names.length; i++) {
+ properties[i] = this.property(names[i]);
+ }
+
+ return properties;
+};
+
+
+/**
+ * Return the internal properties for this object as an array of
+ * InternalPropertyMirror objects.
+ * @return {Array} Property mirrors for this object
+ */
+ObjectMirror.prototype.internalProperties = function() {
+ return ObjectMirror.GetInternalProperties(this.value_);
+}
+
+
+ObjectMirror.prototype.property = function(name) {
+ var details = %DebugGetPropertyDetails(this.value_, builtins.$toName(name));
+ if (details) {
+ return new PropertyMirror(this, name, details);
+ }
+
+ // Nothing found.
+ return GetUndefinedMirror();
+};
+
+
+
+/**
+ * Try to find a property from its value.
+ * @param {Mirror} value The property value to look for
+ * @return {PropertyMirror} The property with the specified value. If no
+ * property was found with the specified value UndefinedMirror is returned
+ */
+ObjectMirror.prototype.lookupProperty = function(value) {
+ var properties = this.properties();
+
+ // Look for property value in properties.
+ for (var i = 0; i < properties.length; i++) {
+
+ // Skip properties which are defined through assessors.
+ var property = properties[i];
+ if (property.propertyType() != PropertyType.AccessorConstant) {
+ if (%_ObjectEquals(property.value_, value.value_)) {
+ return property;
+ }
+ }
+ }
+
+ // Nothing found.
+ return GetUndefinedMirror();
+};
+
+
+/**
+ * Returns objects which has direct references to this object
+ * @param {number} opt_max_objects Optional parameter specifying the maximum
+ * number of referencing objects to return.
+ * @return {Array} The objects which has direct references to this object.
+ */
+ObjectMirror.prototype.referencedBy = function(opt_max_objects) {
+ // Find all objects with direct references to this object.
+ var result = %DebugReferencedBy(this.value_,
+ Mirror.prototype, opt_max_objects || 0);
+
+ // Make mirrors for all the references found.
+ for (var i = 0; i < result.length; i++) {
+ result[i] = MakeMirror(result[i]);
+ }
+
+ return result;
+};
+
+
+ObjectMirror.prototype.toText = function() {
+ var name;
+ var ctor = this.constructorFunction();
+ if (!ctor.isFunction()) {
+ name = this.className();
+ } else {
+ name = ctor.name();
+ if (!name) {
+ name = this.className();
+ }
+ }
+ return '#<' + name + '>';
+};
+
+
+/**
+ * Return the internal properties of the value, such as [[PrimitiveValue]] of
+ * scalar wrapper objects, properties of the bound function and properties of
+ * the promise.
+ * This method is done static to be accessible from Debug API with the bare
+ * values without mirrors.
+ * @return {Array} array (possibly empty) of InternalProperty instances
+ */
+ObjectMirror.GetInternalProperties = function(value) {
+ var properties = %DebugGetInternalProperties(value);
+ var result = [];
+ for (var i = 0; i < properties.length; i += 2) {
+ result.push(new InternalPropertyMirror(properties[i], properties[i + 1]));
+ }
+ return result;
+}
+
+
+/**
+ * Mirror object for functions.
+ * @param {function} value The function object reflected by this mirror.
+ * @constructor
+ * @extends ObjectMirror
+ */
+function FunctionMirror(value) {
+ %_CallFunction(this, value, FUNCTION_TYPE, ObjectMirror);
+ this.resolved_ = true;
+}
+inherits(FunctionMirror, ObjectMirror);
+
+
+/**
+ * Returns whether the function is resolved.
+ * @return {boolean} True if the function is resolved. Unresolved functions can
+ * only originate as functions from stack frames
+ */
+FunctionMirror.prototype.resolved = function() {
+ return this.resolved_;
+};
+
+
+/**
+ * Returns the name of the function.
+ * @return {string} Name of the function
+ */
+FunctionMirror.prototype.name = function() {
+ return %FunctionGetName(this.value_);
+};
+
+
+/**
+ * Returns the inferred name of the function.
+ * @return {string} Name of the function
+ */
+FunctionMirror.prototype.inferredName = function() {
+ return %FunctionGetInferredName(this.value_);
+};
+
+
+/**
+ * Returns the source code for the function.
+ * @return {string or undefined} The source code for the function. If the
+ * function is not resolved undefined will be returned.
+ */
+FunctionMirror.prototype.source = function() {
+ // Return source if function is resolved. Otherwise just fall through to
+ // return undefined.
+ if (this.resolved()) {
+ return builtins.$functionSourceString(this.value_);
+ }
+};
+
+
+/**
+ * Returns the script object for the function.
+ * @return {ScriptMirror or undefined} Script object for the function or
+ * undefined if the function has no script
+ */
+FunctionMirror.prototype.script = function() {
+ // Return script if function is resolved. Otherwise just fall through
+ // to return undefined.
+ if (this.resolved()) {
+ if (this.script_) {
+ return this.script_;
+ }
+ var script = %FunctionGetScript(this.value_);
+ if (script) {
+ return this.script_ = MakeMirror(script);
+ }
+ }
+};
+
+
+/**
+ * Returns the script source position for the function. Only makes sense
+ * for functions which has a script defined.
+ * @return {Number or undefined} in-script position for the function
+ */
+FunctionMirror.prototype.sourcePosition_ = function() {
+ // Return position if function is resolved. Otherwise just fall
+ // through to return undefined.
+ if (this.resolved()) {
+ return %FunctionGetScriptSourcePosition(this.value_);
+ }
+};
+
+
+/**
+ * Returns the script source location object for the function. Only makes sense
+ * for functions which has a script defined.
+ * @return {Location or undefined} in-script location for the function begin
+ */
+FunctionMirror.prototype.sourceLocation = function() {
+ if (this.resolved()) {
+ var script = this.script();
+ if (script) {
+ return script.locationFromPosition(this.sourcePosition_(), true);
+ }
+ }
+};
+
+
+/**
+ * Returns objects constructed by this function.
+ * @param {number} opt_max_instances Optional parameter specifying the maximum
+ * number of instances to return.
+ * @return {Array or undefined} The objects constructed by this function.
+ */
+FunctionMirror.prototype.constructedBy = function(opt_max_instances) {
+ if (this.resolved()) {
+ // Find all objects constructed from this function.
+ var result = %DebugConstructedBy(this.value_, opt_max_instances || 0);
+
+ // Make mirrors for all the instances found.
+ for (var i = 0; i < result.length; i++) {
+ result[i] = MakeMirror(result[i]);
+ }
+
+ return result;
+ } else {
+ return [];
+ }
+};
+
+
+FunctionMirror.prototype.scopeCount = function() {
+ if (this.resolved()) {
+ if (IS_UNDEFINED(this.scopeCount_)) {
+ this.scopeCount_ = %GetFunctionScopeCount(this.value());
+ }
+ return this.scopeCount_;
+ } else {
+ return 0;
+ }
+};
+
+
+FunctionMirror.prototype.scope = function(index) {
+ if (this.resolved()) {
+ return new ScopeMirror(UNDEFINED, this, index);
+ }
+};
+
+
+FunctionMirror.prototype.toText = function() {
+ return this.source();
+};
+
+
+/**
+ * Mirror object for unresolved functions.
+ * @param {string} value The name for the unresolved function reflected by this
+ * mirror.
+ * @constructor
+ * @extends ObjectMirror
+ */
+function UnresolvedFunctionMirror(value) {
+ // Construct this using the ValueMirror as an unresolved function is not a
+ // real object but just a string.
+ %_CallFunction(this, FUNCTION_TYPE, value, ValueMirror);
+ this.propertyCount_ = 0;
+ this.elementCount_ = 0;
+ this.resolved_ = false;
+}
+inherits(UnresolvedFunctionMirror, FunctionMirror);
+
+
+UnresolvedFunctionMirror.prototype.className = function() {
+ return 'Function';
+};
+
+
+UnresolvedFunctionMirror.prototype.constructorFunction = function() {
+ return GetUndefinedMirror();
+};
+
+
+UnresolvedFunctionMirror.prototype.prototypeObject = function() {
+ return GetUndefinedMirror();
+};
+
+
+UnresolvedFunctionMirror.prototype.protoObject = function() {
+ return GetUndefinedMirror();
+};
+
+
+UnresolvedFunctionMirror.prototype.name = function() {
+ return this.value_;
+};
+
+
+UnresolvedFunctionMirror.prototype.inferredName = function() {
+ return undefined;
+};
+
+
+UnresolvedFunctionMirror.prototype.propertyNames = function(kind, limit) {
+ return [];
+};
+
+
+/**
+ * Mirror object for arrays.
+ * @param {Array} value The Array object reflected by this mirror
+ * @constructor
+ * @extends ObjectMirror
+ */
+function ArrayMirror(value) {
+ %_CallFunction(this, value, ObjectMirror);
+}
+inherits(ArrayMirror, ObjectMirror);
+
+
+ArrayMirror.prototype.length = function() {
+ return this.value_.length;
+};
+
+
+ArrayMirror.prototype.indexedPropertiesFromRange = function(opt_from_index,
+ opt_to_index) {
+ var from_index = opt_from_index || 0;
+ var to_index = opt_to_index || this.length() - 1;
+ if (from_index > to_index) return new Array();
+ var values = new Array(to_index - from_index + 1);
+ for (var i = from_index; i <= to_index; i++) {
+ var details = %DebugGetPropertyDetails(this.value_, builtins.$toString(i));
+ var value;
+ if (details) {
+ value = new PropertyMirror(this, i, details);
+ } else {
+ value = GetUndefinedMirror();
+ }
+ values[i - from_index] = value;
+ }
+ return values;
+};
+
+
+/**
+ * Mirror object for dates.
+ * @param {Date} value The Date object reflected by this mirror
+ * @constructor
+ * @extends ObjectMirror
+ */
+function DateMirror(value) {
+ %_CallFunction(this, value, ObjectMirror);
+}
+inherits(DateMirror, ObjectMirror);
+
+
+DateMirror.prototype.toText = function() {
+ var s = JSON.stringify(this.value_);
+ return s.substring(1, s.length - 1); // cut quotes
+};
+
+
+/**
+ * Mirror object for regular expressions.
+ * @param {RegExp} value The RegExp object reflected by this mirror
+ * @constructor
+ * @extends ObjectMirror
+ */
+function RegExpMirror(value) {
+ %_CallFunction(this, value, REGEXP_TYPE, ObjectMirror);
+}
+inherits(RegExpMirror, ObjectMirror);
+
+
+/**
+ * Returns the source to the regular expression.
+ * @return {string or undefined} The source to the regular expression
+ */
+RegExpMirror.prototype.source = function() {
+ return this.value_.source;
+};
+
+
+/**
+ * Returns whether this regular expression has the global (g) flag set.
+ * @return {boolean} Value of the global flag
+ */
+RegExpMirror.prototype.global = function() {
+ return this.value_.global;
+};
+
+
+/**
+ * Returns whether this regular expression has the ignore case (i) flag set.
+ * @return {boolean} Value of the ignore case flag
+ */
+RegExpMirror.prototype.ignoreCase = function() {
+ return this.value_.ignoreCase;
+};
+
+
+/**
+ * Returns whether this regular expression has the multiline (m) flag set.
+ * @return {boolean} Value of the multiline flag
+ */
+RegExpMirror.prototype.multiline = function() {
+ return this.value_.multiline;
+};
+
+
+/**
+ * Returns whether this regular expression has the sticky (y) flag set.
+ * @return {boolean} Value of the sticky flag
+ */
+RegExpMirror.prototype.sticky = function() {
+ return this.value_.sticky;
+};
+
+
+/**
+ * Returns whether this regular expression has the unicode (u) flag set.
+ * @return {boolean} Value of the unicode flag
+ */
+RegExpMirror.prototype.unicode = function() {
+ return this.value_.unicode;
+};
+
+
+RegExpMirror.prototype.toText = function() {
+ // Simpel to text which is used when on specialization in subclass.
+ return "/" + this.source() + "/";
+};
+
+
+/**
+ * Mirror object for error objects.
+ * @param {Error} value The error object reflected by this mirror
+ * @constructor
+ * @extends ObjectMirror
+ */
+function ErrorMirror(value) {
+ %_CallFunction(this, value, ERROR_TYPE, ObjectMirror);
+}
+inherits(ErrorMirror, ObjectMirror);
+
+
+/**
+ * Returns the message for this eror object.
+ * @return {string or undefined} The message for this eror object
+ */
+ErrorMirror.prototype.message = function() {
+ return this.value_.message;
+};
+
+
+ErrorMirror.prototype.toText = function() {
+ // Use the same text representation as in messages.js.
+ var text;
+ try {
+ text = %_CallFunction(this.value_, builtins.$errorToString);
+ } catch (e) {
+ text = '#<Error>';
+ }
+ return text;
+};
+
+
+/**
+ * Mirror object for a Promise object.
+ * @param {Object} value The Promise object
+ * @constructor
+ * @extends ObjectMirror
+ */
+function PromiseMirror(value) {
+ %_CallFunction(this, value, PROMISE_TYPE, ObjectMirror);
+}
+inherits(PromiseMirror, ObjectMirror);
+
+
+function PromiseGetStatus_(value) {
+ var status = %DebugGetProperty(value, builtins.$promiseStatus);
+ if (status == 0) return "pending";
+ if (status == 1) return "resolved";
+ return "rejected";
+}
+
+
+function PromiseGetValue_(value) {
+ return %DebugGetProperty(value, builtins.$promiseValue);
+}
+
+
+PromiseMirror.prototype.status = function() {
+ return PromiseGetStatus_(this.value_);
+};
+
+
+PromiseMirror.prototype.promiseValue = function() {
+ return MakeMirror(PromiseGetValue_(this.value_));
+};
+
+
+function MapMirror(value) {
+ %_CallFunction(this, value, MAP_TYPE, ObjectMirror);
+}
+inherits(MapMirror, ObjectMirror);
+
+
+/**
+ * Returns an array of key/value pairs of a map.
+ * This will keep keys alive for WeakMaps.
+ *
+ * @param {number=} opt_limit Max elements to return.
+ * @returns {Array.<Object>} Array of key/value pairs of a map.
+ */
+MapMirror.prototype.entries = function(opt_limit) {
+ var result = [];
+
+ if (IS_WEAKMAP(this.value_)) {
+ var entries = %GetWeakMapEntries(this.value_, opt_limit || 0);
+ for (var i = 0; i < entries.length; i += 2) {
+ result.push({
+ key: entries[i],
+ value: entries[i + 1]
+ });
+ }
+ return result;
+ }
+
+ var iter = %_CallFunction(this.value_, builtins.$mapEntries);
+ var next;
+ while ((!opt_limit || result.length < opt_limit) &&
+ !(next = iter.next()).done) {
+ result.push({
+ key: next.value[0],
+ value: next.value[1]
+ });
+ }
+ return result;
+};
+
+
+function SetMirror(value) {
+ %_CallFunction(this, value, SET_TYPE, ObjectMirror);
+}
+inherits(SetMirror, ObjectMirror);
+
+
+function IteratorGetValues_(iter, next_function, opt_limit) {
+ var result = [];
+ var next;
+ while ((!opt_limit || result.length < opt_limit) &&
+ !(next = %_CallFunction(iter, next_function)).done) {
+ result.push(next.value);
+ }
+ return result;
+}
+
+
+/**
+ * Returns an array of elements of a set.
+ * This will keep elements alive for WeakSets.
+ *
+ * @param {number=} opt_limit Max elements to return.
+ * @returns {Array.<Object>} Array of elements of a set.
+ */
+SetMirror.prototype.values = function(opt_limit) {
+ if (IS_WEAKSET(this.value_)) {
+ return %GetWeakSetValues(this.value_, opt_limit || 0);
+ }
+
+ var iter = %_CallFunction(this.value_, builtins.$setValues);
+ return IteratorGetValues_(iter, builtins.$setIteratorNext, opt_limit);
+};
+
+
+function IteratorMirror(value) {
+ %_CallFunction(this, value, ITERATOR_TYPE, ObjectMirror);
+}
+inherits(IteratorMirror, ObjectMirror);
+
+
+/**
+ * Returns a preview of elements of an iterator.
+ * Does not change the backing iterator state.
+ *
+ * @param {number=} opt_limit Max elements to return.
+ * @returns {Array.<Object>} Array of elements of an iterator.
+ */
+IteratorMirror.prototype.preview = function(opt_limit) {
+ if (IS_MAP_ITERATOR(this.value_)) {
+ return IteratorGetValues_(%MapIteratorClone(this.value_),
+ builtins.$mapIteratorNext,
+ opt_limit);
+ } else if (IS_SET_ITERATOR(this.value_)) {
+ return IteratorGetValues_(%SetIteratorClone(this.value_),
+ builtins.$setIteratorNext,
+ opt_limit);
+ }
+};
+
+
+/**
+ * Mirror object for a Generator object.
+ * @param {Object} data The Generator object
+ * @constructor
+ * @extends Mirror
+ */
+function GeneratorMirror(value) {
+ %_CallFunction(this, value, GENERATOR_TYPE, ObjectMirror);
+}
+inherits(GeneratorMirror, ObjectMirror);
+
+
+function GeneratorGetStatus_(value) {
+ var continuation = %GeneratorGetContinuation(value);
+ if (continuation < 0) return "running";
+ if (continuation == 0) return "closed";
+ return "suspended";
+}
+
+
+GeneratorMirror.prototype.status = function() {
+ return GeneratorGetStatus_(this.value_);
+};
+
+
+GeneratorMirror.prototype.sourcePosition_ = function() {
+ return %GeneratorGetSourcePosition(this.value_);
+};
+
+
+GeneratorMirror.prototype.sourceLocation = function() {
+ var pos = this.sourcePosition_();
+ if (!IS_UNDEFINED(pos)) {
+ var script = this.func().script();
+ if (script) {
+ return script.locationFromPosition(pos, true);
+ }
+ }
+};
+
+
+GeneratorMirror.prototype.func = function() {
+ if (!this.func_) {
+ this.func_ = MakeMirror(%GeneratorGetFunction(this.value_));
+ }
+ return this.func_;
+};
+
+
+GeneratorMirror.prototype.context = function() {
+ if (!this.context_) {
+ this.context_ = new ContextMirror(%GeneratorGetContext(this.value_));
+ }
+ return this.context_;
+};
+
+
+GeneratorMirror.prototype.receiver = function() {
+ if (!this.receiver_) {
+ this.receiver_ = MakeMirror(%GeneratorGetReceiver(this.value_));
+ }
+ return this.receiver_;
+};
+
+
+/**
+ * Base mirror object for properties.
+ * @param {ObjectMirror} mirror The mirror object having this property
+ * @param {string} name The name of the property
+ * @param {Array} details Details about the property
+ * @constructor
+ * @extends Mirror
+ */
+function PropertyMirror(mirror, name, details) {
+ %_CallFunction(this, PROPERTY_TYPE, Mirror);
+ this.mirror_ = mirror;
+ this.name_ = name;
+ this.value_ = details[0];
+ this.details_ = details[1];
+ this.is_interceptor_ = details[2];
+ if (details.length > 3) {
+ this.exception_ = details[3];
+ this.getter_ = details[4];
+ this.setter_ = details[5];
+ }
+}
+inherits(PropertyMirror, Mirror);
+
+
+PropertyMirror.prototype.isReadOnly = function() {
+ return (this.attributes() & PropertyAttribute.ReadOnly) != 0;
+};
+
+
+PropertyMirror.prototype.isEnum = function() {
+ return (this.attributes() & PropertyAttribute.DontEnum) == 0;
+};
+
+
+PropertyMirror.prototype.canDelete = function() {
+ return (this.attributes() & PropertyAttribute.DontDelete) == 0;
+};
+
+
+PropertyMirror.prototype.name = function() {
+ return this.name_;
+};
+
+
+PropertyMirror.prototype.isIndexed = function() {
+ for (var i = 0; i < this.name_.length; i++) {
+ if (this.name_[i] < '0' || '9' < this.name_[i]) {
+ return false;
+ }
+ }
+ return true;
+};
+
+
+PropertyMirror.prototype.value = function() {
+ return MakeMirror(this.value_, false);
+};
+
+
+/**
+ * Returns whether this property value is an exception.
+ * @return {booolean} True if this property value is an exception
+ */
+PropertyMirror.prototype.isException = function() {
+ return this.exception_ ? true : false;
+};
+
+
+PropertyMirror.prototype.attributes = function() {
+ return %DebugPropertyAttributesFromDetails(this.details_);
+};
+
+
+PropertyMirror.prototype.propertyType = function() {
+ return %DebugPropertyTypeFromDetails(this.details_);
+};
+
+
+PropertyMirror.prototype.insertionIndex = function() {
+ return %DebugPropertyIndexFromDetails(this.details_);
+};
+
+
+/**
+ * Returns whether this property has a getter defined through __defineGetter__.
+ * @return {booolean} True if this property has a getter
+ */
+PropertyMirror.prototype.hasGetter = function() {
+ return this.getter_ ? true : false;
+};
+
+
+/**
+ * Returns whether this property has a setter defined through __defineSetter__.
+ * @return {booolean} True if this property has a setter
+ */
+PropertyMirror.prototype.hasSetter = function() {
+ return this.setter_ ? true : false;
+};
+
+
+/**
+ * Returns the getter for this property defined through __defineGetter__.
+ * @return {Mirror} FunctionMirror reflecting the getter function or
+ * UndefinedMirror if there is no getter for this property
+ */
+PropertyMirror.prototype.getter = function() {
+ if (this.hasGetter()) {
+ return MakeMirror(this.getter_);
+ } else {
+ return GetUndefinedMirror();
+ }
+};
+
+
+/**
+ * Returns the setter for this property defined through __defineSetter__.
+ * @return {Mirror} FunctionMirror reflecting the setter function or
+ * UndefinedMirror if there is no setter for this property
+ */
+PropertyMirror.prototype.setter = function() {
+ if (this.hasSetter()) {
+ return MakeMirror(this.setter_);
+ } else {
+ return GetUndefinedMirror();
+ }
+};
+
+
+/**
+ * Returns whether this property is natively implemented by the host or a set
+ * through JavaScript code.
+ * @return {boolean} True if the property is
+ * UndefinedMirror if there is no setter for this property
+ */
+PropertyMirror.prototype.isNative = function() {
+ return this.is_interceptor_ ||
+ ((this.propertyType() == PropertyType.AccessorConstant) &&
+ !this.hasGetter() && !this.hasSetter());
+};
+
+
+/**
+ * Mirror object for internal properties. Internal property reflects properties
+ * not accessible from user code such as [[BoundThis]] in bound function.
+ * Their names are merely symbolic.
+ * @param {string} name The name of the property
+ * @param {value} property value
+ * @constructor
+ * @extends Mirror
+ */
+function InternalPropertyMirror(name, value) {
+ %_CallFunction(this, INTERNAL_PROPERTY_TYPE, Mirror);
+ this.name_ = name;
+ this.value_ = value;
+}
+inherits(InternalPropertyMirror, Mirror);
+
+
+InternalPropertyMirror.prototype.name = function() {
+ return this.name_;
+};
+
+
+InternalPropertyMirror.prototype.value = function() {
+ return MakeMirror(this.value_, false);
+};
+
+
+var kFrameDetailsFrameIdIndex = 0;
+var kFrameDetailsReceiverIndex = 1;
+var kFrameDetailsFunctionIndex = 2;
+var kFrameDetailsArgumentCountIndex = 3;
+var kFrameDetailsLocalCountIndex = 4;
+var kFrameDetailsSourcePositionIndex = 5;
+var kFrameDetailsConstructCallIndex = 6;
+var kFrameDetailsAtReturnIndex = 7;
+var kFrameDetailsFlagsIndex = 8;
+var kFrameDetailsFirstDynamicIndex = 9;
+
+var kFrameDetailsNameIndex = 0;
+var kFrameDetailsValueIndex = 1;
+var kFrameDetailsNameValueSize = 2;
+
+var kFrameDetailsFlagDebuggerFrameMask = 1 << 0;
+var kFrameDetailsFlagOptimizedFrameMask = 1 << 1;
+var kFrameDetailsFlagInlinedFrameIndexMask = 7 << 2;
+
+/**
+ * Wrapper for the frame details information retreived from the VM. The frame
+ * details from the VM is an array with the following content. See runtime.cc
+ * Runtime_GetFrameDetails.
+ * 0: Id
+ * 1: Receiver
+ * 2: Function
+ * 3: Argument count
+ * 4: Local count
+ * 5: Source position
+ * 6: Construct call
+ * 7: Is at return
+ * 8: Flags (debugger frame, optimized frame, inlined frame index)
+ * Arguments name, value
+ * Locals name, value
+ * Return value if any
+ * @param {number} break_id Current break id
+ * @param {number} index Frame number
+ * @constructor
+ */
+function FrameDetails(break_id, index) {
+ this.break_id_ = break_id;
+ this.details_ = %GetFrameDetails(break_id, index);
+}
+
+
+FrameDetails.prototype.frameId = function() {
+ %CheckExecutionState(this.break_id_);
+ return this.details_[kFrameDetailsFrameIdIndex];
+};
+
+
+FrameDetails.prototype.receiver = function() {
+ %CheckExecutionState(this.break_id_);
+ return this.details_[kFrameDetailsReceiverIndex];
+};
+
+
+FrameDetails.prototype.func = function() {
+ %CheckExecutionState(this.break_id_);
+ return this.details_[kFrameDetailsFunctionIndex];
+};
+
+
+FrameDetails.prototype.isConstructCall = function() {
+ %CheckExecutionState(this.break_id_);
+ return this.details_[kFrameDetailsConstructCallIndex];
+};
+
+
+FrameDetails.prototype.isAtReturn = function() {
+ %CheckExecutionState(this.break_id_);
+ return this.details_[kFrameDetailsAtReturnIndex];
+};
+
+
+FrameDetails.prototype.isDebuggerFrame = function() {
+ %CheckExecutionState(this.break_id_);
+ var f = kFrameDetailsFlagDebuggerFrameMask;
+ return (this.details_[kFrameDetailsFlagsIndex] & f) == f;
+};
+
+
+FrameDetails.prototype.isOptimizedFrame = function() {
+ %CheckExecutionState(this.break_id_);
+ var f = kFrameDetailsFlagOptimizedFrameMask;
+ return (this.details_[kFrameDetailsFlagsIndex] & f) == f;
+};
+
+
+FrameDetails.prototype.isInlinedFrame = function() {
+ return this.inlinedFrameIndex() > 0;
+};
+
+
+FrameDetails.prototype.inlinedFrameIndex = function() {
+ %CheckExecutionState(this.break_id_);
+ var f = kFrameDetailsFlagInlinedFrameIndexMask;
+ return (this.details_[kFrameDetailsFlagsIndex] & f) >> 2;
+};
+
+
+FrameDetails.prototype.argumentCount = function() {
+ %CheckExecutionState(this.break_id_);
+ return this.details_[kFrameDetailsArgumentCountIndex];
+};
+
+
+FrameDetails.prototype.argumentName = function(index) {
+ %CheckExecutionState(this.break_id_);
+ if (index >= 0 && index < this.argumentCount()) {
+ return this.details_[kFrameDetailsFirstDynamicIndex +
+ index * kFrameDetailsNameValueSize +
+ kFrameDetailsNameIndex];
+ }
+};
+
+
+FrameDetails.prototype.argumentValue = function(index) {
+ %CheckExecutionState(this.break_id_);
+ if (index >= 0 && index < this.argumentCount()) {
+ return this.details_[kFrameDetailsFirstDynamicIndex +
+ index * kFrameDetailsNameValueSize +
+ kFrameDetailsValueIndex];
+ }
+};
+
+
+FrameDetails.prototype.localCount = function() {
+ %CheckExecutionState(this.break_id_);
+ return this.details_[kFrameDetailsLocalCountIndex];
+};
+
+
+FrameDetails.prototype.sourcePosition = function() {
+ %CheckExecutionState(this.break_id_);
+ return this.details_[kFrameDetailsSourcePositionIndex];
+};
+
+
+FrameDetails.prototype.localName = function(index) {
+ %CheckExecutionState(this.break_id_);
+ if (index >= 0 && index < this.localCount()) {
+ var locals_offset = kFrameDetailsFirstDynamicIndex +
+ this.argumentCount() * kFrameDetailsNameValueSize;
+ return this.details_[locals_offset +
+ index * kFrameDetailsNameValueSize +
+ kFrameDetailsNameIndex];
+ }
+};
+
+
+FrameDetails.prototype.localValue = function(index) {
+ %CheckExecutionState(this.break_id_);
+ if (index >= 0 && index < this.localCount()) {
+ var locals_offset = kFrameDetailsFirstDynamicIndex +
+ this.argumentCount() * kFrameDetailsNameValueSize;
+ return this.details_[locals_offset +
+ index * kFrameDetailsNameValueSize +
+ kFrameDetailsValueIndex];
+ }
+};
+
+
+FrameDetails.prototype.returnValue = function() {
+ %CheckExecutionState(this.break_id_);
+ var return_value_offset =
+ kFrameDetailsFirstDynamicIndex +
+ (this.argumentCount() + this.localCount()) * kFrameDetailsNameValueSize;
+ if (this.details_[kFrameDetailsAtReturnIndex]) {
+ return this.details_[return_value_offset];
+ }
+};
+
+
+FrameDetails.prototype.scopeCount = function() {
+ if (IS_UNDEFINED(this.scopeCount_)) {
+ this.scopeCount_ = %GetScopeCount(this.break_id_, this.frameId());
+ }
+ return this.scopeCount_;
+};
+
+
+FrameDetails.prototype.stepInPositionsImpl = function() {
+ return %GetStepInPositions(this.break_id_, this.frameId());
+};
+
+
+/**
+ * Mirror object for stack frames.
+ * @param {number} break_id The break id in the VM for which this frame is
+ valid
+ * @param {number} index The frame index (top frame is index 0)
+ * @constructor
+ * @extends Mirror
+ */
+function FrameMirror(break_id, index) {
+ %_CallFunction(this, FRAME_TYPE, Mirror);
+ this.break_id_ = break_id;
+ this.index_ = index;
+ this.details_ = new FrameDetails(break_id, index);
+}
+inherits(FrameMirror, Mirror);
+
+
+FrameMirror.prototype.details = function() {
+ return this.details_;
+};
+
+
+FrameMirror.prototype.index = function() {
+ return this.index_;
+};
+
+
+FrameMirror.prototype.func = function() {
+ if (this.func_) {
+ return this.func_;
+ }
+
+ // Get the function for this frame from the VM.
+ var f = this.details_.func();
+
+ // Create a function mirror. NOTE: MakeMirror cannot be used here as the
+ // value returned from the VM might be a string if the function for the
+ // frame is unresolved.
+ if (IS_FUNCTION(f)) {
+ return this.func_ = MakeMirror(f);
+ } else {
+ return new UnresolvedFunctionMirror(f);
+ }
+};
+
+
+FrameMirror.prototype.receiver = function() {
+ return MakeMirror(this.details_.receiver());
+};
+
+
+FrameMirror.prototype.isConstructCall = function() {
+ return this.details_.isConstructCall();
+};
+
+
+FrameMirror.prototype.isAtReturn = function() {
+ return this.details_.isAtReturn();
+};
+
+
+FrameMirror.prototype.isDebuggerFrame = function() {
+ return this.details_.isDebuggerFrame();
+};
+
+
+FrameMirror.prototype.isOptimizedFrame = function() {
+ return this.details_.isOptimizedFrame();
+};
+
+
+FrameMirror.prototype.isInlinedFrame = function() {
+ return this.details_.isInlinedFrame();
+};
+
+
+FrameMirror.prototype.inlinedFrameIndex = function() {
+ return this.details_.inlinedFrameIndex();
+};
+
+
+FrameMirror.prototype.argumentCount = function() {
+ return this.details_.argumentCount();
+};
+
+
+FrameMirror.prototype.argumentName = function(index) {
+ return this.details_.argumentName(index);
+};
+
+
+FrameMirror.prototype.argumentValue = function(index) {
+ return MakeMirror(this.details_.argumentValue(index));
+};
+
+
+FrameMirror.prototype.localCount = function() {
+ return this.details_.localCount();
+};
+
+
+FrameMirror.prototype.localName = function(index) {
+ return this.details_.localName(index);
+};
+
+
+FrameMirror.prototype.localValue = function(index) {
+ return MakeMirror(this.details_.localValue(index));
+};
+
+
+FrameMirror.prototype.returnValue = function() {
+ return MakeMirror(this.details_.returnValue());
+};
+
+
+FrameMirror.prototype.sourcePosition = function() {
+ return this.details_.sourcePosition();
+};
+
+
+FrameMirror.prototype.sourceLocation = function() {
+ var func = this.func();
+ if (func.resolved()) {
+ var script = func.script();
+ if (script) {
+ return script.locationFromPosition(this.sourcePosition(), true);
+ }
+ }
+};
+
+
+FrameMirror.prototype.sourceLine = function() {
+ var location = this.sourceLocation();
+ if (location) {
+ return location.line;
+ }
+};
+
+
+FrameMirror.prototype.sourceColumn = function() {
+ var location = this.sourceLocation();
+ if (location) {
+ return location.column;
+ }
+};
+
+
+FrameMirror.prototype.sourceLineText = function() {
+ var location = this.sourceLocation();
+ if (location) {
+ return location.sourceText();
+ }
+};
+
+
+FrameMirror.prototype.scopeCount = function() {
+ return this.details_.scopeCount();
+};
+
+
+FrameMirror.prototype.scope = function(index) {
+ return new ScopeMirror(this, UNDEFINED, index);
+};
+
+
+FrameMirror.prototype.allScopes = function(opt_ignore_nested_scopes) {
+ var scopeDetails = %GetAllScopesDetails(this.break_id_,
+ this.details_.frameId(),
+ this.details_.inlinedFrameIndex(),
+ !!opt_ignore_nested_scopes);
+ var result = [];
+ for (var i = 0; i < scopeDetails.length; ++i) {
+ result.push(new ScopeMirror(this, UNDEFINED, i, scopeDetails[i]));
+ }
+ return result;
+};
+
+
+FrameMirror.prototype.stepInPositions = function() {
+ var script = this.func().script();
+ var funcOffset = this.func().sourcePosition_();
+
+ var stepInRaw = this.details_.stepInPositionsImpl();
+ var result = [];
+ if (stepInRaw) {
+ for (var i = 0; i < stepInRaw.length; i++) {
+ var posStruct = {};
+ var offset = script.locationFromPosition(funcOffset + stepInRaw[i],
+ true);
+ serializeLocationFields(offset, posStruct);
+ var item = {
+ position: posStruct
+ };
+ result.push(item);
+ }
+ }
+
+ return result;
+};
+
+
+FrameMirror.prototype.evaluate = function(source, disable_break,
+ opt_context_object) {
+ return MakeMirror(%DebugEvaluate(this.break_id_,
+ this.details_.frameId(),
+ this.details_.inlinedFrameIndex(),
+ source,
+ Boolean(disable_break),
+ opt_context_object));
+};
+
+
+FrameMirror.prototype.invocationText = function() {
+ // Format frame invoaction (receiver, function and arguments).
+ var result = '';
+ var func = this.func();
+ var receiver = this.receiver();
+ if (this.isConstructCall()) {
+ // For constructor frames display new followed by the function name.
+ result += 'new ';
+ result += func.name() ? func.name() : '[anonymous]';
+ } else if (this.isDebuggerFrame()) {
+ result += '[debugger]';
+ } else {
+ // If the receiver has a className which is 'global' don't display it.
+ var display_receiver =
+ !receiver.className || (receiver.className() != 'global');
+ if (display_receiver) {
+ result += receiver.toText();
+ }
+ // Try to find the function as a property in the receiver. Include the
+ // prototype chain in the lookup.
+ var property = GetUndefinedMirror();
+ if (receiver.isObject()) {
+ for (var r = receiver;
+ !r.isNull() && property.isUndefined();
+ r = r.protoObject()) {
+ property = r.lookupProperty(func);
+ }
+ }
+ if (!property.isUndefined()) {
+ // The function invoked was found on the receiver. Use the property name
+ // for the backtrace.
+ if (!property.isIndexed()) {
+ if (display_receiver) {
+ result += '.';
+ }
+ result += property.name();
+ } else {
+ result += '[';
+ result += property.name();
+ result += ']';
+ }
+ // Also known as - if the name in the function doesn't match the name
+ // under which it was looked up.
+ if (func.name() && func.name() != property.name()) {
+ result += '(aka ' + func.name() + ')';
+ }
+ } else {
+ // The function invoked was not found on the receiver. Use the function
+ // name if available for the backtrace.
+ if (display_receiver) {
+ result += '.';
+ }
+ result += func.name() ? func.name() : '[anonymous]';
+ }
+ }
+
+ // Render arguments for normal frames.
+ if (!this.isDebuggerFrame()) {
+ result += '(';
+ for (var i = 0; i < this.argumentCount(); i++) {
+ if (i != 0) result += ', ';
+ if (this.argumentName(i)) {
+ result += this.argumentName(i);
+ result += '=';
+ }
+ result += this.argumentValue(i).toText();
+ }
+ result += ')';
+ }
+
+ if (this.isAtReturn()) {
+ result += ' returning ';
+ result += this.returnValue().toText();
+ }
+
+ return result;
+};
+
+
+FrameMirror.prototype.sourceAndPositionText = function() {
+ // Format source and position.
+ var result = '';
+ var func = this.func();
+ if (func.resolved()) {
+ var script = func.script();
+ if (script) {
+ if (script.name()) {
+ result += script.name();
+ } else {
+ result += '[unnamed]';
+ }
+ if (!this.isDebuggerFrame()) {
+ var location = this.sourceLocation();
+ result += ' line ';
+ result += !IS_UNDEFINED(location) ? (location.line + 1) : '?';
+ result += ' column ';
+ result += !IS_UNDEFINED(location) ? (location.column + 1) : '?';
+ if (!IS_UNDEFINED(this.sourcePosition())) {
+ result += ' (position ' + (this.sourcePosition() + 1) + ')';
+ }
+ }
+ } else {
+ result += '[no source]';
+ }
+ } else {
+ result += '[unresolved]';
+ }
+
+ return result;
+};
+
+
+FrameMirror.prototype.localsText = function() {
+ // Format local variables.
+ var result = '';
+ var locals_count = this.localCount();
+ if (locals_count > 0) {
+ for (var i = 0; i < locals_count; ++i) {
+ result += ' var ';
+ result += this.localName(i);
+ result += ' = ';
+ result += this.localValue(i).toText();
+ if (i < locals_count - 1) result += '\n';
+ }
+ }
+
+ return result;
+};
+
+
+FrameMirror.prototype.restart = function() {
+ var result = %LiveEditRestartFrame(this.break_id_, this.index_);
+ if (IS_UNDEFINED(result)) {
+ result = "Failed to find requested frame";
+ }
+ return result;
+};
+
+
+FrameMirror.prototype.toText = function(opt_locals) {
+ var result = '';
+ result += '#' + (this.index() <= 9 ? '0' : '') + this.index();
+ result += ' ';
+ result += this.invocationText();
+ result += ' ';
+ result += this.sourceAndPositionText();
+ if (opt_locals) {
+ result += '\n';
+ result += this.localsText();
+ }
+ return result;
+};
+
+
+var kScopeDetailsTypeIndex = 0;
+var kScopeDetailsObjectIndex = 1;
+
+function ScopeDetails(frame, fun, index, opt_details) {
+ if (frame) {
+ this.break_id_ = frame.break_id_;
+ this.details_ = opt_details ||
+ %GetScopeDetails(frame.break_id_,
+ frame.details_.frameId(),
+ frame.details_.inlinedFrameIndex(),
+ index);
+ this.frame_id_ = frame.details_.frameId();
+ this.inlined_frame_id_ = frame.details_.inlinedFrameIndex();
+ } else {
+ this.details_ = opt_details || %GetFunctionScopeDetails(fun.value(), index);
+ this.fun_value_ = fun.value();
+ this.break_id_ = undefined;
+ }
+ this.index_ = index;
+}
+
+
+ScopeDetails.prototype.type = function() {
+ if (!IS_UNDEFINED(this.break_id_)) {
+ %CheckExecutionState(this.break_id_);
+ }
+ return this.details_[kScopeDetailsTypeIndex];
+};
+
+
+ScopeDetails.prototype.object = function() {
+ if (!IS_UNDEFINED(this.break_id_)) {
+ %CheckExecutionState(this.break_id_);
+ }
+ return this.details_[kScopeDetailsObjectIndex];
+};
+
+
+ScopeDetails.prototype.setVariableValueImpl = function(name, new_value) {
+ var raw_res;
+ if (!IS_UNDEFINED(this.break_id_)) {
+ %CheckExecutionState(this.break_id_);
+ raw_res = %SetScopeVariableValue(this.break_id_, this.frame_id_,
+ this.inlined_frame_id_, this.index_, name, new_value);
+ } else {
+ raw_res = %SetScopeVariableValue(this.fun_value_, null, null, this.index_,
+ name, new_value);
+ }
+ if (!raw_res) {
+ throw new Error("Failed to set variable value");
+ }
+};
+
+
+/**
+ * Mirror object for scope of frame or function. Either frame or function must
+ * be specified.
+ * @param {FrameMirror} frame The frame this scope is a part of
+ * @param {FunctionMirror} function The function this scope is a part of
+ * @param {number} index The scope index in the frame
+ * @param {Array=} opt_details Raw scope details data
+ * @constructor
+ * @extends Mirror
+ */
+function ScopeMirror(frame, function, index, opt_details) {
+ %_CallFunction(this, SCOPE_TYPE, Mirror);
+ if (frame) {
+ this.frame_index_ = frame.index_;
+ } else {
+ this.frame_index_ = undefined;
+ }
+ this.scope_index_ = index;
+ this.details_ = new ScopeDetails(frame, function, index, opt_details);
+}
+inherits(ScopeMirror, Mirror);
+
+
+ScopeMirror.prototype.details = function() {
+ return this.details_;
+};
+
+
+ScopeMirror.prototype.frameIndex = function() {
+ return this.frame_index_;
+};
+
+
+ScopeMirror.prototype.scopeIndex = function() {
+ return this.scope_index_;
+};
+
+
+ScopeMirror.prototype.scopeType = function() {
+ return this.details_.type();
+};
+
+
+ScopeMirror.prototype.scopeObject = function() {
+ // For local, closure and script scopes create a transient mirror
+ // as these objects are created on the fly materializing the local
+ // or closure scopes and therefore will not preserve identity.
+ var transient = this.scopeType() == ScopeType.Local ||
+ this.scopeType() == ScopeType.Closure ||
+ this.scopeType() == ScopeType.Script;
+ return MakeMirror(this.details_.object(), transient);
+};
+
+
+ScopeMirror.prototype.setVariableValue = function(name, new_value) {
+ this.details_.setVariableValueImpl(name, new_value);
+};
+
+
+/**
+ * Mirror object for script source.
+ * @param {Script} script The script object
+ * @constructor
+ * @extends Mirror
+ */
+function ScriptMirror(script) {
+ %_CallFunction(this, SCRIPT_TYPE, Mirror);
+ this.script_ = script;
+ this.context_ = new ContextMirror(script.context_data);
+ this.allocateHandle_();
+}
+inherits(ScriptMirror, Mirror);
+
+
+ScriptMirror.prototype.value = function() {
+ return this.script_;
+};
+
+
+ScriptMirror.prototype.name = function() {
+ return this.script_.name || this.script_.nameOrSourceURL();
+};
+
+
+ScriptMirror.prototype.id = function() {
+ return this.script_.id;
+};
+
+
+ScriptMirror.prototype.source = function() {
+ return this.script_.source;
+};
+
+
+ScriptMirror.prototype.setSource = function(source) {
+ %DebugSetScriptSource(this.script_, source);
+};
+
+
+ScriptMirror.prototype.lineOffset = function() {
+ return this.script_.line_offset;
+};
+
+
+ScriptMirror.prototype.columnOffset = function() {
+ return this.script_.column_offset;
+};
+
+
+ScriptMirror.prototype.data = function() {
+ return this.script_.data;
+};
+
+
+ScriptMirror.prototype.scriptType = function() {
+ return this.script_.type;
+};
+
+
+ScriptMirror.prototype.compilationType = function() {
+ return this.script_.compilation_type;
+};
+
+
+ScriptMirror.prototype.lineCount = function() {
+ return this.script_.lineCount();
+};
+
+
+ScriptMirror.prototype.locationFromPosition = function(
+ position, include_resource_offset) {
+ return this.script_.locationFromPosition(position, include_resource_offset);
+};
+
+
+ScriptMirror.prototype.sourceSlice = function (opt_from_line, opt_to_line) {
+ return this.script_.sourceSlice(opt_from_line, opt_to_line);
+};
+
+
+ScriptMirror.prototype.context = function() {
+ return this.context_;
+};
+
+
+ScriptMirror.prototype.evalFromScript = function() {
+ return MakeMirror(this.script_.eval_from_script);
+};
+
+
+ScriptMirror.prototype.evalFromFunctionName = function() {
+ return MakeMirror(this.script_.eval_from_function_name);
+};
+
+
+ScriptMirror.prototype.evalFromLocation = function() {
+ var eval_from_script = this.evalFromScript();
+ if (!eval_from_script.isUndefined()) {
+ var position = this.script_.eval_from_script_position;
+ return eval_from_script.locationFromPosition(position, true);
+ }
+};
+
+
+ScriptMirror.prototype.toText = function() {
+ var result = '';
+ result += this.name();
+ result += ' (lines: ';
+ if (this.lineOffset() > 0) {
+ result += this.lineOffset();
+ result += '-';
+ result += this.lineOffset() + this.lineCount() - 1;
+ } else {
+ result += this.lineCount();
+ }
+ result += ')';
+ return result;
+};
+
+
+/**
+ * Mirror object for context.
+ * @param {Object} data The context data
+ * @constructor
+ * @extends Mirror
+ */
+function ContextMirror(data) {
+ %_CallFunction(this, CONTEXT_TYPE, Mirror);
+ this.data_ = data;
+ this.allocateHandle_();
+}
+inherits(ContextMirror, Mirror);
+
+
+ContextMirror.prototype.data = function() {
+ return this.data_;
+};
+
+
+/**
+ * Returns a mirror serializer
+ *
+ * @param {boolean} details Set to true to include details
+ * @param {Object} options Options comtrolling the serialization
+ * The following options can be set:
+ * includeSource: include ths full source of scripts
+ * @returns {MirrorSerializer} mirror serializer
+ */
+function MakeMirrorSerializer(details, options) {
+ return new JSONProtocolSerializer(details, options);
+}
+
+
+/**
+ * Object for serializing a mirror objects and its direct references.
+ * @param {boolean} details Indicates whether to include details for the mirror
+ * serialized
+ * @constructor
+ */
+function JSONProtocolSerializer(details, options) {
+ this.details_ = details;
+ this.options_ = options;
+ this.mirrors_ = [ ];
+}
+
+
+/**
+ * Returns a serialization of an object reference. The referenced object are
+ * added to the serialization state.
+ *
+ * @param {Mirror} mirror The mirror to serialize
+ * @returns {String} JSON serialization
+ */
+JSONProtocolSerializer.prototype.serializeReference = function(mirror) {
+ return this.serialize_(mirror, true, true);
+};
+
+
+/**
+ * Returns a serialization of an object value. The referenced objects are
+ * added to the serialization state.
+ *
+ * @param {Mirror} mirror The mirror to serialize
+ * @returns {String} JSON serialization
+ */
+JSONProtocolSerializer.prototype.serializeValue = function(mirror) {
+ var json = this.serialize_(mirror, false, true);
+ return json;
+};
+
+
+/**
+ * Returns a serialization of all the objects referenced.
+ *
+ * @param {Mirror} mirror The mirror to serialize.
+ * @returns {Array.<Object>} Array of the referenced objects converted to
+ * protcol objects.
+ */
+JSONProtocolSerializer.prototype.serializeReferencedObjects = function() {
+ // Collect the protocol representation of the referenced objects in an array.
+ var content = [];
+
+ // Get the number of referenced objects.
+ var count = this.mirrors_.length;
+
+ for (var i = 0; i < count; i++) {
+ content.push(this.serialize_(this.mirrors_[i], false, false));
+ }
+
+ return content;
+};
+
+
+JSONProtocolSerializer.prototype.includeSource_ = function() {
+ return this.options_ && this.options_.includeSource;
+};
+
+
+JSONProtocolSerializer.prototype.inlineRefs_ = function() {
+ return this.options_ && this.options_.inlineRefs;
+};
+
+
+JSONProtocolSerializer.prototype.maxStringLength_ = function() {
+ if (IS_UNDEFINED(this.options_) ||
+ IS_UNDEFINED(this.options_.maxStringLength)) {
+ return kMaxProtocolStringLength;
+ }
+ return this.options_.maxStringLength;
+};
+
+
+JSONProtocolSerializer.prototype.add_ = function(mirror) {
+ // If this mirror is already in the list just return.
+ for (var i = 0; i < this.mirrors_.length; i++) {
+ if (this.mirrors_[i] === mirror) {
+ return;
+ }
+ }
+
+ // Add the mirror to the list of mirrors to be serialized.
+ this.mirrors_.push(mirror);
+};
+
+
+/**
+ * Formats mirror object to protocol reference object with some data that can
+ * be used to display the value in debugger.
+ * @param {Mirror} mirror Mirror to serialize.
+ * @return {Object} Protocol reference object.
+ */
+JSONProtocolSerializer.prototype.serializeReferenceWithDisplayData_ =
+ function(mirror) {
+ var o = {};
+ o.ref = mirror.handle();
+ o.type = mirror.type();
+ switch (mirror.type()) {
+ case UNDEFINED_TYPE:
+ case NULL_TYPE:
+ case BOOLEAN_TYPE:
+ case NUMBER_TYPE:
+ o.value = mirror.value();
+ break;
+ case STRING_TYPE:
+ o.value = mirror.getTruncatedValue(this.maxStringLength_());
+ break;
+ case SYMBOL_TYPE:
+ o.description = mirror.description();
+ break;
+ case FUNCTION_TYPE:
+ o.name = mirror.name();
+ o.inferredName = mirror.inferredName();
+ if (mirror.script()) {
+ o.scriptId = mirror.script().id();
+ }
+ break;
+ case ERROR_TYPE:
+ case REGEXP_TYPE:
+ o.value = mirror.toText();
+ break;
+ case OBJECT_TYPE:
+ o.className = mirror.className();
+ break;
+ }
+ return o;
+};
+
+
+JSONProtocolSerializer.prototype.serialize_ = function(mirror, reference,
+ details) {
+ // If serializing a reference to a mirror just return the reference and add
+ // the mirror to the referenced mirrors.
+ if (reference &&
+ (mirror.isValue() || mirror.isScript() || mirror.isContext())) {
+ if (this.inlineRefs_() && mirror.isValue()) {
+ return this.serializeReferenceWithDisplayData_(mirror);
+ } else {
+ this.add_(mirror);
+ return {'ref' : mirror.handle()};
+ }
+ }
+
+ // Collect the JSON property/value pairs.
+ var content = {};
+
+ // Add the mirror handle.
+ if (mirror.isValue() || mirror.isScript() || mirror.isContext()) {
+ content.handle = mirror.handle();
+ }
+
+ // Always add the type.
+ content.type = mirror.type();
+
+ switch (mirror.type()) {
+ case UNDEFINED_TYPE:
+ case NULL_TYPE:
+ // Undefined and null are represented just by their type.
+ break;
+
+ case BOOLEAN_TYPE:
+ // Boolean values are simply represented by their value.
+ content.value = mirror.value();
+ break;
+
+ case NUMBER_TYPE:
+ // Number values are simply represented by their value.
+ content.value = NumberToJSON_(mirror.value());
+ break;
+
+ case STRING_TYPE:
+ // String values might have their value cropped to keep down size.
+ if (this.maxStringLength_() != -1 &&
+ mirror.length() > this.maxStringLength_()) {
+ var str = mirror.getTruncatedValue(this.maxStringLength_());
+ content.value = str;
+ content.fromIndex = 0;
+ content.toIndex = this.maxStringLength_();
+ } else {
+ content.value = mirror.value();
+ }
+ content.length = mirror.length();
+ break;
+
+ case SYMBOL_TYPE:
+ content.description = mirror.description();
+ break;
+
+ case OBJECT_TYPE:
+ case FUNCTION_TYPE:
+ case ERROR_TYPE:
+ case REGEXP_TYPE:
+ case PROMISE_TYPE:
+ case GENERATOR_TYPE:
+ // Add object representation.
+ this.serializeObject_(mirror, content, details);
+ break;
+
+ case PROPERTY_TYPE:
+ case INTERNAL_PROPERTY_TYPE:
+ throw new Error('PropertyMirror cannot be serialized independently');
+ break;
+
+ case FRAME_TYPE:
+ // Add object representation.
+ this.serializeFrame_(mirror, content);
+ break;
+
+ case SCOPE_TYPE:
+ // Add object representation.
+ this.serializeScope_(mirror, content);
+ break;
+
+ case SCRIPT_TYPE:
+ // Script is represented by id, name and source attributes.
+ if (mirror.name()) {
+ content.name = mirror.name();
+ }
+ content.id = mirror.id();
+ content.lineOffset = mirror.lineOffset();
+ content.columnOffset = mirror.columnOffset();
+ content.lineCount = mirror.lineCount();
+ if (mirror.data()) {
+ content.data = mirror.data();
+ }
+ if (this.includeSource_()) {
+ content.source = mirror.source();
+ } else {
+ var sourceStart = mirror.source().substring(0, 80);
+ content.sourceStart = sourceStart;
+ }
+ content.sourceLength = mirror.source().length;
+ content.scriptType = mirror.scriptType();
+ content.compilationType = mirror.compilationType();
+ // For compilation type eval emit information on the script from which
+ // eval was called if a script is present.
+ if (mirror.compilationType() == 1 &&
+ mirror.evalFromScript()) {
+ content.evalFromScript =
+ this.serializeReference(mirror.evalFromScript());
+ var evalFromLocation = mirror.evalFromLocation();
+ if (evalFromLocation) {
+ content.evalFromLocation = { line: evalFromLocation.line,
+ column: evalFromLocation.column };
+ }
+ if (mirror.evalFromFunctionName()) {
+ content.evalFromFunctionName = mirror.evalFromFunctionName();
+ }
+ }
+ if (mirror.context()) {
+ content.context = this.serializeReference(mirror.context());
+ }
+ break;
+
+ case CONTEXT_TYPE:
+ content.data = mirror.data();
+ break;
+ }
+
+ // Always add the text representation.
+ content.text = mirror.toText();
+
+ // Create and return the JSON string.
+ return content;
+};
+
+
+/**
+ * Serialize object information to the following JSON format.
+ *
+ * {"className":"<class name>",
+ * "constructorFunction":{"ref":<number>},
+ * "protoObject":{"ref":<number>},
+ * "prototypeObject":{"ref":<number>},
+ * "namedInterceptor":<boolean>,
+ * "indexedInterceptor":<boolean>,
+ * "properties":[<properties>],
+ * "internalProperties":[<internal properties>]}
+ */
+JSONProtocolSerializer.prototype.serializeObject_ = function(mirror, content,
+ details) {
+ // Add general object properties.
+ content.className = mirror.className();
+ content.constructorFunction =
+ this.serializeReference(mirror.constructorFunction());
+ content.protoObject = this.serializeReference(mirror.protoObject());
+ content.prototypeObject = this.serializeReference(mirror.prototypeObject());
+
+ // Add flags to indicate whether there are interceptors.
+ if (mirror.hasNamedInterceptor()) {
+ content.namedInterceptor = true;
+ }
+ if (mirror.hasIndexedInterceptor()) {
+ content.indexedInterceptor = true;
+ }
+
+ if (mirror.isFunction()) {
+ // Add function specific properties.
+ content.name = mirror.name();
+ if (!IS_UNDEFINED(mirror.inferredName())) {
+ content.inferredName = mirror.inferredName();
+ }
+ content.resolved = mirror.resolved();
+ if (mirror.resolved()) {
+ content.source = mirror.source();
+ }
+ if (mirror.script()) {
+ content.script = this.serializeReference(mirror.script());
+ content.scriptId = mirror.script().id();
+
+ serializeLocationFields(mirror.sourceLocation(), content);
+ }
+
+ content.scopes = [];
+ for (var i = 0; i < mirror.scopeCount(); i++) {
+ var scope = mirror.scope(i);
+ content.scopes.push({
+ type: scope.scopeType(),
+ index: i
+ });
+ }
+ }
+
+ if (mirror.isGenerator()) {
+ // Add generator specific properties.
+
+ // Either 'running', 'closed', or 'suspended'.
+ content.status = mirror.status();
+
+ content.func = this.serializeReference(mirror.func())
+ content.receiver = this.serializeReference(mirror.receiver())
+
+ // If the generator is suspended, the content add line/column properties.
+ serializeLocationFields(mirror.sourceLocation(), content);
+
+ // TODO(wingo): Also serialize a reference to the context (scope chain).
+ }
+
+ if (mirror.isDate()) {
+ // Add date specific properties.
+ content.value = mirror.value();
+ }
+
+ if (mirror.isPromise()) {
+ // Add promise specific properties.
+ content.status = mirror.status();
+ content.promiseValue = this.serializeReference(mirror.promiseValue());
+ }
+
+ // Add actual properties - named properties followed by indexed properties.
+ var propertyNames = mirror.propertyNames(PropertyKind.Named);
+ var propertyIndexes = mirror.propertyNames(PropertyKind.Indexed);
+ var p = new Array(propertyNames.length + propertyIndexes.length);
+ for (var i = 0; i < propertyNames.length; i++) {
+ var propertyMirror = mirror.property(propertyNames[i]);
+ p[i] = this.serializeProperty_(propertyMirror);
+ if (details) {
+ this.add_(propertyMirror.value());
+ }
+ }
+ for (var i = 0; i < propertyIndexes.length; i++) {
+ var propertyMirror = mirror.property(propertyIndexes[i]);
+ p[propertyNames.length + i] = this.serializeProperty_(propertyMirror);
+ if (details) {
+ this.add_(propertyMirror.value());
+ }
+ }
+ content.properties = p;
+
+ var internalProperties = mirror.internalProperties();
+ if (internalProperties.length > 0) {
+ var ip = [];
+ for (var i = 0; i < internalProperties.length; i++) {
+ ip.push(this.serializeInternalProperty_(internalProperties[i]));
+ }
+ content.internalProperties = ip;
+ }
+};
+
+
+/**
+ * Serialize location information to the following JSON format:
+ *
+ * "position":"<position>",
+ * "line":"<line>",
+ * "column":"<column>",
+ *
+ * @param {SourceLocation} location The location to serialize, may be undefined.
+ */
+function serializeLocationFields (location, content) {
+ if (!location) {
+ return;
+ }
+ content.position = location.position;
+ var line = location.line;
+ if (!IS_UNDEFINED(line)) {
+ content.line = line;
+ }
+ var column = location.column;
+ if (!IS_UNDEFINED(column)) {
+ content.column = column;
+ }
+}
+
+
+/**
+ * Serialize property information to the following JSON format for building the
+ * array of properties.
+ *
+ * {"name":"<property name>",
+ * "attributes":<number>,
+ * "propertyType":<number>,
+ * "ref":<number>}
+ *
+ * If the attribute for the property is PropertyAttribute.None it is not added.
+ * Here are a couple of examples.
+ *
+ * {"name":"hello","propertyType":0,"ref":1}
+ * {"name":"length","attributes":7,"propertyType":3,"ref":2}
+ *
+ * @param {PropertyMirror} propertyMirror The property to serialize.
+ * @returns {Object} Protocol object representing the property.
+ */
+JSONProtocolSerializer.prototype.serializeProperty_ = function(propertyMirror) {
+ var result = {};
+
+ result.name = propertyMirror.name();
+ var propertyValue = propertyMirror.value();
+ if (this.inlineRefs_() && propertyValue.isValue()) {
+ result.value = this.serializeReferenceWithDisplayData_(propertyValue);
+ } else {
+ if (propertyMirror.attributes() != PropertyAttribute.None) {
+ result.attributes = propertyMirror.attributes();
+ }
+ result.propertyType = propertyMirror.propertyType();
+ result.ref = propertyValue.handle();
+ }
+ return result;
+};
+
+
+/**
+ * Serialize internal property information to the following JSON format for
+ * building the array of properties.
+ *
+ * {"name":"<property name>",
+ * "ref":<number>}
+ *
+ * {"name":"[[BoundThis]]","ref":117}
+ *
+ * @param {InternalPropertyMirror} propertyMirror The property to serialize.
+ * @returns {Object} Protocol object representing the property.
+ */
+JSONProtocolSerializer.prototype.serializeInternalProperty_ =
+ function(propertyMirror) {
+ var result = {};
+
+ result.name = propertyMirror.name();
+ var propertyValue = propertyMirror.value();
+ if (this.inlineRefs_() && propertyValue.isValue()) {
+ result.value = this.serializeReferenceWithDisplayData_(propertyValue);
+ } else {
+ result.ref = propertyValue.handle();
+ }
+ return result;
+};
+
+
+JSONProtocolSerializer.prototype.serializeFrame_ = function(mirror, content) {
+ content.index = mirror.index();
+ content.receiver = this.serializeReference(mirror.receiver());
+ var func = mirror.func();
+ content.func = this.serializeReference(func);
+ var script = func.script();
+ if (script) {
+ content.script = this.serializeReference(script);
+ }
+ content.constructCall = mirror.isConstructCall();
+ content.atReturn = mirror.isAtReturn();
+ if (mirror.isAtReturn()) {
+ content.returnValue = this.serializeReference(mirror.returnValue());
+ }
+ content.debuggerFrame = mirror.isDebuggerFrame();
+ var x = new Array(mirror.argumentCount());
+ for (var i = 0; i < mirror.argumentCount(); i++) {
+ var arg = {};
+ var argument_name = mirror.argumentName(i);
+ if (argument_name) {
+ arg.name = argument_name;
+ }
+ arg.value = this.serializeReference(mirror.argumentValue(i));
+ x[i] = arg;
+ }
+ content.arguments = x;
+ var x = new Array(mirror.localCount());
+ for (var i = 0; i < mirror.localCount(); i++) {
+ var local = {};
+ local.name = mirror.localName(i);
+ local.value = this.serializeReference(mirror.localValue(i));
+ x[i] = local;
+ }
+ content.locals = x;
+ serializeLocationFields(mirror.sourceLocation(), content);
+ var source_line_text = mirror.sourceLineText();
+ if (!IS_UNDEFINED(source_line_text)) {
+ content.sourceLineText = source_line_text;
+ }
+
+ content.scopes = [];
+ for (var i = 0; i < mirror.scopeCount(); i++) {
+ var scope = mirror.scope(i);
+ content.scopes.push({
+ type: scope.scopeType(),
+ index: i
+ });
+ }
+};
+
+
+JSONProtocolSerializer.prototype.serializeScope_ = function(mirror, content) {
+ content.index = mirror.scopeIndex();
+ content.frameIndex = mirror.frameIndex();
+ content.type = mirror.scopeType();
+ content.object = this.inlineRefs_() ?
+ this.serializeValue(mirror.scopeObject()) :
+ this.serializeReference(mirror.scopeObject());
+};
+
+
+/**
+ * Convert a number to a protocol value. For all finite numbers the number
+ * itself is returned. For non finite numbers NaN, Infinite and
+ * -Infinite the string representation "NaN", "Infinite" or "-Infinite"
+ * (not including the quotes) is returned.
+ *
+ * @param {number} value The number value to convert to a protocol value.
+ * @returns {number|string} Protocol value.
+ */
+function NumberToJSON_(value) {
+ if (isNaN(value)) {
+ return 'NaN';
+ }
+ if (!NUMBER_IS_FINITE(value)) {
+ if (value > 0) {
+ return 'Infinity';
+ } else {
+ return '-Infinity';
+ }
+ }
+ return value;
+}
--- /dev/null
+jyan@ca.ibm.com
+dstence@us.ibm.com
+joransiu@ca.ibm.com
+mbrandy@us.ibm.com
+michael_dawson@ca.ibm.com
--- /dev/null
+// 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"
+
+#if V8_TARGET_ARCH_PPC
+
+#include "src/codegen.h"
+#include "src/debug/debug.h"
+
+namespace v8 {
+namespace internal {
+
+#define __ ACCESS_MASM(masm)
+
+
+void EmitDebugBreakSlot(MacroAssembler* masm) {
+ Label check_size;
+ __ bind(&check_size);
+ for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
+ __ nop(MacroAssembler::DEBUG_BREAK_NOP);
+ }
+ DCHECK_EQ(Assembler::kDebugBreakSlotInstructions,
+ masm->InstructionsGeneratedSince(&check_size));
+}
+
+
+void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode,
+ int call_argc) {
+ // Generate enough nop's to make space for a call instruction. Avoid emitting
+ // the trampoline pool in the debug break slot code.
+ Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm);
+ masm->RecordDebugBreakSlot(mode, call_argc);
+ EmitDebugBreakSlot(masm);
+}
+
+
+void DebugCodegen::ClearDebugBreakSlot(Address pc) {
+ CodePatcher patcher(pc, Assembler::kDebugBreakSlotInstructions);
+ EmitDebugBreakSlot(patcher.masm());
+}
+
+
+void DebugCodegen::PatchDebugBreakSlot(Address pc, Handle<Code> code) {
+ DCHECK_EQ(Code::BUILTIN, code->kind());
+ CodePatcher patcher(pc, Assembler::kDebugBreakSlotInstructions);
+ // Patch the code changing the debug break slot code from
+ //
+ // ori r3, r3, 0
+ // ori r3, r3, 0
+ // ori r3, r3, 0
+ // ori r3, r3, 0
+ // ori r3, r3, 0
+ //
+ // to a call to the debug break code, using a FIXED_SEQUENCE.
+ //
+ // mov r0, <address>
+ // mtlr r0
+ // blrl
+ //
+ Assembler::BlockTrampolinePoolScope block_trampoline_pool(patcher.masm());
+ patcher.masm()->mov(v8::internal::r0,
+ Operand(reinterpret_cast<intptr_t>(code->entry())));
+ patcher.masm()->mtctr(v8::internal::r0);
+ patcher.masm()->bctrl();
+}
+
+
+void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
+ DebugBreakCallHelperMode mode) {
+ __ RecordComment("Debug break");
+ {
+ FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
+
+ // Load padding words on stack.
+ __ LoadSmiLiteral(ip, Smi::FromInt(LiveEdit::kFramePaddingValue));
+ for (int i = 0; i < LiveEdit::kFramePaddingInitialSize; i++) {
+ __ push(ip);
+ }
+ __ LoadSmiLiteral(ip, Smi::FromInt(LiveEdit::kFramePaddingInitialSize));
+ __ push(ip);
+
+ if (mode == SAVE_RESULT_REGISTER) __ push(r3);
+
+ __ mov(r3, Operand::Zero()); // no arguments
+ __ mov(r4,
+ Operand(ExternalReference(
+ Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate())));
+
+ CEntryStub ceb(masm->isolate(), 1);
+ __ CallStub(&ceb);
+
+ if (FLAG_debug_code) {
+ for (int i = 0; i < kNumJSCallerSaved; i++) {
+ Register reg = {JSCallerSavedCode(i)};
+ __ mov(reg, Operand(kDebugZapValue));
+ }
+ }
+
+ if (mode == SAVE_RESULT_REGISTER) __ pop(r3);
+
+ // Don't bother removing padding bytes pushed on the stack
+ // as the frame is going to be restored right away.
+
+ // Leave the internal frame.
+ }
+
+ // Now that the break point has been handled, resume normal execution by
+ // jumping to the target address intended by the caller and that was
+ // overwritten by the address of DebugBreakXXX.
+ ExternalReference after_break_target =
+ ExternalReference::debug_after_break_target_address(masm->isolate());
+ __ mov(ip, Operand(after_break_target));
+ __ LoadP(ip, MemOperand(ip));
+ __ JumpToJSEntry(ip);
+}
+
+
+void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
+ __ Ret();
+}
+
+
+void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
+ ExternalReference restarter_frame_function_slot =
+ ExternalReference::debug_restarter_frame_function_pointer_address(
+ masm->isolate());
+ __ mov(ip, Operand(restarter_frame_function_slot));
+ __ li(r4, Operand::Zero());
+ __ StoreP(r4, MemOperand(ip, 0));
+
+ // Load the function pointer off of our current stack frame.
+ __ LoadP(r4, MemOperand(fp, StandardFrameConstants::kConstantPoolOffset -
+ kPointerSize));
+
+ // Pop return address and frame
+ __ LeaveFrame(StackFrame::INTERNAL);
+
+ // Load context from the function.
+ __ LoadP(cp, FieldMemOperand(r4, JSFunction::kContextOffset));
+
+ // Get function code.
+ __ LoadP(ip, FieldMemOperand(r4, JSFunction::kSharedFunctionInfoOffset));
+ __ LoadP(ip, FieldMemOperand(ip, SharedFunctionInfo::kCodeOffset));
+ __ addi(ip, ip, Operand(Code::kHeaderSize - kHeapObjectTag));
+
+ // Re-run JSFunction, r4 is function, cp is context.
+ __ Jump(ip);
+}
+
+
+const bool LiveEdit::kFrameDropperSupported = true;
+
+#undef __
+} // namespace internal
+} // namespace v8
+
+#endif // V8_TARGET_ARCH_PPC
--- /dev/null
+// Copyright 2012 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"
+
+#if V8_TARGET_ARCH_X64
+
+#include "src/assembler.h"
+#include "src/codegen.h"
+#include "src/debug/debug.h"
+
+
+namespace v8 {
+namespace internal {
+
+#define __ ACCESS_MASM(masm)
+
+
+void EmitDebugBreakSlot(MacroAssembler* masm) {
+ Label check_codesize;
+ __ bind(&check_codesize);
+ __ Nop(Assembler::kDebugBreakSlotLength);
+ DCHECK_EQ(Assembler::kDebugBreakSlotLength,
+ masm->SizeOfCodeGeneratedSince(&check_codesize));
+}
+
+
+void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode,
+ int call_argc) {
+ // Generate enough nop's to make space for a call instruction.
+ masm->RecordDebugBreakSlot(mode, call_argc);
+ EmitDebugBreakSlot(masm);
+}
+
+
+void DebugCodegen::ClearDebugBreakSlot(Address pc) {
+ CodePatcher patcher(pc, Assembler::kDebugBreakSlotLength);
+ EmitDebugBreakSlot(patcher.masm());
+}
+
+
+void DebugCodegen::PatchDebugBreakSlot(Address pc, Handle<Code> code) {
+ DCHECK_EQ(Code::BUILTIN, code->kind());
+ static const int kSize = Assembler::kDebugBreakSlotLength;
+ CodePatcher patcher(pc, kSize);
+ Label check_codesize;
+ patcher.masm()->bind(&check_codesize);
+ patcher.masm()->movp(kScratchRegister, reinterpret_cast<void*>(code->entry()),
+ Assembler::RelocInfoNone());
+ patcher.masm()->call(kScratchRegister);
+ // Check that the size of the code generated is as expected.
+ DCHECK_EQ(kSize, patcher.masm()->SizeOfCodeGeneratedSince(&check_codesize));
+}
+
+
+void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
+ DebugBreakCallHelperMode mode) {
+ __ RecordComment("Debug break");
+
+ // Enter an internal frame.
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // Load padding words on stack.
+ for (int i = 0; i < LiveEdit::kFramePaddingInitialSize; i++) {
+ __ Push(Smi::FromInt(LiveEdit::kFramePaddingValue));
+ }
+ __ Push(Smi::FromInt(LiveEdit::kFramePaddingInitialSize));
+
+ if (mode == SAVE_RESULT_REGISTER) __ Push(rax);
+
+ __ Set(rax, 0); // No arguments (argc == 0).
+ __ Move(rbx, ExternalReference(Runtime::FunctionForId(Runtime::kDebugBreak),
+ masm->isolate()));
+
+ CEntryStub ceb(masm->isolate(), 1);
+ __ CallStub(&ceb);
+
+ if (FLAG_debug_code) {
+ for (int i = 0; i < kNumJSCallerSaved; ++i) {
+ Register reg = {JSCallerSavedCode(i)};
+ __ Set(reg, kDebugZapValue);
+ }
+ }
+
+ if (mode == SAVE_RESULT_REGISTER) __ Pop(rax);
+
+ // Read current padding counter and skip corresponding number of words.
+ __ Pop(kScratchRegister);
+ __ SmiToInteger32(kScratchRegister, kScratchRegister);
+ __ leap(rsp, Operand(rsp, kScratchRegister, times_pointer_size, 0));
+
+ // Get rid of the internal frame.
+ }
+
+ // This call did not replace a call , so there will be an unwanted
+ // return address left on the stack. Here we get rid of that.
+ __ addp(rsp, Immediate(kPCOnStackSize));
+
+ // Now that the break point has been handled, resume normal execution by
+ // jumping to the target address intended by the caller and that was
+ // overwritten by the address of DebugBreakXXX.
+ ExternalReference after_break_target =
+ ExternalReference::debug_after_break_target_address(masm->isolate());
+ __ Move(kScratchRegister, after_break_target);
+ __ Jump(Operand(kScratchRegister, 0));
+}
+
+
+void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
+ masm->ret(0);
+}
+
+
+void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
+ ExternalReference restarter_frame_function_slot =
+ ExternalReference::debug_restarter_frame_function_pointer_address(
+ masm->isolate());
+ __ Move(rax, restarter_frame_function_slot);
+ __ movp(Operand(rax, 0), Immediate(0));
+
+ // We do not know our frame height, but set rsp based on rbp.
+ __ leap(rsp, Operand(rbp, -1 * kPointerSize));
+
+ __ Pop(rdi); // Function.
+ __ popq(rbp);
+
+ // Load context from the function.
+ __ movp(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
+
+ // Get function code.
+ __ movp(rdx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
+ __ movp(rdx, FieldOperand(rdx, SharedFunctionInfo::kCodeOffset));
+ __ leap(rdx, FieldOperand(rdx, Code::kHeaderSize));
+
+ // Re-run JSFunction, rdi is function, rsi is context.
+ __ jmp(rdx);
+}
+
+const bool LiveEdit::kFrameDropperSupported = true;
+
+#undef __
+
+} // namespace internal
+} // namespace v8
+
+#endif // V8_TARGET_ARCH_X64
--- /dev/null
+weiliang.lin@intel.com
--- /dev/null
+// Copyright 2012 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"
+
+#if V8_TARGET_ARCH_X87
+
+#include "src/codegen.h"
+#include "src/debug/debug.h"
+
+
+namespace v8 {
+namespace internal {
+
+#define __ ACCESS_MASM(masm)
+
+
+void EmitDebugBreakSlot(MacroAssembler* masm) {
+ Label check_codesize;
+ __ bind(&check_codesize);
+ __ Nop(Assembler::kDebugBreakSlotLength);
+ DCHECK_EQ(Assembler::kDebugBreakSlotLength,
+ masm->SizeOfCodeGeneratedSince(&check_codesize));
+}
+
+
+void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode,
+ int call_argc) {
+ // Generate enough nop's to make space for a call instruction.
+ masm->RecordDebugBreakSlot(mode, call_argc);
+ EmitDebugBreakSlot(masm);
+}
+
+
+void DebugCodegen::ClearDebugBreakSlot(Address pc) {
+ CodePatcher patcher(pc, Assembler::kDebugBreakSlotLength);
+ EmitDebugBreakSlot(patcher.masm());
+}
+
+
+void DebugCodegen::PatchDebugBreakSlot(Address pc, Handle<Code> code) {
+ DCHECK_EQ(Code::BUILTIN, code->kind());
+ static const int kSize = Assembler::kDebugBreakSlotLength;
+ CodePatcher patcher(pc, kSize);
+
+ // Add a label for checking the size of the code used for returning.
+ Label check_codesize;
+ patcher.masm()->bind(&check_codesize);
+ patcher.masm()->call(code->entry(), RelocInfo::NONE32);
+ // Check that the size of the code generated is as expected.
+ DCHECK_EQ(kSize, patcher.masm()->SizeOfCodeGeneratedSince(&check_codesize));
+}
+
+
+void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
+ DebugBreakCallHelperMode mode) {
+ __ RecordComment("Debug break");
+
+ // Enter an internal frame.
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // Load padding words on stack.
+ for (int i = 0; i < LiveEdit::kFramePaddingInitialSize; i++) {
+ __ push(Immediate(Smi::FromInt(LiveEdit::kFramePaddingValue)));
+ }
+ __ push(Immediate(Smi::FromInt(LiveEdit::kFramePaddingInitialSize)));
+
+ if (mode == SAVE_RESULT_REGISTER) __ push(eax);
+
+ __ Move(eax, Immediate(0)); // No arguments.
+ __ mov(ebx,
+ Immediate(ExternalReference(
+ Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate())));
+
+ CEntryStub ceb(masm->isolate(), 1);
+ __ CallStub(&ceb);
+
+ if (FLAG_debug_code) {
+ for (int i = 0; i < kNumJSCallerSaved; ++i) {
+ Register reg = {JSCallerSavedCode(i)};
+ __ Move(reg, Immediate(kDebugZapValue));
+ }
+ }
+
+ if (mode == SAVE_RESULT_REGISTER) __ pop(eax);
+
+ __ pop(ebx);
+ // We divide stored value by 2 (untagging) and multiply it by word's size.
+ STATIC_ASSERT(kSmiTagSize == 1 && kSmiShiftSize == 0);
+ __ lea(esp, Operand(esp, ebx, times_half_pointer_size, 0));
+
+ // Get rid of the internal frame.
+ }
+
+ // This call did not replace a call , so there will be an unwanted
+ // return address left on the stack. Here we get rid of that.
+ __ add(esp, Immediate(kPointerSize));
+
+ // Now that the break point has been handled, resume normal execution by
+ // jumping to the target address intended by the caller and that was
+ // overwritten by the address of DebugBreakXXX.
+ ExternalReference after_break_target =
+ ExternalReference::debug_after_break_target_address(masm->isolate());
+ __ jmp(Operand::StaticVariable(after_break_target));
+}
+
+
+void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
+ masm->ret(0);
+}
+
+
+void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
+ ExternalReference restarter_frame_function_slot =
+ ExternalReference::debug_restarter_frame_function_pointer_address(
+ masm->isolate());
+ __ mov(Operand::StaticVariable(restarter_frame_function_slot), Immediate(0));
+
+ // We do not know our frame height, but set esp based on ebp.
+ __ lea(esp, Operand(ebp, -1 * kPointerSize));
+
+ __ pop(edi); // Function.
+ __ pop(ebp);
+
+ // Load context from the function.
+ __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
+
+ // Get function code.
+ __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
+ __ mov(edx, FieldOperand(edx, SharedFunctionInfo::kCodeOffset));
+ __ lea(edx, FieldOperand(edx, Code::kHeaderSize));
+
+ // Re-run JSFunction, edi is function, esi is context.
+ __ jmp(edx);
+}
+
+
+const bool LiveEdit::kFrameDropperSupported = true;
+
+#undef __
+
+} // namespace internal
+} // namespace v8
+
+#endif // V8_TARGET_ARCH_X87
#include "src/code-stubs.h"
#include "src/codegen.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/deoptimizer.h"
#include "src/disasm.h"
#include "src/disassembler.h"
#include "src/code-stubs.h"
#include "src/codegen.h"
#include "src/compiler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/full-codegen/full-codegen.h"
#include "src/ic/ic.h"
#include "src/parser.h"
#include "src/code-stubs.h"
#include "src/codegen.h"
#include "src/compiler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/full-codegen/full-codegen.h"
#include "src/ic/ic.h"
#include "src/parser.h"
#include "src/code-factory.h"
#include "src/codegen.h"
#include "src/compiler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
+#include "src/debug/liveedit.h"
#include "src/full-codegen/full-codegen.h"
-#include "src/liveedit.h"
#include "src/macro-assembler.h"
#include "src/prettyprinter.h"
#include "src/scopeinfo.h"
#include "src/code-stubs.h"
#include "src/codegen.h"
#include "src/compiler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/full-codegen/full-codegen.h"
#include "src/ic/ic.h"
#include "src/parser.h"
#include "src/code-stubs.h"
#include "src/codegen.h"
#include "src/compiler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/full-codegen/full-codegen.h"
#include "src/ic/ic.h"
#include "src/parser.h"
#include "src/code-stubs.h"
#include "src/codegen.h"
#include "src/compiler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/full-codegen/full-codegen.h"
#include "src/ic/ic.h"
#include "src/parser.h"
#include "src/code-stubs.h"
#include "src/codegen.h"
#include "src/compiler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/full-codegen/full-codegen.h"
#include "src/ic/ic.h"
#include "src/parser.h"
#include "src/code-stubs.h"
#include "src/codegen.h"
#include "src/compiler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/full-codegen/full-codegen.h"
#include "src/ic/ic.h"
#include "src/parser.h"
#include "src/code-stubs.h"
#include "src/codegen.h"
#include "src/compiler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/full-codegen/full-codegen.h"
#include "src/ic/ic.h"
#include "src/parser.h"
#include "src/allocation-tracker.h"
#include "src/code-stubs.h"
#include "src/conversions.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/heap-profiler.h"
#include "src/types.h"
#include "src/compilation-cache.h"
#include "src/conversions.h"
#include "src/cpu-profiler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/deoptimizer.h"
#include "src/global-handles.h"
#include "src/heap/gc-idle-time-handler.h"
#include "src/ia32/assembler-ia32.h"
#include "src/assembler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
namespace v8 {
namespace internal {
+++ /dev/null
-// Copyright 2012 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"
-
-#if V8_TARGET_ARCH_IA32
-
-#include "src/codegen.h"
-#include "src/debug.h"
-
-
-namespace v8 {
-namespace internal {
-
-#define __ ACCESS_MASM(masm)
-
-
-void EmitDebugBreakSlot(MacroAssembler* masm) {
- Label check_codesize;
- __ bind(&check_codesize);
- __ Nop(Assembler::kDebugBreakSlotLength);
- DCHECK_EQ(Assembler::kDebugBreakSlotLength,
- masm->SizeOfCodeGeneratedSince(&check_codesize));
-}
-
-
-void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode,
- int call_argc) {
- // Generate enough nop's to make space for a call instruction.
- masm->RecordDebugBreakSlot(mode, call_argc);
- EmitDebugBreakSlot(masm);
-}
-
-
-void DebugCodegen::ClearDebugBreakSlot(Address pc) {
- CodePatcher patcher(pc, Assembler::kDebugBreakSlotLength);
- EmitDebugBreakSlot(patcher.masm());
-}
-
-
-void DebugCodegen::PatchDebugBreakSlot(Address pc, Handle<Code> code) {
- DCHECK_EQ(Code::BUILTIN, code->kind());
- static const int kSize = Assembler::kDebugBreakSlotLength;
- CodePatcher patcher(pc, kSize);
-
- // Add a label for checking the size of the code used for returning.
- Label check_codesize;
- patcher.masm()->bind(&check_codesize);
- patcher.masm()->call(code->entry(), RelocInfo::NONE32);
- // Check that the size of the code generated is as expected.
- DCHECK_EQ(kSize, patcher.masm()->SizeOfCodeGeneratedSince(&check_codesize));
-}
-
-
-void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
- DebugBreakCallHelperMode mode) {
- __ RecordComment("Debug break");
-
- // Enter an internal frame.
- {
- FrameScope scope(masm, StackFrame::INTERNAL);
-
- // Load padding words on stack.
- for (int i = 0; i < LiveEdit::kFramePaddingInitialSize; i++) {
- __ push(Immediate(Smi::FromInt(LiveEdit::kFramePaddingValue)));
- }
- __ push(Immediate(Smi::FromInt(LiveEdit::kFramePaddingInitialSize)));
-
- if (mode == SAVE_RESULT_REGISTER) __ push(eax);
-
- __ Move(eax, Immediate(0)); // No arguments.
- __ mov(ebx,
- Immediate(ExternalReference(
- Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate())));
-
- CEntryStub ceb(masm->isolate(), 1);
- __ CallStub(&ceb);
-
- if (FLAG_debug_code) {
- for (int i = 0; i < kNumJSCallerSaved; ++i) {
- Register reg = {JSCallerSavedCode(i)};
- __ Move(reg, Immediate(kDebugZapValue));
- }
- }
-
- if (mode == SAVE_RESULT_REGISTER) __ pop(eax);
-
- __ pop(ebx);
- // We divide stored value by 2 (untagging) and multiply it by word's size.
- STATIC_ASSERT(kSmiTagSize == 1 && kSmiShiftSize == 0);
- __ lea(esp, Operand(esp, ebx, times_half_pointer_size, 0));
-
- // Get rid of the internal frame.
- }
-
- // This call did not replace a call , so there will be an unwanted
- // return address left on the stack. Here we get rid of that.
- __ add(esp, Immediate(kPointerSize));
-
- // Now that the break point has been handled, resume normal execution by
- // jumping to the target address intended by the caller and that was
- // overwritten by the address of DebugBreakXXX.
- ExternalReference after_break_target =
- ExternalReference::debug_after_break_target_address(masm->isolate());
- __ jmp(Operand::StaticVariable(after_break_target));
-}
-
-
-void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
- masm->ret(0);
-}
-
-
-void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
- ExternalReference restarter_frame_function_slot =
- ExternalReference::debug_restarter_frame_function_pointer_address(
- masm->isolate());
- __ mov(Operand::StaticVariable(restarter_frame_function_slot), Immediate(0));
-
- // We do not know our frame height, but set esp based on ebp.
- __ lea(esp, Operand(ebp, -1 * kPointerSize));
-
- __ pop(edi); // Function.
- __ pop(ebp);
-
- // Load context from the function.
- __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
-
- // Get function code.
- __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
- __ mov(edx, FieldOperand(edx, SharedFunctionInfo::kCodeOffset));
- __ lea(edx, FieldOperand(edx, Code::kHeaderSize));
-
- // Re-run JSFunction, edi is function, esi is context.
- __ jmp(edx);
-}
-
-
-const bool LiveEdit::kFrameDropperSupported = true;
-
-#undef __
-
-} // namespace internal
-} // namespace v8
-
-#endif // V8_TARGET_ARCH_IA32
#include "src/bootstrapper.h"
#include "src/codegen.h"
#include "src/cpu-profiler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/runtime/runtime.h"
namespace v8 {
#include "src/ic/ic.h"
#include "src/compiler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/macro-assembler.h"
#include "src/prototype.h"
#include "src/compilation-cache.h"
#include "src/compilation-statistics.h"
#include "src/cpu-profiler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/deoptimizer.h"
#include "src/heap/spaces.h"
#include "src/heap-profiler.h"
+++ /dev/null
-// Copyright 2012 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.
-
-// LiveEdit feature implementation. The script should be executed after
-// debug-debugger.js.
-
-// A LiveEdit namespace. It contains functions that modifies JavaScript code
-// according to changes of script source (if possible).
-//
-// When new script source is put in, the difference is calculated textually,
-// in form of list of delete/add/change chunks. The functions that include
-// change chunk(s) get recompiled, or their enclosing functions are
-// recompiled instead.
-// If the function may not be recompiled (e.g. it was completely erased in new
-// version of the script) it remains unchanged, but the code that could
-// create a new instance of this function goes away. An old version of script
-// is created to back up this obsolete function.
-// All unchanged functions have their positions updated accordingly.
-//
-// LiveEdit namespace is declared inside a single function constructor.
-
-"use strict";
-
-Debug.LiveEdit = new function() {
-
- // Forward declaration for minifier.
- var FunctionStatus;
-
- // Applies the change to the script.
- // The change is in form of list of chunks encoded in a single array as
- // a series of triplets (pos1_start, pos1_end, pos2_end)
- function ApplyPatchMultiChunk(script, diff_array, new_source, preview_only,
- change_log) {
-
- var old_source = script.source;
-
- // Gather compile information about old version of script.
- var old_compile_info = GatherCompileInfo(old_source, script);
-
- // Build tree structures for old and new versions of the script.
- var root_old_node = BuildCodeInfoTree(old_compile_info);
-
- var pos_translator = new PosTranslator(diff_array);
-
- // Analyze changes.
- MarkChangedFunctions(root_old_node, pos_translator.GetChunks());
-
- // Find all SharedFunctionInfo's that were compiled from this script.
- FindLiveSharedInfos(root_old_node, script);
-
- // Gather compile information about new version of script.
- var new_compile_info;
- try {
- new_compile_info = GatherCompileInfo(new_source, script);
- } catch (e) {
- var failure =
- new Failure("Failed to compile new version of script: " + e);
- if (e instanceof SyntaxError) {
- var details = {
- type: "liveedit_compile_error",
- syntaxErrorMessage: e.message
- };
- CopyErrorPositionToDetails(e, details);
- failure.details = details;
- }
- throw failure;
- }
- var root_new_node = BuildCodeInfoTree(new_compile_info);
-
- // Link recompiled script data with other data.
- FindCorrespondingFunctions(root_old_node, root_new_node);
-
- // Prepare to-do lists.
- var replace_code_list = new Array();
- var link_to_old_script_list = new Array();
- var link_to_original_script_list = new Array();
- var update_positions_list = new Array();
-
- function HarvestTodo(old_node) {
- function CollectDamaged(node) {
- link_to_old_script_list.push(node);
- for (var i = 0; i < node.children.length; i++) {
- CollectDamaged(node.children[i]);
- }
- }
-
- // Recursively collects all newly compiled functions that are going into
- // business and should have link to the actual script updated.
- function CollectNew(node_list) {
- for (var i = 0; i < node_list.length; i++) {
- link_to_original_script_list.push(node_list[i]);
- CollectNew(node_list[i].children);
- }
- }
-
- if (old_node.status == FunctionStatus.DAMAGED) {
- CollectDamaged(old_node);
- return;
- }
- if (old_node.status == FunctionStatus.UNCHANGED) {
- update_positions_list.push(old_node);
- } else if (old_node.status == FunctionStatus.SOURCE_CHANGED) {
- update_positions_list.push(old_node);
- } else if (old_node.status == FunctionStatus.CHANGED) {
- replace_code_list.push(old_node);
- CollectNew(old_node.unmatched_new_nodes);
- }
- for (var i = 0; i < old_node.children.length; i++) {
- HarvestTodo(old_node.children[i]);
- }
- }
-
- var preview_description = {
- change_tree: DescribeChangeTree(root_old_node),
- textual_diff: {
- old_len: old_source.length,
- new_len: new_source.length,
- chunks: diff_array
- },
- updated: false
- };
-
- if (preview_only) {
- return preview_description;
- }
-
- HarvestTodo(root_old_node);
-
- // Collect shared infos for functions whose code need to be patched.
- var replaced_function_infos = new Array();
- for (var i = 0; i < replace_code_list.length; i++) {
- var live_shared_function_infos =
- replace_code_list[i].live_shared_function_infos;
-
- if (live_shared_function_infos) {
- for (var j = 0; j < live_shared_function_infos.length; j++) {
- replaced_function_infos.push(live_shared_function_infos[j]);
- }
- }
- }
-
- // We haven't changed anything before this line yet.
- // Committing all changes.
-
- // Check that function being patched is not currently on stack or drop them.
- var dropped_functions_number =
- CheckStackActivations(replaced_function_infos, change_log);
-
- // Our current implementation requires client to manually issue "step in"
- // command for correct stack state if the stack was modified.
- preview_description.stack_modified = dropped_functions_number != 0;
-
- // Start with breakpoints. Convert their line/column positions and
- // temporary remove.
- var break_points_restorer = TemporaryRemoveBreakPoints(script, change_log);
-
- var old_script;
-
- // Create an old script only if there are function that should be linked
- // to old version.
- if (link_to_old_script_list.length == 0) {
- %LiveEditReplaceScript(script, new_source, null);
- old_script = UNDEFINED;
- } else {
- var old_script_name = CreateNameForOldScript(script);
-
- // Update the script text and create a new script representing an old
- // version of the script.
- old_script = %LiveEditReplaceScript(script, new_source,
- old_script_name);
-
- var link_to_old_script_report = new Array();
- change_log.push( { linked_to_old_script: link_to_old_script_report } );
-
- // We need to link to old script all former nested functions.
- for (var i = 0; i < link_to_old_script_list.length; i++) {
- LinkToOldScript(link_to_old_script_list[i], old_script,
- link_to_old_script_report);
- }
-
- preview_description.created_script_name = old_script_name;
- }
-
- // Link to an actual script all the functions that we are going to use.
- for (var i = 0; i < link_to_original_script_list.length; i++) {
- %LiveEditFunctionSetScript(
- link_to_original_script_list[i].info.shared_function_info, script);
- }
-
- for (var i = 0; i < replace_code_list.length; i++) {
- PatchFunctionCode(replace_code_list[i], change_log);
- }
-
- var position_patch_report = new Array();
- change_log.push( {position_patched: position_patch_report} );
-
- for (var i = 0; i < update_positions_list.length; i++) {
- // TODO(LiveEdit): take into account whether it's source_changed or
- // unchanged and whether positions changed at all.
- PatchPositions(update_positions_list[i], diff_array,
- position_patch_report);
-
- if (update_positions_list[i].live_shared_function_infos) {
- update_positions_list[i].live_shared_function_infos.
- forEach(function (info) {
- %LiveEditFunctionSourceUpdated(info.raw_array);
- });
- }
- }
-
- break_points_restorer(pos_translator, old_script);
-
- preview_description.updated = true;
- return preview_description;
- }
- // Function is public.
- this.ApplyPatchMultiChunk = ApplyPatchMultiChunk;
-
-
- // Fully compiles source string as a script. Returns Array of
- // FunctionCompileInfo -- a descriptions of all functions of the script.
- // Elements of array are ordered by start positions of functions (from top
- // to bottom) in the source. Fields outer_index and next_sibling_index help
- // to navigate the nesting structure of functions.
- //
- // All functions get compiled linked to script provided as parameter script.
- // TODO(LiveEdit): consider not using actual scripts as script, because
- // we have to manually erase all links right after compile.
- function GatherCompileInfo(source, script) {
- // Get function info, elements are partially sorted (it is a tree of
- // nested functions serialized as parent followed by serialized children.
- var raw_compile_info = %LiveEditGatherCompileInfo(script, source);
-
- // Sort function infos by start position field.
- var compile_info = new Array();
- var old_index_map = new Array();
- for (var i = 0; i < raw_compile_info.length; i++) {
- var info = new FunctionCompileInfo(raw_compile_info[i]);
- // Remove all links to the actual script. Breakpoints system and
- // LiveEdit itself believe that any function in heap that points to a
- // particular script is a regular function.
- // For some functions we will restore this link later.
- %LiveEditFunctionSetScript(info.shared_function_info, UNDEFINED);
- compile_info.push(info);
- old_index_map.push(i);
- }
-
- for (var i = 0; i < compile_info.length; i++) {
- var k = i;
- for (var j = i + 1; j < compile_info.length; j++) {
- if (compile_info[k].start_position > compile_info[j].start_position) {
- k = j;
- }
- }
- if (k != i) {
- var temp_info = compile_info[k];
- var temp_index = old_index_map[k];
- compile_info[k] = compile_info[i];
- old_index_map[k] = old_index_map[i];
- compile_info[i] = temp_info;
- old_index_map[i] = temp_index;
- }
- }
-
- // After sorting update outer_index field using old_index_map. Also
- // set next_sibling_index field.
- var current_index = 0;
-
- // The recursive function, that goes over all children of a particular
- // node (i.e. function info).
- function ResetIndexes(new_parent_index, old_parent_index) {
- var previous_sibling = -1;
- while (current_index < compile_info.length &&
- compile_info[current_index].outer_index == old_parent_index) {
- var saved_index = current_index;
- compile_info[saved_index].outer_index = new_parent_index;
- if (previous_sibling != -1) {
- compile_info[previous_sibling].next_sibling_index = saved_index;
- }
- previous_sibling = saved_index;
- current_index++;
- ResetIndexes(saved_index, old_index_map[saved_index]);
- }
- if (previous_sibling != -1) {
- compile_info[previous_sibling].next_sibling_index = -1;
- }
- }
-
- ResetIndexes(-1, -1);
- Assert(current_index == compile_info.length);
-
- return compile_info;
- }
-
-
- // Replaces function's Code.
- function PatchFunctionCode(old_node, change_log) {
- var new_info = old_node.corresponding_node.info;
- if (old_node.live_shared_function_infos) {
- old_node.live_shared_function_infos.forEach(function (old_info) {
- %LiveEditReplaceFunctionCode(new_info.raw_array,
- old_info.raw_array);
-
- // The function got a new code. However, this new code brings all new
- // instances of SharedFunctionInfo for nested functions. However,
- // we want the original instances to be used wherever possible.
- // (This is because old instances and new instances will be both
- // linked to a script and breakpoints subsystem does not really
- // expects this; neither does LiveEdit subsystem on next call).
- for (var i = 0; i < old_node.children.length; i++) {
- if (old_node.children[i].corresponding_node) {
- var corresponding_child_info =
- old_node.children[i].corresponding_node.info.
- shared_function_info;
-
- if (old_node.children[i].live_shared_function_infos) {
- old_node.children[i].live_shared_function_infos.
- forEach(function (old_child_info) {
- %LiveEditReplaceRefToNestedFunction(
- old_info.info,
- corresponding_child_info,
- old_child_info.info);
- });
- }
- }
- }
- });
-
- change_log.push( {function_patched: new_info.function_name} );
- } else {
- change_log.push( {function_patched: new_info.function_name,
- function_info_not_found: true} );
- }
- }
-
-
- // Makes a function associated with another instance of a script (the
- // one representing its old version). This way the function still
- // may access its own text.
- function LinkToOldScript(old_info_node, old_script, report_array) {
- if (old_info_node.live_shared_function_infos) {
- old_info_node.live_shared_function_infos.
- forEach(function (info) {
- %LiveEditFunctionSetScript(info.info, old_script);
- });
-
- report_array.push( { name: old_info_node.info.function_name } );
- } else {
- report_array.push(
- { name: old_info_node.info.function_name, not_found: true } );
- }
- }
-
-
- // Returns function that restores breakpoints.
- function TemporaryRemoveBreakPoints(original_script, change_log) {
- var script_break_points = GetScriptBreakPoints(original_script);
-
- var break_points_update_report = [];
- change_log.push( { break_points_update: break_points_update_report } );
-
- var break_point_old_positions = [];
- for (var i = 0; i < script_break_points.length; i++) {
- var break_point = script_break_points[i];
-
- break_point.clear();
-
- // TODO(LiveEdit): be careful with resource offset here.
- var break_point_position = Debug.findScriptSourcePosition(original_script,
- break_point.line(), break_point.column());
-
- var old_position_description = {
- position: break_point_position,
- line: break_point.line(),
- column: break_point.column()
- };
- break_point_old_positions.push(old_position_description);
- }
-
-
- // Restores breakpoints and creates their copies in the "old" copy of
- // the script.
- return function (pos_translator, old_script_copy_opt) {
- // Update breakpoints (change positions and restore them in old version
- // of script.
- for (var i = 0; i < script_break_points.length; i++) {
- var break_point = script_break_points[i];
- if (old_script_copy_opt) {
- var clone = break_point.cloneForOtherScript(old_script_copy_opt);
- clone.set(old_script_copy_opt);
-
- break_points_update_report.push( {
- type: "copied_to_old",
- id: break_point.number(),
- new_id: clone.number(),
- positions: break_point_old_positions[i]
- } );
- }
-
- var updated_position = pos_translator.Translate(
- break_point_old_positions[i].position,
- PosTranslator.ShiftWithTopInsideChunkHandler);
-
- var new_location =
- original_script.locationFromPosition(updated_position, false);
-
- break_point.update_positions(new_location.line, new_location.column);
-
- var new_position_description = {
- position: updated_position,
- line: new_location.line,
- column: new_location.column
- };
-
- break_point.set(original_script);
-
- break_points_update_report.push( { type: "position_changed",
- id: break_point.number(),
- old_positions: break_point_old_positions[i],
- new_positions: new_position_description
- } );
- }
- };
- }
-
-
- function Assert(condition, message) {
- if (!condition) {
- if (message) {
- throw "Assert " + message;
- } else {
- throw "Assert";
- }
- }
- }
-
- function DiffChunk(pos1, pos2, len1, len2) {
- this.pos1 = pos1;
- this.pos2 = pos2;
- this.len1 = len1;
- this.len2 = len2;
- }
-
- function PosTranslator(diff_array) {
- var chunks = new Array();
- var current_diff = 0;
- for (var i = 0; i < diff_array.length; i += 3) {
- var pos1_begin = diff_array[i];
- var pos2_begin = pos1_begin + current_diff;
- var pos1_end = diff_array[i + 1];
- var pos2_end = diff_array[i + 2];
- chunks.push(new DiffChunk(pos1_begin, pos2_begin, pos1_end - pos1_begin,
- pos2_end - pos2_begin));
- current_diff = pos2_end - pos1_end;
- }
- this.chunks = chunks;
- }
- PosTranslator.prototype.GetChunks = function() {
- return this.chunks;
- };
-
- PosTranslator.prototype.Translate = function(pos, inside_chunk_handler) {
- var array = this.chunks;
- if (array.length == 0 || pos < array[0].pos1) {
- return pos;
- }
- var chunk_index1 = 0;
- var chunk_index2 = array.length - 1;
-
- while (chunk_index1 < chunk_index2) {
- var middle_index = Math.floor((chunk_index1 + chunk_index2) / 2);
- if (pos < array[middle_index + 1].pos1) {
- chunk_index2 = middle_index;
- } else {
- chunk_index1 = middle_index + 1;
- }
- }
- var chunk = array[chunk_index1];
- if (pos >= chunk.pos1 + chunk.len1) {
- return pos + chunk.pos2 + chunk.len2 - chunk.pos1 - chunk.len1;
- }
-
- if (!inside_chunk_handler) {
- inside_chunk_handler = PosTranslator.DefaultInsideChunkHandler;
- }
- return inside_chunk_handler(pos, chunk);
- };
-
- PosTranslator.DefaultInsideChunkHandler = function(pos, diff_chunk) {
- Assert(false, "Cannot translate position in changed area");
- };
-
- PosTranslator.ShiftWithTopInsideChunkHandler =
- function(pos, diff_chunk) {
- // We carelessly do not check whether we stay inside the chunk after
- // translation.
- return pos - diff_chunk.pos1 + diff_chunk.pos2;
- };
-
- var FunctionStatus = {
- // No change to function or its inner functions; however its positions
- // in script may have been shifted.
- UNCHANGED: "unchanged",
- // The code of a function remains unchanged, but something happened inside
- // some inner functions.
- SOURCE_CHANGED: "source changed",
- // The code of a function is changed or some nested function cannot be
- // properly patched so this function must be recompiled.
- CHANGED: "changed",
- // Function is changed but cannot be patched.
- DAMAGED: "damaged"
- };
-
- function CodeInfoTreeNode(code_info, children, array_index) {
- this.info = code_info;
- this.children = children;
- // an index in array of compile_info
- this.array_index = array_index;
- this.parent = UNDEFINED;
-
- this.status = FunctionStatus.UNCHANGED;
- // Status explanation is used for debugging purposes and will be shown
- // in user UI if some explanations are needed.
- this.status_explanation = UNDEFINED;
- this.new_start_pos = UNDEFINED;
- this.new_end_pos = UNDEFINED;
- this.corresponding_node = UNDEFINED;
- this.unmatched_new_nodes = UNDEFINED;
-
- // 'Textual' correspondence/matching is weaker than 'pure'
- // correspondence/matching. We need 'textual' level for visual presentation
- // in UI, we use 'pure' level for actual code manipulation.
- // Sometimes only function body is changed (functions in old and new script
- // textually correspond), but we cannot patch the code, so we see them
- // as an old function deleted and new function created.
- this.textual_corresponding_node = UNDEFINED;
- this.textually_unmatched_new_nodes = UNDEFINED;
-
- this.live_shared_function_infos = UNDEFINED;
- }
-
- // From array of function infos that is implicitly a tree creates
- // an actual tree of functions in script.
- function BuildCodeInfoTree(code_info_array) {
- // Throughtout all function we iterate over input array.
- var index = 0;
-
- // Recursive function that builds a branch of tree.
- function BuildNode() {
- var my_index = index;
- index++;
- var child_array = new Array();
- while (index < code_info_array.length &&
- code_info_array[index].outer_index == my_index) {
- child_array.push(BuildNode());
- }
- var node = new CodeInfoTreeNode(code_info_array[my_index], child_array,
- my_index);
- for (var i = 0; i < child_array.length; i++) {
- child_array[i].parent = node;
- }
- return node;
- }
-
- var root = BuildNode();
- Assert(index == code_info_array.length);
- return root;
- }
-
- // Applies a list of the textual diff chunks onto the tree of functions.
- // Determines status of each function (from unchanged to damaged). However
- // children of unchanged functions are ignored.
- function MarkChangedFunctions(code_info_tree, chunks) {
-
- // A convenient iterator over diff chunks that also translates
- // positions from old to new in a current non-changed part of script.
- var chunk_it = new function() {
- var chunk_index = 0;
- var pos_diff = 0;
- this.current = function() { return chunks[chunk_index]; };
- this.next = function() {
- var chunk = chunks[chunk_index];
- pos_diff = chunk.pos2 + chunk.len2 - (chunk.pos1 + chunk.len1);
- chunk_index++;
- };
- this.done = function() { return chunk_index >= chunks.length; };
- this.TranslatePos = function(pos) { return pos + pos_diff; };
- };
-
- // A recursive function that processes internals of a function and all its
- // inner functions. Iterator chunk_it initially points to a chunk that is
- // below function start.
- function ProcessInternals(info_node) {
- info_node.new_start_pos = chunk_it.TranslatePos(
- info_node.info.start_position);
- var child_index = 0;
- var code_changed = false;
- var source_changed = false;
- // Simultaneously iterates over child functions and over chunks.
- while (!chunk_it.done() &&
- chunk_it.current().pos1 < info_node.info.end_position) {
- if (child_index < info_node.children.length) {
- var child = info_node.children[child_index];
-
- if (child.info.end_position <= chunk_it.current().pos1) {
- ProcessUnchangedChild(child);
- child_index++;
- continue;
- } else if (child.info.start_position >=
- chunk_it.current().pos1 + chunk_it.current().len1) {
- code_changed = true;
- chunk_it.next();
- continue;
- } else if (child.info.start_position <= chunk_it.current().pos1 &&
- child.info.end_position >= chunk_it.current().pos1 +
- chunk_it.current().len1) {
- ProcessInternals(child);
- source_changed = source_changed ||
- ( child.status != FunctionStatus.UNCHANGED );
- code_changed = code_changed ||
- ( child.status == FunctionStatus.DAMAGED );
- child_index++;
- continue;
- } else {
- code_changed = true;
- child.status = FunctionStatus.DAMAGED;
- child.status_explanation =
- "Text diff overlaps with function boundary";
- child_index++;
- continue;
- }
- } else {
- if (chunk_it.current().pos1 + chunk_it.current().len1 <=
- info_node.info.end_position) {
- info_node.status = FunctionStatus.CHANGED;
- chunk_it.next();
- continue;
- } else {
- info_node.status = FunctionStatus.DAMAGED;
- info_node.status_explanation =
- "Text diff overlaps with function boundary";
- return;
- }
- }
- Assert("Unreachable", false);
- }
- while (child_index < info_node.children.length) {
- var child = info_node.children[child_index];
- ProcessUnchangedChild(child);
- child_index++;
- }
- if (code_changed) {
- info_node.status = FunctionStatus.CHANGED;
- } else if (source_changed) {
- info_node.status = FunctionStatus.SOURCE_CHANGED;
- }
- info_node.new_end_pos =
- chunk_it.TranslatePos(info_node.info.end_position);
- }
-
- function ProcessUnchangedChild(node) {
- node.new_start_pos = chunk_it.TranslatePos(node.info.start_position);
- node.new_end_pos = chunk_it.TranslatePos(node.info.end_position);
- }
-
- ProcessInternals(code_info_tree);
- }
-
- // For each old function (if it is not damaged) tries to find a corresponding
- // function in new script. Typically it should succeed (non-damaged functions
- // by definition may only have changes inside their bodies). However there are
- // reasons for correspondence not to be found; function with unmodified text
- // in new script may become enclosed into other function; the innocent change
- // inside function body may in fact be something like "} function B() {" that
- // splits a function into 2 functions.
- function FindCorrespondingFunctions(old_code_tree, new_code_tree) {
-
- // A recursive function that tries to find a correspondence for all
- // child functions and for their inner functions.
- function ProcessNode(old_node, new_node) {
- var scope_change_description =
- IsFunctionContextLocalsChanged(old_node.info, new_node.info);
- if (scope_change_description) {
- old_node.status = FunctionStatus.CHANGED;
- }
-
- var old_children = old_node.children;
- var new_children = new_node.children;
-
- var unmatched_new_nodes_list = [];
- var textually_unmatched_new_nodes_list = [];
-
- var old_index = 0;
- var new_index = 0;
- while (old_index < old_children.length) {
- if (old_children[old_index].status == FunctionStatus.DAMAGED) {
- old_index++;
- } else if (new_index < new_children.length) {
- if (new_children[new_index].info.start_position <
- old_children[old_index].new_start_pos) {
- unmatched_new_nodes_list.push(new_children[new_index]);
- textually_unmatched_new_nodes_list.push(new_children[new_index]);
- new_index++;
- } else if (new_children[new_index].info.start_position ==
- old_children[old_index].new_start_pos) {
- if (new_children[new_index].info.end_position ==
- old_children[old_index].new_end_pos) {
- old_children[old_index].corresponding_node =
- new_children[new_index];
- old_children[old_index].textual_corresponding_node =
- new_children[new_index];
- if (scope_change_description) {
- old_children[old_index].status = FunctionStatus.DAMAGED;
- old_children[old_index].status_explanation =
- "Enclosing function is now incompatible. " +
- scope_change_description;
- old_children[old_index].corresponding_node = UNDEFINED;
- } else if (old_children[old_index].status !=
- FunctionStatus.UNCHANGED) {
- ProcessNode(old_children[old_index],
- new_children[new_index]);
- if (old_children[old_index].status == FunctionStatus.DAMAGED) {
- unmatched_new_nodes_list.push(
- old_children[old_index].corresponding_node);
- old_children[old_index].corresponding_node = UNDEFINED;
- old_node.status = FunctionStatus.CHANGED;
- }
- }
- } else {
- old_children[old_index].status = FunctionStatus.DAMAGED;
- old_children[old_index].status_explanation =
- "No corresponding function in new script found";
- old_node.status = FunctionStatus.CHANGED;
- unmatched_new_nodes_list.push(new_children[new_index]);
- textually_unmatched_new_nodes_list.push(new_children[new_index]);
- }
- new_index++;
- old_index++;
- } else {
- old_children[old_index].status = FunctionStatus.DAMAGED;
- old_children[old_index].status_explanation =
- "No corresponding function in new script found";
- old_node.status = FunctionStatus.CHANGED;
- old_index++;
- }
- } else {
- old_children[old_index].status = FunctionStatus.DAMAGED;
- old_children[old_index].status_explanation =
- "No corresponding function in new script found";
- old_node.status = FunctionStatus.CHANGED;
- old_index++;
- }
- }
-
- while (new_index < new_children.length) {
- unmatched_new_nodes_list.push(new_children[new_index]);
- textually_unmatched_new_nodes_list.push(new_children[new_index]);
- new_index++;
- }
-
- if (old_node.status == FunctionStatus.CHANGED) {
- if (old_node.info.param_num != new_node.info.param_num) {
- old_node.status = FunctionStatus.DAMAGED;
- old_node.status_explanation = "Changed parameter number: " +
- old_node.info.param_num + " and " + new_node.info.param_num;
- }
- }
- old_node.unmatched_new_nodes = unmatched_new_nodes_list;
- old_node.textually_unmatched_new_nodes =
- textually_unmatched_new_nodes_list;
- }
-
- ProcessNode(old_code_tree, new_code_tree);
-
- old_code_tree.corresponding_node = new_code_tree;
- old_code_tree.textual_corresponding_node = new_code_tree;
-
- Assert(old_code_tree.status != FunctionStatus.DAMAGED,
- "Script became damaged");
- }
-
- function FindLiveSharedInfos(old_code_tree, script) {
- var shared_raw_list = %LiveEditFindSharedFunctionInfosForScript(script);
-
- var shared_infos = new Array();
-
- for (var i = 0; i < shared_raw_list.length; i++) {
- shared_infos.push(new SharedInfoWrapper(shared_raw_list[i]));
- }
-
- // Finds all SharedFunctionInfos that corresponds to compile info
- // in old version of the script.
- function FindFunctionInfos(compile_info) {
- var wrappers = [];
-
- for (var i = 0; i < shared_infos.length; i++) {
- var wrapper = shared_infos[i];
- if (wrapper.start_position == compile_info.start_position &&
- wrapper.end_position == compile_info.end_position) {
- wrappers.push(wrapper);
- }
- }
-
- if (wrappers.length > 0) {
- return wrappers;
- }
- }
-
- function TraverseTree(node) {
- node.live_shared_function_infos = FindFunctionInfos(node.info);
-
- for (var i = 0; i < node.children.length; i++) {
- TraverseTree(node.children[i]);
- }
- }
-
- TraverseTree(old_code_tree);
- }
-
-
- // An object describing function compilation details. Its index fields
- // apply to indexes inside array that stores these objects.
- function FunctionCompileInfo(raw_array) {
- this.function_name = raw_array[0];
- this.start_position = raw_array[1];
- this.end_position = raw_array[2];
- this.param_num = raw_array[3];
- this.code = raw_array[4];
- this.code_scope_info = raw_array[5];
- this.scope_info = raw_array[6];
- this.outer_index = raw_array[7];
- this.shared_function_info = raw_array[8];
- this.next_sibling_index = null;
- this.raw_array = raw_array;
- }
-
- function SharedInfoWrapper(raw_array) {
- this.function_name = raw_array[0];
- this.start_position = raw_array[1];
- this.end_position = raw_array[2];
- this.info = raw_array[3];
- this.raw_array = raw_array;
- }
-
- // Changes positions (including all statements) in function.
- function PatchPositions(old_info_node, diff_array, report_array) {
- if (old_info_node.live_shared_function_infos) {
- old_info_node.live_shared_function_infos.forEach(function (info) {
- %LiveEditPatchFunctionPositions(info.raw_array,
- diff_array);
- });
-
- report_array.push( { name: old_info_node.info.function_name } );
- } else {
- // TODO(LiveEdit): function is not compiled yet or is already collected.
- report_array.push(
- { name: old_info_node.info.function_name, info_not_found: true } );
- }
- }
-
- // Adds a suffix to script name to mark that it is old version.
- function CreateNameForOldScript(script) {
- // TODO(635): try better than this; support several changes.
- return script.name + " (old)";
- }
-
- // Compares a function scope heap structure, old and new version, whether it
- // changed or not. Returns explanation if they differ.
- function IsFunctionContextLocalsChanged(function_info1, function_info2) {
- var scope_info1 = function_info1.scope_info;
- var scope_info2 = function_info2.scope_info;
-
- var scope_info1_text;
- var scope_info2_text;
-
- if (scope_info1) {
- scope_info1_text = scope_info1.toString();
- } else {
- scope_info1_text = "";
- }
- if (scope_info2) {
- scope_info2_text = scope_info2.toString();
- } else {
- scope_info2_text = "";
- }
-
- if (scope_info1_text != scope_info2_text) {
- return "Variable map changed: [" + scope_info1_text +
- "] => [" + scope_info2_text + "]";
- }
- // No differences. Return undefined.
- return;
- }
-
- // Minifier forward declaration.
- var FunctionPatchabilityStatus;
-
- // For array of wrapped shared function infos checks that none of them
- // have activations on stack (of any thread). Throws a Failure exception
- // if this proves to be false.
- function CheckStackActivations(shared_wrapper_list, change_log) {
- var shared_list = new Array();
- for (var i = 0; i < shared_wrapper_list.length; i++) {
- shared_list[i] = shared_wrapper_list[i].info;
- }
- var result = %LiveEditCheckAndDropActivations(shared_list, true);
- if (result[shared_list.length]) {
- // Extra array element may contain error message.
- throw new Failure(result[shared_list.length]);
- }
-
- var problems = new Array();
- var dropped = new Array();
- for (var i = 0; i < shared_list.length; i++) {
- var shared = shared_wrapper_list[i];
- if (result[i] == FunctionPatchabilityStatus.REPLACED_ON_ACTIVE_STACK) {
- dropped.push({ name: shared.function_name } );
- } else if (result[i] != FunctionPatchabilityStatus.AVAILABLE_FOR_PATCH) {
- var description = {
- name: shared.function_name,
- start_pos: shared.start_position,
- end_pos: shared.end_position,
- replace_problem:
- FunctionPatchabilityStatus.SymbolName(result[i])
- };
- problems.push(description);
- }
- }
- if (dropped.length > 0) {
- change_log.push({ dropped_from_stack: dropped });
- }
- if (problems.length > 0) {
- change_log.push( { functions_on_stack: problems } );
- throw new Failure("Blocked by functions on stack");
- }
-
- return dropped.length;
- }
-
- // A copy of the FunctionPatchabilityStatus enum from liveedit.h
- var FunctionPatchabilityStatus = {
- AVAILABLE_FOR_PATCH: 1,
- BLOCKED_ON_ACTIVE_STACK: 2,
- BLOCKED_ON_OTHER_STACK: 3,
- BLOCKED_UNDER_NATIVE_CODE: 4,
- REPLACED_ON_ACTIVE_STACK: 5,
- BLOCKED_UNDER_GENERATOR: 6,
- BLOCKED_ACTIVE_GENERATOR: 7
- };
-
- FunctionPatchabilityStatus.SymbolName = function(code) {
- var enumeration = FunctionPatchabilityStatus;
- for (var name in enumeration) {
- if (enumeration[name] == code) {
- return name;
- }
- }
- };
-
-
- // A logical failure in liveedit process. This means that change_log
- // is valid and consistent description of what happened.
- function Failure(message) {
- this.message = message;
- }
- // Function (constructor) is public.
- this.Failure = Failure;
-
- Failure.prototype.toString = function() {
- return "LiveEdit Failure: " + this.message;
- };
-
- function CopyErrorPositionToDetails(e, details) {
- function createPositionStruct(script, position) {
- if (position == -1) return;
- var location = script.locationFromPosition(position, true);
- if (location == null) return;
- return {
- line: location.line + 1,
- column: location.column + 1,
- position: position
- };
- }
-
- if (!("scriptObject" in e) || !("startPosition" in e)) {
- return;
- }
-
- var script = e.scriptObject;
-
- var position_struct = {
- start: createPositionStruct(script, e.startPosition),
- end: createPositionStruct(script, e.endPosition)
- };
- details.position = position_struct;
- }
-
- // A testing entry.
- function GetPcFromSourcePos(func, source_pos) {
- return %GetFunctionCodePositionFromSource(func, source_pos);
- }
- // Function is public.
- this.GetPcFromSourcePos = GetPcFromSourcePos;
-
- // LiveEdit main entry point: changes a script text to a new string.
- function SetScriptSource(script, new_source, preview_only, change_log) {
- var old_source = script.source;
- var diff = CompareStrings(old_source, new_source);
- return ApplyPatchMultiChunk(script, diff, new_source, preview_only,
- change_log);
- }
- // Function is public.
- this.SetScriptSource = SetScriptSource;
-
- function CompareStrings(s1, s2) {
- return %LiveEditCompareStrings(s1, s2);
- }
-
- // Applies the change to the script.
- // The change is always a substring (change_pos, change_pos + change_len)
- // being replaced with a completely different string new_str.
- // This API is a legacy and is obsolete.
- //
- // @param {Script} script that is being changed
- // @param {Array} change_log a list that collects engineer-readable
- // description of what happened.
- function ApplySingleChunkPatch(script, change_pos, change_len, new_str,
- change_log) {
- var old_source = script.source;
-
- // Prepare new source string.
- var new_source = old_source.substring(0, change_pos) +
- new_str + old_source.substring(change_pos + change_len);
-
- return ApplyPatchMultiChunk(script,
- [ change_pos, change_pos + change_len, change_pos + new_str.length],
- new_source, false, change_log);
- }
-
- // Creates JSON description for a change tree.
- function DescribeChangeTree(old_code_tree) {
-
- function ProcessOldNode(node) {
- var child_infos = [];
- for (var i = 0; i < node.children.length; i++) {
- var child = node.children[i];
- if (child.status != FunctionStatus.UNCHANGED) {
- child_infos.push(ProcessOldNode(child));
- }
- }
- var new_child_infos = [];
- if (node.textually_unmatched_new_nodes) {
- for (var i = 0; i < node.textually_unmatched_new_nodes.length; i++) {
- var child = node.textually_unmatched_new_nodes[i];
- new_child_infos.push(ProcessNewNode(child));
- }
- }
- var res = {
- name: node.info.function_name,
- positions: DescribePositions(node),
- status: node.status,
- children: child_infos,
- new_children: new_child_infos
- };
- if (node.status_explanation) {
- res.status_explanation = node.status_explanation;
- }
- if (node.textual_corresponding_node) {
- res.new_positions = DescribePositions(node.textual_corresponding_node);
- }
- return res;
- }
-
- function ProcessNewNode(node) {
- var child_infos = [];
- // Do not list ancestors.
- if (false) {
- for (var i = 0; i < node.children.length; i++) {
- child_infos.push(ProcessNewNode(node.children[i]));
- }
- }
- var res = {
- name: node.info.function_name,
- positions: DescribePositions(node),
- children: child_infos,
- };
- return res;
- }
-
- function DescribePositions(node) {
- return {
- start_position: node.info.start_position,
- end_position: node.info.end_position
- };
- }
-
- return ProcessOldNode(old_code_tree);
- }
-
- // Functions are public for tests.
- this.TestApi = {
- PosTranslator: PosTranslator,
- CompareStrings: CompareStrings,
- ApplySingleChunkPatch: ApplySingleChunkPatch
- };
-};
+++ /dev/null
-// Copyright 2012 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/liveedit.h"
-
-#include "src/code-stubs.h"
-#include "src/compilation-cache.h"
-#include "src/compiler.h"
-#include "src/debug.h"
-#include "src/deoptimizer.h"
-#include "src/global-handles.h"
-#include "src/messages.h"
-#include "src/parser.h"
-#include "src/scopeinfo.h"
-#include "src/scopes.h"
-#include "src/v8memory.h"
-
-namespace v8 {
-namespace internal {
-
-void SetElementSloppy(Handle<JSObject> object,
- uint32_t index,
- Handle<Object> value) {
- // Ignore return value from SetElement. It can only be a failure if there
- // are element setters causing exceptions and the debugger context has none
- // of these.
- Object::SetElement(object->GetIsolate(), object, index, value, SLOPPY)
- .Assert();
-}
-
-
-// A simple implementation of dynamic programming algorithm. It solves
-// the problem of finding the difference of 2 arrays. It uses a table of results
-// of subproblems. Each cell contains a number together with 2-bit flag
-// that helps building the chunk list.
-class Differencer {
- public:
- explicit Differencer(Comparator::Input* input)
- : input_(input), len1_(input->GetLength1()), len2_(input->GetLength2()) {
- buffer_ = NewArray<int>(len1_ * len2_);
- }
- ~Differencer() {
- DeleteArray(buffer_);
- }
-
- void Initialize() {
- int array_size = len1_ * len2_;
- for (int i = 0; i < array_size; i++) {
- buffer_[i] = kEmptyCellValue;
- }
- }
-
- // Makes sure that result for the full problem is calculated and stored
- // in the table together with flags showing a path through subproblems.
- void FillTable() {
- CompareUpToTail(0, 0);
- }
-
- void SaveResult(Comparator::Output* chunk_writer) {
- ResultWriter writer(chunk_writer);
-
- int pos1 = 0;
- int pos2 = 0;
- while (true) {
- if (pos1 < len1_) {
- if (pos2 < len2_) {
- Direction dir = get_direction(pos1, pos2);
- switch (dir) {
- case EQ:
- writer.eq();
- pos1++;
- pos2++;
- break;
- case SKIP1:
- writer.skip1(1);
- pos1++;
- break;
- case SKIP2:
- case SKIP_ANY:
- writer.skip2(1);
- pos2++;
- break;
- default:
- UNREACHABLE();
- }
- } else {
- writer.skip1(len1_ - pos1);
- break;
- }
- } else {
- if (len2_ != pos2) {
- writer.skip2(len2_ - pos2);
- }
- break;
- }
- }
- writer.close();
- }
-
- private:
- Comparator::Input* input_;
- int* buffer_;
- int len1_;
- int len2_;
-
- enum Direction {
- EQ = 0,
- SKIP1,
- SKIP2,
- SKIP_ANY,
-
- MAX_DIRECTION_FLAG_VALUE = SKIP_ANY
- };
-
- // Computes result for a subtask and optionally caches it in the buffer table.
- // All results values are shifted to make space for flags in the lower bits.
- int CompareUpToTail(int pos1, int pos2) {
- if (pos1 < len1_) {
- if (pos2 < len2_) {
- int cached_res = get_value4(pos1, pos2);
- if (cached_res == kEmptyCellValue) {
- Direction dir;
- int res;
- if (input_->Equals(pos1, pos2)) {
- res = CompareUpToTail(pos1 + 1, pos2 + 1);
- dir = EQ;
- } else {
- int res1 = CompareUpToTail(pos1 + 1, pos2) +
- (1 << kDirectionSizeBits);
- int res2 = CompareUpToTail(pos1, pos2 + 1) +
- (1 << kDirectionSizeBits);
- if (res1 == res2) {
- res = res1;
- dir = SKIP_ANY;
- } else if (res1 < res2) {
- res = res1;
- dir = SKIP1;
- } else {
- res = res2;
- dir = SKIP2;
- }
- }
- set_value4_and_dir(pos1, pos2, res, dir);
- cached_res = res;
- }
- return cached_res;
- } else {
- return (len1_ - pos1) << kDirectionSizeBits;
- }
- } else {
- return (len2_ - pos2) << kDirectionSizeBits;
- }
- }
-
- inline int& get_cell(int i1, int i2) {
- return buffer_[i1 + i2 * len1_];
- }
-
- // Each cell keeps a value plus direction. Value is multiplied by 4.
- void set_value4_and_dir(int i1, int i2, int value4, Direction dir) {
- DCHECK((value4 & kDirectionMask) == 0);
- get_cell(i1, i2) = value4 | dir;
- }
-
- int get_value4(int i1, int i2) {
- return get_cell(i1, i2) & (kMaxUInt32 ^ kDirectionMask);
- }
- Direction get_direction(int i1, int i2) {
- return static_cast<Direction>(get_cell(i1, i2) & kDirectionMask);
- }
-
- static const int kDirectionSizeBits = 2;
- static const int kDirectionMask = (1 << kDirectionSizeBits) - 1;
- static const int kEmptyCellValue = ~0u << kDirectionSizeBits;
-
- // This method only holds static assert statement (unfortunately you cannot
- // place one in class scope).
- void StaticAssertHolder() {
- STATIC_ASSERT(MAX_DIRECTION_FLAG_VALUE < (1 << kDirectionSizeBits));
- }
-
- class ResultWriter {
- public:
- explicit ResultWriter(Comparator::Output* chunk_writer)
- : chunk_writer_(chunk_writer), pos1_(0), pos2_(0),
- pos1_begin_(-1), pos2_begin_(-1), has_open_chunk_(false) {
- }
- void eq() {
- FlushChunk();
- pos1_++;
- pos2_++;
- }
- void skip1(int len1) {
- StartChunk();
- pos1_ += len1;
- }
- void skip2(int len2) {
- StartChunk();
- pos2_ += len2;
- }
- void close() {
- FlushChunk();
- }
-
- private:
- Comparator::Output* chunk_writer_;
- int pos1_;
- int pos2_;
- int pos1_begin_;
- int pos2_begin_;
- bool has_open_chunk_;
-
- void StartChunk() {
- if (!has_open_chunk_) {
- pos1_begin_ = pos1_;
- pos2_begin_ = pos2_;
- has_open_chunk_ = true;
- }
- }
-
- void FlushChunk() {
- if (has_open_chunk_) {
- chunk_writer_->AddChunk(pos1_begin_, pos2_begin_,
- pos1_ - pos1_begin_, pos2_ - pos2_begin_);
- has_open_chunk_ = false;
- }
- }
- };
-};
-
-
-void Comparator::CalculateDifference(Comparator::Input* input,
- Comparator::Output* result_writer) {
- Differencer differencer(input);
- differencer.Initialize();
- differencer.FillTable();
- differencer.SaveResult(result_writer);
-}
-
-
-static bool CompareSubstrings(Handle<String> s1, int pos1,
- Handle<String> s2, int pos2, int len) {
- for (int i = 0; i < len; i++) {
- if (s1->Get(i + pos1) != s2->Get(i + pos2)) {
- return false;
- }
- }
- return true;
-}
-
-
-// Additional to Input interface. Lets switch Input range to subrange.
-// More elegant way would be to wrap one Input as another Input object
-// and translate positions there, but that would cost us additional virtual
-// call per comparison.
-class SubrangableInput : public Comparator::Input {
- public:
- virtual void SetSubrange1(int offset, int len) = 0;
- virtual void SetSubrange2(int offset, int len) = 0;
-};
-
-
-class SubrangableOutput : public Comparator::Output {
- public:
- virtual void SetSubrange1(int offset, int len) = 0;
- virtual void SetSubrange2(int offset, int len) = 0;
-};
-
-
-static int min(int a, int b) {
- return a < b ? a : b;
-}
-
-
-// Finds common prefix and suffix in input. This parts shouldn't take space in
-// linear programming table. Enable subranging in input and output.
-static void NarrowDownInput(SubrangableInput* input,
- SubrangableOutput* output) {
- const int len1 = input->GetLength1();
- const int len2 = input->GetLength2();
-
- int common_prefix_len;
- int common_suffix_len;
-
- {
- common_prefix_len = 0;
- int prefix_limit = min(len1, len2);
- while (common_prefix_len < prefix_limit &&
- input->Equals(common_prefix_len, common_prefix_len)) {
- common_prefix_len++;
- }
-
- common_suffix_len = 0;
- int suffix_limit = min(len1 - common_prefix_len, len2 - common_prefix_len);
-
- while (common_suffix_len < suffix_limit &&
- input->Equals(len1 - common_suffix_len - 1,
- len2 - common_suffix_len - 1)) {
- common_suffix_len++;
- }
- }
-
- if (common_prefix_len > 0 || common_suffix_len > 0) {
- int new_len1 = len1 - common_suffix_len - common_prefix_len;
- int new_len2 = len2 - common_suffix_len - common_prefix_len;
-
- input->SetSubrange1(common_prefix_len, new_len1);
- input->SetSubrange2(common_prefix_len, new_len2);
-
- output->SetSubrange1(common_prefix_len, new_len1);
- output->SetSubrange2(common_prefix_len, new_len2);
- }
-}
-
-
-// A helper class that writes chunk numbers into JSArray.
-// Each chunk is stored as 3 array elements: (pos1_begin, pos1_end, pos2_end).
-class CompareOutputArrayWriter {
- public:
- explicit CompareOutputArrayWriter(Isolate* isolate)
- : array_(isolate->factory()->NewJSArray(10)), current_size_(0) {}
-
- Handle<JSArray> GetResult() {
- return array_;
- }
-
- void WriteChunk(int char_pos1, int char_pos2, int char_len1, int char_len2) {
- Isolate* isolate = array_->GetIsolate();
- SetElementSloppy(array_,
- current_size_,
- Handle<Object>(Smi::FromInt(char_pos1), isolate));
- SetElementSloppy(array_,
- current_size_ + 1,
- Handle<Object>(Smi::FromInt(char_pos1 + char_len1),
- isolate));
- SetElementSloppy(array_,
- current_size_ + 2,
- Handle<Object>(Smi::FromInt(char_pos2 + char_len2),
- isolate));
- current_size_ += 3;
- }
-
- private:
- Handle<JSArray> array_;
- int current_size_;
-};
-
-
-// Represents 2 strings as 2 arrays of tokens.
-// TODO(LiveEdit): Currently it's actually an array of charactres.
-// Make array of tokens instead.
-class TokensCompareInput : public Comparator::Input {
- public:
- TokensCompareInput(Handle<String> s1, int offset1, int len1,
- Handle<String> s2, int offset2, int len2)
- : s1_(s1), offset1_(offset1), len1_(len1),
- s2_(s2), offset2_(offset2), len2_(len2) {
- }
- virtual int GetLength1() {
- return len1_;
- }
- virtual int GetLength2() {
- return len2_;
- }
- bool Equals(int index1, int index2) {
- return s1_->Get(offset1_ + index1) == s2_->Get(offset2_ + index2);
- }
-
- private:
- Handle<String> s1_;
- int offset1_;
- int len1_;
- Handle<String> s2_;
- int offset2_;
- int len2_;
-};
-
-
-// Stores compare result in JSArray. Converts substring positions
-// to absolute positions.
-class TokensCompareOutput : public Comparator::Output {
- public:
- TokensCompareOutput(CompareOutputArrayWriter* array_writer,
- int offset1, int offset2)
- : array_writer_(array_writer), offset1_(offset1), offset2_(offset2) {
- }
-
- void AddChunk(int pos1, int pos2, int len1, int len2) {
- array_writer_->WriteChunk(pos1 + offset1_, pos2 + offset2_, len1, len2);
- }
-
- private:
- CompareOutputArrayWriter* array_writer_;
- int offset1_;
- int offset2_;
-};
-
-
-// Wraps raw n-elements line_ends array as a list of n+1 lines. The last line
-// never has terminating new line character.
-class LineEndsWrapper {
- public:
- explicit LineEndsWrapper(Handle<String> string)
- : ends_array_(String::CalculateLineEnds(string, false)),
- string_len_(string->length()) {
- }
- int length() {
- return ends_array_->length() + 1;
- }
- // Returns start for any line including start of the imaginary line after
- // the last line.
- int GetLineStart(int index) {
- if (index == 0) {
- return 0;
- } else {
- return GetLineEnd(index - 1);
- }
- }
- int GetLineEnd(int index) {
- if (index == ends_array_->length()) {
- // End of the last line is always an end of the whole string.
- // If the string ends with a new line character, the last line is an
- // empty string after this character.
- return string_len_;
- } else {
- return GetPosAfterNewLine(index);
- }
- }
-
- private:
- Handle<FixedArray> ends_array_;
- int string_len_;
-
- int GetPosAfterNewLine(int index) {
- return Smi::cast(ends_array_->get(index))->value() + 1;
- }
-};
-
-
-// Represents 2 strings as 2 arrays of lines.
-class LineArrayCompareInput : public SubrangableInput {
- public:
- LineArrayCompareInput(Handle<String> s1, Handle<String> s2,
- LineEndsWrapper line_ends1, LineEndsWrapper line_ends2)
- : s1_(s1), s2_(s2), line_ends1_(line_ends1),
- line_ends2_(line_ends2),
- subrange_offset1_(0), subrange_offset2_(0),
- subrange_len1_(line_ends1_.length()),
- subrange_len2_(line_ends2_.length()) {
- }
- int GetLength1() {
- return subrange_len1_;
- }
- int GetLength2() {
- return subrange_len2_;
- }
- bool Equals(int index1, int index2) {
- index1 += subrange_offset1_;
- index2 += subrange_offset2_;
-
- int line_start1 = line_ends1_.GetLineStart(index1);
- int line_start2 = line_ends2_.GetLineStart(index2);
- int line_end1 = line_ends1_.GetLineEnd(index1);
- int line_end2 = line_ends2_.GetLineEnd(index2);
- int len1 = line_end1 - line_start1;
- int len2 = line_end2 - line_start2;
- if (len1 != len2) {
- return false;
- }
- return CompareSubstrings(s1_, line_start1, s2_, line_start2,
- len1);
- }
- void SetSubrange1(int offset, int len) {
- subrange_offset1_ = offset;
- subrange_len1_ = len;
- }
- void SetSubrange2(int offset, int len) {
- subrange_offset2_ = offset;
- subrange_len2_ = len;
- }
-
- private:
- Handle<String> s1_;
- Handle<String> s2_;
- LineEndsWrapper line_ends1_;
- LineEndsWrapper line_ends2_;
- int subrange_offset1_;
- int subrange_offset2_;
- int subrange_len1_;
- int subrange_len2_;
-};
-
-
-// Stores compare result in JSArray. For each chunk tries to conduct
-// a fine-grained nested diff token-wise.
-class TokenizingLineArrayCompareOutput : public SubrangableOutput {
- public:
- TokenizingLineArrayCompareOutput(LineEndsWrapper line_ends1,
- LineEndsWrapper line_ends2,
- Handle<String> s1, Handle<String> s2)
- : array_writer_(s1->GetIsolate()),
- line_ends1_(line_ends1), line_ends2_(line_ends2), s1_(s1), s2_(s2),
- subrange_offset1_(0), subrange_offset2_(0) {
- }
-
- void AddChunk(int line_pos1, int line_pos2, int line_len1, int line_len2) {
- line_pos1 += subrange_offset1_;
- line_pos2 += subrange_offset2_;
-
- int char_pos1 = line_ends1_.GetLineStart(line_pos1);
- int char_pos2 = line_ends2_.GetLineStart(line_pos2);
- int char_len1 = line_ends1_.GetLineStart(line_pos1 + line_len1) - char_pos1;
- int char_len2 = line_ends2_.GetLineStart(line_pos2 + line_len2) - char_pos2;
-
- if (char_len1 < CHUNK_LEN_LIMIT && char_len2 < CHUNK_LEN_LIMIT) {
- // Chunk is small enough to conduct a nested token-level diff.
- HandleScope subTaskScope(s1_->GetIsolate());
-
- TokensCompareInput tokens_input(s1_, char_pos1, char_len1,
- s2_, char_pos2, char_len2);
- TokensCompareOutput tokens_output(&array_writer_, char_pos1,
- char_pos2);
-
- Comparator::CalculateDifference(&tokens_input, &tokens_output);
- } else {
- array_writer_.WriteChunk(char_pos1, char_pos2, char_len1, char_len2);
- }
- }
- void SetSubrange1(int offset, int len) {
- subrange_offset1_ = offset;
- }
- void SetSubrange2(int offset, int len) {
- subrange_offset2_ = offset;
- }
-
- Handle<JSArray> GetResult() {
- return array_writer_.GetResult();
- }
-
- private:
- static const int CHUNK_LEN_LIMIT = 800;
-
- CompareOutputArrayWriter array_writer_;
- LineEndsWrapper line_ends1_;
- LineEndsWrapper line_ends2_;
- Handle<String> s1_;
- Handle<String> s2_;
- int subrange_offset1_;
- int subrange_offset2_;
-};
-
-
-Handle<JSArray> LiveEdit::CompareStrings(Handle<String> s1,
- Handle<String> s2) {
- s1 = String::Flatten(s1);
- s2 = String::Flatten(s2);
-
- LineEndsWrapper line_ends1(s1);
- LineEndsWrapper line_ends2(s2);
-
- LineArrayCompareInput input(s1, s2, line_ends1, line_ends2);
- TokenizingLineArrayCompareOutput output(line_ends1, line_ends2, s1, s2);
-
- NarrowDownInput(&input, &output);
-
- Comparator::CalculateDifference(&input, &output);
-
- return output.GetResult();
-}
-
-
-// Unwraps JSValue object, returning its field "value"
-static Handle<Object> UnwrapJSValue(Handle<JSValue> jsValue) {
- return Handle<Object>(jsValue->value(), jsValue->GetIsolate());
-}
-
-
-// Wraps any object into a OpaqueReference, that will hide the object
-// from JavaScript.
-static Handle<JSValue> WrapInJSValue(Handle<HeapObject> object) {
- Isolate* isolate = object->GetIsolate();
- Handle<JSFunction> constructor = isolate->opaque_reference_function();
- Handle<JSValue> result =
- Handle<JSValue>::cast(isolate->factory()->NewJSObject(constructor));
- result->set_value(*object);
- return result;
-}
-
-
-static Handle<SharedFunctionInfo> UnwrapSharedFunctionInfoFromJSValue(
- Handle<JSValue> jsValue) {
- Object* shared = jsValue->value();
- CHECK(shared->IsSharedFunctionInfo());
- return Handle<SharedFunctionInfo>(SharedFunctionInfo::cast(shared));
-}
-
-
-static int GetArrayLength(Handle<JSArray> array) {
- Object* length = array->length();
- CHECK(length->IsSmi());
- return Smi::cast(length)->value();
-}
-
-
-void FunctionInfoWrapper::SetInitialProperties(Handle<String> name,
- int start_position,
- int end_position, int param_num,
- int literal_count,
- int parent_index) {
- HandleScope scope(isolate());
- this->SetField(kFunctionNameOffset_, name);
- this->SetSmiValueField(kStartPositionOffset_, start_position);
- this->SetSmiValueField(kEndPositionOffset_, end_position);
- this->SetSmiValueField(kParamNumOffset_, param_num);
- this->SetSmiValueField(kLiteralNumOffset_, literal_count);
- this->SetSmiValueField(kParentIndexOffset_, parent_index);
-}
-
-
-void FunctionInfoWrapper::SetFunctionCode(Handle<Code> function_code,
- Handle<HeapObject> code_scope_info) {
- Handle<JSValue> code_wrapper = WrapInJSValue(function_code);
- this->SetField(kCodeOffset_, code_wrapper);
-
- Handle<JSValue> scope_wrapper = WrapInJSValue(code_scope_info);
- this->SetField(kCodeScopeInfoOffset_, scope_wrapper);
-}
-
-
-void FunctionInfoWrapper::SetSharedFunctionInfo(
- Handle<SharedFunctionInfo> info) {
- Handle<JSValue> info_holder = WrapInJSValue(info);
- this->SetField(kSharedFunctionInfoOffset_, info_holder);
-}
-
-
-Handle<Code> FunctionInfoWrapper::GetFunctionCode() {
- Handle<Object> element = this->GetField(kCodeOffset_);
- Handle<JSValue> value_wrapper = Handle<JSValue>::cast(element);
- Handle<Object> raw_result = UnwrapJSValue(value_wrapper);
- CHECK(raw_result->IsCode());
- return Handle<Code>::cast(raw_result);
-}
-
-
-MaybeHandle<TypeFeedbackVector> FunctionInfoWrapper::GetFeedbackVector() {
- Handle<Object> element = this->GetField(kSharedFunctionInfoOffset_);
- if (element->IsJSValue()) {
- Handle<JSValue> value_wrapper = Handle<JSValue>::cast(element);
- Handle<Object> raw_result = UnwrapJSValue(value_wrapper);
- Handle<SharedFunctionInfo> shared =
- Handle<SharedFunctionInfo>::cast(raw_result);
- return Handle<TypeFeedbackVector>(shared->feedback_vector(), isolate());
- } else {
- // Scripts may never have a SharedFunctionInfo created.
- return MaybeHandle<TypeFeedbackVector>();
- }
-}
-
-
-Handle<Object> FunctionInfoWrapper::GetCodeScopeInfo() {
- Handle<Object> element = this->GetField(kCodeScopeInfoOffset_);
- return UnwrapJSValue(Handle<JSValue>::cast(element));
-}
-
-
-void SharedInfoWrapper::SetProperties(Handle<String> name,
- int start_position,
- int end_position,
- Handle<SharedFunctionInfo> info) {
- HandleScope scope(isolate());
- this->SetField(kFunctionNameOffset_, name);
- Handle<JSValue> info_holder = WrapInJSValue(info);
- this->SetField(kSharedInfoOffset_, info_holder);
- this->SetSmiValueField(kStartPositionOffset_, start_position);
- this->SetSmiValueField(kEndPositionOffset_, end_position);
-}
-
-
-Handle<SharedFunctionInfo> SharedInfoWrapper::GetInfo() {
- Handle<Object> element = this->GetField(kSharedInfoOffset_);
- Handle<JSValue> value_wrapper = Handle<JSValue>::cast(element);
- return UnwrapSharedFunctionInfoFromJSValue(value_wrapper);
-}
-
-
-class FunctionInfoListener {
- public:
- explicit FunctionInfoListener(Isolate* isolate) {
- current_parent_index_ = -1;
- len_ = 0;
- result_ = isolate->factory()->NewJSArray(10);
- }
-
- void FunctionStarted(FunctionLiteral* fun) {
- HandleScope scope(isolate());
- FunctionInfoWrapper info = FunctionInfoWrapper::Create(isolate());
- info.SetInitialProperties(fun->name(), fun->start_position(),
- fun->end_position(), fun->parameter_count(),
- fun->materialized_literal_count(),
- current_parent_index_);
- current_parent_index_ = len_;
- SetElementSloppy(result_, len_, info.GetJSArray());
- len_++;
- }
-
- void FunctionDone() {
- HandleScope scope(isolate());
- FunctionInfoWrapper info =
- FunctionInfoWrapper::cast(
- *Object::GetElement(
- isolate(), result_, current_parent_index_).ToHandleChecked());
- current_parent_index_ = info.GetParentIndex();
- }
-
- // Saves only function code, because for a script function we
- // may never create a SharedFunctionInfo object.
- void FunctionCode(Handle<Code> function_code) {
- FunctionInfoWrapper info =
- FunctionInfoWrapper::cast(
- *Object::GetElement(
- isolate(), result_, current_parent_index_).ToHandleChecked());
- info.SetFunctionCode(function_code,
- Handle<HeapObject>(isolate()->heap()->null_value()));
- }
-
- // Saves full information about a function: its code, its scope info
- // and a SharedFunctionInfo object.
- void FunctionInfo(Handle<SharedFunctionInfo> shared, Scope* scope,
- Zone* zone) {
- if (!shared->IsSharedFunctionInfo()) {
- return;
- }
- FunctionInfoWrapper info =
- FunctionInfoWrapper::cast(
- *Object::GetElement(
- isolate(), result_, current_parent_index_).ToHandleChecked());
- info.SetFunctionCode(Handle<Code>(shared->code()),
- Handle<HeapObject>(shared->scope_info()));
- info.SetSharedFunctionInfo(shared);
-
- Handle<Object> scope_info_list = SerializeFunctionScope(scope, zone);
- info.SetFunctionScopeInfo(scope_info_list);
- }
-
- Handle<JSArray> GetResult() { return result_; }
-
- private:
- Isolate* isolate() const { return result_->GetIsolate(); }
-
- Handle<Object> SerializeFunctionScope(Scope* scope, Zone* zone) {
- Handle<JSArray> scope_info_list = isolate()->factory()->NewJSArray(10);
- int scope_info_length = 0;
-
- // Saves some description of scope. It stores name and indexes of
- // variables in the whole scope chain. Null-named slots delimit
- // scopes of this chain.
- Scope* current_scope = scope;
- while (current_scope != NULL) {
- HandleScope handle_scope(isolate());
- ZoneList<Variable*> stack_list(current_scope->StackLocalCount(), zone);
- ZoneList<Variable*> context_list(
- current_scope->ContextLocalCount(), zone);
- ZoneList<Variable*> globals_list(current_scope->ContextGlobalCount(),
- zone);
- current_scope->CollectStackAndContextLocals(&stack_list, &context_list,
- &globals_list);
- context_list.Sort(&Variable::CompareIndex);
-
- for (int i = 0; i < context_list.length(); i++) {
- SetElementSloppy(scope_info_list,
- scope_info_length,
- context_list[i]->name());
- scope_info_length++;
- SetElementSloppy(
- scope_info_list,
- scope_info_length,
- Handle<Smi>(Smi::FromInt(context_list[i]->index()), isolate()));
- scope_info_length++;
- }
- SetElementSloppy(scope_info_list,
- scope_info_length,
- Handle<Object>(isolate()->heap()->null_value(),
- isolate()));
- scope_info_length++;
-
- current_scope = current_scope->outer_scope();
- }
-
- return scope_info_list;
- }
-
- Handle<JSArray> result_;
- int len_;
- int current_parent_index_;
-};
-
-
-void LiveEdit::InitializeThreadLocal(Debug* debug) {
- debug->thread_local_.frame_drop_mode_ = LiveEdit::FRAMES_UNTOUCHED;
-}
-
-
-bool LiveEdit::SetAfterBreakTarget(Debug* debug) {
- Code* code = NULL;
- Isolate* isolate = debug->isolate_;
- switch (debug->thread_local_.frame_drop_mode_) {
- case FRAMES_UNTOUCHED:
- return false;
- case FRAME_DROPPED_IN_IC_CALL:
- // We must have been calling IC stub. Do not go there anymore.
- code = isolate->builtins()->builtin(Builtins::kPlainReturn_LiveEdit);
- break;
- case FRAME_DROPPED_IN_DEBUG_SLOT_CALL:
- // Debug break slot stub does not return normally, instead it manually
- // cleans the stack and jumps. We should patch the jump address.
- code = isolate->builtins()->builtin(Builtins::kFrameDropper_LiveEdit);
- break;
- case FRAME_DROPPED_IN_DIRECT_CALL:
- // Nothing to do, after_break_target is not used here.
- return true;
- case FRAME_DROPPED_IN_RETURN_CALL:
- code = isolate->builtins()->builtin(Builtins::kFrameDropper_LiveEdit);
- break;
- case CURRENTLY_SET_MODE:
- UNREACHABLE();
- break;
- }
- debug->after_break_target_ = code->entry();
- return true;
-}
-
-
-MaybeHandle<JSArray> LiveEdit::GatherCompileInfo(Handle<Script> script,
- Handle<String> source) {
- Isolate* isolate = script->GetIsolate();
-
- FunctionInfoListener listener(isolate);
- Handle<Object> original_source =
- Handle<Object>(script->source(), isolate);
- script->set_source(*source);
- isolate->set_active_function_info_listener(&listener);
-
- {
- // Creating verbose TryCatch from public API is currently the only way to
- // force code save location. We do not use this the object directly.
- v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
- try_catch.SetVerbose(true);
-
- // A logical 'try' section.
- Compiler::CompileForLiveEdit(script);
- }
-
- // A logical 'catch' section.
- Handle<JSObject> rethrow_exception;
- if (isolate->has_pending_exception()) {
- Handle<Object> exception(isolate->pending_exception(), isolate);
- MessageLocation message_location = isolate->GetMessageLocation();
-
- isolate->clear_pending_message();
- isolate->clear_pending_exception();
-
- // If possible, copy positions from message object to exception object.
- if (exception->IsJSObject() && !message_location.script().is_null()) {
- rethrow_exception = Handle<JSObject>::cast(exception);
-
- Factory* factory = isolate->factory();
- Handle<String> start_pos_key = factory->InternalizeOneByteString(
- STATIC_CHAR_VECTOR("startPosition"));
- Handle<String> end_pos_key =
- factory->InternalizeOneByteString(STATIC_CHAR_VECTOR("endPosition"));
- Handle<String> script_obj_key =
- factory->InternalizeOneByteString(STATIC_CHAR_VECTOR("scriptObject"));
- Handle<Smi> start_pos(
- Smi::FromInt(message_location.start_pos()), isolate);
- Handle<Smi> end_pos(Smi::FromInt(message_location.end_pos()), isolate);
- Handle<JSObject> script_obj =
- Script::GetWrapper(message_location.script());
- Object::SetProperty(rethrow_exception, start_pos_key, start_pos, SLOPPY)
- .Assert();
- Object::SetProperty(rethrow_exception, end_pos_key, end_pos, SLOPPY)
- .Assert();
- Object::SetProperty(rethrow_exception, script_obj_key, script_obj, SLOPPY)
- .Assert();
- }
- }
-
- // A logical 'finally' section.
- isolate->set_active_function_info_listener(NULL);
- script->set_source(*original_source);
-
- if (rethrow_exception.is_null()) {
- return listener.GetResult();
- } else {
- return isolate->Throw<JSArray>(rethrow_exception);
- }
-}
-
-
-void LiveEdit::WrapSharedFunctionInfos(Handle<JSArray> array) {
- Isolate* isolate = array->GetIsolate();
- HandleScope scope(isolate);
- int len = GetArrayLength(array);
- for (int i = 0; i < len; i++) {
- Handle<SharedFunctionInfo> info(
- SharedFunctionInfo::cast(
- *Object::GetElement(isolate, array, i).ToHandleChecked()));
- SharedInfoWrapper info_wrapper = SharedInfoWrapper::Create(isolate);
- Handle<String> name_handle(String::cast(info->name()));
- info_wrapper.SetProperties(name_handle, info->start_position(),
- info->end_position(), info);
- SetElementSloppy(array, i, info_wrapper.GetJSArray());
- }
-}
-
-
-// Visitor that finds all references to a particular code object,
-// including "CODE_TARGET" references in other code objects and replaces
-// them on the fly.
-class ReplacingVisitor : public ObjectVisitor {
- public:
- explicit ReplacingVisitor(Code* original, Code* substitution)
- : original_(original), substitution_(substitution) {
- }
-
- virtual void VisitPointers(Object** start, Object** end) {
- for (Object** p = start; p < end; p++) {
- if (*p == original_) {
- *p = substitution_;
- }
- }
- }
-
- virtual void VisitCodeEntry(Address entry) {
- if (Code::GetObjectFromEntryAddress(entry) == original_) {
- Address substitution_entry = substitution_->instruction_start();
- Memory::Address_at(entry) = substitution_entry;
- }
- }
-
- virtual void VisitCodeTarget(RelocInfo* rinfo) {
- if (RelocInfo::IsCodeTarget(rinfo->rmode()) &&
- Code::GetCodeFromTargetAddress(rinfo->target_address()) == original_) {
- Address substitution_entry = substitution_->instruction_start();
- rinfo->set_target_address(substitution_entry);
- }
- }
-
- virtual void VisitDebugTarget(RelocInfo* rinfo) {
- VisitCodeTarget(rinfo);
- }
-
- private:
- Code* original_;
- Code* substitution_;
-};
-
-
-// Finds all references to original and replaces them with substitution.
-static void ReplaceCodeObject(Handle<Code> original,
- Handle<Code> substitution) {
- // Perform a full GC in order to ensure that we are not in the middle of an
- // incremental marking phase when we are replacing the code object.
- // Since we are not in an incremental marking phase we can write pointers
- // to code objects (that are never in new space) without worrying about
- // write barriers.
- Heap* heap = original->GetHeap();
- HeapIterator iterator(heap);
-
- DCHECK(!heap->InNewSpace(*substitution));
-
- ReplacingVisitor visitor(*original, *substitution);
-
- // Iterate over all roots. Stack frames may have pointer into original code,
- // so temporary replace the pointers with offset numbers
- // in prologue/epilogue.
- heap->IterateRoots(&visitor, VISIT_ALL);
-
- // Now iterate over all pointers of all objects, including code_target
- // implicit pointers.
- for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
- obj->Iterate(&visitor);
- }
-}
-
-
-// Patch function literals.
-// Name 'literals' is a misnomer. Rather it's a cache for complex object
-// boilerplates and for a native context. We must clean cached values.
-// Additionally we may need to allocate a new array if number of literals
-// changed.
-class LiteralFixer {
- public:
- static void PatchLiterals(FunctionInfoWrapper* compile_info_wrapper,
- Handle<SharedFunctionInfo> shared_info,
- Isolate* isolate) {
- int new_literal_count = compile_info_wrapper->GetLiteralCount();
- int old_literal_count = shared_info->num_literals();
-
- if (old_literal_count == new_literal_count) {
- // If literal count didn't change, simply go over all functions
- // and clear literal arrays.
- ClearValuesVisitor visitor;
- IterateJSFunctions(shared_info, &visitor);
- } else {
- // When literal count changes, we have to create new array instances.
- // Since we cannot create instances when iterating heap, we should first
- // collect all functions and fix their literal arrays.
- Handle<FixedArray> function_instances =
- CollectJSFunctions(shared_info, isolate);
- for (int i = 0; i < function_instances->length(); i++) {
- Handle<JSFunction> fun(JSFunction::cast(function_instances->get(i)));
- Handle<FixedArray> new_literals =
- isolate->factory()->NewFixedArray(new_literal_count);
- fun->set_literals(*new_literals);
- }
-
- shared_info->set_num_literals(new_literal_count);
- }
- }
-
- private:
- // Iterates all function instances in the HEAP that refers to the
- // provided shared_info.
- template<typename Visitor>
- static void IterateJSFunctions(Handle<SharedFunctionInfo> shared_info,
- Visitor* visitor) {
- HeapIterator iterator(shared_info->GetHeap());
- for (HeapObject* obj = iterator.next(); obj != NULL;
- obj = iterator.next()) {
- if (obj->IsJSFunction()) {
- JSFunction* function = JSFunction::cast(obj);
- if (function->shared() == *shared_info) {
- visitor->visit(function);
- }
- }
- }
- }
-
- // Finds all instances of JSFunction that refers to the provided shared_info
- // and returns array with them.
- static Handle<FixedArray> CollectJSFunctions(
- Handle<SharedFunctionInfo> shared_info, Isolate* isolate) {
- CountVisitor count_visitor;
- count_visitor.count = 0;
- IterateJSFunctions(shared_info, &count_visitor);
- int size = count_visitor.count;
-
- Handle<FixedArray> result = isolate->factory()->NewFixedArray(size);
- if (size > 0) {
- CollectVisitor collect_visitor(result);
- IterateJSFunctions(shared_info, &collect_visitor);
- }
- return result;
- }
-
- class ClearValuesVisitor {
- public:
- void visit(JSFunction* fun) {
- FixedArray* literals = fun->literals();
- int len = literals->length();
- for (int j = 0; j < len; j++) {
- literals->set_undefined(j);
- }
- }
- };
-
- class CountVisitor {
- public:
- void visit(JSFunction* fun) {
- count++;
- }
- int count;
- };
-
- class CollectVisitor {
- public:
- explicit CollectVisitor(Handle<FixedArray> output)
- : m_output(output), m_pos(0) {}
-
- void visit(JSFunction* fun) {
- m_output->set(m_pos, fun);
- m_pos++;
- }
- private:
- Handle<FixedArray> m_output;
- int m_pos;
- };
-};
-
-
-// Marks code that shares the same shared function info or has inlined
-// code that shares the same function info.
-class DependentFunctionMarker: public OptimizedFunctionVisitor {
- public:
- SharedFunctionInfo* shared_info_;
- bool found_;
-
- explicit DependentFunctionMarker(SharedFunctionInfo* shared_info)
- : shared_info_(shared_info), found_(false) { }
-
- virtual void EnterContext(Context* context) { } // Don't care.
- virtual void LeaveContext(Context* context) { } // Don't care.
- virtual void VisitFunction(JSFunction* function) {
- // It should be guaranteed by the iterator that everything is optimized.
- DCHECK(function->code()->kind() == Code::OPTIMIZED_FUNCTION);
- if (function->Inlines(shared_info_)) {
- // Mark the code for deoptimization.
- function->code()->set_marked_for_deoptimization(true);
- found_ = true;
- }
- }
-};
-
-
-static void DeoptimizeDependentFunctions(SharedFunctionInfo* function_info) {
- DisallowHeapAllocation no_allocation;
- DependentFunctionMarker marker(function_info);
- // TODO(titzer): need to traverse all optimized code to find OSR code here.
- Deoptimizer::VisitAllOptimizedFunctions(function_info->GetIsolate(), &marker);
-
- if (marker.found_) {
- // Only go through with the deoptimization if something was found.
- Deoptimizer::DeoptimizeMarkedCode(function_info->GetIsolate());
- }
-}
-
-
-void LiveEdit::ReplaceFunctionCode(
- Handle<JSArray> new_compile_info_array,
- Handle<JSArray> shared_info_array) {
- Isolate* isolate = new_compile_info_array->GetIsolate();
-
- FunctionInfoWrapper compile_info_wrapper(new_compile_info_array);
- SharedInfoWrapper shared_info_wrapper(shared_info_array);
-
- Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo();
-
- if (shared_info->code()->kind() == Code::FUNCTION) {
- Handle<Code> code = compile_info_wrapper.GetFunctionCode();
- ReplaceCodeObject(Handle<Code>(shared_info->code()), code);
- Handle<Object> code_scope_info = compile_info_wrapper.GetCodeScopeInfo();
- if (code_scope_info->IsFixedArray()) {
- shared_info->set_scope_info(ScopeInfo::cast(*code_scope_info));
- }
- shared_info->DisableOptimization(kLiveEdit);
- // Update the type feedback vector, if needed.
- MaybeHandle<TypeFeedbackVector> feedback_vector =
- compile_info_wrapper.GetFeedbackVector();
- if (!feedback_vector.is_null()) {
- shared_info->set_feedback_vector(*feedback_vector.ToHandleChecked());
- }
- }
-
- int start_position = compile_info_wrapper.GetStartPosition();
- int end_position = compile_info_wrapper.GetEndPosition();
- shared_info->set_start_position(start_position);
- shared_info->set_end_position(end_position);
-
- LiteralFixer::PatchLiterals(&compile_info_wrapper, shared_info, isolate);
-
- shared_info->set_construct_stub(
- isolate->builtins()->builtin(Builtins::kJSConstructStubGeneric));
-
- DeoptimizeDependentFunctions(*shared_info);
- isolate->compilation_cache()->Remove(shared_info);
-}
-
-
-void LiveEdit::FunctionSourceUpdated(Handle<JSArray> shared_info_array) {
- SharedInfoWrapper shared_info_wrapper(shared_info_array);
- Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo();
-
- DeoptimizeDependentFunctions(*shared_info);
- shared_info_array->GetIsolate()->compilation_cache()->Remove(shared_info);
-}
-
-
-void LiveEdit::SetFunctionScript(Handle<JSValue> function_wrapper,
- Handle<Object> script_handle) {
- Handle<SharedFunctionInfo> shared_info =
- UnwrapSharedFunctionInfoFromJSValue(function_wrapper);
- CHECK(script_handle->IsScript() || script_handle->IsUndefined());
- SharedFunctionInfo::SetScript(shared_info, script_handle);
- shared_info->DisableOptimization(kLiveEdit);
-
- function_wrapper->GetIsolate()->compilation_cache()->Remove(shared_info);
-}
-
-
-// For a script text change (defined as position_change_array), translates
-// position in unchanged text to position in changed text.
-// Text change is a set of non-overlapping regions in text, that have changed
-// their contents and length. It is specified as array of groups of 3 numbers:
-// (change_begin, change_end, change_end_new_position).
-// Each group describes a change in text; groups are sorted by change_begin.
-// Only position in text beyond any changes may be successfully translated.
-// If a positions is inside some region that changed, result is currently
-// undefined.
-static int TranslatePosition(int original_position,
- Handle<JSArray> position_change_array) {
- int position_diff = 0;
- int array_len = GetArrayLength(position_change_array);
- Isolate* isolate = position_change_array->GetIsolate();
- // TODO(635): binary search may be used here
- for (int i = 0; i < array_len; i += 3) {
- HandleScope scope(isolate);
- Handle<Object> element = Object::GetElement(
- isolate, position_change_array, i).ToHandleChecked();
- CHECK(element->IsSmi());
- int chunk_start = Handle<Smi>::cast(element)->value();
- if (original_position < chunk_start) {
- break;
- }
- element = Object::GetElement(
- isolate, position_change_array, i + 1).ToHandleChecked();
- CHECK(element->IsSmi());
- int chunk_end = Handle<Smi>::cast(element)->value();
- // Position mustn't be inside a chunk.
- DCHECK(original_position >= chunk_end);
- element = Object::GetElement(
- isolate, position_change_array, i + 2).ToHandleChecked();
- CHECK(element->IsSmi());
- int chunk_changed_end = Handle<Smi>::cast(element)->value();
- position_diff = chunk_changed_end - chunk_end;
- }
-
- return original_position + position_diff;
-}
-
-
-// Auto-growing buffer for writing relocation info code section. This buffer
-// is a simplified version of buffer from Assembler. Unlike Assembler, this
-// class is platform-independent and it works without dealing with instructions.
-// As specified by RelocInfo format, the buffer is filled in reversed order:
-// from upper to lower addresses.
-// It uses NewArray/DeleteArray for memory management.
-class RelocInfoBuffer {
- public:
- RelocInfoBuffer(int buffer_initial_capicity, byte* pc) {
- buffer_size_ = buffer_initial_capicity + kBufferGap;
- buffer_ = NewArray<byte>(buffer_size_);
-
- reloc_info_writer_.Reposition(buffer_ + buffer_size_, pc);
- }
- ~RelocInfoBuffer() {
- DeleteArray(buffer_);
- }
-
- // As specified by RelocInfo format, the buffer is filled in reversed order:
- // from upper to lower addresses.
- void Write(const RelocInfo* rinfo) {
- if (buffer_ + kBufferGap >= reloc_info_writer_.pos()) {
- Grow();
- }
- reloc_info_writer_.Write(rinfo);
- }
-
- Vector<byte> GetResult() {
- // Return the bytes from pos up to end of buffer.
- int result_size =
- static_cast<int>((buffer_ + buffer_size_) - reloc_info_writer_.pos());
- return Vector<byte>(reloc_info_writer_.pos(), result_size);
- }
-
- private:
- void Grow() {
- // Compute new buffer size.
- int new_buffer_size;
- if (buffer_size_ < 2 * KB) {
- new_buffer_size = 4 * KB;
- } else {
- new_buffer_size = 2 * buffer_size_;
- }
- // Some internal data structures overflow for very large buffers,
- // they must ensure that kMaximalBufferSize is not too large.
- if (new_buffer_size > kMaximalBufferSize) {
- V8::FatalProcessOutOfMemory("RelocInfoBuffer::GrowBuffer");
- }
-
- // Set up new buffer.
- byte* new_buffer = NewArray<byte>(new_buffer_size);
-
- // Copy the data.
- int curently_used_size =
- static_cast<int>(buffer_ + buffer_size_ - reloc_info_writer_.pos());
- MemMove(new_buffer + new_buffer_size - curently_used_size,
- reloc_info_writer_.pos(), curently_used_size);
-
- reloc_info_writer_.Reposition(
- new_buffer + new_buffer_size - curently_used_size,
- reloc_info_writer_.last_pc());
-
- DeleteArray(buffer_);
- buffer_ = new_buffer;
- buffer_size_ = new_buffer_size;
- }
-
- RelocInfoWriter reloc_info_writer_;
- byte* buffer_;
- int buffer_size_;
-
- static const int kBufferGap = RelocInfoWriter::kMaxSize;
- static const int kMaximalBufferSize = 512*MB;
-};
-
-
-// Patch positions in code (changes relocation info section) and possibly
-// returns new instance of code.
-static Handle<Code> PatchPositionsInCode(
- Handle<Code> code,
- Handle<JSArray> position_change_array) {
- Isolate* isolate = code->GetIsolate();
-
- RelocInfoBuffer buffer_writer(code->relocation_size(),
- code->instruction_start());
-
- {
- for (RelocIterator it(*code); !it.done(); it.next()) {
- RelocInfo* rinfo = it.rinfo();
- if (RelocInfo::IsPosition(rinfo->rmode())) {
- int position = static_cast<int>(rinfo->data());
- int new_position = TranslatePosition(position,
- position_change_array);
- if (position != new_position) {
- RelocInfo info_copy(rinfo->pc(), rinfo->rmode(), new_position, NULL);
- buffer_writer.Write(&info_copy);
- continue;
- }
- }
- if (RelocInfo::IsRealRelocMode(rinfo->rmode())) {
- buffer_writer.Write(it.rinfo());
- }
- }
- }
-
- Vector<byte> buffer = buffer_writer.GetResult();
-
- if (buffer.length() == code->relocation_size()) {
- // Simply patch relocation area of code.
- MemCopy(code->relocation_start(), buffer.start(), buffer.length());
- return code;
- } else {
- // Relocation info section now has different size. We cannot simply
- // rewrite it inside code object. Instead we have to create a new
- // code object.
- Handle<Code> result(isolate->factory()->CopyCode(code, buffer));
- return result;
- }
-}
-
-
-void LiveEdit::PatchFunctionPositions(Handle<JSArray> shared_info_array,
- Handle<JSArray> position_change_array) {
- SharedInfoWrapper shared_info_wrapper(shared_info_array);
- Handle<SharedFunctionInfo> info = shared_info_wrapper.GetInfo();
-
- int old_function_start = info->start_position();
- int new_function_start = TranslatePosition(old_function_start,
- position_change_array);
- int new_function_end = TranslatePosition(info->end_position(),
- position_change_array);
- int new_function_token_pos =
- TranslatePosition(info->function_token_position(), position_change_array);
-
- info->set_start_position(new_function_start);
- info->set_end_position(new_function_end);
- info->set_function_token_position(new_function_token_pos);
-
- if (info->code()->kind() == Code::FUNCTION) {
- // Patch relocation info section of the code.
- Handle<Code> patched_code = PatchPositionsInCode(Handle<Code>(info->code()),
- position_change_array);
- if (*patched_code != info->code()) {
- // Replace all references to the code across the heap. In particular,
- // some stubs may refer to this code and this code may be being executed
- // on stack (it is safe to substitute the code object on stack, because
- // we only change the structure of rinfo and leave instructions
- // untouched).
- ReplaceCodeObject(Handle<Code>(info->code()), patched_code);
- }
- }
-}
-
-
-static Handle<Script> CreateScriptCopy(Handle<Script> original) {
- Isolate* isolate = original->GetIsolate();
-
- Handle<String> original_source(String::cast(original->source()));
- Handle<Script> copy = isolate->factory()->NewScript(original_source);
-
- copy->set_name(original->name());
- copy->set_line_offset(original->line_offset());
- copy->set_column_offset(original->column_offset());
- copy->set_type(original->type());
- copy->set_context_data(original->context_data());
- copy->set_eval_from_shared(original->eval_from_shared());
- copy->set_eval_from_instructions_offset(
- original->eval_from_instructions_offset());
-
- // Copy all the flags, but clear compilation state.
- copy->set_flags(original->flags());
- copy->set_compilation_state(Script::COMPILATION_STATE_INITIAL);
-
- return copy;
-}
-
-
-Handle<Object> LiveEdit::ChangeScriptSource(Handle<Script> original_script,
- Handle<String> new_source,
- Handle<Object> old_script_name) {
- Isolate* isolate = original_script->GetIsolate();
- Handle<Object> old_script_object;
- if (old_script_name->IsString()) {
- Handle<Script> old_script = CreateScriptCopy(original_script);
- old_script->set_name(String::cast(*old_script_name));
- old_script_object = old_script;
- isolate->debug()->OnAfterCompile(old_script);
- } else {
- old_script_object = isolate->factory()->null_value();
- }
-
- original_script->set_source(*new_source);
-
- // Drop line ends so that they will be recalculated.
- original_script->set_line_ends(isolate->heap()->undefined_value());
-
- return old_script_object;
-}
-
-
-
-void LiveEdit::ReplaceRefToNestedFunction(
- Handle<JSValue> parent_function_wrapper,
- Handle<JSValue> orig_function_wrapper,
- Handle<JSValue> subst_function_wrapper) {
-
- Handle<SharedFunctionInfo> parent_shared =
- UnwrapSharedFunctionInfoFromJSValue(parent_function_wrapper);
- Handle<SharedFunctionInfo> orig_shared =
- UnwrapSharedFunctionInfoFromJSValue(orig_function_wrapper);
- Handle<SharedFunctionInfo> subst_shared =
- UnwrapSharedFunctionInfoFromJSValue(subst_function_wrapper);
-
- for (RelocIterator it(parent_shared->code()); !it.done(); it.next()) {
- if (it.rinfo()->rmode() == RelocInfo::EMBEDDED_OBJECT) {
- if (it.rinfo()->target_object() == *orig_shared) {
- it.rinfo()->set_target_object(*subst_shared);
- }
- }
- }
-}
-
-
-// Check an activation against list of functions. If there is a function
-// that matches, its status in result array is changed to status argument value.
-static bool CheckActivation(Handle<JSArray> shared_info_array,
- Handle<JSArray> result,
- StackFrame* frame,
- LiveEdit::FunctionPatchabilityStatus status) {
- if (!frame->is_java_script()) return false;
-
- Handle<JSFunction> function(JavaScriptFrame::cast(frame)->function());
-
- Isolate* isolate = shared_info_array->GetIsolate();
- int len = GetArrayLength(shared_info_array);
- for (int i = 0; i < len; i++) {
- HandleScope scope(isolate);
- Handle<Object> element =
- Object::GetElement(isolate, shared_info_array, i).ToHandleChecked();
- Handle<JSValue> jsvalue = Handle<JSValue>::cast(element);
- Handle<SharedFunctionInfo> shared =
- UnwrapSharedFunctionInfoFromJSValue(jsvalue);
-
- if (function->Inlines(*shared)) {
- SetElementSloppy(result, i, Handle<Smi>(Smi::FromInt(status), isolate));
- return true;
- }
- }
- return false;
-}
-
-
-// Iterates over handler chain and removes all elements that are inside
-// frames being dropped.
-static bool FixTryCatchHandler(StackFrame* top_frame,
- StackFrame* bottom_frame) {
- Address* pointer_address =
- &Memory::Address_at(top_frame->isolate()->get_address_from_id(
- Isolate::kHandlerAddress));
-
- while (*pointer_address < top_frame->sp()) {
- pointer_address = &Memory::Address_at(*pointer_address);
- }
- Address* above_frame_address = pointer_address;
- while (*pointer_address < bottom_frame->fp()) {
- pointer_address = &Memory::Address_at(*pointer_address);
- }
- bool change = *above_frame_address != *pointer_address;
- *above_frame_address = *pointer_address;
- return change;
-}
-
-
-// Initializes an artificial stack frame. The data it contains is used for:
-// a. successful work of frame dropper code which eventually gets control,
-// b. being compatible with regular stack structure for various stack
-// iterators.
-// Returns address of stack allocated pointer to restarted function,
-// the value that is called 'restarter_frame_function_pointer'. The value
-// at this address (possibly updated by GC) may be used later when preparing
-// 'step in' operation.
-// Frame structure (conforms InternalFrame structure):
-// -- code
-// -- SMI maker
-// -- function (slot is called "context")
-// -- frame base
-static Object** SetUpFrameDropperFrame(StackFrame* bottom_js_frame,
- Handle<Code> code) {
- DCHECK(bottom_js_frame->is_java_script());
-
- Address fp = bottom_js_frame->fp();
-
- // Move function pointer into "context" slot.
- Memory::Object_at(fp + StandardFrameConstants::kContextOffset) =
- Memory::Object_at(fp + JavaScriptFrameConstants::kFunctionOffset);
-
- Memory::Object_at(fp + InternalFrameConstants::kCodeOffset) = *code;
- Memory::Object_at(fp + StandardFrameConstants::kMarkerOffset) =
- Smi::FromInt(StackFrame::INTERNAL);
-
- return reinterpret_cast<Object**>(&Memory::Object_at(
- fp + StandardFrameConstants::kContextOffset));
-}
-
-
-// Removes specified range of frames from stack. There may be 1 or more
-// frames in range. Anyway the bottom frame is restarted rather than dropped,
-// and therefore has to be a JavaScript frame.
-// Returns error message or NULL.
-static const char* DropFrames(Vector<StackFrame*> frames,
- int top_frame_index,
- int bottom_js_frame_index,
- LiveEdit::FrameDropMode* mode,
- Object*** restarter_frame_function_pointer) {
- if (!LiveEdit::kFrameDropperSupported) {
- return "Stack manipulations are not supported in this architecture.";
- }
-
- StackFrame* pre_top_frame = frames[top_frame_index - 1];
- StackFrame* top_frame = frames[top_frame_index];
- StackFrame* bottom_js_frame = frames[bottom_js_frame_index];
-
- DCHECK(bottom_js_frame->is_java_script());
-
- // Check the nature of the top frame.
- Isolate* isolate = bottom_js_frame->isolate();
- Code* pre_top_frame_code = pre_top_frame->LookupCode();
- bool frame_has_padding = true;
- if (pre_top_frame_code->is_inline_cache_stub() &&
- pre_top_frame_code->is_debug_stub()) {
- // OK, we can drop inline cache calls.
- *mode = LiveEdit::FRAME_DROPPED_IN_IC_CALL;
- } else if (pre_top_frame_code ==
- isolate->builtins()->builtin(Builtins::kSlot_DebugBreak)) {
- // OK, we can drop debug break slot.
- *mode = LiveEdit::FRAME_DROPPED_IN_DEBUG_SLOT_CALL;
- } else if (pre_top_frame_code ==
- isolate->builtins()->builtin(Builtins::kFrameDropper_LiveEdit)) {
- // OK, we can drop our own code.
- pre_top_frame = frames[top_frame_index - 2];
- top_frame = frames[top_frame_index - 1];
- *mode = LiveEdit::CURRENTLY_SET_MODE;
- frame_has_padding = false;
- } else if (pre_top_frame_code ==
- isolate->builtins()->builtin(Builtins::kReturn_DebugBreak)) {
- *mode = LiveEdit::FRAME_DROPPED_IN_RETURN_CALL;
- } else if (pre_top_frame_code->kind() == Code::STUB &&
- CodeStub::GetMajorKey(pre_top_frame_code) == CodeStub::CEntry) {
- // Entry from our unit tests on 'debugger' statement.
- // It's fine, we support this case.
- *mode = LiveEdit::FRAME_DROPPED_IN_DIRECT_CALL;
- // We don't have a padding from 'debugger' statement call.
- // Here the stub is CEntry, it's not debug-only and can't be padded.
- // If anyone would complain, a proxy padded stub could be added.
- frame_has_padding = false;
- } else if (pre_top_frame->type() == StackFrame::ARGUMENTS_ADAPTOR) {
- // This must be adaptor that remain from the frame dropping that
- // is still on stack. A frame dropper frame must be above it.
- DCHECK(frames[top_frame_index - 2]->LookupCode() ==
- isolate->builtins()->builtin(Builtins::kFrameDropper_LiveEdit));
- pre_top_frame = frames[top_frame_index - 3];
- top_frame = frames[top_frame_index - 2];
- *mode = LiveEdit::CURRENTLY_SET_MODE;
- frame_has_padding = false;
- } else {
- return "Unknown structure of stack above changing function";
- }
-
- Address unused_stack_top = top_frame->sp();
- int new_frame_size = LiveEdit::kFrameDropperFrameSize * kPointerSize;
- Address unused_stack_bottom = bottom_js_frame->fp()
- - new_frame_size + kPointerSize; // Bigger address end is exclusive.
-
- Address* top_frame_pc_address = top_frame->pc_address();
-
- // top_frame may be damaged below this point. Do not used it.
- DCHECK(!(top_frame = NULL));
-
- if (unused_stack_top > unused_stack_bottom) {
- if (frame_has_padding) {
- int shortage_bytes =
- static_cast<int>(unused_stack_top - unused_stack_bottom);
-
- Address padding_start = pre_top_frame->fp() -
- LiveEdit::kFrameDropperFrameSize * kPointerSize;
-
- Address padding_pointer = padding_start;
- Smi* padding_object = Smi::FromInt(LiveEdit::kFramePaddingValue);
- while (Memory::Object_at(padding_pointer) == padding_object) {
- padding_pointer -= kPointerSize;
- }
- int padding_counter =
- Smi::cast(Memory::Object_at(padding_pointer))->value();
- if (padding_counter * kPointerSize < shortage_bytes) {
- return "Not enough space for frame dropper frame "
- "(even with padding frame)";
- }
- Memory::Object_at(padding_pointer) =
- Smi::FromInt(padding_counter - shortage_bytes / kPointerSize);
-
- StackFrame* pre_pre_frame = frames[top_frame_index - 2];
-
- MemMove(padding_start + kPointerSize - shortage_bytes,
- padding_start + kPointerSize,
- LiveEdit::kFrameDropperFrameSize * kPointerSize);
-
- pre_top_frame->UpdateFp(pre_top_frame->fp() - shortage_bytes);
- pre_pre_frame->SetCallerFp(pre_top_frame->fp());
- unused_stack_top -= shortage_bytes;
-
- STATIC_ASSERT(sizeof(Address) == kPointerSize);
- top_frame_pc_address -= shortage_bytes / kPointerSize;
- } else {
- return "Not enough space for frame dropper frame";
- }
- }
-
- // Committing now. After this point we should return only NULL value.
-
- FixTryCatchHandler(pre_top_frame, bottom_js_frame);
- // Make sure FixTryCatchHandler is idempotent.
- DCHECK(!FixTryCatchHandler(pre_top_frame, bottom_js_frame));
-
- Handle<Code> code = isolate->builtins()->FrameDropper_LiveEdit();
- *top_frame_pc_address = code->entry();
- pre_top_frame->SetCallerFp(bottom_js_frame->fp());
-
- *restarter_frame_function_pointer =
- SetUpFrameDropperFrame(bottom_js_frame, code);
-
- DCHECK((**restarter_frame_function_pointer)->IsJSFunction());
-
- for (Address a = unused_stack_top;
- a < unused_stack_bottom;
- a += kPointerSize) {
- Memory::Object_at(a) = Smi::FromInt(0);
- }
-
- return NULL;
-}
-
-
-// Describes a set of call frames that execute any of listed functions.
-// Finding no such frames does not mean error.
-class MultipleFunctionTarget {
- public:
- MultipleFunctionTarget(Handle<JSArray> shared_info_array,
- Handle<JSArray> result)
- : m_shared_info_array(shared_info_array),
- m_result(result) {}
- bool MatchActivation(StackFrame* frame,
- LiveEdit::FunctionPatchabilityStatus status) {
- return CheckActivation(m_shared_info_array, m_result, frame, status);
- }
- const char* GetNotFoundMessage() const {
- return NULL;
- }
- private:
- Handle<JSArray> m_shared_info_array;
- Handle<JSArray> m_result;
-};
-
-
-// Drops all call frame matched by target and all frames above them.
-template <typename TARGET>
-static const char* DropActivationsInActiveThreadImpl(Isolate* isolate,
- TARGET& target, // NOLINT
- bool do_drop) {
- Debug* debug = isolate->debug();
- Zone zone;
- Vector<StackFrame*> frames = CreateStackMap(isolate, &zone);
-
-
- int top_frame_index = -1;
- int frame_index = 0;
- for (; frame_index < frames.length(); frame_index++) {
- StackFrame* frame = frames[frame_index];
- if (frame->id() == debug->break_frame_id()) {
- top_frame_index = frame_index;
- break;
- }
- if (target.MatchActivation(
- frame, LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE)) {
- // We are still above break_frame. It is not a target frame,
- // it is a problem.
- return "Debugger mark-up on stack is not found";
- }
- }
-
- if (top_frame_index == -1) {
- // We haven't found break frame, but no function is blocking us anyway.
- return target.GetNotFoundMessage();
- }
-
- bool target_frame_found = false;
- int bottom_js_frame_index = top_frame_index;
- bool non_droppable_frame_found = false;
- LiveEdit::FunctionPatchabilityStatus non_droppable_reason;
-
- for (; frame_index < frames.length(); frame_index++) {
- StackFrame* frame = frames[frame_index];
- if (frame->is_exit()) {
- non_droppable_frame_found = true;
- non_droppable_reason = LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE;
- break;
- }
- if (frame->is_java_script() &&
- JavaScriptFrame::cast(frame)->function()->shared()->is_generator()) {
- non_droppable_frame_found = true;
- non_droppable_reason = LiveEdit::FUNCTION_BLOCKED_UNDER_GENERATOR;
- break;
- }
- if (target.MatchActivation(
- frame, LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) {
- target_frame_found = true;
- bottom_js_frame_index = frame_index;
- }
- }
-
- if (non_droppable_frame_found) {
- // There is a C or generator frame on stack. We can't drop C frames, and we
- // can't restart generators. Check that there are no target frames below
- // them.
- for (; frame_index < frames.length(); frame_index++) {
- StackFrame* frame = frames[frame_index];
- if (frame->is_java_script()) {
- if (target.MatchActivation(frame, non_droppable_reason)) {
- // Fail.
- return NULL;
- }
- }
- }
- }
-
- if (!do_drop) {
- // We are in check-only mode.
- return NULL;
- }
-
- if (!target_frame_found) {
- // Nothing to drop.
- return target.GetNotFoundMessage();
- }
-
- LiveEdit::FrameDropMode drop_mode = LiveEdit::FRAMES_UNTOUCHED;
- Object** restarter_frame_function_pointer = NULL;
- const char* error_message = DropFrames(frames, top_frame_index,
- bottom_js_frame_index, &drop_mode,
- &restarter_frame_function_pointer);
-
- if (error_message != NULL) {
- return error_message;
- }
-
- // Adjust break_frame after some frames has been dropped.
- StackFrame::Id new_id = StackFrame::NO_ID;
- for (int i = bottom_js_frame_index + 1; i < frames.length(); i++) {
- if (frames[i]->type() == StackFrame::JAVA_SCRIPT) {
- new_id = frames[i]->id();
- break;
- }
- }
- debug->FramesHaveBeenDropped(
- new_id, drop_mode, restarter_frame_function_pointer);
- return NULL;
-}
-
-
-// Fills result array with statuses of functions. Modifies the stack
-// removing all listed function if possible and if do_drop is true.
-static const char* DropActivationsInActiveThread(
- Handle<JSArray> shared_info_array, Handle<JSArray> result, bool do_drop) {
- MultipleFunctionTarget target(shared_info_array, result);
- Isolate* isolate = shared_info_array->GetIsolate();
-
- const char* message =
- DropActivationsInActiveThreadImpl(isolate, target, do_drop);
- if (message) {
- return message;
- }
-
- int array_len = GetArrayLength(shared_info_array);
-
- // Replace "blocked on active" with "replaced on active" status.
- for (int i = 0; i < array_len; i++) {
- Handle<Object> obj =
- Object::GetElement(isolate, result, i).ToHandleChecked();
- if (*obj == Smi::FromInt(LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) {
- Handle<Object> replaced(
- Smi::FromInt(LiveEdit::FUNCTION_REPLACED_ON_ACTIVE_STACK), isolate);
- SetElementSloppy(result, i, replaced);
- }
- }
- return NULL;
-}
-
-
-bool LiveEdit::FindActiveGenerators(Handle<FixedArray> shared_info_array,
- Handle<FixedArray> result,
- int len) {
- Isolate* isolate = shared_info_array->GetIsolate();
- bool found_suspended_activations = false;
-
- DCHECK_LE(len, result->length());
-
- FunctionPatchabilityStatus active = FUNCTION_BLOCKED_ACTIVE_GENERATOR;
-
- Heap* heap = isolate->heap();
- HeapIterator iterator(heap);
- HeapObject* obj = NULL;
- while ((obj = iterator.next()) != NULL) {
- if (!obj->IsJSGeneratorObject()) continue;
-
- JSGeneratorObject* gen = JSGeneratorObject::cast(obj);
- if (gen->is_closed()) continue;
-
- HandleScope scope(isolate);
-
- for (int i = 0; i < len; i++) {
- Handle<JSValue> jsvalue =
- Handle<JSValue>::cast(FixedArray::get(shared_info_array, i));
- Handle<SharedFunctionInfo> shared =
- UnwrapSharedFunctionInfoFromJSValue(jsvalue);
-
- if (gen->function()->shared() == *shared) {
- result->set(i, Smi::FromInt(active));
- found_suspended_activations = true;
- }
- }
- }
-
- return found_suspended_activations;
-}
-
-
-class InactiveThreadActivationsChecker : public ThreadVisitor {
- public:
- InactiveThreadActivationsChecker(Handle<JSArray> shared_info_array,
- Handle<JSArray> result)
- : shared_info_array_(shared_info_array), result_(result),
- has_blocked_functions_(false) {
- }
- void VisitThread(Isolate* isolate, ThreadLocalTop* top) {
- for (StackFrameIterator it(isolate, top); !it.done(); it.Advance()) {
- has_blocked_functions_ |= CheckActivation(
- shared_info_array_, result_, it.frame(),
- LiveEdit::FUNCTION_BLOCKED_ON_OTHER_STACK);
- }
- }
- bool HasBlockedFunctions() {
- return has_blocked_functions_;
- }
-
- private:
- Handle<JSArray> shared_info_array_;
- Handle<JSArray> result_;
- bool has_blocked_functions_;
-};
-
-
-Handle<JSArray> LiveEdit::CheckAndDropActivations(
- Handle<JSArray> shared_info_array, bool do_drop) {
- Isolate* isolate = shared_info_array->GetIsolate();
- int len = GetArrayLength(shared_info_array);
-
- DCHECK(shared_info_array->HasFastElements());
- Handle<FixedArray> shared_info_array_elements(
- FixedArray::cast(shared_info_array->elements()));
-
- Handle<JSArray> result = isolate->factory()->NewJSArray(len);
- Handle<FixedArray> result_elements =
- JSObject::EnsureWritableFastElements(result);
-
- // Fill the default values.
- for (int i = 0; i < len; i++) {
- FunctionPatchabilityStatus status = FUNCTION_AVAILABLE_FOR_PATCH;
- result_elements->set(i, Smi::FromInt(status));
- }
-
- // Scan the heap for active generators -- those that are either currently
- // running (as we wouldn't want to restart them, because we don't know where
- // to restart them from) or suspended. Fail if any one corresponds to the set
- // of functions being edited.
- if (FindActiveGenerators(shared_info_array_elements, result_elements, len)) {
- return result;
- }
-
- // Check inactive threads. Fail if some functions are blocked there.
- InactiveThreadActivationsChecker inactive_threads_checker(shared_info_array,
- result);
- isolate->thread_manager()->IterateArchivedThreads(
- &inactive_threads_checker);
- if (inactive_threads_checker.HasBlockedFunctions()) {
- return result;
- }
-
- // Try to drop activations from the current stack.
- const char* error_message =
- DropActivationsInActiveThread(shared_info_array, result, do_drop);
- if (error_message != NULL) {
- // Add error message as an array extra element.
- Handle<String> str =
- isolate->factory()->NewStringFromAsciiChecked(error_message);
- SetElementSloppy(result, len, str);
- }
- return result;
-}
-
-
-// Describes a single callframe a target. Not finding this frame
-// means an error.
-class SingleFrameTarget {
- public:
- explicit SingleFrameTarget(JavaScriptFrame* frame)
- : m_frame(frame),
- m_saved_status(LiveEdit::FUNCTION_AVAILABLE_FOR_PATCH) {}
-
- bool MatchActivation(StackFrame* frame,
- LiveEdit::FunctionPatchabilityStatus status) {
- if (frame->fp() == m_frame->fp()) {
- m_saved_status = status;
- return true;
- }
- return false;
- }
- const char* GetNotFoundMessage() const {
- return "Failed to found requested frame";
- }
- LiveEdit::FunctionPatchabilityStatus saved_status() {
- return m_saved_status;
- }
- private:
- JavaScriptFrame* m_frame;
- LiveEdit::FunctionPatchabilityStatus m_saved_status;
-};
-
-
-// Finds a drops required frame and all frames above.
-// Returns error message or NULL.
-const char* LiveEdit::RestartFrame(JavaScriptFrame* frame) {
- SingleFrameTarget target(frame);
-
- const char* result =
- DropActivationsInActiveThreadImpl(frame->isolate(), target, true);
- if (result != NULL) {
- return result;
- }
- if (target.saved_status() == LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE) {
- return "Function is blocked under native code";
- }
- if (target.saved_status() == LiveEdit::FUNCTION_BLOCKED_UNDER_GENERATOR) {
- return "Function is blocked under a generator activation";
- }
- return NULL;
-}
-
-
-LiveEditFunctionTracker::LiveEditFunctionTracker(Isolate* isolate,
- FunctionLiteral* fun)
- : isolate_(isolate) {
- if (isolate_->active_function_info_listener() != NULL) {
- isolate_->active_function_info_listener()->FunctionStarted(fun);
- }
-}
-
-
-LiveEditFunctionTracker::~LiveEditFunctionTracker() {
- if (isolate_->active_function_info_listener() != NULL) {
- isolate_->active_function_info_listener()->FunctionDone();
- }
-}
-
-
-void LiveEditFunctionTracker::RecordFunctionInfo(
- Handle<SharedFunctionInfo> info, FunctionLiteral* lit,
- Zone* zone) {
- if (isolate_->active_function_info_listener() != NULL) {
- isolate_->active_function_info_listener()->FunctionInfo(info, lit->scope(),
- zone);
- }
-}
-
-
-void LiveEditFunctionTracker::RecordRootFunctionInfo(Handle<Code> code) {
- isolate_->active_function_info_listener()->FunctionCode(code);
-}
-
-
-bool LiveEditFunctionTracker::IsActive(Isolate* isolate) {
- return isolate->active_function_info_listener() != NULL;
-}
-
-} // namespace internal
-} // namespace v8
+++ /dev/null
-// Copyright 2012 the V8 project authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef V8_LIVEEDIT_H_
-#define V8_LIVEEDIT_H_
-
-
-
-// Live Edit feature implementation.
-// User should be able to change script on already running VM. This feature
-// matches hot swap features in other frameworks.
-//
-// The basic use-case is when user spots some mistake in function body
-// from debugger and wishes to change the algorithm without restart.
-//
-// A single change always has a form of a simple replacement (in pseudo-code):
-// script.source[positions, positions+length] = new_string;
-// Implementation first determines, which function's body includes this
-// change area. Then both old and new versions of script are fully compiled
-// in order to analyze, whether the function changed its outer scope
-// expectations (or number of parameters). If it didn't, function's code is
-// patched with a newly compiled code. If it did change, enclosing function
-// gets patched. All inner functions are left untouched, whatever happened
-// to them in a new script version. However, new version of code will
-// instantiate newly compiled functions.
-
-
-#include "src/allocation.h"
-#include "src/compiler.h"
-
-namespace v8 {
-namespace internal {
-
-// This class collects some specific information on structure of functions
-// in a particular script. It gets called from compiler all the time, but
-// actually records any data only when liveedit operation is in process;
-// in any other time this class is very cheap.
-//
-// The primary interest of the Tracker is to record function scope structures
-// in order to analyze whether function code maybe safely patched (with new
-// code successfully reading existing data from function scopes). The Tracker
-// also collects compiled function codes.
-class LiveEditFunctionTracker {
- public:
- explicit LiveEditFunctionTracker(Isolate* isolate, FunctionLiteral* fun);
- ~LiveEditFunctionTracker();
- void RecordFunctionInfo(Handle<SharedFunctionInfo> info,
- FunctionLiteral* lit, Zone* zone);
- void RecordRootFunctionInfo(Handle<Code> code);
-
- static bool IsActive(Isolate* isolate);
-
- private:
- Isolate* isolate_;
-};
-
-
-class LiveEdit : AllStatic {
- public:
- // Describes how exactly a frame has been dropped from stack.
- enum FrameDropMode {
- // No frame has been dropped.
- FRAMES_UNTOUCHED,
- // The top JS frame had been calling IC stub. IC stub mustn't be called now.
- FRAME_DROPPED_IN_IC_CALL,
- // The top JS frame had been calling debug break slot stub. Patch the
- // address this stub jumps to in the end.
- FRAME_DROPPED_IN_DEBUG_SLOT_CALL,
- // The top JS frame had been calling some C++ function. The return address
- // gets patched automatically.
- FRAME_DROPPED_IN_DIRECT_CALL,
- FRAME_DROPPED_IN_RETURN_CALL,
- CURRENTLY_SET_MODE
- };
-
- static void InitializeThreadLocal(Debug* debug);
-
- static bool SetAfterBreakTarget(Debug* debug);
-
- MUST_USE_RESULT static MaybeHandle<JSArray> GatherCompileInfo(
- Handle<Script> script,
- Handle<String> source);
-
- static void WrapSharedFunctionInfos(Handle<JSArray> array);
-
- static void ReplaceFunctionCode(Handle<JSArray> new_compile_info_array,
- Handle<JSArray> shared_info_array);
-
- static void FunctionSourceUpdated(Handle<JSArray> shared_info_array);
-
- // Updates script field in FunctionSharedInfo.
- static void SetFunctionScript(Handle<JSValue> function_wrapper,
- Handle<Object> script_handle);
-
- static void PatchFunctionPositions(Handle<JSArray> shared_info_array,
- Handle<JSArray> position_change_array);
-
- // For a script updates its source field. If old_script_name is provided
- // (i.e. is a String), also creates a copy of the script with its original
- // source and sends notification to debugger.
- static Handle<Object> ChangeScriptSource(Handle<Script> original_script,
- Handle<String> new_source,
- Handle<Object> old_script_name);
-
- // In a code of a parent function replaces original function as embedded
- // object with a substitution one.
- static void ReplaceRefToNestedFunction(Handle<JSValue> parent_function_shared,
- Handle<JSValue> orig_function_shared,
- Handle<JSValue> subst_function_shared);
-
- // Find open generator activations, and set corresponding "result" elements to
- // FUNCTION_BLOCKED_ACTIVE_GENERATOR.
- static bool FindActiveGenerators(Handle<FixedArray> shared_info_array,
- Handle<FixedArray> result, int len);
-
- // Checks listed functions on stack and return array with corresponding
- // FunctionPatchabilityStatus statuses; extra array element may
- // contain general error message. Modifies the current stack and
- // has restart the lowest found frames and drops all other frames above
- // if possible and if do_drop is true.
- static Handle<JSArray> CheckAndDropActivations(
- Handle<JSArray> shared_info_array, bool do_drop);
-
- // Restarts the call frame and completely drops all frames above it.
- // Return error message or NULL.
- static const char* RestartFrame(JavaScriptFrame* frame);
-
- // A copy of this is in liveedit-debugger.js.
- enum FunctionPatchabilityStatus {
- FUNCTION_AVAILABLE_FOR_PATCH = 1,
- FUNCTION_BLOCKED_ON_ACTIVE_STACK = 2,
- FUNCTION_BLOCKED_ON_OTHER_STACK = 3,
- FUNCTION_BLOCKED_UNDER_NATIVE_CODE = 4,
- FUNCTION_REPLACED_ON_ACTIVE_STACK = 5,
- FUNCTION_BLOCKED_UNDER_GENERATOR = 6,
- FUNCTION_BLOCKED_ACTIVE_GENERATOR = 7
- };
-
- // Compares 2 strings line-by-line, then token-wise and returns diff in form
- // of array of triplets (pos1, pos1_end, pos2_end) describing list
- // of diff chunks.
- static Handle<JSArray> CompareStrings(Handle<String> s1,
- Handle<String> s2);
-
- // Architecture-specific constant.
- static const bool kFrameDropperSupported;
-
- /**
- * Defines layout of a stack frame that supports padding. This is a regular
- * internal frame that has a flexible stack structure. LiveEdit can shift
- * its lower part up the stack, taking up the 'padding' space when additional
- * stack memory is required.
- * Such frame is expected immediately above the topmost JavaScript frame.
- *
- * Stack Layout:
- * --- Top
- * LiveEdit routine frames
- * ---
- * C frames of debug handler
- * ---
- * ...
- * ---
- * An internal frame that has n padding words:
- * - any number of words as needed by code -- upper part of frame
- * - padding size: a Smi storing n -- current size of padding
- * - padding: n words filled with kPaddingValue in form of Smi
- * - 3 context/type words of a regular InternalFrame
- * - fp
- * ---
- * Topmost JavaScript frame
- * ---
- * ...
- * --- Bottom
- */
- // A size of frame base including fp. Padding words starts right above
- // the base.
- static const int kFrameDropperFrameSize = 4;
- // A number of words that should be reserved on stack for the LiveEdit use.
- // Stored on stack in form of Smi.
- static const int kFramePaddingInitialSize = 1;
- // A value that padding words are filled with (in form of Smi). Going
- // bottom-top, the first word not having this value is a counter word.
- static const int kFramePaddingValue = kFramePaddingInitialSize + 1;
-};
-
-
-// A general-purpose comparator between 2 arrays.
-class Comparator {
- public:
- // Holds 2 arrays of some elements allowing to compare any pair of
- // element from the first array and element from the second array.
- class Input {
- public:
- virtual int GetLength1() = 0;
- virtual int GetLength2() = 0;
- virtual bool Equals(int index1, int index2) = 0;
-
- protected:
- virtual ~Input() {}
- };
-
- // Receives compare result as a series of chunks.
- class Output {
- public:
- // Puts another chunk in result list. Note that technically speaking
- // only 3 arguments actually needed with 4th being derivable.
- virtual void AddChunk(int pos1, int pos2, int len1, int len2) = 0;
-
- protected:
- virtual ~Output() {}
- };
-
- // Finds the difference between 2 arrays of elements.
- static void CalculateDifference(Input* input,
- Output* result_writer);
-};
-
-
-
-// Simple helper class that creates more or less typed structures over
-// JSArray object. This is an adhoc method of passing structures from C++
-// to JavaScript.
-template<typename S>
-class JSArrayBasedStruct {
- public:
- static S Create(Isolate* isolate) {
- Factory* factory = isolate->factory();
- Handle<JSArray> array = factory->NewJSArray(S::kSize_);
- return S(array);
- }
-
- static S cast(Object* object) {
- JSArray* array = JSArray::cast(object);
- Handle<JSArray> array_handle(array);
- return S(array_handle);
- }
-
- explicit JSArrayBasedStruct(Handle<JSArray> array) : array_(array) {
- }
-
- Handle<JSArray> GetJSArray() {
- return array_;
- }
-
- Isolate* isolate() const {
- return array_->GetIsolate();
- }
-
- protected:
- void SetField(int field_position, Handle<Object> value) {
- Object::SetElement(isolate(), array_, field_position, value, SLOPPY)
- .Assert();
- }
-
- void SetSmiValueField(int field_position, int value) {
- SetField(field_position, Handle<Smi>(Smi::FromInt(value), isolate()));
- }
-
- Handle<Object> GetField(int field_position) {
- return Object::GetElement(
- isolate(), array_, field_position).ToHandleChecked();
- }
-
- int GetSmiValueField(int field_position) {
- Handle<Object> res = GetField(field_position);
- return Handle<Smi>::cast(res)->value();
- }
-
- private:
- Handle<JSArray> array_;
-};
-
-
-// Represents some function compilation details. This structure will be used
-// from JavaScript. It contains Code object, which is kept wrapped
-// into a BlindReference for sanitizing reasons.
-class FunctionInfoWrapper : public JSArrayBasedStruct<FunctionInfoWrapper> {
- public:
- explicit FunctionInfoWrapper(Handle<JSArray> array)
- : JSArrayBasedStruct<FunctionInfoWrapper>(array) {
- }
-
- void SetInitialProperties(Handle<String> name, int start_position,
- int end_position, int param_num, int literal_count,
- int parent_index);
-
- void SetFunctionCode(Handle<Code> function_code,
- Handle<HeapObject> code_scope_info);
-
- void SetFunctionScopeInfo(Handle<Object> scope_info_array) {
- this->SetField(kFunctionScopeInfoOffset_, scope_info_array);
- }
-
- void SetSharedFunctionInfo(Handle<SharedFunctionInfo> info);
-
- int GetLiteralCount() {
- return this->GetSmiValueField(kLiteralNumOffset_);
- }
-
- int GetParentIndex() {
- return this->GetSmiValueField(kParentIndexOffset_);
- }
-
- Handle<Code> GetFunctionCode();
-
- MaybeHandle<TypeFeedbackVector> GetFeedbackVector();
-
- Handle<Object> GetCodeScopeInfo();
-
- int GetStartPosition() {
- return this->GetSmiValueField(kStartPositionOffset_);
- }
-
- int GetEndPosition() { return this->GetSmiValueField(kEndPositionOffset_); }
-
- private:
- static const int kFunctionNameOffset_ = 0;
- static const int kStartPositionOffset_ = 1;
- static const int kEndPositionOffset_ = 2;
- static const int kParamNumOffset_ = 3;
- static const int kCodeOffset_ = 4;
- static const int kCodeScopeInfoOffset_ = 5;
- static const int kFunctionScopeInfoOffset_ = 6;
- static const int kParentIndexOffset_ = 7;
- static const int kSharedFunctionInfoOffset_ = 8;
- static const int kLiteralNumOffset_ = 9;
- static const int kSize_ = 10;
-
- friend class JSArrayBasedStruct<FunctionInfoWrapper>;
-};
-
-
-// Wraps SharedFunctionInfo along with some of its fields for passing it
-// back to JavaScript. SharedFunctionInfo object itself is additionally
-// wrapped into BlindReference for sanitizing reasons.
-class SharedInfoWrapper : public JSArrayBasedStruct<SharedInfoWrapper> {
- public:
- static bool IsInstance(Handle<JSArray> array) {
- if (array->length() != Smi::FromInt(kSize_)) return false;
- Handle<Object> element(
- Object::GetElement(array->GetIsolate(),
- array,
- kSharedInfoOffset_).ToHandleChecked());
- if (!element->IsJSValue()) return false;
- return Handle<JSValue>::cast(element)->value()->IsSharedFunctionInfo();
- }
-
- explicit SharedInfoWrapper(Handle<JSArray> array)
- : JSArrayBasedStruct<SharedInfoWrapper>(array) {
- }
-
- void SetProperties(Handle<String> name,
- int start_position,
- int end_position,
- Handle<SharedFunctionInfo> info);
-
- Handle<SharedFunctionInfo> GetInfo();
-
- private:
- static const int kFunctionNameOffset_ = 0;
- static const int kStartPositionOffset_ = 1;
- static const int kEndPositionOffset_ = 2;
- static const int kSharedInfoOffset_ = 3;
- static const int kSize_ = 4;
-
- friend class JSArrayBasedStruct<SharedInfoWrapper>;
-};
-
-} } // namespace v8::internal
-
-#endif /* V*_LIVEEDIT_H_ */
#include "src/mips/assembler-mips.h"
#include "src/assembler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
namespace v8 {
#if V8_TARGET_ARCH_MIPS
#include "src/codegen.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/deoptimizer.h"
#include "src/full-codegen/full-codegen.h"
#include "src/interpreter/bytecodes.h"
+++ /dev/null
-// Copyright 2012 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"
-
-#if V8_TARGET_ARCH_MIPS
-
-#include "src/codegen.h"
-#include "src/debug.h"
-
-namespace v8 {
-namespace internal {
-
-#define __ ACCESS_MASM(masm)
-
-
-void EmitDebugBreakSlot(MacroAssembler* masm) {
- Label check_size;
- __ bind(&check_size);
- for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
- __ nop(MacroAssembler::DEBUG_BREAK_NOP);
- }
- DCHECK_EQ(Assembler::kDebugBreakSlotInstructions,
- masm->InstructionsGeneratedSince(&check_size));
-}
-
-
-void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode,
- int call_argc) {
- // Generate enough nop's to make space for a call instruction. Avoid emitting
- // the trampoline pool in the debug break slot code.
- Assembler::BlockTrampolinePoolScope block_pool(masm);
- masm->RecordDebugBreakSlot(mode, call_argc);
- EmitDebugBreakSlot(masm);
-}
-
-
-void DebugCodegen::ClearDebugBreakSlot(Address pc) {
- CodePatcher patcher(pc, Assembler::kDebugBreakSlotInstructions);
- EmitDebugBreakSlot(patcher.masm());
-}
-
-
-void DebugCodegen::PatchDebugBreakSlot(Address pc, Handle<Code> code) {
- DCHECK_EQ(Code::BUILTIN, code->kind());
- CodePatcher patcher(pc, Assembler::kDebugBreakSlotInstructions);
- // Patch the code changing the debug break slot code from:
- // nop(DEBUG_BREAK_NOP) - nop(1) is sll(zero_reg, zero_reg, 1)
- // nop(DEBUG_BREAK_NOP)
- // nop(DEBUG_BREAK_NOP)
- // nop(DEBUG_BREAK_NOP)
- // to a call to the debug break slot code.
- // li t9, address (lui t9 / ori t9 instruction pair)
- // call t9 (jalr t9 / nop instruction pair)
- patcher.masm()->li(v8::internal::t9,
- Operand(reinterpret_cast<int32_t>(code->entry())));
- patcher.masm()->Call(v8::internal::t9);
-}
-
-
-void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
- DebugBreakCallHelperMode mode) {
- __ RecordComment("Debug break");
- {
- FrameScope scope(masm, StackFrame::INTERNAL);
-
- // Load padding words on stack.
- __ li(at, Operand(Smi::FromInt(LiveEdit::kFramePaddingValue)));
- __ Subu(sp, sp,
- Operand(kPointerSize * LiveEdit::kFramePaddingInitialSize));
- for (int i = LiveEdit::kFramePaddingInitialSize - 1; i >= 0; i--) {
- __ sw(at, MemOperand(sp, kPointerSize * i));
- }
- __ li(at, Operand(Smi::FromInt(LiveEdit::kFramePaddingInitialSize)));
- __ push(at);
-
- if (mode == SAVE_RESULT_REGISTER) __ push(v0);
-
- __ PrepareCEntryArgs(0); // No arguments.
- __ PrepareCEntryFunction(ExternalReference(
- Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate()));
-
- CEntryStub ceb(masm->isolate(), 1);
- __ CallStub(&ceb);
-
- if (FLAG_debug_code) {
- for (int i = 0; i < kNumJSCallerSaved; i++) {
- Register reg = {JSCallerSavedCode(i)};
- __ li(reg, kDebugZapValue);
- }
- }
-
- if (mode == SAVE_RESULT_REGISTER) __ pop(v0);
-
- // Don't bother removing padding bytes pushed on the stack
- // as the frame is going to be restored right away.
-
- // Leave the internal frame.
- }
-
- // Now that the break point has been handled, resume normal execution by
- // jumping to the target address intended by the caller and that was
- // overwritten by the address of DebugBreakXXX.
- ExternalReference after_break_target =
- ExternalReference::debug_after_break_target_address(masm->isolate());
- __ li(t9, Operand(after_break_target));
- __ lw(t9, MemOperand(t9));
- __ Jump(t9);
-}
-
-
-void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
- __ Ret();
-}
-
-
-void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
- ExternalReference restarter_frame_function_slot =
- ExternalReference::debug_restarter_frame_function_pointer_address(
- masm->isolate());
- __ li(at, Operand(restarter_frame_function_slot));
- __ sw(zero_reg, MemOperand(at, 0));
-
- // We do not know our frame height, but set sp based on fp.
- __ Subu(sp, fp, Operand(kPointerSize));
-
- __ Pop(ra, fp, a1); // Return address, Frame, Function.
-
- // Load context from the function.
- __ lw(cp, FieldMemOperand(a1, JSFunction::kContextOffset));
-
- // Get function code.
- __ lw(at, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
- __ lw(at, FieldMemOperand(at, SharedFunctionInfo::kCodeOffset));
- __ Addu(t9, at, Operand(Code::kHeaderSize - kHeapObjectTag));
-
- // Re-run JSFunction, a1 is function, cp is context.
- __ Jump(t9);
-}
-
-
-const bool LiveEdit::kFrameDropperSupported = true;
-
-#undef __
-
-} // namespace internal
-} // namespace v8
-
-#endif // V8_TARGET_ARCH_MIPS
#include "src/bootstrapper.h"
#include "src/codegen.h"
#include "src/cpu-profiler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/runtime/runtime.h"
namespace v8 {
#include "src/mips64/assembler-mips64.h"
#include "src/assembler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
namespace v8 {
#if V8_TARGET_ARCH_MIPS64
#include "src/codegen.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/deoptimizer.h"
#include "src/full-codegen/full-codegen.h"
#include "src/runtime/runtime.h"
+++ /dev/null
-// Copyright 2012 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"
-
-#if V8_TARGET_ARCH_MIPS64
-
-#include "src/codegen.h"
-#include "src/debug.h"
-
-namespace v8 {
-namespace internal {
-
-#define __ ACCESS_MASM(masm)
-
-void EmitDebugBreakSlot(MacroAssembler* masm) {
- Label check_size;
- __ bind(&check_size);
- for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
- __ nop(MacroAssembler::DEBUG_BREAK_NOP);
- }
- DCHECK_EQ(Assembler::kDebugBreakSlotInstructions,
- masm->InstructionsGeneratedSince(&check_size));
-}
-
-
-void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode,
- int call_argc) {
- // Generate enough nop's to make space for a call instruction. Avoid emitting
- // the trampoline pool in the debug break slot code.
- Assembler::BlockTrampolinePoolScope block_pool(masm);
- masm->RecordDebugBreakSlot(mode, call_argc);
- EmitDebugBreakSlot(masm);
-}
-
-
-void DebugCodegen::ClearDebugBreakSlot(Address pc) {
- CodePatcher patcher(pc, Assembler::kDebugBreakSlotInstructions);
- EmitDebugBreakSlot(patcher.masm());
-}
-
-
-void DebugCodegen::PatchDebugBreakSlot(Address pc, Handle<Code> code) {
- DCHECK_EQ(Code::BUILTIN, code->kind());
- CodePatcher patcher(pc, Assembler::kDebugBreakSlotInstructions);
- // Patch the code changing the debug break slot code from:
- // nop(DEBUG_BREAK_NOP) - nop(1) is sll(zero_reg, zero_reg, 1)
- // nop(DEBUG_BREAK_NOP)
- // nop(DEBUG_BREAK_NOP)
- // nop(DEBUG_BREAK_NOP)
- // nop(DEBUG_BREAK_NOP)
- // nop(DEBUG_BREAK_NOP)
- // to a call to the debug break slot code.
- // li t9, address (4-instruction sequence on mips64)
- // call t9 (jalr t9 / nop instruction pair)
- patcher.masm()->li(v8::internal::t9,
- Operand(reinterpret_cast<int64_t>(code->entry())),
- ADDRESS_LOAD);
- patcher.masm()->Call(v8::internal::t9);
-}
-
-
-void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
- DebugBreakCallHelperMode mode) {
- __ RecordComment("Debug break");
- {
- FrameScope scope(masm, StackFrame::INTERNAL);
-
- // Load padding words on stack.
- __ li(at, Operand(Smi::FromInt(LiveEdit::kFramePaddingValue)));
- __ Dsubu(sp, sp,
- Operand(kPointerSize * LiveEdit::kFramePaddingInitialSize));
- for (int i = LiveEdit::kFramePaddingInitialSize - 1; i >= 0; i--) {
- __ sd(at, MemOperand(sp, kPointerSize * i));
- }
- __ li(at, Operand(Smi::FromInt(LiveEdit::kFramePaddingInitialSize)));
- __ push(at);
-
- if (mode == SAVE_RESULT_REGISTER) __ push(v0);
-
- __ PrepareCEntryArgs(0); // No arguments.
- __ PrepareCEntryFunction(ExternalReference(
- Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate()));
-
- CEntryStub ceb(masm->isolate(), 1);
- __ CallStub(&ceb);
-
- if (FLAG_debug_code) {
- for (int i = 0; i < kNumJSCallerSaved; i++) {
- Register reg = {JSCallerSavedCode(i)};
- __ li(reg, kDebugZapValue);
- }
- }
-
- if (mode == SAVE_RESULT_REGISTER) __ pop(v0);
-
- // Don't bother removing padding bytes pushed on the stack
- // as the frame is going to be restored right away.
-
- // Leave the internal frame.
- }
-
- // Now that the break point has been handled, resume normal execution by
- // jumping to the target address intended by the caller and that was
- // overwritten by the address of DebugBreakXXX.
- ExternalReference after_break_target =
- ExternalReference::debug_after_break_target_address(masm->isolate());
- __ li(t9, Operand(after_break_target));
- __ ld(t9, MemOperand(t9));
- __ Jump(t9);
-}
-
-
-void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
- __ Ret();
-}
-
-
-void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
- ExternalReference restarter_frame_function_slot =
- ExternalReference::debug_restarter_frame_function_pointer_address(
- masm->isolate());
- __ li(at, Operand(restarter_frame_function_slot));
- __ sw(zero_reg, MemOperand(at, 0));
-
- // We do not know our frame height, but set sp based on fp.
- __ Dsubu(sp, fp, Operand(kPointerSize));
-
- __ Pop(ra, fp, a1); // Return address, Frame, Function.
-
- // Load context from the function.
- __ ld(cp, FieldMemOperand(a1, JSFunction::kContextOffset));
-
- // Get function code.
- __ ld(at, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
- __ ld(at, FieldMemOperand(at, SharedFunctionInfo::kCodeOffset));
- __ Daddu(t9, at, Operand(Code::kHeaderSize - kHeapObjectTag));
-
- // Re-run JSFunction, a1 is function, cp is context.
- __ Jump(t9);
-}
-
-
-const bool LiveEdit::kFrameDropperSupported = true;
-
-#undef __
-
-} // namespace internal
-} // namespace v8
-
-#endif // V8_TARGET_ARCH_MIPS64
#include "src/bootstrapper.h"
#include "src/codegen.h"
#include "src/cpu-profiler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/runtime/runtime.h"
namespace v8 {
+++ /dev/null
-// Copyright 2006-2012 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.
-"use strict";
-
-// Handle id counters.
-var next_handle_ = 0;
-var next_transient_handle_ = -1;
-
-// Mirror cache.
-var mirror_cache_ = [];
-var mirror_cache_enabled_ = true;
-
-
-function ToggleMirrorCache(value) {
- mirror_cache_enabled_ = value;
- next_handle_ = 0;
- mirror_cache_ = [];
-}
-
-
-// Wrapper to check whether an object is a Promise. The call may not work
-// if promises are not enabled.
-// TODO(yangguo): remove try-catch once promises are enabled by default.
-function ObjectIsPromise(value) {
- try {
- return IS_SPEC_OBJECT(value) &&
- !IS_UNDEFINED(%DebugGetProperty(value, builtins.$promiseStatus));
- } catch (e) {
- return false;
- }
-}
-
-
-/**
- * Returns the mirror for a specified value or object.
- *
- * @param {value or Object} value the value or object to retreive the mirror for
- * @param {boolean} transient indicate whether this object is transient and
- * should not be added to the mirror cache. The default is not transient.
- * @returns {Mirror} the mirror reflects the passed value or object
- */
-function MakeMirror(value, opt_transient) {
- var mirror;
-
- // Look for non transient mirrors in the mirror cache.
- if (!opt_transient && mirror_cache_enabled_) {
- for (var id in mirror_cache_) {
- mirror = mirror_cache_[id];
- if (mirror.value() === value) {
- return mirror;
- }
- // Special check for NaN as NaN == NaN is false.
- if (mirror.isNumber() && isNaN(mirror.value()) &&
- typeof value == 'number' && isNaN(value)) {
- return mirror;
- }
- }
- }
-
- if (IS_UNDEFINED(value)) {
- mirror = new UndefinedMirror();
- } else if (IS_NULL(value)) {
- mirror = new NullMirror();
- } else if (IS_BOOLEAN(value)) {
- mirror = new BooleanMirror(value);
- } else if (IS_NUMBER(value)) {
- mirror = new NumberMirror(value);
- } else if (IS_STRING(value)) {
- mirror = new StringMirror(value);
- } else if (IS_SYMBOL(value)) {
- mirror = new SymbolMirror(value);
- } else if (IS_ARRAY(value)) {
- mirror = new ArrayMirror(value);
- } else if (IS_DATE(value)) {
- mirror = new DateMirror(value);
- } else if (IS_FUNCTION(value)) {
- mirror = new FunctionMirror(value);
- } else if (IS_REGEXP(value)) {
- mirror = new RegExpMirror(value);
- } else if (IS_ERROR(value)) {
- mirror = new ErrorMirror(value);
- } else if (IS_SCRIPT(value)) {
- mirror = new ScriptMirror(value);
- } else if (IS_MAP(value) || IS_WEAKMAP(value)) {
- mirror = new MapMirror(value);
- } else if (IS_SET(value) || IS_WEAKSET(value)) {
- mirror = new SetMirror(value);
- } else if (IS_MAP_ITERATOR(value) || IS_SET_ITERATOR(value)) {
- mirror = new IteratorMirror(value);
- } else if (ObjectIsPromise(value)) {
- mirror = new PromiseMirror(value);
- } else if (IS_GENERATOR(value)) {
- mirror = new GeneratorMirror(value);
- } else {
- mirror = new ObjectMirror(value, OBJECT_TYPE, opt_transient);
- }
-
- if (mirror_cache_enabled_) mirror_cache_[mirror.handle()] = mirror;
- return mirror;
-}
-
-
-/**
- * Returns the mirror for a specified mirror handle.
- *
- * @param {number} handle the handle to find the mirror for
- * @returns {Mirror or undefiend} the mirror with the requested handle or
- * undefined if no mirror with the requested handle was found
- */
-function LookupMirror(handle) {
- if (!mirror_cache_enabled_) throw new Error("Mirror cache is disabled");
- return mirror_cache_[handle];
-}
-
-
-/**
- * Returns the mirror for the undefined value.
- *
- * @returns {Mirror} the mirror reflects the undefined value
- */
-function GetUndefinedMirror() {
- return MakeMirror(UNDEFINED);
-}
-
-
-/**
- * Inherit the prototype methods from one constructor into another.
- *
- * The Function.prototype.inherits from lang.js rewritten as a standalone
- * function (not on Function.prototype). NOTE: If this file is to be loaded
- * during bootstrapping this function needs to be revritten using some native
- * functions as prototype setup using normal JavaScript does not work as
- * expected during bootstrapping (see mirror.js in r114903).
- *
- * @param {function} ctor Constructor function which needs to inherit the
- * prototype
- * @param {function} superCtor Constructor function to inherit prototype from
- */
-function inherits(ctor, superCtor) {
- var tempCtor = function(){};
- tempCtor.prototype = superCtor.prototype;
- ctor.super_ = superCtor.prototype;
- ctor.prototype = new tempCtor();
- ctor.prototype.constructor = ctor;
-}
-
-
-// Type names of the different mirrors.
-var UNDEFINED_TYPE = 'undefined';
-var NULL_TYPE = 'null';
-var BOOLEAN_TYPE = 'boolean';
-var NUMBER_TYPE = 'number';
-var STRING_TYPE = 'string';
-var SYMBOL_TYPE = 'symbol';
-var OBJECT_TYPE = 'object';
-var FUNCTION_TYPE = 'function';
-var REGEXP_TYPE = 'regexp';
-var ERROR_TYPE = 'error';
-var PROPERTY_TYPE = 'property';
-var INTERNAL_PROPERTY_TYPE = 'internalProperty';
-var FRAME_TYPE = 'frame';
-var SCRIPT_TYPE = 'script';
-var CONTEXT_TYPE = 'context';
-var SCOPE_TYPE = 'scope';
-var PROMISE_TYPE = 'promise';
-var MAP_TYPE = 'map';
-var SET_TYPE = 'set';
-var ITERATOR_TYPE = 'iterator';
-var GENERATOR_TYPE = 'generator';
-
-// Maximum length when sending strings through the JSON protocol.
-var kMaxProtocolStringLength = 80;
-
-// Different kind of properties.
-var PropertyKind = {};
-PropertyKind.Named = 1;
-PropertyKind.Indexed = 2;
-
-
-// A copy of the PropertyType enum from property-details.h
-var PropertyType = {};
-PropertyType.Data = 0;
-PropertyType.DataConstant = 2;
-PropertyType.AccessorConstant = 3;
-
-
-// Different attributes for a property.
-var PropertyAttribute = {};
-PropertyAttribute.None = NONE;
-PropertyAttribute.ReadOnly = READ_ONLY;
-PropertyAttribute.DontEnum = DONT_ENUM;
-PropertyAttribute.DontDelete = DONT_DELETE;
-
-
-// A copy of the scope types from runtime-debug.cc.
-// NOTE: these constants should be backward-compatible, so
-// add new ones to the end of this list.
-var ScopeType = { Global: 0,
- Local: 1,
- With: 2,
- Closure: 3,
- Catch: 4,
- Block: 5,
- Script: 6 };
-
-
-// Mirror hierarchy:
-// - Mirror
-// - ValueMirror
-// - UndefinedMirror
-// - NullMirror
-// - NumberMirror
-// - StringMirror
-// - SymbolMirror
-// - ObjectMirror
-// - FunctionMirror
-// - UnresolvedFunctionMirror
-// - ArrayMirror
-// - DateMirror
-// - RegExpMirror
-// - ErrorMirror
-// - PromiseMirror
-// - MapMirror
-// - SetMirror
-// - IteratorMirror
-// - GeneratorMirror
-// - PropertyMirror
-// - InternalPropertyMirror
-// - FrameMirror
-// - ScriptMirror
-
-
-/**
- * Base class for all mirror objects.
- * @param {string} type The type of the mirror
- * @constructor
- */
-function Mirror(type) {
- this.type_ = type;
-}
-
-
-Mirror.prototype.type = function() {
- return this.type_;
-};
-
-
-/**
- * Check whether the mirror reflects a value.
- * @returns {boolean} True if the mirror reflects a value.
- */
-Mirror.prototype.isValue = function() {
- return this instanceof ValueMirror;
-};
-
-
-/**
- * Check whether the mirror reflects the undefined value.
- * @returns {boolean} True if the mirror reflects the undefined value.
- */
-Mirror.prototype.isUndefined = function() {
- return this instanceof UndefinedMirror;
-};
-
-
-/**
- * Check whether the mirror reflects the null value.
- * @returns {boolean} True if the mirror reflects the null value
- */
-Mirror.prototype.isNull = function() {
- return this instanceof NullMirror;
-};
-
-
-/**
- * Check whether the mirror reflects a boolean value.
- * @returns {boolean} True if the mirror reflects a boolean value
- */
-Mirror.prototype.isBoolean = function() {
- return this instanceof BooleanMirror;
-};
-
-
-/**
- * Check whether the mirror reflects a number value.
- * @returns {boolean} True if the mirror reflects a number value
- */
-Mirror.prototype.isNumber = function() {
- return this instanceof NumberMirror;
-};
-
-
-/**
- * Check whether the mirror reflects a string value.
- * @returns {boolean} True if the mirror reflects a string value
- */
-Mirror.prototype.isString = function() {
- return this instanceof StringMirror;
-};
-
-
-/**
- * Check whether the mirror reflects a symbol.
- * @returns {boolean} True if the mirror reflects a symbol
- */
-Mirror.prototype.isSymbol = function() {
- return this instanceof SymbolMirror;
-};
-
-
-/**
- * Check whether the mirror reflects an object.
- * @returns {boolean} True if the mirror reflects an object
- */
-Mirror.prototype.isObject = function() {
- return this instanceof ObjectMirror;
-};
-
-
-/**
- * Check whether the mirror reflects a function.
- * @returns {boolean} True if the mirror reflects a function
- */
-Mirror.prototype.isFunction = function() {
- return this instanceof FunctionMirror;
-};
-
-
-/**
- * Check whether the mirror reflects an unresolved function.
- * @returns {boolean} True if the mirror reflects an unresolved function
- */
-Mirror.prototype.isUnresolvedFunction = function() {
- return this instanceof UnresolvedFunctionMirror;
-};
-
-
-/**
- * Check whether the mirror reflects an array.
- * @returns {boolean} True if the mirror reflects an array
- */
-Mirror.prototype.isArray = function() {
- return this instanceof ArrayMirror;
-};
-
-
-/**
- * Check whether the mirror reflects a date.
- * @returns {boolean} True if the mirror reflects a date
- */
-Mirror.prototype.isDate = function() {
- return this instanceof DateMirror;
-};
-
-
-/**
- * Check whether the mirror reflects a regular expression.
- * @returns {boolean} True if the mirror reflects a regular expression
- */
-Mirror.prototype.isRegExp = function() {
- return this instanceof RegExpMirror;
-};
-
-
-/**
- * Check whether the mirror reflects an error.
- * @returns {boolean} True if the mirror reflects an error
- */
-Mirror.prototype.isError = function() {
- return this instanceof ErrorMirror;
-};
-
-
-/**
- * Check whether the mirror reflects a promise.
- * @returns {boolean} True if the mirror reflects a promise
- */
-Mirror.prototype.isPromise = function() {
- return this instanceof PromiseMirror;
-};
-
-
-/**
- * Check whether the mirror reflects a generator object.
- * @returns {boolean} True if the mirror reflects a generator object
- */
-Mirror.prototype.isGenerator = function() {
- return this instanceof GeneratorMirror;
-};
-
-
-/**
- * Check whether the mirror reflects a property.
- * @returns {boolean} True if the mirror reflects a property
- */
-Mirror.prototype.isProperty = function() {
- return this instanceof PropertyMirror;
-};
-
-
-/**
- * Check whether the mirror reflects an internal property.
- * @returns {boolean} True if the mirror reflects an internal property
- */
-Mirror.prototype.isInternalProperty = function() {
- return this instanceof InternalPropertyMirror;
-};
-
-
-/**
- * Check whether the mirror reflects a stack frame.
- * @returns {boolean} True if the mirror reflects a stack frame
- */
-Mirror.prototype.isFrame = function() {
- return this instanceof FrameMirror;
-};
-
-
-/**
- * Check whether the mirror reflects a script.
- * @returns {boolean} True if the mirror reflects a script
- */
-Mirror.prototype.isScript = function() {
- return this instanceof ScriptMirror;
-};
-
-
-/**
- * Check whether the mirror reflects a context.
- * @returns {boolean} True if the mirror reflects a context
- */
-Mirror.prototype.isContext = function() {
- return this instanceof ContextMirror;
-};
-
-
-/**
- * Check whether the mirror reflects a scope.
- * @returns {boolean} True if the mirror reflects a scope
- */
-Mirror.prototype.isScope = function() {
- return this instanceof ScopeMirror;
-};
-
-
-/**
- * Check whether the mirror reflects a map.
- * @returns {boolean} True if the mirror reflects a map
- */
-Mirror.prototype.isMap = function() {
- return this instanceof MapMirror;
-};
-
-
-/**
- * Check whether the mirror reflects a set.
- * @returns {boolean} True if the mirror reflects a set
- */
-Mirror.prototype.isSet = function() {
- return this instanceof SetMirror;
-};
-
-
-/**
- * Check whether the mirror reflects an iterator.
- * @returns {boolean} True if the mirror reflects an iterator
- */
-Mirror.prototype.isIterator = function() {
- return this instanceof IteratorMirror;
-};
-
-
-/**
- * Allocate a handle id for this object.
- */
-Mirror.prototype.allocateHandle_ = function() {
- if (mirror_cache_enabled_) this.handle_ = next_handle_++;
-};
-
-
-/**
- * Allocate a transient handle id for this object. Transient handles are
- * negative.
- */
-Mirror.prototype.allocateTransientHandle_ = function() {
- this.handle_ = next_transient_handle_--;
-};
-
-
-Mirror.prototype.toText = function() {
- // Simpel to text which is used when on specialization in subclass.
- return "#<" + this.constructor.name + ">";
-};
-
-
-/**
- * Base class for all value mirror objects.
- * @param {string} type The type of the mirror
- * @param {value} value The value reflected by this mirror
- * @param {boolean} transient indicate whether this object is transient with a
- * transient handle
- * @constructor
- * @extends Mirror
- */
-function ValueMirror(type, value, transient) {
- %_CallFunction(this, type, Mirror);
- this.value_ = value;
- if (!transient) {
- this.allocateHandle_();
- } else {
- this.allocateTransientHandle_();
- }
-}
-inherits(ValueMirror, Mirror);
-
-
-Mirror.prototype.handle = function() {
- return this.handle_;
-};
-
-
-/**
- * Check whether this is a primitive value.
- * @return {boolean} True if the mirror reflects a primitive value
- */
-ValueMirror.prototype.isPrimitive = function() {
- var type = this.type();
- return type === 'undefined' ||
- type === 'null' ||
- type === 'boolean' ||
- type === 'number' ||
- type === 'string' ||
- type === 'symbol';
-};
-
-
-/**
- * Get the actual value reflected by this mirror.
- * @return {value} The value reflected by this mirror
- */
-ValueMirror.prototype.value = function() {
- return this.value_;
-};
-
-
-/**
- * Mirror object for Undefined.
- * @constructor
- * @extends ValueMirror
- */
-function UndefinedMirror() {
- %_CallFunction(this, UNDEFINED_TYPE, UNDEFINED, ValueMirror);
-}
-inherits(UndefinedMirror, ValueMirror);
-
-
-UndefinedMirror.prototype.toText = function() {
- return 'undefined';
-};
-
-
-/**
- * Mirror object for null.
- * @constructor
- * @extends ValueMirror
- */
-function NullMirror() {
- %_CallFunction(this, NULL_TYPE, null, ValueMirror);
-}
-inherits(NullMirror, ValueMirror);
-
-
-NullMirror.prototype.toText = function() {
- return 'null';
-};
-
-
-/**
- * Mirror object for boolean values.
- * @param {boolean} value The boolean value reflected by this mirror
- * @constructor
- * @extends ValueMirror
- */
-function BooleanMirror(value) {
- %_CallFunction(this, BOOLEAN_TYPE, value, ValueMirror);
-}
-inherits(BooleanMirror, ValueMirror);
-
-
-BooleanMirror.prototype.toText = function() {
- return this.value_ ? 'true' : 'false';
-};
-
-
-/**
- * Mirror object for number values.
- * @param {number} value The number value reflected by this mirror
- * @constructor
- * @extends ValueMirror
- */
-function NumberMirror(value) {
- %_CallFunction(this, NUMBER_TYPE, value, ValueMirror);
-}
-inherits(NumberMirror, ValueMirror);
-
-
-NumberMirror.prototype.toText = function() {
- return %_NumberToString(this.value_);
-};
-
-
-/**
- * Mirror object for string values.
- * @param {string} value The string value reflected by this mirror
- * @constructor
- * @extends ValueMirror
- */
-function StringMirror(value) {
- %_CallFunction(this, STRING_TYPE, value, ValueMirror);
-}
-inherits(StringMirror, ValueMirror);
-
-
-StringMirror.prototype.length = function() {
- return this.value_.length;
-};
-
-StringMirror.prototype.getTruncatedValue = function(maxLength) {
- if (maxLength != -1 && this.length() > maxLength) {
- return this.value_.substring(0, maxLength) +
- '... (length: ' + this.length() + ')';
- }
- return this.value_;
-};
-
-StringMirror.prototype.toText = function() {
- return this.getTruncatedValue(kMaxProtocolStringLength);
-};
-
-
-/**
- * Mirror object for a Symbol
- * @param {Object} value The Symbol
- * @constructor
- * @extends Mirror
- */
-function SymbolMirror(value) {
- %_CallFunction(this, SYMBOL_TYPE, value, ValueMirror);
-}
-inherits(SymbolMirror, ValueMirror);
-
-
-SymbolMirror.prototype.description = function() {
- return %SymbolDescription(%_ValueOf(this.value_));
-}
-
-
-SymbolMirror.prototype.toText = function() {
- return %_CallFunction(this.value_, builtins.$symbolToString);
-}
-
-
-/**
- * Mirror object for objects.
- * @param {object} value The object reflected by this mirror
- * @param {boolean} transient indicate whether this object is transient with a
- * transient handle
- * @constructor
- * @extends ValueMirror
- */
-function ObjectMirror(value, type, transient) {
- %_CallFunction(this, type || OBJECT_TYPE, value, transient, ValueMirror);
-}
-inherits(ObjectMirror, ValueMirror);
-
-
-ObjectMirror.prototype.className = function() {
- return %_ClassOf(this.value_);
-};
-
-
-ObjectMirror.prototype.constructorFunction = function() {
- return MakeMirror(%DebugGetProperty(this.value_, 'constructor'));
-};
-
-
-ObjectMirror.prototype.prototypeObject = function() {
- return MakeMirror(%DebugGetProperty(this.value_, 'prototype'));
-};
-
-
-ObjectMirror.prototype.protoObject = function() {
- return MakeMirror(%DebugGetPrototype(this.value_));
-};
-
-
-ObjectMirror.prototype.hasNamedInterceptor = function() {
- // Get information on interceptors for this object.
- var x = %GetInterceptorInfo(this.value_);
- return (x & 2) != 0;
-};
-
-
-ObjectMirror.prototype.hasIndexedInterceptor = function() {
- // Get information on interceptors for this object.
- var x = %GetInterceptorInfo(this.value_);
- return (x & 1) != 0;
-};
-
-
-// Get all own property names except for private symbols.
-function TryGetPropertyNames(object) {
- try {
- // TODO(yangguo): Should there be a special debugger implementation of
- // %GetOwnPropertyNames that doesn't perform access checks?
- return %GetOwnPropertyNames(object, PROPERTY_ATTRIBUTES_PRIVATE_SYMBOL);
- } catch (e) {
- // Might have hit a failed access check.
- return [];
- }
-}
-
-
-/**
- * Return the property names for this object.
- * @param {number} kind Indicate whether named, indexed or both kinds of
- * properties are requested
- * @param {number} limit Limit the number of names returend to the specified
- value
- * @return {Array} Property names for this object
- */
-ObjectMirror.prototype.propertyNames = function(kind, limit) {
- // Find kind and limit and allocate array for the result
- kind = kind || PropertyKind.Named | PropertyKind.Indexed;
-
- var propertyNames;
- var elementNames;
- var total = 0;
-
- // Find all the named properties.
- if (kind & PropertyKind.Named) {
- propertyNames = TryGetPropertyNames(this.value_);
- total += propertyNames.length;
-
- // Get names for named interceptor properties if any.
- if (this.hasNamedInterceptor() && (kind & PropertyKind.Named)) {
- var namedInterceptorNames =
- %GetNamedInterceptorPropertyNames(this.value_);
- if (namedInterceptorNames) {
- propertyNames = propertyNames.concat(namedInterceptorNames);
- total += namedInterceptorNames.length;
- }
- }
- }
-
- // Find all the indexed properties.
- if (kind & PropertyKind.Indexed) {
- // Get own element names.
- elementNames = %GetOwnElementNames(this.value_);
- total += elementNames.length;
-
- // Get names for indexed interceptor properties.
- if (this.hasIndexedInterceptor() && (kind & PropertyKind.Indexed)) {
- var indexedInterceptorNames =
- %GetIndexedInterceptorElementNames(this.value_);
- if (indexedInterceptorNames) {
- elementNames = elementNames.concat(indexedInterceptorNames);
- total += indexedInterceptorNames.length;
- }
- }
- }
- limit = Math.min(limit || total, total);
-
- var names = new Array(limit);
- var index = 0;
-
- // Copy names for named properties.
- if (kind & PropertyKind.Named) {
- for (var i = 0; index < limit && i < propertyNames.length; i++) {
- names[index++] = propertyNames[i];
- }
- }
-
- // Copy names for indexed properties.
- if (kind & PropertyKind.Indexed) {
- for (var i = 0; index < limit && i < elementNames.length; i++) {
- names[index++] = elementNames[i];
- }
- }
-
- return names;
-};
-
-
-/**
- * Return the properties for this object as an array of PropertyMirror objects.
- * @param {number} kind Indicate whether named, indexed or both kinds of
- * properties are requested
- * @param {number} limit Limit the number of properties returned to the
- specified value
- * @return {Array} Property mirrors for this object
- */
-ObjectMirror.prototype.properties = function(kind, limit) {
- var names = this.propertyNames(kind, limit);
- var properties = new Array(names.length);
- for (var i = 0; i < names.length; i++) {
- properties[i] = this.property(names[i]);
- }
-
- return properties;
-};
-
-
-/**
- * Return the internal properties for this object as an array of
- * InternalPropertyMirror objects.
- * @return {Array} Property mirrors for this object
- */
-ObjectMirror.prototype.internalProperties = function() {
- return ObjectMirror.GetInternalProperties(this.value_);
-}
-
-
-ObjectMirror.prototype.property = function(name) {
- var details = %DebugGetPropertyDetails(this.value_, builtins.$toName(name));
- if (details) {
- return new PropertyMirror(this, name, details);
- }
-
- // Nothing found.
- return GetUndefinedMirror();
-};
-
-
-
-/**
- * Try to find a property from its value.
- * @param {Mirror} value The property value to look for
- * @return {PropertyMirror} The property with the specified value. If no
- * property was found with the specified value UndefinedMirror is returned
- */
-ObjectMirror.prototype.lookupProperty = function(value) {
- var properties = this.properties();
-
- // Look for property value in properties.
- for (var i = 0; i < properties.length; i++) {
-
- // Skip properties which are defined through assessors.
- var property = properties[i];
- if (property.propertyType() != PropertyType.AccessorConstant) {
- if (%_ObjectEquals(property.value_, value.value_)) {
- return property;
- }
- }
- }
-
- // Nothing found.
- return GetUndefinedMirror();
-};
-
-
-/**
- * Returns objects which has direct references to this object
- * @param {number} opt_max_objects Optional parameter specifying the maximum
- * number of referencing objects to return.
- * @return {Array} The objects which has direct references to this object.
- */
-ObjectMirror.prototype.referencedBy = function(opt_max_objects) {
- // Find all objects with direct references to this object.
- var result = %DebugReferencedBy(this.value_,
- Mirror.prototype, opt_max_objects || 0);
-
- // Make mirrors for all the references found.
- for (var i = 0; i < result.length; i++) {
- result[i] = MakeMirror(result[i]);
- }
-
- return result;
-};
-
-
-ObjectMirror.prototype.toText = function() {
- var name;
- var ctor = this.constructorFunction();
- if (!ctor.isFunction()) {
- name = this.className();
- } else {
- name = ctor.name();
- if (!name) {
- name = this.className();
- }
- }
- return '#<' + name + '>';
-};
-
-
-/**
- * Return the internal properties of the value, such as [[PrimitiveValue]] of
- * scalar wrapper objects, properties of the bound function and properties of
- * the promise.
- * This method is done static to be accessible from Debug API with the bare
- * values without mirrors.
- * @return {Array} array (possibly empty) of InternalProperty instances
- */
-ObjectMirror.GetInternalProperties = function(value) {
- var properties = %DebugGetInternalProperties(value);
- var result = [];
- for (var i = 0; i < properties.length; i += 2) {
- result.push(new InternalPropertyMirror(properties[i], properties[i + 1]));
- }
- return result;
-}
-
-
-/**
- * Mirror object for functions.
- * @param {function} value The function object reflected by this mirror.
- * @constructor
- * @extends ObjectMirror
- */
-function FunctionMirror(value) {
- %_CallFunction(this, value, FUNCTION_TYPE, ObjectMirror);
- this.resolved_ = true;
-}
-inherits(FunctionMirror, ObjectMirror);
-
-
-/**
- * Returns whether the function is resolved.
- * @return {boolean} True if the function is resolved. Unresolved functions can
- * only originate as functions from stack frames
- */
-FunctionMirror.prototype.resolved = function() {
- return this.resolved_;
-};
-
-
-/**
- * Returns the name of the function.
- * @return {string} Name of the function
- */
-FunctionMirror.prototype.name = function() {
- return %FunctionGetName(this.value_);
-};
-
-
-/**
- * Returns the inferred name of the function.
- * @return {string} Name of the function
- */
-FunctionMirror.prototype.inferredName = function() {
- return %FunctionGetInferredName(this.value_);
-};
-
-
-/**
- * Returns the source code for the function.
- * @return {string or undefined} The source code for the function. If the
- * function is not resolved undefined will be returned.
- */
-FunctionMirror.prototype.source = function() {
- // Return source if function is resolved. Otherwise just fall through to
- // return undefined.
- if (this.resolved()) {
- return builtins.$functionSourceString(this.value_);
- }
-};
-
-
-/**
- * Returns the script object for the function.
- * @return {ScriptMirror or undefined} Script object for the function or
- * undefined if the function has no script
- */
-FunctionMirror.prototype.script = function() {
- // Return script if function is resolved. Otherwise just fall through
- // to return undefined.
- if (this.resolved()) {
- if (this.script_) {
- return this.script_;
- }
- var script = %FunctionGetScript(this.value_);
- if (script) {
- return this.script_ = MakeMirror(script);
- }
- }
-};
-
-
-/**
- * Returns the script source position for the function. Only makes sense
- * for functions which has a script defined.
- * @return {Number or undefined} in-script position for the function
- */
-FunctionMirror.prototype.sourcePosition_ = function() {
- // Return position if function is resolved. Otherwise just fall
- // through to return undefined.
- if (this.resolved()) {
- return %FunctionGetScriptSourcePosition(this.value_);
- }
-};
-
-
-/**
- * Returns the script source location object for the function. Only makes sense
- * for functions which has a script defined.
- * @return {Location or undefined} in-script location for the function begin
- */
-FunctionMirror.prototype.sourceLocation = function() {
- if (this.resolved()) {
- var script = this.script();
- if (script) {
- return script.locationFromPosition(this.sourcePosition_(), true);
- }
- }
-};
-
-
-/**
- * Returns objects constructed by this function.
- * @param {number} opt_max_instances Optional parameter specifying the maximum
- * number of instances to return.
- * @return {Array or undefined} The objects constructed by this function.
- */
-FunctionMirror.prototype.constructedBy = function(opt_max_instances) {
- if (this.resolved()) {
- // Find all objects constructed from this function.
- var result = %DebugConstructedBy(this.value_, opt_max_instances || 0);
-
- // Make mirrors for all the instances found.
- for (var i = 0; i < result.length; i++) {
- result[i] = MakeMirror(result[i]);
- }
-
- return result;
- } else {
- return [];
- }
-};
-
-
-FunctionMirror.prototype.scopeCount = function() {
- if (this.resolved()) {
- if (IS_UNDEFINED(this.scopeCount_)) {
- this.scopeCount_ = %GetFunctionScopeCount(this.value());
- }
- return this.scopeCount_;
- } else {
- return 0;
- }
-};
-
-
-FunctionMirror.prototype.scope = function(index) {
- if (this.resolved()) {
- return new ScopeMirror(UNDEFINED, this, index);
- }
-};
-
-
-FunctionMirror.prototype.toText = function() {
- return this.source();
-};
-
-
-/**
- * Mirror object for unresolved functions.
- * @param {string} value The name for the unresolved function reflected by this
- * mirror.
- * @constructor
- * @extends ObjectMirror
- */
-function UnresolvedFunctionMirror(value) {
- // Construct this using the ValueMirror as an unresolved function is not a
- // real object but just a string.
- %_CallFunction(this, FUNCTION_TYPE, value, ValueMirror);
- this.propertyCount_ = 0;
- this.elementCount_ = 0;
- this.resolved_ = false;
-}
-inherits(UnresolvedFunctionMirror, FunctionMirror);
-
-
-UnresolvedFunctionMirror.prototype.className = function() {
- return 'Function';
-};
-
-
-UnresolvedFunctionMirror.prototype.constructorFunction = function() {
- return GetUndefinedMirror();
-};
-
-
-UnresolvedFunctionMirror.prototype.prototypeObject = function() {
- return GetUndefinedMirror();
-};
-
-
-UnresolvedFunctionMirror.prototype.protoObject = function() {
- return GetUndefinedMirror();
-};
-
-
-UnresolvedFunctionMirror.prototype.name = function() {
- return this.value_;
-};
-
-
-UnresolvedFunctionMirror.prototype.inferredName = function() {
- return undefined;
-};
-
-
-UnresolvedFunctionMirror.prototype.propertyNames = function(kind, limit) {
- return [];
-};
-
-
-/**
- * Mirror object for arrays.
- * @param {Array} value The Array object reflected by this mirror
- * @constructor
- * @extends ObjectMirror
- */
-function ArrayMirror(value) {
- %_CallFunction(this, value, ObjectMirror);
-}
-inherits(ArrayMirror, ObjectMirror);
-
-
-ArrayMirror.prototype.length = function() {
- return this.value_.length;
-};
-
-
-ArrayMirror.prototype.indexedPropertiesFromRange = function(opt_from_index,
- opt_to_index) {
- var from_index = opt_from_index || 0;
- var to_index = opt_to_index || this.length() - 1;
- if (from_index > to_index) return new Array();
- var values = new Array(to_index - from_index + 1);
- for (var i = from_index; i <= to_index; i++) {
- var details = %DebugGetPropertyDetails(this.value_, builtins.$toString(i));
- var value;
- if (details) {
- value = new PropertyMirror(this, i, details);
- } else {
- value = GetUndefinedMirror();
- }
- values[i - from_index] = value;
- }
- return values;
-};
-
-
-/**
- * Mirror object for dates.
- * @param {Date} value The Date object reflected by this mirror
- * @constructor
- * @extends ObjectMirror
- */
-function DateMirror(value) {
- %_CallFunction(this, value, ObjectMirror);
-}
-inherits(DateMirror, ObjectMirror);
-
-
-DateMirror.prototype.toText = function() {
- var s = JSON.stringify(this.value_);
- return s.substring(1, s.length - 1); // cut quotes
-};
-
-
-/**
- * Mirror object for regular expressions.
- * @param {RegExp} value The RegExp object reflected by this mirror
- * @constructor
- * @extends ObjectMirror
- */
-function RegExpMirror(value) {
- %_CallFunction(this, value, REGEXP_TYPE, ObjectMirror);
-}
-inherits(RegExpMirror, ObjectMirror);
-
-
-/**
- * Returns the source to the regular expression.
- * @return {string or undefined} The source to the regular expression
- */
-RegExpMirror.prototype.source = function() {
- return this.value_.source;
-};
-
-
-/**
- * Returns whether this regular expression has the global (g) flag set.
- * @return {boolean} Value of the global flag
- */
-RegExpMirror.prototype.global = function() {
- return this.value_.global;
-};
-
-
-/**
- * Returns whether this regular expression has the ignore case (i) flag set.
- * @return {boolean} Value of the ignore case flag
- */
-RegExpMirror.prototype.ignoreCase = function() {
- return this.value_.ignoreCase;
-};
-
-
-/**
- * Returns whether this regular expression has the multiline (m) flag set.
- * @return {boolean} Value of the multiline flag
- */
-RegExpMirror.prototype.multiline = function() {
- return this.value_.multiline;
-};
-
-
-/**
- * Returns whether this regular expression has the sticky (y) flag set.
- * @return {boolean} Value of the sticky flag
- */
-RegExpMirror.prototype.sticky = function() {
- return this.value_.sticky;
-};
-
-
-/**
- * Returns whether this regular expression has the unicode (u) flag set.
- * @return {boolean} Value of the unicode flag
- */
-RegExpMirror.prototype.unicode = function() {
- return this.value_.unicode;
-};
-
-
-RegExpMirror.prototype.toText = function() {
- // Simpel to text which is used when on specialization in subclass.
- return "/" + this.source() + "/";
-};
-
-
-/**
- * Mirror object for error objects.
- * @param {Error} value The error object reflected by this mirror
- * @constructor
- * @extends ObjectMirror
- */
-function ErrorMirror(value) {
- %_CallFunction(this, value, ERROR_TYPE, ObjectMirror);
-}
-inherits(ErrorMirror, ObjectMirror);
-
-
-/**
- * Returns the message for this eror object.
- * @return {string or undefined} The message for this eror object
- */
-ErrorMirror.prototype.message = function() {
- return this.value_.message;
-};
-
-
-ErrorMirror.prototype.toText = function() {
- // Use the same text representation as in messages.js.
- var text;
- try {
- text = %_CallFunction(this.value_, builtins.$errorToString);
- } catch (e) {
- text = '#<Error>';
- }
- return text;
-};
-
-
-/**
- * Mirror object for a Promise object.
- * @param {Object} value The Promise object
- * @constructor
- * @extends ObjectMirror
- */
-function PromiseMirror(value) {
- %_CallFunction(this, value, PROMISE_TYPE, ObjectMirror);
-}
-inherits(PromiseMirror, ObjectMirror);
-
-
-function PromiseGetStatus_(value) {
- var status = %DebugGetProperty(value, builtins.$promiseStatus);
- if (status == 0) return "pending";
- if (status == 1) return "resolved";
- return "rejected";
-}
-
-
-function PromiseGetValue_(value) {
- return %DebugGetProperty(value, builtins.$promiseValue);
-}
-
-
-PromiseMirror.prototype.status = function() {
- return PromiseGetStatus_(this.value_);
-};
-
-
-PromiseMirror.prototype.promiseValue = function() {
- return MakeMirror(PromiseGetValue_(this.value_));
-};
-
-
-function MapMirror(value) {
- %_CallFunction(this, value, MAP_TYPE, ObjectMirror);
-}
-inherits(MapMirror, ObjectMirror);
-
-
-/**
- * Returns an array of key/value pairs of a map.
- * This will keep keys alive for WeakMaps.
- *
- * @param {number=} opt_limit Max elements to return.
- * @returns {Array.<Object>} Array of key/value pairs of a map.
- */
-MapMirror.prototype.entries = function(opt_limit) {
- var result = [];
-
- if (IS_WEAKMAP(this.value_)) {
- var entries = %GetWeakMapEntries(this.value_, opt_limit || 0);
- for (var i = 0; i < entries.length; i += 2) {
- result.push({
- key: entries[i],
- value: entries[i + 1]
- });
- }
- return result;
- }
-
- var iter = %_CallFunction(this.value_, builtins.$mapEntries);
- var next;
- while ((!opt_limit || result.length < opt_limit) &&
- !(next = iter.next()).done) {
- result.push({
- key: next.value[0],
- value: next.value[1]
- });
- }
- return result;
-};
-
-
-function SetMirror(value) {
- %_CallFunction(this, value, SET_TYPE, ObjectMirror);
-}
-inherits(SetMirror, ObjectMirror);
-
-
-function IteratorGetValues_(iter, next_function, opt_limit) {
- var result = [];
- var next;
- while ((!opt_limit || result.length < opt_limit) &&
- !(next = %_CallFunction(iter, next_function)).done) {
- result.push(next.value);
- }
- return result;
-}
-
-
-/**
- * Returns an array of elements of a set.
- * This will keep elements alive for WeakSets.
- *
- * @param {number=} opt_limit Max elements to return.
- * @returns {Array.<Object>} Array of elements of a set.
- */
-SetMirror.prototype.values = function(opt_limit) {
- if (IS_WEAKSET(this.value_)) {
- return %GetWeakSetValues(this.value_, opt_limit || 0);
- }
-
- var iter = %_CallFunction(this.value_, builtins.$setValues);
- return IteratorGetValues_(iter, builtins.$setIteratorNext, opt_limit);
-};
-
-
-function IteratorMirror(value) {
- %_CallFunction(this, value, ITERATOR_TYPE, ObjectMirror);
-}
-inherits(IteratorMirror, ObjectMirror);
-
-
-/**
- * Returns a preview of elements of an iterator.
- * Does not change the backing iterator state.
- *
- * @param {number=} opt_limit Max elements to return.
- * @returns {Array.<Object>} Array of elements of an iterator.
- */
-IteratorMirror.prototype.preview = function(opt_limit) {
- if (IS_MAP_ITERATOR(this.value_)) {
- return IteratorGetValues_(%MapIteratorClone(this.value_),
- builtins.$mapIteratorNext,
- opt_limit);
- } else if (IS_SET_ITERATOR(this.value_)) {
- return IteratorGetValues_(%SetIteratorClone(this.value_),
- builtins.$setIteratorNext,
- opt_limit);
- }
-};
-
-
-/**
- * Mirror object for a Generator object.
- * @param {Object} data The Generator object
- * @constructor
- * @extends Mirror
- */
-function GeneratorMirror(value) {
- %_CallFunction(this, value, GENERATOR_TYPE, ObjectMirror);
-}
-inherits(GeneratorMirror, ObjectMirror);
-
-
-function GeneratorGetStatus_(value) {
- var continuation = %GeneratorGetContinuation(value);
- if (continuation < 0) return "running";
- if (continuation == 0) return "closed";
- return "suspended";
-}
-
-
-GeneratorMirror.prototype.status = function() {
- return GeneratorGetStatus_(this.value_);
-};
-
-
-GeneratorMirror.prototype.sourcePosition_ = function() {
- return %GeneratorGetSourcePosition(this.value_);
-};
-
-
-GeneratorMirror.prototype.sourceLocation = function() {
- var pos = this.sourcePosition_();
- if (!IS_UNDEFINED(pos)) {
- var script = this.func().script();
- if (script) {
- return script.locationFromPosition(pos, true);
- }
- }
-};
-
-
-GeneratorMirror.prototype.func = function() {
- if (!this.func_) {
- this.func_ = MakeMirror(%GeneratorGetFunction(this.value_));
- }
- return this.func_;
-};
-
-
-GeneratorMirror.prototype.context = function() {
- if (!this.context_) {
- this.context_ = new ContextMirror(%GeneratorGetContext(this.value_));
- }
- return this.context_;
-};
-
-
-GeneratorMirror.prototype.receiver = function() {
- if (!this.receiver_) {
- this.receiver_ = MakeMirror(%GeneratorGetReceiver(this.value_));
- }
- return this.receiver_;
-};
-
-
-/**
- * Base mirror object for properties.
- * @param {ObjectMirror} mirror The mirror object having this property
- * @param {string} name The name of the property
- * @param {Array} details Details about the property
- * @constructor
- * @extends Mirror
- */
-function PropertyMirror(mirror, name, details) {
- %_CallFunction(this, PROPERTY_TYPE, Mirror);
- this.mirror_ = mirror;
- this.name_ = name;
- this.value_ = details[0];
- this.details_ = details[1];
- this.is_interceptor_ = details[2];
- if (details.length > 3) {
- this.exception_ = details[3];
- this.getter_ = details[4];
- this.setter_ = details[5];
- }
-}
-inherits(PropertyMirror, Mirror);
-
-
-PropertyMirror.prototype.isReadOnly = function() {
- return (this.attributes() & PropertyAttribute.ReadOnly) != 0;
-};
-
-
-PropertyMirror.prototype.isEnum = function() {
- return (this.attributes() & PropertyAttribute.DontEnum) == 0;
-};
-
-
-PropertyMirror.prototype.canDelete = function() {
- return (this.attributes() & PropertyAttribute.DontDelete) == 0;
-};
-
-
-PropertyMirror.prototype.name = function() {
- return this.name_;
-};
-
-
-PropertyMirror.prototype.isIndexed = function() {
- for (var i = 0; i < this.name_.length; i++) {
- if (this.name_[i] < '0' || '9' < this.name_[i]) {
- return false;
- }
- }
- return true;
-};
-
-
-PropertyMirror.prototype.value = function() {
- return MakeMirror(this.value_, false);
-};
-
-
-/**
- * Returns whether this property value is an exception.
- * @return {booolean} True if this property value is an exception
- */
-PropertyMirror.prototype.isException = function() {
- return this.exception_ ? true : false;
-};
-
-
-PropertyMirror.prototype.attributes = function() {
- return %DebugPropertyAttributesFromDetails(this.details_);
-};
-
-
-PropertyMirror.prototype.propertyType = function() {
- return %DebugPropertyTypeFromDetails(this.details_);
-};
-
-
-PropertyMirror.prototype.insertionIndex = function() {
- return %DebugPropertyIndexFromDetails(this.details_);
-};
-
-
-/**
- * Returns whether this property has a getter defined through __defineGetter__.
- * @return {booolean} True if this property has a getter
- */
-PropertyMirror.prototype.hasGetter = function() {
- return this.getter_ ? true : false;
-};
-
-
-/**
- * Returns whether this property has a setter defined through __defineSetter__.
- * @return {booolean} True if this property has a setter
- */
-PropertyMirror.prototype.hasSetter = function() {
- return this.setter_ ? true : false;
-};
-
-
-/**
- * Returns the getter for this property defined through __defineGetter__.
- * @return {Mirror} FunctionMirror reflecting the getter function or
- * UndefinedMirror if there is no getter for this property
- */
-PropertyMirror.prototype.getter = function() {
- if (this.hasGetter()) {
- return MakeMirror(this.getter_);
- } else {
- return GetUndefinedMirror();
- }
-};
-
-
-/**
- * Returns the setter for this property defined through __defineSetter__.
- * @return {Mirror} FunctionMirror reflecting the setter function or
- * UndefinedMirror if there is no setter for this property
- */
-PropertyMirror.prototype.setter = function() {
- if (this.hasSetter()) {
- return MakeMirror(this.setter_);
- } else {
- return GetUndefinedMirror();
- }
-};
-
-
-/**
- * Returns whether this property is natively implemented by the host or a set
- * through JavaScript code.
- * @return {boolean} True if the property is
- * UndefinedMirror if there is no setter for this property
- */
-PropertyMirror.prototype.isNative = function() {
- return this.is_interceptor_ ||
- ((this.propertyType() == PropertyType.AccessorConstant) &&
- !this.hasGetter() && !this.hasSetter());
-};
-
-
-/**
- * Mirror object for internal properties. Internal property reflects properties
- * not accessible from user code such as [[BoundThis]] in bound function.
- * Their names are merely symbolic.
- * @param {string} name The name of the property
- * @param {value} property value
- * @constructor
- * @extends Mirror
- */
-function InternalPropertyMirror(name, value) {
- %_CallFunction(this, INTERNAL_PROPERTY_TYPE, Mirror);
- this.name_ = name;
- this.value_ = value;
-}
-inherits(InternalPropertyMirror, Mirror);
-
-
-InternalPropertyMirror.prototype.name = function() {
- return this.name_;
-};
-
-
-InternalPropertyMirror.prototype.value = function() {
- return MakeMirror(this.value_, false);
-};
-
-
-var kFrameDetailsFrameIdIndex = 0;
-var kFrameDetailsReceiverIndex = 1;
-var kFrameDetailsFunctionIndex = 2;
-var kFrameDetailsArgumentCountIndex = 3;
-var kFrameDetailsLocalCountIndex = 4;
-var kFrameDetailsSourcePositionIndex = 5;
-var kFrameDetailsConstructCallIndex = 6;
-var kFrameDetailsAtReturnIndex = 7;
-var kFrameDetailsFlagsIndex = 8;
-var kFrameDetailsFirstDynamicIndex = 9;
-
-var kFrameDetailsNameIndex = 0;
-var kFrameDetailsValueIndex = 1;
-var kFrameDetailsNameValueSize = 2;
-
-var kFrameDetailsFlagDebuggerFrameMask = 1 << 0;
-var kFrameDetailsFlagOptimizedFrameMask = 1 << 1;
-var kFrameDetailsFlagInlinedFrameIndexMask = 7 << 2;
-
-/**
- * Wrapper for the frame details information retreived from the VM. The frame
- * details from the VM is an array with the following content. See runtime.cc
- * Runtime_GetFrameDetails.
- * 0: Id
- * 1: Receiver
- * 2: Function
- * 3: Argument count
- * 4: Local count
- * 5: Source position
- * 6: Construct call
- * 7: Is at return
- * 8: Flags (debugger frame, optimized frame, inlined frame index)
- * Arguments name, value
- * Locals name, value
- * Return value if any
- * @param {number} break_id Current break id
- * @param {number} index Frame number
- * @constructor
- */
-function FrameDetails(break_id, index) {
- this.break_id_ = break_id;
- this.details_ = %GetFrameDetails(break_id, index);
-}
-
-
-FrameDetails.prototype.frameId = function() {
- %CheckExecutionState(this.break_id_);
- return this.details_[kFrameDetailsFrameIdIndex];
-};
-
-
-FrameDetails.prototype.receiver = function() {
- %CheckExecutionState(this.break_id_);
- return this.details_[kFrameDetailsReceiverIndex];
-};
-
-
-FrameDetails.prototype.func = function() {
- %CheckExecutionState(this.break_id_);
- return this.details_[kFrameDetailsFunctionIndex];
-};
-
-
-FrameDetails.prototype.isConstructCall = function() {
- %CheckExecutionState(this.break_id_);
- return this.details_[kFrameDetailsConstructCallIndex];
-};
-
-
-FrameDetails.prototype.isAtReturn = function() {
- %CheckExecutionState(this.break_id_);
- return this.details_[kFrameDetailsAtReturnIndex];
-};
-
-
-FrameDetails.prototype.isDebuggerFrame = function() {
- %CheckExecutionState(this.break_id_);
- var f = kFrameDetailsFlagDebuggerFrameMask;
- return (this.details_[kFrameDetailsFlagsIndex] & f) == f;
-};
-
-
-FrameDetails.prototype.isOptimizedFrame = function() {
- %CheckExecutionState(this.break_id_);
- var f = kFrameDetailsFlagOptimizedFrameMask;
- return (this.details_[kFrameDetailsFlagsIndex] & f) == f;
-};
-
-
-FrameDetails.prototype.isInlinedFrame = function() {
- return this.inlinedFrameIndex() > 0;
-};
-
-
-FrameDetails.prototype.inlinedFrameIndex = function() {
- %CheckExecutionState(this.break_id_);
- var f = kFrameDetailsFlagInlinedFrameIndexMask;
- return (this.details_[kFrameDetailsFlagsIndex] & f) >> 2;
-};
-
-
-FrameDetails.prototype.argumentCount = function() {
- %CheckExecutionState(this.break_id_);
- return this.details_[kFrameDetailsArgumentCountIndex];
-};
-
-
-FrameDetails.prototype.argumentName = function(index) {
- %CheckExecutionState(this.break_id_);
- if (index >= 0 && index < this.argumentCount()) {
- return this.details_[kFrameDetailsFirstDynamicIndex +
- index * kFrameDetailsNameValueSize +
- kFrameDetailsNameIndex];
- }
-};
-
-
-FrameDetails.prototype.argumentValue = function(index) {
- %CheckExecutionState(this.break_id_);
- if (index >= 0 && index < this.argumentCount()) {
- return this.details_[kFrameDetailsFirstDynamicIndex +
- index * kFrameDetailsNameValueSize +
- kFrameDetailsValueIndex];
- }
-};
-
-
-FrameDetails.prototype.localCount = function() {
- %CheckExecutionState(this.break_id_);
- return this.details_[kFrameDetailsLocalCountIndex];
-};
-
-
-FrameDetails.prototype.sourcePosition = function() {
- %CheckExecutionState(this.break_id_);
- return this.details_[kFrameDetailsSourcePositionIndex];
-};
-
-
-FrameDetails.prototype.localName = function(index) {
- %CheckExecutionState(this.break_id_);
- if (index >= 0 && index < this.localCount()) {
- var locals_offset = kFrameDetailsFirstDynamicIndex +
- this.argumentCount() * kFrameDetailsNameValueSize;
- return this.details_[locals_offset +
- index * kFrameDetailsNameValueSize +
- kFrameDetailsNameIndex];
- }
-};
-
-
-FrameDetails.prototype.localValue = function(index) {
- %CheckExecutionState(this.break_id_);
- if (index >= 0 && index < this.localCount()) {
- var locals_offset = kFrameDetailsFirstDynamicIndex +
- this.argumentCount() * kFrameDetailsNameValueSize;
- return this.details_[locals_offset +
- index * kFrameDetailsNameValueSize +
- kFrameDetailsValueIndex];
- }
-};
-
-
-FrameDetails.prototype.returnValue = function() {
- %CheckExecutionState(this.break_id_);
- var return_value_offset =
- kFrameDetailsFirstDynamicIndex +
- (this.argumentCount() + this.localCount()) * kFrameDetailsNameValueSize;
- if (this.details_[kFrameDetailsAtReturnIndex]) {
- return this.details_[return_value_offset];
- }
-};
-
-
-FrameDetails.prototype.scopeCount = function() {
- if (IS_UNDEFINED(this.scopeCount_)) {
- this.scopeCount_ = %GetScopeCount(this.break_id_, this.frameId());
- }
- return this.scopeCount_;
-};
-
-
-FrameDetails.prototype.stepInPositionsImpl = function() {
- return %GetStepInPositions(this.break_id_, this.frameId());
-};
-
-
-/**
- * Mirror object for stack frames.
- * @param {number} break_id The break id in the VM for which this frame is
- valid
- * @param {number} index The frame index (top frame is index 0)
- * @constructor
- * @extends Mirror
- */
-function FrameMirror(break_id, index) {
- %_CallFunction(this, FRAME_TYPE, Mirror);
- this.break_id_ = break_id;
- this.index_ = index;
- this.details_ = new FrameDetails(break_id, index);
-}
-inherits(FrameMirror, Mirror);
-
-
-FrameMirror.prototype.details = function() {
- return this.details_;
-};
-
-
-FrameMirror.prototype.index = function() {
- return this.index_;
-};
-
-
-FrameMirror.prototype.func = function() {
- if (this.func_) {
- return this.func_;
- }
-
- // Get the function for this frame from the VM.
- var f = this.details_.func();
-
- // Create a function mirror. NOTE: MakeMirror cannot be used here as the
- // value returned from the VM might be a string if the function for the
- // frame is unresolved.
- if (IS_FUNCTION(f)) {
- return this.func_ = MakeMirror(f);
- } else {
- return new UnresolvedFunctionMirror(f);
- }
-};
-
-
-FrameMirror.prototype.receiver = function() {
- return MakeMirror(this.details_.receiver());
-};
-
-
-FrameMirror.prototype.isConstructCall = function() {
- return this.details_.isConstructCall();
-};
-
-
-FrameMirror.prototype.isAtReturn = function() {
- return this.details_.isAtReturn();
-};
-
-
-FrameMirror.prototype.isDebuggerFrame = function() {
- return this.details_.isDebuggerFrame();
-};
-
-
-FrameMirror.prototype.isOptimizedFrame = function() {
- return this.details_.isOptimizedFrame();
-};
-
-
-FrameMirror.prototype.isInlinedFrame = function() {
- return this.details_.isInlinedFrame();
-};
-
-
-FrameMirror.prototype.inlinedFrameIndex = function() {
- return this.details_.inlinedFrameIndex();
-};
-
-
-FrameMirror.prototype.argumentCount = function() {
- return this.details_.argumentCount();
-};
-
-
-FrameMirror.prototype.argumentName = function(index) {
- return this.details_.argumentName(index);
-};
-
-
-FrameMirror.prototype.argumentValue = function(index) {
- return MakeMirror(this.details_.argumentValue(index));
-};
-
-
-FrameMirror.prototype.localCount = function() {
- return this.details_.localCount();
-};
-
-
-FrameMirror.prototype.localName = function(index) {
- return this.details_.localName(index);
-};
-
-
-FrameMirror.prototype.localValue = function(index) {
- return MakeMirror(this.details_.localValue(index));
-};
-
-
-FrameMirror.prototype.returnValue = function() {
- return MakeMirror(this.details_.returnValue());
-};
-
-
-FrameMirror.prototype.sourcePosition = function() {
- return this.details_.sourcePosition();
-};
-
-
-FrameMirror.prototype.sourceLocation = function() {
- var func = this.func();
- if (func.resolved()) {
- var script = func.script();
- if (script) {
- return script.locationFromPosition(this.sourcePosition(), true);
- }
- }
-};
-
-
-FrameMirror.prototype.sourceLine = function() {
- var location = this.sourceLocation();
- if (location) {
- return location.line;
- }
-};
-
-
-FrameMirror.prototype.sourceColumn = function() {
- var location = this.sourceLocation();
- if (location) {
- return location.column;
- }
-};
-
-
-FrameMirror.prototype.sourceLineText = function() {
- var location = this.sourceLocation();
- if (location) {
- return location.sourceText();
- }
-};
-
-
-FrameMirror.prototype.scopeCount = function() {
- return this.details_.scopeCount();
-};
-
-
-FrameMirror.prototype.scope = function(index) {
- return new ScopeMirror(this, UNDEFINED, index);
-};
-
-
-FrameMirror.prototype.allScopes = function(opt_ignore_nested_scopes) {
- var scopeDetails = %GetAllScopesDetails(this.break_id_,
- this.details_.frameId(),
- this.details_.inlinedFrameIndex(),
- !!opt_ignore_nested_scopes);
- var result = [];
- for (var i = 0; i < scopeDetails.length; ++i) {
- result.push(new ScopeMirror(this, UNDEFINED, i, scopeDetails[i]));
- }
- return result;
-};
-
-
-FrameMirror.prototype.stepInPositions = function() {
- var script = this.func().script();
- var funcOffset = this.func().sourcePosition_();
-
- var stepInRaw = this.details_.stepInPositionsImpl();
- var result = [];
- if (stepInRaw) {
- for (var i = 0; i < stepInRaw.length; i++) {
- var posStruct = {};
- var offset = script.locationFromPosition(funcOffset + stepInRaw[i],
- true);
- serializeLocationFields(offset, posStruct);
- var item = {
- position: posStruct
- };
- result.push(item);
- }
- }
-
- return result;
-};
-
-
-FrameMirror.prototype.evaluate = function(source, disable_break,
- opt_context_object) {
- return MakeMirror(%DebugEvaluate(this.break_id_,
- this.details_.frameId(),
- this.details_.inlinedFrameIndex(),
- source,
- Boolean(disable_break),
- opt_context_object));
-};
-
-
-FrameMirror.prototype.invocationText = function() {
- // Format frame invoaction (receiver, function and arguments).
- var result = '';
- var func = this.func();
- var receiver = this.receiver();
- if (this.isConstructCall()) {
- // For constructor frames display new followed by the function name.
- result += 'new ';
- result += func.name() ? func.name() : '[anonymous]';
- } else if (this.isDebuggerFrame()) {
- result += '[debugger]';
- } else {
- // If the receiver has a className which is 'global' don't display it.
- var display_receiver =
- !receiver.className || (receiver.className() != 'global');
- if (display_receiver) {
- result += receiver.toText();
- }
- // Try to find the function as a property in the receiver. Include the
- // prototype chain in the lookup.
- var property = GetUndefinedMirror();
- if (receiver.isObject()) {
- for (var r = receiver;
- !r.isNull() && property.isUndefined();
- r = r.protoObject()) {
- property = r.lookupProperty(func);
- }
- }
- if (!property.isUndefined()) {
- // The function invoked was found on the receiver. Use the property name
- // for the backtrace.
- if (!property.isIndexed()) {
- if (display_receiver) {
- result += '.';
- }
- result += property.name();
- } else {
- result += '[';
- result += property.name();
- result += ']';
- }
- // Also known as - if the name in the function doesn't match the name
- // under which it was looked up.
- if (func.name() && func.name() != property.name()) {
- result += '(aka ' + func.name() + ')';
- }
- } else {
- // The function invoked was not found on the receiver. Use the function
- // name if available for the backtrace.
- if (display_receiver) {
- result += '.';
- }
- result += func.name() ? func.name() : '[anonymous]';
- }
- }
-
- // Render arguments for normal frames.
- if (!this.isDebuggerFrame()) {
- result += '(';
- for (var i = 0; i < this.argumentCount(); i++) {
- if (i != 0) result += ', ';
- if (this.argumentName(i)) {
- result += this.argumentName(i);
- result += '=';
- }
- result += this.argumentValue(i).toText();
- }
- result += ')';
- }
-
- if (this.isAtReturn()) {
- result += ' returning ';
- result += this.returnValue().toText();
- }
-
- return result;
-};
-
-
-FrameMirror.prototype.sourceAndPositionText = function() {
- // Format source and position.
- var result = '';
- var func = this.func();
- if (func.resolved()) {
- var script = func.script();
- if (script) {
- if (script.name()) {
- result += script.name();
- } else {
- result += '[unnamed]';
- }
- if (!this.isDebuggerFrame()) {
- var location = this.sourceLocation();
- result += ' line ';
- result += !IS_UNDEFINED(location) ? (location.line + 1) : '?';
- result += ' column ';
- result += !IS_UNDEFINED(location) ? (location.column + 1) : '?';
- if (!IS_UNDEFINED(this.sourcePosition())) {
- result += ' (position ' + (this.sourcePosition() + 1) + ')';
- }
- }
- } else {
- result += '[no source]';
- }
- } else {
- result += '[unresolved]';
- }
-
- return result;
-};
-
-
-FrameMirror.prototype.localsText = function() {
- // Format local variables.
- var result = '';
- var locals_count = this.localCount();
- if (locals_count > 0) {
- for (var i = 0; i < locals_count; ++i) {
- result += ' var ';
- result += this.localName(i);
- result += ' = ';
- result += this.localValue(i).toText();
- if (i < locals_count - 1) result += '\n';
- }
- }
-
- return result;
-};
-
-
-FrameMirror.prototype.restart = function() {
- var result = %LiveEditRestartFrame(this.break_id_, this.index_);
- if (IS_UNDEFINED(result)) {
- result = "Failed to find requested frame";
- }
- return result;
-};
-
-
-FrameMirror.prototype.toText = function(opt_locals) {
- var result = '';
- result += '#' + (this.index() <= 9 ? '0' : '') + this.index();
- result += ' ';
- result += this.invocationText();
- result += ' ';
- result += this.sourceAndPositionText();
- if (opt_locals) {
- result += '\n';
- result += this.localsText();
- }
- return result;
-};
-
-
-var kScopeDetailsTypeIndex = 0;
-var kScopeDetailsObjectIndex = 1;
-
-function ScopeDetails(frame, fun, index, opt_details) {
- if (frame) {
- this.break_id_ = frame.break_id_;
- this.details_ = opt_details ||
- %GetScopeDetails(frame.break_id_,
- frame.details_.frameId(),
- frame.details_.inlinedFrameIndex(),
- index);
- this.frame_id_ = frame.details_.frameId();
- this.inlined_frame_id_ = frame.details_.inlinedFrameIndex();
- } else {
- this.details_ = opt_details || %GetFunctionScopeDetails(fun.value(), index);
- this.fun_value_ = fun.value();
- this.break_id_ = undefined;
- }
- this.index_ = index;
-}
-
-
-ScopeDetails.prototype.type = function() {
- if (!IS_UNDEFINED(this.break_id_)) {
- %CheckExecutionState(this.break_id_);
- }
- return this.details_[kScopeDetailsTypeIndex];
-};
-
-
-ScopeDetails.prototype.object = function() {
- if (!IS_UNDEFINED(this.break_id_)) {
- %CheckExecutionState(this.break_id_);
- }
- return this.details_[kScopeDetailsObjectIndex];
-};
-
-
-ScopeDetails.prototype.setVariableValueImpl = function(name, new_value) {
- var raw_res;
- if (!IS_UNDEFINED(this.break_id_)) {
- %CheckExecutionState(this.break_id_);
- raw_res = %SetScopeVariableValue(this.break_id_, this.frame_id_,
- this.inlined_frame_id_, this.index_, name, new_value);
- } else {
- raw_res = %SetScopeVariableValue(this.fun_value_, null, null, this.index_,
- name, new_value);
- }
- if (!raw_res) {
- throw new Error("Failed to set variable value");
- }
-};
-
-
-/**
- * Mirror object for scope of frame or function. Either frame or function must
- * be specified.
- * @param {FrameMirror} frame The frame this scope is a part of
- * @param {FunctionMirror} function The function this scope is a part of
- * @param {number} index The scope index in the frame
- * @param {Array=} opt_details Raw scope details data
- * @constructor
- * @extends Mirror
- */
-function ScopeMirror(frame, function, index, opt_details) {
- %_CallFunction(this, SCOPE_TYPE, Mirror);
- if (frame) {
- this.frame_index_ = frame.index_;
- } else {
- this.frame_index_ = undefined;
- }
- this.scope_index_ = index;
- this.details_ = new ScopeDetails(frame, function, index, opt_details);
-}
-inherits(ScopeMirror, Mirror);
-
-
-ScopeMirror.prototype.details = function() {
- return this.details_;
-};
-
-
-ScopeMirror.prototype.frameIndex = function() {
- return this.frame_index_;
-};
-
-
-ScopeMirror.prototype.scopeIndex = function() {
- return this.scope_index_;
-};
-
-
-ScopeMirror.prototype.scopeType = function() {
- return this.details_.type();
-};
-
-
-ScopeMirror.prototype.scopeObject = function() {
- // For local, closure and script scopes create a transient mirror
- // as these objects are created on the fly materializing the local
- // or closure scopes and therefore will not preserve identity.
- var transient = this.scopeType() == ScopeType.Local ||
- this.scopeType() == ScopeType.Closure ||
- this.scopeType() == ScopeType.Script;
- return MakeMirror(this.details_.object(), transient);
-};
-
-
-ScopeMirror.prototype.setVariableValue = function(name, new_value) {
- this.details_.setVariableValueImpl(name, new_value);
-};
-
-
-/**
- * Mirror object for script source.
- * @param {Script} script The script object
- * @constructor
- * @extends Mirror
- */
-function ScriptMirror(script) {
- %_CallFunction(this, SCRIPT_TYPE, Mirror);
- this.script_ = script;
- this.context_ = new ContextMirror(script.context_data);
- this.allocateHandle_();
-}
-inherits(ScriptMirror, Mirror);
-
-
-ScriptMirror.prototype.value = function() {
- return this.script_;
-};
-
-
-ScriptMirror.prototype.name = function() {
- return this.script_.name || this.script_.nameOrSourceURL();
-};
-
-
-ScriptMirror.prototype.id = function() {
- return this.script_.id;
-};
-
-
-ScriptMirror.prototype.source = function() {
- return this.script_.source;
-};
-
-
-ScriptMirror.prototype.setSource = function(source) {
- %DebugSetScriptSource(this.script_, source);
-};
-
-
-ScriptMirror.prototype.lineOffset = function() {
- return this.script_.line_offset;
-};
-
-
-ScriptMirror.prototype.columnOffset = function() {
- return this.script_.column_offset;
-};
-
-
-ScriptMirror.prototype.data = function() {
- return this.script_.data;
-};
-
-
-ScriptMirror.prototype.scriptType = function() {
- return this.script_.type;
-};
-
-
-ScriptMirror.prototype.compilationType = function() {
- return this.script_.compilation_type;
-};
-
-
-ScriptMirror.prototype.lineCount = function() {
- return this.script_.lineCount();
-};
-
-
-ScriptMirror.prototype.locationFromPosition = function(
- position, include_resource_offset) {
- return this.script_.locationFromPosition(position, include_resource_offset);
-};
-
-
-ScriptMirror.prototype.sourceSlice = function (opt_from_line, opt_to_line) {
- return this.script_.sourceSlice(opt_from_line, opt_to_line);
-};
-
-
-ScriptMirror.prototype.context = function() {
- return this.context_;
-};
-
-
-ScriptMirror.prototype.evalFromScript = function() {
- return MakeMirror(this.script_.eval_from_script);
-};
-
-
-ScriptMirror.prototype.evalFromFunctionName = function() {
- return MakeMirror(this.script_.eval_from_function_name);
-};
-
-
-ScriptMirror.prototype.evalFromLocation = function() {
- var eval_from_script = this.evalFromScript();
- if (!eval_from_script.isUndefined()) {
- var position = this.script_.eval_from_script_position;
- return eval_from_script.locationFromPosition(position, true);
- }
-};
-
-
-ScriptMirror.prototype.toText = function() {
- var result = '';
- result += this.name();
- result += ' (lines: ';
- if (this.lineOffset() > 0) {
- result += this.lineOffset();
- result += '-';
- result += this.lineOffset() + this.lineCount() - 1;
- } else {
- result += this.lineCount();
- }
- result += ')';
- return result;
-};
-
-
-/**
- * Mirror object for context.
- * @param {Object} data The context data
- * @constructor
- * @extends Mirror
- */
-function ContextMirror(data) {
- %_CallFunction(this, CONTEXT_TYPE, Mirror);
- this.data_ = data;
- this.allocateHandle_();
-}
-inherits(ContextMirror, Mirror);
-
-
-ContextMirror.prototype.data = function() {
- return this.data_;
-};
-
-
-/**
- * Returns a mirror serializer
- *
- * @param {boolean} details Set to true to include details
- * @param {Object} options Options comtrolling the serialization
- * The following options can be set:
- * includeSource: include ths full source of scripts
- * @returns {MirrorSerializer} mirror serializer
- */
-function MakeMirrorSerializer(details, options) {
- return new JSONProtocolSerializer(details, options);
-}
-
-
-/**
- * Object for serializing a mirror objects and its direct references.
- * @param {boolean} details Indicates whether to include details for the mirror
- * serialized
- * @constructor
- */
-function JSONProtocolSerializer(details, options) {
- this.details_ = details;
- this.options_ = options;
- this.mirrors_ = [ ];
-}
-
-
-/**
- * Returns a serialization of an object reference. The referenced object are
- * added to the serialization state.
- *
- * @param {Mirror} mirror The mirror to serialize
- * @returns {String} JSON serialization
- */
-JSONProtocolSerializer.prototype.serializeReference = function(mirror) {
- return this.serialize_(mirror, true, true);
-};
-
-
-/**
- * Returns a serialization of an object value. The referenced objects are
- * added to the serialization state.
- *
- * @param {Mirror} mirror The mirror to serialize
- * @returns {String} JSON serialization
- */
-JSONProtocolSerializer.prototype.serializeValue = function(mirror) {
- var json = this.serialize_(mirror, false, true);
- return json;
-};
-
-
-/**
- * Returns a serialization of all the objects referenced.
- *
- * @param {Mirror} mirror The mirror to serialize.
- * @returns {Array.<Object>} Array of the referenced objects converted to
- * protcol objects.
- */
-JSONProtocolSerializer.prototype.serializeReferencedObjects = function() {
- // Collect the protocol representation of the referenced objects in an array.
- var content = [];
-
- // Get the number of referenced objects.
- var count = this.mirrors_.length;
-
- for (var i = 0; i < count; i++) {
- content.push(this.serialize_(this.mirrors_[i], false, false));
- }
-
- return content;
-};
-
-
-JSONProtocolSerializer.prototype.includeSource_ = function() {
- return this.options_ && this.options_.includeSource;
-};
-
-
-JSONProtocolSerializer.prototype.inlineRefs_ = function() {
- return this.options_ && this.options_.inlineRefs;
-};
-
-
-JSONProtocolSerializer.prototype.maxStringLength_ = function() {
- if (IS_UNDEFINED(this.options_) ||
- IS_UNDEFINED(this.options_.maxStringLength)) {
- return kMaxProtocolStringLength;
- }
- return this.options_.maxStringLength;
-};
-
-
-JSONProtocolSerializer.prototype.add_ = function(mirror) {
- // If this mirror is already in the list just return.
- for (var i = 0; i < this.mirrors_.length; i++) {
- if (this.mirrors_[i] === mirror) {
- return;
- }
- }
-
- // Add the mirror to the list of mirrors to be serialized.
- this.mirrors_.push(mirror);
-};
-
-
-/**
- * Formats mirror object to protocol reference object with some data that can
- * be used to display the value in debugger.
- * @param {Mirror} mirror Mirror to serialize.
- * @return {Object} Protocol reference object.
- */
-JSONProtocolSerializer.prototype.serializeReferenceWithDisplayData_ =
- function(mirror) {
- var o = {};
- o.ref = mirror.handle();
- o.type = mirror.type();
- switch (mirror.type()) {
- case UNDEFINED_TYPE:
- case NULL_TYPE:
- case BOOLEAN_TYPE:
- case NUMBER_TYPE:
- o.value = mirror.value();
- break;
- case STRING_TYPE:
- o.value = mirror.getTruncatedValue(this.maxStringLength_());
- break;
- case SYMBOL_TYPE:
- o.description = mirror.description();
- break;
- case FUNCTION_TYPE:
- o.name = mirror.name();
- o.inferredName = mirror.inferredName();
- if (mirror.script()) {
- o.scriptId = mirror.script().id();
- }
- break;
- case ERROR_TYPE:
- case REGEXP_TYPE:
- o.value = mirror.toText();
- break;
- case OBJECT_TYPE:
- o.className = mirror.className();
- break;
- }
- return o;
-};
-
-
-JSONProtocolSerializer.prototype.serialize_ = function(mirror, reference,
- details) {
- // If serializing a reference to a mirror just return the reference and add
- // the mirror to the referenced mirrors.
- if (reference &&
- (mirror.isValue() || mirror.isScript() || mirror.isContext())) {
- if (this.inlineRefs_() && mirror.isValue()) {
- return this.serializeReferenceWithDisplayData_(mirror);
- } else {
- this.add_(mirror);
- return {'ref' : mirror.handle()};
- }
- }
-
- // Collect the JSON property/value pairs.
- var content = {};
-
- // Add the mirror handle.
- if (mirror.isValue() || mirror.isScript() || mirror.isContext()) {
- content.handle = mirror.handle();
- }
-
- // Always add the type.
- content.type = mirror.type();
-
- switch (mirror.type()) {
- case UNDEFINED_TYPE:
- case NULL_TYPE:
- // Undefined and null are represented just by their type.
- break;
-
- case BOOLEAN_TYPE:
- // Boolean values are simply represented by their value.
- content.value = mirror.value();
- break;
-
- case NUMBER_TYPE:
- // Number values are simply represented by their value.
- content.value = NumberToJSON_(mirror.value());
- break;
-
- case STRING_TYPE:
- // String values might have their value cropped to keep down size.
- if (this.maxStringLength_() != -1 &&
- mirror.length() > this.maxStringLength_()) {
- var str = mirror.getTruncatedValue(this.maxStringLength_());
- content.value = str;
- content.fromIndex = 0;
- content.toIndex = this.maxStringLength_();
- } else {
- content.value = mirror.value();
- }
- content.length = mirror.length();
- break;
-
- case SYMBOL_TYPE:
- content.description = mirror.description();
- break;
-
- case OBJECT_TYPE:
- case FUNCTION_TYPE:
- case ERROR_TYPE:
- case REGEXP_TYPE:
- case PROMISE_TYPE:
- case GENERATOR_TYPE:
- // Add object representation.
- this.serializeObject_(mirror, content, details);
- break;
-
- case PROPERTY_TYPE:
- case INTERNAL_PROPERTY_TYPE:
- throw new Error('PropertyMirror cannot be serialized independently');
- break;
-
- case FRAME_TYPE:
- // Add object representation.
- this.serializeFrame_(mirror, content);
- break;
-
- case SCOPE_TYPE:
- // Add object representation.
- this.serializeScope_(mirror, content);
- break;
-
- case SCRIPT_TYPE:
- // Script is represented by id, name and source attributes.
- if (mirror.name()) {
- content.name = mirror.name();
- }
- content.id = mirror.id();
- content.lineOffset = mirror.lineOffset();
- content.columnOffset = mirror.columnOffset();
- content.lineCount = mirror.lineCount();
- if (mirror.data()) {
- content.data = mirror.data();
- }
- if (this.includeSource_()) {
- content.source = mirror.source();
- } else {
- var sourceStart = mirror.source().substring(0, 80);
- content.sourceStart = sourceStart;
- }
- content.sourceLength = mirror.source().length;
- content.scriptType = mirror.scriptType();
- content.compilationType = mirror.compilationType();
- // For compilation type eval emit information on the script from which
- // eval was called if a script is present.
- if (mirror.compilationType() == 1 &&
- mirror.evalFromScript()) {
- content.evalFromScript =
- this.serializeReference(mirror.evalFromScript());
- var evalFromLocation = mirror.evalFromLocation();
- if (evalFromLocation) {
- content.evalFromLocation = { line: evalFromLocation.line,
- column: evalFromLocation.column };
- }
- if (mirror.evalFromFunctionName()) {
- content.evalFromFunctionName = mirror.evalFromFunctionName();
- }
- }
- if (mirror.context()) {
- content.context = this.serializeReference(mirror.context());
- }
- break;
-
- case CONTEXT_TYPE:
- content.data = mirror.data();
- break;
- }
-
- // Always add the text representation.
- content.text = mirror.toText();
-
- // Create and return the JSON string.
- return content;
-};
-
-
-/**
- * Serialize object information to the following JSON format.
- *
- * {"className":"<class name>",
- * "constructorFunction":{"ref":<number>},
- * "protoObject":{"ref":<number>},
- * "prototypeObject":{"ref":<number>},
- * "namedInterceptor":<boolean>,
- * "indexedInterceptor":<boolean>,
- * "properties":[<properties>],
- * "internalProperties":[<internal properties>]}
- */
-JSONProtocolSerializer.prototype.serializeObject_ = function(mirror, content,
- details) {
- // Add general object properties.
- content.className = mirror.className();
- content.constructorFunction =
- this.serializeReference(mirror.constructorFunction());
- content.protoObject = this.serializeReference(mirror.protoObject());
- content.prototypeObject = this.serializeReference(mirror.prototypeObject());
-
- // Add flags to indicate whether there are interceptors.
- if (mirror.hasNamedInterceptor()) {
- content.namedInterceptor = true;
- }
- if (mirror.hasIndexedInterceptor()) {
- content.indexedInterceptor = true;
- }
-
- if (mirror.isFunction()) {
- // Add function specific properties.
- content.name = mirror.name();
- if (!IS_UNDEFINED(mirror.inferredName())) {
- content.inferredName = mirror.inferredName();
- }
- content.resolved = mirror.resolved();
- if (mirror.resolved()) {
- content.source = mirror.source();
- }
- if (mirror.script()) {
- content.script = this.serializeReference(mirror.script());
- content.scriptId = mirror.script().id();
-
- serializeLocationFields(mirror.sourceLocation(), content);
- }
-
- content.scopes = [];
- for (var i = 0; i < mirror.scopeCount(); i++) {
- var scope = mirror.scope(i);
- content.scopes.push({
- type: scope.scopeType(),
- index: i
- });
- }
- }
-
- if (mirror.isGenerator()) {
- // Add generator specific properties.
-
- // Either 'running', 'closed', or 'suspended'.
- content.status = mirror.status();
-
- content.func = this.serializeReference(mirror.func())
- content.receiver = this.serializeReference(mirror.receiver())
-
- // If the generator is suspended, the content add line/column properties.
- serializeLocationFields(mirror.sourceLocation(), content);
-
- // TODO(wingo): Also serialize a reference to the context (scope chain).
- }
-
- if (mirror.isDate()) {
- // Add date specific properties.
- content.value = mirror.value();
- }
-
- if (mirror.isPromise()) {
- // Add promise specific properties.
- content.status = mirror.status();
- content.promiseValue = this.serializeReference(mirror.promiseValue());
- }
-
- // Add actual properties - named properties followed by indexed properties.
- var propertyNames = mirror.propertyNames(PropertyKind.Named);
- var propertyIndexes = mirror.propertyNames(PropertyKind.Indexed);
- var p = new Array(propertyNames.length + propertyIndexes.length);
- for (var i = 0; i < propertyNames.length; i++) {
- var propertyMirror = mirror.property(propertyNames[i]);
- p[i] = this.serializeProperty_(propertyMirror);
- if (details) {
- this.add_(propertyMirror.value());
- }
- }
- for (var i = 0; i < propertyIndexes.length; i++) {
- var propertyMirror = mirror.property(propertyIndexes[i]);
- p[propertyNames.length + i] = this.serializeProperty_(propertyMirror);
- if (details) {
- this.add_(propertyMirror.value());
- }
- }
- content.properties = p;
-
- var internalProperties = mirror.internalProperties();
- if (internalProperties.length > 0) {
- var ip = [];
- for (var i = 0; i < internalProperties.length; i++) {
- ip.push(this.serializeInternalProperty_(internalProperties[i]));
- }
- content.internalProperties = ip;
- }
-};
-
-
-/**
- * Serialize location information to the following JSON format:
- *
- * "position":"<position>",
- * "line":"<line>",
- * "column":"<column>",
- *
- * @param {SourceLocation} location The location to serialize, may be undefined.
- */
-function serializeLocationFields (location, content) {
- if (!location) {
- return;
- }
- content.position = location.position;
- var line = location.line;
- if (!IS_UNDEFINED(line)) {
- content.line = line;
- }
- var column = location.column;
- if (!IS_UNDEFINED(column)) {
- content.column = column;
- }
-}
-
-
-/**
- * Serialize property information to the following JSON format for building the
- * array of properties.
- *
- * {"name":"<property name>",
- * "attributes":<number>,
- * "propertyType":<number>,
- * "ref":<number>}
- *
- * If the attribute for the property is PropertyAttribute.None it is not added.
- * Here are a couple of examples.
- *
- * {"name":"hello","propertyType":0,"ref":1}
- * {"name":"length","attributes":7,"propertyType":3,"ref":2}
- *
- * @param {PropertyMirror} propertyMirror The property to serialize.
- * @returns {Object} Protocol object representing the property.
- */
-JSONProtocolSerializer.prototype.serializeProperty_ = function(propertyMirror) {
- var result = {};
-
- result.name = propertyMirror.name();
- var propertyValue = propertyMirror.value();
- if (this.inlineRefs_() && propertyValue.isValue()) {
- result.value = this.serializeReferenceWithDisplayData_(propertyValue);
- } else {
- if (propertyMirror.attributes() != PropertyAttribute.None) {
- result.attributes = propertyMirror.attributes();
- }
- result.propertyType = propertyMirror.propertyType();
- result.ref = propertyValue.handle();
- }
- return result;
-};
-
-
-/**
- * Serialize internal property information to the following JSON format for
- * building the array of properties.
- *
- * {"name":"<property name>",
- * "ref":<number>}
- *
- * {"name":"[[BoundThis]]","ref":117}
- *
- * @param {InternalPropertyMirror} propertyMirror The property to serialize.
- * @returns {Object} Protocol object representing the property.
- */
-JSONProtocolSerializer.prototype.serializeInternalProperty_ =
- function(propertyMirror) {
- var result = {};
-
- result.name = propertyMirror.name();
- var propertyValue = propertyMirror.value();
- if (this.inlineRefs_() && propertyValue.isValue()) {
- result.value = this.serializeReferenceWithDisplayData_(propertyValue);
- } else {
- result.ref = propertyValue.handle();
- }
- return result;
-};
-
-
-JSONProtocolSerializer.prototype.serializeFrame_ = function(mirror, content) {
- content.index = mirror.index();
- content.receiver = this.serializeReference(mirror.receiver());
- var func = mirror.func();
- content.func = this.serializeReference(func);
- var script = func.script();
- if (script) {
- content.script = this.serializeReference(script);
- }
- content.constructCall = mirror.isConstructCall();
- content.atReturn = mirror.isAtReturn();
- if (mirror.isAtReturn()) {
- content.returnValue = this.serializeReference(mirror.returnValue());
- }
- content.debuggerFrame = mirror.isDebuggerFrame();
- var x = new Array(mirror.argumentCount());
- for (var i = 0; i < mirror.argumentCount(); i++) {
- var arg = {};
- var argument_name = mirror.argumentName(i);
- if (argument_name) {
- arg.name = argument_name;
- }
- arg.value = this.serializeReference(mirror.argumentValue(i));
- x[i] = arg;
- }
- content.arguments = x;
- var x = new Array(mirror.localCount());
- for (var i = 0; i < mirror.localCount(); i++) {
- var local = {};
- local.name = mirror.localName(i);
- local.value = this.serializeReference(mirror.localValue(i));
- x[i] = local;
- }
- content.locals = x;
- serializeLocationFields(mirror.sourceLocation(), content);
- var source_line_text = mirror.sourceLineText();
- if (!IS_UNDEFINED(source_line_text)) {
- content.sourceLineText = source_line_text;
- }
-
- content.scopes = [];
- for (var i = 0; i < mirror.scopeCount(); i++) {
- var scope = mirror.scope(i);
- content.scopes.push({
- type: scope.scopeType(),
- index: i
- });
- }
-};
-
-
-JSONProtocolSerializer.prototype.serializeScope_ = function(mirror, content) {
- content.index = mirror.scopeIndex();
- content.frameIndex = mirror.frameIndex();
- content.type = mirror.scopeType();
- content.object = this.inlineRefs_() ?
- this.serializeValue(mirror.scopeObject()) :
- this.serializeReference(mirror.scopeObject());
-};
-
-
-/**
- * Convert a number to a protocol value. For all finite numbers the number
- * itself is returned. For non finite numbers NaN, Infinite and
- * -Infinite the string representation "NaN", "Infinite" or "-Infinite"
- * (not including the quotes) is returned.
- *
- * @param {number} value The number value to convert to a protocol value.
- * @returns {number|string} Protocol value.
- */
-function NumberToJSON_(value) {
- if (isNaN(value)) {
- return 'NaN';
- }
- if (!NUMBER_IS_FINITE(value)) {
- if (value > 0) {
- return 'Infinity';
- } else {
- return '-Infinity';
- }
- }
- return value;
-}
#include "src/compiler.h"
#include "src/cpu-profiler.h"
#include "src/date.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/deoptimizer.h"
#include "src/elements.h"
#include "src/execution.h"
#include "src/pending-compilation-error-handler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/handles.h"
#include "src/isolate.h"
#include "src/messages.h"
#include "src/ppc/assembler-ppc.h"
#include "src/assembler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
namespace v8 {
#if V8_TARGET_ARCH_PPC
#include "src/codegen.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/deoptimizer.h"
#include "src/full-codegen/full-codegen.h"
#include "src/interpreter/bytecodes.h"
+++ /dev/null
-// 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"
-
-#if V8_TARGET_ARCH_PPC
-
-#include "src/codegen.h"
-#include "src/debug.h"
-
-namespace v8 {
-namespace internal {
-
-#define __ ACCESS_MASM(masm)
-
-
-void EmitDebugBreakSlot(MacroAssembler* masm) {
- Label check_size;
- __ bind(&check_size);
- for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
- __ nop(MacroAssembler::DEBUG_BREAK_NOP);
- }
- DCHECK_EQ(Assembler::kDebugBreakSlotInstructions,
- masm->InstructionsGeneratedSince(&check_size));
-}
-
-
-void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode,
- int call_argc) {
- // Generate enough nop's to make space for a call instruction. Avoid emitting
- // the trampoline pool in the debug break slot code.
- Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm);
- masm->RecordDebugBreakSlot(mode, call_argc);
- EmitDebugBreakSlot(masm);
-}
-
-
-void DebugCodegen::ClearDebugBreakSlot(Address pc) {
- CodePatcher patcher(pc, Assembler::kDebugBreakSlotInstructions);
- EmitDebugBreakSlot(patcher.masm());
-}
-
-
-void DebugCodegen::PatchDebugBreakSlot(Address pc, Handle<Code> code) {
- DCHECK_EQ(Code::BUILTIN, code->kind());
- CodePatcher patcher(pc, Assembler::kDebugBreakSlotInstructions);
- // Patch the code changing the debug break slot code from
- //
- // ori r3, r3, 0
- // ori r3, r3, 0
- // ori r3, r3, 0
- // ori r3, r3, 0
- // ori r3, r3, 0
- //
- // to a call to the debug break code, using a FIXED_SEQUENCE.
- //
- // mov r0, <address>
- // mtlr r0
- // blrl
- //
- Assembler::BlockTrampolinePoolScope block_trampoline_pool(patcher.masm());
- patcher.masm()->mov(v8::internal::r0,
- Operand(reinterpret_cast<intptr_t>(code->entry())));
- patcher.masm()->mtctr(v8::internal::r0);
- patcher.masm()->bctrl();
-}
-
-
-void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
- DebugBreakCallHelperMode mode) {
- __ RecordComment("Debug break");
- {
- FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
-
- // Load padding words on stack.
- __ LoadSmiLiteral(ip, Smi::FromInt(LiveEdit::kFramePaddingValue));
- for (int i = 0; i < LiveEdit::kFramePaddingInitialSize; i++) {
- __ push(ip);
- }
- __ LoadSmiLiteral(ip, Smi::FromInt(LiveEdit::kFramePaddingInitialSize));
- __ push(ip);
-
- if (mode == SAVE_RESULT_REGISTER) __ push(r3);
-
- __ mov(r3, Operand::Zero()); // no arguments
- __ mov(r4,
- Operand(ExternalReference(
- Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate())));
-
- CEntryStub ceb(masm->isolate(), 1);
- __ CallStub(&ceb);
-
- if (FLAG_debug_code) {
- for (int i = 0; i < kNumJSCallerSaved; i++) {
- Register reg = {JSCallerSavedCode(i)};
- __ mov(reg, Operand(kDebugZapValue));
- }
- }
-
- if (mode == SAVE_RESULT_REGISTER) __ pop(r3);
-
- // Don't bother removing padding bytes pushed on the stack
- // as the frame is going to be restored right away.
-
- // Leave the internal frame.
- }
-
- // Now that the break point has been handled, resume normal execution by
- // jumping to the target address intended by the caller and that was
- // overwritten by the address of DebugBreakXXX.
- ExternalReference after_break_target =
- ExternalReference::debug_after_break_target_address(masm->isolate());
- __ mov(ip, Operand(after_break_target));
- __ LoadP(ip, MemOperand(ip));
- __ JumpToJSEntry(ip);
-}
-
-
-void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
- __ Ret();
-}
-
-
-void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
- ExternalReference restarter_frame_function_slot =
- ExternalReference::debug_restarter_frame_function_pointer_address(
- masm->isolate());
- __ mov(ip, Operand(restarter_frame_function_slot));
- __ li(r4, Operand::Zero());
- __ StoreP(r4, MemOperand(ip, 0));
-
- // Load the function pointer off of our current stack frame.
- __ LoadP(r4, MemOperand(fp, StandardFrameConstants::kConstantPoolOffset -
- kPointerSize));
-
- // Pop return address and frame
- __ LeaveFrame(StackFrame::INTERNAL);
-
- // Load context from the function.
- __ LoadP(cp, FieldMemOperand(r4, JSFunction::kContextOffset));
-
- // Get function code.
- __ LoadP(ip, FieldMemOperand(r4, JSFunction::kSharedFunctionInfoOffset));
- __ LoadP(ip, FieldMemOperand(ip, SharedFunctionInfo::kCodeOffset));
- __ addi(ip, ip, Operand(Code::kHeaderSize - kHeapObjectTag));
-
- // Re-run JSFunction, r4 is function, cp is context.
- __ Jump(ip);
-}
-
-
-const bool LiveEdit::kFrameDropperSupported = true;
-
-#undef __
-} // namespace internal
-} // namespace v8
-
-#endif // V8_TARGET_ARCH_PPC
#include "src/bootstrapper.h"
#include "src/codegen.h"
#include "src/cpu-profiler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/runtime/runtime.h"
namespace v8 {
#include "src/profile-generator-inl.h"
#include "src/compiler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/deoptimizer.h"
#include "src/global-handles.h"
#include "src/sampler.h"
// Order of properties is significant.
// Must fit in the BitField PropertyDetails::TypeField.
-// A copy of this is in mirror-debugger.js.
+// A copy of this is in debug/mirrors.js.
enum PropertyType {
DATA = (kField << 1) | kData,
DATA_CONSTANT = (kDescriptor << 1) | kData,
#include "src/v8.h"
#include "src/arguments.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/messages.h"
#include "src/runtime/runtime.h"
#include "src/runtime/runtime-utils.h"
#include "src/accessors.h"
#include "src/arguments.h"
#include "src/compiler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/deoptimizer.h"
#include "src/parser.h"
#include "src/runtime/runtime.h"
#include "src/arguments.h"
#include "src/bootstrapper.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/messages.h"
#include "src/parser.h"
#include "src/prettyprinter.h"
#include "src/v8.h"
#include "src/arguments.h"
-#include "src/debug.h"
-#include "src/liveedit.h"
+#include "src/debug/debug.h"
+#include "src/debug/liveedit.h"
#include "src/runtime/runtime.h"
#include "src/runtime/runtime-utils.h"
#include "src/arguments.h"
#include "src/bootstrapper.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/messages.h"
#include "src/runtime/runtime.h"
#include "src/runtime/runtime-utils.h"
#include "src/v8.h"
#include "src/arguments.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/runtime/runtime-utils.h"
namespace v8 {
#include "src/base/once.h"
#include "src/base/platform/platform.h"
#include "src/bootstrapper.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/deoptimizer.h"
#include "src/elements.h"
#include "src/frames.h"
#include "src/api.h"
#include "src/bootstrapper.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/execution.h"
#include "src/regexp-stack.h"
#include "src/v8threads.h"
#include "src/x64/assembler-x64.h"
#include "src/base/cpu.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/v8memory.h"
namespace v8 {
+++ /dev/null
-// Copyright 2012 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"
-
-#if V8_TARGET_ARCH_X64
-
-#include "src/assembler.h"
-#include "src/codegen.h"
-#include "src/debug.h"
-
-
-namespace v8 {
-namespace internal {
-
-#define __ ACCESS_MASM(masm)
-
-
-void EmitDebugBreakSlot(MacroAssembler* masm) {
- Label check_codesize;
- __ bind(&check_codesize);
- __ Nop(Assembler::kDebugBreakSlotLength);
- DCHECK_EQ(Assembler::kDebugBreakSlotLength,
- masm->SizeOfCodeGeneratedSince(&check_codesize));
-}
-
-
-void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode,
- int call_argc) {
- // Generate enough nop's to make space for a call instruction.
- masm->RecordDebugBreakSlot(mode, call_argc);
- EmitDebugBreakSlot(masm);
-}
-
-
-void DebugCodegen::ClearDebugBreakSlot(Address pc) {
- CodePatcher patcher(pc, Assembler::kDebugBreakSlotLength);
- EmitDebugBreakSlot(patcher.masm());
-}
-
-
-void DebugCodegen::PatchDebugBreakSlot(Address pc, Handle<Code> code) {
- DCHECK_EQ(Code::BUILTIN, code->kind());
- static const int kSize = Assembler::kDebugBreakSlotLength;
- CodePatcher patcher(pc, kSize);
- Label check_codesize;
- patcher.masm()->bind(&check_codesize);
- patcher.masm()->movp(kScratchRegister, reinterpret_cast<void*>(code->entry()),
- Assembler::RelocInfoNone());
- patcher.masm()->call(kScratchRegister);
- // Check that the size of the code generated is as expected.
- DCHECK_EQ(kSize, patcher.masm()->SizeOfCodeGeneratedSince(&check_codesize));
-}
-
-
-void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
- DebugBreakCallHelperMode mode) {
- __ RecordComment("Debug break");
-
- // Enter an internal frame.
- {
- FrameScope scope(masm, StackFrame::INTERNAL);
-
- // Load padding words on stack.
- for (int i = 0; i < LiveEdit::kFramePaddingInitialSize; i++) {
- __ Push(Smi::FromInt(LiveEdit::kFramePaddingValue));
- }
- __ Push(Smi::FromInt(LiveEdit::kFramePaddingInitialSize));
-
- if (mode == SAVE_RESULT_REGISTER) __ Push(rax);
-
- __ Set(rax, 0); // No arguments (argc == 0).
- __ Move(rbx, ExternalReference(Runtime::FunctionForId(Runtime::kDebugBreak),
- masm->isolate()));
-
- CEntryStub ceb(masm->isolate(), 1);
- __ CallStub(&ceb);
-
- if (FLAG_debug_code) {
- for (int i = 0; i < kNumJSCallerSaved; ++i) {
- Register reg = {JSCallerSavedCode(i)};
- __ Set(reg, kDebugZapValue);
- }
- }
-
- if (mode == SAVE_RESULT_REGISTER) __ Pop(rax);
-
- // Read current padding counter and skip corresponding number of words.
- __ Pop(kScratchRegister);
- __ SmiToInteger32(kScratchRegister, kScratchRegister);
- __ leap(rsp, Operand(rsp, kScratchRegister, times_pointer_size, 0));
-
- // Get rid of the internal frame.
- }
-
- // This call did not replace a call , so there will be an unwanted
- // return address left on the stack. Here we get rid of that.
- __ addp(rsp, Immediate(kPCOnStackSize));
-
- // Now that the break point has been handled, resume normal execution by
- // jumping to the target address intended by the caller and that was
- // overwritten by the address of DebugBreakXXX.
- ExternalReference after_break_target =
- ExternalReference::debug_after_break_target_address(masm->isolate());
- __ Move(kScratchRegister, after_break_target);
- __ Jump(Operand(kScratchRegister, 0));
-}
-
-
-void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
- masm->ret(0);
-}
-
-
-void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
- ExternalReference restarter_frame_function_slot =
- ExternalReference::debug_restarter_frame_function_pointer_address(
- masm->isolate());
- __ Move(rax, restarter_frame_function_slot);
- __ movp(Operand(rax, 0), Immediate(0));
-
- // We do not know our frame height, but set rsp based on rbp.
- __ leap(rsp, Operand(rbp, -1 * kPointerSize));
-
- __ Pop(rdi); // Function.
- __ popq(rbp);
-
- // Load context from the function.
- __ movp(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
-
- // Get function code.
- __ movp(rdx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
- __ movp(rdx, FieldOperand(rdx, SharedFunctionInfo::kCodeOffset));
- __ leap(rdx, FieldOperand(rdx, Code::kHeaderSize));
-
- // Re-run JSFunction, rdi is function, rsi is context.
- __ jmp(rdx);
-}
-
-const bool LiveEdit::kFrameDropperSupported = true;
-
-#undef __
-
-} // namespace internal
-} // namespace v8
-
-#endif // V8_TARGET_ARCH_X64
#include "src/bootstrapper.h"
#include "src/codegen.h"
#include "src/cpu-profiler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/heap/heap.h"
#include "src/x64/assembler-x64.h"
#include "src/x64/macro-assembler-x64.h"
#include "src/x87/assembler-x87.h"
#include "src/assembler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
namespace v8 {
namespace internal {
+++ /dev/null
-// Copyright 2012 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"
-
-#if V8_TARGET_ARCH_X87
-
-#include "src/codegen.h"
-#include "src/debug.h"
-
-
-namespace v8 {
-namespace internal {
-
-#define __ ACCESS_MASM(masm)
-
-
-void EmitDebugBreakSlot(MacroAssembler* masm) {
- Label check_codesize;
- __ bind(&check_codesize);
- __ Nop(Assembler::kDebugBreakSlotLength);
- DCHECK_EQ(Assembler::kDebugBreakSlotLength,
- masm->SizeOfCodeGeneratedSince(&check_codesize));
-}
-
-
-void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode,
- int call_argc) {
- // Generate enough nop's to make space for a call instruction.
- masm->RecordDebugBreakSlot(mode, call_argc);
- EmitDebugBreakSlot(masm);
-}
-
-
-void DebugCodegen::ClearDebugBreakSlot(Address pc) {
- CodePatcher patcher(pc, Assembler::kDebugBreakSlotLength);
- EmitDebugBreakSlot(patcher.masm());
-}
-
-
-void DebugCodegen::PatchDebugBreakSlot(Address pc, Handle<Code> code) {
- DCHECK_EQ(Code::BUILTIN, code->kind());
- static const int kSize = Assembler::kDebugBreakSlotLength;
- CodePatcher patcher(pc, kSize);
-
- // Add a label for checking the size of the code used for returning.
- Label check_codesize;
- patcher.masm()->bind(&check_codesize);
- patcher.masm()->call(code->entry(), RelocInfo::NONE32);
- // Check that the size of the code generated is as expected.
- DCHECK_EQ(kSize, patcher.masm()->SizeOfCodeGeneratedSince(&check_codesize));
-}
-
-
-void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
- DebugBreakCallHelperMode mode) {
- __ RecordComment("Debug break");
-
- // Enter an internal frame.
- {
- FrameScope scope(masm, StackFrame::INTERNAL);
-
- // Load padding words on stack.
- for (int i = 0; i < LiveEdit::kFramePaddingInitialSize; i++) {
- __ push(Immediate(Smi::FromInt(LiveEdit::kFramePaddingValue)));
- }
- __ push(Immediate(Smi::FromInt(LiveEdit::kFramePaddingInitialSize)));
-
- if (mode == SAVE_RESULT_REGISTER) __ push(eax);
-
- __ Move(eax, Immediate(0)); // No arguments.
- __ mov(ebx,
- Immediate(ExternalReference(
- Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate())));
-
- CEntryStub ceb(masm->isolate(), 1);
- __ CallStub(&ceb);
-
- if (FLAG_debug_code) {
- for (int i = 0; i < kNumJSCallerSaved; ++i) {
- Register reg = {JSCallerSavedCode(i)};
- __ Move(reg, Immediate(kDebugZapValue));
- }
- }
-
- if (mode == SAVE_RESULT_REGISTER) __ pop(eax);
-
- __ pop(ebx);
- // We divide stored value by 2 (untagging) and multiply it by word's size.
- STATIC_ASSERT(kSmiTagSize == 1 && kSmiShiftSize == 0);
- __ lea(esp, Operand(esp, ebx, times_half_pointer_size, 0));
-
- // Get rid of the internal frame.
- }
-
- // This call did not replace a call , so there will be an unwanted
- // return address left on the stack. Here we get rid of that.
- __ add(esp, Immediate(kPointerSize));
-
- // Now that the break point has been handled, resume normal execution by
- // jumping to the target address intended by the caller and that was
- // overwritten by the address of DebugBreakXXX.
- ExternalReference after_break_target =
- ExternalReference::debug_after_break_target_address(masm->isolate());
- __ jmp(Operand::StaticVariable(after_break_target));
-}
-
-
-void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
- masm->ret(0);
-}
-
-
-void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
- ExternalReference restarter_frame_function_slot =
- ExternalReference::debug_restarter_frame_function_pointer_address(
- masm->isolate());
- __ mov(Operand::StaticVariable(restarter_frame_function_slot), Immediate(0));
-
- // We do not know our frame height, but set esp based on ebp.
- __ lea(esp, Operand(ebp, -1 * kPointerSize));
-
- __ pop(edi); // Function.
- __ pop(ebp);
-
- // Load context from the function.
- __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
-
- // Get function code.
- __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
- __ mov(edx, FieldOperand(edx, SharedFunctionInfo::kCodeOffset));
- __ lea(edx, FieldOperand(edx, Code::kHeaderSize));
-
- // Re-run JSFunction, edi is function, esi is context.
- __ jmp(edx);
-}
-
-
-const bool LiveEdit::kFrameDropperSupported = true;
-
-#undef __
-
-} // namespace internal
-} // namespace v8
-
-#endif // V8_TARGET_ARCH_X87
#include "src/bootstrapper.h"
#include "src/codegen.h"
#include "src/cpu-profiler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/runtime/runtime.h"
namespace v8 {
#include "test/cctest/cctest.h"
#include "include/libplatform/libplatform.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "test/cctest/print-extension.h"
#include "test/cctest/profiler-extension.h"
#include "test/cctest/trace-extension.h"
#include "src/base/platform/platform.h"
#include "src/base/smart-pointers.h"
#include "src/compilation-cache.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/execution.h"
#include "src/futex-emulation.h"
#include "src/objects.h"
#include "src/base/platform/condition-variable.h"
#include "src/base/platform/platform.h"
#include "src/compilation-cache.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/deoptimizer.h"
#include "src/frames.h"
#include "src/utils.h"
#include "src/api.h"
#include "src/base/platform/platform.h"
#include "src/compilation-cache.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/deoptimizer.h"
#include "src/isolate.h"
#include "test/cctest/cctest.h"
#include "test/cctest/cctest.h"
#include "src/api.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/execution.h"
#include "src/factory.h"
#include "src/global-handles.h"
#include "src/v8.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/disasm.h"
#include "src/disassembler.h"
#include "src/macro-assembler.h"
#include "src/v8.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/disasm.h"
#include "src/disassembler.h"
#include "src/ic/ic.h"
#include "src/v8.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/disasm.h"
#include "src/disassembler.h"
#include "src/macro-assembler.h"
#include "src/v8.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/disasm.h"
#include "src/disassembler.h"
#include "src/macro-assembler.h"
#include "src/v8.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/disasm.h"
#include "src/disassembler.h"
#include "src/macro-assembler.h"
#include "src/v8.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/disasm.h"
#include "src/disassembler.h"
#include "src/ic/ic.h"
#include "src/v8.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/disasm.h"
#include "src/disassembler.h"
#include "src/ic/ic.h"
#include "test/cctest/cctest.h"
#include "src/api.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/execution.h"
#include "src/factory.h"
#include "src/global-handles.h"
#include "src/v8.h"
#include "src/api.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/string-search.h"
#include "test/cctest/cctest.h"
#include "include/v8-profiler.h"
#include "src/allocation-tracker.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/hashmap.h"
#include "src/heap-profiler.h"
#include "test/cctest/cctest.h"
#include "src/v8.h"
-#include "src/liveedit.h"
+#include "src/debug/liveedit.h"
#include "test/cctest/cctest.h"
#include "src/bootstrapper.h"
#include "src/compilation-cache.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/heap/spaces.h"
#include "src/objects.h"
#include "src/parser.h"
}
}
-// A copy of the scope types from mirror-debugger.js.
+// A copy of the scope types from debug/mirrors.js.
var ScopeType = { Global: 0,
Local: 1,
With: 2,
}
}
-// A copy of the scope types from mirror-debugger.js.
+// A copy of the scope types from debug/mirrors.js.
var ScopeType = { Global: 0,
Local: 1,
With: 2,
#include "include/libplatform/libplatform.h"
#include "src/base/platform/time.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
#include "src/flags.h"
#include "src/isolate.h"
#include "src/v8.h"
'../../src/dateparser-inl.h',
'../../src/dateparser.cc',
'../../src/dateparser.h',
- '../../src/debug.cc',
- '../../src/debug.h',
+ '../../src/debug/debug.cc',
+ '../../src/debug/debug.h',
+ '../../src/debug/liveedit.cc',
+ '../../src/debug/liveedit.h',
'../../src/deoptimizer.cc',
'../../src/deoptimizer.h',
'../../src/disasm.h',
'../../src/lithium.cc',
'../../src/lithium.h',
'../../src/lithium-inl.h',
- '../../src/liveedit.cc',
- '../../src/liveedit.h',
'../../src/log-inl.h',
'../../src/log-utils.cc',
'../../src/log-utils.h',
'../../src/arm/constants-arm.h',
'../../src/arm/constants-arm.cc',
'../../src/arm/cpu-arm.cc',
- '../../src/arm/debug-arm.cc',
'../../src/arm/deoptimizer-arm.cc',
'../../src/arm/disasm-arm.cc',
'../../src/arm/frames-arm.cc',
'../../src/compiler/arm/instruction-codes-arm.h',
'../../src/compiler/arm/instruction-selector-arm.cc',
'../../src/compiler/arm/linkage-arm.cc',
+ '../../src/debug/arm/debug-arm.cc',
'../../src/full-codegen/arm/full-codegen-arm.cc',
'../../src/ic/arm/access-compiler-arm.cc',
'../../src/ic/arm/handler-compiler-arm.cc',
'../../src/arm64/code-stubs-arm64.h',
'../../src/arm64/constants-arm64.h',
'../../src/arm64/cpu-arm64.cc',
- '../../src/arm64/debug-arm64.cc',
'../../src/arm64/decoder-arm64.cc',
'../../src/arm64/decoder-arm64.h',
'../../src/arm64/decoder-arm64-inl.h',
'../../src/compiler/arm64/instruction-codes-arm64.h',
'../../src/compiler/arm64/instruction-selector-arm64.cc',
'../../src/compiler/arm64/linkage-arm64.cc',
+ '../../src/debug/arm64/debug-arm64.cc',
'../../src/full-codegen/arm64/full-codegen-arm64.cc',
'../../src/ic/arm64/access-compiler-arm64.cc',
'../../src/ic/arm64/handler-compiler-arm64.cc',
'../../src/ia32/codegen-ia32.cc',
'../../src/ia32/codegen-ia32.h',
'../../src/ia32/cpu-ia32.cc',
- '../../src/ia32/debug-ia32.cc',
'../../src/ia32/deoptimizer-ia32.cc',
'../../src/ia32/disasm-ia32.cc',
'../../src/ia32/frames-ia32.cc',
'../../src/compiler/ia32/instruction-codes-ia32.h',
'../../src/compiler/ia32/instruction-selector-ia32.cc',
'../../src/compiler/ia32/linkage-ia32.cc',
+ '../../src/debug/ia32/debug-ia32.cc',
'../../src/full-codegen/ia32/full-codegen-ia32.cc',
'../../src/ic/ia32/access-compiler-ia32.cc',
'../../src/ic/ia32/handler-compiler-ia32.cc',
'../../src/x87/codegen-x87.cc',
'../../src/x87/codegen-x87.h',
'../../src/x87/cpu-x87.cc',
- '../../src/x87/debug-x87.cc',
'../../src/x87/deoptimizer-x87.cc',
'../../src/x87/disasm-x87.cc',
'../../src/x87/frames-x87.cc',
'../../src/compiler/x87/instruction-codes-x87.h',
'../../src/compiler/x87/instruction-selector-x87.cc',
'../../src/compiler/x87/linkage-x87.cc',
+ '../../src/debug/x87/debug-x87.cc',
'../../src/full-codegen/x87/full-codegen-x87.cc',
'../../src/ic/x87/access-compiler-x87.cc',
'../../src/ic/x87/handler-compiler-x87.cc',
'../../src/mips/constants-mips.cc',
'../../src/mips/constants-mips.h',
'../../src/mips/cpu-mips.cc',
- '../../src/mips/debug-mips.cc',
'../../src/mips/deoptimizer-mips.cc',
'../../src/mips/disasm-mips.cc',
'../../src/mips/frames-mips.cc',
'../../src/compiler/mips/instruction-selector-mips.cc',
'../../src/compiler/mips/linkage-mips.cc',
'../../src/full-codegen/mips/full-codegen-mips.cc',
+ '../../src/debug/mips/debug-mips.cc',
'../../src/ic/mips/access-compiler-mips.cc',
'../../src/ic/mips/handler-compiler-mips.cc',
'../../src/ic/mips/ic-mips.cc',
'../../src/mips64/constants-mips64.cc',
'../../src/mips64/constants-mips64.h',
'../../src/mips64/cpu-mips64.cc',
- '../../src/mips64/debug-mips64.cc',
'../../src/mips64/deoptimizer-mips64.cc',
'../../src/mips64/disasm-mips64.cc',
'../../src/mips64/frames-mips64.cc',
'../../src/compiler/mips64/instruction-codes-mips64.h',
'../../src/compiler/mips64/instruction-selector-mips64.cc',
'../../src/compiler/mips64/linkage-mips64.cc',
+ '../../src/debug/mips64/debug-mips64.cc',
'../../src/full-codegen/mips64/full-codegen-mips64.cc',
'../../src/ic/mips64/access-compiler-mips64.cc',
'../../src/ic/mips64/handler-compiler-mips64.cc',
'../../src/x64/codegen-x64.cc',
'../../src/x64/codegen-x64.h',
'../../src/x64/cpu-x64.cc',
- '../../src/x64/debug-x64.cc',
'../../src/x64/deoptimizer-x64.cc',
'../../src/x64/disasm-x64.cc',
'../../src/x64/frames-x64.cc',
'../../src/x64/macro-assembler-x64.h',
'../../src/x64/regexp-macro-assembler-x64.cc',
'../../src/x64/regexp-macro-assembler-x64.h',
+ '../../src/debug/x64/debug-x64.cc',
'../../src/full-codegen/x64/full-codegen-x64.cc',
'../../src/ic/x64/access-compiler-x64.cc',
'../../src/ic/x64/handler-compiler-x64.cc',
'../../src/ppc/constants-ppc.h',
'../../src/ppc/constants-ppc.cc',
'../../src/ppc/cpu-ppc.cc',
- '../../src/ppc/debug-ppc.cc',
'../../src/ppc/deoptimizer-ppc.cc',
'../../src/ppc/disasm-ppc.cc',
'../../src/ppc/frames-ppc.cc',
'../../src/compiler/ppc/instruction-codes-ppc.h',
'../../src/compiler/ppc/instruction-selector-ppc.cc',
'../../src/compiler/ppc/linkage-ppc.cc',
+ '../../src/debug/ppc/debug-ppc.cc',
'../../src/full-codegen/ppc/full-codegen-ppc.cc',
'../../src/ic/ppc/access-compiler-ppc.cc',
'../../src/ic/ppc/handler-compiler-ppc.cc',
'../../src/json.js',
'../../src/array-iterator.js',
'../../src/string-iterator.js',
- '../../src/debug-debugger.js',
- '../../src/mirror-debugger.js',
- '../../src/liveedit-debugger.js',
'../../src/templates.js',
'../../src/harmony-array.js',
'../../src/harmony-typedarray.js',
+ '../../src/debug/debug.js',
+ '../../src/debug/mirrors.js',
+ '../../src/debug/liveedit.js',
],
'experimental_library_files': [
'../../src/macros.py',
def IsDebuggerFile(filename):
- return filename.endswith("-debugger.js")
+ return "debug" in filename
def IsMacroFile(filename):
return filename.endswith("macros.py")
result.is_debugger_id.append(is_debugger)
name = os.path.basename(source)[:-3]
- result.names.append(name if not is_debugger else name[:-9])
+ result.names.append(name)
return result