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/runtime/runtime-utils.h"
      6 
      7 #include "src/arguments.h"
      8 #include "src/assembler.h"
      9 #include "src/compiler/wasm-compiler.h"
     10 #include "src/conversions.h"
     11 #include "src/debug/debug.h"
     12 #include "src/factory.h"
     13 #include "src/frames-inl.h"
     14 #include "src/objects-inl.h"
     15 #include "src/v8memory.h"
     16 #include "src/wasm/wasm-module.h"
     17 #include "src/wasm/wasm-objects.h"
     18 #include "src/wasm/wasm-opcodes.h"
     19 
     20 namespace v8 {
     21 namespace internal {
     22 
     23 namespace {
     24 WasmInstanceObject* GetWasmInstanceOnStackTop(Isolate* isolate) {
     25   DisallowHeapAllocation no_allocation;
     26   const Address entry = Isolate::c_entry_fp(isolate->thread_local_top());
     27   Address pc =
     28       Memory::Address_at(entry + StandardFrameConstants::kCallerPCOffset);
     29   Code* code = isolate->inner_pointer_to_code_cache()->GetCacheEntry(pc)->code;
     30   DCHECK_EQ(Code::WASM_FUNCTION, code->kind());
     31   WasmInstanceObject* owning_instance = wasm::GetOwningWasmInstance(code);
     32   CHECK_NOT_NULL(owning_instance);
     33   return owning_instance;
     34 }
     35 Context* GetWasmContextOnStackTop(Isolate* isolate) {
     36   return GetWasmInstanceOnStackTop(isolate)
     37       ->compiled_module()
     38       ->ptr_to_native_context();
     39 }
     40 }  // namespace
     41 
     42 RUNTIME_FUNCTION(Runtime_WasmMemorySize) {
     43   HandleScope scope(isolate);
     44   DCHECK_EQ(0, args.length());
     45 
     46   Handle<WasmInstanceObject> instance(GetWasmInstanceOnStackTop(isolate),
     47                                       isolate);
     48   return *isolate->factory()->NewNumberFromInt(
     49       wasm::GetInstanceMemorySize(isolate, instance));
     50 }
     51 
     52 RUNTIME_FUNCTION(Runtime_WasmGrowMemory) {
     53   HandleScope scope(isolate);
     54   DCHECK_EQ(1, args.length());
     55   CONVERT_UINT32_ARG_CHECKED(delta_pages, 0);
     56   Handle<WasmInstanceObject> instance(GetWasmInstanceOnStackTop(isolate),
     57                                       isolate);
     58 
     59   // Set the current isolate's context.
     60   DCHECK_NULL(isolate->context());
     61   isolate->set_context(instance->compiled_module()->ptr_to_native_context());
     62 
     63   return *isolate->factory()->NewNumberFromInt(
     64       wasm::GrowMemory(isolate, instance, delta_pages));
     65 }
     66 
     67 Object* ThrowRuntimeError(Isolate* isolate, int message_id, int byte_offset,
     68                           bool patch_source_position) {
     69   HandleScope scope(isolate);
     70   DCHECK_NULL(isolate->context());
     71   isolate->set_context(GetWasmContextOnStackTop(isolate));
     72   Handle<Object> error_obj = isolate->factory()->NewWasmRuntimeError(
     73       static_cast<MessageTemplate::Template>(message_id));
     74 
     75   if (!patch_source_position) {
     76     return isolate->Throw(*error_obj);
     77   }
     78 
     79   // For wasm traps, the byte offset (a.k.a source position) can not be
     80   // determined from relocation info, since the explicit checks for traps
     81   // converge in one singe block which calls this runtime function.
     82   // We hence pass the byte offset explicitely, and patch it into the top-most
     83   // frame (a wasm frame) on the collected stack trace.
     84   // TODO(wasm): This implementation is temporary, see bug #5007:
     85   // https://bugs.chromium.org/p/v8/issues/detail?id=5007
     86   Handle<JSObject> error = Handle<JSObject>::cast(error_obj);
     87   Handle<Object> stack_trace_obj = JSReceiver::GetDataProperty(
     88       error, isolate->factory()->stack_trace_symbol());
     89   // Patch the stack trace (array of <receiver, function, code, position>).
     90   if (stack_trace_obj->IsJSArray()) {
     91     Handle<FrameArray> stack_elements(
     92         FrameArray::cast(JSArray::cast(*stack_trace_obj)->elements()));
     93     DCHECK(stack_elements->Code(0)->kind() == AbstractCode::WASM_FUNCTION);
     94     DCHECK(stack_elements->Offset(0)->value() >= 0);
     95     stack_elements->SetOffset(0, Smi::FromInt(-1 - byte_offset));
     96   }
     97 
     98   // Patch the detailed stack trace (array of JSObjects with various
     99   // properties).
    100   Handle<Object> detailed_stack_trace_obj = JSReceiver::GetDataProperty(
    101       error, isolate->factory()->detailed_stack_trace_symbol());
    102   if (detailed_stack_trace_obj->IsJSArray()) {
    103     Handle<FixedArray> stack_elements(
    104         FixedArray::cast(JSArray::cast(*detailed_stack_trace_obj)->elements()));
    105     DCHECK_GE(stack_elements->length(), 1);
    106     Handle<JSObject> top_frame(JSObject::cast(stack_elements->get(0)));
    107     Handle<String> wasm_offset_key =
    108         isolate->factory()->InternalizeOneByteString(
    109             STATIC_CHAR_VECTOR("column"));
    110     LookupIterator it(top_frame, wasm_offset_key, top_frame,
    111                       LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR);
    112     if (it.IsFound()) {
    113       DCHECK(JSReceiver::GetDataProperty(&it)->IsSmi());
    114       // Make column number 1-based here.
    115       Maybe<bool> data_set = JSReceiver::SetDataProperty(
    116           &it, handle(Smi::FromInt(byte_offset + 1), isolate));
    117       DCHECK(data_set.IsJust() && data_set.FromJust() == true);
    118       USE(data_set);
    119     }
    120   }
    121 
    122   return isolate->Throw(*error_obj);
    123 }
    124 
    125 RUNTIME_FUNCTION(Runtime_ThrowWasmErrorFromTrapIf) {
    126   DCHECK_EQ(1, args.length());
    127   CONVERT_SMI_ARG_CHECKED(message_id, 0);
    128   return ThrowRuntimeError(isolate, message_id, 0, false);
    129 }
    130 
    131 RUNTIME_FUNCTION(Runtime_ThrowWasmError) {
    132   DCHECK_EQ(2, args.length());
    133   CONVERT_SMI_ARG_CHECKED(message_id, 0);
    134   CONVERT_SMI_ARG_CHECKED(byte_offset, 1);
    135   return ThrowRuntimeError(isolate, message_id, byte_offset, true);
    136 }
    137 
    138 RUNTIME_FUNCTION(Runtime_WasmThrowTypeError) {
    139   HandleScope scope(isolate);
    140   DCHECK_EQ(0, args.length());
    141   THROW_NEW_ERROR_RETURN_FAILURE(
    142       isolate, NewTypeError(MessageTemplate::kWasmTrapTypeError));
    143 }
    144 
    145 RUNTIME_FUNCTION(Runtime_WasmThrow) {
    146   HandleScope scope(isolate);
    147   DCHECK_EQ(2, args.length());
    148   CONVERT_SMI_ARG_CHECKED(lower, 0);
    149   CONVERT_SMI_ARG_CHECKED(upper, 1);
    150 
    151   const int32_t thrown_value = (upper << 16) | lower;
    152 
    153   // Set the current isolate's context.
    154   DCHECK_NULL(isolate->context());
    155   isolate->set_context(GetWasmContextOnStackTop(isolate));
    156 
    157   return isolate->Throw(*isolate->factory()->NewNumberFromInt(thrown_value));
    158 }
    159 
    160 RUNTIME_FUNCTION(Runtime_WasmGetCaughtExceptionValue) {
    161   HandleScope scope(isolate);
    162   DCHECK_EQ(1, args.length());
    163   Object* exception = args[0];
    164   // The unwinder will only deliver exceptions to wasm if the exception is a
    165   // Number or a Smi (which we have just converted to a Number.) This logic
    166   // lives in Isolate::is_catchable_by_wasm(Object*).
    167   CHECK(exception->IsNumber());
    168   return exception;
    169 }
    170 
    171 RUNTIME_FUNCTION(Runtime_WasmRunInterpreter) {
    172   DCHECK_EQ(3, args.length());
    173   HandleScope scope(isolate);
    174   CONVERT_ARG_HANDLE_CHECKED(JSObject, instance_obj, 0);
    175   CONVERT_NUMBER_CHECKED(int32_t, func_index, Int32, args[1]);
    176   CONVERT_ARG_HANDLE_CHECKED(Object, arg_buffer_obj, 2);
    177   CHECK(WasmInstanceObject::IsWasmInstanceObject(*instance_obj));
    178   Handle<WasmInstanceObject> instance =
    179       Handle<WasmInstanceObject>::cast(instance_obj);
    180 
    181   // The arg buffer is the raw pointer to the caller's stack. It looks like a
    182   // Smi (lowest bit not set, as checked by IsSmi), but is no valid Smi. We just
    183   // cast it back to the raw pointer.
    184   CHECK(!arg_buffer_obj->IsHeapObject());
    185   CHECK(arg_buffer_obj->IsSmi());
    186   uint8_t* arg_buffer = reinterpret_cast<uint8_t*>(*arg_buffer_obj);
    187 
    188   // Set the current isolate's context.
    189   DCHECK_NULL(isolate->context());
    190   isolate->set_context(instance->compiled_module()->ptr_to_native_context());
    191 
    192   instance->debug_info()->RunInterpreter(func_index, arg_buffer);
    193   return isolate->heap()->undefined_value();
    194 }
    195 
    196 RUNTIME_FUNCTION(Runtime_WasmStackGuard) {
    197   SealHandleScope shs(isolate);
    198   DCHECK_EQ(0, args.length());
    199 
    200   // Set the current isolate's context.
    201   DCHECK_NULL(isolate->context());
    202   isolate->set_context(GetWasmContextOnStackTop(isolate));
    203 
    204   // Check if this is a real stack overflow.
    205   StackLimitCheck check(isolate);
    206   if (check.JsHasOverflowed()) return isolate->StackOverflow();
    207 
    208   return isolate->stack_guard()->HandleInterrupts();
    209 }
    210 
    211 }  // namespace internal
    212 }  // namespace v8
    213