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,
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;
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]);
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 (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, 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(
isolate, element, Object::GetElement(isolate, shared_array, i));
isolate, new_element, Object::GetElement(isolate, new_shared_array, i));
RUNTIME_ASSERT(
element->IsJSValue() &&
Handle<JSValue>::cast(element)->value()->IsSharedFunctionInfo());
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)
......
// 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 @@
// 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); });
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