// Copyright 2016 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/arguments-inl.h" #include "src/compiler/wasm-compiler.h" #include "src/conversions.h" #include "src/debug/debug.h" #include "src/frame-constants.h" #include "src/heap/factory.h" #include "src/objects-inl.h" #include "src/objects/frame-array-inl.h" #include "src/runtime/runtime-utils.h" #include "src/trap-handler/trap-handler.h" #include "src/v8memory.h" #include "src/wasm/module-compiler.h" #include "src/wasm/wasm-code-manager.h" #include "src/wasm/wasm-constants.h" #include "src/wasm/wasm-engine.h" #include "src/wasm/wasm-objects.h" namespace v8 { namespace internal { namespace { WasmInstanceObject* GetWasmInstanceOnStackTop(Isolate* isolate) { StackFrameIterator it(isolate, isolate->thread_local_top()); // On top: C entry stub. DCHECK_EQ(StackFrame::EXIT, it.frame()->type()); it.Advance(); // Next: the wasm (compiled or interpreted) frame. WasmInstanceObject* result = nullptr; if (it.frame()->is_wasm_compiled()) { result = WasmCompiledFrame::cast(it.frame())->wasm_instance(); } else { DCHECK(it.frame()->is_wasm_interpreter_entry()); result = WasmInterpreterEntryFrame::cast(it.frame())->wasm_instance(); } return result; } Context* GetNativeContextFromWasmInstanceOnStackTop(Isolate* isolate) { return GetWasmInstanceOnStackTop(isolate)->native_context(); } class ClearThreadInWasmScope { public: explicit ClearThreadInWasmScope(bool coming_from_wasm) : coming_from_wasm_(coming_from_wasm) { DCHECK_EQ(trap_handler::IsTrapHandlerEnabled() && coming_from_wasm, trap_handler::IsThreadInWasm()); if (coming_from_wasm) trap_handler::ClearThreadInWasm(); } ~ClearThreadInWasmScope() { DCHECK(!trap_handler::IsThreadInWasm()); if (coming_from_wasm_) trap_handler::SetThreadInWasm(); } private: const bool coming_from_wasm_; }; } // namespace RUNTIME_FUNCTION(Runtime_WasmGrowMemory) { HandleScope scope(isolate); DCHECK_EQ(2, args.length()); CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0); // {delta_pages} is checked to be a positive smi in the WasmGrowMemory builtin // which calls this runtime function. CONVERT_UINT32_ARG_CHECKED(delta_pages, 1); // This runtime function is always being called from wasm code. ClearThreadInWasmScope flag_scope(true); // Set the current isolate's context. DCHECK_NULL(isolate->context()); isolate->set_context(instance->native_context()); int ret = WasmMemoryObject::Grow( isolate, handle(instance->memory_object(), isolate), delta_pages); // The WasmGrowMemory builtin which calls this runtime function expects us to // always return a Smi. return Smi::FromInt(ret); } RUNTIME_FUNCTION(Runtime_ThrowWasmError) { DCHECK_EQ(1, args.length()); CONVERT_SMI_ARG_CHECKED(message_id, 0); ClearThreadInWasmScope clear_wasm_flag(isolate->context() == nullptr); HandleScope scope(isolate); DCHECK_NULL(isolate->context()); isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate)); Handle error_obj = isolate->factory()->NewWasmRuntimeError( static_cast(message_id)); return isolate->Throw(*error_obj); } RUNTIME_FUNCTION(Runtime_ThrowWasmStackOverflow) { SealHandleScope shs(isolate); DCHECK_LE(0, args.length()); DCHECK_NULL(isolate->context()); isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate)); return isolate->StackOverflow(); } RUNTIME_FUNCTION(Runtime_WasmThrowTypeError) { HandleScope scope(isolate); DCHECK_EQ(0, args.length()); THROW_NEW_ERROR_RETURN_FAILURE( isolate, NewTypeError(MessageTemplate::kWasmTrapTypeError)); } RUNTIME_FUNCTION(Runtime_WasmThrowCreate) { // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls. HandleScope scope(isolate); DCHECK_NULL(isolate->context()); isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate)); DCHECK_EQ(2, args.length()); Handle exception = isolate->factory()->NewWasmRuntimeError( static_cast( MessageTemplate::kWasmExceptionError)); isolate->set_wasm_caught_exception(*exception); CONVERT_ARG_HANDLE_CHECKED(Smi, id, 0); CHECK(!JSReceiver::SetProperty(isolate, exception, isolate->factory()->InternalizeUtf8String( wasm::WasmException::kRuntimeIdStr), id, LanguageMode::kStrict) .is_null()); CONVERT_SMI_ARG_CHECKED(size, 1); Handle values = isolate->factory()->NewJSTypedArray(ElementsKind::UINT16_ELEMENTS, size); CHECK(!JSReceiver::SetProperty(isolate, exception, isolate->factory()->InternalizeUtf8String( wasm::WasmException::kRuntimeValuesStr), values, LanguageMode::kStrict) .is_null()); return ReadOnlyRoots(isolate).undefined_value(); } RUNTIME_FUNCTION(Runtime_WasmThrow) { // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls. HandleScope scope(isolate); DCHECK_NULL(isolate->context()); isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate)); DCHECK_EQ(0, args.length()); Handle exception(isolate->get_wasm_caught_exception(), isolate); CHECK(!exception.is_null()); isolate->clear_wasm_caught_exception(); return isolate->Throw(*exception); } RUNTIME_FUNCTION(Runtime_WasmGetExceptionRuntimeId) { // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls. HandleScope scope(isolate); DCHECK_NULL(isolate->context()); isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate)); Handle except_obj(isolate->get_wasm_caught_exception(), isolate); if (!except_obj.is_null() && except_obj->IsJSReceiver()) { Handle exception(JSReceiver::cast(*except_obj), isolate); Handle tag; if (JSReceiver::GetProperty(isolate, exception, isolate->factory()->InternalizeUtf8String( wasm::WasmException::kRuntimeIdStr)) .ToHandle(&tag)) { if (tag->IsSmi()) { return *tag; } } } return Smi::FromInt(wasm::kInvalidExceptionTag); } RUNTIME_FUNCTION(Runtime_WasmExceptionGetElement) { // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls. HandleScope scope(isolate); DCHECK_NULL(isolate->context()); isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate)); DCHECK_EQ(1, args.length()); Handle except_obj(isolate->get_wasm_caught_exception(), isolate); if (!except_obj.is_null() && except_obj->IsJSReceiver()) { Handle exception(JSReceiver::cast(*except_obj), isolate); Handle values_obj; if (JSReceiver::GetProperty(isolate, exception, isolate->factory()->InternalizeUtf8String( wasm::WasmException::kRuntimeValuesStr)) .ToHandle(&values_obj)) { if (values_obj->IsJSTypedArray()) { Handle values = Handle::cast(values_obj); CHECK_EQ(values->type(), kExternalUint16Array); CONVERT_SMI_ARG_CHECKED(index, 0); CHECK_LT(index, Smi::ToInt(values->length())); auto* vals = reinterpret_cast(values->GetBuffer()->backing_store()); return Smi::FromInt(vals[index]); } } } return Smi::FromInt(0); } RUNTIME_FUNCTION(Runtime_WasmExceptionSetElement) { // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls. HandleScope scope(isolate); DCHECK_EQ(2, args.length()); DCHECK_NULL(isolate->context()); isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate)); Handle except_obj(isolate->get_wasm_caught_exception(), isolate); if (!except_obj.is_null() && except_obj->IsJSReceiver()) { Handle exception(JSReceiver::cast(*except_obj), isolate); Handle values_obj; if (JSReceiver::GetProperty(isolate, exception, isolate->factory()->InternalizeUtf8String( wasm::WasmException::kRuntimeValuesStr)) .ToHandle(&values_obj)) { if (values_obj->IsJSTypedArray()) { Handle values = Handle::cast(values_obj); CHECK_EQ(values->type(), kExternalUint16Array); CONVERT_SMI_ARG_CHECKED(index, 0); CHECK_LT(index, Smi::ToInt(values->length())); CONVERT_SMI_ARG_CHECKED(value, 1); auto* vals = reinterpret_cast(values->GetBuffer()->backing_store()); vals[index] = static_cast(value); } } } return ReadOnlyRoots(isolate).undefined_value(); } RUNTIME_FUNCTION(Runtime_WasmRunInterpreter) { DCHECK_EQ(2, args.length()); HandleScope scope(isolate); CONVERT_NUMBER_CHECKED(int32_t, func_index, Int32, args[0]); CONVERT_ARG_HANDLE_CHECKED(Object, arg_buffer_obj, 1); Handle instance(GetWasmInstanceOnStackTop(isolate), isolate); // The arg buffer is the raw pointer to the caller's stack. It looks like a // Smi (lowest bit not set, as checked by IsSmi), but is no valid Smi. We just // cast it back to the raw pointer. CHECK(!arg_buffer_obj->IsHeapObject()); CHECK(arg_buffer_obj->IsSmi()); Address arg_buffer = reinterpret_cast
(*arg_buffer_obj); ClearThreadInWasmScope wasm_flag(true); // Set the current isolate's context. DCHECK_NULL(isolate->context()); isolate->set_context(instance->native_context()); // Find the frame pointer of the interpreter entry. Address frame_pointer = 0; { StackFrameIterator it(isolate, isolate->thread_local_top()); // On top: C entry stub. DCHECK_EQ(StackFrame::EXIT, it.frame()->type()); it.Advance(); // Next: the wasm interpreter entry. DCHECK_EQ(StackFrame::WASM_INTERPRETER_ENTRY, it.frame()->type()); frame_pointer = it.frame()->fp(); } // Run the function in the interpreter. Note that neither the {WasmDebugInfo} // nor the {InterpreterHandle} have to exist, because interpretation might // have been triggered by another Isolate sharing the same WasmEngine. Handle debug_info = WasmInstanceObject::GetOrCreateDebugInfo(instance); bool success = WasmDebugInfo::RunInterpreter( isolate, debug_info, frame_pointer, func_index, arg_buffer); if (!success) { DCHECK(isolate->has_pending_exception()); return ReadOnlyRoots(isolate).exception(); } return ReadOnlyRoots(isolate).undefined_value(); } RUNTIME_FUNCTION(Runtime_WasmStackGuard) { SealHandleScope shs(isolate); DCHECK_EQ(0, args.length()); DCHECK(!trap_handler::IsTrapHandlerEnabled() || trap_handler::IsThreadInWasm()); ClearThreadInWasmScope wasm_flag(true); // Set the current isolate's context. DCHECK_NULL(isolate->context()); isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate)); // Check if this is a real stack overflow. StackLimitCheck check(isolate); if (check.JsHasOverflowed()) return isolate->StackOverflow(); return isolate->stack_guard()->HandleInterrupts(); } RUNTIME_FUNCTION(Runtime_WasmCompileLazy) { HandleScope scope(isolate); DCHECK_EQ(2, args.length()); CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0); CONVERT_SMI_ARG_CHECKED(func_index, 1); ClearThreadInWasmScope wasm_flag(true); #ifdef DEBUG StackFrameIterator it(isolate, isolate->thread_local_top()); // On top: C entry stub. DCHECK_EQ(StackFrame::EXIT, it.frame()->type()); it.Advance(); // Next: the wasm lazy compile frame. DCHECK_EQ(StackFrame::WASM_COMPILE_LAZY, it.frame()->type()); DCHECK_EQ(*instance, WasmCompileLazyFrame::cast(it.frame())->wasm_instance()); #endif Address entrypoint = wasm::CompileLazy( isolate, instance->module_object()->native_module(), func_index); return reinterpret_cast(entrypoint); } } // namespace internal } // namespace v8