#include "src/runtime/runtime.h"
#include <stdio.h>
#include <iomanip>
#include <memory>
#include "include/v8-function.h"
#include "include/v8-profiler.h"
#include "src/api/api-inl.h"
#include "src/base/macros.h"
#include "src/base/numbers/double.h"
#include "src/codegen/compiler.h"
#include "src/codegen/pending-optimization-table.h"
#include "src/common/globals.h"
#include "src/compiler-dispatcher/lazy-compile-dispatcher.h"
#include "src/compiler-dispatcher/optimizing-compile-dispatcher.h"
#include "src/debug/debug-evaluate.h"
#include "src/deoptimizer/deoptimizer.h"
#include "src/execution/arguments-inl.h"
#include "src/execution/frames-inl.h"
#include "src/execution/frames.h"
#include "src/execution/isolate-inl.h"
#include "src/execution/protectors-inl.h"
#include "src/execution/tiering-manager.h"
#include "src/flags/flags.h"
#include "src/heap/heap-layout-inl.h"
#include "src/heap/heap-write-barrier-inl.h"
#include "src/heap/pretenuring-handler-inl.h"
#include "src/ic/stub-cache.h"
#include "src/objects/bytecode-array.h"
#include "src/objects/js-collection-inl.h"
#include "src/profiler/heap-profiler.h"
#include "src/utils/utils.h"
#ifdef V8_ENABLE_MAGLEV
#include "src/maglev/maglev-concurrent-dispatcher.h"
#endif
#include "src/objects/js-array-buffer-inl.h"
#include "src/objects/js-atomics-synchronization-inl.h"
#include "src/objects/js-function-inl.h"
#include "src/objects/js-regexp-inl.h"
#include "src/objects/smi.h"
#include "src/profiler/heap-snapshot-generator.h"
#include "src/regexp/regexp.h"
#include "src/snapshot/snapshot.h"
#ifdef V8_ENABLE_MAGLEV
#include "src/maglev/maglev.h"
#endif
#if V8_ENABLE_WEBASSEMBLY
#include "src/wasm/wasm-engine.h"
#endif
namespace v8 {
namespace internal {
namespace {
#define CHECK_UNLESS_FUZZING(condition) \
do { \
if (V8_UNLIKELY(!(condition))) { \
if (v8_flags.fuzzing) { \
return GetReadOnlyRoots().undefined_value(); \
} else { \
CHECK_FAILED_HANDLER(#condition); \
} \
} \
} while (false)
#define CHECK_UNLESS_FUZZING_RETURN_FALSE(condition) \
do { \
if (V8_UNLIKELY(!(condition))) { \
if (v8_flags.fuzzing) { \
return false; \
} else { \
CHECK_FAILED_HANDLER(#condition); \
} \
} \
} while (false)
V8_WARN_UNUSED_RESULT bool CheckMarkedForManualOptimization(
Isolate* isolate, Tagged<JSFunction> function) {
if (!ManualOptimizationTable::IsMarkedForManualOptimization(isolate,
function)) {
PrintF("Error: Function ");
ShortPrint(function);
PrintF(
" should be prepared for optimization with "
"%%PrepareFunctionForOptimization before "
"%%OptimizeFunctionOnNextCall / %%OptimizeMaglevOnNextCall / "
"%%OptimizeOsr ");
return false;
}
return true;
}
V8_WARN_UNUSED_RESULT Tagged<Object> ReturnFuzzSafe(Tagged<Object> value,
Isolate* isolate) {
return v8_flags.correctness_fuzzer_suppressions
? ReadOnlyRoots(isolate).undefined_value()
: value;
}
#define CONVERT_INT32_ARG_FUZZ_SAFE(name, index) \
CHECK_UNLESS_FUZZING(IsNumber(args[index])); \
int32_t name = 0; \
CHECK_UNLESS_FUZZING(Object::ToInt32(args[index], &name));
#define CONVERT_BOOLEAN_ARG_FUZZ_SAFE(name, index) \
CHECK_UNLESS_FUZZING(IsBoolean(args[index])); \
bool name = IsTrue(args[index], isolate);
bool IsAsmWasmFunction(Isolate* isolate, Tagged<JSFunction> function) {
DisallowGarbageCollection no_gc;
#if V8_ENABLE_WEBASSEMBLY
return function->shared()->HasAsmWasmData() ||
function->code(isolate)->builtin_id() == Builtin::kInstantiateAsmJs;
#else
return false;
#endif
}
}
RUNTIME_FUNCTION(Runtime_ClearMegamorphicStubCache) {
HandleScope scope(isolate);
isolate->load_stub_cache()->Clear();
isolate->store_stub_cache()->Clear();
isolate->define_own_stub_cache()->Clear();
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_ConstructDouble) {
HandleScope scope(isolate);
DCHECK_EQ(args.length(), 2);
uint32_t hi = NumberToUint32(args[0]);
uint32_t lo = NumberToUint32(args[1]);
uint64_t result = (static_cast<uint64_t>(hi) << 32) | lo;
return *isolate->factory()->NewNumber(base::uint64_to_double(result));
}
RUNTIME_FUNCTION(Runtime_StringIsFlat) {
HandleScope scope(isolate);
DCHECK_EQ(args.length(), 1);
DirectHandle<String> s = args.at<String>(0);
return isolate->heap()->ToBoolean(s->IsFlat());
}
RUNTIME_FUNCTION(Runtime_ConstructConsString) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 2);
CHECK_UNLESS_FUZZING(IsString(args[0]));
CHECK_UNLESS_FUZZING(IsString(args[1]));
DirectHandle<String> left = args.at<String>(0);
DirectHandle<String> right = args.at<String>(1);
CHECK_UNLESS_FUZZING(left->length() + right->length() >=
ConsString::kMinLength);
CHECK_UNLESS_FUZZING(left->length() + right->length() <= String::kMaxLength);
return *isolate->factory()->NewConsString(left, right).ToHandleChecked();
}
RUNTIME_FUNCTION(Runtime_ConstructSlicedString) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 2);
CHECK_UNLESS_FUZZING(IsString(args[0]));
CHECK_UNLESS_FUZZING(IsSmi(args[1]));
Handle<String> string = args.at<String>(0);
uint32_t index = args.smi_value_at(1);
CHECK_UNLESS_FUZZING(index < string->length());
DirectHandle<String> sliced_string =
isolate->factory()->NewSubString(string, index, string->length());
CHECK_UNLESS_FUZZING(IsSlicedString(*sliced_string));
return *sliced_string;
}
RUNTIME_FUNCTION(Runtime_ConstructInternalizedString) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
CHECK_UNLESS_FUZZING(IsString(args[0]));
Handle<String> string = args.at<String>(0);
DirectHandle<String> internalized =
isolate->factory()->InternalizeString(string);
CHECK(IsInternalizedString(*string) ||
(IsThinString(*string) &&
IsInternalizedString(Cast<ThinString>(*string)->actual())) ||
HeapLayout::InAnySharedSpace(*string));
CHECK(IsInternalizedString(*internalized));
return *internalized;
}
RUNTIME_FUNCTION(Runtime_ConstructThinString) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
CHECK_UNLESS_FUZZING(IsString(args[0]));
Handle<String> string = args.at<String>(0);
if (IsThinString(*string)) {
return *string;
}
if (!IsConsString(*string)) {
CHECK_UNLESS_FUZZING(string->length() >= ConsString::kMinLength);
string = isolate->factory()->NewConsString(
isolate->factory()->empty_string(), string, string->length(),
string->IsOneByteRepresentation(),
AllocationType::kOld);
}
CHECK(IsConsString(*string));
DirectHandle<String> internalized =
isolate->factory()->InternalizeString(string);
CHECK_NE(*internalized, *string);
CHECK(IsThinString(*string));
return *string;
}
RUNTIME_FUNCTION(Runtime_DeoptimizeFunction) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
Handle<Object> function_object = args.at(0);
CHECK_UNLESS_FUZZING(IsJSFunction(*function_object));
auto function = Cast<JSFunction>(function_object);
if (function->IsTieringRequestedOrInProgress()) {
if (function->tiering_in_progress()) {
isolate->AbortConcurrentOptimization(BlockingBehavior::kBlock);
}
function->ResetTieringRequests();
}
if (function->HasAttachedOptimizedCode(isolate)) {
Deoptimizer::DeoptimizeFunction(*function, LazyDeoptimizeReason::kTesting);
}
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_DeoptimizeNow) {
HandleScope scope(isolate);
DirectHandle<JSFunction> function;
JavaScriptStackFrameIterator it(isolate);
if (!it.done()) function = direct_handle(it.frame()->function(), isolate);
CHECK_UNLESS_FUZZING(!function.is_null());
if (function->HasAttachedOptimizedCode(isolate)) {
Deoptimizer::DeoptimizeFunction(*function, LazyDeoptimizeReason::kTesting);
}
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_LeakHole) {
HandleScope scope(isolate);
return ReadOnlyRoots(isolate).the_hole_value();
}
RUNTIME_FUNCTION(Runtime_RunningInSimulator) {
SealHandleScope shs(isolate);
#if defined(USE_SIMULATOR)
return ReadOnlyRoots(isolate).true_value();
#else
return ReadOnlyRoots(isolate).false_value();
#endif
}
RUNTIME_FUNCTION(Runtime_RuntimeEvaluateREPL) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
CHECK_UNLESS_FUZZING(IsString(args[0]));
Handle<String> source = args.at<String>(0);
DirectHandle<Object> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result,
DebugEvaluate::Global(isolate, source,
debug::EvaluateGlobalMode::kDefault,
REPLMode::kYes));
return *result;
}
RUNTIME_FUNCTION(Runtime_ICsAreEnabled) {
SealHandleScope shs(isolate);
return isolate->heap()->ToBoolean(v8_flags.use_ic);
}
RUNTIME_FUNCTION(Runtime_IsConcurrentRecompilationSupported) {
SealHandleScope shs(isolate);
return isolate->heap()->ToBoolean(
isolate->concurrent_recompilation_enabled());
}
RUNTIME_FUNCTION(Runtime_IsAtomicsWaitAllowed) {
SealHandleScope shs(isolate);
return isolate->heap()->ToBoolean(isolate->allow_atomics_wait());
}
namespace {
bool CanOptimizeFunction(CodeKind target_kind,
DirectHandle<JSFunction> function, Isolate* isolate,
IsCompiledScope* is_compiled_scope) {
CHECK_UNLESS_FUZZING_RETURN_FALSE(is_compiled_scope->is_compiled() ||
Compiler::Compile(isolate, function,
Compiler::CLEAR_EXCEPTION,
is_compiled_scope));
if (target_kind == CodeKind::TURBOFAN_JS && !v8_flags.turbofan) return false;
if (target_kind == CodeKind::MAGLEV && !maglev::IsMaglevEnabled()) {
return false;
}
if (function->shared()->optimization_disabled(target_kind)) {
return false;
}
CHECK_UNLESS_FUZZING_RETURN_FALSE(!IsAsmWasmFunction(isolate, *function));
if (!v8_flags.fuzzing) {
CHECK(CheckMarkedForManualOptimization(isolate, *function));
}
CHECK_UNLESS_FUZZING_RETURN_FALSE(
!function->is_compiled(isolate) ||
function->HasAvailableCodeKind(isolate, CodeKind::INTERPRETED_FUNCTION));
if (function->HasAvailableCodeKind(isolate, target_kind) ||
function->HasAvailableHigherTierCodeThan(isolate, target_kind)) {
DCHECK(function->HasAttachedOptimizedCode(isolate) ||
function->ChecksTieringState(isolate));
return false;
}
return true;
}
Tagged<Object> OptimizeFunctionOnNextCall(RuntimeArguments& args,
Isolate* isolate,
CodeKind target_kind) {
CHECK_UNLESS_FUZZING(args.length() == 1 || args.length() == 2);
DirectHandle<Object> function_object = args.at(0);
CHECK_UNLESS_FUZZING(IsJSFunction(*function_object));
DirectHandle<JSFunction> function = Cast<JSFunction>(function_object);
IsCompiledScope is_compiled_scope(
function->shared()->is_compiled_scope(isolate));
if (!CanOptimizeFunction(target_kind, function, isolate,
&is_compiled_scope)) {
return ReadOnlyRoots(isolate).undefined_value();
}
ConcurrencyMode concurrency_mode = ConcurrencyMode::kSynchronous;
if (args.length() == 2) {
DirectHandle<Object> type = args.at(1);
CHECK_UNLESS_FUZZING(IsString(*type));
if (Cast<String>(type)->IsOneByteEqualTo(
base::StaticCharVector("concurrent")) &&
isolate->concurrent_recompilation_enabled()) {
concurrency_mode = ConcurrencyMode::kConcurrent;
}
}
if (!function->is_compiled(isolate)) {
DCHECK(function->shared()->HasBytecodeArray());
Tagged<Code> code = *BUILTIN_CODE(isolate, InterpreterEntryTrampoline);
if (function->shared()->HasBaselineCode()) {
code = function->shared()->baseline_code(kAcquireLoad);
}
function->UpdateCode(isolate, code);
}
TraceManualRecompile(*function, target_kind, concurrency_mode);
JSFunction::EnsureFeedbackVector(isolate, function, &is_compiled_scope);
if (function->GetActiveTier(isolate) != target_kind) {
function->RequestOptimization(isolate, target_kind, concurrency_mode);
}
return ReadOnlyRoots(isolate).undefined_value();
}
bool EnsureCompiledAndFeedbackVector(Isolate* isolate,
DirectHandle<JSFunction> function,
IsCompiledScope* is_compiled_scope) {
*is_compiled_scope = function->shared()->is_compiled_scope(isolate);
if (!is_compiled_scope->is_compiled()) {
DCHECK(function->shared()->allows_lazy_compilation());
if (!Compiler::Compile(isolate, function, Compiler::CLEAR_EXCEPTION,
is_compiled_scope)) {
return false;
}
}
if (!function->shared()->HasFeedbackMetadata()) {
return false;
}
JSFunction::EnsureFeedbackVector(isolate, function, is_compiled_scope);
return true;
}
}
RUNTIME_FUNCTION(Runtime_CompileBaseline) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
DirectHandle<Object> function_object = args.at(0);
CHECK_UNLESS_FUZZING(IsJSFunction(*function_object));
DirectHandle<JSFunction> function = Cast<JSFunction>(function_object);
IsCompiledScope is_compiled_scope =
function->shared(isolate)->is_compiled_scope(isolate);
CHECK_UNLESS_FUZZING(function->shared(isolate)->IsUserJavaScript());
CHECK_UNLESS_FUZZING(is_compiled_scope.is_compiled() ||
Compiler::Compile(isolate, function,
Compiler::CLEAR_EXCEPTION,
&is_compiled_scope));
CHECK_UNLESS_FUZZING(Compiler::CompileBaseline(
isolate, function, Compiler::CLEAR_EXCEPTION, &is_compiled_scope));
return ReadOnlyRoots(isolate).undefined_value();
}
#ifdef V8_ENABLE_MAGLEV
RUNTIME_FUNCTION(Runtime_BenchMaglev) {
HandleScope scope(isolate);
DCHECK_EQ(args.length(), 2);
Handle<JSFunction> function = args.at<JSFunction>(0);
int count = args.smi_value_at(1);
DirectHandle<Code> code;
base::ElapsedTimer timer;
timer.Start();
code = Maglev::Compile(isolate, function, BytecodeOffset::None())
.ToHandleChecked();
for (int i = 1; i < count; ++i) {
HandleScope handle_scope(isolate);
Maglev::Compile(isolate, function, BytecodeOffset::None());
}
PrintF("Maglev compile time: %g ms!\n",
timer.Elapsed().InMillisecondsF() / count);
function->UpdateOptimizedCode(isolate, *code);
return ReadOnlyRoots(isolate).undefined_value();
}
#else
RUNTIME_FUNCTION(Runtime_BenchMaglev) {
PrintF("Maglev is not enabled.\n");
return ReadOnlyRoots(isolate).undefined_value();
}
#endif
RUNTIME_FUNCTION(Runtime_BenchTurbofan) {
HandleScope scope(isolate);
DCHECK_EQ(args.length(), 2);
DirectHandle<JSFunction> function = args.at<JSFunction>(0);
int count = args.smi_value_at(1);
base::ElapsedTimer timer;
timer.Start();
Compiler::CompileOptimized(isolate, function, ConcurrencyMode::kSynchronous,
CodeKind::TURBOFAN_JS);
for (int i = 1; i < count; ++i) {
Compiler::CompileOptimized(isolate, function, ConcurrencyMode::kSynchronous,
CodeKind::TURBOFAN_JS);
}
double compile_time = timer.Elapsed().InMillisecondsF() / count;
return *isolate->factory()->NewNumber(compile_time);
}
RUNTIME_FUNCTION(Runtime_ActiveTierIsIgnition) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
CHECK_UNLESS_FUZZING(IsJSFunction(args[0]));
DirectHandle<JSFunction> function = args.at<JSFunction>(0);
return isolate->heap()->ToBoolean(function->ActiveTierIsIgnition(isolate));
}
RUNTIME_FUNCTION(Runtime_ActiveTierIsSparkplug) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
CHECK_UNLESS_FUZZING(IsJSFunction(args[0]));
DirectHandle<JSFunction> function = args.at<JSFunction>(0);
return isolate->heap()->ToBoolean(function->ActiveTierIsBaseline(isolate));
}
RUNTIME_FUNCTION(Runtime_ActiveTierIsMaglev) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
CHECK_UNLESS_FUZZING(IsJSFunction(args[0]));
DirectHandle<JSFunction> function = args.at<JSFunction>(0);
return isolate->heap()->ToBoolean(function->ActiveTierIsMaglev(isolate));
}
RUNTIME_FUNCTION(Runtime_ActiveTierIsTurbofan) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
CHECK_UNLESS_FUZZING(IsJSFunction(args[0]));
DirectHandle<JSFunction> function = args.at<JSFunction>(0);
return isolate->heap()->ToBoolean(function->ActiveTierIsTurbofan(isolate));
}
RUNTIME_FUNCTION(Runtime_IsSparkplugEnabled) {
return isolate->heap()->ToBoolean(v8_flags.sparkplug);
}
RUNTIME_FUNCTION(Runtime_IsMaglevEnabled) {
return isolate->heap()->ToBoolean(maglev::IsMaglevEnabled());
}
RUNTIME_FUNCTION(Runtime_IsTurbofanEnabled) {
return isolate->heap()->ToBoolean(v8_flags.turbofan);
}
RUNTIME_FUNCTION(Runtime_CurrentFrameIsTurbofan) {
HandleScope scope(isolate);
JavaScriptStackFrameIterator it(isolate);
return isolate->heap()->ToBoolean(it.frame()->is_turbofan());
}
#ifdef V8_ENABLE_MAGLEV
RUNTIME_FUNCTION(Runtime_OptimizeMaglevOnNextCall) {
HandleScope scope(isolate);
return OptimizeFunctionOnNextCall(
args, isolate,
v8_flags.optimize_maglev_optimizes_to_turbofan ? CodeKind::TURBOFAN_JS
: CodeKind::MAGLEV);
}
#else
RUNTIME_FUNCTION(Runtime_OptimizeMaglevOnNextCall) {
if (!v8_flags.fuzzing) PrintF("Maglev is not enabled.\n");
return ReadOnlyRoots(isolate).undefined_value();
}
#endif
RUNTIME_FUNCTION(Runtime_OptimizeFunctionOnNextCall) {
HandleScope scope(isolate);
return OptimizeFunctionOnNextCall(
args, isolate,
v8_flags.optimize_on_next_call_optimizes_to_maglev
? CodeKind::MAGLEV
: CodeKind::TURBOFAN_JS);
}
RUNTIME_FUNCTION(Runtime_EnsureFeedbackVectorForFunction) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
CHECK_UNLESS_FUZZING(IsJSFunction(args[0]));
DirectHandle<JSFunction> function = args.at<JSFunction>(0);
if (function->has_feedback_vector()) {
return ReadOnlyRoots(isolate).undefined_value();
}
IsCompiledScope is_compiled_scope;
EnsureCompiledAndFeedbackVector(isolate, function, &is_compiled_scope);
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_PrepareFunctionForOptimization) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1 || args.length() == 2);
CHECK_UNLESS_FUZZING(IsJSFunction(args[0]));
DirectHandle<JSFunction> function = args.at<JSFunction>(0);
IsCompiledScope is_compiled_scope;
CHECK_UNLESS_FUZZING(
EnsureCompiledAndFeedbackVector(isolate, function, &is_compiled_scope));
if (function->shared()->all_optimization_disabled()) {
return ReadOnlyRoots(isolate).undefined_value();
}
CHECK_UNLESS_FUZZING(!IsAsmWasmFunction(isolate, *function));
ManualOptimizationTable::MarkFunctionForManualOptimization(
isolate, function, &is_compiled_scope);
return ReadOnlyRoots(isolate).undefined_value();
}
namespace {
void FinalizeOptimization(Isolate* isolate) {
DCHECK(isolate->concurrent_recompilation_enabled());
isolate->optimizing_compile_dispatcher()->WaitUntilCompilationJobsDone();
isolate->optimizing_compile_dispatcher()->InstallOptimizedFunctions();
isolate->optimizing_compile_dispatcher()->set_finalize(true);
#if V8_ENABLE_MAGLEV
if (isolate->maglev_concurrent_dispatcher()->is_enabled()) {
isolate->maglev_concurrent_dispatcher()->AwaitCompileJobs();
isolate->maglev_concurrent_dispatcher()->FinalizeFinishedJobs();
}
#endif
}
BytecodeOffset OffsetOfNextJumpLoop(Isolate* isolate,
Handle<BytecodeArray> bytecode_array,
int current_offset) {
interpreter::BytecodeArrayIterator it(bytecode_array, current_offset);
for (; !it.done(); it.Advance()) {
if (it.current_bytecode() != interpreter::Bytecode::kJumpLoop) {
continue;
}
if (!base::IsInRange(current_offset, it.GetJumpTargetOffset(),
it.current_offset())) {
continue;
}
return BytecodeOffset(it.current_offset());
}
it.SetOffset(current_offset);
for (; !it.done(); it.Advance()) {
if (it.current_bytecode() == interpreter::Bytecode::kJumpLoop) {
return BytecodeOffset(it.current_offset());
}
}
return BytecodeOffset::None();
}
}
RUNTIME_FUNCTION(Runtime_OptimizeOsr) {
HandleScope handle_scope(isolate);
DirectHandle<JSFunction> function;
int stack_depth = 0;
if (args.length() == 1) {
CHECK_UNLESS_FUZZING(IsSmi(args[0]));
stack_depth = args.smi_value_at(0);
}
JavaScriptStackFrameIterator it(isolate);
while (!it.done() && stack_depth--) it.Advance();
if (!it.done()) {
if (it.frame()->is_turbofan()) {
if (v8_flags.trace_osr) {
CodeTracer::Scope scope(isolate->GetCodeTracer());
PrintF(scope.file(),
"[OSR - %%OptimizeOsr failed because the current function could "
"not be found.]\n");
}
return ReadOnlyRoots(isolate).undefined_value();
} else if (it.frame()->is_maglev()) {
function = MaglevFrame::cast(it.frame())->GetInnermostFunction();
} else {
function = direct_handle(it.frame()->function(), isolate);
}
}
CHECK_UNLESS_FUZZING(!function.is_null());
if (V8_UNLIKELY((!v8_flags.turbofan && !maglev::IsMaglevEnabled()) ||
(!v8_flags.use_osr && !maglev::IsMaglevOsrEnabled()))) {
return ReadOnlyRoots(isolate).undefined_value();
}
CHECK_UNLESS_FUZZING(function->shared()->allows_lazy_compilation());
CHECK_UNLESS_FUZZING(!function->shared()->all_optimization_disabled());
if (!v8_flags.fuzzing) {
CHECK(CheckMarkedForManualOptimization(isolate, *function));
}
if (function->HasAvailableOptimizedCode(isolate) &&
(!function->code(isolate)->is_maglevved() || !v8_flags.osr_from_maglev)) {
DCHECK(function->HasAttachedOptimizedCode(isolate) ||
function->ChecksTieringState(isolate));
return ReadOnlyRoots(isolate).undefined_value();
}
if (!it.frame()->is_unoptimized() &&
(!it.frame()->is_maglev() || !v8_flags.osr_from_maglev)) {
return ReadOnlyRoots(isolate).undefined_value();
}
IsCompiledScope is_compiled_scope(
function->shared()->is_compiled_scope(isolate));
JSFunction::EnsureFeedbackVector(isolate, function, &is_compiled_scope);
isolate->tiering_manager()->RequestOsrAtNextOpportunity(*function);
bool concurrent_osr =
isolate->concurrent_recompilation_enabled() && v8_flags.concurrent_osr;
bool is_maglev = false;
if (it.frame()->is_maglev() || concurrent_osr) {
BytecodeOffset osr_offset = BytecodeOffset::None();
if (it.frame()->is_unoptimized()) {
UnoptimizedJSFrame* frame = UnoptimizedJSFrame::cast(it.frame());
Handle<BytecodeArray> bytecode_array(frame->GetBytecodeArray(), isolate);
const int current_offset = frame->GetBytecodeOffset();
osr_offset =
OffsetOfNextJumpLoop(isolate, bytecode_array, current_offset);
} else {
MaglevFrame* frame = MaglevFrame::cast(it.frame());
Handle<BytecodeArray> bytecode_array(
function->shared()->GetBytecodeArray(isolate), isolate);
const BytecodeOffset current_offset = frame->GetBytecodeOffsetForOSR();
osr_offset = OffsetOfNextJumpLoop(
isolate, bytecode_array,
current_offset.IsNone() ? 0 : current_offset.ToInt());
is_maglev = true;
}
if (osr_offset.IsNone()) {
return ReadOnlyRoots(isolate).undefined_value();
}
if (concurrent_osr) {
FinalizeOptimization(isolate);
}
auto unused_result = Compiler::CompileOptimizedOSR(
isolate, function, osr_offset,
concurrent_osr ? ConcurrencyMode::kConcurrent
: ConcurrencyMode::kSynchronous,
(maglev::IsMaglevOsrEnabled() && !it.frame()->is_maglev())
? CodeKind::MAGLEV
: CodeKind::TURBOFAN_JS);
USE(unused_result);
if (concurrent_osr) {
FinalizeOptimization(isolate);
}
if (is_maglev) {
function->feedback_vector()->set_osr_urgency(
FeedbackVector::kMaxOsrUrgency);
}
}
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_BaselineOsr) {
HandleScope scope(isolate);
JavaScriptStackFrameIterator it(isolate);
DirectHandle<JSFunction> function(it.frame()->function(), isolate);
CHECK_UNLESS_FUZZING(!function.is_null());
if (!v8_flags.sparkplug || !v8_flags.use_osr) {
return ReadOnlyRoots(isolate).undefined_value();
}
if (!it.frame()->is_unoptimized()) {
return ReadOnlyRoots(isolate).undefined_value();
}
IsCompiledScope is_compiled_scope(
function->shared()->is_compiled_scope(isolate));
Compiler::CompileBaseline(isolate, function, Compiler::CLEAR_EXCEPTION,
&is_compiled_scope);
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_NeverOptimizeFunction) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
Handle<Object> function_object = args.at(0);
PtrComprCageBase cage_base(isolate);
CHECK_UNLESS_FUZZING(IsJSFunction(*function_object, cage_base));
auto function = Cast<JSFunction>(function_object);
DirectHandle<SharedFunctionInfo> sfi(function->shared(cage_base), isolate);
CodeKind code_kind = sfi->abstract_code(isolate)->kind(cage_base);
switch (code_kind) {
case CodeKind::INTERPRETED_FUNCTION:
break;
case CodeKind::BUILTIN:
CHECK_UNLESS_FUZZING(!HeapLayout::InReadOnlySpace(*sfi));
break;
default:
CHECK_UNLESS_FUZZING(false);
break;
}
if (isolate->lazy_compile_dispatcher() &&
isolate->lazy_compile_dispatcher()->IsEnqueued(sfi)) {
isolate->lazy_compile_dispatcher()->FinishNow(sfi);
}
sfi->DisableOptimization(isolate, BailoutReason::kNeverOptimize);
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_GetOptimizationStatus) {
HandleScope scope(isolate);
DCHECK_EQ(args.length(), 1);
int status = 0;
if (v8_flags.lite_mode || v8_flags.jitless || !V8_ENABLE_TURBOFAN_BOOL) {
status |= static_cast<int>(OptimizationStatus::kLiteMode);
}
if (!isolate->use_optimizer()) {
status |= static_cast<int>(OptimizationStatus::kNeverOptimize);
}
if (v8_flags.deopt_every_n_times) {
status |= static_cast<int>(OptimizationStatus::kMaybeDeopted);
}
if (v8_flags.optimize_on_next_call_optimizes_to_maglev) {
status |= static_cast<int>(
OptimizationStatus::kOptimizeOnNextCallOptimizesToMaglev);
}
if (v8_flags.optimize_maglev_optimizes_to_turbofan) {
status |= static_cast<int>(
OptimizationStatus::kOptimizeMaglevOptimizesToTurbofan);
}
Handle<Object> function_object = args.at(0);
if (IsUndefined(*function_object)) return Smi::FromInt(status);
CHECK_UNLESS_FUZZING(IsJSFunction(*function_object));
auto function = Cast<JSFunction>(function_object);
status |= static_cast<int>(OptimizationStatus::kIsFunction);
if (function->has_feedback_vector()) {
if (function->tiering_in_progress()) {
status |= static_cast<int>(OptimizationStatus::kOptimizingConcurrently);
} else if (function->GetRequestedOptimizationIfAny(
isolate, ConcurrencyMode::kConcurrent) == CodeKind::MAGLEV) {
status |= static_cast<int>(
OptimizationStatus::kMarkedForConcurrentMaglevOptimization);
} else if (function->GetRequestedOptimizationIfAny(
isolate, ConcurrencyMode::kSynchronous) ==
CodeKind::MAGLEV) {
status |=
static_cast<int>(OptimizationStatus::kMarkedForMaglevOptimization);
} else if (function->GetRequestedOptimizationIfAny(
isolate, ConcurrencyMode::kConcurrent) ==
CodeKind::TURBOFAN_JS) {
status |= static_cast<int>(
OptimizationStatus::kMarkedForConcurrentOptimization);
} else if (function->GetRequestedOptimizationIfAny(
isolate, ConcurrencyMode::kSynchronous) ==
CodeKind::TURBOFAN_JS) {
status |= static_cast<int>(OptimizationStatus::kMarkedForOptimization);
}
}
if (function->HasAttachedOptimizedCode(isolate)) {
Tagged<Code> code = function->code(isolate);
if (code->marked_for_deoptimization()) {
status |= static_cast<int>(OptimizationStatus::kMarkedForDeoptimization);
} else {
status |= static_cast<int>(OptimizationStatus::kOptimized);
}
if (code->is_maglevved()) {
status |= static_cast<int>(OptimizationStatus::kMaglevved);
} else if (code->is_turbofanned()) {
status |= static_cast<int>(OptimizationStatus::kTurboFanned);
}
}
if (function->HasAttachedCodeKind(isolate, CodeKind::BASELINE)) {
status |= static_cast<int>(OptimizationStatus::kBaseline);
}
if (function->ActiveTierIsIgnition(isolate)) {
status |= static_cast<int>(OptimizationStatus::kInterpreted);
}
if (!function->is_compiled(isolate)) {
status |= static_cast<int>(OptimizationStatus::kIsLazy);
}
JavaScriptFrame* frame = nullptr;
JavaScriptStackFrameIterator it(isolate);
while (!it.done()) {
if (it.frame()->function() == *function) {
frame = it.frame();
break;
}
it.Advance();
}
if (frame != nullptr) {
status |= static_cast<int>(OptimizationStatus::kIsExecuting);
if (frame->is_turbofan()) {
status |=
static_cast<int>(OptimizationStatus::kTopmostFrameIsTurboFanned);
} else if (frame->is_interpreted()) {
status |=
static_cast<int>(OptimizationStatus::kTopmostFrameIsInterpreted);
} else if (frame->is_baseline()) {
status |= static_cast<int>(OptimizationStatus::kTopmostFrameIsBaseline);
} else if (frame->is_maglev()) {
status |= static_cast<int>(OptimizationStatus::kTopmostFrameIsMaglev);
}
}
return Smi::FromInt(status);
}
RUNTIME_FUNCTION(Runtime_GetFunctionForCurrentFrame) {
HandleScope scope(isolate);
DCHECK_EQ(args.length(), 0);
JavaScriptStackFrameIterator it(isolate);
DCHECK(!it.done());
return it.frame()->function();
}
RUNTIME_FUNCTION(Runtime_DisableOptimizationFinalization) {
if (isolate->concurrent_recompilation_enabled()) {
isolate->optimizing_compile_dispatcher()->WaitUntilCompilationJobsDone();
isolate->optimizing_compile_dispatcher()->InstallOptimizedFunctions();
isolate->stack_guard()->ClearInstallCode();
isolate->optimizing_compile_dispatcher()->set_finalize(false);
}
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_WaitForBackgroundOptimization) {
if (isolate->concurrent_recompilation_enabled()) {
isolate->optimizing_compile_dispatcher()->WaitUntilCompilationJobsDone();
#if V8_ENABLE_MAGLEV
if (isolate->maglev_concurrent_dispatcher()->is_enabled()) {
isolate->maglev_concurrent_dispatcher()->AwaitCompileJobs();
}
#endif
}
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_FinalizeOptimization) {
if (isolate->concurrent_recompilation_enabled()) {
FinalizeOptimization(isolate);
}
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_ForceFlush) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
Handle<Object> function_object = args.at(0);
CHECK_UNLESS_FUZZING(IsJSFunction(*function_object));
auto function = Cast<JSFunction>(function_object);
Tagged<SharedFunctionInfo> sfi = function->shared(isolate);
CHECK_UNLESS_FUZZING(sfi->CanDiscardCompiled());
for (JavaScriptStackFrameIterator frame_it(isolate); !frame_it.done();
frame_it.Advance()) {
std::vector<Tagged<SharedFunctionInfo>> infos;
frame_it.frame()->GetFunctions(&infos);
for (auto infos_it = infos.rbegin(); infos_it != infos.rend(); ++infos_it) {
CHECK_UNLESS_FUZZING(*infos_it != sfi);
}
}
SharedFunctionInfo::DiscardCompiled(isolate, direct_handle(sfi, isolate));
function->ResetIfCodeFlushed(isolate);
return ReadOnlyRoots(isolate).undefined_value();
}
static void ReturnNull(const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
info.GetReturnValue().SetNull();
}
RUNTIME_FUNCTION(Runtime_GetUndetectable) {
HandleScope scope(isolate);
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
Local<v8::ObjectTemplate> desc = v8::ObjectTemplate::New(v8_isolate);
desc->MarkAsUndetectable();
desc->SetCallAsFunctionHandler(ReturnNull);
Local<v8::Object> obj =
desc->NewInstance(v8_isolate->GetCurrentContext()).ToLocalChecked();
return *Utils::OpenDirectHandle(*obj);
}
namespace {
void call_as_function(const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(ValidateCallbackInfo(info));
v8::Isolate* isolate = info.GetIsolate();
auto context = isolate->GetCurrentContext();
auto global = context->Global();
auto target_function_name = info.Data().As<v8::String>();
v8::Local<v8::Function> target;
{
Local<Value> result;
if (!global->Get(context, target_function_name).ToLocal(&result)) {
return;
}
if (!result->IsFunction()) {
isolate->ThrowError("Target function is not callable");
return;
}
target = result.As<Function>();
}
int argc = info.Length();
v8::LocalVector<v8::Value> args(isolate, argc);
for (int i = 0; i < argc; i++) {
args[i] = info[i];
}
Local<Value> result;
if (!target->Call(context, info.This(), argc, args.data()).ToLocal(&result)) {
return;
}
info.GetReturnValue().Set(result);
}
}
RUNTIME_FUNCTION(Runtime_GetAbstractModuleSource) {
HandleScope scope(isolate);
DisallowGarbageCollection no_gc;
Tagged<JSFunction> abstract_module_source_function =
isolate->native_context()->abstract_module_source_function();
CHECK(IsJSFunction(*abstract_module_source_function));
return abstract_module_source_function;
}
RUNTIME_FUNCTION(Runtime_GetCallable) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
DirectHandle<String> target_function_name = args.at<String>(0);
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(v8_isolate);
Local<v8::ObjectTemplate> instance_template = t->InstanceTemplate();
instance_template->SetCallAsFunctionHandler(
call_as_function, v8::Utils::ToLocal(target_function_name));
v8_isolate->GetCurrentContext();
Local<v8::Object> instance =
t->GetFunction(v8_isolate->GetCurrentContext())
.ToLocalChecked()
->NewInstance(v8_isolate->GetCurrentContext())
.ToLocalChecked();
return *Utils::OpenDirectHandle(*instance);
}
RUNTIME_FUNCTION(Runtime_ClearFunctionFeedback) {
HandleScope scope(isolate);
DCHECK_EQ(args.length(), 1);
DirectHandle<JSFunction> function = args.at<JSFunction>(0);
function->ClearAllTypeFeedbackInfoForTesting(isolate);
function->ResetTieringRequests();
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_NotifyContextDisposed) {
HandleScope scope(isolate);
isolate->heap()->NotifyContextDisposed(true);
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_SetAllocationTimeout) {
SealHandleScope shs(isolate);
CHECK_UNLESS_FUZZING(args.length() == 2 || args.length() == 3);
#ifdef V8_ENABLE_ALLOCATION_TIMEOUT
CONVERT_INT32_ARG_FUZZ_SAFE(interval, 0);
HeapAllocator::SetAllocationGcInterval(interval);
CONVERT_INT32_ARG_FUZZ_SAFE(timeout, 1);
isolate->heap()->set_allocation_timeout(timeout);
#endif
#ifdef DEBUG
if (args.length() == 3) {
CONVERT_BOOLEAN_ARG_FUZZ_SAFE(inline_allocation, 2);
if (inline_allocation) {
isolate->heap()->EnableInlineAllocation();
} else {
isolate->heap()->DisableInlineAllocation();
}
}
#endif
return ReadOnlyRoots(isolate).undefined_value();
}
namespace {
int FixedArrayLenFromSize(int size) {
return std::min({(size - OFFSET_OF_DATA_START(FixedArray)) / kTaggedSize,
FixedArray::kMaxRegularLength});
}
void FillUpOneNewSpacePage(Isolate* isolate, Heap* heap,
SemiSpaceNewSpace* space) {
DCHECK(!v8_flags.single_generation);
heap->FreeMainThreadLinearAllocationAreas();
PauseAllocationObserversScope pause_observers(heap);
while (space->GetSpaceRemainingOnCurrentPageForTesting() > 0) {
int space_remaining = space->GetSpaceRemainingOnCurrentPageForTesting();
int length = FixedArrayLenFromSize(space_remaining);
if (length > 0) {
DirectHandle<FixedArray> padding =
isolate->factory()->NewFixedArray(length, AllocationType::kYoung);
DCHECK(heap->new_space()->Contains(*padding));
space_remaining -= padding->Size();
} else {
space->FillCurrentPageForTesting();
}
heap->FreeMainThreadLinearAllocationAreas();
}
}
}
RUNTIME_FUNCTION(Runtime_SimulateNewspaceFull) {
HandleScope scope(isolate);
Heap* heap = isolate->heap();
heap->FreeMainThreadLinearAllocationAreas();
AlwaysAllocateScopeForTesting always_allocate(heap);
if (v8_flags.minor_ms) {
if (heap->minor_sweeping_in_progress()) {
heap->EnsureYoungSweepingCompleted();
}
auto* space = heap->paged_new_space()->paged_space();
space->AllocatePageUpToCapacityForTesting();
space->ResetFreeList();
} else {
SemiSpaceNewSpace* space = heap->semi_space_new_space();
do {
FillUpOneNewSpacePage(isolate, heap, space);
} while (space->AddFreshPage());
}
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_ScheduleGCInStackCheck) {
SealHandleScope shs(isolate);
isolate->RequestInterrupt(
[](v8::Isolate* isolate, void*) {
isolate->RequestGarbageCollectionForTesting(
v8::Isolate::kFullGarbageCollection);
},
nullptr);
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_TakeHeapSnapshot) {
if (v8_flags.fuzzing) {
return ReadOnlyRoots(isolate).undefined_value();
}
std::string filename = "heap.heapsnapshot";
if (args.length() >= 1) {
HandleScope hs(isolate);
DirectHandle<String> filename_as_js_string = args.at<String>(0);
std::unique_ptr<char[]> buffer = filename_as_js_string->ToCString();
filename = std::string(buffer.get());
}
HeapProfiler* heap_profiler = isolate->heap()->heap_profiler();
v8::HeapProfiler::HeapSnapshotOptions options;
options.numerics_mode = v8::HeapProfiler::NumericsMode::kExposeNumericValues;
options.snapshot_mode = v8::HeapProfiler::HeapSnapshotMode::kExposeInternals;
heap_profiler->TakeSnapshotToFile(options, filename);
return ReadOnlyRoots(isolate).undefined_value();
}
static void DebugPrintImpl(Tagged<MaybeObject> maybe_object, std::ostream& os) {
if (maybe_object.IsCleared()) {
os << "[weak cleared]";
} else {
Tagged<Object> object = maybe_object.GetHeapObjectOrSmi();
bool weak = maybe_object.IsWeak();
#ifdef OBJECT_PRINT
os << "DebugPrint: ";
if (weak) os << "[weak] ";
Print(object, os);
if (IsHeapObject(object)) {
Print(Cast<HeapObject>(object)->map(), os);
}
#else
if (weak) os << "[weak] ";
os << Brief(object);
#endif
}
os << std::endl;
}
RUNTIME_FUNCTION(Runtime_DebugPrint) {
SealHandleScope shs(isolate);
if (args.length() == 0) {
return ReadOnlyRoots(isolate).undefined_value();
}
std::unique_ptr<std::ostream> output_stream(new StdoutStream());
if (args.length() >= 2) {
if (IsSmi(args[1])) {
int output_int = Cast<Smi>(args[1]).value();
if (output_int == fileno(stderr)) {
output_stream.reset(new StderrStream());
}
}
}
Tagged<MaybeObject> maybe_object(*args.address_of_arg_at(0));
DebugPrintImpl(maybe_object, *output_stream);
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_DebugPrintGeneric) {
static constexpr int kNum16BitChunks = 4;
SealHandleScope shs(isolate);
CHECK_UNLESS_FUZZING(args.length() == 2 + kNum16BitChunks + 1);
CHECK(IsSmi(args[1]));
DebugPrintValueType value_type =
static_cast<DebugPrintValueType>(Cast<Smi>(args[1]).value());
CHECK(IsSmi(args[6]));
int stream_int = Cast<Smi>(args[6]).value();
FILE* output_stream = stream_int == fileno(stderr) ? stderr : stdout;
if (IsString(args[0])) {
Tagged<String> prefix = Cast<String>(args[0]);
StringCharacterStream stream(prefix);
while (stream.HasMore()) {
uint16_t character = stream.GetNext();
PrintF(output_stream, "%c", character);
}
}
uint64_t value = 0;
if (value_type != DebugPrintValueType::kTagged) {
for (int i = 0; i < kNum16BitChunks; ++i) {
value <<= 16;
CHECK(IsSmi(args[2 + i]));
uint32_t chunk = Cast<Smi>(args[2 + i]).value();
CHECK_EQ(chunk & 0xFFFF0000, 0);
value |= chunk;
}
}
switch (value_type) {
case DebugPrintValueType::kWord32: {
PrintF(output_stream, "0x%" PRIx32 "\n", static_cast<uint32_t>(value));
break;
}
case DebugPrintValueType::kWord64: {
PrintF(output_stream, "0x%" PRIx64 "\n", value);
break;
}
case DebugPrintValueType::kFloat32: {
const float f = base::bit_cast<float>(static_cast<uint32_t>(value));
if (std::isnan(f)) {
PrintF(output_stream, "%g (0x%" PRIx32 ")\n", f,
static_cast<uint32_t>(value));
} else {
PrintF(output_stream, "%.20g\n", f);
}
break;
}
case DebugPrintValueType::kFloat64: {
const double d = base::bit_cast<double>(value);
if (std::isnan(d)) {
PrintF(output_stream, "%g (0x%" PRIx64 ")\n", d, value);
} else {
PrintF(output_stream, "%.20g\n", d);
}
break;
}
case DebugPrintValueType::kTagged: {
Tagged<Object> tagged = args[5];
CHECK(IsHeapObject(tagged));
if (IsString(tagged) && !IsString(args[0])) {
Tagged<String> text = Cast<String>(tagged);
StringCharacterStream stream(text);
while (stream.HasMore()) {
uint16_t character = stream.GetNext();
PrintF(output_stream, "%c", character);
}
PrintF(output_stream, "\n");
fflush(output_stream);
} else {
Tagged<MaybeObject> maybe_object(tagged);
OFStream fstream(output_stream);
DebugPrintImpl(maybe_object, fstream);
}
break;
}
}
fflush(output_stream);
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_DebugPrintPtr) {
SealHandleScope shs(isolate);
DCHECK_EQ(args.length(), 1);
StdoutStream os;
Tagged<MaybeObject> maybe_object(*args.address_of_arg_at(0));
if (!maybe_object.IsCleared()) {
Tagged<Object> object = maybe_object.GetHeapObjectOrSmi();
size_t pointer;
if (Object::ToIntegerIndex(object, &pointer)) {
Tagged<MaybeObject> from_pointer(static_cast<Address>(pointer));
DebugPrintImpl(from_pointer, os);
}
}
return args[0];
}
RUNTIME_FUNCTION(Runtime_DebugPrintWord) {
static constexpr int kNum16BitChunks = 4;
SealHandleScope shs(isolate);
CHECK_UNLESS_FUZZING(args.length() == kNum16BitChunks + 1);
uint64_t value = 0;
for (int i = 0; i < kNum16BitChunks; ++i) {
value <<= 16;
CHECK(IsSmi(args[i]));
uint32_t chunk = Cast<Smi>(args[i]).value();
CHECK_EQ(chunk & 0xFFFF0000, 0);
value |= chunk;
}
if (!IsSmi(args[4]) || (Cast<Smi>(args[4]).value() == fileno(stderr))) {
StderrStream os;
os << "0x" << std::hex << value << std::dec << std::endl;
} else {
StdoutStream os;
os << "0x" << std::hex << value << std::dec << std::endl;
}
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_DebugPrintFloat) {
static constexpr int kNum16BitChunks = 4;
SealHandleScope shs(isolate);
CHECK_UNLESS_FUZZING(args.length() == kNum16BitChunks + 1);
uint64_t value = 0;
for (int i = 0; i < kNum16BitChunks; ++i) {
value <<= 16;
CHECK(IsSmi(args[i]));
uint32_t chunk = Cast<Smi>(args[i]).value();
CHECK_EQ(chunk & 0xFFFF0000, 0);
value |= chunk;
}
if (!IsSmi(args[4]) || (Cast<Smi>(args[4]).value() == fileno(stderr))) {
StderrStream os;
std::streamsize precision = os.precision();
const double d = base::bit_cast<double>(value);
os << std::setprecision(20) << d;
if (std::isnan(d)) {
os << " (0x" << std::hex << value << std::dec << ")";
}
os << std::endl;
os.precision(precision);
} else {
StdoutStream os;
std::streamsize precision = os.precision();
const double d = base::bit_cast<double>(value);
os << std::setprecision(20) << d;
if (std::isnan(d)) {
os << " (0x" << std::hex << value << std::dec << ")";
}
os << std::endl;
os.precision(precision);
}
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_PrintWithNameForAssert) {
SealHandleScope shs(isolate);
CHECK_UNLESS_FUZZING(args.length() == 2);
CHECK_UNLESS_FUZZING(IsString(args[0]));
auto name = Cast<String>(args[0]);
PrintF(" * ");
StringCharacterStream stream(name);
while (stream.HasMore()) {
uint16_t character = stream.GetNext();
PrintF("%c", character);
}
PrintF(": ");
ShortPrint(args[1]);
PrintF("\n");
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_DebugTrace) {
SealHandleScope shs(isolate);
isolate->PrintStack(stdout);
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_GlobalPrint) {
SealHandleScope shs(isolate);
FILE* output_stream = stdout;
if (args.length() >= 2) {
if (IsSmi(args[1])) {
int output_int = Cast<Smi>(args[1]).value();
if (output_int == fileno(stderr)) {
output_stream = stderr;
}
}
}
if (!IsString(args[0])) {
return args[0];
}
auto string = Cast<String>(args[0]);
StringCharacterStream stream(string);
while (stream.HasMore()) {
uint16_t character = stream.GetNext();
PrintF(output_stream, "%c", character);
}
fflush(output_stream);
return string;
}
RUNTIME_FUNCTION(Runtime_SystemBreak) {
HandleScope scope(isolate);
base::OS::DebugBreak();
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_SetForceSlowPath) {
SealHandleScope shs(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
Tagged<Object> arg = args[0];
if (IsTrue(arg, isolate)) {
isolate->set_force_slow_path(true);
} else {
DCHECK(IsFalse(arg, isolate) || v8_flags.fuzzing);
isolate->set_force_slow_path(false);
}
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_Abort) {
SealHandleScope shs(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
int message_id = args.smi_value_at(0);
const char* message = GetAbortReason(static_cast<AbortReason>(message_id));
base::OS::PrintError("abort: %s\n", message);
isolate->PrintStack(stderr);
base::OS::Abort();
UNREACHABLE();
}
RUNTIME_FUNCTION(Runtime_AbortJS) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
DirectHandle<String> message = args.at<String>(0);
if (v8_flags.disable_abortjs) {
base::OS::PrintError("[disabled] abort: %s\n", message->ToCString().get());
return Tagged<Object>();
}
base::OS::PrintError("abort: %s\n", message->ToCString().get());
isolate->PrintStack(stderr);
base::OS::Abort();
UNREACHABLE();
}
RUNTIME_FUNCTION(Runtime_AbortCSADcheck) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
DirectHandle<String> message = args.at<String>(0);
if (base::ControlledCrashesAreHarmless()) {
base::OS::PrintError(
"Safely terminating process due to CSA check failure\n");
base::OS::PrintError("The following harmless failure was encountered: %s\n",
message->ToCString().get());
} else {
std::unique_ptr<char[]> message_str = message->ToCString();
base::OS::PrintError("abort: CSA_DCHECK failed: %s\n\n", message_str.get());
isolate->PushStackTraceAndDie(reinterpret_cast<void*>(message->ptr()),
message_str.get());
}
base::OS::Abort();
UNREACHABLE();
}
RUNTIME_FUNCTION(Runtime_DisassembleFunction) {
HandleScope scope(isolate);
#ifdef DEBUG
CHECK_UNLESS_FUZZING(args.length() == 1);
DirectHandle<JSFunction> func = args.at<JSFunction>(0);
IsCompiledScope is_compiled_scope;
CHECK(func->shared()->is_compiled() ||
Compiler::Compile(isolate, func, Compiler::KEEP_EXCEPTION,
&is_compiled_scope));
StdoutStream os;
Print(func->code(isolate), os);
os << std::endl;
#endif
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_VerifyGetJSBuiltinState) {
HandleScope scope(isolate);
#ifdef DEBUG
CHECK_UNLESS_FUZZING(args.length() == 1);
if (!v8_flags.fuzzing) {
bool allow_non_initial_state = Object::BooleanValue(args[0], isolate);
isolate->builtins()->VerifyGetJSBuiltinState(allow_non_initial_state);
}
#endif
return ReadOnlyRoots(isolate).undefined_value();
}
namespace {
int StackSize(Isolate* isolate) {
int n = 0;
for (JavaScriptStackFrameIterator it(isolate); !it.done(); it.Advance()) n++;
return n;
}
void PrintIndentation(int stack_size) {
const int max_display = 80;
if (stack_size <= max_display) {
PrintF("%4d:%*s", stack_size, stack_size, "");
} else {
PrintF("%4d:%*s", stack_size, max_display, "...");
}
}
}
RUNTIME_FUNCTION(Runtime_TraceEnter) {
SealHandleScope shs(isolate);
PrintIndentation(StackSize(isolate));
JavaScriptFrame::PrintTop(isolate, stdout, true, false);
PrintF(" {\n");
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_TraceExit) {
SealHandleScope shs(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
Tagged<Object> obj = args[0];
PrintIndentation(StackSize(isolate));
PrintF("} -> ");
ShortPrint(obj);
PrintF("\n");
return obj;
}
RUNTIME_FUNCTION(Runtime_HaveSameMap) {
SealHandleScope shs(isolate);
CHECK_UNLESS_FUZZING(args.length() == 2);
CHECK_UNLESS_FUZZING(!IsSmi(args[0]));
CHECK_UNLESS_FUZZING(!IsSmi(args[1]));
auto obj1 = Cast<HeapObject>(args[0]);
auto obj2 = Cast<HeapObject>(args[1]);
return isolate->heap()->ToBoolean(obj1->map() == obj2->map());
}
RUNTIME_FUNCTION(Runtime_InLargeObjectSpace) {
SealHandleScope shs(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
CHECK_UNLESS_FUZZING(IsHeapObject(args[0]));
auto obj = Cast<HeapObject>(args[0]);
return isolate->heap()->ToBoolean(
(isolate->heap()->new_lo_space() &&
isolate->heap()->new_lo_space()->Contains(obj)) ||
isolate->heap()->code_lo_space()->Contains(obj) ||
isolate->heap()->lo_space()->Contains(obj));
}
RUNTIME_FUNCTION(Runtime_HasElementsInALargeObjectSpace) {
SealHandleScope shs(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
CHECK_UNLESS_FUZZING(IsJSArray(args[0]));
auto array = Cast<JSArray>(args[0]);
Tagged<FixedArrayBase> elements = array->elements();
return isolate->heap()->ToBoolean(
(isolate->heap()->new_lo_space() &&
isolate->heap()->new_lo_space()->Contains(elements)) ||
isolate->heap()->lo_space()->Contains(elements));
}
RUNTIME_FUNCTION(Runtime_HasCowElements) {
SealHandleScope shs(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
CHECK_UNLESS_FUZZING(IsJSArray(args[0]));
auto array = Cast<JSArray>(args[0]);
Tagged<FixedArrayBase> elements = array->elements();
return isolate->heap()->ToBoolean(elements->IsCowArray());
}
RUNTIME_FUNCTION(Runtime_InYoungGeneration) {
SealHandleScope shs(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
Tagged<Object> obj = args[0];
return isolate->heap()->ToBoolean(HeapLayout::InYoungGeneration(obj));
}
RUNTIME_FUNCTION(Runtime_PretenureAllocationSite) {
DisallowGarbageCollection no_gc;
CHECK_UNLESS_FUZZING(args.length() == 1);
Tagged<Object> arg = args[0];
CHECK_UNLESS_FUZZING(IsJSObject(arg));
Tagged<JSObject> object = Cast<JSObject>(arg);
if (!v8_flags.sticky_mark_bits && !HeapLayout::InYoungGeneration(object)) {
return ReturnFuzzSafe(ReadOnlyRoots(isolate).false_value(), isolate);
}
Heap* heap = isolate->heap();
PretenuringHandler* pretenuring_handler = heap->pretenuring_handler();
Tagged<AllocationMemento> memento = PretenuringHandler::FindAllocationMemento<
PretenuringHandler::kForRuntime>(heap, object->map(), object);
if (memento.is_null())
return ReturnFuzzSafe(ReadOnlyRoots(isolate).false_value(), isolate);
Tagged<AllocationSite> site = memento->GetAllocationSite();
pretenuring_handler->PretenureAllocationSiteOnNextCollection(site);
return ReturnFuzzSafe(ReadOnlyRoots(isolate).true_value(), isolate);
}
namespace {
v8::ModifyCodeGenerationFromStringsResult DisallowCodegenFromStringsCallback(
v8::Local<v8::Context> context, v8::Local<v8::Value> source,
bool is_code_kind) {
return {false, {}};
}
}
RUNTIME_FUNCTION(Runtime_DisallowCodegenFromStrings) {
SealHandleScope shs(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
CHECK_UNLESS_FUZZING(IsBoolean(args[0]));
bool flag = Cast<Boolean>(args[0])->ToBool(isolate);
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
v8_isolate->SetModifyCodeGenerationFromStringsCallback(
flag ? DisallowCodegenFromStringsCallback : nullptr);
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_RegexpHasBytecode) {
SealHandleScope shs(isolate);
CHECK_UNLESS_FUZZING(args.length() == 2);
CHECK_UNLESS_FUZZING(IsJSRegExp(args[0]));
CHECK_UNLESS_FUZZING(IsBoolean(args[1]));
auto regexp = args.at<JSRegExp>(0);
bool is_latin1 = args.at<Boolean>(1)->ToBool(isolate);
bool result = false;
if (regexp->has_data()) {
Tagged<RegExpData> data = regexp->data(isolate);
if (data->type_tag() == RegExpData::Type::IRREGEXP) {
result = TrustedCast<IrRegExpData>(data)->has_bytecode(is_latin1);
}
}
return isolate->heap()->ToBoolean(result);
}
RUNTIME_FUNCTION(Runtime_RegexpHasNativeCode) {
SealHandleScope shs(isolate);
CHECK_UNLESS_FUZZING(args.length() == 2);
CHECK_UNLESS_FUZZING(IsJSRegExp(args[0]));
CHECK_UNLESS_FUZZING(IsBoolean(args[1]));
auto regexp = args.at<JSRegExp>(0);
bool is_latin1 = args.at<Boolean>(1)->ToBool(isolate);
bool result = false;
if (regexp->has_data()) {
Tagged<RegExpData> data = regexp->data(isolate);
if (data->type_tag() == RegExpData::Type::IRREGEXP) {
result = TrustedCast<IrRegExpData>(data)->has_code(is_latin1);
}
}
return isolate->heap()->ToBoolean(result);
}
RUNTIME_FUNCTION(Runtime_RegexpTypeTag) {
HandleScope shs(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
CHECK_UNLESS_FUZZING(IsJSRegExp(args[0]));
auto regexp = Cast<JSRegExp>(args[0]);
const char* type_str;
if (regexp->has_data()) {
switch (regexp->data(isolate)->type_tag()) {
case RegExpData::Type::ATOM:
type_str = "ATOM";
break;
case RegExpData::Type::IRREGEXP:
type_str = "IRREGEXP";
break;
case RegExpData::Type::EXPERIMENTAL:
type_str = "EXPERIMENTAL";
break;
default:
UNREACHABLE();
}
} else {
type_str = "NOT_COMPILED";
}
return *isolate->factory()->NewStringFromAsciiChecked(type_str);
}
RUNTIME_FUNCTION(Runtime_RegexpIsUnmodified) {
HandleScope shs(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
CHECK_UNLESS_FUZZING(IsJSRegExp(args[0]));
DirectHandle<JSRegExp> regexp = args.at<JSRegExp>(0);
return isolate->heap()->ToBoolean(
RegExp::IsUnmodifiedRegExp(isolate, regexp));
}
#define ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(Name) \
RUNTIME_FUNCTION(Runtime_##Name) { \
CHECK_UNLESS_FUZZING(args.length() == 1); \
CHECK_UNLESS_FUZZING(IsJSObject(args[0])); \
auto obj = args.at<JSObject>(0); \
return isolate->heap()->ToBoolean(obj->Name()); \
}
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(HasFastElements)
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(HasSmiElements)
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(HasObjectElements)
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(HasSmiOrObjectElements)
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(HasDoubleElements)
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(HasHoleyElements)
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(HasDictionaryElements)
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(HasPackedElements)
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(HasSloppyArgumentsElements)
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(HasFastProperties)
#undef ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION
#define FIXED_TYPED_ARRAYS_CHECK_RUNTIME_FUNCTION(Type, type, TYPE, ctype) \
RUNTIME_FUNCTION(Runtime_HasFixed##Type##Elements) { \
CHECK_UNLESS_FUZZING(args.length() == 1); \
CHECK_UNLESS_FUZZING(IsJSObject(args[0])); \
auto obj = Cast<JSObject>(args[0]); \
return isolate->heap()->ToBoolean(obj->HasFixed##Type##Elements()); \
}
TYPED_ARRAYS(FIXED_TYPED_ARRAYS_CHECK_RUNTIME_FUNCTION)
#undef FIXED_TYPED_ARRAYS_CHECK_RUNTIME_FUNCTION
RUNTIME_FUNCTION(Runtime_IsConcatSpreadableProtector) {
SealHandleScope shs(isolate);
return isolate->heap()->ToBoolean(
Protectors::IsIsConcatSpreadableLookupChainIntact(isolate));
}
RUNTIME_FUNCTION(Runtime_TypedArraySpeciesProtector) {
SealHandleScope shs(isolate);
return isolate->heap()->ToBoolean(
Protectors::IsTypedArraySpeciesLookupChainIntact(isolate));
}
RUNTIME_FUNCTION(Runtime_RegExpSpeciesProtector) {
SealHandleScope shs(isolate);
return isolate->heap()->ToBoolean(
Protectors::IsRegExpSpeciesLookupChainIntact(isolate));
}
RUNTIME_FUNCTION(Runtime_PromiseSpeciesProtector) {
SealHandleScope shs(isolate);
return isolate->heap()->ToBoolean(
Protectors::IsPromiseSpeciesLookupChainIntact(isolate));
}
RUNTIME_FUNCTION(Runtime_ArraySpeciesProtector) {
SealHandleScope shs(isolate);
return isolate->heap()->ToBoolean(
Protectors::IsArraySpeciesLookupChainIntact(isolate));
}
RUNTIME_FUNCTION(Runtime_MapIteratorProtector) {
SealHandleScope shs(isolate);
return isolate->heap()->ToBoolean(
Protectors::IsMapIteratorLookupChainIntact(isolate));
}
RUNTIME_FUNCTION(Runtime_SetIteratorProtector) {
SealHandleScope shs(isolate);
return isolate->heap()->ToBoolean(
Protectors::IsSetIteratorLookupChainIntact(isolate));
}
RUNTIME_FUNCTION(Runtime_StringIteratorProtector) {
SealHandleScope shs(isolate);
return isolate->heap()->ToBoolean(
Protectors::IsStringIteratorLookupChainIntact(isolate));
}
RUNTIME_FUNCTION(Runtime_ArrayIteratorProtector) {
SealHandleScope shs(isolate);
return isolate->heap()->ToBoolean(
Protectors::IsArrayIteratorLookupChainIntact(isolate));
}
RUNTIME_FUNCTION(Runtime_NoElementsProtector) {
SealHandleScope shs(isolate);
return isolate->heap()->ToBoolean(Protectors::IsNoElementsIntact(isolate));
}
RUNTIME_FUNCTION(Runtime_StringWrapperToPrimitiveProtector) {
SealHandleScope shs(isolate);
return isolate->heap()->ToBoolean(
Protectors::IsStringWrapperToPrimitiveIntact(isolate));
}
RUNTIME_FUNCTION(Runtime_SerializeDeserializeNow) {
HandleScope scope(isolate);
Snapshot::SerializeDeserializeAndVerifyForTesting(isolate,
isolate->native_context());
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_HeapObjectVerify) {
HandleScope shs(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
DirectHandle<Object> object = args.at(0);
#ifdef VERIFY_HEAP
Object::ObjectVerify(*object, isolate);
#else
CHECK(IsObject(*object));
if (IsHeapObject(*object)) {
CHECK(IsMap(Cast<HeapObject>(*object)->map()));
} else {
CHECK(IsSmi(*object));
}
#endif
return isolate->heap()->ToBoolean(true);
}
RUNTIME_FUNCTION(Runtime_CompleteInobjectSlackTracking) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
CHECK_UNLESS_FUZZING(IsJSObject(args[0]));
DirectHandle<JSObject> object = args.at<JSObject>(0);
MapUpdater::CompleteInobjectSlackTracking(isolate, object->map());
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_MajorGCForCompilerTesting) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(v8_flags.allow_natives_syntax ||
v8_flags.allow_natives_for_differential_fuzzing);
CHECK_UNLESS_FUZZING(args.length() == 0);
isolate->heap()->CollectGarbage(OLD_SPACE, GarbageCollectionReason::kTesting);
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_TurbofanStaticAssert) {
SealHandleScope shs(isolate);
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_IsBeingInterpreted) {
SealHandleScope shs(isolate);
return ReadOnlyRoots(isolate).true_value();
}
RUNTIME_FUNCTION(Runtime_EnableCodeLoggingForTesting) {
class NoopListener final : public LogEventListener {
void CodeCreateEvent(CodeTag tag, DirectHandle<AbstractCode> code,
const char* name) final {}
void CodeCreateEvent(CodeTag tag, DirectHandle<AbstractCode> code,
DirectHandle<Name> name) final {}
void CodeCreateEvent(CodeTag tag, DirectHandle<AbstractCode> code,
DirectHandle<SharedFunctionInfo> shared,
DirectHandle<Name> script_name) final {}
void CodeCreateEvent(CodeTag tag, DirectHandle<AbstractCode> code,
DirectHandle<SharedFunctionInfo> shared,
DirectHandle<Name> script_name, int line,
int column) final {}
#if V8_ENABLE_WEBASSEMBLY
void CodeCreateEvent(CodeTag tag, const wasm::WasmCode* code,
wasm::WasmName name, std::string_view source_url,
int code_offset, int script_id) final {}
#endif
void CallbackEvent(DirectHandle<Name> name, Address entry_point) final {}
void GetterCallbackEvent(DirectHandle<Name> name,
Address entry_point) final {}
void SetterCallbackEvent(DirectHandle<Name> name,
Address entry_point) final {}
void RegExpCodeCreateEvent(DirectHandle<AbstractCode> code,
DirectHandle<String> source,
RegExpFlags flags) final {}
void CodeMoveEvent(Tagged<InstructionStream> from,
Tagged<InstructionStream> to) final {}
void BytecodeMoveEvent(Tagged<BytecodeArray> from,
Tagged<BytecodeArray> to) final {}
void SharedFunctionInfoMoveEvent(Address from, Address to) final {}
void NativeContextMoveEvent(Address from, Address to) final {}
void CodeMovingGCEvent() final {}
void CodeDisableOptEvent(DirectHandle<AbstractCode> code,
DirectHandle<SharedFunctionInfo> shared) final {}
void CodeDeoptEvent(DirectHandle<Code> code, DeoptimizeKind kind,
Address pc, int fp_to_sp_delta) final {}
void CodeDependencyChangeEvent(DirectHandle<Code> code,
DirectHandle<SharedFunctionInfo> shared,
const char* reason) final {}
void WeakCodeClearEvent() final {}
bool is_listening_to_code_events() final { return true; }
};
static base::LeakyObject<NoopListener> noop_listener;
#if V8_ENABLE_WEBASSEMBLY
wasm::GetWasmEngine()->EnableCodeLogging(isolate);
#endif
isolate->logger()->AddListener(noop_listener.get());
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_NewRegExpWithBacktrackLimit) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 3);
CHECK_UNLESS_FUZZING(IsString(args[0]));
CHECK_UNLESS_FUZZING(IsString(args[1]));
CHECK_UNLESS_FUZZING(IsSmi(args[2]));
DirectHandle<String> pattern = args.at<String>(0);
DirectHandle<String> flags_string = args.at<String>(1);
int backtrack_limit = args.smi_value_at(2);
CHECK_UNLESS_FUZZING(backtrack_limit >= 0);
auto maybe_flags = JSRegExp::FlagsFromString(isolate, flags_string);
CHECK_UNLESS_FUZZING(maybe_flags.has_value());
JSRegExp::Flags flags = maybe_flags.value();
RETURN_RESULT_OR_FAILURE(
isolate, JSRegExp::New(isolate, pattern, flags, backtrack_limit));
}
RUNTIME_FUNCTION(Runtime_Is64Bit) {
SealHandleScope shs(isolate);
return isolate->heap()->ToBoolean(kSystemPointerSize == 8);
}
RUNTIME_FUNCTION(Runtime_BigIntMaxLengthBits) {
HandleScope scope(isolate);
return *isolate->factory()->NewNumber(BigInt::kMaxLengthBits);
}
RUNTIME_FUNCTION(Runtime_IsSameHeapObject) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 2);
CHECK_UNLESS_FUZZING(IsHeapObject(args[0]));
CHECK_UNLESS_FUZZING(IsHeapObject(args[1]));
DirectHandle<HeapObject> obj1 = args.at<HeapObject>(0);
DirectHandle<HeapObject> obj2 = args.at<HeapObject>(1);
return isolate->heap()->ToBoolean(obj1->address() == obj2->address());
}
RUNTIME_FUNCTION(Runtime_IsSharedString) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
CHECK_UNLESS_FUZZING(IsHeapObject(args[0]));
DirectHandle<HeapObject> obj = args.at<HeapObject>(0);
return isolate->heap()->ToBoolean(IsString(*obj) &&
Cast<String>(obj)->IsShared());
}
RUNTIME_FUNCTION(Runtime_IsInWritableSharedSpace) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
if (!IsHeapObject(args[0])) return ReadOnlyRoots(isolate).false_value();
DirectHandle<HeapObject> obj = args.at<HeapObject>(0);
return isolate->heap()->ToBoolean(HeapLayout::InWritableSharedSpace(*obj));
}
RUNTIME_FUNCTION(Runtime_ShareObject) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
CHECK_UNLESS_FUZZING(v8_flags.shared_heap);
CHECK_UNLESS_FUZZING(v8_flags.shared_strings);
if (IsSmi(args[0])) return args[0];
CHECK_UNLESS_FUZZING(IsHeapObject(args[0]));
Handle<HeapObject> obj = args.at<HeapObject>(0);
ShouldThrow should_throw = v8_flags.fuzzing ? kDontThrow : kThrowOnError;
MaybeDirectHandle<Object> maybe_shared =
Object::Share(isolate, obj, should_throw);
DirectHandle<Object> shared;
CHECK_UNLESS_FUZZING(maybe_shared.ToHandle(&shared));
return *shared;
}
RUNTIME_FUNCTION(Runtime_IsInPlaceInternalizableString) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
CHECK_UNLESS_FUZZING(IsHeapObject(args[0]));
DirectHandle<HeapObject> obj = args.at<HeapObject>(0);
return isolate->heap()->ToBoolean(
IsString(*obj) && String::IsInPlaceInternalizable(Cast<String>(*obj)));
}
RUNTIME_FUNCTION(Runtime_IsInternalizedString) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
CHECK_UNLESS_FUZZING(IsHeapObject(args[0]));
DirectHandle<HeapObject> obj = args.at<HeapObject>(0);
return isolate->heap()->ToBoolean(IsInternalizedString(*obj));
}
RUNTIME_FUNCTION(Runtime_StringToCString) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
CHECK_UNLESS_FUZZING(IsString(args[0]));
DirectHandle<String> string = args.at<String>(0);
size_t output_length;
auto bytes = string->ToCString(&output_length);
DirectHandle<JSArrayBuffer> result =
isolate->factory()
->NewJSArrayBufferAndBackingStore(output_length,
InitializedFlag::kUninitialized)
.ToHandleChecked();
memcpy(result->backing_store(), bytes.get(), output_length);
return *result;
}
RUNTIME_FUNCTION(Runtime_StringUtf8Value) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
CHECK_UNLESS_FUZZING(IsString(args[0]));
DirectHandle<String> string = args.at<String>(0);
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
v8::String::Utf8Value value(v8_isolate, v8::Utils::ToLocal(string));
DirectHandle<JSArrayBuffer> result =
isolate->factory()
->NewJSArrayBufferAndBackingStore(value.length(),
InitializedFlag::kUninitialized)
.ToHandleChecked();
memcpy(result->backing_store(), *value, value.length());
return *result;
}
RUNTIME_FUNCTION(Runtime_SharedGC) {
SealHandleScope scope(isolate);
CHECK_UNLESS_FUZZING(isolate->has_shared_space());
isolate->heap()->CollectGarbageShared(isolate->main_thread_local_heap(),
GarbageCollectionReason::kTesting);
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_AtomicsSynchronizationPrimitiveNumWaitersForTesting) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
CHECK_UNLESS_FUZZING(IsJSSynchronizationPrimitive(*args.at(0)));
DirectHandle<JSSynchronizationPrimitive> primitive =
args.at<JSSynchronizationPrimitive>(0);
return primitive->NumWaitersForTesting(isolate);
}
RUNTIME_FUNCTION(Runtime_GetWeakCollectionSize) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
CHECK_UNLESS_FUZZING(IsJSWeakCollection(args[0]));
DirectHandle<JSWeakCollection> collection = args.at<JSWeakCollection>(0);
return Smi::FromInt(
Cast<EphemeronHashTable>(collection->table())->NumberOfElements());
}
RUNTIME_FUNCTION(Runtime_SetPriorityBestEffort) {
isolate->SetPriority(v8::Isolate::Priority::kBestEffort);
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_SetPriorityUserVisible) {
isolate->SetPriority(v8::Isolate::Priority::kUserVisible);
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_SetPriorityUserBlocking) {
isolate->SetPriority(v8::Isolate::Priority::kUserBlocking);
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_IsEfficiencyModeEnabled) {
if (isolate->EfficiencyModeEnabled()) {
return ReadOnlyRoots(isolate).true_value();
}
return ReadOnlyRoots(isolate).false_value();
}
RUNTIME_FUNCTION(Runtime_IsUndefinedDoubleEnabled) {
#ifdef V8_ENABLE_UNDEFINED_DOUBLE
return ReadOnlyRoots(isolate).true_value();
#else
return ReadOnlyRoots(isolate).false_value();
#endif
}
RUNTIME_FUNCTION(Runtime_SetBatterySaverMode) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
if (*args.at<Object>(0) == ReadOnlyRoots(isolate).true_value()) {
isolate->set_battery_saver_mode_enabled(true);
} else {
isolate->set_battery_saver_mode_enabled(false);
}
if (v8_flags.battery_saver_mode.value().has_value()) {
return ReadOnlyRoots(isolate).false_value();
}
return ReadOnlyRoots(isolate).true_value();
}
RUNTIME_FUNCTION(Runtime_IsWasmTieringPredictable) {
DCHECK_EQ(args.length(), 0);
const bool single_isolate = g_num_isolates_for_testing == 1;
const bool stress_deopt = v8_flags.deopt_every_n_times > 0;
return ReadOnlyRoots(isolate).boolean_value(single_isolate && !stress_deopt);
}
RUNTIME_FUNCTION(Runtime_GetFeedback) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 1);
DirectHandle<Object> function_object = args.at(0);
CHECK_UNLESS_FUZZING(IsJSFunction(*function_object));
DirectHandle<JSFunction> function = Cast<JSFunction>(function_object);
bool has_feedback_vector = function->has_feedback_vector();
bool has_bytecode_array = function->shared()->HasBytecodeArray();
CHECK_UNLESS_FUZZING(has_feedback_vector || has_bytecode_array);
#ifdef V8_JITLESS
return ReadOnlyRoots(isolate).undefined_value();
#else
#ifdef OBJECT_PRINT
struct FeedbackValue {
std::string slot_kind_;
std::string details_;
};
std::vector<FeedbackValue> extracted_feedbacks;
if (has_feedback_vector) {
DirectHandle<FeedbackVector> feedback_vector =
direct_handle(function->feedback_vector(), isolate);
CHECK_UNLESS_FUZZING(feedback_vector->has_metadata());
IsCompiledScope is_compiled_scope(
function->shared()->is_compiled_scope(isolate));
USE(is_compiled_scope);
FeedbackMetadataIterator iter(handle(feedback_vector->metadata(), isolate));
while (iter.HasNext()) {
FeedbackSlot slot = iter.Next();
FeedbackSlotKind kind = iter.kind();
FeedbackValue feedback_value;
{
std::ostringstream out;
out << kind;
feedback_value.slot_kind_ = out.str();
}
FeedbackNexus nexus(isolate, *feedback_vector, slot);
{
std::ostringstream out;
nexus.Print(out);
feedback_value.details_ = out.str();
}
extracted_feedbacks.push_back(feedback_value);
}
}
if (has_bytecode_array) {
Handle<BytecodeArray> bytecode_array =
handle(function->shared()->GetBytecodeArray(isolate), isolate);
interpreter::BytecodeArrayIterator it(bytecode_array);
for (; !it.done(); it.Advance()) {
auto bytecode = it.current_bytecode();
if (!interpreter::Bytecodes::IsEmbeddedFeedbackBytecode(bytecode)) {
continue;
}
FeedbackValue feedback_value;
std::ostringstream out;
if (interpreter::Bytecodes::IsCompareWithEmbeddedFeedback(bytecode)) {
out << "CompareOp";
feedback_value.slot_kind_ = out.str();
out << ":" << it.GetEmbeddedCompareOperationHint();
feedback_value.details_ = out.str();
} else {
UNREACHABLE();
}
extracted_feedbacks.push_back(feedback_value);
}
}
int result_size = static_cast<int>(extracted_feedbacks.size());
DirectHandle<FixedArray> result = isolate->factory()->NewFixedArray(
static_cast<int>(extracted_feedbacks.size()));
for (int idx = 0; idx < result_size; idx++) {
const auto& feedback_value = extracted_feedbacks[idx];
DirectHandle<FixedArray> sub_result = isolate->factory()->NewFixedArray(2);
DirectHandle<String> kind_string =
isolate->factory()->NewStringFromAsciiChecked(
feedback_value.slot_kind_);
sub_result->set(0, *kind_string);
DirectHandle<String> details_string =
isolate->factory()->NewStringFromAsciiChecked(feedback_value.details_);
sub_result->set(1, *details_string);
DirectHandle<JSArray> sub_result_array =
isolate->factory()->NewJSArrayWithElements(sub_result);
result->set(idx, *sub_result_array);
}
return *isolate->factory()->NewJSArrayWithElements(result);
#else
return ReadOnlyRoots(isolate).undefined_value();
#endif
#endif
}
RUNTIME_FUNCTION(Runtime_ArrayBufferDetachForceWasm) {
HandleScope scope(isolate);
DisallowGarbageCollection no_gc;
DCHECK_LE(args.length(), 2);
DCHECK(IsJSArrayBuffer(*args.at(0)));
auto array_buffer = Cast<JSArrayBuffer>(args.at(0));
DCHECK(array_buffer->GetBackingStore()->is_wasm_memory());
DCHECK(!array_buffer->is_shared());
constexpr bool kForceForWasmMemory = true;
MAYBE_RETURN(JSArrayBuffer::Detach(array_buffer, kForceForWasmMemory,
args.atOrUndefined(isolate, 1)),
ReadOnlyRoots(isolate).exception());
return ReadOnlyRoots(isolate).undefined_value();
}
namespace {
template <typename ArgType>
bool ParseArgumentsForTablePrinter(const RuntimeArguments& args,
Isolate* isolate, ArgType* arg1,
ArgType* arg2) {
if (args.length() > 2) {
return false;
}
if (args.length() == 1) {
Tagged<Smi> smi;
if (!TryCast(args[0], &smi)) {
return false;
}
*arg1 = static_cast<ArgType>(Smi::ToInt(smi));
*arg2 = static_cast<ArgType>(Smi::ToInt(smi) + 1);
} else if (args.length() == 2) {
Tagged<Smi> smi;
if (!TryCast(args[0], &smi)) {
return false;
}
*arg1 = static_cast<ArgType>(Smi::ToInt(smi));
if (!TryCast(args[1], &smi)) {
return false;
}
*arg2 = static_cast<ArgType>(Smi::ToInt(smi));
}
return true;
}
template <typename EntryFilter>
void PrintCppHeapPointerTableImpl(Isolate* isolate,
CppHeapPointerHandle min_handle,
CppHeapPointerHandle max_handle,
EntryFilter entry_filter) {
PrintF("CppHeapPointerTable:\n");
#ifdef OBJECT_PRINT
#ifdef V8_COMPRESS_POINTERS
const auto& table = Isolate::Current()->cpp_heap_pointer_table();
table.Print(Isolate::Current()->heap()->cpp_heap_pointer_space(), "Old space",
min_handle, max_handle, entry_filter);
#else
PrintF("Table not used in this configuration.\n");
#endif
#else
PrintF("Object printing not enabled.\n");
#endif
}
template <typename EntryFilter>
void PrintExternalPointerTableImpl(Isolate* isolate,
ExternalPointerHandle min_handle,
ExternalPointerHandle max_handle,
EntryFilter entry_filter) {
PrintF("ExternalPointerTable:\n");
#ifdef OBJECT_PRINT
#ifdef V8_COMPRESS_POINTERS
const auto& table = Isolate::Current()->external_pointer_table();
table.Print(Isolate::Current()->heap()->read_only_external_pointer_space(),
"Read-only space", min_handle, max_handle, entry_filter);
table.Print(Isolate::Current()->heap()->young_external_pointer_space(),
"Young space", min_handle, max_handle, entry_filter);
table.Print(Isolate::Current()->heap()->old_external_pointer_space(),
"Old space", min_handle, max_handle, entry_filter);
#else
PrintF("Table not used in this configuration.\n");
#endif
#else
PrintF("Object printing not enabled.\n");
#endif
}
}
RUNTIME_FUNCTION(Runtime_DebugPrintCppHeapPointerTable) {
using HandleType = CppHeapPointerHandle;
HandleType min_handle = std::numeric_limits<HandleType>::min();
HandleType max_handle = std::numeric_limits<HandleType>::max();
if (!ParseArgumentsForTablePrinter(args, isolate, &min_handle, &max_handle)) {
return ReadOnlyRoots(isolate).undefined_value();
}
PrintCppHeapPointerTableImpl(isolate, min_handle, max_handle,
[](CppHeapPointerTag) { return true; });
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_DebugPrintCppHeapPointerTableFilterTag) {
using HandleType = CppHeapPointerHandle;
const HandleType min_handle = std::numeric_limits<HandleType>::min();
const HandleType max_handle = std::numeric_limits<HandleType>::max();
CppHeapPointerTag min_tag = CppHeapPointerTag::kFirstTag;
CppHeapPointerTag max_tag = CppHeapPointerTag::kLastTag;
if (!ParseArgumentsForTablePrinter(args, isolate, &min_tag, &max_tag)) {
return ReadOnlyRoots(isolate).undefined_value();
}
PrintCppHeapPointerTableImpl(isolate, min_handle, max_handle,
[min_tag, max_tag](CppHeapPointerTag tag) {
return tag >= min_tag && tag < max_tag;
});
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_DebugPrintExternalPointerTable) {
using HandleType = ExternalPointerHandle;
HandleType min_handle = std::numeric_limits<HandleType>::min();
HandleType max_handle = std::numeric_limits<HandleType>::max();
if (!ParseArgumentsForTablePrinter(args, isolate, &min_handle, &max_handle)) {
return ReadOnlyRoots(isolate).undefined_value();
}
PrintExternalPointerTableImpl(isolate, min_handle, max_handle,
[](ExternalPointerTag) { return true; });
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_DebugPrintExternalPointerTableFilterTag) {
using HandleType = ExternalPointerHandle;
const HandleType min_handle = std::numeric_limits<HandleType>::min();
const HandleType max_handle = std::numeric_limits<HandleType>::max();
ExternalPointerTag min_tag = ExternalPointerTag::kFirstExternalPointerTag;
ExternalPointerTag max_tag = ExternalPointerTag::kLastExternalPointerTag;
if (!ParseArgumentsForTablePrinter(args, isolate, &min_tag, &max_tag)) {
return ReadOnlyRoots(isolate).undefined_value();
}
PrintExternalPointerTableImpl(isolate, min_handle, max_handle,
[min_tag, max_tag](ExternalPointerTag tag) {
return tag >= min_tag && tag < max_tag;
});
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_GetHoleNaNUpper) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 0);
return *isolate->factory()->NewNumberFromUint(kHoleNanUpper32);
}
RUNTIME_FUNCTION(Runtime_GetHoleNaNLower) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 0);
return *isolate->factory()->NewNumberFromUint(kHoleNanLower32);
}
RUNTIME_FUNCTION(Runtime_GetHoleNaN) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 0);
return *isolate->factory()->NewHeapNumberFromBits(kHoleNanInt64);
}
RUNTIME_FUNCTION(Runtime_GetUndefinedNaN) {
HandleScope scope(isolate);
CHECK_UNLESS_FUZZING(args.length() == 0);
#if V8_ENABLE_UNDEFINED_DOUBLE
return *isolate->factory()->NewHeapNumberFromBits(kUndefinedNanInt64);
#else
CHECK_UNLESS_FUZZING(false && "undefined NaNs are disabled via build flag");
return ReadOnlyRoots(isolate).undefined_value();
#endif
}
}
}