// Copyright 2014 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/runtime/runtime-utils.h"
#include <memory>
#include "src/arguments.h"
#include "src/assembler-inl.h"
#include "src/compiler-dispatcher/optimizing-compile-dispatcher.h"
#include "src/compiler.h"
#include "src/deoptimizer.h"
#include "src/frames-inl.h"
#include "src/full-codegen/full-codegen.h"
#include "src/isolate-inl.h"
#include "src/runtime-profiler.h"
#include "src/snapshot/code-serializer.h"
#include "src/snapshot/natives.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-objects.h"
namespace {
struct WasmCompileControls {
uint32_t MaxWasmBufferSize = std::numeric_limits<uint32_t>::max();
bool AllowAnySizeForAsync = true;
};
// We need per-isolate controls, because we sometimes run tests in multiple
// isolates
// concurrently.
// To avoid upsetting the static initializer count, we lazy initialize this.
v8::base::LazyInstance<std::map<v8::Isolate*, WasmCompileControls>>::type
g_PerIsolateWasmControls = LAZY_INSTANCE_INITIALIZER;
bool IsWasmCompileAllowed(v8::Isolate* isolate, v8::Local<v8::Value> value,
bool is_async) {
DCHECK_GT(g_PerIsolateWasmControls.Get().count(isolate), 0);
const WasmCompileControls& ctrls = g_PerIsolateWasmControls.Get().at(isolate);
return (is_async && ctrls.AllowAnySizeForAsync) ||
(v8::Local<v8::ArrayBuffer>::Cast(value)->ByteLength() <=
ctrls.MaxWasmBufferSize);
}
// Use the compile controls for instantiation, too
bool IsWasmInstantiateAllowed(v8::Isolate* isolate,
v8::Local<v8::Value> module_or_bytes,
v8::MaybeLocal<v8::Value> ffi, bool is_async) {
DCHECK_GT(g_PerIsolateWasmControls.Get().count(isolate), 0);
const WasmCompileControls& ctrls = g_PerIsolateWasmControls.Get().at(isolate);
if (is_async && ctrls.AllowAnySizeForAsync) return true;
if (!module_or_bytes->IsWebAssemblyCompiledModule()) {
return IsWasmCompileAllowed(isolate, module_or_bytes, is_async);
}
v8::Local<v8::WasmCompiledModule> module =
v8::Local<v8::WasmCompiledModule>::Cast(module_or_bytes);
return static_cast<uint32_t>(module->GetWasmWireBytes()->Length()) <=
ctrls.MaxWasmBufferSize;
}
} // namespace
namespace v8 {
namespace internal {
RUNTIME_FUNCTION(Runtime_ConstructDouble) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_NUMBER_CHECKED(uint32_t, hi, Uint32, args[0]);
CONVERT_NUMBER_CHECKED(uint32_t, lo, Uint32, args[1]);
uint64_t result = (static_cast<uint64_t>(hi) << 32) | lo;
return *isolate->factory()->NewNumber(uint64_to_double(result));
}
RUNTIME_FUNCTION(Runtime_DeoptimizeFunction) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
// This function is used by fuzzers to get coverage in compiler.
// Ignore calls on non-function objects to avoid runtime errors.
CONVERT_ARG_HANDLE_CHECKED(Object, function_object, 0);
if (!function_object->IsJSFunction()) {
return isolate->heap()->undefined_value();
}
Handle<JSFunction> function = Handle<JSFunction>::cast(function_object);
// If the function is not optimized, just return.
if (!function->IsOptimized()) return isolate->heap()->undefined_value();
// TODO(turbofan): Deoptimization is not supported yet.
if (function->code()->is_turbofanned() &&
function->shared()->asm_function()) {
return isolate->heap()->undefined_value();
}
Deoptimizer::DeoptimizeFunction(*function);
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_DeoptimizeNow) {
HandleScope scope(isolate);
DCHECK_EQ(0, args.length());
Handle<JSFunction> function;
// Find the JavaScript function on the top of the stack.
JavaScriptFrameIterator it(isolate);
if (!it.done()) function = Handle<JSFunction>(it.frame()->function());
if (function.is_null()) return isolate->heap()->undefined_value();
// If the function is not optimized, just return.
if (!function->IsOptimized()) return isolate->heap()->undefined_value();
// TODO(turbofan): Deoptimization is not supported yet.
if (function->code()->is_turbofanned() &&
function->shared()->asm_function()) {
return isolate->heap()->undefined_value();
}
Deoptimizer::DeoptimizeFunction(*function);
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_RunningInSimulator) {
SealHandleScope shs(isolate);
DCHECK_EQ(0, args.length());
#if defined(USE_SIMULATOR)
return isolate->heap()->true_value();
#else
return isolate->heap()->false_value();
#endif
}
RUNTIME_FUNCTION(Runtime_IsConcurrentRecompilationSupported) {
SealHandleScope shs(isolate);
DCHECK_EQ(0, args.length());
return isolate->heap()->ToBoolean(
isolate->concurrent_recompilation_enabled());
}
RUNTIME_FUNCTION(Runtime_OptimizeFunctionOnNextCall) {
HandleScope scope(isolate);
// This function is used by fuzzers, ignore calls with bogus arguments count.
if (args.length() != 1 && args.length() != 2) {
return isolate->heap()->undefined_value();
}
// This function is used by fuzzers to get coverage for optimizations
// in compiler. Ignore calls on non-function objects to avoid runtime errors.
CONVERT_ARG_HANDLE_CHECKED(Object, function_object, 0);
if (!function_object->IsJSFunction()) {
return isolate->heap()->undefined_value();
}
Handle<JSFunction> function = Handle<JSFunction>::cast(function_object);
// The following condition was lifted from the DCHECK inside
// JSFunction::MarkForOptimization().
if (!(function->shared()->allows_lazy_compilation() ||
(function->code()->kind() == Code::FUNCTION &&
!function->shared()->optimization_disabled()))) {
return isolate->heap()->undefined_value();
}
// If function isn't compiled, compile it now.
if (!function->shared()->is_compiled() &&
!Compiler::Compile(function, Compiler::CLEAR_EXCEPTION)) {
return isolate->heap()->undefined_value();
}
// If the function is already optimized, just return.
if (function->IsOptimized()) return isolate->heap()->undefined_value();
function->MarkForOptimization();
Code* unoptimized = function->shared()->code();
if (args.length() == 2 && unoptimized->kind() == Code::FUNCTION) {
CONVERT_ARG_HANDLE_CHECKED(String, type, 1);
if (type->IsOneByteEqualTo(STATIC_CHAR_VECTOR("concurrent")) &&
isolate->concurrent_recompilation_enabled()) {
function->AttemptConcurrentOptimization();
}
}
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_InterpretFunctionOnNextCall) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(Object, function_object, 0);
if (!function_object->IsJSFunction()) {
return isolate->heap()->undefined_value();
}
Handle<JSFunction> function = Handle<JSFunction>::cast(function_object);
// Do not tier down if we are already on optimized code. Replacing optimized
// code without actual deoptimization can lead to funny bugs.
if (function->code()->kind() != Code::OPTIMIZED_FUNCTION &&
function->shared()->HasBytecodeArray()) {
function->ReplaceCode(*isolate->builtins()->InterpreterEntryTrampoline());
}
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_BaselineFunctionOnNextCall) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(Object, function_object, 0);
if (!function_object->IsJSFunction()) {
return isolate->heap()->undefined_value();
}
Handle<JSFunction> function = Handle<JSFunction>::cast(function_object);
// If function isn't compiled, compile it now.
if (!function->shared()->is_compiled() &&
!Compiler::Compile(function, Compiler::CLEAR_EXCEPTION)) {
return isolate->heap()->undefined_value();
}
// Do not tier down if we are already on optimized code. Replacing optimized
// code without actual deoptimization can lead to funny bugs.
if (function->code()->kind() != Code::OPTIMIZED_FUNCTION &&
function->code()->kind() != Code::FUNCTION) {
if (function->shared()->HasBaselineCode()) {
function->ReplaceCode(function->shared()->code());
} else {
function->MarkForBaseline();
}
}
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_OptimizeOsr) {
HandleScope scope(isolate);
DCHECK(args.length() == 0 || args.length() == 1);
Handle<JSFunction> function;
// The optional parameter determines the frame being targeted.
int stack_depth = args.length() == 1 ? args.smi_at(0) : 0;
// Find the JavaScript function on the top of the stack.
JavaScriptFrameIterator it(isolate);
while (!it.done() && stack_depth--) it.Advance();
if (!it.done()) function = Handle<JSFunction>(it.frame()->function());
if (function.is_null()) return isolate->heap()->undefined_value();
// If the function is already optimized, just return.
if (function->IsOptimized()) return isolate->heap()->undefined_value();
// Make the profiler arm all back edges in unoptimized code.
if (it.frame()->type() == StackFrame::JAVA_SCRIPT ||
it.frame()->type() == StackFrame::INTERPRETED) {
isolate->runtime_profiler()->AttemptOnStackReplacement(
it.frame(), AbstractCode::kMaxLoopNestingMarker);
}
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_NeverOptimizeFunction) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_CHECKED(JSFunction, function, 0);
function->shared()->set_disable_optimization_reason(
kOptimizationDisabledForTest);
function->shared()->set_optimization_disabled(true);
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_GetOptimizationStatus) {
HandleScope scope(isolate);
DCHECK(args.length() == 1 || args.length() == 2);
int status = 0;
if (!isolate->use_crankshaft()) {
status |= static_cast<int>(OptimizationStatus::kNeverOptimize);
}
if (FLAG_always_opt || FLAG_prepare_always_opt) {
status |= static_cast<int>(OptimizationStatus::kAlwaysOptimize);
}
if (FLAG_deopt_every_n_times) {
status |= static_cast<int>(OptimizationStatus::kMaybeDeopted);
}
// This function is used by fuzzers to get coverage for optimizations
// in compiler. Ignore calls on non-function objects to avoid runtime errors.
CONVERT_ARG_HANDLE_CHECKED(Object, function_object, 0);
if (!function_object->IsJSFunction()) {
return Smi::FromInt(status);
}
Handle<JSFunction> function = Handle<JSFunction>::cast(function_object);
status |= static_cast<int>(OptimizationStatus::kIsFunction);
bool sync_with_compiler_thread = true;
if (args.length() == 2) {
CONVERT_ARG_HANDLE_CHECKED(Object, sync_object, 1);
if (!sync_object->IsString()) return isolate->heap()->undefined_value();
Handle<String> sync = Handle<String>::cast(sync_object);
if (sync->IsOneByteEqualTo(STATIC_CHAR_VECTOR("no sync"))) {
sync_with_compiler_thread = false;
}
}
if (isolate->concurrent_recompilation_enabled() &&
sync_with_compiler_thread) {
while (function->IsInOptimizationQueue()) {
isolate->optimizing_compile_dispatcher()->InstallOptimizedFunctions();
base::OS::Sleep(base::TimeDelta::FromMilliseconds(50));
}
}
if (function->IsOptimized()) {
status |= static_cast<int>(OptimizationStatus::kOptimized);
if (function->code()->is_turbofanned()) {
status |= static_cast<int>(OptimizationStatus::kTurboFanned);
}
}
if (function->IsInterpreted()) {
status |= static_cast<int>(OptimizationStatus::kInterpreted);
}
return Smi::FromInt(status);
}
RUNTIME_FUNCTION(Runtime_UnblockConcurrentRecompilation) {
DCHECK_EQ(0, args.length());
if (FLAG_block_concurrent_recompilation &&
isolate->concurrent_recompilation_enabled()) {
isolate->optimizing_compile_dispatcher()->Unblock();
}
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_GetOptimizationCount) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
return Smi::FromInt(function->shared()->opt_count());
}
static void ReturnThis(const v8::FunctionCallbackInfo<v8::Value>& args) {
args.GetReturnValue().Set(args.This());
}
RUNTIME_FUNCTION(Runtime_GetUndetectable) {
HandleScope scope(isolate);
DCHECK_EQ(0, args.length());
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
Local<v8::ObjectTemplate> desc = v8::ObjectTemplate::New(v8_isolate);
desc->MarkAsUndetectable();
desc->SetCallAsFunctionHandler(ReturnThis);
Local<v8::Object> obj;
if (!desc->NewInstance(v8_isolate->GetCurrentContext()).ToLocal(&obj)) {
return nullptr;
}
return *Utils::OpenHandle(*obj);
}
static void call_as_function(const v8::FunctionCallbackInfo<v8::Value>& args) {
double v1 = args[0]
->NumberValue(v8::Isolate::GetCurrent()->GetCurrentContext())
.ToChecked();
double v2 = args[1]
->NumberValue(v8::Isolate::GetCurrent()->GetCurrentContext())
.ToChecked();
args.GetReturnValue().Set(
v8::Number::New(v8::Isolate::GetCurrent(), v1 - v2));
}
// Returns a callable object. The object returns the difference of its two
// parameters when it is called.
RUNTIME_FUNCTION(Runtime_GetCallable) {
HandleScope scope(isolate);
DCHECK_EQ(0, args.length());
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(v8_isolate);
Local<ObjectTemplate> instance_template = t->InstanceTemplate();
instance_template->SetCallAsFunctionHandler(call_as_function);
v8_isolate->GetCurrentContext();
Local<v8::Object> instance =
t->GetFunction(v8_isolate->GetCurrentContext())
.ToLocalChecked()
->NewInstance(v8_isolate->GetCurrentContext())
.ToLocalChecked();
return *Utils::OpenHandle(*instance);
}
RUNTIME_FUNCTION(Runtime_ClearFunctionFeedback) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
function->ClearTypeFeedbackInfo();
Code* unoptimized = function->shared()->code();
if (unoptimized->kind() == Code::FUNCTION) {
unoptimized->ClearInlineCaches();
}
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_CheckWasmWrapperElision) {
// This only supports the case where the function being exported
// calls an intermediate function, and the intermediate function
// calls exactly one imported function
HandleScope scope(isolate);
CHECK(args.length() == 2);
// It takes two parameters, the first one is the JSFunction,
// The second one is the type
CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
// If type is 0, it means that it is supposed to be a direct call into a WASM
// function
// If type is 1, it means that it is supposed to have wrappers
CONVERT_ARG_HANDLE_CHECKED(Smi, type, 1);
Handle<Code> export_code = handle(function->code());
CHECK(export_code->kind() == Code::JS_TO_WASM_FUNCTION);
int const mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET);
// check the type of the $export_fct
Handle<Code> export_fct;
int count = 0;
for (RelocIterator it(*export_code, mask); !it.done(); it.next()) {
RelocInfo* rinfo = it.rinfo();
Address target_address = rinfo->target_address();
Code* target = Code::GetCodeFromTargetAddress(target_address);
if (target->kind() == Code::WASM_FUNCTION) {
++count;
export_fct = handle(target);
}
}
CHECK(count == 1);
// check the type of the intermediate_fct
Handle<Code> intermediate_fct;
count = 0;
for (RelocIterator it(*export_fct, mask); !it.done(); it.next()) {
RelocInfo* rinfo = it.rinfo();
Address target_address = rinfo->target_address();
Code* target = Code::GetCodeFromTargetAddress(target_address);
if (target->kind() == Code::WASM_FUNCTION) {
++count;
intermediate_fct = handle(target);
}
}
CHECK(count == 1);
// check the type of the imported exported function, it should be also a WASM
// function in our case
Handle<Code> imported_fct;
CHECK(type->value() == 0 || type->value() == 1);
Code::Kind target_kind =
type->value() == 0 ? Code::WASM_FUNCTION : Code::WASM_TO_JS_FUNCTION;
count = 0;
for (RelocIterator it(*intermediate_fct, mask); !it.done(); it.next()) {
RelocInfo* rinfo = it.rinfo();
Address target_address = rinfo->target_address();
Code* target = Code::GetCodeFromTargetAddress(target_address);
if (target->kind() == target_kind) {
++count;
imported_fct = handle(target);
}
}
CHECK_LE(count, 1);
return isolate->heap()->ToBoolean(count == 1);
}
RUNTIME_FUNCTION(Runtime_SetWasmCompileControls) {
HandleScope scope(isolate);
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
CHECK(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(Smi, block_size, 0);
CONVERT_BOOLEAN_ARG_CHECKED(allow_async, 1);
WasmCompileControls& ctrl = (*g_PerIsolateWasmControls.Pointer())[v8_isolate];
ctrl.AllowAnySizeForAsync = allow_async;
ctrl.MaxWasmBufferSize = static_cast<uint32_t>(block_size->value());
isolate->set_allow_wasm_compile_callback(IsWasmCompileAllowed);
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_SetWasmInstantiateControls) {
HandleScope scope(isolate);
CHECK(args.length() == 0);
isolate->set_allow_wasm_instantiate_callback(IsWasmInstantiateAllowed);
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_NotifyContextDisposed) {
HandleScope scope(isolate);
DCHECK_EQ(0, args.length());
isolate->heap()->NotifyContextDisposed(true);
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_SetAllocationTimeout) {
SealHandleScope shs(isolate);
DCHECK(args.length() == 2 || args.length() == 3);
#ifdef DEBUG
CONVERT_INT32_ARG_CHECKED(interval, 0);
CONVERT_INT32_ARG_CHECKED(timeout, 1);
isolate->heap()->set_allocation_timeout(timeout);
FLAG_gc_interval = interval;
if (args.length() == 3) {
// Enable/disable inline allocation if requested.
CONVERT_BOOLEAN_ARG_CHECKED(inline_allocation, 2);
if (inline_allocation) {
isolate->heap()->EnableInlineAllocation();
} else {
isolate->heap()->DisableInlineAllocation();
}
}
#endif
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_DebugPrint) {
SealHandleScope shs(isolate);
DCHECK_EQ(1, args.length());
OFStream os(stdout);
#ifdef DEBUG
if (args[0]->IsString() && isolate->context() != nullptr) {
// If we have a string, assume it's a code "marker"
// and print some interesting cpu debugging info.
JavaScriptFrameIterator it(isolate);
JavaScriptFrame* frame = it.frame();
os << "fp = " << static_cast<void*>(frame->fp())
<< ", sp = " << static_cast<void*>(frame->sp())
<< ", caller_sp = " << static_cast<void*>(frame->caller_sp()) << ": ";
} else {
os << "DebugPrint: ";
}
args[0]->Print(os);
if (args[0]->IsHeapObject()) {
HeapObject::cast(args[0])->map()->Print(os);
}
#else
// ShortPrint is available in release mode. Print is not.
os << Brief(args[0]);
#endif
os << std::endl;
return args[0]; // return TOS
}
RUNTIME_FUNCTION(Runtime_DebugTrace) {
SealHandleScope shs(isolate);
DCHECK_EQ(0, args.length());
isolate->PrintStack(stdout);
return isolate->heap()->undefined_value();
}
// This will not allocate (flatten the string), but it may run
// very slowly for very deeply nested ConsStrings. For debugging use only.
RUNTIME_FUNCTION(Runtime_GlobalPrint) {
SealHandleScope shs(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_CHECKED(String, string, 0);
StringCharacterStream stream(string);
while (stream.HasMore()) {
uint16_t character = stream.GetNext();
PrintF("%c", character);
}
return string;
}
RUNTIME_FUNCTION(Runtime_SystemBreak) {
// The code below doesn't create handles, but when breaking here in GDB
// having a handle scope might be useful.
HandleScope scope(isolate);
DCHECK_EQ(0, args.length());
base::OS::DebugBreak();
return isolate->heap()->undefined_value();
}
// Sets a v8 flag.
RUNTIME_FUNCTION(Runtime_SetFlags) {
SealHandleScope shs(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_CHECKED(String, arg, 0);
std::unique_ptr<char[]> flags =
arg->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
FlagList::SetFlagsFromString(flags.get(), StrLength(flags.get()));
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_Abort) {
SealHandleScope shs(isolate);
DCHECK_EQ(1, args.length());
CONVERT_SMI_ARG_CHECKED(message_id, 0);
const char* message =
GetBailoutReason(static_cast<BailoutReason>(message_id));
base::OS::PrintError("abort: %s\n", message);
isolate->PrintStack(stderr);
base::OS::Abort();
UNREACHABLE();
return NULL;
}
RUNTIME_FUNCTION(Runtime_AbortJS) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(String, message, 0);
base::OS::PrintError("abort: %s\n", message->ToCString().get());
isolate->PrintStack(stderr);
base::OS::Abort();
UNREACHABLE();
return NULL;
}
RUNTIME_FUNCTION(Runtime_NativeScriptsCount) {
DCHECK_EQ(0, args.length());
return Smi::FromInt(Natives::GetBuiltinsCount());
}
// TODO(5510): remove this.
RUNTIME_FUNCTION(Runtime_GetV8Version) {
HandleScope scope(isolate);
DCHECK_EQ(0, args.length());
const char* version_string = v8::V8::GetVersion();
return *isolate->factory()->NewStringFromAsciiChecked(version_string);
}
RUNTIME_FUNCTION(Runtime_DisassembleFunction) {
HandleScope scope(isolate);
#ifdef DEBUG
DCHECK_EQ(1, args.length());
// Get the function and make sure it is compiled.
CONVERT_ARG_HANDLE_CHECKED(JSFunction, func, 0);
if (!Compiler::Compile(func, Compiler::KEEP_EXCEPTION)) {
return isolate->heap()->exception();
}
OFStream os(stdout);
func->code()->Print(os);
os << std::endl;
#endif // DEBUG
return isolate->heap()->undefined_value();
}
namespace {
int StackSize(Isolate* isolate) {
int n = 0;
for (JavaScriptFrameIterator it(isolate); !it.done(); it.Advance()) n++;
return n;
}
void PrintIndentation(Isolate* isolate) {
const int nmax = 80;
int n = StackSize(isolate);
if (n <= nmax) {
PrintF("%4d:%*s", n, n, "");
} else {
PrintF("%4d:%*s", n, nmax, "...");
}
}
} // namespace
RUNTIME_FUNCTION(Runtime_TraceEnter) {
SealHandleScope shs(isolate);
DCHECK_EQ(0, args.length());
PrintIndentation(isolate);
JavaScriptFrame::PrintTop(isolate, stdout, true, false);
PrintF(" {\n");
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_TraceExit) {
SealHandleScope shs(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_CHECKED(Object, obj, 0);
PrintIndentation(isolate);
PrintF("} -> ");
obj->ShortPrint();
PrintF("\n");
return obj; // return TOS
}
RUNTIME_FUNCTION(Runtime_TraceTailCall) {
SealHandleScope shs(isolate);
DCHECK_EQ(0, args.length());
PrintIndentation(isolate);
PrintF("} -> tail call ->\n");
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_GetExceptionDetails) {
HandleScope shs(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSObject, exception_obj, 0);
Factory* factory = isolate->factory();
Handle<JSMessageObject> message_obj =
isolate->CreateMessage(exception_obj, nullptr);
Handle<JSObject> message = factory->NewJSObject(isolate->object_function());
Handle<String> key;
Handle<Object> value;
key = factory->NewStringFromAsciiChecked("start_pos");
value = handle(Smi::FromInt(message_obj->start_position()), isolate);
JSObject::SetProperty(message, key, value, STRICT).Assert();
key = factory->NewStringFromAsciiChecked("end_pos");
value = handle(Smi::FromInt(message_obj->end_position()), isolate);
JSObject::SetProperty(message, key, value, STRICT).Assert();
return *message;
}
RUNTIME_FUNCTION(Runtime_HaveSameMap) {
SealHandleScope shs(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_CHECKED(JSObject, obj1, 0);
CONVERT_ARG_CHECKED(JSObject, obj2, 1);
return isolate->heap()->ToBoolean(obj1->map() == obj2->map());
}
RUNTIME_FUNCTION(Runtime_InNewSpace) {
SealHandleScope shs(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_CHECKED(Object, obj, 0);
return isolate->heap()->ToBoolean(isolate->heap()->InNewSpace(obj));
}
RUNTIME_FUNCTION(Runtime_IsAsmWasmCode) {
SealHandleScope shs(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_CHECKED(JSFunction, function, 0);
if (!function->shared()->HasAsmWasmData()) {
// Doesn't have wasm data.
return isolate->heap()->false_value();
}
if (function->shared()->code() !=
isolate->builtins()->builtin(Builtins::kInstantiateAsmJs)) {
// Hasn't been compiled yet.
return isolate->heap()->false_value();
}
return isolate->heap()->true_value();
}
namespace {
bool DisallowCodegenFromStringsCallback(v8::Local<v8::Context> context) {
return false;
}
}
RUNTIME_FUNCTION(Runtime_DisallowCodegenFromStrings) {
SealHandleScope shs(isolate);
DCHECK_EQ(0, args.length());
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
v8_isolate->SetAllowCodeGenerationFromStringsCallback(
DisallowCodegenFromStringsCallback);
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_IsWasmCode) {
SealHandleScope shs(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_CHECKED(JSFunction, function, 0);
bool is_js_to_wasm = function->code()->kind() == Code::JS_TO_WASM_FUNCTION;
return isolate->heap()->ToBoolean(is_js_to_wasm);
}
#define ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(Name) \
RUNTIME_FUNCTION(Runtime_Has##Name) { \
CONVERT_ARG_CHECKED(JSObject, obj, 0); \
return isolate->heap()->ToBoolean(obj->Has##Name()); \
}
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastSmiElements)
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastObjectElements)
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastSmiOrObjectElements)
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastDoubleElements)
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastHoleyElements)
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(DictionaryElements)
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(SloppyArgumentsElements)
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FixedTypedArrayElements)
// Properties test sitting with elements tests - not fooling anyone.
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastProperties)
#undef ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION
#define FIXED_TYPED_ARRAYS_CHECK_RUNTIME_FUNCTION(Type, type, TYPE, ctype, s) \
RUNTIME_FUNCTION(Runtime_HasFixed##Type##Elements) { \
CONVERT_ARG_CHECKED(JSObject, obj, 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_SpeciesProtector) {
SealHandleScope shs(isolate);
DCHECK_EQ(0, args.length());
return isolate->heap()->ToBoolean(isolate->IsArraySpeciesLookupChainIntact());
}
#define CONVERT_ARG_HANDLE_CHECKED_2(Type, name, index) \
CHECK(Type::Is##Type(args[index])); \
Handle<Type> name = args.at<Type>(index);
// Take a compiled wasm module, serialize it and copy the buffer into an array
// buffer, which is then returned.
RUNTIME_FUNCTION(Runtime_SerializeWasmModule) {
HandleScope shs(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED_2(WasmModuleObject, module_obj, 0);
Handle<WasmCompiledModule> orig(module_obj->compiled_module());
std::unique_ptr<ScriptData> data =
WasmCompiledModuleSerializer::SerializeWasmModule(isolate, orig);
void* buff = isolate->array_buffer_allocator()->Allocate(data->length());
Handle<JSArrayBuffer> ret = isolate->factory()->NewJSArrayBuffer();
JSArrayBuffer::Setup(ret, isolate, false, buff, data->length());
memcpy(buff, data->data(), data->length());
return *ret;
}
// Take an array buffer and attempt to reconstruct a compiled wasm module.
// Return undefined if unsuccessful.
RUNTIME_FUNCTION(Runtime_DeserializeWasmModule) {
HandleScope shs(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, buffer, 0);
CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, wire_bytes, 1);
Address mem_start = static_cast<Address>(buffer->backing_store());
int mem_size = static_cast<int>(buffer->byte_length()->Number());
// DeserializeWasmModule will allocate. We assume JSArrayBuffer doesn't
// get relocated.
ScriptData sc(mem_start, mem_size);
bool already_external = wire_bytes->is_external();
if (!already_external) {
wire_bytes->set_is_external(true);
isolate->heap()->UnregisterArrayBuffer(*wire_bytes);
}
MaybeHandle<FixedArray> maybe_compiled_module =
WasmCompiledModuleSerializer::DeserializeWasmModule(
isolate, &sc,
Vector<const uint8_t>(
reinterpret_cast<uint8_t*>(wire_bytes->backing_store()),
static_cast<int>(wire_bytes->byte_length()->Number())));
if (!already_external) {
wire_bytes->set_is_external(false);
isolate->heap()->RegisterNewArrayBuffer(*wire_bytes);
}
Handle<FixedArray> compiled_module;
if (!maybe_compiled_module.ToHandle(&compiled_module)) {
return isolate->heap()->undefined_value();
}
return *WasmModuleObject::New(
isolate, Handle<WasmCompiledModule>::cast(compiled_module));
}
RUNTIME_FUNCTION(Runtime_ValidateWasmInstancesChain) {
HandleScope shs(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED_2(WasmModuleObject, module_obj, 0);
CONVERT_ARG_HANDLE_CHECKED(Smi, instance_count, 1);
wasm::testing::ValidateInstancesChain(isolate, module_obj,
instance_count->value());
return isolate->heap()->ToBoolean(true);
}
RUNTIME_FUNCTION(Runtime_ValidateWasmModuleState) {
HandleScope shs(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED_2(WasmModuleObject, module_obj, 0);
wasm::testing::ValidateModuleState(isolate, module_obj);
return isolate->heap()->ToBoolean(true);
}
RUNTIME_FUNCTION(Runtime_ValidateWasmOrphanedInstance) {
HandleScope shs(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED_2(WasmInstanceObject, instance, 0);
wasm::testing::ValidateOrphanedInstance(isolate, instance);
return isolate->heap()->ToBoolean(true);
}
RUNTIME_FUNCTION(Runtime_Verify) {
HandleScope shs(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(Object, object, 0);
#ifdef VERIFY_HEAP
object->ObjectVerify();
#else
CHECK(object->IsObject());
if (object->IsHeapObject()) {
CHECK(HeapObject::cast(*object)->map()->IsMap());
} else {
CHECK(object->IsSmi());
}
#endif
return isolate->heap()->ToBoolean(true);
}
} // namespace internal
} // namespace v8