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

Unified Diff: src/liveedit.cc

Issue 2943002: Reimplement stack manipulations for LiveEdit (Closed)
Patch Set: follow codereview Created 10 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/liveedit.h ('k') | src/liveedit-debugger.js » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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);
}
« no previous file with comments | « src/liveedit.h ('k') | src/liveedit-debugger.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698