Revert "[debug] introduced runtime side effect check"

This reverts commit 7a2c371383.

Reason for revert: msan is broken

Original change's description:
> [debug] introduced runtime side effect check
> 
> This CL demonstrates minimum valuable addition to existing debug evaluate
> without side effects mechanism.
> With this CL user can evaluate expressions like:
> [a,b] // create any kind of temporary array literals
> [a,b].reduce((x,y) => x + y, 0); // use reduce method
> [1,2,3].fill(2); // change temporary arrays
> 
> The core idea: any change of the object created during evaluation without
> side effects is side effect free. As soon as we try to store this temporary
> object to object existed before evaluation we will terminate execution.
> 
> Implementation:
> - track all objects allocated during evaluation and mark them as temporary,
> - patch all bytecodes which change objects.
> 
> A little more details (including performance analysis): [1].
> 
> [1] https://docs.google.com/document/d/10qqAtZADspPnpYa6SEdYRxrddfKIZJIzbLtGpsZQkRo/edit#
> 
> Bug: v8:7588
> Change-Id: I69f7b96e1ebd7ad0022219e8213211c7be72a111
> Reviewed-on: https://chromium-review.googlesource.com/972615
> Commit-Queue: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org>
> Reviewed-by: Yang Guo <yangguo@chromium.org>
> Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#52370}

TBR=ulan@chromium.org,rmcilroy@chromium.org,yangguo@chromium.org,kozyatinskiy@chromium.org,leszeks@chromium.org

Change-Id: Ied1739c6308b13a4981189e0999f5912316cf456
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: v8:7588
Reviewed-on: https://chromium-review.googlesource.com/996135
Reviewed-by: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org>
Commit-Queue: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#52371}
This commit is contained in:
Aleksey Kozyatinskiy 2018-04-04 22:40:18 +00:00 committed by Commit Bot
parent 7a2c371383
commit 539a24432b
38 changed files with 214 additions and 724 deletions

View File

@ -1322,6 +1322,7 @@ v8_source_set("v8_base") {
"src/allocation.cc",
"src/allocation.h",
"src/api-arguments-inl.h",
"src/api-arguments.cc",
"src/api-arguments.h",
"src/api-natives.cc",
"src/api-natives.h",

View File

@ -22,14 +22,14 @@ namespace internal {
DCHECK(!name->IsPrivate()); \
DCHECK_IMPLIES(name->IsSymbol(), interceptor->can_intercept_symbols());
#define PREPARE_CALLBACK_INFO(ISOLATE, F, RETURN_VALUE, API_RETURN_TYPE, \
CALLBACK_INFO) \
if (ISOLATE->debug_execution_mode() == DebugInfo::kSideEffects && \
!ISOLATE->debug()->PerformSideEffectCheckForCallback(CALLBACK_INFO)) { \
return RETURN_VALUE(); \
} \
VMState<EXTERNAL> state(ISOLATE); \
ExternalCallbackScope call_scope(ISOLATE, FUNCTION_ADDR(F)); \
#define PREPARE_CALLBACK_INFO(ISOLATE, F, RETURN_VALUE, API_RETURN_TYPE, \
CALLBACK_INFO) \
if (ISOLATE->needs_side_effect_check() && \
!PerformSideEffectCheck(ISOLATE, *CALLBACK_INFO)) { \
return RETURN_VALUE(); \
} \
VMState<EXTERNAL> state(ISOLATE); \
ExternalCallbackScope call_scope(ISOLATE, FUNCTION_ADDR(F)); \
PropertyCallbackInfo<API_RETURN_TYPE> callback_info(begin());
#define CREATE_NAMED_CALLBACK(FUNCTION, TYPE, RETURN_TYPE, API_RETURN_TYPE, \
@ -83,8 +83,8 @@ Handle<Object> FunctionCallbackArguments::Call(CallHandlerInfo* handler) {
RuntimeCallTimerScope timer(isolate, RuntimeCallCounterId::kFunctionCallback);
v8::FunctionCallback f =
v8::ToCData<v8::FunctionCallback>(handler->callback());
if (isolate->debug_execution_mode() == DebugInfo::kSideEffects &&
!isolate->debug()->PerformSideEffectCheckForCallback(handle(handler))) {
if (isolate->needs_side_effect_check() &&
!PerformSideEffectCheck(isolate, handler)) {
return Handle<Object>();
}
VMState<EXTERNAL> state(isolate);
@ -158,6 +158,7 @@ Handle<Object> PropertyCallbackArguments::CallNamedSetter(
Isolate* isolate = this->isolate();
RuntimeCallTimerScope timer(isolate,
RuntimeCallCounterId::kNamedSetterCallback);
DCHECK(!isolate->needs_side_effect_check());
Handle<Object> side_effect_check_not_supported;
PREPARE_CALLBACK_INFO(isolate, f, Handle<Object>, v8::Value,
side_effect_check_not_supported);
@ -176,6 +177,8 @@ Handle<Object> PropertyCallbackArguments::CallNamedDefiner(
RuntimeCallCounterId::kNamedDefinerCallback);
GenericNamedPropertyDefinerCallback f =
ToCData<GenericNamedPropertyDefinerCallback>(interceptor->definer());
// We should not have come this far when side effect checks are enabled.
DCHECK(!isolate->needs_side_effect_check());
Handle<Object> side_effect_check_not_supported;
PREPARE_CALLBACK_INFO(isolate, f, Handle<Object>, v8::Value,
side_effect_check_not_supported);
@ -193,6 +196,8 @@ Handle<Object> PropertyCallbackArguments::CallIndexedSetter(
RuntimeCallCounterId::kIndexedSetterCallback);
IndexedPropertySetterCallback f =
ToCData<IndexedPropertySetterCallback>(interceptor->setter());
// We should not have come this far when side effect checks are enabled.
DCHECK(!isolate->needs_side_effect_check());
Handle<Object> side_effect_check_not_supported;
PREPARE_CALLBACK_INFO(isolate, f, Handle<Object>, v8::Value,
side_effect_check_not_supported);
@ -211,6 +216,8 @@ Handle<Object> PropertyCallbackArguments::CallIndexedDefiner(
RuntimeCallCounterId::kIndexedDefinerCallback);
IndexedPropertyDefinerCallback f =
ToCData<IndexedPropertyDefinerCallback>(interceptor->definer());
// We should not have come this far when side effect checks are enabled.
DCHECK(!isolate->needs_side_effect_check());
Handle<Object> side_effect_check_not_supported;
PREPARE_CALLBACK_INFO(isolate, f, Handle<Object>, v8::Value,
side_effect_check_not_supported);
@ -288,6 +295,8 @@ Handle<Object> PropertyCallbackArguments::CallAccessorSetter(
RuntimeCallCounterId::kAccessorSetterCallback);
AccessorNameSetterCallback f =
ToCData<AccessorNameSetterCallback>(accessor_info->setter());
// We should not have come this far when side effect checks are enabled.
DCHECK(!isolate->needs_side_effect_check());
Handle<Object> side_effect_check_not_supported;
PREPARE_CALLBACK_INFO(isolate, f, Handle<Object>, void,
side_effect_check_not_supported);

25
src/api-arguments.cc Normal file
View File

@ -0,0 +1,25 @@
// Copyright 2016 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.
#include "src/api-arguments.h"
#include "src/api-arguments-inl.h"
#include "src/debug/debug.h"
#include "src/objects-inl.h"
#include "src/tracing/trace-event.h"
#include "src/vm-state-inl.h"
namespace v8 {
namespace internal {
// static
bool CustomArgumentsBase ::PerformSideEffectCheck(Isolate* isolate,
Object* callback_info) {
// TODO(7515): always pass a valid callback info object.
if (callback_info == nullptr) return false;
return isolate->debug()->PerformSideEffectCheckForCallback(callback_info);
}
} // namespace internal
} // namespace v8

View File

@ -6,7 +6,6 @@
#define V8_API_ARGUMENTS_H_
#include "src/api.h"
#include "src/debug/debug.h"
#include "src/isolate.h"
#include "src/visitors.h"
@ -20,6 +19,7 @@ class CustomArgumentsBase : public Relocatable {
protected:
explicit inline CustomArgumentsBase(Isolate* isolate)
: Relocatable(isolate) {}
static bool PerformSideEffectCheck(Isolate* isolate, Object* callback_info);
};
template <typename T>

View File

@ -1037,31 +1037,11 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
// kInterpreterBytecodeArrayRegister is already loaded with
// SharedFunctionInfo::kFunctionDataOffset.
__ bind(&maybe_load_debug_bytecode_array);
__ ldr(r9, FieldMemOperand(r4, DebugInfo::kDebugBytecodeArrayOffset), ne);
__ JumpIfRoot(r9, Heap::kUndefinedValueRootIndex, &bytecode_array_loaded);
__ mov(kInterpreterBytecodeArrayRegister, r9);
__ ldr(r9, FieldMemOperand(r4, DebugInfo::kFlagsOffset));
__ SmiUntag(r9);
__ And(r9, r9, Operand(DebugInfo::kDebugExecutionMode));
ExternalReference debug_execution_mode =
ExternalReference::debug_execution_mode_address(masm->isolate());
__ mov(r4, Operand(debug_execution_mode));
__ ldrsb(r4, MemOperand(r4));
STATIC_ASSERT(static_cast<int>(DebugInfo::kDebugExecutionMode) ==
static_cast<int>(DebugInfo::kSideEffects));
__ cmp(r4, r9);
__ b(eq, &bytecode_array_loaded);
__ push(closure);
__ push(feedback_vector);
__ push(kInterpreterBytecodeArrayRegister);
__ push(closure);
__ CallRuntime(Runtime::kDebugApplyInstrumentation);
__ pop(kInterpreterBytecodeArrayRegister);
__ pop(feedback_vector);
__ pop(closure);
__ tst(r9, Operand(DebugInfo::kHasBreakInfo));
__ ldr(kInterpreterBytecodeArrayRegister,
FieldMemOperand(r4, DebugInfo::kDebugBytecodeArrayOffset), ne);
__ b(&bytecode_array_loaded);
}

View File

@ -1131,27 +1131,12 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
// kInterpreterBytecodeArrayRegister is already loaded with
// SharedFunctionInfo::kFunctionDataOffset.
__ Bind(&maybe_load_debug_bytecode_array);
__ Ldrsw(x10, FieldMemOperand(x11, DebugInfo::kDebugBytecodeArrayOffset));
__ JumpIfRoot(x10, Heap::kUndefinedValueRootIndex, &bytecode_array_loaded);
__ Ldrsw(x10, UntagSmiFieldMemOperand(x11, DebugInfo::kFlagsOffset));
__ TestAndBranchIfAllClear(x10, DebugInfo::kHasBreakInfo,
&bytecode_array_loaded);
__ Ldr(kInterpreterBytecodeArrayRegister,
FieldMemOperand(x11, DebugInfo::kDebugBytecodeArrayOffset));
__ Ldrsw(x10, UntagSmiFieldMemOperand(x11, DebugInfo::kFlagsOffset));
__ And(x10, x10, Immediate(DebugInfo::kDebugExecutionMode));
STATIC_ASSERT(static_cast<int>(DebugInfo::kDebugExecutionMode) ==
static_cast<int>(DebugInfo::kSideEffects));
ExternalReference debug_execution_mode =
ExternalReference::debug_execution_mode_address(masm->isolate());
__ Mov(x0, Operand(debug_execution_mode));
__ Ldrsb(x0, MemOperand(x0));
__ CompareAndBranch(x10, x0, eq, &bytecode_array_loaded);
__ Push(closure, feedback_vector);
__ PushArgument(closure);
__ CallRuntime(Runtime::kDebugApplyInstrumentation);
__ Pop(feedback_vector, closure);
__ jmp(&bytecode_array_loaded);
__ B(&bytecode_array_loaded);
}
static void Generate_InterpreterPushArgs(MacroAssembler* masm,

View File

@ -853,8 +853,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
// Get the bytecode array from the function object (or from the DebugInfo if
// it is present) and load it into kInterpreterBytecodeArrayRegister.
Label maybe_load_debug_bytecode_array, bytecode_array_loaded,
apply_instrumentation;
Label maybe_load_debug_bytecode_array, bytecode_array_loaded;
__ mov(eax, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
__ mov(kInterpreterBytecodeArrayRegister,
FieldOperand(eax, SharedFunctionInfo::kFunctionDataOffset));
@ -974,29 +973,15 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
// kInterpreterBytecodeArrayRegister is already loaded with
// SharedFunctionInfo::kFunctionDataOffset.
__ bind(&maybe_load_debug_bytecode_array);
__ mov(eax, FieldOperand(eax, SharedFunctionInfo::kDebugInfoOffset));
__ mov(ecx, FieldOperand(eax, DebugInfo::kDebugBytecodeArrayOffset));
__ JumpIfRoot(ecx, Heap::kUndefinedValueRootIndex, &bytecode_array_loaded);
__ mov(kInterpreterBytecodeArrayRegister, ecx);
__ mov(ecx, FieldOperand(eax, DebugInfo::kFlagsOffset));
__ SmiUntag(ecx);
__ and_(ecx, Immediate(DebugInfo::kDebugExecutionMode));
STATIC_ASSERT(static_cast<int>(DebugInfo::kDebugExecutionMode) ==
static_cast<int>(DebugInfo::kSideEffects));
ExternalReference debug_execution_mode =
ExternalReference::debug_execution_mode_address(masm->isolate());
__ cmp(ecx, Operand::StaticVariable(debug_execution_mode));
__ j(equal, &bytecode_array_loaded);
__ pop(ecx); // get JSFunction from stack
__ push(ecx);
__ push(ebx); // preserve feedback_vector and bytecode array register
__ push(kInterpreterBytecodeArrayRegister);
__ push(ecx); // pass function as argument
__ CallRuntime(Runtime::kDebugApplyInstrumentation);
__ pop(kInterpreterBytecodeArrayRegister);
__ push(ebx); // feedback_vector == ebx, so save it.
__ mov(ecx, FieldOperand(eax, SharedFunctionInfo::kDebugInfoOffset));
__ mov(ebx, FieldOperand(ecx, DebugInfo::kFlagsOffset));
__ SmiUntag(ebx);
__ test(ebx, Immediate(DebugInfo::kHasBreakInfo));
__ pop(ebx);
__ j(zero, &bytecode_array_loaded);
__ mov(kInterpreterBytecodeArrayRegister,
FieldOperand(ecx, DebugInfo::kDebugBytecodeArrayOffset));
__ jmp(&bytecode_array_loaded);
}

View File

@ -1024,30 +1024,12 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
// kInterpreterBytecodeArrayRegister is already loaded with
// SharedFunctionInfo::kFunctionDataOffset.
__ bind(&maybe_load_debug_bytecode_array);
__ lw(t1, FieldMemOperand(t0, DebugInfo::kDebugBytecodeArrayOffset));
__ JumpIfRoot(t1, Heap::kUndefinedValueRootIndex, &bytecode_array_loaded);
__ mov(kInterpreterBytecodeArrayRegister, t1);
__ lw(t1, FieldMemOperand(t0, DebugInfo::kFlagsOffset));
__ SmiUntag(t1);
__ And(t1, t1, Operand(DebugInfo::kDebugExecutionMode));
ExternalReference debug_execution_mode =
ExternalReference::debug_execution_mode_address(masm->isolate());
__ li(t0, Operand(debug_execution_mode));
__ lb(t0, MemOperand(t0));
STATIC_ASSERT(static_cast<int>(DebugInfo::kDebugExecutionMode) ==
static_cast<int>(DebugInfo::kSideEffects));
__ Branch(&bytecode_array_loaded, eq, t0, Operand(t1));
__ push(closure);
__ push(feedback_vector);
__ push(kInterpreterBytecodeArrayRegister);
__ push(closure);
__ CallRuntime(Runtime::kDebugApplyInstrumentation);
__ pop(kInterpreterBytecodeArrayRegister);
__ pop(feedback_vector);
__ pop(closure);
__ And(t1, t1, Operand(DebugInfo::kHasBreakInfo));
__ Branch(&bytecode_array_loaded, eq, t1, Operand(zero_reg));
__ lw(kInterpreterBytecodeArrayRegister,
FieldMemOperand(t0, DebugInfo::kDebugBytecodeArrayOffset));
__ Branch(&bytecode_array_loaded);
}

View File

@ -1022,30 +1022,12 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
// kInterpreterBytecodeArrayRegister is already loaded with
// SharedFunctionInfo::kFunctionDataOffset.
__ bind(&maybe_load_debug_bytecode_array);
__ Ld(a5, FieldMemOperand(a4, DebugInfo::kDebugBytecodeArrayOffset));
__ JumpIfRoot(a5, Heap::kUndefinedValueRootIndex, &bytecode_array_loaded);
__ mov(kInterpreterBytecodeArrayRegister, a5);
__ Ld(a5, FieldMemOperand(a4, DebugInfo::kFlagsOffset));
__ SmiUntag(a5);
__ And(a5, a5, Operand(DebugInfo::kDebugExecutionMode));
ExternalReference debug_execution_mode =
ExternalReference::debug_execution_mode_address(masm->isolate());
__ li(a4, Operand(debug_execution_mode));
__ Lb(a4, MemOperand(a4));
STATIC_ASSERT(static_cast<int>(DebugInfo::kDebugExecutionMode) ==
static_cast<int>(DebugInfo::kSideEffects));
__ Branch(&bytecode_array_loaded, eq, a4, Operand(a5));
__ push(closure);
__ push(feedback_vector);
__ push(kInterpreterBytecodeArrayRegister);
__ push(closure);
__ CallRuntime(Runtime::kDebugApplyInstrumentation);
__ pop(kInterpreterBytecodeArrayRegister);
__ pop(feedback_vector);
__ pop(closure);
__ And(a5, a5, Operand(DebugInfo::kHasBreakInfo));
__ Branch(&bytecode_array_loaded, eq, a5, Operand(zero_reg));
__ Ld(kInterpreterBytecodeArrayRegister,
FieldMemOperand(a4, DebugInfo::kDebugBytecodeArrayOffset));
__ Branch(&bytecode_array_loaded);
}

View File

@ -1041,33 +1041,13 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
// kInterpreterBytecodeArrayRegister is already loaded with
// SharedFunctionInfo::kFunctionDataOffset.
__ bind(&maybe_load_debug_bytecode_array);
__ movp(rax, FieldOperand(closure, JSFunction::kSharedFunctionInfoOffset));
__ movp(rcx, FieldOperand(rax, SharedFunctionInfo::kDebugInfoOffset));
__ movp(kScratchRegister,
__ SmiToInteger32(kScratchRegister,
FieldOperand(rcx, DebugInfo::kFlagsOffset));
__ testl(kScratchRegister, Immediate(DebugInfo::kHasBreakInfo));
__ j(zero, &bytecode_array_loaded);
__ movp(kInterpreterBytecodeArrayRegister,
FieldOperand(rcx, DebugInfo::kDebugBytecodeArrayOffset));
__ JumpIfRoot(kScratchRegister, Heap::kUndefinedValueRootIndex,
&bytecode_array_loaded);
__ movp(kInterpreterBytecodeArrayRegister, kScratchRegister);
__ SmiToInteger32(rax, FieldOperand(rcx, DebugInfo::kFlagsOffset));
__ andb(rax, Immediate(DebugInfo::kDebugExecutionMode));
STATIC_ASSERT(static_cast<int>(DebugInfo::kDebugExecutionMode) ==
static_cast<int>(DebugInfo::kSideEffects));
ExternalReference debug_execution_mode_address =
ExternalReference::debug_execution_mode_address(masm->isolate());
Operand debug_execution_mode =
masm->ExternalOperand(debug_execution_mode_address);
__ cmpb(rax, debug_execution_mode);
__ j(equal, &bytecode_array_loaded);
__ Push(closure);
__ Push(feedback_vector);
__ Push(kInterpreterBytecodeArrayRegister);
__ Push(closure);
__ CallRuntime(Runtime::kDebugApplyInstrumentation);
__ Pop(kInterpreterBytecodeArrayRegister);
__ Pop(feedback_vector);
__ Pop(closure);
__ jmp(&bytecode_array_loaded);
}

View File

@ -41,11 +41,9 @@ MaybeHandle<Object> DebugEvaluate::Global(Isolate* isolate,
Handle<JSFunction> fun =
isolate->factory()->NewFunctionFromSharedFunctionInfo(shared_info,
context);
if (throw_on_side_effect) isolate->debug()->StartSideEffectCheckMode();
MaybeHandle<Object> result = Execution::Call(
isolate, fun, Handle<JSObject>(context->global_proxy()), 0, nullptr);
if (throw_on_side_effect) isolate->debug()->StopSideEffectCheckMode();
return result;
NoSideEffectScope no_side_effect(isolate, throw_on_side_effect);
return Execution::Call(isolate, fun,
Handle<JSObject>(context->global_proxy()), 0, nullptr);
}
MaybeHandle<Object> DebugEvaluate::Local(Isolate* isolate,
@ -137,14 +135,11 @@ MaybeHandle<Object> DebugEvaluate::Evaluate(
Object);
Handle<Object> result;
bool sucess = false;
if (throw_on_side_effect) isolate->debug()->StartSideEffectCheckMode();
sucess = Execution::Call(isolate, eval_fun, receiver, 0, nullptr)
.ToHandle(&result);
if (throw_on_side_effect) isolate->debug()->StopSideEffectCheckMode();
if (!sucess) {
DCHECK(isolate->has_pending_exception());
return MaybeHandle<Object>();
{
NoSideEffectScope no_side_effect(isolate, throw_on_side_effect);
ASSIGN_RETURN_ON_EXCEPTION(
isolate, result,
Execution::Call(isolate, eval_fun, receiver, 0, nullptr), Object);
}
// Skip the global proxy as it has no properties and always delegates to the
@ -576,6 +571,10 @@ bool BytecodeHasNoSideEffect(interpreter::Bytecode bytecode) {
case Bytecode::kSetPendingMessage:
return true;
default:
if (FLAG_trace_side_effect_free_debug_evaluate) {
PrintF("[debug-evaluate] bytecode %s may cause side effect.\n",
Bytecodes::ToString(bytecode));
}
return false;
}
}
@ -850,25 +849,10 @@ bool BuiltinHasNoSideEffect(Builtins::Name id) {
}
}
bool BytecodeRequiresRuntimeCheck(interpreter::Bytecode bytecode) {
typedef interpreter::Bytecode Bytecode;
switch (bytecode) {
case Bytecode::kStaNamedProperty:
case Bytecode::kStaNamedOwnProperty:
case Bytecode::kStaKeyedProperty:
case Bytecode::kStaInArrayLiteral:
case Bytecode::kStaDataPropertyInLiteral:
return true;
default:
return false;
}
}
} // anonymous namespace
// static
DebugEvaluate::SideEffectState DebugEvaluate::FunctionGetSideEffectState(
Handle<SharedFunctionInfo> info) {
bool DebugEvaluate::FunctionHasNoSideEffect(Handle<SharedFunctionInfo> info) {
if (FLAG_trace_side_effect_free_debug_evaluate) {
PrintF("[debug-evaluate] Checking function %s for side effect.\n",
info->DebugName()->ToCString().get());
@ -879,7 +863,6 @@ DebugEvaluate::SideEffectState DebugEvaluate::FunctionGetSideEffectState(
// Check bytecodes against whitelist.
Handle<BytecodeArray> bytecode_array(info->bytecode_array());
if (FLAG_trace_side_effect_free_debug_evaluate) bytecode_array->Print();
bool requires_runtime_checks = false;
for (interpreter::BytecodeArrayIterator it(bytecode_array); !it.done();
it.Advance()) {
interpreter::Bytecode bytecode = it.current_bytecode();
@ -890,29 +873,18 @@ DebugEvaluate::SideEffectState DebugEvaluate::FunctionGetSideEffectState(
? it.GetIntrinsicIdOperand(0)
: it.GetRuntimeIdOperand(0);
if (IntrinsicHasNoSideEffect(id)) continue;
return kHasSideEffects;
return false;
}
if (BytecodeHasNoSideEffect(bytecode)) continue;
if (BytecodeRequiresRuntimeCheck(bytecode)) {
requires_runtime_checks = true;
continue;
}
if (FLAG_trace_side_effect_free_debug_evaluate) {
PrintF("[debug-evaluate] bytecode %s may cause side effect.\n",
interpreter::Bytecodes::ToString(bytecode));
}
// Did not match whitelist.
return kHasSideEffects;
return false;
}
return requires_runtime_checks ? kRequiresRuntimeChecks : kHasNoSideEffect;
return true;
} else if (info->IsApiFunction()) {
if (info->GetCode()->is_builtin()) {
return info->GetCode()->builtin_index() == Builtins::kHandleApiCall
? kHasNoSideEffect
: kHasSideEffects;
return info->GetCode()->builtin_index() == Builtins::kHandleApiCall;
}
} else {
// Check built-ins against whitelist.
@ -951,11 +923,11 @@ DebugEvaluate::SideEffectState DebugEvaluate::FunctionGetSideEffectState(
DCHECK(!failed);
}
#endif // DEBUG
return kHasNoSideEffect;
return true;
}
}
return kHasSideEffects;
return false;
}
// static
@ -986,20 +958,5 @@ bool DebugEvaluate::CallbackHasNoSideEffect(Object* callback_info) {
return false;
}
// static
void DebugEvaluate::ApplySideEffectChecks(
Handle<BytecodeArray> bytecode_array) {
for (interpreter::BytecodeArrayIterator it(bytecode_array); !it.done();
it.Advance()) {
interpreter::Bytecode bytecode = it.current_bytecode();
if (BytecodeRequiresRuntimeCheck(bytecode)) {
interpreter::Bytecode debugbreak =
interpreter::Bytecodes::GetDebugBreak(bytecode);
bytecode_array->set(it.current_offset(),
interpreter::Bytecodes::ToByte(debugbreak));
}
}
}
} // namespace internal
} // namespace v8

View File

@ -37,15 +37,8 @@ class DebugEvaluate : public AllStatic {
static MaybeHandle<Object> WithTopmostArguments(Isolate* isolate,
Handle<String> source);
enum SideEffectState {
kHasSideEffects,
kRequiresRuntimeChecks,
kHasNoSideEffect
};
static SideEffectState FunctionGetSideEffectState(
Handle<SharedFunctionInfo> info);
static bool FunctionHasNoSideEffect(Handle<SharedFunctionInfo> info);
static bool CallbackHasNoSideEffect(Object* callback_info);
static void ApplySideEffectChecks(Handle<BytecodeArray> bytecode_array);
private:
// This class builds a context chain for evaluation of expressions

View File

@ -5,7 +5,6 @@
#include "src/debug/debug.h"
#include <memory>
#include <unordered_set>
#include "src/api.h"
#include "src/arguments.h"
@ -23,7 +22,6 @@
#include "src/global-handles.h"
#include "src/globals.h"
#include "src/interpreter/bytecode-array-accessor.h"
#include "src/interpreter/bytecode-array-iterator.h"
#include "src/interpreter/interpreter.h"
#include "src/isolate-inl.h"
#include "src/log.h"
@ -36,36 +34,6 @@
namespace v8 {
namespace internal {
class Debug::TemporaryObjectsTracker : public HeapObjectAllocationTracker {
public:
TemporaryObjectsTracker() = default;
~TemporaryObjectsTracker() = default;
void AllocationEvent(Address addr, int) override { objects_.insert(addr); }
void MoveEvent(Address from, Address to, int) override {
if (from == to) return;
auto it = objects_.find(from);
if (it == objects_.end()) {
// If temporary object was collected we can get MoveEvent which moves
// existing non temporary object to the address where we had temporary
// object. So we should mark new address as non temporary.
objects_.erase(to);
return;
}
objects_.erase(it);
objects_.insert(to);
}
bool HasObject(Address addr) const {
return objects_.find(addr) != objects_.end();
}
private:
std::unordered_set<Address> objects_;
DISALLOW_COPY_AND_ASSIGN(TemporaryObjectsTracker);
};
Debug::Debug(Isolate* isolate)
: debug_context_(Handle<Context>()),
is_active_(false),
@ -83,8 +51,6 @@ Debug::Debug(Isolate* isolate)
ThreadInit();
}
Debug::~Debug() { DCHECK_NULL(debug_delegate_); }
BreakLocation BreakLocation::FromFrame(Handle<DebugInfo> debug_info,
JavaScriptFrame* frame) {
if (debug_info->CanBreakAtEntry()) {
@ -452,8 +418,6 @@ void Debug::Break(JavaScriptFrame* frame, Handle<JSFunction> break_target) {
// Return if we fail to retrieve debug info.
Handle<SharedFunctionInfo> shared(break_target->shared());
if (!EnsureBreakInfo(shared)) return;
PrepareFunctionForDebugExecution(shared);
Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
// Find the break location where execution has stopped.
@ -644,8 +608,7 @@ bool Debug::SetBreakPoint(Handle<JSFunction> function,
// Make sure the function is compiled and has set up the debug info.
Handle<SharedFunctionInfo> shared(function->shared());
if (!EnsureBreakInfo(shared)) return false;
PrepareFunctionForDebugExecution(shared);
PrepareFunctionForBreakPoints(shared);
Handle<DebugInfo> debug_info(shared->GetDebugInfo());
// Source positions starts with zero.
DCHECK_LE(0, *source_position);
@ -686,7 +649,7 @@ bool Debug::SetBreakPointForScript(Handle<Script> script,
// Make sure the function has set up the debug info.
Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>::cast(result);
if (!EnsureBreakInfo(shared)) return false;
PrepareFunctionForDebugExecution(shared);
PrepareFunctionForBreakPoints(shared);
// Find position within function. The script position might be before the
// source position of the first function.
@ -726,19 +689,6 @@ int Debug::FindBreakablePosition(Handle<DebugInfo> debug_info,
}
}
void Debug::ApplyInstrumentation(Handle<SharedFunctionInfo> shared) {
DCHECK(shared->HasBytecodeArray());
Handle<DebugInfo> debug_info(GetOrCreateDebugInfo(shared));
DCHECK_NE(debug_info->DebugExecutionMode(), isolate_->debug_execution_mode());
if (isolate_->debug_execution_mode() == DebugInfo::kBreakpoints) {
ClearSideEffectChecks(debug_info);
ApplyBreakPoints(debug_info);
} else {
ClearBreakPoints(debug_info);
ApplySideEffectChecks(debug_info);
}
}
void Debug::ApplyBreakPoints(Handle<DebugInfo> debug_info) {
DisallowHeapAllocation no_gc;
if (debug_info->CanBreakAtEntry()) {
@ -756,7 +706,6 @@ void Debug::ApplyBreakPoints(Handle<DebugInfo> debug_info) {
it.SetDebugBreak();
}
}
debug_info->SetDebugExecutionMode(DebugInfo::kBreakpoints);
}
void Debug::ClearBreakPoints(Handle<DebugInfo> debug_info) {
@ -765,9 +714,7 @@ void Debug::ClearBreakPoints(Handle<DebugInfo> debug_info) {
} else {
// If we attempt to clear breakpoints but none exist, simply return. This
// can happen e.g. CoverageInfos exist but no breakpoints are set.
if (!debug_info->HasDebugBytecodeArray() || !debug_info->HasBreakInfo()) {
return;
}
if (!debug_info->HasDebugBytecodeArray()) return;
DisallowHeapAllocation no_gc;
for (BreakIterator it(debug_info); !it.Done(); it.Next()) {
@ -781,7 +728,6 @@ void Debug::ClearBreakPoint(Handle<BreakPoint> break_point) {
for (DebugInfoListNode* node = debug_info_list_; node != nullptr;
node = node->next()) {
if (!node->debug_info()->HasBreakInfo()) continue;
Handle<Object> result =
DebugInfo::FindBreakPointInfo(node->debug_info(), break_point);
if (result->IsUndefined(isolate_)) continue;
@ -826,8 +772,7 @@ void Debug::FloodWithOneShot(Handle<SharedFunctionInfo> shared,
if (IsBlackboxed(shared)) return;
// Make sure the function is compiled and has set up the debug info.
if (!EnsureBreakInfo(shared)) return;
PrepareFunctionForDebugExecution(shared);
PrepareFunctionForBreakPoints(shared);
Handle<DebugInfo> debug_info(shared->GetDebugInfo());
// Flood the function with break points.
DCHECK(debug_info->HasDebugBytecodeArray());
@ -1021,8 +966,6 @@ void Debug::PrepareStep(StepAction step_action) {
Handle<JSFunction> function(summary.function());
Handle<SharedFunctionInfo> shared(function->shared());
if (!EnsureBreakInfo(shared)) return;
PrepareFunctionForDebugExecution(shared);
Handle<DebugInfo> debug_info(shared->GetDebugInfo());
BreakLocation location = BreakLocation::FromFrame(debug_info, js_frame);
@ -1215,25 +1158,15 @@ void Debug::DeoptimizeFunction(Handle<SharedFunctionInfo> shared) {
}
}
void Debug::PrepareFunctionForDebugExecution(
Handle<SharedFunctionInfo> shared) {
void Debug::PrepareFunctionForBreakPoints(Handle<SharedFunctionInfo> shared) {
// To prepare bytecode for debugging, we already need to have the debug
// info (containing the debug copy) upfront, but since we do not recompile,
// preparing for break points cannot fail.
DCHECK(shared->is_compiled());
DCHECK(shared->HasDebugInfo());
DCHECK(shared->HasBreakInfo());
Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared);
if (debug_info->flags() & DebugInfo::kPreparedForDebugExecution) return;
// Make a copy of the bytecode array if available.
Handle<Object> maybe_debug_bytecode_array =
isolate_->factory()->undefined_value();
if (shared->HasBytecodeArray()) {
Handle<BytecodeArray> original(shared->bytecode_array());
maybe_debug_bytecode_array =
isolate_->factory()->CopyBytecodeArray(original);
}
debug_info->set_debug_bytecode_array(*maybe_debug_bytecode_array);
if (debug_info->IsPreparedForBreakpoints()) return;
if (debug_info->CanBreakAtEntry()) {
// Deopt everything in case the function is inlined anywhere.
@ -1246,8 +1179,9 @@ void Debug::PrepareFunctionForDebugExecution(
redirect_visitor.VisitThread(isolate_, isolate_->thread_local_top());
isolate_->thread_manager()->IterateArchivedThreads(&redirect_visitor);
}
debug_info->set_flags(debug_info->flags() |
DebugInfo::kPreparedForDebugExecution);
DebugInfo::kPreparedForBreakpoints);
}
void Debug::InstallDebugBreakTrampoline() {
@ -1334,7 +1268,6 @@ bool Debug::GetPossibleBreakpoints(Handle<Script> script, int start_position,
Handle<SharedFunctionInfo> shared =
Handle<SharedFunctionInfo>::cast(result);
if (!EnsureBreakInfo(shared)) return false;
PrepareFunctionForDebugExecution(shared);
Handle<DebugInfo> debug_info(shared->GetDebugInfo());
FindBreakablePositions(debug_info, start_position, end_position, locations);
@ -1368,7 +1301,6 @@ bool Debug::GetPossibleBreakpoints(Handle<Script> script, int start_position,
}
}
if (!EnsureBreakInfo(candidate)) return false;
PrepareFunctionForDebugExecution(candidate);
}
if (was_compiled) continue;
@ -1464,7 +1396,7 @@ Handle<Object> Debug::FindSharedFunctionInfoInScript(Handle<Script> script,
// If the iteration count is larger than 1, we had to compile the outer
// function in order to create this shared function info. So there can
// be no JSFunction referencing it. We can anticipate creating a debug
// info while bypassing PrepareFunctionForDebugExecution.
// info while bypassing PrepareFunctionForBreakpoints.
if (iteration > 1) {
AllowHeapAllocation allow_before_return;
CreateBreakInfo(shared_handle);
@ -1513,10 +1445,18 @@ void Debug::CreateBreakInfo(Handle<SharedFunctionInfo> shared) {
Handle<FixedArray> break_points(
factory->NewFixedArray(DebugInfo::kEstimatedNofBreakPointsInFunction));
// Make a copy of the bytecode array if available.
Handle<Object> maybe_debug_bytecode_array = factory->undefined_value();
if (shared->HasBytecodeArray()) {
Handle<BytecodeArray> original(shared->bytecode_array());
maybe_debug_bytecode_array = factory->CopyBytecodeArray(original);
}
int flags = debug_info->flags();
flags |= DebugInfo::kHasBreakInfo;
if (CanBreakAtEntry(shared)) flags |= DebugInfo::kCanBreakAtEntry;
debug_info->set_flags(flags);
debug_info->set_debug_bytecode_array(*maybe_debug_bytecode_array);
debug_info->set_break_points(*break_points);
}
@ -2144,9 +2084,8 @@ void Debug::UpdateState() {
void Debug::UpdateHookOnFunctionCall() {
STATIC_ASSERT(LastStepAction == StepIn);
hook_on_function_call_ =
thread_local_.last_step_action_ == StepIn ||
isolate_->debug_execution_mode() == DebugInfo::kSideEffects;
hook_on_function_call_ = thread_local_.last_step_action_ == StepIn ||
isolate_->needs_side_effect_check();
}
MaybeHandle<Object> Debug::Call(Handle<Object> fun, Handle<Object> data) {
@ -2315,57 +2254,8 @@ ReturnValueScope::~ReturnValueScope() {
debug_->set_return_value(*return_value_);
}
void Debug::StartSideEffectCheckMode() {
DCHECK(isolate_->debug_execution_mode() != DebugInfo::kSideEffects);
isolate_->set_debug_execution_mode(DebugInfo::kSideEffects);
UpdateHookOnFunctionCall();
side_effect_check_failed_ = false;
DCHECK(!temporary_objects_);
temporary_objects_.reset(new TemporaryObjectsTracker());
isolate_->heap()->AddHeapObjectAllocationTracker(temporary_objects_.get());
}
void Debug::StopSideEffectCheckMode() {
DCHECK(isolate_->debug_execution_mode() == DebugInfo::kSideEffects);
if (side_effect_check_failed_) {
DCHECK(isolate_->has_pending_exception());
DCHECK_EQ(isolate_->heap()->termination_exception(),
isolate_->pending_exception());
// Convert the termination exception into a regular exception.
isolate_->CancelTerminateExecution();
isolate_->Throw(*isolate_->factory()->NewEvalError(
MessageTemplate::kNoSideEffectDebugEvaluate));
}
isolate_->set_debug_execution_mode(DebugInfo::kBreakpoints);
UpdateHookOnFunctionCall();
side_effect_check_failed_ = false;
DCHECK(temporary_objects_);
isolate_->heap()->RemoveHeapObjectAllocationTracker(temporary_objects_.get());
temporary_objects_.reset();
}
void Debug::ApplySideEffectChecks(Handle<DebugInfo> debug_info) {
DCHECK(debug_info->HasDebugBytecodeArray());
Handle<BytecodeArray> debug_bytecode(debug_info->DebugBytecodeArray());
DebugEvaluate::ApplySideEffectChecks(debug_bytecode);
debug_info->SetDebugExecutionMode(DebugInfo::kSideEffects);
}
void Debug::ClearSideEffectChecks(Handle<DebugInfo> debug_info) {
DCHECK(debug_info->HasDebugBytecodeArray());
Handle<BytecodeArray> debug_bytecode(debug_info->DebugBytecodeArray());
Handle<BytecodeArray> original(debug_info->OriginalBytecodeArray());
for (interpreter::BytecodeArrayIterator it(debug_bytecode); !it.done();
it.Advance()) {
debug_bytecode->set(it.current_offset(),
original->get(it.current_offset()));
}
}
bool Debug::PerformSideEffectCheck(Handle<JSFunction> function) {
DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects);
DCHECK(isolate_->needs_side_effect_check());
DisallowJavascriptExecution no_js(isolate_);
if (!function->is_compiled() &&
!Compiler::Compile(function, Compiler::KEEP_EXCEPTION)) {
@ -2381,28 +2271,12 @@ bool Debug::PerformSideEffectCheck(Handle<JSFunction> function) {
isolate_->TerminateExecution();
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;
}
bool Debug::PerformSideEffectCheckForCallback(Handle<Object> callback_info) {
DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects);
// TODO(7515): always pass a valid callback info object.
if (!callback_info.is_null() &&
DebugEvaluate::CallbackHasNoSideEffect(*callback_info)) {
return true;
}
bool Debug::PerformSideEffectCheckForCallback(Object* callback_info) {
DCHECK(isolate_->needs_side_effect_check());
if (DebugEvaluate::CallbackHasNoSideEffect(callback_info)) return true;
side_effect_check_failed_ = true;
// Throw an uncatchable termination exception.
isolate_->TerminateExecution();
@ -2410,32 +2284,6 @@ bool Debug::PerformSideEffectCheckForCallback(Handle<Object> callback_info) {
return false;
}
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)) {
return true;
}
}
if (FLAG_trace_side_effect_free_debug_evaluate) {
JavaScriptFrameIterator it(isolate_);
InterpretedFrame* interpreted_frame =
reinterpret_cast<InterpretedFrame*>(it.frame());
SharedFunctionInfo* shared = interpreted_frame->function()->shared();
BytecodeArray* bytecode_array = shared->bytecode_array();
int bytecode_offset = interpreted_frame->GetBytecodeOffset();
interpreter::Bytecode bytecode =
interpreter::Bytecodes::FromByte(bytecode_array->get(bytecode_offset));
PrintF("[debug-evaluate] %s failed runtime side effect check.\n",
interpreter::Bytecodes::ToString(bytecode));
}
side_effect_check_failed_ = true;
// Throw an uncatchable termination exception.
isolate_->TerminateExecution();
return false;
}
void LegacyDebugDelegate::PromiseEventOccurred(
v8::debug::PromiseDebugActionType type, int id, bool is_blackboxed) {
DebugScope debug_scope(isolate_->debug());
@ -2546,5 +2394,21 @@ void NativeDebugDelegate::ProcessDebugEvent(v8::DebugEvent event,
CHECK(!isolate->has_scheduled_exception());
}
NoSideEffectScope::~NoSideEffectScope() {
if (isolate_->needs_side_effect_check() &&
isolate_->debug()->side_effect_check_failed_) {
DCHECK(isolate_->has_pending_exception());
DCHECK_EQ(isolate_->heap()->termination_exception(),
isolate_->pending_exception());
// Convert the termination exception into a regular exception.
isolate_->CancelTerminateExecution();
isolate_->Throw(*isolate_->factory()->NewEvalError(
MessageTemplate::kNoSideEffectDebugEvaluate));
}
isolate_->set_needs_side_effect_check(false);
isolate_->debug()->UpdateHookOnFunctionCall();
isolate_->debug()->side_effect_check_failed_ = false;
}
} // namespace internal
} // namespace v8

View File

@ -267,7 +267,7 @@ class Debug {
void ClearStepOut();
void DeoptimizeFunction(Handle<SharedFunctionInfo> shared);
void PrepareFunctionForDebugExecution(Handle<SharedFunctionInfo> shared);
void PrepareFunctionForBreakPoints(Handle<SharedFunctionInfo> shared);
void InstallDebugBreakTrampoline();
bool GetPossibleBreakpoints(Handle<Script> script, int start_position,
int end_position, bool restrict_to_function,
@ -336,18 +336,8 @@ class Debug {
return is_active() && !debug_context().is_null() && break_id() != 0;
}
// Apply proper instrumentation depends on debug_execution_mode.
void ApplyInstrumentation(Handle<SharedFunctionInfo> shared);
void StartSideEffectCheckMode();
void StopSideEffectCheckMode();
void ApplySideEffectChecks(Handle<DebugInfo> debug_info);
void ClearSideEffectChecks(Handle<DebugInfo> debug_info);
bool PerformSideEffectCheck(Handle<JSFunction> function);
bool PerformSideEffectCheckForCallback(Handle<Object> callback_info);
bool PerformSideEffectCheckForObject(Handle<Object> object);
bool PerformSideEffectCheckForCallback(Object* callback_info);
// Flags and states.
DebugScope* debugger_entry() {
@ -413,7 +403,7 @@ class Debug {
private:
explicit Debug(Isolate* isolate);
~Debug();
~Debug() { DCHECK_NULL(debug_delegate_); }
void UpdateState();
void UpdateHookOnFunctionCall();
@ -427,8 +417,7 @@ class Debug {
int CurrentFrameCount();
inline bool ignore_events() const {
return is_suppressed_ || !is_active_ ||
isolate_->debug_execution_mode() == DebugInfo::kSideEffects;
return is_suppressed_ || !is_active_ || isolate_->needs_side_effect_check();
}
inline bool break_disabled() const { return break_disabled_; }
@ -533,10 +522,6 @@ class Debug {
// List of active debug info objects.
DebugInfoListNode* debug_info_list_;
// Used for side effect check to mark temporary objects.
class TemporaryObjectsTracker;
std::unique_ptr<TemporaryObjectsTracker> temporary_objects_;
// Used to collect histogram data on debugger feature usage.
DebugFeatureTracker feature_tracker_;
@ -744,6 +729,23 @@ class SuppressDebug BASE_EMBEDDED {
DISALLOW_COPY_AND_ASSIGN(SuppressDebug);
};
class NoSideEffectScope {
public:
NoSideEffectScope(Isolate* isolate, bool disallow_side_effects)
: isolate_(isolate) {
// NoSideEffectScope is not re-entrant if already enabled.
CHECK(!isolate->needs_side_effect_check());
isolate->set_needs_side_effect_check(disallow_side_effects);
isolate->debug()->UpdateHookOnFunctionCall();
isolate->debug()->side_effect_check_failed_ = false;
}
~NoSideEffectScope();
private:
Isolate* isolate_;
DISALLOW_COPY_AND_ASSIGN(NoSideEffectScope);
};
// Code generator routines.
class DebugCodegen : public AllStatic {
public:

View File

@ -907,11 +907,6 @@ ExternalReference ExternalReference::debug_hook_on_function_call_address(
return ExternalReference(isolate->debug()->hook_on_function_call_address());
}
ExternalReference ExternalReference::debug_execution_mode_address(
Isolate* isolate) {
return ExternalReference(isolate->debug_execution_mode_address());
}
ExternalReference ExternalReference::runtime_function_table_address(
Isolate* isolate) {
return ExternalReference(

View File

@ -48,7 +48,6 @@ class StatsCounter;
"copy_typed_array_elements_to_typed_array") \
V(cpu_features, "cpu_features") \
V(date_cache_stamp, "date_cache_stamp") \
V(debug_execution_mode_address, "Isolate::debug_execution_mode()") \
V(debug_hook_on_function_call_address, \
"Debug::hook_on_function_call_address()") \
V(debug_is_active_address, "Debug::is_active_address()") \

View File

@ -359,8 +359,9 @@ AllocationResult Heap::AllocateRaw(int size_in_bytes, AllocationSpace space,
void Heap::OnAllocationEvent(HeapObject* object, int size_in_bytes) {
for (auto& tracker : allocation_trackers_) {
tracker->AllocationEvent(object->address(), size_in_bytes);
HeapProfiler* profiler = isolate_->heap_profiler();
if (profiler->is_tracking_allocations()) {
profiler->AllocationEvent(object->address(), size_in_bytes);
}
if (FLAG_verify_predictable) {
@ -392,9 +393,6 @@ void Heap::OnMoveEvent(HeapObject* target, HeapObject* source,
heap_profiler->ObjectMoveEvent(source->address(), target->address(),
size_in_bytes);
}
for (auto& tracker : allocation_trackers_) {
tracker->MoveEvent(source->address(), target->address(), size_in_bytes);
}
if (target->IsSharedFunctionInfo()) {
LOG_CODE_EVENT(isolate_, SharedFunctionInfoMoveEvent(source->address(),
target->address()));

View File

@ -442,20 +442,6 @@ void Heap::ReportStatisticsAfterGC() {
}
}
void Heap::AddHeapObjectAllocationTracker(
HeapObjectAllocationTracker* tracker) {
if (allocation_trackers_.empty()) DisableInlineAllocation();
allocation_trackers_.push_back(tracker);
}
void Heap::RemoveHeapObjectAllocationTracker(
HeapObjectAllocationTracker* tracker) {
allocation_trackers_.erase(std::remove(allocation_trackers_.begin(),
allocation_trackers_.end(), tracker),
allocation_trackers_.end());
if (allocation_trackers_.empty()) EnableInlineAllocation();
}
void Heap::AddRetainingPathTarget(Handle<HeapObject> object,
RetainingPathOption option) {
if (!FLAG_track_retaining_path) {
@ -1956,8 +1942,7 @@ static bool IsLogging(Isolate* isolate) {
return FLAG_verify_predictable || isolate->logger()->is_logging() ||
isolate->is_profiling() ||
(isolate->heap_profiler() != nullptr &&
isolate->heap_profiler()->is_tracking_object_moves()) ||
isolate->heap()->has_heap_object_allocation_tracker();
isolate->heap_profiler()->is_tracking_object_moves());
}
class PageScavengingItem final : public ItemParallelJob::Item {
@ -3163,10 +3148,11 @@ void Heap::RightTrimFixedArray(FixedArrayBase* object, int elements_to_trim) {
// avoid races with the sweeper thread.
object->synchronized_set_length(len - elements_to_trim);
// Notify the heap object allocation tracker of change in object layout. The
// array may not be moved during GC, and size has to be adjusted nevertheless.
for (auto& tracker : allocation_trackers_) {
tracker->UpdateObjectSizeEvent(object->address(), object->Size());
// Notify the heap profiler of change in object layout. The array may not be
// moved during GC, and size has to be adjusted nevertheless.
HeapProfiler* profiler = isolate()->heap_profiler();
if (profiler->is_tracking_allocations()) {
profiler->UpdateObjectSizeEvent(object->address(), object->Size());
}
}

View File

@ -422,7 +422,6 @@ class GCIdleTimeAction;
class GCIdleTimeHandler;
class GCIdleTimeHeapState;
class GCTracer;
class HeapObjectAllocationTracker;
class HeapObjectsFilter;
class HeapStats;
class HistogramTimer;
@ -1620,15 +1619,6 @@ class Heap {
}
// ===========================================================================
// Heap object allocation tracking. ==========================================
// ===========================================================================
void AddHeapObjectAllocationTracker(HeapObjectAllocationTracker* tracker);
void RemoveHeapObjectAllocationTracker(HeapObjectAllocationTracker* tracker);
bool has_heap_object_allocation_tracker() const {
return !allocation_trackers_.empty();
}
// Retaining path tracking. ==================================================
// ===========================================================================
@ -2680,8 +2670,6 @@ class Heap {
// stores the option of the corresponding target.
std::map<int, RetainingPathOption> retaining_path_target_option_;
std::vector<HeapObjectAllocationTracker*> allocation_trackers_;
// Classes in "heap" can be friends.
friend class AlwaysAllocateScope;
friend class ConcurrentMarking;
@ -2954,16 +2942,6 @@ class AllocationObserver {
V8_EXPORT_PRIVATE const char* AllocationSpaceName(AllocationSpace space);
// -----------------------------------------------------------------------------
// Allows observation of heap object allocations.
class HeapObjectAllocationTracker {
public:
virtual void AllocationEvent(Address addr, int size) = 0;
virtual void MoveEvent(Address from, Address to, int size) {}
virtual void UpdateObjectSizeEvent(Address addr, int size) {}
virtual ~HeapObjectAllocationTracker() = default;
};
} // namespace internal
} // namespace v8

View File

@ -3383,8 +3383,7 @@ void MarkCompactCollectorBase::CreateAndExecuteEvacuationTasks(
const bool profiling =
heap()->isolate()->is_profiling() ||
heap()->isolate()->logger()->is_logging_code_events() ||
heap()->isolate()->heap_profiler()->is_tracking_object_moves() ||
heap()->has_heap_object_allocation_tracker();
heap()->isolate()->heap_profiler()->is_tracking_object_moves();
ProfilingMigrationObserver profiling_observer(heap());
const int wanted_num_tasks =

View File

@ -26,7 +26,6 @@
#include "src/heap/heap.h"
#include "src/messages.h"
#include "src/objects/code.h"
#include "src/objects/debug-objects.h"
#include "src/runtime/runtime.h"
#include "src/unicode.h"
@ -438,7 +437,7 @@ typedef std::vector<HeapObject*> DebugObjectCache;
/* true if a trace is being formatted through Error.prepareStackTrace. */ \
V(bool, formatting_stack_trace, false) \
/* Perform side effect checks on function call and API callbacks. */ \
V(DebugInfo::ExecutionMode, debug_execution_mode, DebugInfo::kBreakpoints) \
V(bool, needs_side_effect_check, false) \
/* Current code coverage mode */ \
V(debug::Coverage::Mode, code_coverage_mode, debug::Coverage::kBestEffort) \
V(debug::TypeProfile::Mode, type_profile_mode, debug::TypeProfile::kNone) \
@ -1240,10 +1239,6 @@ class Isolate {
return reinterpret_cast<Address>(&handle_scope_implementer_);
}
Address debug_execution_mode_address() {
return reinterpret_cast<Address>(&debug_execution_mode_);
}
void DebugStateUpdated();
void SetPromiseHook(PromiseHook hook);

View File

@ -13641,12 +13641,8 @@ String* SharedFunctionInfo::DebugName() {
// static
bool SharedFunctionInfo::HasNoSideEffect(Handle<SharedFunctionInfo> info) {
if (!info->computed_has_no_side_effect()) {
DebugEvaluate::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);
bool has_no_side_effect = DebugEvaluate::FunctionHasNoSideEffect(info);
info->set_has_no_side_effect(has_no_side_effect);
info->set_computed_has_no_side_effect(true);
}
return info->has_no_side_effect();

View File

@ -12,13 +12,9 @@ bool DebugInfo::IsEmpty() const { return flags() == kNone; }
bool DebugInfo::HasBreakInfo() const { return (flags() & kHasBreakInfo) != 0; }
DebugInfo::ExecutionMode DebugInfo::DebugExecutionMode() const {
return (flags() & kDebugExecutionMode) != 0 ? kSideEffects : kBreakpoints;
}
void DebugInfo::SetDebugExecutionMode(ExecutionMode value) {
set_flags(value == kSideEffects ? (flags() | kDebugExecutionMode)
: (flags() & ~kDebugExecutionMode));
bool DebugInfo::IsPreparedForBreakpoints() const {
DCHECK(HasBreakInfo());
return (flags() & kPreparedForBreakpoints) != 0;
}
bool DebugInfo::ClearBreakInfo() {
@ -28,9 +24,8 @@ bool DebugInfo::ClearBreakInfo() {
set_break_points(isolate->heap()->empty_fixed_array());
int new_flags = flags();
new_flags &= ~kHasBreakInfo & ~kPreparedForDebugExecution;
new_flags &= ~kHasBreakInfo & ~kPreparedForBreakpoints;
new_flags &= ~kBreakAtEntry & ~kCanBreakAtEntry;
new_flags &= ~kDebugExecutionMode;
set_flags(new_flags);
return new_flags == kNone;

View File

@ -24,13 +24,11 @@ class DebugInfo : public Struct {
enum Flag {
kNone = 0,
kHasBreakInfo = 1 << 0,
kPreparedForDebugExecution = 1 << 1,
kPreparedForBreakpoints = 1 << 1,
kHasCoverageInfo = 1 << 2,
kBreakAtEntry = 1 << 3,
kCanBreakAtEntry = 1 << 4,
kDebugExecutionMode = 1 << 5
kCanBreakAtEntry = 1 << 4
};
typedef base::Flags<Flag> Flags;
// A bitfield that lists uses of the current instance.
@ -45,27 +43,13 @@ class DebugInfo : public Struct {
// DebugInfo can be detached from the SharedFunctionInfo iff it is empty.
bool IsEmpty() const;
// --- Debug execution ---
// -----------------------
enum ExecutionMode { kBreakpoints = 0, kSideEffects = kDebugExecutionMode };
// Returns current debug execution mode. Debug execution mode defines by
// applied to bytecode patching. False for breakpoints, true for side effect
// checks.
ExecutionMode DebugExecutionMode() const;
void SetDebugExecutionMode(ExecutionMode value);
inline bool HasDebugBytecodeArray();
inline BytecodeArray* OriginalBytecodeArray();
inline BytecodeArray* DebugBytecodeArray();
// --- Break points ---
// --------------------
bool HasBreakInfo() const;
bool IsPreparedForBreakpoints() const;
// Clears all fields related to break points. Returns true iff the
// DebugInfo is now empty.
bool ClearBreakInfo();
@ -98,6 +82,11 @@ class DebugInfo : public Struct {
// Get the number of break points for this function.
int GetBreakPointCount();
inline bool HasDebugBytecodeArray();
inline BytecodeArray* OriginalBytecodeArray();
inline BytecodeArray* DebugBytecodeArray();
// Returns whether we should be able to break before entering the function.
// This is true for functions with no source, e.g. builtins.
bool CanBreakAtEntry() const;

View File

@ -197,9 +197,6 @@ 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)

View File

@ -240,9 +240,6 @@ class SharedFunctionInfo : public HeapObject {
// 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)
@ -514,16 +511,15 @@ 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(HasNoSideEffectBit, bool, 1, _) \
V(ComputedHasNoSideEffectBit, bool, 1, _) \
V(DebugIsBlackboxedBit, bool, 1, _) \
V(ComputedDebugIsBlackboxedBit, bool, 1, _) \
V(HasReportedBinaryCoverageBit, bool, 1, _) \
V(DebuggingIdBits, int, 20, _)
DEFINE_BIT_FIELDS(DEBUGGER_HINTS_BIT_FIELDS)

View File

@ -132,10 +132,10 @@ v8::AllocationProfile* HeapProfiler::GetAllocationProfile() {
void HeapProfiler::StartHeapObjectsTracking(bool track_allocations) {
ids_->UpdateHeapObjectsMap();
is_tracking_object_moves_ = true;
DCHECK(!allocation_tracker_);
DCHECK(!is_tracking_allocations());
if (track_allocations) {
allocation_tracker_.reset(new AllocationTracker(ids_.get(), names_.get()));
heap()->AddHeapObjectAllocationTracker(this);
heap()->DisableInlineAllocation();
heap()->isolate()->debug()->feature_tracker()->Track(
DebugFeatureTracker::kAllocationTracking);
}
@ -148,9 +148,9 @@ SnapshotObjectId HeapProfiler::PushHeapObjectsStats(OutputStream* stream,
void HeapProfiler::StopHeapObjectsTracking() {
ids_->StopHeapObjectsTracking();
if (allocation_tracker_) {
if (is_tracking_allocations()) {
allocation_tracker_.reset();
heap()->RemoveHeapObjectAllocationTracker(this);
heap()->EnableInlineAllocation();
}
}
@ -206,7 +206,7 @@ Handle<HeapObject> HeapProfiler::FindHeapObjectById(SnapshotObjectId id) {
void HeapProfiler::ClearHeapObjectMap() {
ids_.reset(new HeapObjectsMap(heap()));
if (!allocation_tracker_) is_tracking_object_moves_ = false;
if (!is_tracking_allocations()) is_tracking_object_moves_ = false;
}

View File

@ -24,7 +24,7 @@ class HeapSnapshot;
class SamplingHeapProfiler;
class StringsStorage;
class HeapProfiler : public HeapObjectAllocationTracker {
class HeapProfiler {
public:
explicit HeapProfiler(Heap* heap);
~HeapProfiler();
@ -57,9 +57,9 @@ class HeapProfiler : public HeapObjectAllocationTracker {
void ObjectMoveEvent(Address from, Address to, int size);
void AllocationEvent(Address addr, int size) override;
void AllocationEvent(Address addr, int size);
void UpdateObjectSizeEvent(Address addr, int size) override;
void UpdateObjectSizeEvent(Address addr, int size);
void DefineWrapperClass(
uint16_t class_id, v8::HeapProfiler::WrapperInfoCallback callback);
@ -79,6 +79,7 @@ class HeapProfiler : public HeapObjectAllocationTracker {
}
bool is_tracking_object_moves() const { return is_tracking_object_moves_; }
bool is_tracking_allocations() const { return !!allocation_tracker_; }
Handle<HeapObject> FindHeapObjectById(SnapshotObjectId id);
void ClearHeapObjectMap();

View File

@ -16,7 +16,6 @@
#include "src/debug/liveedit.h"
#include "src/frames-inl.h"
#include "src/globals.h"
#include "src/interpreter/bytecode-array-accessor.h"
#include "src/interpreter/bytecodes.h"
#include "src/interpreter/interpreter.h"
#include "src/isolate-inl.h"
@ -44,9 +43,7 @@ RUNTIME_FUNCTION_RETURN_PAIR(Runtime_DebugBreakOnBytecode) {
// Get the top-most JavaScript frame.
JavaScriptFrameIterator it(isolate);
if (isolate->debug_execution_mode() == DebugInfo::kBreakpoints) {
isolate->debug()->Break(it.frame(), handle(it.frame()->function()));
}
isolate->debug()->Break(it.frame(), handle(it.frame()->function()));
// Return the handler from the original bytecode array.
DCHECK(it.frame()->is_interpreted());
@ -56,20 +53,6 @@ RUNTIME_FUNCTION_RETURN_PAIR(Runtime_DebugBreakOnBytecode) {
BytecodeArray* bytecode_array = shared->bytecode_array();
int bytecode_offset = interpreted_frame->GetBytecodeOffset();
Bytecode bytecode = Bytecodes::FromByte(bytecode_array->get(bytecode_offset));
bool side_effect_check_failed = false;
if (isolate->debug_execution_mode() == DebugInfo::kSideEffects) {
int offset = interpreted_frame->GetBytecodeOffset();
interpreter::BytecodeArrayAccessor bytecode_accessor(handle(bytecode_array),
offset);
interpreter::Register reg = bytecode_accessor.GetRegisterOperand(0);
Handle<Object> first_operand = handle(
interpreted_frame->ReadInterpreterRegister(reg.index()), isolate);
if (!isolate->debug()->PerformSideEffectCheckForObject(first_operand)) {
side_effect_check_failed = true;
}
}
if (Bytecodes::Returns(bytecode)) {
// If we are returning (or suspending), reset the bytecode array on the
// interpreted stack frame to the non-debug variant so that the interpreter
@ -87,8 +70,7 @@ RUNTIME_FUNCTION_RETURN_PAIR(Runtime_DebugBreakOnBytecode) {
isolate->interpreter()->GetAndMaybeDeserializeBytecodeHandler(bytecode,
operand_scale);
return MakePair(side_effect_check_failed ? isolate->heap()->exception()
: isolate->debug()->return_value(),
return MakePair(isolate->debug()->return_value(),
Smi::FromInt(static_cast<uint8_t>(bytecode)));
}
@ -109,14 +91,6 @@ RUNTIME_FUNCTION(Runtime_DebugBreakAtEntry) {
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_DebugApplyInstrumentation) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
isolate->debug()->ApplyInstrumentation(handle(function->shared()));
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_HandleDebuggerStatement) {
SealHandleScope shs(isolate);
DCHECK_EQ(0, args.length());
@ -1679,7 +1653,7 @@ RUNTIME_FUNCTION(Runtime_DebugOnFunctionCall) {
if (isolate->debug()->last_step_action() >= StepIn) {
isolate->debug()->PrepareStepIn(fun);
}
if (isolate->debug_execution_mode() == DebugInfo::kSideEffects &&
if (isolate->needs_side_effect_check() &&
!isolate->debug()->PerformSideEffectCheck(fun)) {
return isolate->heap()->exception();
}

View File

@ -130,7 +130,6 @@ namespace internal {
F(CheckExecutionState, 1, 1) \
F(ClearStepping, 0, 1) \
F(CollectGarbage, 1, 1) \
F(DebugApplyInstrumentation, 1, 1) \
F(DebugAsyncFunctionPromiseCreated, 1, 1) \
F(DebugBreakAtEntry, 1, 1) \
F(DebugCollectCoverage, 0, 1) \

View File

@ -6381,7 +6381,6 @@ TEST(BreakLocationIterator) {
EnableDebugger(isolate);
CHECK(i_isolate->debug()->EnsureBreakInfo(shared));
i_isolate->debug()->PrepareFunctionForDebugExecution(shared);
Handle<i::DebugInfo> debug_info(shared->GetDebugInfo());
@ -6679,9 +6678,10 @@ TEST(DebugEvaluateNoSideEffect) {
// itself contains additional sanity checks.
for (i::Handle<i::JSFunction> fun : all_functions) {
bool failed = false;
isolate->debug()->StartSideEffectCheckMode();
failed = !isolate->debug()->PerformSideEffectCheck(fun);
isolate->debug()->StopSideEffectCheckMode();
{
i::NoSideEffectScope scope(isolate, true);
failed = !isolate->debug()->PerformSideEffectCheck(fun);
}
if (failed) isolate->clear_pending_exception();
}
DisableDebugger(env->GetIsolate());

View File

@ -11,7 +11,6 @@ var symbol_for_a = Symbol.for("a");
var typed_array = new Uint8Array([1, 2, 3]);
var array_buffer = new ArrayBuffer(3);
var data_view = new DataView(new ArrayBuffer(8), 0, 8);
var array = [1,2,3];
function listener(event, exec_state, event_data, data) {
if (event != Debug.DebugEvent.Break) return;
@ -72,7 +71,7 @@ function listener(event, exec_state, event_data, data) {
"map", "findIndex"
];
var fails = ["toString", "join", "toLocaleString", "pop", "push", "reverse",
"shift", "unshift", "splice", "sort", "copyWithin"];
"shift", "unshift", "splice", "sort", "copyWithin", "fill"];
for (f of Object.getOwnPropertyNames(Array.prototype)) {
if (typeof Array.prototype[f] === "function") {
if (fails.includes(f)) {
@ -89,9 +88,6 @@ function listener(event, exec_state, event_data, data) {
}
}
success([1,1,1], '[1,2,3].fill(1)');
fail(`array.fill(1)`);
// Test ArrayBuffer functions.
success(3, `array_buffer.byteLength`);
success(2, `array_buffer.slice(1, 3).byteLength`);

View File

@ -73,7 +73,7 @@ function listener(event, exec_state, event_data, data) {
success("s2", `string + two`);
fail(`[...array]`);
success(3, `max(...array)`);
success({s:1}, `({[string]:1})`);
fail(`({[string]:1})`);
fail(`[a, b] = [1, 2]`);
success(2, `def(2)`);
success(1, `def()`);

View File

@ -1,64 +0,0 @@
// Copyright 2018 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.
Debug = debug.Debug;
// StaNamedProperty
var a = {name: 'foo'};
function set_name(a) {
a.name = 'bar';
return a.name;
}
fail(`set_name(a)`);
success('bar', `set_name({name: 'foo'})`);
// StaNamedOwnProperty
var name_value = 'value';
function create_object_literal() {
var obj = {name: name_value};
return obj.name;
};
success('value', `create_object_literal()`);
// StaKeyedProperty
var arrayValue = 1;
function create_array_literal() {
return [arrayValue];
}
var b = { 1: 2 };
success([arrayValue], `create_array_literal()`)
fail(`b[1] ^= 2`);
// StaInArrayLiteral
function return_array_use_spread(a) {
return [...a];
}
fail(`return_array_use_spread([1])`);
// CallAccessorSetter
var array = [1,2,3];
fail(`array.length = 2`);
// TODO(7515): this one should be side effect free
fail(`[1,2,3].length = 2`);
// StaDataPropertyInLiteral
function return_literal_with_data_property(a) {
return {[a] : 1};
}
success({foo: 1}, `return_literal_with_data_property('foo')`);
function success(expectation, source) {
const result = Debug.evaluateGlobal(source, true).value();
if (expectation !== undefined) assertEquals(expectation, result);
}
function fail(source) {
assertThrows(() => Debug.evaluateGlobal(source, true),
EvalError);
}

View File

@ -67,8 +67,8 @@ function listener(event, exec_state, event_data, data) {
// Constructed literals.
success([1], "[1]");
success({x: 1}, "({x: 1})");
success([1], "[a]");
success({x: 1}, "({x: a})");
fail("[a]");
fail("({x: a})");
// Test that template literal evaluation fails.
fail("simple_return`1`");
// Test that non-read-only code fails.

View File

@ -731,9 +731,9 @@ class DebugWrapper {
};
}
evaluateGlobal(expr, throw_on_side_effect) {
execStateEvaluateGlobal(expr) {
const {msgid, msg} = this.createMessage(
"Runtime.evaluate", { expression : expr, throwOnSideEffect: throw_on_side_effect });
"Runtime.evaluate", { expression : expr });
this.sendMessage(msg);
const reply = this.takeReplyChecked(msgid);
@ -830,7 +830,7 @@ class DebugWrapper {
let execState = { frames : params.callFrames,
prepareStep : this.execStatePrepareStep.bind(this),
evaluateGlobal :
(expr) => this.evaluateGlobal(expr),
(expr) => this.execStateEvaluateGlobal(expr),
frame : (index) => this.execStateFrame(
index ? params.callFrames[index]
: params.callFrames[0]),

View File

@ -1,26 +0,0 @@
Tests how breakpoints and side effects live together.
Test breakpoint, should pause inside foo:
foo (test.js:3:2)
(anonymous) (:0:0)
Run foo with no side effects:
{
className : EvalError
description : EvalError: Possible side-effect in debug-evaluate
objectId : <objectId>
subtype : error
type : object
}
Test breakpoint after run with side effect check:
foo (test.js:3:2)
(anonymous) (:0:0)
Run foo with no side effects after debugger disabled:
{
className : EvalError
description : EvalError: Possible side-effect in debug-evaluate
objectId : <objectId>
subtype : error
type : object
}

View File

@ -1,58 +0,0 @@
// Copyright 2018 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.
let {session, contextGroup, Protocol} =
InspectorTest.start('Tests how breakpoints and side effects live together.');
(async function main() {
session.setupScriptMap();
Protocol.Debugger.enable();
Protocol.Runtime.evaluate({expression: `
var a = 1;
function foo() {
a = 2;
}
//# sourceURL=test.js`});
Protocol.Debugger.setBreakpointByUrl({url: 'test.js', lineNumber: 3});
InspectorTest.log('Test breakpoint, should pause inside foo:');
{
const evaluatePromise = Protocol.Runtime.evaluate({expression: 'foo()'});
const {params:{callFrames}} = await Protocol.Debugger.oncePaused();
session.logCallFrames(callFrames);
Protocol.Debugger.resume();
await evaluatePromise;
}
InspectorTest.log('\nRun foo with no side effects:');
{
const {result:{result}} = await Protocol.Runtime.evaluate({
expression: 'foo()',
throwOnSideEffect: true
});
InspectorTest.logMessage(result);
}
InspectorTest.log('\nTest breakpoint after run with side effect check:');
{
const evaluatePromise = Protocol.Runtime.evaluate({expression: 'foo()'});
const {params:{callFrames}} = await Protocol.Debugger.oncePaused();
session.logCallFrames(callFrames);
Protocol.Debugger.resume();
await evaluatePromise;
}
await Protocol.Debugger.disable();
InspectorTest.log('\nRun foo with no side effects after debugger disabled:');
{
const {result:{result}} = await Protocol.Runtime.evaluate({
expression: 'foo()',
throwOnSideEffect: true
});
InspectorTest.logMessage(result);
}
InspectorTest.completeTest();
})();