Home | History | Annotate | Download | only in runtime
      1 // Copyright 2016 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "src/arguments-inl.h"
      6 #include "src/compiler/wasm-compiler.h"
      7 #include "src/conversions.h"
      8 #include "src/debug/debug.h"
      9 #include "src/frame-constants.h"
     10 #include "src/heap/factory.h"
     11 #include "src/objects-inl.h"
     12 #include "src/objects/frame-array-inl.h"
     13 #include "src/runtime/runtime-utils.h"
     14 #include "src/trap-handler/trap-handler.h"
     15 #include "src/v8memory.h"
     16 #include "src/wasm/module-compiler.h"
     17 #include "src/wasm/wasm-code-manager.h"
     18 #include "src/wasm/wasm-constants.h"
     19 #include "src/wasm/wasm-engine.h"
     20 #include "src/wasm/wasm-objects.h"
     21 
     22 namespace v8 {
     23 namespace internal {
     24 
     25 namespace {
     26 
     27 WasmInstanceObject* GetWasmInstanceOnStackTop(Isolate* isolate) {
     28   StackFrameIterator it(isolate, isolate->thread_local_top());
     29   // On top: C entry stub.
     30   DCHECK_EQ(StackFrame::EXIT, it.frame()->type());
     31   it.Advance();
     32   // Next: the wasm (compiled or interpreted) frame.
     33   WasmInstanceObject* result = nullptr;
     34   if (it.frame()->is_wasm_compiled()) {
     35     result = WasmCompiledFrame::cast(it.frame())->wasm_instance();
     36   } else {
     37     DCHECK(it.frame()->is_wasm_interpreter_entry());
     38     result = WasmInterpreterEntryFrame::cast(it.frame())->wasm_instance();
     39   }
     40   return result;
     41 }
     42 
     43 Context* GetNativeContextFromWasmInstanceOnStackTop(Isolate* isolate) {
     44   return GetWasmInstanceOnStackTop(isolate)->native_context();
     45 }
     46 
     47 class ClearThreadInWasmScope {
     48  public:
     49   explicit ClearThreadInWasmScope(bool coming_from_wasm)
     50       : coming_from_wasm_(coming_from_wasm) {
     51     DCHECK_EQ(trap_handler::IsTrapHandlerEnabled() && coming_from_wasm,
     52               trap_handler::IsThreadInWasm());
     53     if (coming_from_wasm) trap_handler::ClearThreadInWasm();
     54   }
     55   ~ClearThreadInWasmScope() {
     56     DCHECK(!trap_handler::IsThreadInWasm());
     57     if (coming_from_wasm_) trap_handler::SetThreadInWasm();
     58   }
     59 
     60  private:
     61   const bool coming_from_wasm_;
     62 };
     63 
     64 }  // namespace
     65 
     66 RUNTIME_FUNCTION(Runtime_WasmGrowMemory) {
     67   HandleScope scope(isolate);
     68   DCHECK_EQ(2, args.length());
     69   CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
     70   // {delta_pages} is checked to be a positive smi in the WasmGrowMemory builtin
     71   // which calls this runtime function.
     72   CONVERT_UINT32_ARG_CHECKED(delta_pages, 1);
     73 
     74   // This runtime function is always being called from wasm code.
     75   ClearThreadInWasmScope flag_scope(true);
     76 
     77   // Set the current isolate's context.
     78   DCHECK_NULL(isolate->context());
     79   isolate->set_context(instance->native_context());
     80 
     81   int ret = WasmMemoryObject::Grow(
     82       isolate, handle(instance->memory_object(), isolate), delta_pages);
     83   // The WasmGrowMemory builtin which calls this runtime function expects us to
     84   // always return a Smi.
     85   return Smi::FromInt(ret);
     86 }
     87 
     88 RUNTIME_FUNCTION(Runtime_ThrowWasmError) {
     89   DCHECK_EQ(1, args.length());
     90   CONVERT_SMI_ARG_CHECKED(message_id, 0);
     91   ClearThreadInWasmScope clear_wasm_flag(isolate->context() == nullptr);
     92 
     93   HandleScope scope(isolate);
     94   DCHECK_NULL(isolate->context());
     95   isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
     96   Handle<Object> error_obj = isolate->factory()->NewWasmRuntimeError(
     97       static_cast<MessageTemplate::Template>(message_id));
     98   return isolate->Throw(*error_obj);
     99 }
    100 
    101 RUNTIME_FUNCTION(Runtime_ThrowWasmStackOverflow) {
    102   SealHandleScope shs(isolate);
    103   DCHECK_LE(0, args.length());
    104   DCHECK_NULL(isolate->context());
    105   isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
    106   return isolate->StackOverflow();
    107 }
    108 
    109 RUNTIME_FUNCTION(Runtime_WasmThrowTypeError) {
    110   HandleScope scope(isolate);
    111   DCHECK_EQ(0, args.length());
    112   THROW_NEW_ERROR_RETURN_FAILURE(
    113       isolate, NewTypeError(MessageTemplate::kWasmTrapTypeError));
    114 }
    115 
    116 RUNTIME_FUNCTION(Runtime_WasmThrowCreate) {
    117   // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls.
    118   HandleScope scope(isolate);
    119   DCHECK_NULL(isolate->context());
    120   isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
    121   DCHECK_EQ(2, args.length());
    122   Handle<Object> exception = isolate->factory()->NewWasmRuntimeError(
    123       static_cast<MessageTemplate::Template>(
    124           MessageTemplate::kWasmExceptionError));
    125   isolate->set_wasm_caught_exception(*exception);
    126   CONVERT_ARG_HANDLE_CHECKED(Smi, id, 0);
    127   CHECK(!JSReceiver::SetProperty(isolate, exception,
    128                                  isolate->factory()->InternalizeUtf8String(
    129                                      wasm::WasmException::kRuntimeIdStr),
    130                                  id, LanguageMode::kStrict)
    131              .is_null());
    132   CONVERT_SMI_ARG_CHECKED(size, 1);
    133   Handle<JSTypedArray> values =
    134       isolate->factory()->NewJSTypedArray(ElementsKind::UINT16_ELEMENTS, size);
    135   CHECK(!JSReceiver::SetProperty(isolate, exception,
    136                                  isolate->factory()->InternalizeUtf8String(
    137                                      wasm::WasmException::kRuntimeValuesStr),
    138                                  values, LanguageMode::kStrict)
    139              .is_null());
    140   return ReadOnlyRoots(isolate).undefined_value();
    141 }
    142 
    143 RUNTIME_FUNCTION(Runtime_WasmThrow) {
    144   // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls.
    145   HandleScope scope(isolate);
    146   DCHECK_NULL(isolate->context());
    147   isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
    148   DCHECK_EQ(0, args.length());
    149   Handle<Object> exception(isolate->get_wasm_caught_exception(), isolate);
    150   CHECK(!exception.is_null());
    151   isolate->clear_wasm_caught_exception();
    152   return isolate->Throw(*exception);
    153 }
    154 
    155 RUNTIME_FUNCTION(Runtime_WasmGetExceptionRuntimeId) {
    156   // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls.
    157   HandleScope scope(isolate);
    158   DCHECK_NULL(isolate->context());
    159   isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
    160   Handle<Object> except_obj(isolate->get_wasm_caught_exception(), isolate);
    161   if (!except_obj.is_null() && except_obj->IsJSReceiver()) {
    162     Handle<JSReceiver> exception(JSReceiver::cast(*except_obj), isolate);
    163     Handle<Object> tag;
    164     if (JSReceiver::GetProperty(isolate, exception,
    165                                 isolate->factory()->InternalizeUtf8String(
    166                                     wasm::WasmException::kRuntimeIdStr))
    167             .ToHandle(&tag)) {
    168       if (tag->IsSmi()) {
    169         return *tag;
    170       }
    171     }
    172   }
    173   return Smi::FromInt(wasm::kInvalidExceptionTag);
    174 }
    175 
    176 RUNTIME_FUNCTION(Runtime_WasmExceptionGetElement) {
    177   // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls.
    178   HandleScope scope(isolate);
    179   DCHECK_NULL(isolate->context());
    180   isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
    181   DCHECK_EQ(1, args.length());
    182   Handle<Object> except_obj(isolate->get_wasm_caught_exception(), isolate);
    183   if (!except_obj.is_null() && except_obj->IsJSReceiver()) {
    184     Handle<JSReceiver> exception(JSReceiver::cast(*except_obj), isolate);
    185     Handle<Object> values_obj;
    186     if (JSReceiver::GetProperty(isolate, exception,
    187                                 isolate->factory()->InternalizeUtf8String(
    188                                     wasm::WasmException::kRuntimeValuesStr))
    189             .ToHandle(&values_obj)) {
    190       if (values_obj->IsJSTypedArray()) {
    191         Handle<JSTypedArray> values = Handle<JSTypedArray>::cast(values_obj);
    192         CHECK_EQ(values->type(), kExternalUint16Array);
    193         CONVERT_SMI_ARG_CHECKED(index, 0);
    194         CHECK_LT(index, Smi::ToInt(values->length()));
    195         auto* vals =
    196             reinterpret_cast<uint16_t*>(values->GetBuffer()->backing_store());
    197         return Smi::FromInt(vals[index]);
    198       }
    199     }
    200   }
    201   return Smi::FromInt(0);
    202 }
    203 
    204 RUNTIME_FUNCTION(Runtime_WasmExceptionSetElement) {
    205   // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls.
    206   HandleScope scope(isolate);
    207   DCHECK_EQ(2, args.length());
    208   DCHECK_NULL(isolate->context());
    209   isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
    210   Handle<Object> except_obj(isolate->get_wasm_caught_exception(), isolate);
    211   if (!except_obj.is_null() && except_obj->IsJSReceiver()) {
    212     Handle<JSReceiver> exception(JSReceiver::cast(*except_obj), isolate);
    213     Handle<Object> values_obj;
    214     if (JSReceiver::GetProperty(isolate, exception,
    215                                 isolate->factory()->InternalizeUtf8String(
    216                                     wasm::WasmException::kRuntimeValuesStr))
    217             .ToHandle(&values_obj)) {
    218       if (values_obj->IsJSTypedArray()) {
    219         Handle<JSTypedArray> values = Handle<JSTypedArray>::cast(values_obj);
    220         CHECK_EQ(values->type(), kExternalUint16Array);
    221         CONVERT_SMI_ARG_CHECKED(index, 0);
    222         CHECK_LT(index, Smi::ToInt(values->length()));
    223         CONVERT_SMI_ARG_CHECKED(value, 1);
    224         auto* vals =
    225             reinterpret_cast<uint16_t*>(values->GetBuffer()->backing_store());
    226         vals[index] = static_cast<uint16_t>(value);
    227       }
    228     }
    229   }
    230   return ReadOnlyRoots(isolate).undefined_value();
    231 }
    232 
    233 RUNTIME_FUNCTION(Runtime_WasmRunInterpreter) {
    234   DCHECK_EQ(2, args.length());
    235   HandleScope scope(isolate);
    236   CONVERT_NUMBER_CHECKED(int32_t, func_index, Int32, args[0]);
    237   CONVERT_ARG_HANDLE_CHECKED(Object, arg_buffer_obj, 1);
    238   Handle<WasmInstanceObject> instance(GetWasmInstanceOnStackTop(isolate),
    239                                       isolate);
    240 
    241   // The arg buffer is the raw pointer to the caller's stack. It looks like a
    242   // Smi (lowest bit not set, as checked by IsSmi), but is no valid Smi. We just
    243   // cast it back to the raw pointer.
    244   CHECK(!arg_buffer_obj->IsHeapObject());
    245   CHECK(arg_buffer_obj->IsSmi());
    246   Address arg_buffer = reinterpret_cast<Address>(*arg_buffer_obj);
    247 
    248   ClearThreadInWasmScope wasm_flag(true);
    249 
    250   // Set the current isolate's context.
    251   DCHECK_NULL(isolate->context());
    252   isolate->set_context(instance->native_context());
    253 
    254   // Find the frame pointer of the interpreter entry.
    255   Address frame_pointer = 0;
    256   {
    257     StackFrameIterator it(isolate, isolate->thread_local_top());
    258     // On top: C entry stub.
    259     DCHECK_EQ(StackFrame::EXIT, it.frame()->type());
    260     it.Advance();
    261     // Next: the wasm interpreter entry.
    262     DCHECK_EQ(StackFrame::WASM_INTERPRETER_ENTRY, it.frame()->type());
    263     frame_pointer = it.frame()->fp();
    264   }
    265 
    266   // Run the function in the interpreter. Note that neither the {WasmDebugInfo}
    267   // nor the {InterpreterHandle} have to exist, because interpretation might
    268   // have been triggered by another Isolate sharing the same WasmEngine.
    269   Handle<WasmDebugInfo> debug_info =
    270       WasmInstanceObject::GetOrCreateDebugInfo(instance);
    271   bool success = WasmDebugInfo::RunInterpreter(
    272       isolate, debug_info, frame_pointer, func_index, arg_buffer);
    273 
    274   if (!success) {
    275     DCHECK(isolate->has_pending_exception());
    276     return ReadOnlyRoots(isolate).exception();
    277   }
    278   return ReadOnlyRoots(isolate).undefined_value();
    279 }
    280 
    281 RUNTIME_FUNCTION(Runtime_WasmStackGuard) {
    282   SealHandleScope shs(isolate);
    283   DCHECK_EQ(0, args.length());
    284   DCHECK(!trap_handler::IsTrapHandlerEnabled() ||
    285          trap_handler::IsThreadInWasm());
    286 
    287   ClearThreadInWasmScope wasm_flag(true);
    288 
    289   // Set the current isolate's context.
    290   DCHECK_NULL(isolate->context());
    291   isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
    292 
    293   // Check if this is a real stack overflow.
    294   StackLimitCheck check(isolate);
    295   if (check.JsHasOverflowed()) return isolate->StackOverflow();
    296 
    297   return isolate->stack_guard()->HandleInterrupts();
    298 }
    299 
    300 RUNTIME_FUNCTION(Runtime_WasmCompileLazy) {
    301   HandleScope scope(isolate);
    302   DCHECK_EQ(2, args.length());
    303   CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
    304   CONVERT_SMI_ARG_CHECKED(func_index, 1);
    305 
    306   ClearThreadInWasmScope wasm_flag(true);
    307 
    308 #ifdef DEBUG
    309   StackFrameIterator it(isolate, isolate->thread_local_top());
    310   // On top: C entry stub.
    311   DCHECK_EQ(StackFrame::EXIT, it.frame()->type());
    312   it.Advance();
    313   // Next: the wasm lazy compile frame.
    314   DCHECK_EQ(StackFrame::WASM_COMPILE_LAZY, it.frame()->type());
    315   DCHECK_EQ(*instance, WasmCompileLazyFrame::cast(it.frame())->wasm_instance());
    316 #endif
    317 
    318   Address entrypoint = wasm::CompileLazy(
    319       isolate, instance->module_object()->native_module(), func_index);
    320   return reinterpret_cast<Object*>(entrypoint);
    321 }
    322 
    323 }  // namespace internal
    324 }  // namespace v8
    325