[debug] allow calls to some builtins on temporary objects

This CL allows SetPrototypeAdd and ArrayIteratorPrototypeNext
to be called on temporary objects during side effect free evaluation.

Bug: v8:7588
Change-Id: Id77848e48d98c243de91bc6c0fae5a0877e693d4
Reviewed-on: https://chromium-review.googlesource.com/998439
Commit-Queue: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org>
Reviewed-by: Yang Guo <yangguo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#52548}
This commit is contained in:
Alexey Kozyatinskiy 2018-04-10 23:03:52 -07:00 committed by Commit Bot
parent e921be5c4f
commit 077205be55
30 changed files with 212 additions and 117 deletions

View File

@ -1380,6 +1380,13 @@ void MacroAssembler::CheckDebugHook(Register fun, Register new_target,
b(eq, &skip_hook);
{
// Load receiver to pass it later to DebugOnFunctionCall hook.
if (actual.is_reg()) {
mov(r4, actual.reg());
} else {
mov(r4, Operand(actual.immediate()));
}
ldr(r4, MemOperand(sp, r4, LSL, kPointerSizeLog2));
FrameScope frame(this,
has_frame() ? StackFrame::NONE : StackFrame::INTERNAL);
if (expected.is_reg()) {
@ -1395,6 +1402,7 @@ void MacroAssembler::CheckDebugHook(Register fun, Register new_target,
}
Push(fun);
Push(fun);
Push(r4);
CallRuntime(Runtime::kDebugOnFunctionCall);
Pop(fun);
if (new_target.is_valid()) {

View File

@ -2168,6 +2168,11 @@ void MacroAssembler::CheckDebugHook(Register fun, Register new_target,
Cbz(x4, &skip_hook);
{
// Load receiver to pass it later to DebugOnFunctionCall hook.
Operand actual_op = actual.is_immediate() ? Operand(actual.immediate())
: Operand(actual.reg());
Mov(x4, actual_op);
Ldr(x4, MemOperand(sp, x4, LSL, kPointerSizeLog2));
FrameScope frame(this,
has_frame() ? StackFrame::NONE : StackFrame::INTERNAL);
@ -2181,8 +2186,7 @@ void MacroAssembler::CheckDebugHook(Register fun, Register new_target,
SmiTag(expected_reg);
SmiTag(actual_reg);
Push(expected_reg, actual_reg, new_target, fun);
PushArgument(fun);
Push(fun, x4);
CallRuntime(Runtime::kDebugOnFunctionCall);
// Restore values from stack.

View File

@ -562,6 +562,8 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) {
{
FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
__ Push(r1, r4);
// Push hole as receiver since we do not use it for stepping.
__ PushRoot(Heap::kTheHoleValueRootIndex);
__ CallRuntime(Runtime::kDebugOnFunctionCall);
__ Pop(r1);
__ ldr(r4, FieldMemOperand(r1, JSGeneratorObject::kFunctionOffset));

View File

@ -611,8 +611,9 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) {
__ Bind(&prepare_step_in_if_stepping);
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ Push(x1, padreg);
__ PushArgument(x4);
// Push hole as receiver since we do not use it for stepping.
__ LoadRoot(x5, Heap::kTheHoleValueRootIndex);
__ Push(x1, padreg, x4, x5);
__ CallRuntime(Runtime::kDebugOnFunctionCall);
__ Pop(padreg, x1);
__ Ldr(x4, FieldMemOperand(x1, JSGeneratorObject::kFunctionOffset));

View File

@ -608,6 +608,8 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) {
FrameScope scope(masm, StackFrame::INTERNAL);
__ Push(edx);
__ Push(edi);
// Push hole as receiver since we do not use it for stepping.
__ PushRoot(Heap::kTheHoleValueRootIndex);
__ CallRuntime(Runtime::kDebugOnFunctionCall);
__ Pop(edx);
__ mov(edi, FieldOperand(edx, JSGeneratorObject::kFunctionOffset));

View File

@ -662,6 +662,8 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) {
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ Push(a1, t0);
// Push hole as receiver since we do not use it for stepping.
__ PushRoot(Heap::kTheHoleValueRootIndex);
__ CallRuntime(Runtime::kDebugOnFunctionCall);
__ Pop(a1);
}

View File

@ -551,6 +551,8 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) {
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ Push(a1, a4);
// Push hole as receiver since we do not use it for stepping.
__ PushRoot(Heap::kTheHoleValueRootIndex);
__ CallRuntime(Runtime::kDebugOnFunctionCall);
__ Pop(a1);
}

View File

@ -674,6 +674,8 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) {
FrameScope scope(masm, StackFrame::INTERNAL);
__ Push(rdx);
__ Push(rdi);
// Push hole as receiver since we do not use it for stepping.
__ PushRoot(Heap::kTheHoleValueRootIndex);
__ CallRuntime(Runtime::kDebugOnFunctionCall);
__ Pop(rdx);
__ movp(rdi, FieldOperand(rdx, JSGeneratorObject::kFunctionOffset));

View File

@ -41,8 +41,8 @@ void DebugCodegen::GenerateFrameDropperTrampoline(MacroAssembler* masm) {
__ Pop(fp, lr); // Frame, Return address.
__ Ldr(x0, FieldMemOperand(x1, JSFunction::kSharedFunctionInfoOffset));
__ Ldr(x0,
FieldMemOperand(x0, SharedFunctionInfo::kFormalParameterCountOffset));
__ Ldrsw(
x0, FieldMemOperand(x0, SharedFunctionInfo::kFormalParameterCountOffset));
__ mov(x2, x0);
ParameterCount dummy1(x2);

View File

@ -580,7 +580,8 @@ bool BytecodeHasNoSideEffect(interpreter::Bytecode bytecode) {
}
}
bool BuiltinHasNoSideEffect(Builtins::Name id) {
SharedFunctionInfo::SideEffectState BuiltinGetSideEffectState(
Builtins::Name id) {
switch (id) {
// Whitelist for builtins.
// Object builtins.
@ -852,13 +853,16 @@ bool BuiltinHasNoSideEffect(Builtins::Name id) {
case Builtins::kMakeSyntaxError:
case Builtins::kMakeRangeError:
case Builtins::kMakeURIError:
return true;
return SharedFunctionInfo::kHasNoSideEffect;
case Builtins::kSetPrototypeAdd:
case Builtins::kArrayIteratorPrototypeNext:
return SharedFunctionInfo::kRequiresRuntimeChecks;
default:
if (FLAG_trace_side_effect_free_debug_evaluate) {
PrintF("[debug-evaluate] built-in %s may cause side effect.\n",
Builtins::name(id));
}
return false;
return SharedFunctionInfo::kHasSideEffects;
}
}
@ -880,7 +884,7 @@ bool BytecodeRequiresRuntimeCheck(interpreter::Bytecode bytecode) {
} // anonymous namespace
// static
DebugEvaluate::SideEffectState DebugEvaluate::FunctionGetSideEffectState(
SharedFunctionInfo::SideEffectState DebugEvaluate::FunctionGetSideEffectState(
Handle<SharedFunctionInfo> info) {
if (FLAG_trace_side_effect_free_debug_evaluate) {
PrintF("[debug-evaluate] Checking function %s for side effect.\n",
@ -903,7 +907,7 @@ DebugEvaluate::SideEffectState DebugEvaluate::FunctionGetSideEffectState(
? it.GetIntrinsicIdOperand(0)
: it.GetRuntimeIdOperand(0);
if (IntrinsicHasNoSideEffect(id)) continue;
return kHasSideEffects;
return SharedFunctionInfo::kHasSideEffects;
}
if (BytecodeHasNoSideEffect(bytecode)) continue;
@ -918,23 +922,27 @@ DebugEvaluate::SideEffectState DebugEvaluate::FunctionGetSideEffectState(
}
// Did not match whitelist.
return kHasSideEffects;
return SharedFunctionInfo::kHasSideEffects;
}
return requires_runtime_checks ? kRequiresRuntimeChecks : kHasNoSideEffect;
return requires_runtime_checks ? SharedFunctionInfo::kRequiresRuntimeChecks
: SharedFunctionInfo::kHasNoSideEffect;
} else if (info->IsApiFunction()) {
if (info->GetCode()->is_builtin()) {
return info->GetCode()->builtin_index() == Builtins::kHandleApiCall
? kHasNoSideEffect
: kHasSideEffects;
? SharedFunctionInfo::kHasNoSideEffect
: SharedFunctionInfo::kHasSideEffects;
}
} else {
// Check built-ins against whitelist.
int builtin_index =
info->HasBuiltinId() ? info->builtin_id() : Builtins::kNoBuiltinId;
DCHECK_NE(Builtins::kDeserializeLazy, builtin_index);
if (Builtins::IsBuiltinId(builtin_index) &&
BuiltinHasNoSideEffect(static_cast<Builtins::Name>(builtin_index))) {
if (!Builtins::IsBuiltinId(builtin_index))
return SharedFunctionInfo::kHasSideEffects;
SharedFunctionInfo::SideEffectState state =
BuiltinGetSideEffectState(static_cast<Builtins::Name>(builtin_index));
#ifdef DEBUG
if (state == SharedFunctionInfo::kHasNoSideEffect) {
Isolate* isolate = info->GetIsolate();
Code* code = isolate->builtins()->builtin(builtin_index);
if (code->builtin_index() == Builtins::kDeserializeLazy) {
@ -963,12 +971,12 @@ DebugEvaluate::SideEffectState DebugEvaluate::FunctionGetSideEffectState(
}
DCHECK(!failed);
}
#endif // DEBUG
return kHasNoSideEffect;
}
#endif // DEBUG
return state;
}
return kHasSideEffects;
return SharedFunctionInfo::kHasSideEffects;
}
// static

View File

@ -9,6 +9,7 @@
#include "src/frames.h"
#include "src/objects.h"
#include "src/objects/shared-function-info.h"
#include "src/objects/string-table.h"
namespace v8 {
@ -37,12 +38,7 @@ class DebugEvaluate : public AllStatic {
static MaybeHandle<Object> WithTopmostArguments(Isolate* isolate,
Handle<String> source);
enum SideEffectState {
kHasSideEffects,
kRequiresRuntimeChecks,
kHasNoSideEffect
};
static SideEffectState FunctionGetSideEffectState(
static SharedFunctionInfo::SideEffectState FunctionGetSideEffectState(
Handle<SharedFunctionInfo> info);
static bool CallbackHasNoSideEffect(Object* callback_info);
static void ApplySideEffectChecks(Handle<BytecodeArray> bytecode_array);

View File

@ -2367,36 +2367,50 @@ void Debug::ClearSideEffectChecks(Handle<DebugInfo> debug_info) {
}
}
bool Debug::PerformSideEffectCheck(Handle<JSFunction> function) {
bool Debug::PerformSideEffectCheck(Handle<JSFunction> function,
Handle<Object> receiver) {
DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects);
DisallowJavascriptExecution no_js(isolate_);
if (!function->is_compiled() &&
!Compiler::Compile(function, Compiler::KEEP_EXCEPTION)) {
return false;
}
if (!SharedFunctionInfo::HasNoSideEffect(handle(function->shared()))) {
if (FLAG_trace_side_effect_free_debug_evaluate) {
PrintF("[debug-evaluate] Function %s failed side effect check.\n",
function->shared()->DebugName()->ToCString().get());
SharedFunctionInfo::SideEffectState side_effect_state =
SharedFunctionInfo::GetSideEffectState(handle(function->shared()));
switch (side_effect_state) {
case SharedFunctionInfo::kHasSideEffects:
if (FLAG_trace_side_effect_free_debug_evaluate) {
PrintF("[debug-evaluate] Function %s failed side effect check.\n",
function->shared()->DebugName()->ToCString().get());
}
side_effect_check_failed_ = true;
// Throw an uncatchable termination exception.
isolate_->TerminateExecution();
return false;
case SharedFunctionInfo::kRequiresRuntimeChecks: {
Handle<SharedFunctionInfo> shared(function->shared());
if (!shared->HasBytecodeArray()) {
return PerformSideEffectCheckForObject(receiver);
}
// If function has bytecode array then prepare function for debug
// execution to perform runtime side effect checks.
DCHECK(shared->is_compiled());
if (shared->GetCode() ==
isolate_->builtins()->builtin(Builtins::kDeserializeLazy)) {
Snapshot::EnsureBuiltinIsDeserialized(isolate_, shared);
}
GetOrCreateDebugInfo(shared);
PrepareFunctionForDebugExecution(shared);
return true;
}
side_effect_check_failed_ = true;
// Throw an uncatchable termination exception.
isolate_->TerminateExecution();
return false;
case SharedFunctionInfo::kHasNoSideEffect:
return true;
case SharedFunctionInfo::kNotComputed:
UNREACHABLE();
return false;
}
// If function has bytecode array then prepare function for debug execution
// to perform runtime side effect checks.
if (function->shared()->requires_runtime_side_effect_checks()) {
Handle<SharedFunctionInfo> shared(function->shared());
DCHECK(shared->is_compiled());
if (shared->GetCode() ==
isolate_->builtins()->builtin(Builtins::kDeserializeLazy)) {
Snapshot::EnsureBuiltinIsDeserialized(isolate_, shared);
}
GetOrCreateDebugInfo(shared);
PrepareFunctionForDebugExecution(shared);
}
return true;
UNREACHABLE();
return false;
}
bool Debug::PerformSideEffectCheckForCallback(Handle<Object> callback_info) {
@ -2435,6 +2449,12 @@ bool Debug::PerformSideEffectCheckAtBytecode(InterpretedFrame* frame) {
}
Handle<Object> object =
handle(frame->ReadInterpreterRegister(reg.index()), isolate_);
return PerformSideEffectCheckForObject(object);
}
bool Debug::PerformSideEffectCheckForObject(Handle<Object> object) {
DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects);
if (object->IsHeapObject()) {
Address address = Handle<HeapObject>::cast(object)->address();
if (temporary_objects_->HasObject(address)) {
@ -2442,8 +2462,7 @@ bool Debug::PerformSideEffectCheckAtBytecode(InterpretedFrame* frame) {
}
}
if (FLAG_trace_side_effect_free_debug_evaluate) {
PrintF("[debug-evaluate] %s failed runtime side effect check.\n",
interpreter::Bytecodes::ToString(bytecode));
PrintF("[debug-evaluate] failed runtime side effect check.\n");
}
side_effect_check_failed_ = true;
// Throw an uncatchable termination exception.

View File

@ -345,7 +345,8 @@ class Debug {
void ApplySideEffectChecks(Handle<DebugInfo> debug_info);
void ClearSideEffectChecks(Handle<DebugInfo> debug_info);
bool PerformSideEffectCheck(Handle<JSFunction> function);
bool PerformSideEffectCheck(Handle<JSFunction> function,
Handle<Object> receiver);
bool PerformSideEffectCheckForCallback(Handle<Object> callback_info);
bool PerformSideEffectCheckAtBytecode(InterpretedFrame* frame);
@ -501,6 +502,7 @@ class Debug {
void FindDebugInfo(Handle<DebugInfo> debug_info, DebugInfoListNode** prev,
DebugInfoListNode** curr);
void FreeDebugInfoListNode(DebugInfoListNode* prev, DebugInfoListNode* node);
bool PerformSideEffectCheckForObject(Handle<Object> object);
// Global handles.
Handle<Context> debug_context_;

View File

@ -1021,12 +1021,19 @@ void MacroAssembler::CheckDebugHook(Register fun, Register new_target,
if (actual.is_reg()) {
SmiTag(actual.reg());
Push(actual.reg());
SmiUntag(actual.reg());
}
if (new_target.is_valid()) {
Push(new_target);
}
Push(fun);
Push(fun);
Operand receiver_op =
actual.is_reg()
? Operand(ebp, actual.reg(), times_pointer_size, kPointerSize * 2)
: Operand(ebp, actual.immediate() * times_pointer_size +
kPointerSize * 2);
Push(receiver_op);
CallRuntime(Runtime::kDebugOnFunctionCall);
Pop(fun);
if (new_target.is_valid()) {

View File

@ -4126,6 +4126,14 @@ void MacroAssembler::CheckDebugHook(Register fun, Register new_target,
Branch(&skip_hook, eq, t0, Operand(zero_reg));
{
// Load receiver to pass it later to DebugOnFunctionCall hook.
if (actual.is_reg()) {
mov(t0, actual.reg());
} else {
li(t0, actual.immediate());
}
Lsa(at, sp, t0, kPointerSizeLog2);
lw(t0, MemOperand(at));
FrameScope frame(this,
has_frame() ? StackFrame::NONE : StackFrame::INTERNAL);
if (expected.is_reg()) {
@ -4141,6 +4149,7 @@ void MacroAssembler::CheckDebugHook(Register fun, Register new_target,
}
Push(fun);
Push(fun);
Push(t0);
CallRuntime(Runtime::kDebugOnFunctionCall);
Pop(fun);
if (new_target.is_valid()) {

View File

@ -4454,6 +4454,14 @@ void MacroAssembler::CheckDebugHook(Register fun, Register new_target,
Branch(&skip_hook, eq, t0, Operand(zero_reg));
{
// Load receiver to pass it later to DebugOnFunctionCall hook.
if (actual.is_reg()) {
mov(t0, actual.reg());
} else {
li(t0, actual.immediate());
}
Dlsa(t0, sp, t0, kPointerSizeLog2);
Ld(t0, MemOperand(t0));
FrameScope frame(this,
has_frame() ? StackFrame::NONE : StackFrame::INTERNAL);
if (expected.is_reg()) {
@ -4469,6 +4477,7 @@ void MacroAssembler::CheckDebugHook(Register fun, Register new_target,
}
Push(fun);
Push(fun);
Push(t0);
CallRuntime(Runtime::kDebugOnFunctionCall);
Pop(fun);
if (new_target.is_valid()) {

View File

@ -13644,17 +13644,14 @@ String* SharedFunctionInfo::DebugName() {
}
// static
bool SharedFunctionInfo::HasNoSideEffect(Handle<SharedFunctionInfo> info) {
if (!info->computed_has_no_side_effect()) {
DebugEvaluate::SideEffectState has_no_side_effect =
SharedFunctionInfo::SideEffectState SharedFunctionInfo::GetSideEffectState(
Handle<SharedFunctionInfo> info) {
if (info->side_effect_state() == kNotComputed) {
SharedFunctionInfo::SideEffectState has_no_side_effect =
DebugEvaluate::FunctionGetSideEffectState(info);
info->set_has_no_side_effect(has_no_side_effect !=
DebugEvaluate::kHasSideEffects);
info->set_requires_runtime_side_effect_checks(
has_no_side_effect == DebugEvaluate::kRequiresRuntimeChecks);
info->set_computed_has_no_side_effect(true);
info->set_side_effect_state(has_no_side_effect);
}
return info->has_no_side_effect();
return static_cast<SideEffectState>(info->side_effect_state());
}
// The filter is a pattern that matches function names in this way:

View File

@ -217,14 +217,8 @@ BIT_FIELD_ACCESSORS(SharedFunctionInfo, debugger_hints, is_anonymous_expression,
SharedFunctionInfo::IsAnonymousExpressionBit)
BIT_FIELD_ACCESSORS(SharedFunctionInfo, debugger_hints, deserialized,
SharedFunctionInfo::IsDeserializedBit)
BIT_FIELD_ACCESSORS(SharedFunctionInfo, debugger_hints, has_no_side_effect,
SharedFunctionInfo::HasNoSideEffectBit)
BIT_FIELD_ACCESSORS(SharedFunctionInfo, debugger_hints,
requires_runtime_side_effect_checks,
SharedFunctionInfo::RequiresRuntimeSideEffectChecksBit)
BIT_FIELD_ACCESSORS(SharedFunctionInfo, debugger_hints,
computed_has_no_side_effect,
SharedFunctionInfo::ComputedHasNoSideEffectBit)
BIT_FIELD_ACCESSORS(SharedFunctionInfo, debugger_hints, side_effect_state,
SharedFunctionInfo::SideEffectStateBits)
BIT_FIELD_ACCESSORS(SharedFunctionInfo, debugger_hints, debug_is_blackboxed,
SharedFunctionInfo::DebugIsBlackboxedBit)
BIT_FIELD_ACCESSORS(SharedFunctionInfo, debugger_hints,

View File

@ -256,15 +256,6 @@ class SharedFunctionInfo : public HeapObject {
// Indicates that the the shared function info is deserialized from cache.
DECL_BOOLEAN_ACCESSORS(deserialized)
// Indicates that the function cannot cause side-effects.
DECL_BOOLEAN_ACCESSORS(has_no_side_effect)
// Indicates that the function requires runtime side-effect checks.
DECL_BOOLEAN_ACCESSORS(requires_runtime_side_effect_checks);
// Indicates that |has_no_side_effect| has been computed and set.
DECL_BOOLEAN_ACCESSORS(computed_has_no_side_effect)
// Indicates that the function should be skipped during stepping.
DECL_BOOLEAN_ACCESSORS(debug_is_blackboxed)
@ -282,8 +273,13 @@ class SharedFunctionInfo : public HeapObject {
// The function's name if it is non-empty, otherwise the inferred name.
String* DebugName();
// The function cannot cause any side effects.
static bool HasNoSideEffect(Handle<SharedFunctionInfo> info);
enum SideEffectState {
kNotComputed = 0,
kHasSideEffects = 1,
kRequiresRuntimeChecks = 2,
kHasNoSideEffect = 3,
};
static SideEffectState GetSideEffectState(Handle<SharedFunctionInfo> info);
// Used for flags such as --turbo-filter.
bool PassesFilter(const char* raw_filter);
@ -549,16 +545,14 @@ class SharedFunctionInfo : public HeapObject {
STATIC_ASSERT(kLastFunctionKind <= FunctionKindBits::kMax);
// Bit positions in |debugger_hints|.
#define DEBUGGER_HINTS_BIT_FIELDS(V, _) \
V(IsAnonymousExpressionBit, bool, 1, _) \
V(NameShouldPrintAsAnonymousBit, bool, 1, _) \
V(IsDeserializedBit, bool, 1, _) \
V(HasNoSideEffectBit, bool, 1, _) \
V(RequiresRuntimeSideEffectChecksBit, bool, 1, _) \
V(ComputedHasNoSideEffectBit, bool, 1, _) \
V(DebugIsBlackboxedBit, bool, 1, _) \
V(ComputedDebugIsBlackboxedBit, bool, 1, _) \
V(HasReportedBinaryCoverageBit, bool, 1, _) \
#define DEBUGGER_HINTS_BIT_FIELDS(V, _) \
V(IsAnonymousExpressionBit, bool, 1, _) \
V(NameShouldPrintAsAnonymousBit, bool, 1, _) \
V(IsDeserializedBit, bool, 1, _) \
V(SideEffectStateBits, int, 2, _) \
V(DebugIsBlackboxedBit, bool, 1, _) \
V(ComputedDebugIsBlackboxedBit, bool, 1, _) \
V(HasReportedBinaryCoverageBit, bool, 1, _) \
V(DebuggingIdBits, int, 20, _)
DEFINE_BIT_FIELDS(DEBUGGER_HINTS_BIT_FIELDS)
@ -580,6 +574,9 @@ class SharedFunctionInfo : public HeapObject {
// function.
DECL_ACCESSORS(outer_scope_info, HeapObject)
inline int side_effect_state() const;
inline void set_side_effect_state(int value);
inline void set_kind(FunctionKind kind);
inline void set_needs_home_object(bool value);

View File

@ -1664,16 +1664,18 @@ RUNTIME_FUNCTION(Runtime_ScriptPositionInfo2) {
// or perform a side effect check.
RUNTIME_FUNCTION(Runtime_DebugOnFunctionCall) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSFunction, fun, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 1);
if (isolate->debug()->needs_check_on_function_call()) {
// Ensure that the callee will perform debug check on function call too.
Deoptimizer::DeoptimizeFunction(*fun);
if (isolate->debug()->last_step_action() >= StepIn) {
DCHECK_EQ(isolate->debug_execution_mode(), DebugInfo::kBreakpoints);
isolate->debug()->PrepareStepIn(fun);
}
if (isolate->debug_execution_mode() == DebugInfo::kSideEffects &&
!isolate->debug()->PerformSideEffectCheck(fun)) {
!isolate->debug()->PerformSideEffectCheck(fun, receiver)) {
return isolate->heap()->exception();
}
}

View File

@ -143,7 +143,7 @@ namespace internal {
F(DebugGetPropertyDetails, 2, 1) \
F(DebugGetPrototype, 1, 1) \
F(DebugIsActive, 0, 1) \
F(DebugOnFunctionCall, 1, 1) \
F(DebugOnFunctionCall, 2, 1) \
F(DebugPopPromise, 0, 1) \
F(DebugPrepareStepInSuspendedGenerator, 0, 1) \
F(DebugPropertyAttributesFromDetails, 1, 1) \

View File

@ -2142,12 +2142,14 @@ void MacroAssembler::CheckDebugHook(Register fun, Register new_target,
if (actual.is_reg()) {
Integer32ToSmi(actual.reg(), actual.reg());
Push(actual.reg());
SmiToInteger64(actual.reg(), actual.reg());
}
if (new_target.is_valid()) {
Push(new_target);
}
Push(fun);
Push(fun);
Push(StackArgumentsAccessor(rbp, actual).GetReceiverOperand());
CallRuntime(Runtime::kDebugOnFunctionCall);
Pop(fun);
if (new_target.is_valid()) {

View File

@ -6680,7 +6680,8 @@ TEST(DebugEvaluateNoSideEffect) {
for (i::Handle<i::JSFunction> fun : all_functions) {
bool failed = false;
isolate->debug()->StartSideEffectCheckMode();
failed = !isolate->debug()->PerformSideEffectCheck(fun);
failed = !isolate->debug()->PerformSideEffectCheck(
fun, v8::Utils::OpenHandle(*env->Global()));
isolate->debug()->StopSideEffectCheckMode();
if (failed) isolate->clear_pending_exception();
}

View File

@ -93,7 +93,7 @@ function listener(event, exec_state, event_data, data) {
success(undefined, `set.forEach(()=>1)`);
success(true, `set.has(1)`);
success(2, `set.size`);
fail(`new Set([1])`);
success(1, `new Set([1]).size`);
fail(`set.add(2)`);
fail(`set.delete(1)`);
fail(`set.clear()`);

View File

@ -64,14 +64,16 @@ var successes = [
}
})()`
],
[6,
`(function() { // Iterator.prototype.next performs stores.
var sum = 0;
for (let i of [1, 2, 3]) sum += i;
return sum;
})()`
]
];
var fails = [
`(function() { // Iterator.prototype.next performs stores.
var sum = 0;
for (let i of [1, 2, 3]) sum += i;
return sum;
})()`,
`(function() { // Store to scope object.
with (o) {
p = 2;

View File

@ -71,13 +71,13 @@ function listener(event, exec_state, event_data, data) {
success(NaN, `string * two`);
success("s2", `string + two`);
success("s2", `string + two`);
fail(`[...array]`);
success([1,2,3], `[...array]`);
success(3, `max(...array)`);
success({s:1}, `({[string]:1})`);
fail(`[a, b] = [1, 2]`);
success(2, `def(2)`);
success(1, `def()`);
fail(`d1(['a'])`); // Iterator.prototype.next performs stores.
success('ab', `d1(['a'])`);
success("XYz", `d2({x:'X', y:'Y'})`);
} catch (e) {
exception = e;

View File

@ -45,7 +45,7 @@ function return_array_use_spread(a) {
return [...a];
}
fail(`return_array_use_spread([1])`);
success([1], `return_array_use_spread([1])`);
// CallAccessorSetter
var array = [1,2,3];
@ -60,6 +60,33 @@ function return_literal_with_data_property(a) {
success({foo: 1}, `return_literal_with_data_property('foo')`);
// Array builtins with temporary objects
success(6, `(() => {
let s = 0;
for (const a of [1,2,3])
s += a;
return s;
})()`);
success(6, `(() => {
let s = 0;
for (const a of array)
s += a;
return s;
})()`);
var arrayIterator = array.entries();
fail(`(() => {
let s = 0;
for (const a of arrayIterator)
s += a;
return s;
})()`);
// SetAdd builtin on temporary object
var set = new Set([1,2]);
fail(`set.add(3).size`);
success(1, `new Set().add(1).size`);
function success(expectation, source) {
const result = Debug.evaluateGlobal(source, true).value();
if (expectation !== undefined) assertEquals(expectation, result);

View File

@ -1,38 +1,38 @@
Tests Debugger.setScriptSource
Running test: addLineAfter
var x = 1;
var x = a;
#debugger;
return x + 2;
return x + b;
---
Break location after LiveEdit:
var x = 1;
var x = a;
#debugger;
var x = 3;
stackChanged: true
Protocol.Debugger.stepInto
function foo() {
var x = #1;
function foo(a,b,c) {
var x = #a;
debugger;
Running test: addLineBefore
var x = 1;
var x = a;
#debugger;
return x + 2;
return x + b;
---
Break location after LiveEdit:
var x = 1;
var x = a;
var x = #3;
debugger;
stackChanged: true
Protocol.Debugger.stepInto
function foo() {
var x = #1;
function foo(a,b,c) {
var x = #a;
var x = 3;

View File

@ -7,10 +7,10 @@ let {session, contextGroup, Protocol} =
session.setupScriptMap();
function foo() {
var x = 1;
function foo(a,b,c) {
var x = a;
debugger;
return x + 2;
return x + b;
}
function boo() {
@ -25,7 +25,7 @@ InspectorTest.runAsyncTestSuite([
Protocol.Runtime.evaluate({expression: foo.toString()});
let {params:{scriptId}} = await Protocol.Debugger.onceScriptParsed();
Protocol.Runtime.evaluate({
expression: 'setTimeout(foo, 0)//# sourceURL=test.js'});
expression: 'setTimeout(() => foo(1,2,3), 0)//# sourceURL=test.js'});
let {params:{callFrames}} = await Protocol.Debugger.oncePaused();
await session.logSourceLocation(callFrames[0].location);
await replaceInSource(scriptId, 'debugger;', 'debugger;\nvar x = 3;');

View File

@ -70,4 +70,4 @@ Test that debug break does not trigger with throwOnSideEffect
value : 2
}
}
}
}