V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
runtime-wasm.cc
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/counters.h"
9 #include "src/debug/debug.h"
10 #include "src/frame-constants.h"
11 #include "src/heap/factory.h"
12 #include "src/message-template.h"
13 #include "src/objects-inl.h"
14 #include "src/objects/frame-array-inl.h"
15 #include "src/runtime/runtime-utils.h"
16 #include "src/trap-handler/trap-handler.h"
17 #include "src/v8memory.h"
18 #include "src/wasm/module-compiler.h"
19 #include "src/wasm/wasm-code-manager.h"
20 #include "src/wasm/wasm-constants.h"
21 #include "src/wasm/wasm-engine.h"
22 #include "src/wasm/wasm-objects.h"
23 
24 namespace v8 {
25 namespace internal {
26 
27 namespace {
28 
29 WasmInstanceObject* GetWasmInstanceOnStackTop(Isolate* isolate) {
30  StackFrameIterator it(isolate, isolate->thread_local_top());
31  // On top: C entry stub.
32  DCHECK_EQ(StackFrame::EXIT, it.frame()->type());
33  it.Advance();
34  // Next: the wasm compiled frame.
35  DCHECK(it.frame()->is_wasm_compiled());
36  WasmCompiledFrame* frame = WasmCompiledFrame::cast(it.frame());
37  return frame->wasm_instance();
38 }
39 
40 Context GetNativeContextFromWasmInstanceOnStackTop(Isolate* isolate) {
41  return GetWasmInstanceOnStackTop(isolate)->native_context();
42 }
43 
44 class ClearThreadInWasmScope {
45  public:
46  ClearThreadInWasmScope() {
47  DCHECK_EQ(trap_handler::IsTrapHandlerEnabled(),
48  trap_handler::IsThreadInWasm());
49  trap_handler::ClearThreadInWasm();
50  }
51  ~ClearThreadInWasmScope() {
52  DCHECK(!trap_handler::IsThreadInWasm());
53  trap_handler::SetThreadInWasm();
54  }
55 };
56 
57 } // namespace
58 
59 RUNTIME_FUNCTION(Runtime_WasmMemoryGrow) {
60  HandleScope scope(isolate);
61  DCHECK_EQ(2, args.length());
62  CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
63  // {delta_pages} is checked to be a positive smi in the WasmMemoryGrow builtin
64  // which calls this runtime function.
65  CONVERT_UINT32_ARG_CHECKED(delta_pages, 1);
66 
67  // This runtime function is always being called from wasm code.
68  ClearThreadInWasmScope flag_scope;
69 
70  int ret = WasmMemoryObject::Grow(
71  isolate, handle(instance->memory_object(), isolate), delta_pages);
72  // The WasmMemoryGrow builtin which calls this runtime function expects us to
73  // always return a Smi.
74  return Smi::FromInt(ret);
75 }
76 
77 RUNTIME_FUNCTION(Runtime_ThrowWasmError) {
78  DCHECK_EQ(1, args.length());
79  CONVERT_SMI_ARG_CHECKED(message_id, 0);
80  ClearThreadInWasmScope clear_wasm_flag;
81 
82  HandleScope scope(isolate);
83  Handle<Object> error_obj = isolate->factory()->NewWasmRuntimeError(
84  MessageTemplateFromInt(message_id));
85  return isolate->Throw(*error_obj);
86 }
87 
88 RUNTIME_FUNCTION(Runtime_ThrowWasmStackOverflow) {
89  SealHandleScope shs(isolate);
90  DCHECK_LE(0, args.length());
91  DCHECK(isolate->context().is_null());
92  isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
93  return isolate->StackOverflow();
94 }
95 
96 RUNTIME_FUNCTION(Runtime_WasmThrowTypeError) {
97  HandleScope scope(isolate);
98  DCHECK_EQ(0, args.length());
99  THROW_NEW_ERROR_RETURN_FAILURE(
100  isolate, NewTypeError(MessageTemplate::kWasmTrapTypeError));
101 }
102 
103 RUNTIME_FUNCTION(Runtime_WasmThrowCreate) {
104  // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls.
105  HandleScope scope(isolate);
106  DCHECK_EQ(2, args.length());
107  DCHECK(isolate->context().is_null());
108  isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
109  CONVERT_ARG_CHECKED(HeapObject, tag_raw, 0);
110  CONVERT_SMI_ARG_CHECKED(size, 1);
111  // TODO(mstarzinger): Manually box because parameters are not visited yet.
112  Handle<Object> tag(tag_raw, isolate);
113  Handle<Object> exception = isolate->factory()->NewWasmRuntimeError(
114  MessageTemplate::kWasmExceptionError);
115  CHECK(
116  !JSReceiver::SetProperty(isolate, exception,
117  isolate->factory()->wasm_exception_tag_symbol(),
118  tag, LanguageMode::kStrict)
119  .is_null());
120  Handle<FixedArray> values = isolate->factory()->NewFixedArray(size);
121  CHECK(!JSReceiver::SetProperty(
122  isolate, exception,
123  isolate->factory()->wasm_exception_values_symbol(), values,
124  LanguageMode::kStrict)
125  .is_null());
126  return *exception;
127 }
128 
129 RUNTIME_FUNCTION(Runtime_WasmExceptionGetTag) {
130  // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls.
131  HandleScope scope(isolate);
132  DCHECK_EQ(1, args.length());
133  DCHECK(isolate->context().is_null());
134  isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
135  CONVERT_ARG_CHECKED(Object, except_obj_raw, 0);
136  // TODO(mstarzinger): Manually box because parameters are not visited yet.
137  Handle<Object> except_obj(except_obj_raw, isolate);
138  if (!except_obj.is_null() && except_obj->IsJSReceiver()) {
139  Handle<JSReceiver> exception(JSReceiver::cast(*except_obj), isolate);
140  Handle<Object> tag;
141  if (JSReceiver::GetProperty(isolate, exception,
142  isolate->factory()->wasm_exception_tag_symbol())
143  .ToHandle(&tag)) {
144  return *tag;
145  }
146  }
147  return ReadOnlyRoots(isolate).undefined_value();
148 }
149 
150 RUNTIME_FUNCTION(Runtime_WasmExceptionGetValues) {
151  // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls.
152  HandleScope scope(isolate);
153  DCHECK_EQ(1, args.length());
154  DCHECK(isolate->context().is_null());
155  isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
156  CONVERT_ARG_CHECKED(Object, except_obj_raw, 0);
157  // TODO(mstarzinger): Manually box because parameters are not visited yet.
158  Handle<Object> except_obj(except_obj_raw, isolate);
159  if (!except_obj.is_null() && except_obj->IsJSReceiver()) {
160  Handle<JSReceiver> exception(JSReceiver::cast(*except_obj), isolate);
161  Handle<Object> values;
162  if (JSReceiver::GetProperty(
163  isolate, exception,
164  isolate->factory()->wasm_exception_values_symbol())
165  .ToHandle(&values)) {
166  DCHECK(values->IsFixedArray());
167  return *values;
168  }
169  }
170  return ReadOnlyRoots(isolate).undefined_value();
171 }
172 
173 RUNTIME_FUNCTION(Runtime_WasmRunInterpreter) {
174  DCHECK_EQ(2, args.length());
175  HandleScope scope(isolate);
176  CONVERT_NUMBER_CHECKED(int32_t, func_index, Int32, args[0]);
177  CONVERT_ARG_HANDLE_CHECKED(Object, arg_buffer_obj, 1);
178 
179  // The arg buffer is the raw pointer to the caller's stack. It looks like a
180  // Smi (lowest bit not set, as checked by IsSmi), but is no valid Smi. We just
181  // cast it back to the raw pointer.
182  CHECK(!arg_buffer_obj->IsHeapObject());
183  CHECK(arg_buffer_obj->IsSmi());
184  Address arg_buffer = reinterpret_cast<Address>(*arg_buffer_obj);
185 
186  ClearThreadInWasmScope wasm_flag;
187 
188  // Find the frame pointer and instance of the interpreter frame on the stack.
189  Handle<WasmInstanceObject> instance;
190  Address frame_pointer = 0;
191  {
192  StackFrameIterator it(isolate, isolate->thread_local_top());
193  // On top: C entry stub.
194  DCHECK_EQ(StackFrame::EXIT, it.frame()->type());
195  it.Advance();
196  // Next: the wasm interpreter entry.
197  DCHECK_EQ(StackFrame::WASM_INTERPRETER_ENTRY, it.frame()->type());
198  instance = handle(
199  WasmInterpreterEntryFrame::cast(it.frame())->wasm_instance(), isolate);
200  frame_pointer = it.frame()->fp();
201  }
202 
203  // Set the current isolate's context.
204  DCHECK(isolate->context().is_null());
205  isolate->set_context(instance->native_context());
206 
207  // Run the function in the interpreter. Note that neither the {WasmDebugInfo}
208  // nor the {InterpreterHandle} have to exist, because interpretation might
209  // have been triggered by another Isolate sharing the same WasmEngine.
210  Handle<WasmDebugInfo> debug_info =
211  WasmInstanceObject::GetOrCreateDebugInfo(instance);
212  bool success = WasmDebugInfo::RunInterpreter(
213  isolate, debug_info, frame_pointer, func_index, arg_buffer);
214 
215  if (!success) {
216  DCHECK(isolate->has_pending_exception());
217  return ReadOnlyRoots(isolate).exception();
218  }
219  return ReadOnlyRoots(isolate).undefined_value();
220 }
221 
222 RUNTIME_FUNCTION(Runtime_WasmStackGuard) {
223  SealHandleScope shs(isolate);
224  DCHECK_EQ(0, args.length());
225  DCHECK(!trap_handler::IsTrapHandlerEnabled() ||
226  trap_handler::IsThreadInWasm());
227 
228  ClearThreadInWasmScope wasm_flag;
229 
230  // Check if this is a real stack overflow.
231  StackLimitCheck check(isolate);
232  if (check.JsHasOverflowed()) return isolate->StackOverflow();
233 
234  return isolate->stack_guard()->HandleInterrupts();
235 }
236 
237 RUNTIME_FUNCTION(Runtime_WasmCompileLazy) {
238  HandleScope scope(isolate);
239  DCHECK_EQ(2, args.length());
240  CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
241  CONVERT_SMI_ARG_CHECKED(func_index, 1);
242 
243  ClearThreadInWasmScope wasm_flag;
244 
245 #ifdef DEBUG
246  StackFrameIterator it(isolate, isolate->thread_local_top());
247  // On top: C entry stub.
248  DCHECK_EQ(StackFrame::EXIT, it.frame()->type());
249  it.Advance();
250  // Next: the wasm lazy compile frame.
251  DCHECK_EQ(StackFrame::WASM_COMPILE_LAZY, it.frame()->type());
252  DCHECK_EQ(*instance, WasmCompileLazyFrame::cast(it.frame())->wasm_instance());
253 #endif
254 
255  Address entrypoint = wasm::CompileLazy(
256  isolate, instance->module_object()->native_module(), func_index);
257  return reinterpret_cast<Object*>(entrypoint);
258 }
259 
260 // Should be called from within a handle scope
261 Handle<JSArrayBuffer> getSharedArrayBuffer(Handle<WasmInstanceObject> instance,
262  Isolate* isolate, uint32_t address) {
263  DCHECK(instance->has_memory_object());
264  Handle<JSArrayBuffer> array_buffer(instance->memory_object()->array_buffer(),
265  isolate);
266 
267  // Validation should have failed if the memory was not shared.
268  DCHECK(array_buffer->is_shared());
269 
270  // Should have trapped if address was OOB
271  DCHECK_LT(address, array_buffer->byte_length());
272  return array_buffer;
273 }
274 
275 RUNTIME_FUNCTION(Runtime_WasmAtomicWake) {
276  HandleScope scope(isolate);
277  DCHECK_EQ(3, args.length());
278  CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
279  CONVERT_NUMBER_CHECKED(uint32_t, address, Uint32, args[1]);
280  CONVERT_NUMBER_CHECKED(uint32_t, count, Uint32, args[2]);
281  Handle<JSArrayBuffer> array_buffer =
282  getSharedArrayBuffer(instance, isolate, address);
283  return FutexEmulation::Wake(array_buffer, address, count);
284 }
285 
286 RUNTIME_FUNCTION(Runtime_WasmI32AtomicWait) {
287  HandleScope scope(isolate);
288  DCHECK_EQ(4, args.length());
289  CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
290  CONVERT_NUMBER_CHECKED(uint32_t, address, Uint32, args[1]);
291  CONVERT_NUMBER_CHECKED(int32_t, expected_value, Int32, args[2]);
292  CONVERT_DOUBLE_ARG_CHECKED(timeout, 3);
293  timeout = timeout < 0 ? V8_INFINITY
294  : timeout / (base::Time::kNanosecondsPerMicrosecond *
295  base::Time::kMicrosecondsPerMillisecond);
296  Handle<JSArrayBuffer> array_buffer =
297  getSharedArrayBuffer(instance, isolate, address);
298  return FutexEmulation::Wait(isolate, array_buffer, address, expected_value,
299  timeout);
300 }
301 
302 } // namespace internal
303 } // namespace v8
Definition: libplatform.h:13