[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}
This commit is contained in:
parent
186f67085c
commit
6fca870240
@ -49,6 +49,8 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone,
|
||||
receiver_info = NONE;
|
||||
}
|
||||
|
||||
bool has_new_target = scope->new_target_var() != nullptr;
|
||||
|
||||
// Determine use and location of the function variable if it is present.
|
||||
VariableAllocationInfo function_name_info;
|
||||
VariableMode function_variable_mode;
|
||||
@ -90,6 +92,7 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone,
|
||||
LanguageModeField::encode(scope->language_mode()) |
|
||||
DeclarationScopeField::encode(scope->is_declaration_scope()) |
|
||||
ReceiverVariableField::encode(receiver_info) |
|
||||
HasNewTargetField::encode(has_new_target) |
|
||||
FunctionVariableField::encode(function_name_info) |
|
||||
FunctionVariableMode::encode(function_variable_mode) |
|
||||
AsmModuleField::encode(scope->asm_module()) |
|
||||
@ -374,6 +377,9 @@ bool ScopeInfo::HasAllocatedReceiver() {
|
||||
}
|
||||
|
||||
|
||||
bool ScopeInfo::HasNewTarget() { return HasNewTargetField::decode(Flags()); }
|
||||
|
||||
|
||||
bool ScopeInfo::HasFunctionName() {
|
||||
if (length() > 0) {
|
||||
return NONE != FunctionVariableField::decode(Flags());
|
||||
|
@ -138,6 +138,9 @@ void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
|
||||
// Load context from the function.
|
||||
__ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
|
||||
|
||||
// Clear new.target as a safety measure.
|
||||
__ LoadRoot(r3, Heap::kUndefinedValueRootIndex);
|
||||
|
||||
// Get function code.
|
||||
__ ldr(ip, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ ldr(ip, FieldMemOperand(ip, SharedFunctionInfo::kCodeOffset));
|
||||
|
@ -148,6 +148,9 @@ void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
|
||||
// Load context from the function.
|
||||
__ Ldr(cp, FieldMemOperand(x1, JSFunction::kContextOffset));
|
||||
|
||||
// Clear new.target as a safety measure.
|
||||
__ LoadRoot(x3, Heap::kUndefinedValueRootIndex);
|
||||
|
||||
// Get function code.
|
||||
__ Ldr(scratch, FieldMemOperand(x1, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ Ldr(scratch, FieldMemOperand(scratch, SharedFunctionInfo::kCodeOffset));
|
||||
|
@ -125,13 +125,16 @@ void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
|
||||
// Load context from the function.
|
||||
__ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
|
||||
|
||||
// Clear new.target register as a safety measure.
|
||||
__ mov(edx, masm->isolate()->factory()->undefined_value());
|
||||
|
||||
// Get function code.
|
||||
__ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ mov(edx, FieldOperand(edx, SharedFunctionInfo::kCodeOffset));
|
||||
__ lea(edx, FieldOperand(edx, Code::kHeaderSize));
|
||||
__ mov(ebx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ mov(ebx, FieldOperand(ebx, SharedFunctionInfo::kCodeOffset));
|
||||
__ lea(ebx, FieldOperand(ebx, Code::kHeaderSize));
|
||||
|
||||
// Re-run JSFunction, edi is function, esi is context.
|
||||
__ jmp(edx);
|
||||
__ jmp(ebx);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1660,20 +1660,60 @@ static const char* DropFrames(Vector<StackFrame*> frames,
|
||||
// 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) {}
|
||||
MultipleFunctionTarget(Handle<JSArray> old_shared_array,
|
||||
Handle<JSArray> new_shared_array,
|
||||
Handle<JSArray> result)
|
||||
: old_shared_array_(old_shared_array),
|
||||
new_shared_array_(new_shared_array),
|
||||
result_(result) {}
|
||||
bool MatchActivation(StackFrame* frame,
|
||||
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 {
|
||||
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:
|
||||
Handle<JSArray> m_shared_info_array;
|
||||
Handle<JSArray> m_result;
|
||||
Handle<JSArray> old_shared_array_;
|
||||
Handle<JSArray> new_shared_array_;
|
||||
Handle<JSArray> result_;
|
||||
};
|
||||
|
||||
|
||||
@ -1720,11 +1760,14 @@ static const char* DropActivationsInActiveThreadImpl(Isolate* isolate,
|
||||
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 (frame->is_java_script()) {
|
||||
SharedFunctionInfo* shared =
|
||||
JavaScriptFrame::cast(frame)->function()->shared();
|
||||
if (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)) {
|
||||
@ -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) {
|
||||
// We are in check-only mode.
|
||||
return NULL;
|
||||
@ -1785,9 +1831,10 @@ static const char* DropActivationsInActiveThreadImpl(Isolate* isolate,
|
||||
// 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();
|
||||
Handle<JSArray> old_shared_array, Handle<JSArray> new_shared_array,
|
||||
Handle<JSArray> result, bool do_drop) {
|
||||
MultipleFunctionTarget target(old_shared_array, new_shared_array, result);
|
||||
Isolate* isolate = old_shared_array->GetIsolate();
|
||||
|
||||
const char* message =
|
||||
DropActivationsInActiveThreadImpl(isolate, target, do_drop);
|
||||
@ -1795,7 +1842,7 @@ static const char* DropActivationsInActiveThread(
|
||||
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.
|
||||
for (int i = 0; i < array_len; i++) {
|
||||
@ -1851,16 +1898,16 @@ bool LiveEdit::FindActiveGenerators(Handle<FixedArray> shared_info_array,
|
||||
|
||||
class InactiveThreadActivationsChecker : public ThreadVisitor {
|
||||
public:
|
||||
InactiveThreadActivationsChecker(Handle<JSArray> shared_info_array,
|
||||
InactiveThreadActivationsChecker(Handle<JSArray> old_shared_array,
|
||||
Handle<JSArray> result)
|
||||
: shared_info_array_(shared_info_array), result_(result),
|
||||
has_blocked_functions_(false) {
|
||||
}
|
||||
: old_shared_array_(old_shared_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);
|
||||
has_blocked_functions_ |=
|
||||
CheckActivation(old_shared_array_, result_, it.frame(),
|
||||
LiveEdit::FUNCTION_BLOCKED_ON_OTHER_STACK);
|
||||
}
|
||||
}
|
||||
bool HasBlockedFunctions() {
|
||||
@ -1868,20 +1915,21 @@ class InactiveThreadActivationsChecker : public ThreadVisitor {
|
||||
}
|
||||
|
||||
private:
|
||||
Handle<JSArray> shared_info_array_;
|
||||
Handle<JSArray> old_shared_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);
|
||||
Handle<JSArray> old_shared_array, Handle<JSArray> new_shared_array,
|
||||
bool do_drop) {
|
||||
Isolate* isolate = old_shared_array->GetIsolate();
|
||||
int len = GetArrayLength(old_shared_array);
|
||||
|
||||
DCHECK(shared_info_array->HasFastElements());
|
||||
Handle<FixedArray> shared_info_array_elements(
|
||||
FixedArray::cast(shared_info_array->elements()));
|
||||
DCHECK(old_shared_array->HasFastElements());
|
||||
Handle<FixedArray> old_shared_array_elements(
|
||||
FixedArray::cast(old_shared_array->elements()));
|
||||
|
||||
Handle<JSArray> result = isolate->factory()->NewJSArray(len);
|
||||
Handle<FixedArray> result_elements =
|
||||
@ -1897,12 +1945,12 @@ Handle<JSArray> LiveEdit::CheckAndDropActivations(
|
||||
// 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)) {
|
||||
if (FindActiveGenerators(old_shared_array_elements, result_elements, len)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// 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);
|
||||
isolate->thread_manager()->IterateArchivedThreads(
|
||||
&inactive_threads_checker);
|
||||
@ -1911,8 +1959,8 @@ Handle<JSArray> LiveEdit::CheckAndDropActivations(
|
||||
}
|
||||
|
||||
// Try to drop activations from the current stack.
|
||||
const char* error_message =
|
||||
DropActivationsInActiveThread(shared_info_array, result, do_drop);
|
||||
const char* error_message = DropActivationsInActiveThread(
|
||||
old_shared_array, new_shared_array, result, do_drop);
|
||||
if (error_message != NULL) {
|
||||
// Add error message as an array extra element.
|
||||
Handle<String> str =
|
||||
@ -1945,6 +1993,17 @@ class SingleFrameTarget {
|
||||
LiveEdit::FunctionPatchabilityStatus 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:
|
||||
JavaScriptFrame* m_frame;
|
||||
LiveEdit::FunctionPatchabilityStatus m_saved_status;
|
||||
|
@ -117,7 +117,8 @@ class LiveEdit : AllStatic {
|
||||
// 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);
|
||||
Handle<JSArray> old_shared_array, Handle<JSArray> new_shared_array,
|
||||
bool do_drop);
|
||||
|
||||
// Restarts the call frame and completely drops all frames above it.
|
||||
// Return error message or NULL.
|
||||
@ -131,7 +132,8 @@ class LiveEdit : AllStatic {
|
||||
FUNCTION_BLOCKED_UNDER_NATIVE_CODE = 4,
|
||||
FUNCTION_REPLACED_ON_ACTIVE_STACK = 5,
|
||||
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
|
||||
|
@ -142,14 +142,17 @@
|
||||
HarvestTodo(root_old_node);
|
||||
|
||||
// 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++) {
|
||||
var live_shared_function_infos =
|
||||
replace_code_list[i].live_shared_function_infos;
|
||||
var old_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++) {
|
||||
replaced_function_infos.push(live_shared_function_infos[j]);
|
||||
if (old_infos) {
|
||||
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 @@
|
||||
|
||||
// Check that function being patched is not currently on stack or drop them.
|
||||
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"
|
||||
// command for correct stack state if the stack was modified.
|
||||
@ -910,21 +915,24 @@
|
||||
// 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 GlobalArray();
|
||||
for (var i = 0; i < shared_wrapper_list.length; i++) {
|
||||
shared_list[i] = shared_wrapper_list[i].info;
|
||||
function CheckStackActivations(old_shared_wrapper_list,
|
||||
new_shared_list,
|
||||
change_log) {
|
||||
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);
|
||||
if (result[shared_list.length]) {
|
||||
var result = %LiveEditCheckAndDropActivations(
|
||||
old_shared_list, new_shared_list, true);
|
||||
if (result[old_shared_wrapper_list.length]) {
|
||||
// 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 dropped = new GlobalArray();
|
||||
for (var i = 0; i < shared_list.length; i++) {
|
||||
var shared = shared_wrapper_list[i];
|
||||
for (var i = 0; i < old_shared_list.length; i++) {
|
||||
var shared = old_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) {
|
||||
@ -957,7 +965,8 @@
|
||||
BLOCKED_UNDER_NATIVE_CODE: 4,
|
||||
REPLACED_ON_ACTIVE_STACK: 5,
|
||||
BLOCKED_UNDER_GENERATOR: 6,
|
||||
BLOCKED_ACTIVE_GENERATOR: 7
|
||||
BLOCKED_ACTIVE_GENERATOR: 7,
|
||||
BLOCKED_NO_NEW_TARGET_ON_RESTART: 8
|
||||
};
|
||||
|
||||
FunctionPatchabilityStatus.SymbolName = function(code) {
|
||||
|
@ -128,6 +128,9 @@ void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
|
||||
// Load context from the function.
|
||||
__ lw(cp, FieldMemOperand(a1, JSFunction::kContextOffset));
|
||||
|
||||
// Clear new.target as a safety measure.
|
||||
__ LoadRoot(a3, Heap::kUndefinedValueRootIndex);
|
||||
|
||||
// Get function code.
|
||||
__ lw(at, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ lw(at, FieldMemOperand(at, SharedFunctionInfo::kCodeOffset));
|
||||
|
@ -130,6 +130,9 @@ void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
|
||||
// Load context from the function.
|
||||
__ ld(cp, FieldMemOperand(a1, JSFunction::kContextOffset));
|
||||
|
||||
// Clear new.target as a safety measure.
|
||||
__ LoadRoot(a3, Heap::kUndefinedValueRootIndex);
|
||||
|
||||
// Get function code.
|
||||
__ ld(at, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ ld(at, FieldMemOperand(at, SharedFunctionInfo::kCodeOffset));
|
||||
|
@ -127,13 +127,16 @@ void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
|
||||
// Load context from the function.
|
||||
__ movp(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
|
||||
|
||||
// Clear new.target as a safety measure.
|
||||
__ LoadRoot(rdx, Heap::kUndefinedValueRootIndex);
|
||||
|
||||
// Get function code.
|
||||
__ movp(rdx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ movp(rdx, FieldOperand(rdx, SharedFunctionInfo::kCodeOffset));
|
||||
__ leap(rdx, FieldOperand(rdx, Code::kHeaderSize));
|
||||
__ movp(rbx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ movp(rbx, FieldOperand(rbx, SharedFunctionInfo::kCodeOffset));
|
||||
__ leap(rbx, FieldOperand(rbx, Code::kHeaderSize));
|
||||
|
||||
// Re-run JSFunction, rdi is function, rsi is context.
|
||||
__ jmp(rdx);
|
||||
__ jmp(rbx);
|
||||
}
|
||||
|
||||
const bool LiveEdit::kFrameDropperSupported = true;
|
||||
|
@ -4011,6 +4011,9 @@ class ScopeInfo : public FixedArray {
|
||||
// or context-allocated?
|
||||
bool HasAllocatedReceiver();
|
||||
|
||||
// Does this scope declare a "new.target" binding?
|
||||
bool HasNewTarget();
|
||||
|
||||
// Is this scope the scope of a named function expression?
|
||||
bool HasFunctionName();
|
||||
|
||||
@ -4219,9 +4222,10 @@ class ScopeInfo : public FixedArray {
|
||||
class ReceiverVariableField
|
||||
: public BitField<VariableAllocationInfo, DeclarationScopeField::kNext,
|
||||
2> {};
|
||||
class HasNewTargetField
|
||||
: public BitField<bool, ReceiverVariableField::kNext, 1> {};
|
||||
class FunctionVariableField
|
||||
: public BitField<VariableAllocationInfo, ReceiverVariableField::kNext,
|
||||
2> {};
|
||||
: public BitField<VariableAllocationInfo, HasNewTargetField::kNext, 2> {};
|
||||
class FunctionVariableMode
|
||||
: public BitField<VariableMode, FunctionVariableField::kNext, 3> {};
|
||||
class AsmModuleField : public BitField<bool, FunctionVariableMode::kNext, 1> {
|
||||
|
@ -200,22 +200,34 @@ RUNTIME_FUNCTION(Runtime_LiveEditPatchFunctionPositions) {
|
||||
RUNTIME_FUNCTION(Runtime_LiveEditCheckAndDropActivations) {
|
||||
HandleScope scope(isolate);
|
||||
CHECK(isolate->debug()->live_edit_enabled());
|
||||
DCHECK(args.length() == 2);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSArray, shared_array, 0);
|
||||
CONVERT_BOOLEAN_ARG_CHECKED(do_drop, 1);
|
||||
RUNTIME_ASSERT(shared_array->length()->IsSmi());
|
||||
RUNTIME_ASSERT(shared_array->HasFastElements())
|
||||
int array_length = Smi::cast(shared_array->length())->value();
|
||||
DCHECK(args.length() == 3);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSArray, old_shared_array, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSArray, new_shared_array, 1);
|
||||
CONVERT_BOOLEAN_ARG_CHECKED(do_drop, 2);
|
||||
USE(new_shared_array);
|
||||
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++) {
|
||||
Handle<Object> element;
|
||||
Handle<Object> old_element;
|
||||
Handle<Object> new_element;
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, element, Object::GetElement(isolate, shared_array, i));
|
||||
isolate, old_element, Object::GetElement(isolate, old_shared_array, i));
|
||||
RUNTIME_ASSERT(
|
||||
element->IsJSValue() &&
|
||||
Handle<JSValue>::cast(element)->value()->IsSharedFunctionInfo());
|
||||
old_element->IsJSValue() &&
|
||||
Handle<JSValue>::cast(old_element)->value()->IsSharedFunctionInfo());
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, new_element, Object::GetElement(isolate, new_shared_array, i));
|
||||
RUNTIME_ASSERT(
|
||||
new_element->IsUndefined() ||
|
||||
(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 {
|
||||
F(LiveEditFunctionSetScript, 2, 1) \
|
||||
F(LiveEditReplaceRefToNestedFunction, 3, 1) \
|
||||
F(LiveEditPatchFunctionPositions, 2, 1) \
|
||||
F(LiveEditCheckAndDropActivations, 2, 1) \
|
||||
F(LiveEditCheckAndDropActivations, 3, 1) \
|
||||
F(LiveEditCompareStrings, 2, 1) \
|
||||
F(LiveEditRestartFrame, 2, 1)
|
||||
|
||||
|
75
test/mjsunit/es6/debug-liveedit-new-target-1.js
Normal file
75
test/mjsunit/es6/debug-liveedit-new-target-1.js
Normal file
@ -0,0 +1,75 @@
|
||||
// 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.
|
63
test/mjsunit/es6/debug-liveedit-new-target-2.js
Normal file
63
test/mjsunit/es6/debug-liveedit-new-target-2.js
Normal file
@ -0,0 +1,63 @@
|
||||
// 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);
|
73
test/mjsunit/es6/debug-liveedit-new-target-3.js
Normal file
73
test/mjsunit/es6/debug-liveedit-new-target-3.js
Normal file
@ -0,0 +1,73 @@
|
||||
// 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 @@
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
var a = new Array();
|
||||
var b = new Array();
|
||||
Object.freeze(a);
|
||||
assertThrows(function() { %LiveEditCheckAndDropActivations(a, true); });
|
||||
assertThrows(function() { %LiveEditCheckAndDropActivations(a, b, true); });
|
||||
|
Loading…
Reference in New Issue
Block a user