Commit 6fca8702 authored by yangguo's avatar yangguo Committed by Commit bot

[debugger] do not restart frames that reference new.target for liveedit.

R=mstarzinger@chromium.org

Review URL: https://codereview.chromium.org/1493363002

Cr-Commit-Position: refs/heads/master@{#32572}
parent 186f6708
...@@ -49,6 +49,8 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone, ...@@ -49,6 +49,8 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone,
receiver_info = NONE; receiver_info = NONE;
} }
bool has_new_target = scope->new_target_var() != nullptr;
// Determine use and location of the function variable if it is present. // Determine use and location of the function variable if it is present.
VariableAllocationInfo function_name_info; VariableAllocationInfo function_name_info;
VariableMode function_variable_mode; VariableMode function_variable_mode;
...@@ -90,6 +92,7 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone, ...@@ -90,6 +92,7 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone,
LanguageModeField::encode(scope->language_mode()) | LanguageModeField::encode(scope->language_mode()) |
DeclarationScopeField::encode(scope->is_declaration_scope()) | DeclarationScopeField::encode(scope->is_declaration_scope()) |
ReceiverVariableField::encode(receiver_info) | ReceiverVariableField::encode(receiver_info) |
HasNewTargetField::encode(has_new_target) |
FunctionVariableField::encode(function_name_info) | FunctionVariableField::encode(function_name_info) |
FunctionVariableMode::encode(function_variable_mode) | FunctionVariableMode::encode(function_variable_mode) |
AsmModuleField::encode(scope->asm_module()) | AsmModuleField::encode(scope->asm_module()) |
...@@ -374,6 +377,9 @@ bool ScopeInfo::HasAllocatedReceiver() { ...@@ -374,6 +377,9 @@ bool ScopeInfo::HasAllocatedReceiver() {
} }
bool ScopeInfo::HasNewTarget() { return HasNewTargetField::decode(Flags()); }
bool ScopeInfo::HasFunctionName() { bool ScopeInfo::HasFunctionName() {
if (length() > 0) { if (length() > 0) {
return NONE != FunctionVariableField::decode(Flags()); return NONE != FunctionVariableField::decode(Flags());
......
...@@ -138,6 +138,9 @@ void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) { ...@@ -138,6 +138,9 @@ void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
// Load context from the function. // Load context from the function.
__ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset)); __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
// Clear new.target as a safety measure.
__ LoadRoot(r3, Heap::kUndefinedValueRootIndex);
// Get function code. // Get function code.
__ ldr(ip, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset)); __ ldr(ip, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
__ ldr(ip, FieldMemOperand(ip, SharedFunctionInfo::kCodeOffset)); __ ldr(ip, FieldMemOperand(ip, SharedFunctionInfo::kCodeOffset));
......
...@@ -148,6 +148,9 @@ void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) { ...@@ -148,6 +148,9 @@ void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
// Load context from the function. // Load context from the function.
__ Ldr(cp, FieldMemOperand(x1, JSFunction::kContextOffset)); __ Ldr(cp, FieldMemOperand(x1, JSFunction::kContextOffset));
// Clear new.target as a safety measure.
__ LoadRoot(x3, Heap::kUndefinedValueRootIndex);
// Get function code. // Get function code.
__ Ldr(scratch, FieldMemOperand(x1, JSFunction::kSharedFunctionInfoOffset)); __ Ldr(scratch, FieldMemOperand(x1, JSFunction::kSharedFunctionInfoOffset));
__ Ldr(scratch, FieldMemOperand(scratch, SharedFunctionInfo::kCodeOffset)); __ Ldr(scratch, FieldMemOperand(scratch, SharedFunctionInfo::kCodeOffset));
......
...@@ -125,13 +125,16 @@ void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) { ...@@ -125,13 +125,16 @@ void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
// Load context from the function. // Load context from the function.
__ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
// Clear new.target register as a safety measure.
__ mov(edx, masm->isolate()->factory()->undefined_value());
// Get function code. // Get function code.
__ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); __ mov(ebx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
__ mov(edx, FieldOperand(edx, SharedFunctionInfo::kCodeOffset)); __ mov(ebx, FieldOperand(ebx, SharedFunctionInfo::kCodeOffset));
__ lea(edx, FieldOperand(edx, Code::kHeaderSize)); __ lea(ebx, FieldOperand(ebx, Code::kHeaderSize));
// Re-run JSFunction, edi is function, esi is context. // Re-run JSFunction, edi is function, esi is context.
__ jmp(edx); __ jmp(ebx);
} }
......
...@@ -1660,20 +1660,60 @@ static const char* DropFrames(Vector<StackFrame*> frames, ...@@ -1660,20 +1660,60 @@ static const char* DropFrames(Vector<StackFrame*> frames,
// Finding no such frames does not mean error. // Finding no such frames does not mean error.
class MultipleFunctionTarget { class MultipleFunctionTarget {
public: public:
MultipleFunctionTarget(Handle<JSArray> shared_info_array, MultipleFunctionTarget(Handle<JSArray> old_shared_array,
Handle<JSArray> result) Handle<JSArray> new_shared_array,
: m_shared_info_array(shared_info_array), Handle<JSArray> result)
m_result(result) {} : old_shared_array_(old_shared_array),
new_shared_array_(new_shared_array),
result_(result) {}
bool MatchActivation(StackFrame* frame, bool MatchActivation(StackFrame* frame,
LiveEdit::FunctionPatchabilityStatus status) { LiveEdit::FunctionPatchabilityStatus status) {
return CheckActivation(m_shared_info_array, m_result, frame, status); return CheckActivation(old_shared_array_, result_, frame, status);
} }
const char* GetNotFoundMessage() const { const char* GetNotFoundMessage() const {
return NULL; return NULL;
} }
bool FrameUsesNewTarget(StackFrame* frame) {
if (!frame->is_java_script()) return false;
JavaScriptFrame* jsframe = JavaScriptFrame::cast(frame);
Handle<SharedFunctionInfo> old_shared(jsframe->function()->shared());
Isolate* isolate = old_shared->GetIsolate();
int len = GetArrayLength(old_shared_array_);
// Find corresponding new shared function info and return whether it
// references new.target.
for (int i = 0; i < len; i++) {
HandleScope scope(isolate);
Handle<Object> old_element =
Object::GetElement(isolate, old_shared_array_, i).ToHandleChecked();
if (!old_shared.is_identical_to(UnwrapSharedFunctionInfoFromJSValue(
Handle<JSValue>::cast(old_element)))) {
continue;
}
Handle<Object> new_element =
Object::GetElement(isolate, new_shared_array_, i).ToHandleChecked();
if (new_element->IsUndefined()) return false;
Handle<SharedFunctionInfo> new_shared =
UnwrapSharedFunctionInfoFromJSValue(
Handle<JSValue>::cast(new_element));
if (new_shared->scope_info()->HasNewTarget()) {
SetElementSloppy(
result_, i,
Handle<Smi>(
Smi::FromInt(
LiveEdit::FUNCTION_BLOCKED_NO_NEW_TARGET_ON_RESTART),
isolate));
return true;
}
return false;
}
return false;
}
private: private:
Handle<JSArray> m_shared_info_array; Handle<JSArray> old_shared_array_;
Handle<JSArray> m_result; Handle<JSArray> new_shared_array_;
Handle<JSArray> result_;
}; };
...@@ -1720,11 +1760,14 @@ static const char* DropActivationsInActiveThreadImpl(Isolate* isolate, ...@@ -1720,11 +1760,14 @@ static const char* DropActivationsInActiveThreadImpl(Isolate* isolate,
non_droppable_reason = LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE; non_droppable_reason = LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE;
break; break;
} }
if (frame->is_java_script() && if (frame->is_java_script()) {
JavaScriptFrame::cast(frame)->function()->shared()->is_generator()) { SharedFunctionInfo* shared =
non_droppable_frame_found = true; JavaScriptFrame::cast(frame)->function()->shared();
non_droppable_reason = LiveEdit::FUNCTION_BLOCKED_UNDER_GENERATOR; if (shared->is_generator()) {
break; non_droppable_frame_found = true;
non_droppable_reason = LiveEdit::FUNCTION_BLOCKED_UNDER_GENERATOR;
break;
}
} }
if (target.MatchActivation( if (target.MatchActivation(
frame, LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) { frame, LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) {
...@@ -1748,6 +1791,9 @@ static const char* DropActivationsInActiveThreadImpl(Isolate* isolate, ...@@ -1748,6 +1791,9 @@ static const char* DropActivationsInActiveThreadImpl(Isolate* isolate,
} }
} }
// We cannot restart a frame that uses new.target.
if (target.FrameUsesNewTarget(frames[bottom_js_frame_index])) return NULL;
if (!do_drop) { if (!do_drop) {
// We are in check-only mode. // We are in check-only mode.
return NULL; return NULL;
...@@ -1785,9 +1831,10 @@ static const char* DropActivationsInActiveThreadImpl(Isolate* isolate, ...@@ -1785,9 +1831,10 @@ static const char* DropActivationsInActiveThreadImpl(Isolate* isolate,
// Fills result array with statuses of functions. Modifies the stack // Fills result array with statuses of functions. Modifies the stack
// removing all listed function if possible and if do_drop is true. // removing all listed function if possible and if do_drop is true.
static const char* DropActivationsInActiveThread( static const char* DropActivationsInActiveThread(
Handle<JSArray> shared_info_array, Handle<JSArray> result, bool do_drop) { Handle<JSArray> old_shared_array, Handle<JSArray> new_shared_array,
MultipleFunctionTarget target(shared_info_array, result); Handle<JSArray> result, bool do_drop) {
Isolate* isolate = shared_info_array->GetIsolate(); MultipleFunctionTarget target(old_shared_array, new_shared_array, result);
Isolate* isolate = old_shared_array->GetIsolate();
const char* message = const char* message =
DropActivationsInActiveThreadImpl(isolate, target, do_drop); DropActivationsInActiveThreadImpl(isolate, target, do_drop);
...@@ -1795,7 +1842,7 @@ static const char* DropActivationsInActiveThread( ...@@ -1795,7 +1842,7 @@ static const char* DropActivationsInActiveThread(
return message; return message;
} }
int array_len = GetArrayLength(shared_info_array); int array_len = GetArrayLength(old_shared_array);
// Replace "blocked on active" with "replaced on active" status. // Replace "blocked on active" with "replaced on active" status.
for (int i = 0; i < array_len; i++) { for (int i = 0; i < array_len; i++) {
...@@ -1851,16 +1898,16 @@ bool LiveEdit::FindActiveGenerators(Handle<FixedArray> shared_info_array, ...@@ -1851,16 +1898,16 @@ bool LiveEdit::FindActiveGenerators(Handle<FixedArray> shared_info_array,
class InactiveThreadActivationsChecker : public ThreadVisitor { class InactiveThreadActivationsChecker : public ThreadVisitor {
public: public:
InactiveThreadActivationsChecker(Handle<JSArray> shared_info_array, InactiveThreadActivationsChecker(Handle<JSArray> old_shared_array,
Handle<JSArray> result) Handle<JSArray> result)
: shared_info_array_(shared_info_array), result_(result), : old_shared_array_(old_shared_array),
has_blocked_functions_(false) { result_(result),
} has_blocked_functions_(false) {}
void VisitThread(Isolate* isolate, ThreadLocalTop* top) { void VisitThread(Isolate* isolate, ThreadLocalTop* top) {
for (StackFrameIterator it(isolate, top); !it.done(); it.Advance()) { for (StackFrameIterator it(isolate, top); !it.done(); it.Advance()) {
has_blocked_functions_ |= CheckActivation( has_blocked_functions_ |=
shared_info_array_, result_, it.frame(), CheckActivation(old_shared_array_, result_, it.frame(),
LiveEdit::FUNCTION_BLOCKED_ON_OTHER_STACK); LiveEdit::FUNCTION_BLOCKED_ON_OTHER_STACK);
} }
} }
bool HasBlockedFunctions() { bool HasBlockedFunctions() {
...@@ -1868,20 +1915,21 @@ class InactiveThreadActivationsChecker : public ThreadVisitor { ...@@ -1868,20 +1915,21 @@ class InactiveThreadActivationsChecker : public ThreadVisitor {
} }
private: private:
Handle<JSArray> shared_info_array_; Handle<JSArray> old_shared_array_;
Handle<JSArray> result_; Handle<JSArray> result_;
bool has_blocked_functions_; bool has_blocked_functions_;
}; };
Handle<JSArray> LiveEdit::CheckAndDropActivations( Handle<JSArray> LiveEdit::CheckAndDropActivations(
Handle<JSArray> shared_info_array, bool do_drop) { Handle<JSArray> old_shared_array, Handle<JSArray> new_shared_array,
Isolate* isolate = shared_info_array->GetIsolate(); bool do_drop) {
int len = GetArrayLength(shared_info_array); Isolate* isolate = old_shared_array->GetIsolate();
int len = GetArrayLength(old_shared_array);
DCHECK(shared_info_array->HasFastElements()); DCHECK(old_shared_array->HasFastElements());
Handle<FixedArray> shared_info_array_elements( Handle<FixedArray> old_shared_array_elements(
FixedArray::cast(shared_info_array->elements())); FixedArray::cast(old_shared_array->elements()));
Handle<JSArray> result = isolate->factory()->NewJSArray(len); Handle<JSArray> result = isolate->factory()->NewJSArray(len);
Handle<FixedArray> result_elements = Handle<FixedArray> result_elements =
...@@ -1897,12 +1945,12 @@ Handle<JSArray> LiveEdit::CheckAndDropActivations( ...@@ -1897,12 +1945,12 @@ Handle<JSArray> LiveEdit::CheckAndDropActivations(
// running (as we wouldn't want to restart them, because we don't know where // 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 // to restart them from) or suspended. Fail if any one corresponds to the set
// of functions being edited. // of functions being edited.
if (FindActiveGenerators(shared_info_array_elements, result_elements, len)) { if (FindActiveGenerators(old_shared_array_elements, result_elements, len)) {
return result; return result;
} }
// Check inactive threads. Fail if some functions are blocked there. // Check inactive threads. Fail if some functions are blocked there.
InactiveThreadActivationsChecker inactive_threads_checker(shared_info_array, InactiveThreadActivationsChecker inactive_threads_checker(old_shared_array,
result); result);
isolate->thread_manager()->IterateArchivedThreads( isolate->thread_manager()->IterateArchivedThreads(
&inactive_threads_checker); &inactive_threads_checker);
...@@ -1911,8 +1959,8 @@ Handle<JSArray> LiveEdit::CheckAndDropActivations( ...@@ -1911,8 +1959,8 @@ Handle<JSArray> LiveEdit::CheckAndDropActivations(
} }
// Try to drop activations from the current stack. // Try to drop activations from the current stack.
const char* error_message = const char* error_message = DropActivationsInActiveThread(
DropActivationsInActiveThread(shared_info_array, result, do_drop); old_shared_array, new_shared_array, result, do_drop);
if (error_message != NULL) { if (error_message != NULL) {
// Add error message as an array extra element. // Add error message as an array extra element.
Handle<String> str = Handle<String> str =
...@@ -1945,6 +1993,17 @@ class SingleFrameTarget { ...@@ -1945,6 +1993,17 @@ class SingleFrameTarget {
LiveEdit::FunctionPatchabilityStatus saved_status() { LiveEdit::FunctionPatchabilityStatus saved_status() {
return m_saved_status; return m_saved_status;
} }
void set_status(LiveEdit::FunctionPatchabilityStatus status) {
m_saved_status = status;
}
bool FrameUsesNewTarget(StackFrame* frame) {
if (!frame->is_java_script()) return false;
JavaScriptFrame* jsframe = JavaScriptFrame::cast(frame);
Handle<SharedFunctionInfo> shared(jsframe->function()->shared());
return shared->scope_info()->HasNewTarget();
}
private: private:
JavaScriptFrame* m_frame; JavaScriptFrame* m_frame;
LiveEdit::FunctionPatchabilityStatus m_saved_status; LiveEdit::FunctionPatchabilityStatus m_saved_status;
......
...@@ -117,7 +117,8 @@ class LiveEdit : AllStatic { ...@@ -117,7 +117,8 @@ class LiveEdit : AllStatic {
// has restart the lowest found frames and drops all other frames above // has restart the lowest found frames and drops all other frames above
// if possible and if do_drop is true. // if possible and if do_drop is true.
static Handle<JSArray> CheckAndDropActivations( static Handle<JSArray> CheckAndDropActivations(
Handle<JSArray> shared_info_array, bool do_drop); Handle<JSArray> old_shared_array, Handle<JSArray> new_shared_array,
bool do_drop);
// Restarts the call frame and completely drops all frames above it. // Restarts the call frame and completely drops all frames above it.
// Return error message or NULL. // Return error message or NULL.
...@@ -131,7 +132,8 @@ class LiveEdit : AllStatic { ...@@ -131,7 +132,8 @@ class LiveEdit : AllStatic {
FUNCTION_BLOCKED_UNDER_NATIVE_CODE = 4, FUNCTION_BLOCKED_UNDER_NATIVE_CODE = 4,
FUNCTION_REPLACED_ON_ACTIVE_STACK = 5, FUNCTION_REPLACED_ON_ACTIVE_STACK = 5,
FUNCTION_BLOCKED_UNDER_GENERATOR = 6, FUNCTION_BLOCKED_UNDER_GENERATOR = 6,
FUNCTION_BLOCKED_ACTIVE_GENERATOR = 7 FUNCTION_BLOCKED_ACTIVE_GENERATOR = 7,
FUNCTION_BLOCKED_NO_NEW_TARGET_ON_RESTART = 8
}; };
// Compares 2 strings line-by-line, then token-wise and returns diff in form // Compares 2 strings line-by-line, then token-wise and returns diff in form
......
...@@ -142,14 +142,17 @@ ...@@ -142,14 +142,17 @@
HarvestTodo(root_old_node); HarvestTodo(root_old_node);
// Collect shared infos for functions whose code need to be patched. // Collect shared infos for functions whose code need to be patched.
var replaced_function_infos = new GlobalArray(); var replaced_function_old_infos = new GlobalArray();
var replaced_function_new_infos = new GlobalArray();
for (var i = 0; i < replace_code_list.length; i++) { for (var i = 0; i < replace_code_list.length; i++) {
var live_shared_function_infos = var old_infos = replace_code_list[i].live_shared_function_infos;
replace_code_list[i].live_shared_function_infos; var new_info =
replace_code_list[i].corresponding_node.info.shared_function_info;
if (live_shared_function_infos) {
for (var j = 0; j < live_shared_function_infos.length; j++) { if (old_infos) {
replaced_function_infos.push(live_shared_function_infos[j]); for (var j = 0; j < old_infos.length; j++) {
replaced_function_old_infos.push(old_infos[j]);
replaced_function_new_infos.push(new_info);
} }
} }
} }
...@@ -159,7 +162,9 @@ ...@@ -159,7 +162,9 @@
// Check that function being patched is not currently on stack or drop them. // Check that function being patched is not currently on stack or drop them.
var dropped_functions_number = var dropped_functions_number =
CheckStackActivations(replaced_function_infos, change_log); CheckStackActivations(replaced_function_old_infos,
replaced_function_new_infos,
change_log);
// Our current implementation requires client to manually issue "step in" // Our current implementation requires client to manually issue "step in"
// command for correct stack state if the stack was modified. // command for correct stack state if the stack was modified.
...@@ -910,21 +915,24 @@ ...@@ -910,21 +915,24 @@
// For array of wrapped shared function infos checks that none of them // For array of wrapped shared function infos checks that none of them
// have activations on stack (of any thread). Throws a Failure exception // have activations on stack (of any thread). Throws a Failure exception
// if this proves to be false. // if this proves to be false.
function CheckStackActivations(shared_wrapper_list, change_log) { function CheckStackActivations(old_shared_wrapper_list,
var shared_list = new GlobalArray(); new_shared_list,
for (var i = 0; i < shared_wrapper_list.length; i++) { change_log) {
shared_list[i] = shared_wrapper_list[i].info; var old_shared_list = new GlobalArray();
for (var i = 0; i < old_shared_wrapper_list.length; i++) {
old_shared_list[i] = old_shared_wrapper_list[i].info;
} }
var result = %LiveEditCheckAndDropActivations(shared_list, true); var result = %LiveEditCheckAndDropActivations(
if (result[shared_list.length]) { old_shared_list, new_shared_list, true);
if (result[old_shared_wrapper_list.length]) {
// Extra array element may contain error message. // Extra array element may contain error message.
throw new Failure(result[shared_list.length]); throw new Failure(result[old_shared_wrapper_list.length]);
} }
var problems = new GlobalArray(); var problems = new GlobalArray();
var dropped = new GlobalArray(); var dropped = new GlobalArray();
for (var i = 0; i < shared_list.length; i++) { for (var i = 0; i < old_shared_list.length; i++) {
var shared = shared_wrapper_list[i]; var shared = old_shared_wrapper_list[i];
if (result[i] == FunctionPatchabilityStatus.REPLACED_ON_ACTIVE_STACK) { if (result[i] == FunctionPatchabilityStatus.REPLACED_ON_ACTIVE_STACK) {
dropped.push({ name: shared.function_name } ); dropped.push({ name: shared.function_name } );
} else if (result[i] != FunctionPatchabilityStatus.AVAILABLE_FOR_PATCH) { } else if (result[i] != FunctionPatchabilityStatus.AVAILABLE_FOR_PATCH) {
...@@ -957,7 +965,8 @@ ...@@ -957,7 +965,8 @@
BLOCKED_UNDER_NATIVE_CODE: 4, BLOCKED_UNDER_NATIVE_CODE: 4,
REPLACED_ON_ACTIVE_STACK: 5, REPLACED_ON_ACTIVE_STACK: 5,
BLOCKED_UNDER_GENERATOR: 6, BLOCKED_UNDER_GENERATOR: 6,
BLOCKED_ACTIVE_GENERATOR: 7 BLOCKED_ACTIVE_GENERATOR: 7,
BLOCKED_NO_NEW_TARGET_ON_RESTART: 8
}; };
FunctionPatchabilityStatus.SymbolName = function(code) { FunctionPatchabilityStatus.SymbolName = function(code) {
......
...@@ -128,6 +128,9 @@ void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) { ...@@ -128,6 +128,9 @@ void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
// Load context from the function. // Load context from the function.
__ lw(cp, FieldMemOperand(a1, JSFunction::kContextOffset)); __ lw(cp, FieldMemOperand(a1, JSFunction::kContextOffset));
// Clear new.target as a safety measure.
__ LoadRoot(a3, Heap::kUndefinedValueRootIndex);
// Get function code. // Get function code.
__ lw(at, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset)); __ lw(at, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
__ lw(at, FieldMemOperand(at, SharedFunctionInfo::kCodeOffset)); __ lw(at, FieldMemOperand(at, SharedFunctionInfo::kCodeOffset));
......
...@@ -130,6 +130,9 @@ void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) { ...@@ -130,6 +130,9 @@ void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
// Load context from the function. // Load context from the function.
__ ld(cp, FieldMemOperand(a1, JSFunction::kContextOffset)); __ ld(cp, FieldMemOperand(a1, JSFunction::kContextOffset));
// Clear new.target as a safety measure.
__ LoadRoot(a3, Heap::kUndefinedValueRootIndex);
// Get function code. // Get function code.
__ ld(at, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset)); __ ld(at, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
__ ld(at, FieldMemOperand(at, SharedFunctionInfo::kCodeOffset)); __ ld(at, FieldMemOperand(at, SharedFunctionInfo::kCodeOffset));
......
...@@ -127,13 +127,16 @@ void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) { ...@@ -127,13 +127,16 @@ void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
// Load context from the function. // Load context from the function.
__ movp(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); __ movp(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
// Clear new.target as a safety measure.
__ LoadRoot(rdx, Heap::kUndefinedValueRootIndex);
// Get function code. // Get function code.
__ movp(rdx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset)); __ movp(rbx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
__ movp(rdx, FieldOperand(rdx, SharedFunctionInfo::kCodeOffset)); __ movp(rbx, FieldOperand(rbx, SharedFunctionInfo::kCodeOffset));
__ leap(rdx, FieldOperand(rdx, Code::kHeaderSize)); __ leap(rbx, FieldOperand(rbx, Code::kHeaderSize));
// Re-run JSFunction, rdi is function, rsi is context. // Re-run JSFunction, rdi is function, rsi is context.
__ jmp(rdx); __ jmp(rbx);
} }
const bool LiveEdit::kFrameDropperSupported = true; const bool LiveEdit::kFrameDropperSupported = true;
......
...@@ -4011,6 +4011,9 @@ class ScopeInfo : public FixedArray { ...@@ -4011,6 +4011,9 @@ class ScopeInfo : public FixedArray {
// or context-allocated? // or context-allocated?
bool HasAllocatedReceiver(); bool HasAllocatedReceiver();
// Does this scope declare a "new.target" binding?
bool HasNewTarget();
// Is this scope the scope of a named function expression? // Is this scope the scope of a named function expression?
bool HasFunctionName(); bool HasFunctionName();
...@@ -4219,9 +4222,10 @@ class ScopeInfo : public FixedArray { ...@@ -4219,9 +4222,10 @@ class ScopeInfo : public FixedArray {
class ReceiverVariableField class ReceiverVariableField
: public BitField<VariableAllocationInfo, DeclarationScopeField::kNext, : public BitField<VariableAllocationInfo, DeclarationScopeField::kNext,
2> {}; 2> {};
class HasNewTargetField
: public BitField<bool, ReceiverVariableField::kNext, 1> {};
class FunctionVariableField class FunctionVariableField
: public BitField<VariableAllocationInfo, ReceiverVariableField::kNext, : public BitField<VariableAllocationInfo, HasNewTargetField::kNext, 2> {};
2> {};
class FunctionVariableMode class FunctionVariableMode
: public BitField<VariableMode, FunctionVariableField::kNext, 3> {}; : public BitField<VariableMode, FunctionVariableField::kNext, 3> {};
class AsmModuleField : public BitField<bool, FunctionVariableMode::kNext, 1> { class AsmModuleField : public BitField<bool, FunctionVariableMode::kNext, 1> {
......
...@@ -200,22 +200,34 @@ RUNTIME_FUNCTION(Runtime_LiveEditPatchFunctionPositions) { ...@@ -200,22 +200,34 @@ RUNTIME_FUNCTION(Runtime_LiveEditPatchFunctionPositions) {
RUNTIME_FUNCTION(Runtime_LiveEditCheckAndDropActivations) { RUNTIME_FUNCTION(Runtime_LiveEditCheckAndDropActivations) {
HandleScope scope(isolate); HandleScope scope(isolate);
CHECK(isolate->debug()->live_edit_enabled()); CHECK(isolate->debug()->live_edit_enabled());
DCHECK(args.length() == 2); DCHECK(args.length() == 3);
CONVERT_ARG_HANDLE_CHECKED(JSArray, shared_array, 0); CONVERT_ARG_HANDLE_CHECKED(JSArray, old_shared_array, 0);
CONVERT_BOOLEAN_ARG_CHECKED(do_drop, 1); CONVERT_ARG_HANDLE_CHECKED(JSArray, new_shared_array, 1);
RUNTIME_ASSERT(shared_array->length()->IsSmi()); CONVERT_BOOLEAN_ARG_CHECKED(do_drop, 2);
RUNTIME_ASSERT(shared_array->HasFastElements()) USE(new_shared_array);
int array_length = Smi::cast(shared_array->length())->value(); RUNTIME_ASSERT(old_shared_array->length()->IsSmi());
RUNTIME_ASSERT(new_shared_array->length() == old_shared_array->length());
RUNTIME_ASSERT(old_shared_array->HasFastElements())
RUNTIME_ASSERT(new_shared_array->HasFastElements())
int array_length = Smi::cast(old_shared_array->length())->value();
for (int i = 0; i < array_length; i++) { for (int i = 0; i < array_length; i++) {
Handle<Object> element; Handle<Object> old_element;
Handle<Object> new_element;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, old_element, Object::GetElement(isolate, old_shared_array, i));
RUNTIME_ASSERT(
old_element->IsJSValue() &&
Handle<JSValue>::cast(old_element)->value()->IsSharedFunctionInfo());
ASSIGN_RETURN_FAILURE_ON_EXCEPTION( ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, element, Object::GetElement(isolate, shared_array, i)); isolate, new_element, Object::GetElement(isolate, new_shared_array, i));
RUNTIME_ASSERT( RUNTIME_ASSERT(
element->IsJSValue() && new_element->IsUndefined() ||
Handle<JSValue>::cast(element)->value()->IsSharedFunctionInfo()); (new_element->IsJSValue() &&
Handle<JSValue>::cast(new_element)->value()->IsSharedFunctionInfo()));
} }
return *LiveEdit::CheckAndDropActivations(shared_array, do_drop); return *LiveEdit::CheckAndDropActivations(old_shared_array, new_shared_array,
do_drop);
} }
......
...@@ -377,7 +377,7 @@ namespace internal { ...@@ -377,7 +377,7 @@ namespace internal {
F(LiveEditFunctionSetScript, 2, 1) \ F(LiveEditFunctionSetScript, 2, 1) \
F(LiveEditReplaceRefToNestedFunction, 3, 1) \ F(LiveEditReplaceRefToNestedFunction, 3, 1) \
F(LiveEditPatchFunctionPositions, 2, 1) \ F(LiveEditPatchFunctionPositions, 2, 1) \
F(LiveEditCheckAndDropActivations, 2, 1) \ F(LiveEditCheckAndDropActivations, 3, 1) \
F(LiveEditCompareStrings, 2, 1) \ F(LiveEditCompareStrings, 2, 1) \
F(LiveEditRestartFrame, 2, 1) F(LiveEditRestartFrame, 2, 1)
......
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-reflect --expose-debug-as debug --allow-natives-syntax
// Test that live-editing a frame that uses new.target fails.
Debug = debug.Debug
var calls = 0;
var exceptions = 0;
var results = [];
var replace_again;
eval(`
function LogNewTarget() {
calls++;
ReplaceOnce();
results.push(true);
results.push(new.target);
}
`);
function Dummy() {}
function Replace(fun, original, patch) {
%ExecuteInDebugContext(function() {
var change_log = [];
try {
var script = Debug.findScript(fun);
var patch_pos = script.source.indexOf(original);
Debug.LiveEdit.TestApi.ApplySingleChunkPatch(
script, patch_pos, original.length, patch, change_log);
} catch (e) {
assertEquals("BLOCKED_NO_NEW_TARGET_ON_RESTART",
change_log[0].functions_on_stack[0].replace_problem);
assertInstanceof(e, Debug.LiveEdit.Failure);
exceptions++;
}
});
}
function ReplaceOnce() {
if (replace_again) {
replace_again = false;
Replace(LogNewTarget, "true", "false");
}
}
function Revert() {
Replace(LogNewTarget, "false", "true");
}
replace_again = true;
ReplaceOnce();
new LogNewTarget();
Revert();
assertEquals(1, calls);
assertEquals(0, exceptions);
assertEquals([false, LogNewTarget], results);
replace_again = true;
LogNewTarget();
replace_again = true;
new LogNewTarget();
replace_again = true;
Reflect.construct(LogNewTarget, [], Dummy);
assertEquals(
[false, LogNewTarget, true, undefined, true, LogNewTarget, true, Dummy],
results);
assertEquals(4, calls); // No restarts
assertEquals(3, exceptions); // Replace failed.
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --expose-debug-as debug --allow-natives-syntax
// Test that live-editing a frame to introduce new.target fails.
Debug = debug.Debug
var calls = 0;
var exceptions = 0;
var results = [];
var replace_again;
eval(`
function LogNewTarget() {
calls++;
ReplaceOnce();
results.push(true);
}
`);
function Replace(fun, original, patch) {
%ExecuteInDebugContext(function() {
var change_log = [];
try {
var script = Debug.findScript(fun);
var patch_pos = script.source.indexOf(original);
Debug.LiveEdit.TestApi.ApplySingleChunkPatch(
script, patch_pos, original.length, patch, change_log);
} catch (e) {
assertEquals("BLOCKED_NO_NEW_TARGET_ON_RESTART",
change_log[0].functions_on_stack[0].replace_problem);
assertInstanceof(e, Debug.LiveEdit.Failure);
exceptions++;
}
});
}
function ReplaceOnce(x) {
if (replace_again) {
replace_again = false;
Replace(LogNewTarget, "true", "new.target");
}
}
function Revert() {
Replace(LogNewTarget, "new.target", "true");
}
replace_again = true;
ReplaceOnce();
new LogNewTarget();
Revert();
assertEquals(1, calls);
assertEquals(0, exceptions);
assertEquals([LogNewTarget], results);
replace_again = true;
new LogNewTarget();
assertEquals(2, calls); // No restarts
assertEquals(1, exceptions); // Replace failed.
assertEquals([LogNewTarget, true], results);
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --expose-debug-as debug --allow-natives-syntax
// Test that live-editing a frame above one that uses new.target succeeds.
Debug = debug.Debug
var wrapper_calls = 0;
var construct_calls = 0;
var exceptions = 0;
var results = [];
var replace_again;
eval(`
function LogNewTarget(arg) {
construct_calls++;
results.push(new.target);
}
function Wrapper() {
wrapper_calls++;
ReplaceOnce();
new LogNewTarget(true);
}
`);
function Replace(fun, original, patch) {
%ExecuteInDebugContext(function() {
var change_log = [];
try {
var script = Debug.findScript(fun);
var patch_pos = script.source.indexOf(original);
Debug.LiveEdit.TestApi.ApplySingleChunkPatch(
script, patch_pos, original.length, patch, change_log);
} catch (e) {
exceptions++;
}
});
}
function ReplaceOnce(x) {
if (replace_again) {
replace_again = false;
Replace(Wrapper, "true", "false");
}
}
function Revert() {
Replace(Wrapper, "false", "true");
}
replace_again = true;
ReplaceOnce();
Wrapper();
Revert();
assertEquals(1, construct_calls);
assertEquals(1, wrapper_calls);
assertEquals(0, exceptions); // Replace succeeds
assertEquals([LogNewTarget], results);
Wrapper();
assertEquals(2, construct_calls);
assertEquals(2, wrapper_calls);
assertEquals(0, exceptions); // Replace succeeds
assertEquals([LogNewTarget, LogNewTarget], results);
replace_again = true;
Wrapper();
assertEquals(3, construct_calls);
assertEquals(4, wrapper_calls); // Restarts
assertEquals(0, exceptions); // Replace succeeds
assertEquals([LogNewTarget, LogNewTarget, LogNewTarget], results);
...@@ -5,5 +5,6 @@ ...@@ -5,5 +5,6 @@
// Flags: --allow-natives-syntax // Flags: --allow-natives-syntax
var a = new Array(); var a = new Array();
var b = new Array();
Object.freeze(a); Object.freeze(a);
assertThrows(function() { %LiveEditCheckAndDropActivations(a, true); }); assertThrows(function() { %LiveEditCheckAndDropActivations(a, b, true); });
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment