Line data Source code
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 4105 : WasmInstanceObject GetWasmInstanceOnStackTop(Isolate* isolate) {
30 4105 : StackFrameIterator it(isolate, isolate->thread_local_top());
31 : // On top: C entry stub.
32 : DCHECK_EQ(StackFrame::EXIT, it.frame()->type());
33 4105 : it.Advance();
34 : // Next: the wasm compiled frame.
35 : DCHECK(it.frame()->is_wasm_compiled());
36 4105 : WasmCompiledFrame* frame = WasmCompiledFrame::cast(it.frame());
37 8210 : return frame->wasm_instance();
38 : }
39 :
40 2413 : Context GetNativeContextFromWasmInstanceOnStackTop(Isolate* isolate) {
41 2413 : return GetWasmInstanceOnStackTop(isolate)->native_context();
42 : }
43 :
44 : class ClearThreadInWasmScope {
45 : public:
46 218462 : ClearThreadInWasmScope() {
47 : DCHECK_EQ(trap_handler::IsTrapHandlerEnabled(),
48 : trap_handler::IsThreadInWasm());
49 : trap_handler::ClearThreadInWasm();
50 218462 : }
51 218462 : ~ClearThreadInWasmScope() {
52 : DCHECK(!trap_handler::IsThreadInWasm());
53 : trap_handler::SetThreadInWasm();
54 218462 : }
55 : };
56 :
57 : } // namespace
58 :
59 36 : RUNTIME_FUNCTION(Runtime_WasmIsValidAnyFuncValue) {
60 36 : HandleScope scope(isolate);
61 : DCHECK_EQ(1, args.length());
62 36 : CONVERT_ARG_HANDLE_CHECKED(Object, function, 0);
63 :
64 72 : if (function->IsNull(isolate)) {
65 0 : return Smi::FromInt(true);
66 : }
67 36 : if (WasmExportedFunction::IsWasmExportedFunction(*function)) {
68 18 : return Smi::FromInt(true);
69 : }
70 18 : return Smi::FromInt(false);
71 : }
72 :
73 2196 : RUNTIME_FUNCTION(Runtime_WasmMemoryGrow) {
74 2196 : HandleScope scope(isolate);
75 : DCHECK_EQ(2, args.length());
76 4392 : CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
77 : // {delta_pages} is checked to be a positive smi in the WasmMemoryGrow builtin
78 : // which calls this runtime function.
79 4392 : CONVERT_UINT32_ARG_CHECKED(delta_pages, 1);
80 :
81 : // This runtime function is always being called from wasm code.
82 4392 : ClearThreadInWasmScope flag_scope;
83 :
84 : int ret = WasmMemoryObject::Grow(
85 6588 : isolate, handle(instance->memory_object(), isolate), delta_pages);
86 : // The WasmMemoryGrow builtin which calls this runtime function expects us to
87 : // always return a Smi.
88 4392 : return Smi::FromInt(ret);
89 : }
90 :
91 167114 : RUNTIME_FUNCTION(Runtime_ThrowWasmError) {
92 : DCHECK_EQ(1, args.length());
93 334228 : CONVERT_SMI_ARG_CHECKED(message_id, 0);
94 167114 : ClearThreadInWasmScope clear_wasm_flag;
95 :
96 334228 : HandleScope scope(isolate);
97 : Handle<Object> error_obj = isolate->factory()->NewWasmRuntimeError(
98 167114 : MessageTemplateFromInt(message_id));
99 334228 : return isolate->Throw(*error_obj);
100 : }
101 :
102 10 : RUNTIME_FUNCTION(Runtime_ThrowWasmStackOverflow) {
103 : SealHandleScope shs(isolate);
104 : DCHECK_LE(0, args.length());
105 : DCHECK(isolate->context().is_null());
106 10 : isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
107 10 : return isolate->StackOverflow();
108 : }
109 :
110 126 : RUNTIME_FUNCTION(Runtime_WasmThrowTypeError) {
111 126 : HandleScope scope(isolate);
112 : DCHECK_EQ(0, args.length());
113 252 : THROW_NEW_ERROR_RETURN_FAILURE(
114 126 : isolate, NewTypeError(MessageTemplate::kWasmTrapTypeError));
115 : }
116 :
117 738 : RUNTIME_FUNCTION(Runtime_WasmThrowCreate) {
118 : // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls.
119 738 : HandleScope scope(isolate);
120 : DCHECK_EQ(2, args.length());
121 : DCHECK(isolate->context().is_null());
122 738 : isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
123 1476 : CONVERT_ARG_CHECKED(HeapObject, tag_raw, 0);
124 1476 : CONVERT_SMI_ARG_CHECKED(size, 1);
125 : // TODO(mstarzinger): Manually box because parameters are not visited yet.
126 : Handle<Object> tag(tag_raw, isolate);
127 : Handle<Object> exception = isolate->factory()->NewWasmRuntimeError(
128 738 : MessageTemplate::kWasmExceptionError);
129 1476 : CHECK(!Object::SetProperty(isolate, exception,
130 : isolate->factory()->wasm_exception_tag_symbol(),
131 : tag, LanguageMode::kStrict)
132 : .is_null());
133 738 : Handle<FixedArray> values = isolate->factory()->NewFixedArray(size);
134 1476 : CHECK(!Object::SetProperty(isolate, exception,
135 : isolate->factory()->wasm_exception_values_symbol(),
136 : values, LanguageMode::kStrict)
137 : .is_null());
138 738 : return *exception;
139 : }
140 :
141 540 : RUNTIME_FUNCTION(Runtime_WasmExceptionGetTag) {
142 : // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls.
143 540 : HandleScope scope(isolate);
144 : DCHECK_EQ(1, args.length());
145 : DCHECK(isolate->context().is_null());
146 540 : isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
147 540 : CONVERT_ARG_CHECKED(Object, except_obj_raw, 0);
148 : // TODO(mstarzinger): Manually box because parameters are not visited yet.
149 : Handle<Object> except_obj(except_obj_raw, isolate);
150 1620 : if (!except_obj.is_null() && except_obj->IsJSReceiver()) {
151 : Handle<JSReceiver> exception(JSReceiver::cast(*except_obj), isolate);
152 : Handle<Object> tag;
153 486 : if (JSReceiver::GetProperty(isolate, exception,
154 972 : isolate->factory()->wasm_exception_tag_symbol())
155 972 : .ToHandle(&tag)) {
156 486 : return *tag;
157 : }
158 : }
159 540 : return ReadOnlyRoots(isolate).undefined_value();
160 : }
161 :
162 1125 : RUNTIME_FUNCTION(Runtime_WasmExceptionGetValues) {
163 : // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls.
164 1125 : HandleScope scope(isolate);
165 : DCHECK_EQ(1, args.length());
166 : DCHECK(isolate->context().is_null());
167 1125 : isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
168 1125 : CONVERT_ARG_CHECKED(Object, except_obj_raw, 0);
169 : // TODO(mstarzinger): Manually box because parameters are not visited yet.
170 : Handle<Object> except_obj(except_obj_raw, isolate);
171 3375 : if (!except_obj.is_null() && except_obj->IsJSReceiver()) {
172 : Handle<JSReceiver> exception(JSReceiver::cast(*except_obj), isolate);
173 : Handle<Object> values;
174 1125 : if (JSReceiver::GetProperty(
175 : isolate, exception,
176 2250 : isolate->factory()->wasm_exception_values_symbol())
177 2250 : .ToHandle(&values)) {
178 : DCHECK(values->IsFixedArray());
179 1125 : return *values;
180 : }
181 : }
182 1125 : return ReadOnlyRoots(isolate).undefined_value();
183 : }
184 :
185 39248 : RUNTIME_FUNCTION(Runtime_WasmRunInterpreter) {
186 : DCHECK_EQ(2, args.length());
187 39248 : HandleScope scope(isolate);
188 78496 : CONVERT_NUMBER_CHECKED(int32_t, func_index, Int32, args[0]);
189 39248 : CONVERT_ARG_HANDLE_CHECKED(Object, arg_buffer_obj, 1);
190 :
191 : // The arg buffer is the raw pointer to the caller's stack. It looks like a
192 : // Smi (lowest bit not set, as checked by IsSmi), but is no valid Smi. We just
193 : // cast it back to the raw pointer.
194 78496 : CHECK(!arg_buffer_obj->IsHeapObject());
195 78496 : CHECK(arg_buffer_obj->IsSmi());
196 39248 : Address arg_buffer = arg_buffer_obj->ptr();
197 :
198 78496 : ClearThreadInWasmScope wasm_flag;
199 :
200 : // Find the frame pointer and instance of the interpreter frame on the stack.
201 : Handle<WasmInstanceObject> instance;
202 : Address frame_pointer = 0;
203 : {
204 39248 : StackFrameIterator it(isolate, isolate->thread_local_top());
205 : // On top: C entry stub.
206 : DCHECK_EQ(StackFrame::EXIT, it.frame()->type());
207 39248 : it.Advance();
208 : // Next: the wasm interpreter entry.
209 : DCHECK_EQ(StackFrame::WASM_INTERPRETER_ENTRY, it.frame()->type());
210 : instance = handle(
211 39248 : WasmInterpreterEntryFrame::cast(it.frame())->wasm_instance(), isolate);
212 39248 : frame_pointer = it.frame()->fp();
213 : }
214 :
215 : // Set the current isolate's context.
216 : DCHECK(isolate->context().is_null());
217 39248 : isolate->set_context(instance->native_context());
218 :
219 : // Run the function in the interpreter. Note that neither the {WasmDebugInfo}
220 : // nor the {InterpreterHandle} have to exist, because interpretation might
221 : // have been triggered by another Isolate sharing the same WasmEngine.
222 : Handle<WasmDebugInfo> debug_info =
223 39248 : WasmInstanceObject::GetOrCreateDebugInfo(instance);
224 : bool success = WasmDebugInfo::RunInterpreter(
225 39248 : isolate, debug_info, frame_pointer, func_index, arg_buffer);
226 :
227 39248 : if (!success) {
228 : DCHECK(isolate->has_pending_exception());
229 : return ReadOnlyRoots(isolate).exception();
230 : }
231 39248 : return ReadOnlyRoots(isolate).undefined_value();
232 : }
233 :
234 243 : RUNTIME_FUNCTION(Runtime_WasmStackGuard) {
235 : SealHandleScope shs(isolate);
236 : DCHECK_EQ(0, args.length());
237 : DCHECK(!trap_handler::IsTrapHandlerEnabled() ||
238 : trap_handler::IsThreadInWasm());
239 :
240 243 : ClearThreadInWasmScope wasm_flag;
241 :
242 : // Check if this is a real stack overflow.
243 243 : StackLimitCheck check(isolate);
244 243 : if (check.JsHasOverflowed()) return isolate->StackOverflow();
245 :
246 64 : return isolate->stack_guard()->HandleInterrupts();
247 : }
248 :
249 9661 : RUNTIME_FUNCTION(Runtime_WasmCompileLazy) {
250 9661 : HandleScope scope(isolate);
251 : DCHECK_EQ(2, args.length());
252 19322 : CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
253 19322 : CONVERT_SMI_ARG_CHECKED(func_index, 1);
254 :
255 19322 : ClearThreadInWasmScope wasm_flag;
256 :
257 : #ifdef DEBUG
258 : StackFrameIterator it(isolate, isolate->thread_local_top());
259 : // On top: C entry stub.
260 : DCHECK_EQ(StackFrame::EXIT, it.frame()->type());
261 : it.Advance();
262 : // Next: the wasm lazy compile frame.
263 : DCHECK_EQ(StackFrame::WASM_COMPILE_LAZY, it.frame()->type());
264 : DCHECK_EQ(*instance, WasmCompileLazyFrame::cast(it.frame())->wasm_instance());
265 : #endif
266 :
267 : Address entrypoint = wasm::CompileLazy(
268 19322 : isolate, instance->module_object()->native_module(), func_index);
269 19322 : return Object(entrypoint);
270 : }
271 :
272 : // Should be called from within a handle scope
273 672 : Handle<JSArrayBuffer> getSharedArrayBuffer(Handle<WasmInstanceObject> instance,
274 : Isolate* isolate, uint32_t address) {
275 : DCHECK(instance->has_memory_object());
276 1347 : Handle<JSArrayBuffer> array_buffer(instance->memory_object()->array_buffer(),
277 1347 : isolate);
278 :
279 : // Validation should have failed if the memory was not shared.
280 : DCHECK(array_buffer->is_shared());
281 :
282 : // Should have trapped if address was OOB
283 : DCHECK_LT(address, array_buffer->byte_length());
284 674 : return array_buffer;
285 : }
286 :
287 153 : RUNTIME_FUNCTION(Runtime_WasmAtomicWake) {
288 153 : HandleScope scope(isolate);
289 : DCHECK_EQ(3, args.length());
290 306 : CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
291 306 : CONVERT_NUMBER_CHECKED(uint32_t, address, Uint32, args[1]);
292 306 : CONVERT_NUMBER_CHECKED(uint32_t, count, Uint32, args[2]);
293 : Handle<JSArrayBuffer> array_buffer =
294 153 : getSharedArrayBuffer(instance, isolate, address);
295 153 : return FutexEmulation::Wake(array_buffer, address, count);
296 : }
297 :
298 521 : double WaitTimeoutInMs(double timeout_ns) {
299 : return timeout_ns < 0
300 : ? V8_INFINITY
301 : : timeout_ns / (base::Time::kNanosecondsPerMicrosecond *
302 521 : base::Time::kMicrosecondsPerMillisecond);
303 : }
304 :
305 260 : RUNTIME_FUNCTION(Runtime_WasmI32AtomicWait) {
306 260 : HandleScope scope(isolate);
307 : DCHECK_EQ(4, args.length());
308 520 : CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
309 522 : CONVERT_NUMBER_CHECKED(uint32_t, address, Uint32, args[1]);
310 522 : CONVERT_NUMBER_CHECKED(int32_t, expected_value, Int32, args[2]);
311 522 : CONVERT_DOUBLE_ARG_CHECKED(timeout_ns, 3);
312 261 : double timeout_ms = WaitTimeoutInMs(timeout_ns);
313 : Handle<JSArrayBuffer> array_buffer =
314 260 : getSharedArrayBuffer(instance, isolate, address);
315 : return FutexEmulation::Wait32(isolate, array_buffer, address, expected_value,
316 261 : timeout_ms);
317 : }
318 :
319 260 : RUNTIME_FUNCTION(Runtime_WasmI64AtomicWait) {
320 260 : HandleScope scope(isolate);
321 : DCHECK_EQ(5, args.length());
322 521 : CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
323 522 : CONVERT_NUMBER_CHECKED(uint32_t, address, Uint32, args[1]);
324 522 : CONVERT_NUMBER_CHECKED(uint32_t, expected_value_high, Uint32, args[2]);
325 522 : CONVERT_NUMBER_CHECKED(uint32_t, expected_value_low, Uint32, args[3]);
326 522 : CONVERT_DOUBLE_ARG_CHECKED(timeout_ns, 4);
327 261 : int64_t expected_value = (static_cast<uint64_t>(expected_value_high) << 32) |
328 261 : static_cast<uint64_t>(expected_value_low);
329 261 : double timeout_ms = WaitTimeoutInMs(timeout_ns);
330 : Handle<JSArrayBuffer> array_buffer =
331 261 : getSharedArrayBuffer(instance, isolate, address);
332 : return FutexEmulation::Wait64(isolate, array_buffer, address, expected_value,
333 261 : timeout_ms);
334 : }
335 :
336 0 : RUNTIME_FUNCTION(Runtime_WasmTableInit) {
337 0 : HandleScope scope(isolate);
338 : DCHECK_EQ(5, args.length());
339 : auto instance =
340 0 : Handle<WasmInstanceObject>(GetWasmInstanceOnStackTop(isolate), isolate);
341 0 : CONVERT_UINT32_ARG_CHECKED(table_index, 0);
342 0 : CONVERT_UINT32_ARG_CHECKED(elem_segment_index, 1);
343 0 : CONVERT_UINT32_ARG_CHECKED(dst, 2);
344 0 : CONVERT_UINT32_ARG_CHECKED(src, 3);
345 0 : CONVERT_UINT32_ARG_CHECKED(size, 4);
346 :
347 : PrintF(
348 : "TableInit(table_index=%u, elem_segment_index=%u, dst=%u, src=%u, "
349 : "size=%u)\n",
350 0 : table_index, elem_segment_index, dst, src, size);
351 :
352 0 : USE(instance);
353 0 : USE(table_index);
354 0 : USE(elem_segment_index);
355 0 : USE(dst);
356 0 : USE(src);
357 0 : USE(size);
358 :
359 0 : UNREACHABLE();
360 : }
361 :
362 1692 : RUNTIME_FUNCTION(Runtime_WasmTableCopy) {
363 1692 : HandleScope scope(isolate);
364 : DCHECK_EQ(4, args.length());
365 : auto instance =
366 1692 : Handle<WasmInstanceObject>(GetWasmInstanceOnStackTop(isolate), isolate);
367 3384 : CONVERT_UINT32_ARG_CHECKED(table_index, 0);
368 3384 : CONVERT_UINT32_ARG_CHECKED(dst, 1);
369 3384 : CONVERT_UINT32_ARG_CHECKED(src, 2);
370 3384 : CONVERT_UINT32_ARG_CHECKED(count, 3);
371 :
372 : bool oob = !WasmInstanceObject::CopyTableEntries(
373 1692 : isolate, instance, table_index, dst, src, count);
374 1692 : if (oob) {
375 : // Handle out-of-bounds access here in the runtime call, rather
376 : // than having the lower-level layers deal with JS exceptions.
377 : DCHECK(isolate->context().is_null());
378 1431 : isolate->set_context(instance->native_context());
379 : Handle<Object> error_obj = isolate->factory()->NewWasmRuntimeError(
380 1431 : MessageTemplate::kWasmTrapTableOutOfBounds);
381 1431 : return isolate->Throw(*error_obj);
382 : }
383 1692 : return ReadOnlyRoots(isolate).undefined_value();
384 : }
385 : } // namespace internal
386 183867 : } // namespace v8
|