// 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 <sstream>
|
|
#include "src/api-inl.h"
|
#include "src/arguments-inl.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/isolate-inl.h"
|
#include "src/runtime-profiler.h"
|
#include "src/snapshot/natives.h"
|
#include "src/trap-handler/trap-handler.h"
|
#include "src/wasm/memory-tracing.h"
|
#include "src/wasm/module-compiler.h"
|
#include "src/wasm/wasm-engine.h"
|
#include "src/wasm/wasm-module.h"
|
#include "src/wasm/wasm-objects-inl.h"
|
#include "src/wasm/wasm-serialization.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) ||
|
(value->IsArrayBuffer() &&
|
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,
|
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->GetWasmWireBytesRef().size) <=
|
ctrls.MaxWasmBufferSize;
|
}
|
|
v8::Local<v8::Value> NewRangeException(v8::Isolate* isolate,
|
const char* message) {
|
return v8::Exception::RangeError(
|
v8::String::NewFromOneByte(isolate,
|
reinterpret_cast<const uint8_t*>(message),
|
v8::NewStringType::kNormal)
|
.ToLocalChecked());
|
}
|
|
void ThrowRangeException(v8::Isolate* isolate, const char* message) {
|
isolate->ThrowException(NewRangeException(isolate, message));
|
}
|
|
bool WasmModuleOverride(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
if (IsWasmCompileAllowed(args.GetIsolate(), args[0], false)) return false;
|
ThrowRangeException(args.GetIsolate(), "Sync compile not allowed");
|
return true;
|
}
|
|
bool WasmInstanceOverride(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
if (IsWasmInstantiateAllowed(args.GetIsolate(), args[0], false)) return false;
|
ThrowRangeException(args.GetIsolate(), "Sync instantiate not allowed");
|
return true;
|
}
|
|
} // 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_ConstructConsString) {
|
HandleScope scope(isolate);
|
DCHECK_EQ(2, args.length());
|
CONVERT_ARG_HANDLE_CHECKED(String, left, 0);
|
CONVERT_ARG_HANDLE_CHECKED(String, right, 1);
|
|
CHECK(left->IsOneByteRepresentation());
|
CHECK(right->IsOneByteRepresentation());
|
|
const bool kIsOneByte = true;
|
const int length = left->length() + right->length();
|
return *isolate->factory()->NewConsString(left, right, length, kIsOneByte);
|
}
|
|
RUNTIME_FUNCTION(Runtime_ConstructSlicedString) {
|
HandleScope scope(isolate);
|
DCHECK_EQ(2, args.length());
|
CONVERT_ARG_HANDLE_CHECKED(String, string, 0);
|
CONVERT_ARG_HANDLE_CHECKED(Smi, index, 1);
|
|
CHECK(string->IsOneByteRepresentation());
|
CHECK_LT(index->value(), string->length());
|
|
Handle<String> sliced_string = isolate->factory()->NewSubString(
|
string, index->value(), string->length());
|
CHECK(sliced_string->IsSlicedString());
|
return *sliced_string;
|
}
|
|
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 ReadOnlyRoots(isolate).undefined_value();
|
}
|
Handle<JSFunction> function = Handle<JSFunction>::cast(function_object);
|
|
// If the function is not optimized, just return.
|
if (!function->IsOptimized()) return ReadOnlyRoots(isolate).undefined_value();
|
|
Deoptimizer::DeoptimizeFunction(*function);
|
|
return ReadOnlyRoots(isolate).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(it.frame()->function(), isolate);
|
if (function.is_null()) return ReadOnlyRoots(isolate).undefined_value();
|
|
// If the function is not optimized, just return.
|
if (!function->IsOptimized()) return ReadOnlyRoots(isolate).undefined_value();
|
|
Deoptimizer::DeoptimizeFunction(*function);
|
|
return ReadOnlyRoots(isolate).undefined_value();
|
}
|
|
|
RUNTIME_FUNCTION(Runtime_RunningInSimulator) {
|
SealHandleScope shs(isolate);
|
DCHECK_EQ(0, args.length());
|
#if defined(USE_SIMULATOR)
|
return ReadOnlyRoots(isolate).true_value();
|
#else
|
return ReadOnlyRoots(isolate).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 ReadOnlyRoots(isolate).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 ReadOnlyRoots(isolate).undefined_value();
|
}
|
Handle<JSFunction> function = Handle<JSFunction>::cast(function_object);
|
|
// The following conditions were lifted (in part) from the DCHECK inside
|
// JSFunction::MarkForOptimization().
|
|
if (!function->shared()->allows_lazy_compilation()) {
|
return ReadOnlyRoots(isolate).undefined_value();
|
}
|
|
// If function isn't compiled, compile it now.
|
if (!function->shared()->is_compiled() &&
|
!Compiler::Compile(function, Compiler::CLEAR_EXCEPTION)) {
|
return ReadOnlyRoots(isolate).undefined_value();
|
}
|
|
// If the function is already optimized, just return.
|
if (function->IsOptimized() || function->shared()->HasAsmWasmData()) {
|
return ReadOnlyRoots(isolate).undefined_value();
|
}
|
|
// If the function has optimized code, ensure that we check for it and return.
|
if (function->HasOptimizedCode()) {
|
DCHECK(function->ChecksOptimizationMarker());
|
return ReadOnlyRoots(isolate).undefined_value();
|
}
|
|
ConcurrencyMode concurrency_mode = ConcurrencyMode::kNotConcurrent;
|
if (args.length() == 2) {
|
CONVERT_ARG_HANDLE_CHECKED(String, type, 1);
|
if (type->IsOneByteEqualTo(STATIC_CHAR_VECTOR("concurrent")) &&
|
isolate->concurrent_recompilation_enabled()) {
|
concurrency_mode = ConcurrencyMode::kConcurrent;
|
}
|
}
|
if (FLAG_trace_opt) {
|
PrintF("[manually marking ");
|
function->ShortPrint();
|
PrintF(" for %s optimization]\n",
|
concurrency_mode == ConcurrencyMode::kConcurrent ? "concurrent"
|
: "non-concurrent");
|
}
|
|
// This function may not have been lazily compiled yet, even though its shared
|
// function has.
|
if (!function->is_compiled()) {
|
DCHECK(function->shared()->IsInterpreted());
|
function->set_code(*BUILTIN_CODE(isolate, InterpreterEntryTrampoline));
|
}
|
|
JSFunction::EnsureFeedbackVector(function);
|
function->MarkForOptimization(concurrency_mode);
|
|
return ReadOnlyRoots(isolate).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(it.frame()->function(), isolate);
|
if (function.is_null()) return ReadOnlyRoots(isolate).undefined_value();
|
|
// If the function is already optimized, just return.
|
if (function->IsOptimized()) return ReadOnlyRoots(isolate).undefined_value();
|
|
// Ensure that the function is marked for non-concurrent optimization, so that
|
// subsequent runs don't also optimize.
|
if (!function->HasOptimizedCode()) {
|
if (FLAG_trace_osr) {
|
PrintF("[OSR - OptimizeOsr marking ");
|
function->ShortPrint();
|
PrintF(" for non-concurrent optimization]\n");
|
}
|
function->MarkForOptimization(ConcurrencyMode::kNotConcurrent);
|
}
|
|
// Make the profiler arm all back edges in unoptimized code.
|
if (it.frame()->type() == StackFrame::INTERPRETED) {
|
isolate->runtime_profiler()->AttemptOnStackReplacement(
|
it.frame(), AbstractCode::kMaxLoopNestingMarker);
|
}
|
|
return ReadOnlyRoots(isolate).undefined_value();
|
}
|
|
|
RUNTIME_FUNCTION(Runtime_NeverOptimizeFunction) {
|
HandleScope scope(isolate);
|
DCHECK_EQ(1, args.length());
|
// 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 ReadOnlyRoots(isolate).undefined_value();
|
}
|
Handle<JSFunction> function = Handle<JSFunction>::cast(function_object);
|
function->shared()->DisableOptimization(
|
BailoutReason::kOptimizationDisabledForTest);
|
return ReadOnlyRoots(isolate).undefined_value();
|
}
|
|
RUNTIME_FUNCTION(Runtime_GetOptimizationStatus) {
|
HandleScope scope(isolate);
|
DCHECK(args.length() == 1 || args.length() == 2);
|
int status = 0;
|
if (!isolate->use_optimizer()) {
|
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 ReadOnlyRoots(isolate).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->IsMarkedForOptimization()) {
|
status |= static_cast<int>(OptimizationStatus::kMarkedForOptimization);
|
} else if (function->IsInOptimizationQueue()) {
|
status |=
|
static_cast<int>(OptimizationStatus::kMarkedForConcurrentOptimization);
|
} else if (function->IsInOptimizationQueue()) {
|
status |= static_cast<int>(OptimizationStatus::kOptimizingConcurrently);
|
}
|
|
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);
|
}
|
|
// Additionally, detect activations of this frame on the stack, and report the
|
// status of the topmost frame.
|
JavaScriptFrame* frame = nullptr;
|
JavaScriptFrameIterator 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_optimized()) {
|
status |=
|
static_cast<int>(OptimizationStatus::kTopmostFrameIsTurboFanned);
|
}
|
}
|
|
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 ReadOnlyRoots(isolate).undefined_value();
|
}
|
|
RUNTIME_FUNCTION(Runtime_GetDeoptCount) {
|
HandleScope scope(isolate);
|
DCHECK_EQ(1, args.length());
|
CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
|
// Functions without a feedback vector have never deoptimized.
|
if (!function->has_feedback_vector()) return Smi::kZero;
|
return Smi::FromInt(function->feedback_vector()->deopt_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();
|
return ReadOnlyRoots(isolate).undefined_value();
|
}
|
|
RUNTIME_FUNCTION(Runtime_SetWasmCompileControls) {
|
HandleScope scope(isolate);
|
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
|
CHECK_EQ(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());
|
v8_isolate->SetWasmModuleCallback(WasmModuleOverride);
|
return ReadOnlyRoots(isolate).undefined_value();
|
}
|
|
RUNTIME_FUNCTION(Runtime_SetWasmInstantiateControls) {
|
HandleScope scope(isolate);
|
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
|
CHECK_EQ(args.length(), 0);
|
v8_isolate->SetWasmInstanceCallback(WasmInstanceOverride);
|
return ReadOnlyRoots(isolate).undefined_value();
|
}
|
|
RUNTIME_FUNCTION(Runtime_NotifyContextDisposed) {
|
HandleScope scope(isolate);
|
DCHECK_EQ(0, args.length());
|
isolate->heap()->NotifyContextDisposed(true);
|
return ReadOnlyRoots(isolate).undefined_value();
|
}
|
|
|
RUNTIME_FUNCTION(Runtime_SetAllocationTimeout) {
|
SealHandleScope shs(isolate);
|
DCHECK(args.length() == 2 || args.length() == 3);
|
#ifdef V8_ENABLE_ALLOCATION_TIMEOUT
|
CONVERT_INT32_ARG_CHECKED(timeout, 1);
|
isolate->heap()->set_allocation_timeout(timeout);
|
#endif
|
#ifdef DEBUG
|
CONVERT_INT32_ARG_CHECKED(interval, 0);
|
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 ReadOnlyRoots(isolate).undefined_value();
|
}
|
|
|
RUNTIME_FUNCTION(Runtime_DebugPrint) {
|
SealHandleScope shs(isolate);
|
DCHECK_EQ(1, args.length());
|
|
// Hack: The argument is passed as Object* but here it's really a
|
// MaybeObject*.
|
MaybeObject* maybe_object = reinterpret_cast<MaybeObject*>(args[0]);
|
|
StdoutStream os;
|
if (maybe_object->IsClearedWeakHeapObject()) {
|
os << "[weak cleared]";
|
} else {
|
Object* object;
|
bool weak = false;
|
if (maybe_object->IsWeakHeapObject()) {
|
weak = true;
|
object = maybe_object->ToWeakHeapObject();
|
} else {
|
// Strong reference or SMI.
|
object = maybe_object->ToObject();
|
}
|
|
#ifdef DEBUG
|
if (object->IsString() && isolate->context() != nullptr) {
|
DCHECK(!weak);
|
// If we have a string, assume it's a code "marker"
|
// and print some interesting cpu debugging info.
|
object->Print(os);
|
JavaScriptFrameIterator it(isolate);
|
JavaScriptFrame* frame = it.frame();
|
os << "fp = " << reinterpret_cast<void*>(frame->fp())
|
<< ", sp = " << reinterpret_cast<void*>(frame->sp())
|
<< ", caller_sp = " << reinterpret_cast<void*>(frame->caller_sp())
|
<< ": ";
|
} else {
|
os << "DebugPrint: ";
|
if (weak) {
|
os << "[weak] ";
|
}
|
object->Print(os);
|
}
|
if (object->IsHeapObject()) {
|
HeapObject::cast(object)->map()->Print(os);
|
}
|
#else
|
if (weak) {
|
os << "[weak] ";
|
}
|
// ShortPrint is available in release mode. Print is not.
|
os << Brief(object);
|
#endif
|
}
|
os << std::endl;
|
|
return args[0]; // return TOS
|
}
|
|
RUNTIME_FUNCTION(Runtime_PrintWithNameForAssert) {
|
SealHandleScope shs(isolate);
|
DCHECK_EQ(2, args.length());
|
|
CONVERT_ARG_CHECKED(String, name, 0);
|
|
PrintF(" * ");
|
StringCharacterStream stream(name);
|
while (stream.HasMore()) {
|
uint16_t character = stream.GetNext();
|
PrintF("%c", character);
|
}
|
PrintF(": ");
|
args[1]->ShortPrint();
|
PrintF("\n");
|
|
return ReadOnlyRoots(isolate).undefined_value();
|
}
|
|
RUNTIME_FUNCTION(Runtime_DebugTrace) {
|
SealHandleScope shs(isolate);
|
DCHECK_EQ(0, args.length());
|
isolate->PrintStack(stdout);
|
return ReadOnlyRoots(isolate).undefined_value();
|
}
|
|
RUNTIME_FUNCTION(Runtime_DebugTrackRetainingPath) {
|
HandleScope scope(isolate);
|
DCHECK_LE(1, args.length());
|
DCHECK_GE(2, args.length());
|
if (!FLAG_track_retaining_path) {
|
PrintF("DebugTrackRetainingPath requires --track-retaining-path flag.\n");
|
} else {
|
CONVERT_ARG_HANDLE_CHECKED(HeapObject, object, 0);
|
RetainingPathOption option = RetainingPathOption::kDefault;
|
if (args.length() == 2) {
|
CONVERT_ARG_HANDLE_CHECKED(String, str, 1);
|
const char track_ephemeron_path[] = "track-ephemeron-path";
|
if (str->IsOneByteEqualTo(STATIC_CHAR_VECTOR(track_ephemeron_path))) {
|
option = RetainingPathOption::kTrackEphemeronPath;
|
} else if (str->length() != 0) {
|
PrintF("Unexpected second argument of DebugTrackRetainingPath.\n");
|
PrintF("Expected an empty string or '%s', got '%s'.\n",
|
track_ephemeron_path, str->ToCString().get());
|
}
|
}
|
isolate->heap()->AddRetainingPathTarget(object, option);
|
}
|
return ReadOnlyRoots(isolate).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 ReadOnlyRoots(isolate).undefined_value();
|
}
|
|
|
RUNTIME_FUNCTION(Runtime_SetForceSlowPath) {
|
SealHandleScope shs(isolate);
|
DCHECK_EQ(1, args.length());
|
CONVERT_ARG_CHECKED(Object, arg, 0);
|
if (arg->IsTrue(isolate)) {
|
isolate->set_force_slow_path(true);
|
} else {
|
DCHECK(arg->IsFalse(isolate));
|
isolate->set_force_slow_path(false);
|
}
|
return ReadOnlyRoots(isolate).undefined_value();
|
}
|
|
RUNTIME_FUNCTION(Runtime_Abort) {
|
SealHandleScope shs(isolate);
|
DCHECK_EQ(1, args.length());
|
CONVERT_SMI_ARG_CHECKED(message_id, 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);
|
DCHECK_EQ(1, args.length());
|
CONVERT_ARG_HANDLE_CHECKED(String, message, 0);
|
if (FLAG_disable_abortjs) {
|
base::OS::PrintError("[disabled] abort: %s\n", message->ToCString().get());
|
return nullptr;
|
}
|
base::OS::PrintError("abort: %s\n", message->ToCString().get());
|
isolate->PrintStack(stderr);
|
base::OS::Abort();
|
UNREACHABLE();
|
}
|
|
|
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 (!func->is_compiled() &&
|
!Compiler::Compile(func, Compiler::KEEP_EXCEPTION)) {
|
return ReadOnlyRoots(isolate).exception();
|
}
|
StdoutStream os;
|
func->code()->Print(os);
|
os << std::endl;
|
#endif // DEBUG
|
return ReadOnlyRoots(isolate).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 ReadOnlyRoots(isolate).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_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(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 ReadOnlyRoots(isolate).false_value();
|
}
|
if (function->shared()->HasBuiltinId() &&
|
function->shared()->builtin_id() == Builtins::kInstantiateAsmJs) {
|
// Hasn't been compiled yet.
|
return ReadOnlyRoots(isolate).false_value();
|
}
|
return ReadOnlyRoots(isolate).true_value();
|
}
|
|
namespace {
|
bool DisallowCodegenFromStringsCallback(v8::Local<v8::Context> context,
|
v8::Local<v8::String> source) {
|
return false;
|
}
|
}
|
|
RUNTIME_FUNCTION(Runtime_DisallowCodegenFromStrings) {
|
SealHandleScope shs(isolate);
|
DCHECK_EQ(1, args.length());
|
CONVERT_BOOLEAN_ARG_CHECKED(flag, 0);
|
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
|
v8_isolate->SetAllowCodeGenerationFromStringsCallback(
|
flag ? DisallowCodegenFromStringsCallback : nullptr);
|
return ReadOnlyRoots(isolate).undefined_value();
|
}
|
|
RUNTIME_FUNCTION(Runtime_DisallowWasmCodegen) {
|
SealHandleScope shs(isolate);
|
DCHECK_EQ(1, args.length());
|
CONVERT_BOOLEAN_ARG_CHECKED(flag, 0);
|
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
|
v8_isolate->SetAllowWasmCodeGenerationCallback(
|
flag ? DisallowCodegenFromStringsCallback : nullptr);
|
return ReadOnlyRoots(isolate).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);
|
}
|
|
RUNTIME_FUNCTION(Runtime_IsWasmTrapHandlerEnabled) {
|
DisallowHeapAllocation no_gc;
|
DCHECK_EQ(0, args.length());
|
return isolate->heap()->ToBoolean(trap_handler::IsTrapHandlerEnabled());
|
}
|
|
RUNTIME_FUNCTION(Runtime_GetWasmRecoveredTrapCount) {
|
HandleScope shs(isolate);
|
DCHECK_EQ(0, args.length());
|
size_t trap_count = trap_handler::GetRecoveredTrapCount();
|
return *isolate->factory()->NewNumberFromSize(trap_count);
|
}
|
|
namespace {
|
bool EnableWasmThreads(v8::Local<v8::Context> context) { return true; }
|
|
bool DisableWasmThreads(v8::Local<v8::Context> context) { return false; }
|
} // namespace
|
|
// This runtime function enables WebAssembly threads through an embedder
|
// callback and thereby bypasses the value in FLAG_experimental_wasm_threads.
|
RUNTIME_FUNCTION(Runtime_SetWasmThreadsEnabled) {
|
DCHECK_EQ(1, args.length());
|
CONVERT_BOOLEAN_ARG_CHECKED(flag, 0);
|
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
|
v8_isolate->SetWasmThreadsEnabledCallback(flag ? EnableWasmThreads
|
: DisableWasmThreads);
|
return ReadOnlyRoots(isolate).undefined_value();
|
}
|
|
#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(FastElements)
|
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(SmiElements)
|
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(ObjectElements)
|
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(SmiOrObjectElements)
|
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(DoubleElements)
|
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(HoleyElements)
|
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(DictionaryElements)
|
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(SloppyArgumentsElements)
|
// 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) \
|
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_ArraySpeciesProtector) {
|
SealHandleScope shs(isolate);
|
DCHECK_EQ(0, args.length());
|
return isolate->heap()->ToBoolean(isolate->IsArraySpeciesLookupChainIntact());
|
}
|
|
RUNTIME_FUNCTION(Runtime_TypedArraySpeciesProtector) {
|
SealHandleScope shs(isolate);
|
DCHECK_EQ(0, args.length());
|
return isolate->heap()->ToBoolean(
|
isolate->IsTypedArraySpeciesLookupChainIntact());
|
}
|
|
RUNTIME_FUNCTION(Runtime_PromiseSpeciesProtector) {
|
SealHandleScope shs(isolate);
|
DCHECK_EQ(0, args.length());
|
return isolate->heap()->ToBoolean(
|
isolate->IsPromiseSpeciesLookupChainIntact());
|
}
|
|
// Take a compiled wasm module and serialize it into an array buffer, which is
|
// then returned.
|
RUNTIME_FUNCTION(Runtime_SerializeWasmModule) {
|
HandleScope scope(isolate);
|
DCHECK_EQ(1, args.length());
|
CONVERT_ARG_HANDLE_CHECKED(WasmModuleObject, module_obj, 0);
|
|
wasm::NativeModule* native_module = module_obj->native_module();
|
wasm::WasmSerializer wasm_serializer(isolate, native_module);
|
size_t compiled_size = wasm_serializer.GetSerializedNativeModuleSize();
|
void* array_data = isolate->array_buffer_allocator()->Allocate(compiled_size);
|
Handle<JSArrayBuffer> array_buffer = isolate->factory()->NewJSArrayBuffer();
|
JSArrayBuffer::Setup(array_buffer, isolate, false, array_data, compiled_size);
|
if (!array_data ||
|
!wasm_serializer.SerializeNativeModule(
|
{reinterpret_cast<uint8_t*>(array_data), compiled_size})) {
|
return ReadOnlyRoots(isolate).undefined_value();
|
}
|
return *array_buffer;
|
}
|
|
// Take an array buffer and attempt to reconstruct a compiled wasm module.
|
// Return undefined if unsuccessful.
|
RUNTIME_FUNCTION(Runtime_DeserializeWasmModule) {
|
HandleScope scope(isolate);
|
DCHECK_EQ(2, args.length());
|
CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, buffer, 0);
|
CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, wire_bytes, 1);
|
|
// Note that {wasm::DeserializeNativeModule} will allocate. We assume the
|
// JSArrayBuffer backing store doesn't get relocated.
|
MaybeHandle<WasmModuleObject> maybe_module_object =
|
wasm::DeserializeNativeModule(
|
isolate,
|
{reinterpret_cast<uint8_t*>(buffer->backing_store()),
|
static_cast<size_t>(buffer->byte_length()->Number())},
|
{reinterpret_cast<uint8_t*>(wire_bytes->backing_store()),
|
static_cast<size_t>(wire_bytes->byte_length()->Number())});
|
Handle<WasmModuleObject> module_object;
|
if (!maybe_module_object.ToHandle(&module_object)) {
|
return ReadOnlyRoots(isolate).undefined_value();
|
}
|
return *module_object;
|
}
|
|
RUNTIME_FUNCTION(Runtime_HeapObjectVerify) {
|
HandleScope shs(isolate);
|
DCHECK_EQ(1, args.length());
|
CONVERT_ARG_HANDLE_CHECKED(Object, object, 0);
|
#ifdef VERIFY_HEAP
|
object->ObjectVerify(isolate);
|
#else
|
CHECK(object->IsObject());
|
if (object->IsHeapObject()) {
|
CHECK(HeapObject::cast(*object)->map()->IsMap());
|
} else {
|
CHECK(object->IsSmi());
|
}
|
#endif
|
return isolate->heap()->ToBoolean(true);
|
}
|
|
RUNTIME_FUNCTION(Runtime_WasmGetNumberOfInstances) {
|
SealHandleScope shs(isolate);
|
DCHECK_EQ(1, args.length());
|
CONVERT_ARG_HANDLE_CHECKED(WasmModuleObject, module_obj, 0);
|
int instance_count = 0;
|
WeakArrayList* weak_instance_list = module_obj->weak_instance_list();
|
for (int i = 0; i < weak_instance_list->length(); ++i) {
|
if (weak_instance_list->Get(i)->IsWeakHeapObject()) instance_count++;
|
}
|
return Smi::FromInt(instance_count);
|
}
|
|
RUNTIME_FUNCTION(Runtime_WasmNumInterpretedCalls) {
|
DCHECK_EQ(1, args.length());
|
HandleScope scope(isolate);
|
CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
|
if (!instance->has_debug_info()) return 0;
|
uint64_t num = instance->debug_info()->NumInterpretedCalls();
|
return *isolate->factory()->NewNumberFromSize(static_cast<size_t>(num));
|
}
|
|
RUNTIME_FUNCTION(Runtime_RedirectToWasmInterpreter) {
|
DCHECK_EQ(2, args.length());
|
HandleScope scope(isolate);
|
CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
|
CONVERT_SMI_ARG_CHECKED(function_index, 1);
|
Handle<WasmDebugInfo> debug_info =
|
WasmInstanceObject::GetOrCreateDebugInfo(instance);
|
WasmDebugInfo::RedirectToInterpreter(debug_info,
|
Vector<int>(&function_index, 1));
|
return ReadOnlyRoots(isolate).undefined_value();
|
}
|
|
RUNTIME_FUNCTION(Runtime_WasmTraceMemory) {
|
HandleScope scope(isolate);
|
DCHECK_EQ(1, args.length());
|
CONVERT_ARG_CHECKED(Smi, info_addr, 0);
|
|
wasm::MemoryTracingInfo* info =
|
reinterpret_cast<wasm::MemoryTracingInfo*>(info_addr);
|
|
// Find the caller wasm frame.
|
StackTraceFrameIterator it(isolate);
|
DCHECK(!it.done());
|
DCHECK(it.is_wasm());
|
WasmCompiledFrame* frame = WasmCompiledFrame::cast(it.frame());
|
|
uint8_t* mem_start = reinterpret_cast<uint8_t*>(
|
frame->wasm_instance()->memory_object()->array_buffer()->backing_store());
|
int func_index = frame->function_index();
|
int pos = frame->position();
|
// TODO(titzer): eliminate dependency on WasmModule definition here.
|
int func_start =
|
frame->wasm_instance()->module()->functions[func_index].code.offset();
|
wasm::ExecutionTier tier = frame->wasm_code()->is_liftoff()
|
? wasm::ExecutionTier::kBaseline
|
: wasm::ExecutionTier::kOptimized;
|
wasm::TraceMemoryOperation(tier, info, func_index, pos - func_start,
|
mem_start);
|
return ReadOnlyRoots(isolate).undefined_value();
|
}
|
|
RUNTIME_FUNCTION(Runtime_WasmTierUpFunction) {
|
HandleScope scope(isolate);
|
DCHECK_EQ(2, args.length());
|
CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
|
CONVERT_SMI_ARG_CHECKED(function_index, 1);
|
if (!isolate->wasm_engine()->CompileFunction(
|
isolate, instance->module_object()->native_module(), function_index,
|
wasm::ExecutionTier::kOptimized)) {
|
return ReadOnlyRoots(isolate).exception();
|
}
|
return ReadOnlyRoots(isolate).undefined_value();
|
}
|
|
RUNTIME_FUNCTION(Runtime_IsLiftoffFunction) {
|
HandleScope scope(isolate);
|
DCHECK_EQ(1, args.length());
|
CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
|
CHECK(WasmExportedFunction::IsWasmExportedFunction(*function));
|
Handle<WasmExportedFunction> exp_fun =
|
Handle<WasmExportedFunction>::cast(function);
|
wasm::NativeModule* native_module =
|
exp_fun->instance()->module_object()->native_module();
|
uint32_t func_index = exp_fun->function_index();
|
return isolate->heap()->ToBoolean(
|
native_module->has_code(func_index) &&
|
native_module->code(func_index)->is_liftoff());
|
}
|
|
RUNTIME_FUNCTION(Runtime_CompleteInobjectSlackTracking) {
|
HandleScope scope(isolate);
|
DCHECK_EQ(1, args.length());
|
|
CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
|
object->map()->CompleteInobjectSlackTracking(isolate);
|
|
return ReadOnlyRoots(isolate).undefined_value();
|
}
|
|
RUNTIME_FUNCTION(Runtime_FreezeWasmLazyCompilation) {
|
DCHECK_EQ(1, args.length());
|
DisallowHeapAllocation no_gc;
|
CONVERT_ARG_CHECKED(WasmInstanceObject, instance, 0);
|
|
instance->module_object()->native_module()->set_lazy_compile_frozen(true);
|
return ReadOnlyRoots(isolate).undefined_value();
|
}
|
|
RUNTIME_FUNCTION(Runtime_WasmMemoryHasFullGuardRegion) {
|
DCHECK_EQ(1, args.length());
|
DisallowHeapAllocation no_gc;
|
CONVERT_ARG_CHECKED(WasmMemoryObject, memory, 0);
|
|
return isolate->heap()->ToBoolean(memory->has_full_guard_region(isolate));
|
}
|
|
} // namespace internal
|
} // namespace v8
|