// 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