| Index: src/liveedit.cc
|
| diff --git a/src/liveedit.cc b/src/liveedit.cc
|
| index 04631a3f7cae14a07ccf9b70b41c51f3eeadc383..3c1f47687a97593c68ebc1b0e5dae28dc21bee29 100644
|
| --- a/src/liveedit.cc
|
| +++ b/src/liveedit.cc
|
| @@ -580,6 +580,60 @@ class SharedInfoWrapper : public JSArrayBasedStruct<SharedInfoWrapper> {
|
| friend class JSArrayBasedStruct<SharedInfoWrapper>;
|
| };
|
|
|
| +// Data describing possible stack manipulations or a failure descriptions.
|
| +// The function index is used both in success/failure cases.
|
| +class StackManipulationInfo : public JSArrayBasedStruct<StackManipulationInfo> {
|
| + public:
|
| + static Handle<JSArray> CreateError(const char* error_message,
|
| + int function_index) {
|
| + StackManipulationInfo info = StackManipulationInfo::Create();
|
| + info.SetProperties(error_message, function_index,
|
| + Handle<Object>(Heap::undefined_value()));
|
| + return info.GetJSArray();
|
| + }
|
| +
|
| + static Handle<JSArray> CreateSuccess(int bottom_function_index,
|
| + Handle<Object> drop_data) {
|
| + StackManipulationInfo info = StackManipulationInfo::Create();
|
| + info.SetProperties(NULL, bottom_function_index, drop_data);
|
| + return info.GetJSArray();
|
| + }
|
| +
|
| + static Handle<JSArray> CreateEmpty() {
|
| + StackManipulationInfo info = StackManipulationInfo::Create();
|
| + return info.GetJSArray();
|
| + }
|
| +
|
| + explicit StackManipulationInfo(Handle<JSArray> array)
|
| + : JSArrayBasedStruct<StackManipulationInfo>(array) {
|
| + }
|
| +
|
| + private:
|
| + void SetProperties(const char* error_message, int function_index,
|
| + Handle<Object> drop_data) {
|
| + HandleScope scope;
|
| + if (error_message == NULL) {
|
| + this->SetField(kErrorMessageOffset_,
|
| + Handle<Object>(Heap::undefined_value()));
|
| + } else {
|
| + Handle<String> message_string =
|
| + Factory::NewStringFromAscii(CStrVector(error_message));
|
| + this->SetField(kErrorMessageOffset_, message_string);
|
| + }
|
| + this->SetField(kBottomFunctionIndexOffset_,
|
| + Handle<Object>(Smi::FromInt(function_index)));
|
| + Handle<JSValue> drop_data_wrapper = WrapInJSValue(*drop_data);
|
| + this->SetField(kDropDataOffset_, drop_data_wrapper);
|
| + }
|
| +
|
| + static const int kErrorMessageOffset_ = 0;
|
| + static const int kBottomFunctionIndexOffset_ = 1;
|
| + static const int kDropDataOffset_ = 2;
|
| + static const int kSize_ = 3;
|
| +
|
| + friend class JSArrayBasedStruct<StackManipulationInfo>;
|
| +};
|
| +
|
| class FunctionInfoListener {
|
| public:
|
| FunctionInfoListener() {
|
| @@ -606,9 +660,9 @@ class FunctionInfoListener {
|
| current_parent_index_ = info.GetParentIndex();
|
| }
|
|
|
| -// TODO(LiveEdit): Move private method below.
|
| -// This private section was created here to avoid moving the function
|
| -// to keep already complex diff simpler.
|
| + // TODO(LiveEdit): Move private method below.
|
| + // This private section was created here to avoid moving the function
|
| + // to keep already complex diff simpler.
|
| private:
|
| Object* SerializeFunctionScope(Scope* scope) {
|
| HandleScope handle_scope;
|
| @@ -797,8 +851,25 @@ static void IterateAllThreads(ThreadVisitor* visitor) {
|
| ThreadManager::IterateArchivedThreads(visitor);
|
| }
|
|
|
| +// Forward declarations. Introduced to keep functions in the same order
|
| +// to simplify diff during a complex source modification.
|
| +// TODO(LiveEdit): reorder functions and get rid of forward declarations
|
| +static void PerformActiveStackModification(
|
| + ByteArray* stack_manipulation_data,
|
| + SharedFunctionInfo* expected_function);
|
| +static void AddRestarterRInfo(Handle<Code> code, int patch_pc,
|
| + int source_position);
|
| +
|
| +static Handle<Code> PatchCodeWithRestarter(Handle<Code> code,
|
| + int function_source_pos);
|
| +
|
| +
|
| +
|
| // Finds all references to original and replaces them with substitution.
|
| -static void ReplaceCodeObject(Code* original, Code* substitution) {
|
| +// Performs stack manipulation (if data is provided) in the same transaction.
|
| +static void ReplaceCodeObject(Code* original, Code* substitution,
|
| + ByteArray* stack_manipulation_data,
|
| + SharedFunctionInfo* function) {
|
| ASSERT(!Heap::InNewSpace(substitution));
|
|
|
| AssertNoAllocation no_allocations_please;
|
| @@ -829,6 +900,12 @@ static void ReplaceCodeObject(Code* original, Code* substitution) {
|
| }
|
|
|
| visitor.Replace(substitution);
|
| +
|
| + if (stack_manipulation_data != NULL) {
|
| + // Performs stack manipulations for the cases when the actually function was
|
| + // on stack. The manipulations are expected to restart this function.
|
| + PerformActiveStackModification(stack_manipulation_data, function);
|
| + }
|
| }
|
|
|
|
|
| @@ -839,10 +916,12 @@ static bool IsJSFunctionCode(Code* code) {
|
| }
|
|
|
|
|
| -Object* LiveEdit::ReplaceFunctionCode(Handle<JSArray> new_compile_info_array,
|
| - Handle<JSArray> shared_info_array) {
|
| - HandleScope scope;
|
| +Object* LiveEdit::ReplaceFunctionCode(
|
| + Handle<JSArray> new_compile_info_array,
|
| + Handle<JSArray> shared_info_array,
|
| + Handle<ByteArray> stack_manipulation_data) {
|
|
|
| + HandleScope scope;
|
| if (!SharedInfoWrapper::IsInstance(shared_info_array)) {
|
| return Top::ThrowIllegalOperation();
|
| }
|
| @@ -852,21 +931,28 @@ Object* LiveEdit::ReplaceFunctionCode(Handle<JSArray> new_compile_info_array,
|
|
|
| Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo();
|
|
|
| + Handle<Code> new_code = compile_info_wrapper.GetFunctionCode();
|
| +
|
| + shared_info->set_start_position(compile_info_wrapper.GetStartPosition());
|
| + shared_info->set_end_position(compile_info_wrapper.GetEndPosition());
|
| +
|
| if (IsJSFunctionCode(shared_info->code())) {
|
| - ReplaceCodeObject(shared_info->code(),
|
| - *(compile_info_wrapper.GetFunctionCode()));
|
| + if (stack_manipulation_data.is_null()) {
|
| + ReplaceCodeObject(shared_info->code(), *new_code, NULL, NULL);
|
| + } else {
|
| + new_code = PatchCodeWithRestarter(new_code,
|
| + shared_info->start_position());
|
| + ReplaceCodeObject(shared_info->code(), *new_code,
|
| + *stack_manipulation_data, *shared_info);
|
| + }
|
| }
|
|
|
| if (shared_info->debug_info()->IsDebugInfo()) {
|
| Handle<DebugInfo> debug_info(DebugInfo::cast(shared_info->debug_info()));
|
| - Handle<Code> new_original_code =
|
| - Factory::CopyCode(compile_info_wrapper.GetFunctionCode());
|
| + Handle<Code> new_original_code = Factory::CopyCode(new_code);
|
| debug_info->set_original_code(*new_original_code);
|
| }
|
|
|
| - shared_info->set_start_position(compile_info_wrapper.GetStartPosition());
|
| - shared_info->set_end_position(compile_info_wrapper.GetEndPosition());
|
| -
|
| shared_info->set_construct_stub(
|
| Builtins::builtin(Builtins::JSConstructStubGeneric));
|
|
|
| @@ -874,6 +960,57 @@ Object* LiveEdit::ReplaceFunctionCode(Handle<JSArray> new_compile_info_array,
|
| }
|
|
|
|
|
| +// Returns the offset of restarted patch within the code or -1. Technically
|
| +// patch offset is not its starting point.
|
| +static int GetRestarterPatchOffset(Code* code) {
|
| + Handle<Code> restarter_patch(
|
| + Builtins::builtin(Builtins::RestarterPatch_LiveEdit));
|
| + int patch_len = restarter_patch->instruction_size();
|
| + if (code->instruction_size() < patch_len) {
|
| + return -1;
|
| + }
|
| + Address pos1 = code->instruction_start() + code->instruction_size();
|
| + Address pos2 = restarter_patch->instruction_start() + patch_len;
|
| +
|
| + for (int i = 0; i < patch_len; i++) {
|
| + pos1--;
|
| + pos2--;
|
| + if (*pos1 != *pos2) {
|
| + return -1;
|
| + }
|
| + }
|
| + return code->instruction_size() - patch_len;
|
| +}
|
| +
|
| +
|
| +bool LiveEdit::IsAtFrameResetPatch(const JavaScriptFrame* frame) {
|
| + return frame->fp() == Debug::GetRestartedFrameFp();
|
| +}
|
| +
|
| +
|
| +Address LiveEdit::GetRestarterPatchEntryPoint(Code* code) {
|
| + int entry_offset = GetRestarterPatchOffset(code);
|
| + ASSERT(entry_offset != -1);
|
| + return code->instruction_start() + entry_offset +
|
| + Debug::kFrameRestarterEntryOffset;
|
| +}
|
| +
|
| +
|
| +// Returns patched code instruction offset from entry point.
|
| +static Handle<Code> PatchCodeWithRestarter(Handle<Code> code,
|
| + int function_source_pos) {
|
| + if (GetRestarterPatchOffset(*code) == -1) {
|
| + Handle<Code> restarter_patch(
|
| + Builtins::builtin(Builtins::RestarterPatch_LiveEdit));
|
| + Handle<Code> patched_code = Factory::AddPatchToCode(code, restarter_patch);
|
| + AddRestarterRInfo(patched_code, code->instruction_size(),
|
| + function_source_pos);
|
| + code = patched_code;
|
| + }
|
| + return code;
|
| +}
|
| +
|
| +
|
| // TODO(635): Eval caches its scripts (same text -- same compiled info).
|
| // Make sure we clear such caches.
|
| void LiveEdit::SetFunctionScript(Handle<JSValue> function_wrapper,
|
| @@ -992,9 +1129,34 @@ class RelocInfoBuffer {
|
| 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,
|
| +
|
| +static void AddRestarterRInfo(Handle<Code> code, int patch_pc,
|
| + int source_position) {
|
| + RelocInfoBuffer buffer_writer(code->relocation_size(),
|
| + code->instruction_start());
|
| +
|
| + {
|
| + AssertNoAllocation no_allocations_please;
|
| + for (RelocIterator it(*code); !it.done(); it.next()) {
|
| + buffer_writer.Write(it.rinfo());
|
| + }
|
| + byte* pc = code->instruction_start() + patch_pc;
|
| + intptr_t position_value = source_position;
|
| + RelocInfo additional_info(pc, RelocInfo::POSITION, position_value);
|
| + buffer_writer.Write(&additional_info);
|
| + }
|
| +
|
| + Vector<byte> buffer = buffer_writer.GetResult();
|
| + Handle<ByteArray> array = Factory::NewByteArray(buffer.length(), TENURED);
|
| + code->set_relocation_info(*array);
|
| +
|
| + memcpy(code->relocation_start(), buffer.start(), buffer.length());
|
| +}
|
| +
|
| +
|
| +
|
| +// Patch positions in code (changes relocation info section).
|
| +static void PatchPositionsInCode(Handle<Code> code,
|
| Handle<JSArray> position_change_array) {
|
|
|
| RelocInfoBuffer buffer_writer(code->relocation_size(),
|
| @@ -1020,17 +1182,12 @@ static Handle<Code> PatchPositionsInCode(Handle<Code> code,
|
|
|
| Vector<byte> buffer = buffer_writer.GetResult();
|
|
|
| - if (buffer.length() == code->relocation_size()) {
|
| - // Simply patch relocation area of code.
|
| - memcpy(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(Factory::CopyCode(code, buffer));
|
| - return result;
|
| + if (buffer.length() != code->relocation_size()) {
|
| + // Relocation info section now has different size.
|
| + Handle<ByteArray> array = Factory::NewByteArray(buffer.length(), TENURED);
|
| + code->set_relocation_info(*array);
|
| }
|
| + memcpy(code->relocation_start(), buffer.start(), buffer.length());
|
| }
|
|
|
|
|
| @@ -1057,16 +1214,7 @@ Object* LiveEdit::PatchFunctionPositions(
|
|
|
| if (IsJSFunctionCode(info->code())) {
|
| // 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(info->code(), *patched_code);
|
| - }
|
| + PatchPositionsInCode(Handle<Code>(info->code()), position_change_array);
|
| }
|
|
|
| return Heap::undefined_value();
|
| @@ -1138,26 +1286,27 @@ void LiveEdit::ReplaceRefToNestedFunction(
|
| }
|
|
|
|
|
| -// 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) {
|
| +// Check an activation against the list of functions. If there is a function
|
| +// that matches its index is returned. Otherwise returns -1.
|
| +static int CheckActivation(Handle<JSArray> shared_info_array,
|
| + StackFrame* frame) {
|
| if (!frame->is_java_script()) {
|
| - return false;
|
| + return -1;
|
| }
|
| int len = Smi::cast(shared_info_array->length())->value();
|
| for (int i = 0; i < len; i++) {
|
| JSValue* wrapper = JSValue::cast(shared_info_array->GetElement(i));
|
| + if (!wrapper->value()->IsSharedFunctionInfo()) {
|
| + continue;
|
| + }
|
| Handle<SharedFunctionInfo> shared(
|
| SharedFunctionInfo::cast(wrapper->value()));
|
|
|
| if (frame->code() == shared->code()) {
|
| - SetElement(result, i, Handle<Smi>(Smi::FromInt(status)));
|
| - return true;
|
| + return i;
|
| }
|
| }
|
| - return false;
|
| + return -1;
|
| }
|
|
|
|
|
| @@ -1181,18 +1330,45 @@ static bool FixTryCatchHandler(StackFrame* top_frame,
|
| }
|
|
|
|
|
| -// 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,
|
| - Debug::FrameDropMode* mode) {
|
| - if (Debug::kFrameDropperFrameSize < 0) {
|
| - return "Stack manipulations are not supported in this architecture.";
|
| +// If function was in some nested scope (e.g. 'with'), reset it back to
|
| +// function scope.
|
| +static void ResetFrameScope(JavaScriptFrame* js_frame) {
|
| + Context* original_context = Context::cast(js_frame->context());
|
| + Context* context = original_context;
|
| + while (!context->is_function_context()) {
|
| + context = context->previous();
|
| + ASSERT(context->IsContext());
|
| + }
|
| + if (original_context != context) {
|
| + js_frame->SetContext(context);
|
| + }
|
| +}
|
| +
|
| +
|
| +// The header of stack modification data that is stored in byte array.
|
| +// This header is followed by 3 StackFrame objects.
|
| +struct StackModificationPlanHeader {
|
| + Address unused_stack_top;
|
| + Address unused_stack_bottom;
|
| + Debug::FrameDropMode frame_drop_mode;
|
| +};
|
| +
|
| +
|
| +// Plans the stack manipulations. The input defines a range of frames
|
| +// with the bottom frames being a JavaScriptFrame. Returns a successfully
|
| +// created plan or an error description.
|
| +static Handle<JSArray> PrepareDropFrames(Vector<StackFrame*> frames,
|
| + int top_frame_index,
|
| + int bottom_js_frame_index,
|
| + int target_function_index) {
|
| + if (Debug::kRestartedFrameHeight < 0) {
|
| + return StackManipulationInfo::CreateError(
|
| + "Stack manipulations are not supported in this architecture",
|
| + target_function_index);
|
| }
|
|
|
| + Debug::FrameDropMode mode;
|
| +
|
| 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];
|
| @@ -1203,67 +1379,105 @@ static const char* DropFrames(Vector<StackFrame*> frames,
|
| if (pre_top_frame->code()->is_inline_cache_stub() &&
|
| pre_top_frame->code()->ic_state() == DEBUG_BREAK) {
|
| // OK, we can drop inline cache calls.
|
| - *mode = Debug::FRAME_DROPPED_IN_IC_CALL;
|
| + mode = Debug::FRAME_DROPPED_IN_IC_CALL;
|
| } else if (pre_top_frame->code() == Debug::debug_break_slot()) {
|
| // OK, we can drop debug break slot.
|
| - *mode = Debug::FRAME_DROPPED_IN_DEBUG_SLOT_CALL;
|
| - } else if (pre_top_frame->code() ==
|
| - Builtins::builtin(Builtins::FrameDropper_LiveEdit)) {
|
| + mode = Debug::FRAME_DROPPED_IN_DEBUG_SLOT_CALL;
|
| + } else if (Debug::GetRestartedFrameFp() != 0) {
|
| + // The top frame must be restarted.
|
| // OK, we can drop our own code.
|
| - *mode = Debug::FRAME_DROPPED_IN_DIRECT_CALL;
|
| + mode = Debug::FRAME_DROPPED_IN_DIRECT_CALL;
|
| } else if (pre_top_frame->code()->kind() == Code::STUB &&
|
| pre_top_frame->code()->major_key()) {
|
| // Entry from our unit tests, it's fine, we support this case.
|
| - *mode = Debug::FRAME_DROPPED_IN_DIRECT_CALL;
|
| + mode = Debug::FRAME_DROPPED_IN_DIRECT_CALL;
|
| } else {
|
| - return "Unknown structure of stack above changing function";
|
| + return StackManipulationInfo::CreateError(
|
| + "Unknown structure of stack above JavaScript frames",
|
| + target_function_index);
|
| }
|
|
|
| Address unused_stack_top = top_frame->sp();
|
| Address unused_stack_bottom = bottom_js_frame->fp()
|
| - - Debug::kFrameDropperFrameSize * kPointerSize // Size of the new frame.
|
| + - Debug::kRestartedFrameHeight * kPointerSize // Size of the new frame.
|
| + kPointerSize; // Bigger address end is exclusive.
|
|
|
| if (unused_stack_top > unused_stack_bottom) {
|
| - return "Not enough space for frame dropper frame";
|
| + return StackManipulationInfo::CreateError(
|
| + "Not enough space in stack to do stack manipulations",
|
| + target_function_index);
|
| }
|
| + StackModificationPlanHeader plan;
|
| + plan.unused_stack_top = unused_stack_top;
|
| + plan.unused_stack_bottom = unused_stack_bottom;
|
| + plan.frame_drop_mode = mode;
|
| +
|
| + StackFrame* three_frames[] = { pre_top_frame, top_frame, bottom_js_frame };
|
|
|
| - // Committing now. After this point we should return only NULL value.
|
| + int data_size = sizeof(plan);
|
| + for (int i = 0; i < 3; i++) {
|
| + data_size += GetStackFrameObjectSize(three_frames[i]);
|
| + }
|
| +
|
| + Handle<ByteArray> array = Factory::NewByteArray(data_size);
|
| + {
|
| + AssertNoAllocation no_allocations_please;
|
| + byte* dst = array->GetDataStartAddress();
|
| + memcpy(dst, &plan, sizeof(plan));
|
| + dst += sizeof(plan);
|
| + for (int i = 0; i < 3; i++) {
|
| + int size = GetStackFrameObjectSize(three_frames[i]);
|
| + memcpy(dst, three_frames[i], size);
|
| + dst += size;
|
| + }
|
| + }
|
| + return StackManipulationInfo::CreateSuccess(target_function_index, array);
|
| +}
|
|
|
| +// Actually modifies the active stack. Within the range of frames,
|
| +// restarts the function in a bottom one and zeroes out all other.
|
| +static void DoDropFrames(StackFrame* pre_top_frame, StackFrame* top_frame,
|
| + JavaScriptFrame* bottom_js_frame,
|
| + Address unused_stack_top, Address unused_stack_bottom,
|
| + SharedFunctionInfo* expected_function) {
|
| FixTryCatchHandler(pre_top_frame, bottom_js_frame);
|
| // Make sure FixTryCatchHandler is idempotent.
|
| ASSERT(!FixTryCatchHandler(pre_top_frame, bottom_js_frame));
|
|
|
| - Handle<Code> code(Builtins::builtin(Builtins::FrameDropper_LiveEdit));
|
| - top_frame->set_pc(code->entry());
|
| - pre_top_frame->SetCallerFp(bottom_js_frame->fp());
|
| + ResetFrameScope(bottom_js_frame);
|
| +
|
| + SharedFunctionInfo* bottom_function =
|
| + JSFunction::cast(bottom_js_frame->function())->shared();
|
| +
|
| + ASSERT_EQ(bottom_function, expected_function);
|
| +
|
| + Code* bottom_frame_code = bottom_function->code();
|
|
|
| - Debug::SetUpFrameDropperFrame(bottom_js_frame, code);
|
| + top_frame->set_pc(LiveEdit::GetRestarterPatchEntryPoint(bottom_frame_code));
|
| + pre_top_frame->SetCallerFp(bottom_js_frame->fp());
|
|
|
| for (Address a = unused_stack_top;
|
| - a < unused_stack_bottom;
|
| - a += kPointerSize) {
|
| + a < unused_stack_bottom;
|
| + a += kPointerSize) {
|
| Memory::Object_at(a) = Smi::FromInt(0);
|
| }
|
| -
|
| - return NULL;
|
| }
|
|
|
|
|
| +
|
| static bool IsDropableFrame(StackFrame* frame) {
|
| return !frame->is_exit();
|
| }
|
|
|
| -// 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) {
|
| +// Iterates over the stack and decides whether it contains any of listed
|
| +// functions' activations or not. If it does contain, return the plan on
|
| +// how the stack could be modified or failure description.
|
| +static Handle<JSArray> PrepareActiveStackModification(
|
| + Handle<JSArray> shared_info_array) {
|
|
|
| ZoneScope scope(DELETE_ON_EXIT);
|
| Vector<StackFrame*> frames = CreateStackMap();
|
|
|
| - int array_len = Smi::cast(shared_info_array->length())->value();
|
| -
|
| int top_frame_index = -1;
|
| int frame_index = 0;
|
| for (; frame_index < frames.length(); frame_index++) {
|
| @@ -1272,20 +1486,20 @@ static const char* DropActivationsInActiveThread(
|
| top_frame_index = frame_index;
|
| break;
|
| }
|
| - if (CheckActivation(shared_info_array, result, 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";
|
| + int problem_function_index = CheckActivation(shared_info_array, frame);
|
| + if (problem_function_index != -1) {
|
| + return StackManipulationInfo::CreateError(
|
| + "Debugger mark-up on stack is not found",
|
| + problem_function_index);
|
| }
|
| }
|
|
|
| if (top_frame_index == -1) {
|
| // We haven't found break frame, but no function is blocking us anyway.
|
| - return NULL;
|
| + return StackManipulationInfo::CreateEmpty();
|
| }
|
|
|
| - bool target_frame_found = false;
|
| + int target_function_index = -1;
|
| int bottom_js_frame_index = top_frame_index;
|
| bool c_code_found = false;
|
|
|
| @@ -1295,9 +1509,9 @@ static const char* DropActivationsInActiveThread(
|
| c_code_found = true;
|
| break;
|
| }
|
| - if (CheckActivation(shared_info_array, result, frame,
|
| - LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) {
|
| - target_frame_found = true;
|
| + int problem_function_index = CheckActivation(shared_info_array, frame);
|
| + if (problem_function_index != -1) {
|
| + target_function_index = problem_function_index;
|
| bottom_js_frame_index = frame_index;
|
| }
|
| }
|
| @@ -1308,111 +1522,92 @@ static const char* DropActivationsInActiveThread(
|
| for (; frame_index < frames.length(); frame_index++) {
|
| StackFrame* frame = frames[frame_index];
|
| if (frame->is_java_script()) {
|
| - if (CheckActivation(shared_info_array, result, frame,
|
| - LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE)) {
|
| - // Cannot drop frame under C frames.
|
| - return NULL;
|
| + int problem_function_index = CheckActivation(shared_info_array, frame);
|
| + if (problem_function_index != -1) {
|
| + return StackManipulationInfo::CreateError(
|
| + "Cannot drop frame under C frames",
|
| + problem_function_index);
|
| }
|
| }
|
| }
|
| }
|
|
|
| - if (!do_drop) {
|
| - // We are in check-only mode.
|
| - return NULL;
|
| + if (target_function_index == -1) {
|
| + return StackManipulationInfo::CreateEmpty();
|
| }
|
|
|
| - if (!target_frame_found) {
|
| - // Nothing to drop.
|
| - return NULL;
|
| - }
|
| + return PrepareDropFrames(frames, top_frame_index, bottom_js_frame_index,
|
| + target_function_index);
|
| +}
|
|
|
| - Debug::FrameDropMode drop_mode = Debug::FRAMES_UNTOUCHED;
|
| - const char* error_message = DropFrames(frames, top_frame_index,
|
| - bottom_js_frame_index, &drop_mode);
|
|
|
| - if (error_message != NULL) {
|
| - return error_message;
|
| - }
|
| +// Unpacks stack manipulation data and operates stack manipulation procedure.
|
| +static void PerformActiveStackModification(
|
| + ByteArray* stack_manipulation_data,
|
| + SharedFunctionInfo* expected_function) {
|
| + StackModificationPlanHeader* plan =
|
| + reinterpret_cast<StackModificationPlanHeader*>(
|
| + stack_manipulation_data->GetDataStartAddress());
|
|
|
| - // 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;
|
| - }
|
| + StackFrame* pre_top_frame;
|
| + StackFrame* top_frame;
|
| + StackFrame* bottom_frame;
|
| + {
|
| + byte* pos = reinterpret_cast<byte*>(plan);
|
| + pos += sizeof(StackModificationPlanHeader);
|
| + pre_top_frame = reinterpret_cast<StackFrame*>(pos);
|
| + pos += GetStackFrameObjectSize(pre_top_frame);
|
| + top_frame = reinterpret_cast<StackFrame*>(pos);
|
| + pos += GetStackFrameObjectSize(top_frame);
|
| + bottom_frame = reinterpret_cast<StackFrame*>(pos);
|
| }
|
| - Debug::FramesHaveBeenDropped(new_id, drop_mode);
|
| + JavaScriptFrame* bottom_js_frame = JavaScriptFrame::cast(bottom_frame);
|
|
|
| - // Replace "blocked on active" with "replaced on active" status.
|
| - for (int i = 0; i < array_len; i++) {
|
| - if (result->GetElement(i) ==
|
| - Smi::FromInt(LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) {
|
| - result->SetElement(i, Smi::FromInt(
|
| - LiveEdit::FUNCTION_REPLACED_ON_ACTIVE_STACK));
|
| - }
|
| - }
|
| - return NULL;
|
| +
|
| + DoDropFrames(pre_top_frame, top_frame, bottom_js_frame,
|
| + plan->unused_stack_top, plan->unused_stack_bottom,
|
| + expected_function);
|
| +
|
| + Debug::FramesHaveBeenDropped(bottom_js_frame, plan->frame_drop_mode);
|
| + ASSERT(LiveEdit::IsAtFrameResetPatch(bottom_js_frame));
|
| }
|
|
|
|
|
| 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) {
|
| + explicit InactiveThreadActivationsChecker(Handle<JSArray> shared_info_array)
|
| + : shared_info_array_(shared_info_array), blocked_function_index_(-1) {
|
| }
|
| void VisitThread(ThreadLocalTop* top) {
|
| for (StackFrameIterator it(top); !it.done(); it.Advance()) {
|
| - has_blocked_functions_ |= CheckActivation(
|
| - shared_info_array_, result_, it.frame(),
|
| - LiveEdit::FUNCTION_BLOCKED_ON_OTHER_STACK);
|
| + if (blocked_function_index_ == -1) {
|
| + blocked_function_index_ = CheckActivation(shared_info_array_,
|
| + it.frame());
|
| + }
|
| }
|
| }
|
| - bool HasBlockedFunctions() {
|
| - return has_blocked_functions_;
|
| + int GetBlockedFunctionIndex() {
|
| + return blocked_function_index_;
|
| }
|
|
|
| private:
|
| Handle<JSArray> shared_info_array_;
|
| - Handle<JSArray> result_;
|
| - bool has_blocked_functions_;
|
| + int blocked_function_index_;
|
| };
|
|
|
|
|
| -Handle<JSArray> LiveEdit::CheckAndDropActivations(
|
| - Handle<JSArray> shared_info_array, bool do_drop) {
|
| - int len = Smi::cast(shared_info_array->length())->value();
|
| -
|
| - Handle<JSArray> result = Factory::NewJSArray(len);
|
| -
|
| - // Fill the default values.
|
| - for (int i = 0; i < len; i++) {
|
| - SetElement(result, i,
|
| - Handle<Smi>(Smi::FromInt(FUNCTION_AVAILABLE_FOR_PATCH)));
|
| - }
|
| -
|
| -
|
| +Handle<JSArray> LiveEdit::CheckActivations(Handle<JSArray> shared_info_array) {
|
| // First check inactive threads. Fail if some functions are blocked there.
|
| - InactiveThreadActivationsChecker inactive_threads_checker(shared_info_array,
|
| - result);
|
| + InactiveThreadActivationsChecker inactive_threads_checker(shared_info_array);
|
| ThreadManager::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.
|
| - Vector<const char> vector_message(error_message, StrLength(error_message));
|
| - Handle<String> str = Factory::NewStringFromAscii(vector_message);
|
| - SetElement(result, len, str);
|
| + if (inactive_threads_checker.GetBlockedFunctionIndex() != -1) {
|
| + return StackManipulationInfo::CreateError(
|
| + "Cannot patch function that is activated in inactive thread",
|
| + inactive_threads_checker.GetBlockedFunctionIndex());
|
| }
|
| - return result;
|
| +
|
| + return PrepareActiveStackModification(shared_info_array);
|
| }
|
|
|
|
|
|
|