blob: 040da3a5a6d7e9decb410a94e9f277e5ac05e828 [file] [log] [blame]
// 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.
#include "src/debug/debug-evaluate.h"
#include "src/builtins/accessors.h"
#include "src/codegen/assembler-inl.h"
#include "src/codegen/compiler.h"
#include "src/codegen/reloc-info.h"
#include "src/codegen/script-details.h"
#include "src/common/globals.h"
#include "src/debug/debug-frames.h"
#include "src/debug/debug-scopes.h"
#include "src/debug/debug.h"
#include "src/execution/frames-inl.h"
#include "src/execution/isolate-inl.h"
#include "src/interpreter/bytecode-array-iterator.h"
#include "src/interpreter/bytecodes.h"
#include "src/objects/code-inl.h"
#include "src/objects/contexts.h"
#include "src/objects/string-set-inl.h"
#if V8_ENABLE_WEBASSEMBLY
#include "src/debug/debug-wasm-objects.h"
#endif // V8_ENABLE_WEBASSEMBLY
namespace v8 {
namespace internal {
namespace {
static MaybeHandle<SharedFunctionInfo> GetFunctionInfo(Isolate* isolate,
Handle<String> source,
REPLMode repl_mode) {
ScriptDetails script_details(isolate->factory()->empty_string(),
ScriptOriginOptions(true, true));
script_details.repl_mode = repl_mode;
ScriptCompiler::CompilationDetails compilation_details;
return Compiler::GetSharedFunctionInfoForScript(
isolate, source, script_details, ScriptCompiler::kNoCompileOptions,
ScriptCompiler::kNoCacheNoReason, NOT_NATIVES_CODE, &compilation_details);
}
} // namespace
MaybeHandle<Object> DebugEvaluate::Global(Isolate* isolate,
Handle<String> source,
debug::EvaluateGlobalMode mode,
REPLMode repl_mode) {
Handle<SharedFunctionInfo> shared_info;
if (!GetFunctionInfo(isolate, source, repl_mode).ToHandle(&shared_info)) {
return MaybeHandle<Object>();
}
Handle<NativeContext> context = isolate->native_context();
Handle<JSFunction> function =
Factory::JSFunctionBuilder{isolate, shared_info, context}.Build();
DisableBreak disable_break_scope(
isolate->debug(),
mode == debug::EvaluateGlobalMode::kDisableBreaks ||
mode ==
debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect);
if (mode == debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) {
isolate->debug()->StartSideEffectCheckMode();
}
// TODO(cbruni, 1244145): Use host-defined options from script context.
Handle<FixedArray> host_defined_options(
Script::cast(function->shared()->script())->host_defined_options(),
isolate);
MaybeHandle<Object> result = Execution::CallScript(
isolate, function, Handle<JSObject>(context->global_proxy(), isolate),
host_defined_options);
if (mode == debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) {
isolate->debug()->StopSideEffectCheckMode();
}
return result;
}
MaybeHandle<Object> DebugEvaluate::Local(Isolate* isolate,
StackFrameId frame_id,
int inlined_jsframe_index,
Handle<String> source,
bool throw_on_side_effect) {
// Handle the processing of break.
DisableBreak disable_break_scope(isolate->debug());
// Get the frame where the debugging is performed.
DebuggableStackFrameIterator it(isolate, frame_id);
#if V8_ENABLE_WEBASSEMBLY
if (it.is_wasm()) {
WasmFrame* frame = WasmFrame::cast(it.frame());
Handle<SharedFunctionInfo> outer_info(
isolate->native_context()->empty_function()->shared(), isolate);
Handle<JSObject> context_extension = GetWasmDebugProxy(frame);
Handle<ScopeInfo> scope_info =
ScopeInfo::CreateForWithScope(isolate, Handle<ScopeInfo>::null());
Handle<Context> context = isolate->factory()->NewWithContext(
isolate->native_context(), scope_info, context_extension);
return Evaluate(isolate, outer_info, context, context_extension, source,
throw_on_side_effect);
}
#endif // V8_ENABLE_WEBASSEMBLY
CHECK(it.is_javascript());
JavaScriptFrame* frame = it.javascript_frame();
// This is not a lot different than DebugEvaluate::Global, except that
// variables accessible by the function we are evaluating from are
// materialized and included on top of the native context. Changes to
// the materialized object are written back afterwards.
// Note that the native context is taken from the original context chain,
// which may not be the current native context of the isolate.
ContextBuilder context_builder(isolate, frame, inlined_jsframe_index);
if (isolate->has_exception()) return {};
Handle<Context> context = context_builder.evaluation_context();
Handle<JSObject> receiver(context->global_proxy(), isolate);
MaybeHandle<Object> maybe_result =
Evaluate(isolate, context_builder.outer_info(), context, receiver, source,
throw_on_side_effect);
if (!maybe_result.is_null()) context_builder.UpdateValues();
return maybe_result;
}
MaybeHandle<Object> DebugEvaluate::WithTopmostArguments(Isolate* isolate,
Handle<String> source) {
// Handle the processing of break.
DisableBreak disable_break_scope(isolate->debug());
Factory* factory = isolate->factory();
JavaScriptStackFrameIterator it(isolate);
// Get context and receiver.
Handle<Context> native_context(
Context::cast(it.frame()->context())->native_context(), isolate);
// Materialize arguments as property on an extension object.
Handle<JSObject> materialized = factory->NewSlowJSObjectWithNullProto();
Handle<String> arguments_str = factory->arguments_string();
JSObject::SetOwnPropertyIgnoreAttributes(
materialized, arguments_str,
Accessors::FunctionGetArguments(it.frame(), 0), NONE)
.Check();
// Materialize receiver.
Handle<Object> this_value(it.frame()->receiver(), isolate);
DCHECK_EQ(it.frame()->IsConstructor(), IsTheHole(*this_value, isolate));
if (!IsTheHole(*this_value, isolate)) {
Handle<String> this_str = factory->this_string();
JSObject::SetOwnPropertyIgnoreAttributes(materialized, this_str, this_value,
NONE)
.Check();
}
// Use extension object in a debug-evaluate scope.
Handle<ScopeInfo> scope_info =
ScopeInfo::CreateForWithScope(isolate, Handle<ScopeInfo>::null());
scope_info->SetIsDebugEvaluateScope();
Handle<Context> evaluation_context = factory->NewDebugEvaluateContext(
native_context, scope_info, materialized, Handle<Context>());
Handle<SharedFunctionInfo> outer_info(
native_context->empty_function()->shared(), isolate);
Handle<JSObject> receiver(native_context->global_proxy(), isolate);
const bool throw_on_side_effect = false;
MaybeHandle<Object> maybe_result =
Evaluate(isolate, outer_info, evaluation_context, receiver, source,
throw_on_side_effect);
return maybe_result;
}
// Compile and evaluate source for the given context.
MaybeHandle<Object> DebugEvaluate::Evaluate(
Isolate* isolate, Handle<SharedFunctionInfo> outer_info,
Handle<Context> context, Handle<Object> receiver, Handle<String> source,
bool throw_on_side_effect) {
Handle<JSFunction> eval_fun;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, eval_fun,
Compiler::GetFunctionFromEval(
source, outer_info, context, LanguageMode::kSloppy,
NO_PARSE_RESTRICTION, kNoSourcePosition, kNoSourcePosition,
kNoSourcePosition, ParsingWhileDebugging::kYes),
Object);
Handle<Object> result;
bool success = false;
if (throw_on_side_effect) isolate->debug()->StartSideEffectCheckMode();
success = Execution::Call(isolate, eval_fun, receiver, 0, nullptr)
.ToHandle(&result);
if (throw_on_side_effect) isolate->debug()->StopSideEffectCheckMode();
if (!success) DCHECK(isolate->has_exception());
return success ? result : MaybeHandle<Object>();
}
Handle<SharedFunctionInfo> DebugEvaluate::ContextBuilder::outer_info() const {
return handle(frame_inspector_.GetFunction()->shared(), isolate_);
}
DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate,
JavaScriptFrame* frame,
int inlined_jsframe_index)
: isolate_(isolate),
frame_inspector_(frame, inlined_jsframe_index, isolate),
scope_iterator_(isolate, &frame_inspector_,
ScopeIterator::ReparseStrategy::kScriptIfNeeded) {
Handle<Context> outer_context(frame_inspector_.GetFunction()->context(),
isolate);
evaluation_context_ = outer_context;
Factory* factory = isolate->factory();
if (scope_iterator_.Done()) return;
// To evaluate as if we were running eval at the point of the debug break,
// we reconstruct the context chain as follows:
// - To make stack-allocated variables visible, we materialize them and
// use a debug-evaluate context to wrap both the materialized object and
// the original context.
// - Each scope from the break position up to the function scope is wrapped
// in a debug-evaluate context.
// - Between the function scope and the native context, we only resolve
// variable names that are guaranteed to not be shadowed by stack-allocated
// variables. ScopeInfos between the function scope and the native
// context have a blocklist attached to implement that.
// - The various block lists are calculated by the ScopeIterator during
// iteration.
// Context::Lookup has special handling for debug-evaluate contexts:
// - Look up in the materialized stack variables.
// - Look up in the original context.
// - Once we have seen a debug-evaluate context we start to take the
// block lists into account before moving up the context chain.
for (; scope_iterator_.InInnerScope(); scope_iterator_.Next()) {
ScopeIterator::ScopeType scope_type = scope_iterator_.Type();
if (scope_type == ScopeIterator::ScopeTypeScript) break;
ContextChainElement context_chain_element;
if (scope_type == ScopeIterator::ScopeTypeLocal ||
scope_iterator_.DeclaresLocals(ScopeIterator::Mode::STACK)) {
context_chain_element.materialized_object =
scope_iterator_.ScopeObject(ScopeIterator::Mode::STACK);
}
if (scope_iterator_.HasContext()) {
context_chain_element.wrapped_context = scope_iterator_.CurrentContext();
}
context_chain_.push_back(context_chain_element);
}
Handle<ScopeInfo> scope_info =
IsNativeContext(*evaluation_context_)
? Handle<ScopeInfo>::null()
: handle(evaluation_context_->scope_info(), isolate);
for (auto rit = context_chain_.rbegin(); rit != context_chain_.rend();
rit++) {
ContextChainElement element = *rit;
scope_info = ScopeInfo::CreateForWithScope(isolate, scope_info);
scope_info->SetIsDebugEvaluateScope();
// In the case where the "paused function scope" is the script scope
// itself, we don't need (and don't have) a blocklist.
const bool paused_scope_is_script_scope =
scope_iterator_.Done() || scope_iterator_.InInnerScope();
if (rit == context_chain_.rbegin() && !paused_scope_is_script_scope) {
// The DebugEvaluateContext we create for the closure scope is the only
// DebugEvaluateContext with a block list. This means we'll retrieve
// the existing block list from the paused function scope
// and also associate the temporary scope_info we create here with that
// blocklist.
Handle<ScopeInfo> function_scope_info = handle(
frame_inspector_.GetFunction()->shared()->scope_info(), isolate_);
Handle<Object> block_list = handle(
isolate_->LocalsBlockListCacheGet(function_scope_info), isolate_);
CHECK(IsStringSet(*block_list));
isolate_->LocalsBlockListCacheSet(scope_info, Handle<ScopeInfo>::null(),
Handle<StringSet>::cast(block_list));
}
evaluation_context_ = factory->NewDebugEvaluateContext(
evaluation_context_, scope_info, element.materialized_object,
element.wrapped_context);
}
}
void DebugEvaluate::ContextBuilder::UpdateValues() {
scope_iterator_.Restart();
for (ContextChainElement& element : context_chain_) {
if (!element.materialized_object.is_null()) {
Handle<FixedArray> keys =
KeyAccumulator::GetKeys(isolate_, element.materialized_object,
KeyCollectionMode::kOwnOnly,
ENUMERABLE_STRINGS)
.ToHandleChecked();
for (int i = 0; i < keys->length(); i++) {
DCHECK(IsString(keys->get(i)));
Handle<String> key(String::cast(keys->get(i)), isolate_);
Handle<Object> value = JSReceiver::GetDataProperty(
isolate_, element.materialized_object, key);
scope_iterator_.SetVariableValue(key, value);
}
}
scope_iterator_.Next();
}
}
// static
bool DebugEvaluate::IsSideEffectFreeIntrinsic(Runtime::FunctionId id) {
// Use macro to include only the non-inlined version of an intrinsic.
#define INTRINSIC_ALLOWLIST(V) \
/* Conversions */ \
V(NumberToStringSlow) \
V(ToBigInt) \
V(ToLength) \
V(ToNumber) \
V(ToObject) \
V(ToString) \
/* Type checks */ \
V(IsArray) \
V(IsJSProxy) \
V(IsJSReceiver) \
V(IsSmi) \
/* Loads */ \
V(LoadLookupSlotForCall) \
V(GetPrivateMember) \
V(GetProperty) \
/* Arrays */ \
V(ArraySpeciesConstructor) \
V(HasFastPackedElements) \
V(NewArray) \
V(NormalizeElements) \
V(TypedArrayGetBuffer) \
/* Errors */ \
V(NewTypeError) \
V(ReThrow) \
V(ThrowCalledNonCallable) \
V(ThrowInvalidStringLength) \
V(ThrowIteratorError) \
V(ThrowIteratorResultNotAnObject) \
V(ThrowPatternAssignmentNonCoercible) \
V(ThrowReferenceError) \
V(ThrowSymbolIteratorInvalid) \
/* Strings */ \
V(StringReplaceOneCharWithString) \
V(StringSubstring) \
V(StringToNumber) \
/* BigInts */ \
V(BigIntEqualToBigInt) \
V(BigIntToNumber) \
/* Literals */ \
V(CreateArrayLiteral) \
V(CreateObjectLiteral) \
V(CreateRegExpLiteral) \
V(DefineClass) \
/* Called from builtins */ \
V(AllocateInYoungGeneration) \
V(AllocateInOldGeneration) \
V(ArrayIncludes_Slow) \
V(ArrayIndexOf) \
V(ArrayIsArray) \
V(GetFunctionName) \
V(GlobalPrint) \
V(HasProperty) \
V(ObjectCreate) \
V(ObjectEntries) \
V(ObjectEntriesSkipFastPath) \
V(ObjectHasOwnProperty) \
V(ObjectKeys) \
V(ObjectValues) \
V(ObjectValuesSkipFastPath) \
V(ObjectGetOwnPropertyNames) \
V(ObjectGetOwnPropertyNamesTryFast) \
V(ObjectIsExtensible) \
V(RegExpInitializeAndCompile) \
V(StackGuard) \
V(HandleNoHeapWritesInterrupts) \
V(StringAdd) \
V(StringCharCodeAt) \
V(StringEqual) \
V(StringParseFloat) \
V(StringParseInt) \
V(SymbolDescriptiveString) \
V(ThrowRangeError) \
V(ThrowTypeError) \
V(ToName) \
V(TransitionElementsKind) \
/* Misc. */ \
V(Call) \
V(CompleteInobjectSlackTrackingForMap) \
V(HasInPrototypeChain) \
V(IncrementUseCounter) \
V(MaxSmi) \
V(NewObject) \
V(StringMaxLength) \
V(StringToArray) \
V(AsyncFunctionEnter) \
V(AsyncFunctionResolve) \
/* Test */ \
V(GetOptimizationStatus) \
V(OptimizeFunctionOnNextCall) \
V(OptimizeOsr)
// Intrinsics with inline versions have to be allowlisted here a second time.
#define INLINE_INTRINSIC_ALLOWLIST(V) \
V(AsyncFunctionEnter) \
V(AsyncFunctionResolve)
#define CASE(Name) case Runtime::k##Name:
#define INLINE_CASE(Name) case Runtime::kInline##Name:
switch (id) {
INTRINSIC_ALLOWLIST(CASE)
INLINE_INTRINSIC_ALLOWLIST(INLINE_CASE)
return true;
default:
if (v8_flags.trace_side_effect_free_debug_evaluate) {
PrintF("[debug-evaluate] intrinsic %s may cause side effect.\n",
Runtime::FunctionForId(id)->name);
}
return false;
}
#undef CASE
#undef INLINE_CASE
#undef INTRINSIC_ALLOWLIST
#undef INLINE_INTRINSIC_ALLOWLIST
}
namespace {
bool BytecodeHasNoSideEffect(interpreter::Bytecode bytecode) {
using interpreter::Bytecode;
using interpreter::Bytecodes;
if (Bytecodes::IsWithoutExternalSideEffects(bytecode)) return true;
if (Bytecodes::IsCallOrConstruct(bytecode)) return true;
if (Bytecodes::IsJumpIfToBoolean(bytecode)) return true;
if (Bytecodes::IsPrefixScalingBytecode(bytecode)) return true;
switch (bytecode) {
// Allowlist for bytecodes.
// Loads.
case Bytecode::kLdaLookupSlot:
case Bytecode::kLdaGlobal:
case Bytecode::kGetNamedProperty:
case Bytecode::kGetKeyedProperty:
case Bytecode::kLdaGlobalInsideTypeof:
case Bytecode::kLdaLookupSlotInsideTypeof:
case Bytecode::kGetIterator:
// Arithmetics.
case Bytecode::kAdd:
case Bytecode::kAddSmi:
case Bytecode::kSub:
case Bytecode::kSubSmi:
case Bytecode::kMul:
case Bytecode::kMulSmi:
case Bytecode::kDiv:
case Bytecode::kDivSmi:
case Bytecode::kMod:
case Bytecode::kModSmi:
case Bytecode::kExp:
case Bytecode::kExpSmi:
case Bytecode::kNegate:
case Bytecode::kBitwiseAnd:
case Bytecode::kBitwiseAndSmi:
case Bytecode::kBitwiseNot:
case Bytecode::kBitwiseOr:
case Bytecode::kBitwiseOrSmi:
case Bytecode::kBitwiseXor:
case Bytecode::kBitwiseXorSmi:
case Bytecode::kShiftLeft:
case Bytecode::kShiftLeftSmi:
case Bytecode::kShiftRight:
case Bytecode::kShiftRightSmi:
case Bytecode::kShiftRightLogical:
case Bytecode::kShiftRightLogicalSmi:
case Bytecode::kInc:
case Bytecode::kDec:
case Bytecode::kLogicalNot:
case Bytecode::kToBooleanLogicalNot:
case Bytecode::kTypeOf:
// Contexts.
case Bytecode::kCreateBlockContext:
case Bytecode::kCreateCatchContext:
case Bytecode::kCreateFunctionContext:
case Bytecode::kCreateEvalContext:
case Bytecode::kCreateWithContext:
// Literals.
case Bytecode::kCreateArrayLiteral:
case Bytecode::kCreateEmptyArrayLiteral:
case Bytecode::kCreateArrayFromIterable:
case Bytecode::kCreateObjectLiteral:
case Bytecode::kCreateEmptyObjectLiteral:
case Bytecode::kCreateRegExpLiteral:
// Allocations.
case Bytecode::kCreateClosure:
case Bytecode::kCreateUnmappedArguments:
case Bytecode::kCreateRestParameter:
// Comparisons.
case Bytecode::kTestEqual:
case Bytecode::kTestEqualStrict:
case Bytecode::kTestLessThan:
case Bytecode::kTestLessThanOrEqual:
case Bytecode::kTestGreaterThan:
case Bytecode::kTestGreaterThanOrEqual:
case Bytecode::kTestInstanceOf:
case Bytecode::kTestIn:
case Bytecode::kTestReferenceEqual:
case Bytecode::kTestUndetectable:
case Bytecode::kTestTypeOf:
case Bytecode::kTestUndefined:
case Bytecode::kTestNull:
// Conversions.
case Bytecode::kToObject:
case Bytecode::kToName:
case Bytecode::kToNumber:
case Bytecode::kToNumeric:
case Bytecode::kToString:
case Bytecode::kToBoolean:
// Misc.
case Bytecode::kIncBlockCounter: // Coverage counters.
case Bytecode::kForInEnumerate:
case Bytecode::kForInPrepare:
case Bytecode::kForInNext:
case Bytecode::kForInStep:
case Bytecode::kJumpLoop:
case Bytecode::kThrow:
case Bytecode::kReThrow:
case Bytecode::kThrowReferenceErrorIfHole:
case Bytecode::kThrowSuperNotCalledIfHole:
case Bytecode::kThrowSuperAlreadyCalledIfNotHole:
case Bytecode::kIllegal:
case Bytecode::kCallJSRuntime:
case Bytecode::kReturn:
case Bytecode::kSetPendingMessage:
return true;
default:
return false;
}
}
DebugInfo::SideEffectState BuiltinGetSideEffectState(Builtin id) {
switch (id) {
// Allowlist for builtins.
// Object builtins.
case Builtin::kObjectConstructor:
case Builtin::kObjectCreate:
case Builtin::kObjectEntries:
case Builtin::kObjectGetOwnPropertyDescriptor:
case Builtin::kObjectGetOwnPropertyDescriptors:
case Builtin::kObjectGetOwnPropertyNames:
case Builtin::kObjectGetOwnPropertySymbols:
case Builtin::kObjectGetPrototypeOf:
case Builtin::kObjectGroupBy:
case Builtin::kObjectHasOwn:
case Builtin::kObjectIs:
case Builtin::kObjectIsExtensible:
case Builtin::kObjectIsFrozen:
case Builtin::kObjectIsSealed:
case Builtin::kObjectKeys:
case Builtin::kObjectPrototypeValueOf:
case Builtin::kObjectValues:
case Builtin::kObjectPrototypeHasOwnProperty:
case Builtin::kObjectPrototypeIsPrototypeOf:
case Builtin::kObjectPrototypePropertyIsEnumerable:
case Builtin::kObjectPrototypeToString:
case Builtin::kObjectPrototypeToLocaleString:
// Array builtins.
case Builtin::kArrayIsArray:
case Builtin::kArrayConstructor:
case Builtin::kArrayIndexOf:
case Builtin::kArrayPrototypeValues:
case Builtin::kArrayIncludes:
case Builtin::kArrayPrototypeAt:
case Builtin::kArrayPrototypeConcat:
case Builtin::kArrayPrototypeEntries:
case Builtin::kArrayPrototypeFind:
case Builtin::kArrayPrototypeFindIndex:
case Builtin::kArrayPrototypeFindLast:
case Builtin::kArrayPrototypeFindLastIndex:
case Builtin::kArrayPrototypeFlat:
case Builtin::kArrayPrototypeFlatMap:
case Builtin::kArrayPrototypeJoin:
case Builtin::kArrayPrototypeKeys:
case Builtin::kArrayPrototypeLastIndexOf:
case Builtin::kArrayPrototypeSlice:
case Builtin::kArrayPrototypeToLocaleString:
case Builtin::kArrayPrototypeToReversed:
case Builtin::kArrayPrototypeToSorted:
case Builtin::kArrayPrototypeToSpliced:
case Builtin::kArrayPrototypeToString:
case Builtin::kArrayPrototypeWith:
case Builtin::kArrayForEach:
case Builtin::kArrayEvery:
case Builtin::kArraySome:
case Builtin::kArrayConcat:
case Builtin::kArrayFilter:
case Builtin::kArrayMap:
case Builtin::kArrayReduce:
case Builtin::kArrayReduceRight:
// Trace builtins.
case Builtin::kIsTraceCategoryEnabled:
case Builtin::kTrace:
// TypedArray builtins.
case Builtin::kTypedArrayConstructor:
case Builtin::kTypedArrayOf:
case Builtin::kTypedArrayPrototypeAt:
case Builtin::kTypedArrayPrototypeBuffer:
case Builtin::kTypedArrayPrototypeByteLength:
case Builtin::kTypedArrayPrototypeByteOffset:
case Builtin::kTypedArrayPrototypeLength:
case Builtin::kTypedArrayPrototypeEntries:
case Builtin::kTypedArrayPrototypeKeys:
case Builtin::kTypedArrayPrototypeValues:
case Builtin::kTypedArrayPrototypeFind:
case Builtin::kTypedArrayPrototypeFindIndex:
case Builtin::kTypedArrayPrototypeFindLast:
case Builtin::kTypedArrayPrototypeFindLastIndex:
case Builtin::kTypedArrayPrototypeIncludes:
case Builtin::kTypedArrayPrototypeJoin:
case Builtin::kTypedArrayPrototypeIndexOf:
case Builtin::kTypedArrayPrototypeLastIndexOf:
case Builtin::kTypedArrayPrototypeSlice:
case Builtin::kTypedArrayPrototypeSubArray:
case Builtin::kTypedArrayPrototypeEvery:
case Builtin::kTypedArrayPrototypeSome:
case Builtin::kTypedArrayPrototypeToLocaleString:
case Builtin::kTypedArrayPrototypeFilter:
case Builtin::kTypedArrayPrototypeMap:
case Builtin::kTypedArrayPrototypeReduce:
case Builtin::kTypedArrayPrototypeReduceRight:
case Builtin::kTypedArrayPrototypeForEach:
case Builtin::kTypedArrayPrototypeToReversed:
case Builtin::kTypedArrayPrototypeToSorted:
case Builtin::kTypedArrayPrototypeWith:
// ArrayBuffer builtins.
case Builtin::kArrayBufferConstructor:
case Builtin::kArrayBufferPrototypeGetByteLength:
case Builtin::kArrayBufferIsView:
case Builtin::kArrayBufferPrototypeSlice:
case Builtin::kReturnReceiver:
// DataView builtins.
case Builtin::kDataViewConstructor:
case Builtin::kDataViewPrototypeGetBuffer:
case Builtin::kDataViewPrototypeGetByteLength:
case Builtin::kDataViewPrototypeGetByteOffset:
case Builtin::kDataViewPrototypeGetInt8:
case Builtin::kDataViewPrototypeGetUint8:
case Builtin::kDataViewPrototypeGetInt16:
case Builtin::kDataViewPrototypeGetUint16:
case Builtin::kDataViewPrototypeGetInt32:
case Builtin::kDataViewPrototypeGetUint32:
case Builtin::kDataViewPrototypeGetFloat16:
case Builtin::kDataViewPrototypeGetFloat32:
case Builtin::kDataViewPrototypeGetFloat64:
case Builtin::kDataViewPrototypeGetBigInt64:
case Builtin::kDataViewPrototypeGetBigUint64:
// Boolean bulitins.
case Builtin::kBooleanConstructor:
case Builtin::kBooleanPrototypeToString:
case Builtin::kBooleanPrototypeValueOf:
// Date builtins.
case Builtin::kDateConstructor:
case Builtin::kDateNow:
case Builtin::kDateParse:
case Builtin::kDatePrototypeGetDate:
case Builtin::kDatePrototypeGetDay:
case Builtin::kDatePrototypeGetFullYear:
case Builtin::kDatePrototypeGetHours:
case Builtin::kDatePrototypeGetMilliseconds:
case Builtin::kDatePrototypeGetMinutes:
case Builtin::kDatePrototypeGetMonth:
case Builtin::kDatePrototypeGetSeconds:
case Builtin::kDatePrototypeGetTime:
case Builtin::kDatePrototypeGetTimezoneOffset:
case Builtin::kDatePrototypeGetUTCDate:
case Builtin::kDatePrototypeGetUTCDay:
case Builtin::kDatePrototypeGetUTCFullYear:
case Builtin::kDatePrototypeGetUTCHours:
case Builtin::kDatePrototypeGetUTCMilliseconds:
case Builtin::kDatePrototypeGetUTCMinutes:
case Builtin::kDatePrototypeGetUTCMonth:
case Builtin::kDatePrototypeGetUTCSeconds:
case Builtin::kDatePrototypeGetYear:
case Builtin::kDatePrototypeToDateString:
case Builtin::kDatePrototypeToISOString:
case Builtin::kDatePrototypeToUTCString:
case Builtin::kDatePrototypeToString:
#ifdef V8_INTL_SUPPORT
case Builtin::kDatePrototypeToLocaleString:
case Builtin::kDatePrototypeToLocaleDateString:
case Builtin::kDatePrototypeToLocaleTimeString:
#endif
case Builtin::kDatePrototypeToTimeString:
case Builtin::kDatePrototypeToJson:
case Builtin::kDatePrototypeToPrimitive:
case Builtin::kDatePrototypeValueOf:
// DisposableStack builtins.
case Builtin::kDisposableStackConstructor:
case Builtin::kDisposableStackPrototypeGetDisposed:
// Map builtins.
case Builtin::kMapConstructor:
case Builtin::kMapGroupBy:
case Builtin::kMapPrototypeForEach:
case Builtin::kMapPrototypeGet:
case Builtin::kMapPrototypeHas:
case Builtin::kMapPrototypeEntries:
case Builtin::kMapPrototypeGetSize:
case Builtin::kMapPrototypeKeys:
case Builtin::kMapPrototypeValues:
// WeakMap builtins.
case Builtin::kWeakMapConstructor:
case Builtin::kWeakMapGet:
case Builtin::kWeakMapPrototypeHas:
// Math builtins.
case Builtin::kMathAbs:
case Builtin::kMathAcos:
case Builtin::kMathAcosh:
case Builtin::kMathAsin:
case Builtin::kMathAsinh:
case Builtin::kMathAtan:
case Builtin::kMathAtanh:
case Builtin::kMathAtan2:
case Builtin::kMathCeil:
case Builtin::kMathCbrt:
case Builtin::kMathExpm1:
case Builtin::kMathClz32:
case Builtin::kMathCos:
case Builtin::kMathCosh:
case Builtin::kMathExp:
case Builtin::kMathFloor:
case Builtin::kMathF16round:
case Builtin::kMathFround:
case Builtin::kMathHypot:
case Builtin::kMathImul:
case Builtin::kMathLog:
case Builtin::kMathLog1p:
case Builtin::kMathLog2:
case Builtin::kMathLog10:
case Builtin::kMathMax:
case Builtin::kMathMin:
case Builtin::kMathPow:
case Builtin::kMathRound:
case Builtin::kMathSign:
case Builtin::kMathSin:
case Builtin::kMathSinh:
case Builtin::kMathSqrt:
case Builtin::kMathTan:
case Builtin::kMathTanh:
case Builtin::kMathTrunc:
// Number builtins.
case Builtin::kNumberConstructor:
case Builtin::kNumberIsFinite:
case Builtin::kNumberIsInteger:
case Builtin::kNumberIsNaN:
case Builtin::kNumberIsSafeInteger:
case Builtin::kNumberParseFloat:
case Builtin::kNumberParseInt:
case Builtin::kNumberPrototypeToExponential:
case Builtin::kNumberPrototypeToFixed:
case Builtin::kNumberPrototypeToPrecision:
case Builtin::kNumberPrototypeToString:
case Builtin::kNumberPrototypeToLocaleString:
case Builtin::kNumberPrototypeValueOf:
// BigInt builtins.
case Builtin::kBigIntConstructor:
case Builtin::kBigIntAsIntN:
case Builtin::kBigIntAsUintN:
case Builtin::kBigIntPrototypeToString:
case Builtin::kBigIntPrototypeValueOf:
// Set builtins.
case Builtin::kSetConstructor:
case Builtin::kSetPrototypeEntries:
case Builtin::kSetPrototypeForEach:
case Builtin::kSetPrototypeGetSize:
case Builtin::kSetPrototypeHas:
case Builtin::kSetPrototypeValues:
// WeakSet builtins.
case Builtin::kWeakSetConstructor:
case Builtin::kWeakSetPrototypeHas:
// String builtins. Strings are immutable.
case Builtin::kStringFromCharCode:
case Builtin::kStringFromCodePoint:
case Builtin::kStringConstructor:
case Builtin::kStringListFromIterable:
case Builtin::kStringPrototypeAnchor:
case Builtin::kStringPrototypeAt:
case Builtin::kStringPrototypeBig:
case Builtin::kStringPrototypeBlink:
case Builtin::kStringPrototypeBold:
case Builtin::kStringPrototypeCharAt:
case Builtin::kStringPrototypeCharCodeAt:
case Builtin::kStringPrototypeCodePointAt:
case Builtin::kStringPrototypeConcat:
case Builtin::kStringPrototypeEndsWith:
case Builtin::kStringPrototypeFixed:
case Builtin::kStringPrototypeFontcolor:
case Builtin::kStringPrototypeFontsize:
case Builtin::kStringPrototypeIncludes:
case Builtin::kStringPrototypeIndexOf:
case Builtin::kStringPrototypeIsWellFormed:
case Builtin::kStringPrototypeItalics:
case Builtin::kStringPrototypeLastIndexOf:
case Builtin::kStringPrototypeLink:
case Builtin::kStringPrototypeMatchAll:
case Builtin::kStringPrototypePadEnd:
case Builtin::kStringPrototypePadStart:
case Builtin::kStringPrototypeRepeat:
case Builtin::kStringPrototypeSlice:
case Builtin::kStringPrototypeSmall:
case Builtin::kStringPrototypeStartsWith:
case Builtin::kStringSlowFlatten:
case Builtin::kStringPrototypeStrike:
case Builtin::kStringPrototypeSub:
case Builtin::kStringPrototypeSubstr:
case Builtin::kStringPrototypeSubstring:
case Builtin::kStringPrototypeSup:
case Builtin::kStringPrototypeToString:
#ifndef V8_INTL_SUPPORT
case Builtin::kStringPrototypeToLowerCase:
case Builtin::kStringPrototypeToUpperCase:
#endif
case Builtin::kStringPrototypeToWellFormed:
case Builtin::kStringPrototypeTrim:
case Builtin::kStringPrototypeTrimEnd:
case Builtin::kStringPrototypeTrimStart:
case Builtin::kStringPrototypeValueOf:
case Builtin::kStringToNumber:
case Builtin::kStringSubstring:
// Symbol builtins.
case Builtin::kSymbolConstructor:
case Builtin::kSymbolKeyFor:
case Builtin::kSymbolPrototypeToString:
case Builtin::kSymbolPrototypeValueOf:
case Builtin::kSymbolPrototypeToPrimitive:
// JSON builtins.
case Builtin::kJsonParse:
case Builtin::kJsonStringify:
// Global function builtins.
case Builtin::kGlobalDecodeURI:
case Builtin::kGlobalDecodeURIComponent:
case Builtin::kGlobalEncodeURI:
case Builtin::kGlobalEncodeURIComponent:
case Builtin::kGlobalEscape:
case Builtin::kGlobalUnescape:
case Builtin::kGlobalIsFinite:
case Builtin::kGlobalIsNaN:
// Function builtins.
case Builtin::kFunctionPrototypeToString:
case Builtin::kFunctionPrototypeBind:
case Builtin::kFastFunctionPrototypeBind:
case Builtin::kFunctionPrototypeCall:
case Builtin::kFunctionPrototypeApply:
// Error builtins.
case Builtin::kErrorConstructor:
// RegExp builtins.
case Builtin::kRegExpConstructor:
// Reflect builtins.
case Builtin::kReflectApply:
case Builtin::kReflectConstruct:
case Builtin::kReflectGetOwnPropertyDescriptor:
case Builtin::kReflectGetPrototypeOf:
case Builtin::kReflectHas:
case Builtin::kReflectIsExtensible:
case Builtin::kReflectOwnKeys:
// Internal.
case Builtin::kStrictPoisonPillThrower:
case Builtin::kAllocateInYoungGeneration:
case Builtin::kAllocateInOldGeneration:
case Builtin::kConstructVarargs:
case Builtin::kConstructWithArrayLike:
case Builtin::kGetOwnPropertyDescriptor:
case Builtin::kOrdinaryGetOwnPropertyDescriptor:
return DebugInfo::kHasNoSideEffect;
#ifdef V8_INTL_SUPPORT
// Intl builtins.
case Builtin::kIntlGetCanonicalLocales:
// Intl.Collator builtins.
case Builtin::kCollatorConstructor:
case Builtin::kCollatorInternalCompare:
case Builtin::kCollatorPrototypeCompare:
case Builtin::kCollatorPrototypeResolvedOptions:
case Builtin::kCollatorSupportedLocalesOf:
// Intl.DateTimeFormat builtins.
case Builtin::kDateTimeFormatConstructor:
case Builtin::kDateTimeFormatInternalFormat:
case Builtin::kDateTimeFormatPrototypeFormat:
case Builtin::kDateTimeFormatPrototypeFormatRange:
case Builtin::kDateTimeFormatPrototypeFormatRangeToParts:
case Builtin::kDateTimeFormatPrototypeFormatToParts:
case Builtin::kDateTimeFormatPrototypeResolvedOptions:
case Builtin::kDateTimeFormatSupportedLocalesOf:
// Intl.DisplayNames builtins.
case Builtin::kDisplayNamesConstructor:
case Builtin::kDisplayNamesPrototypeOf:
case Builtin::kDisplayNamesPrototypeResolvedOptions:
case Builtin::kDisplayNamesSupportedLocalesOf:
// Intl.ListFormat builtins.
case Builtin::kListFormatConstructor:
case Builtin::kListFormatPrototypeFormat:
case Builtin::kListFormatPrototypeFormatToParts:
case Builtin::kListFormatPrototypeResolvedOptions:
case Builtin::kListFormatSupportedLocalesOf:
// Intl.Locale builtins.
case Builtin::kLocaleConstructor:
case Builtin::kLocalePrototypeBaseName:
case Builtin::kLocalePrototypeCalendar:
case Builtin::kLocalePrototypeCalendars:
case Builtin::kLocalePrototypeCaseFirst:
case Builtin::kLocalePrototypeCollation:
case Builtin::kLocalePrototypeCollations:
case Builtin::kLocalePrototypeGetCalendars:
case Builtin::kLocalePrototypeGetCollations:
case Builtin::kLocalePrototypeGetHourCycles:
case Builtin::kLocalePrototypeGetNumberingSystems:
case Builtin::kLocalePrototypeGetTextInfo:
case Builtin::kLocalePrototypeGetTimeZones:
case Builtin::kLocalePrototypeGetWeekInfo:
case Builtin::kLocalePrototypeHourCycle:
case Builtin::kLocalePrototypeHourCycles:
case Builtin::kLocalePrototypeLanguage:
case Builtin::kLocalePrototypeMaximize:
case Builtin::kLocalePrototypeMinimize:
case Builtin::kLocalePrototypeNumeric:
case Builtin::kLocalePrototypeNumberingSystem:
case Builtin::kLocalePrototypeNumberingSystems:
case Builtin::kLocalePrototypeRegion:
case Builtin::kLocalePrototypeScript:
case Builtin::kLocalePrototypeTextInfo:
case Builtin::kLocalePrototypeTimeZones:
case Builtin::kLocalePrototypeToString:
case Builtin::kLocalePrototypeWeekInfo:
// Intl.NumberFormat builtins.
case Builtin::kNumberFormatConstructor:
case Builtin::kNumberFormatInternalFormatNumber:
case Builtin::kNumberFormatPrototypeFormatNumber:
case Builtin::kNumberFormatPrototypeFormatToParts:
case Builtin::kNumberFormatPrototypeResolvedOptions:
case Builtin::kNumberFormatSupportedLocalesOf:
// Intl.PluralRules builtins.
case Builtin::kPluralRulesConstructor:
case Builtin::kPluralRulesPrototypeResolvedOptions:
case Builtin::kPluralRulesPrototypeSelect:
case Builtin::kPluralRulesSupportedLocalesOf:
// Intl.RelativeTimeFormat builtins.
case Builtin::kRelativeTimeFormatConstructor:
case Builtin::kRelativeTimeFormatPrototypeFormat:
case Builtin::kRelativeTimeFormatPrototypeFormatToParts:
case Builtin::kRelativeTimeFormatPrototypeResolvedOptions:
case Builtin::kRelativeTimeFormatSupportedLocalesOf:
return DebugInfo::kHasNoSideEffect;
#endif // V8_INTL_SUPPORT
// Set builtins.
case Builtin::kSetIteratorPrototypeNext:
case Builtin::kSetPrototypeAdd:
case Builtin::kSetPrototypeClear:
case Builtin::kSetPrototypeDelete:
// Array builtins.
case Builtin::kArrayIteratorPrototypeNext:
case Builtin::kArrayPrototypeFill:
case Builtin::kArrayPrototypePop:
case Builtin::kArrayPrototypePush:
case Builtin::kArrayPrototypeReverse:
case Builtin::kArrayPrototypeShift:
case Builtin::kArrayPrototypeUnshift:
case Builtin::kArrayPrototypeSort:
case Builtin::kArrayPrototypeSplice:
case Builtin::kArrayUnshift:
// Map builtins.
case Builtin::kMapIteratorPrototypeNext:
case Builtin::kMapPrototypeClear:
case Builtin::kMapPrototypeDelete:
case Builtin::kMapPrototypeSet:
// Date builtins.
case Builtin::kDatePrototypeSetDate:
case Builtin::kDatePrototypeSetFullYear:
case Builtin::kDatePrototypeSetHours:
case Builtin::kDatePrototypeSetMilliseconds:
case Builtin::kDatePrototypeSetMinutes:
case Builtin::kDatePrototypeSetMonth:
case Builtin::kDatePrototypeSetSeconds:
case Builtin::kDatePrototypeSetTime:
case Builtin::kDatePrototypeSetUTCDate:
case Builtin::kDatePrototypeSetUTCFullYear:
case Builtin::kDatePrototypeSetUTCHours:
case Builtin::kDatePrototypeSetUTCMilliseconds:
case Builtin::kDatePrototypeSetUTCMinutes:
case Builtin::kDatePrototypeSetUTCMonth:
case Builtin::kDatePrototypeSetUTCSeconds:
case Builtin::kDatePrototypeSetYear:
// DisposableStack builtins.
case Builtin::kDisposableStackPrototypeUse:
case Builtin::kDisposableStackPrototypeDispose:
case Builtin::kDisposableStackPrototypeAdopt:
case Builtin::kDisposableStackPrototypeDefer:
case Builtin::kDisposableStackPrototypeMove:
// RegExp builtins.
case Builtin::kRegExpPrototypeTest:
case Builtin::kRegExpPrototypeExec:
case Builtin::kRegExpPrototypeSplit:
case Builtin::kRegExpPrototypeFlagsGetter:
case Builtin::kRegExpPrototypeGlobalGetter:
case Builtin::kRegExpPrototypeHasIndicesGetter:
case Builtin::kRegExpPrototypeIgnoreCaseGetter:
case Builtin::kRegExpPrototypeMatchAll:
case Builtin::kRegExpPrototypeMultilineGetter:
case Builtin::kRegExpPrototypeDotAllGetter:
case Builtin::kRegExpPrototypeUnicodeGetter:
case Builtin::kRegExpPrototypeStickyGetter:
return DebugInfo::kRequiresRuntimeChecks;
default:
if (v8_flags.trace_side_effect_free_debug_evaluate) {
PrintF("[debug-evaluate] built-in %s may cause side effect.\n",
Builtins::name(id));
}
return DebugInfo::kHasSideEffects;
}
}
bool BytecodeRequiresRuntimeCheck(interpreter::Bytecode bytecode) {
using interpreter::Bytecode;
switch (bytecode) {
case Bytecode::kSetNamedProperty:
case Bytecode::kDefineNamedOwnProperty:
case Bytecode::kSetKeyedProperty:
case Bytecode::kStaInArrayLiteral:
case Bytecode::kDefineKeyedOwnPropertyInLiteral:
case Bytecode::kStaCurrentContextSlot:
return true;
default:
return interpreter::Bytecodes::IsCallRuntime(bytecode);
}
}
} // anonymous namespace
// static
DebugInfo::SideEffectState DebugEvaluate::FunctionGetSideEffectState(
Isolate* isolate, Handle<SharedFunctionInfo> info) {
if (v8_flags.trace_side_effect_free_debug_evaluate) {
PrintF("[debug-evaluate] Checking function %s for side effect.\n",
info->DebugNameCStr().get());
}
DCHECK(info->is_compiled());
DCHECK(!info->needs_script_context());
if (info->HasBytecodeArray()) {
// Check bytecodes against allowlist.
Handle<BytecodeArray> bytecode_array(info->GetBytecodeArray(isolate),
isolate);
if (v8_flags.trace_side_effect_free_debug_evaluate) {
Print(*bytecode_array);
}
bool requires_runtime_checks = false;
for (interpreter::BytecodeArrayIterator it(bytecode_array); !it.done();
it.Advance()) {
interpreter::Bytecode bytecode = it.current_bytecode();
if (BytecodeHasNoSideEffect(bytecode)) continue;
if (BytecodeRequiresRuntimeCheck(bytecode)) {
requires_runtime_checks = true;
continue;
}
if (v8_flags.trace_side_effect_free_debug_evaluate) {
PrintF("[debug-evaluate] bytecode %s may cause side effect.\n",
interpreter::Bytecodes::ToString(bytecode));
}
// Did not match allowlist.
return DebugInfo::kHasSideEffects;
}
return requires_runtime_checks ? DebugInfo::kRequiresRuntimeChecks
: DebugInfo::kHasNoSideEffect;
} else if (info->IsApiFunction()) {
Tagged<Code> code = info->GetCode(isolate);
if (code->is_builtin()) {
return code->builtin_id() == Builtin::kHandleApiCallOrConstruct
? DebugInfo::kHasNoSideEffect
: DebugInfo::kHasSideEffects;
}
} else {
// Check built-ins against allowlist.
Builtin builtin =
info->HasBuiltinId() ? info->builtin_id() : Builtin::kNoBuiltinId;
if (!Builtins::IsBuiltinId(builtin)) return DebugInfo::kHasSideEffects;
DebugInfo::SideEffectState state = BuiltinGetSideEffectState(builtin);
return state;
}
return DebugInfo::kHasSideEffects;
}
#ifdef DEBUG
static bool TransitivelyCalledBuiltinHasNoSideEffect(Builtin caller,
Builtin callee) {
switch (callee) {
// Transitively called Builtins:
case Builtin::kAbort:
case Builtin::kAbortCSADcheck:
case Builtin::kAdaptorWithBuiltinExitFrame:
case Builtin::kArrayConstructorImpl:
case Builtin::kArrayEveryLoopContinuation:
case Builtin::kArrayFilterLoopContinuation:
case Builtin::kArrayFindIndexLoopContinuation:
case Builtin::kArrayFindLoopContinuation:
case Builtin::kArrayFindLastIndexLoopContinuation:
case Builtin::kArrayFindLastLoopContinuation:
case Builtin::kArrayForEachLoopContinuation:
case Builtin::kArrayIncludesHoleyDoubles:
case Builtin::kArrayIncludesPackedDoubles:
case Builtin::kArrayIncludesSmi:
case Builtin::kArrayIncludesSmiOrObject:
case Builtin::kArrayIndexOfHoleyDoubles:
case Builtin::kArrayIndexOfPackedDoubles:
case Builtin::kArrayIndexOfSmi:
case Builtin::kArrayIndexOfSmiOrObject:
case Builtin::kArrayMapLoopContinuation:
case Builtin::kArrayReduceLoopContinuation:
case Builtin::kArrayReduceRightLoopContinuation:
case Builtin::kArraySomeLoopContinuation:
case Builtin::kArrayTimSort:
case Builtin::kArrayTimSortIntoCopy:
case Builtin::kCall_ReceiverIsAny:
case Builtin::kCall_ReceiverIsNotNullOrUndefined:
case Builtin::kCall_ReceiverIsNullOrUndefined:
case Builtin::kCallWithArrayLike:
case Builtin::kCEntry_Return1_ArgvOnStack_NoBuiltinExit:
case Builtin::kCEntry_Return1_ArgvOnStack_BuiltinExit:
case Builtin::kCEntry_Return1_ArgvInRegister_NoBuiltinExit:
case Builtin::kCEntry_Return2_ArgvOnStack_NoBuiltinExit:
case Builtin::kCEntry_Return2_ArgvOnStack_BuiltinExit:
case Builtin::kCEntry_Return2_ArgvInRegister_NoBuiltinExit:
case Builtin::kWasmCEntry:
case Builtin::kCloneFastJSArray:
case Builtin::kConstruct:
case Builtin::kConvertToLocaleString:
case Builtin::kCreateTypedArray:
case Builtin::kDirectCEntry:
case Builtin::kDoubleToI:
case Builtin::kExtractFastJSArray:
case Builtin::kFastNewObject:
case Builtin::kFindOrderedHashMapEntry:
case Builtin::kFindOrderedHashSetEntry:
case Builtin::kFlattenIntoArrayWithMapFn:
case Builtin::kFlattenIntoArrayWithoutMapFn:
case Builtin::kGenericArrayToReversed:
case Builtin::kGenericArrayWith:
case Builtin::kGetProperty:
case Builtin::kGetPropertyWithReceiver:
case Builtin::kGroupByGeneric:
case Builtin::kHasProperty:
case Builtin::kCreateHTML:
case Builtin::kNonNumberToNumber:
case Builtin::kNonPrimitiveToPrimitive_Number:
case Builtin::kNumberToString:
case Builtin::kObjectToString:
case Builtin::kOrderedHashTableHealIndex:
case Builtin::kOrdinaryToPrimitive_Number:
case Builtin::kOrdinaryToPrimitive_String:
case Builtin::kParseInt:
case Builtin::kProxyHasProperty:
case Builtin::kProxyIsExtensible:
case Builtin::kProxyGetPrototypeOf:
case Builtin::kRecordWriteSaveFP:
case Builtin::kRecordWriteIgnoreFP:
case Builtin::kStringAdd_CheckNone:
case Builtin::kStringEqual:
case Builtin::kStringIndexOf:
case Builtin::kStringRepeat:
case Builtin::kBigIntEqual:
case Builtin::kToInteger:
case Builtin::kToLength:
case Builtin::kToName:
case Builtin::kToObject:
case Builtin::kToString:
case Builtin::kTypedArrayMergeSort:
#ifdef V8_IS_TSAN
case Builtin::kTSANRelaxedStore8IgnoreFP:
case Builtin::kTSANRelaxedStore8SaveFP:
case Builtin::kTSANRelaxedStore16IgnoreFP:
case Builtin::kTSANRelaxedStore16SaveFP:
case Builtin::kTSANRelaxedStore32IgnoreFP:
case Builtin::kTSANRelaxedStore32SaveFP:
case Builtin::kTSANRelaxedStore64IgnoreFP:
case Builtin::kTSANRelaxedStore64SaveFP:
case Builtin::kTSANSeqCstStore8IgnoreFP:
case Builtin::kTSANSeqCstStore8SaveFP:
case Builtin::kTSANSeqCstStore16IgnoreFP:
case Builtin::kTSANSeqCstStore16SaveFP:
case Builtin::kTSANSeqCstStore32IgnoreFP:
case Builtin::kTSANSeqCstStore32SaveFP:
case Builtin::kTSANSeqCstStore64IgnoreFP:
case Builtin::kTSANSeqCstStore64SaveFP:
case Builtin::kTSANRelaxedLoad32IgnoreFP:
case Builtin::kTSANRelaxedLoad32SaveFP:
case Builtin::kTSANRelaxedLoad64IgnoreFP:
case Builtin::kTSANRelaxedLoad64SaveFP:
#endif // V8_IS_TSAN
case Builtin::kWeakMapLookupHashIndex:
return true;
case Builtin::kJoinStackPop:
case Builtin::kJoinStackPush:
switch (caller) {
case Builtin::kArrayPrototypeJoin:
case Builtin::kArrayPrototypeToLocaleString:
case Builtin::kTypedArrayPrototypeJoin:
case Builtin::kTypedArrayPrototypeToLocaleString:
return true;
default:
return false;
}
case Builtin::kFastCreateDataProperty:
switch (caller) {
case Builtin::kArrayPrototypeSlice:
case Builtin::kArrayPrototypeToSpliced:
case Builtin::kArrayPrototypeWith:
case Builtin::kArrayFilter:
return true;
default:
return false;
}
case Builtin::kSetProperty:
switch (caller) {
case Builtin::kArrayPrototypeSlice:
case Builtin::kArrayPrototypeToSorted:
case Builtin::kTypedArrayPrototypeMap:
case Builtin::kStringPrototypeMatchAll:
return true;
default:
return false;
}
default:
return false;
}
}
// static
void DebugEvaluate::VerifyTransitiveBuiltins(Isolate* isolate) {
// TODO(yangguo): also check runtime calls.
bool failed = false;
bool sanity_check = false;
for (Builtin caller = Builtins::kFirst; caller <= Builtins::kLast; ++caller) {
DebugInfo::SideEffectState state = BuiltinGetSideEffectState(caller);
if (state != DebugInfo::kHasNoSideEffect) continue;
Tagged<Code> code = isolate->builtins()->code(caller);
int mode = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) |
RelocInfo::ModeMask(RelocInfo::RELATIVE_CODE_TARGET);
for (RelocIterator it(code, mode); !it.done(); it.next()) {
RelocInfo* rinfo = it.rinfo();
DCHECK(RelocInfo::IsCodeTargetMode(rinfo->rmode()));
Tagged<Code> lookup_result =
isolate->heap()->FindCodeForInnerPointer(rinfo->target_address());
Builtin callee = lookup_result->builtin_id();
if (BuiltinGetSideEffectState(callee) == DebugInfo::kHasNoSideEffect) {
continue;
}
if (TransitivelyCalledBuiltinHasNoSideEffect(caller, callee)) {
sanity_check = true;
continue;
}
PrintF("Allowlisted builtin %s calls non-allowlisted builtin %s\n",
Builtins::name(caller), Builtins::name(callee));
failed = true;
}
}
CHECK(!failed);
#if defined(V8_TARGET_ARCH_PPC) || defined(V8_TARGET_ARCH_PPC64) || \
defined(V8_TARGET_ARCH_MIPS64) || defined(V8_TARGET_ARCH_RISCV32) || \
defined(V8_TARGET_ARCH_RISCV64)
// Isolate-independent builtin calls and jumps do not emit reloc infos
// on PPC. We try to avoid using PC relative code due to performance
// issue with especially older hardwares.
// MIPS64 doesn't have PC relative code currently.
// TODO(mips): Add PC relative code to MIPS64.
USE(sanity_check);
#else
CHECK(sanity_check);
#endif
}
#endif // DEBUG
// 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)) it.ApplyDebugBreak();
}
}
} // namespace internal
} // namespace v8