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 | #include "src/wasm/wasm-value.h" |
24 | |
25 | namespace v8 { |
26 | namespace internal { |
27 | |
28 | namespace { |
29 | |
30 | WasmInstanceObject GetWasmInstanceOnStackTop(Isolate* isolate) { |
31 | StackFrameIterator it(isolate, isolate->thread_local_top()); |
32 | // On top: C entry stub. |
33 | DCHECK_EQ(StackFrame::EXIT, it.frame()->type()); |
34 | it.Advance(); |
35 | // Next: the wasm compiled frame. |
36 | DCHECK(it.frame()->is_wasm_compiled()); |
37 | WasmCompiledFrame* frame = WasmCompiledFrame::cast(it.frame()); |
38 | return frame->wasm_instance(); |
39 | } |
40 | |
41 | Context GetNativeContextFromWasmInstanceOnStackTop(Isolate* isolate) { |
42 | return GetWasmInstanceOnStackTop(isolate)->native_context(); |
43 | } |
44 | |
45 | class ClearThreadInWasmScope { |
46 | public: |
47 | ClearThreadInWasmScope() { |
48 | DCHECK_EQ(trap_handler::IsTrapHandlerEnabled(), |
49 | trap_handler::IsThreadInWasm()); |
50 | trap_handler::ClearThreadInWasm(); |
51 | } |
52 | ~ClearThreadInWasmScope() { |
53 | DCHECK(!trap_handler::IsThreadInWasm()); |
54 | trap_handler::SetThreadInWasm(); |
55 | } |
56 | }; |
57 | |
58 | Object ThrowWasmError(Isolate* isolate, MessageTemplate message) { |
59 | HandleScope scope(isolate); |
60 | Handle<Object> error_obj = isolate->factory()->NewWasmRuntimeError(message); |
61 | return isolate->Throw(*error_obj); |
62 | } |
63 | } // namespace |
64 | |
65 | RUNTIME_FUNCTION(Runtime_WasmIsValidAnyFuncValue) { |
66 | HandleScope scope(isolate); |
67 | DCHECK_EQ(1, args.length()); |
68 | CONVERT_ARG_HANDLE_CHECKED(Object, function, 0); |
69 | |
70 | if (function->IsNull(isolate)) { |
71 | return Smi::FromInt(true); |
72 | } |
73 | if (WasmExportedFunction::IsWasmExportedFunction(*function)) { |
74 | return Smi::FromInt(true); |
75 | } |
76 | return Smi::FromInt(false); |
77 | } |
78 | |
79 | RUNTIME_FUNCTION(Runtime_WasmMemoryGrow) { |
80 | HandleScope scope(isolate); |
81 | DCHECK_EQ(2, args.length()); |
82 | CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0); |
83 | // {delta_pages} is checked to be a positive smi in the WasmMemoryGrow builtin |
84 | // which calls this runtime function. |
85 | CONVERT_UINT32_ARG_CHECKED(delta_pages, 1); |
86 | |
87 | // This runtime function is always being called from wasm code. |
88 | ClearThreadInWasmScope flag_scope; |
89 | |
90 | int ret = WasmMemoryObject::Grow( |
91 | isolate, handle(instance->memory_object(), isolate), delta_pages); |
92 | // The WasmMemoryGrow builtin which calls this runtime function expects us to |
93 | // always return a Smi. |
94 | return Smi::FromInt(ret); |
95 | } |
96 | |
97 | RUNTIME_FUNCTION(Runtime_ThrowWasmError) { |
98 | ClearThreadInWasmScope clear_wasm_flag; |
99 | DCHECK_EQ(1, args.length()); |
100 | CONVERT_SMI_ARG_CHECKED(message_id, 0); |
101 | return ThrowWasmError(isolate, MessageTemplateFromInt(message_id)); |
102 | } |
103 | |
104 | RUNTIME_FUNCTION(Runtime_ThrowWasmStackOverflow) { |
105 | SealHandleScope shs(isolate); |
106 | DCHECK_LE(0, args.length()); |
107 | return isolate->StackOverflow(); |
108 | } |
109 | |
110 | RUNTIME_FUNCTION(Runtime_WasmThrowTypeError) { |
111 | HandleScope scope(isolate); |
112 | DCHECK_EQ(0, args.length()); |
113 | THROW_NEW_ERROR_RETURN_FAILURE( |
114 | isolate, NewTypeError(MessageTemplate::kWasmTrapTypeError)); |
115 | } |
116 | |
117 | RUNTIME_FUNCTION(Runtime_WasmThrowCreate) { |
118 | // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls. |
119 | HandleScope scope(isolate); |
120 | DCHECK_EQ(2, args.length()); |
121 | DCHECK(isolate->context().is_null()); |
122 | isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate)); |
123 | CONVERT_ARG_CHECKED(WasmExceptionTag, tag_raw, 0); |
124 | 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 | MessageTemplate::kWasmExceptionError); |
129 | CHECK(!Object::SetProperty(isolate, exception, |
130 | isolate->factory()->wasm_exception_tag_symbol(), |
131 | tag, StoreOrigin::kMaybeKeyed, |
132 | Just(ShouldThrow::kThrowOnError)) |
133 | .is_null()); |
134 | Handle<FixedArray> values = isolate->factory()->NewFixedArray(size); |
135 | CHECK(!Object::SetProperty(isolate, exception, |
136 | isolate->factory()->wasm_exception_values_symbol(), |
137 | values, StoreOrigin::kMaybeKeyed, |
138 | Just(ShouldThrow::kThrowOnError)) |
139 | .is_null()); |
140 | return *exception; |
141 | } |
142 | |
143 | RUNTIME_FUNCTION(Runtime_WasmExceptionGetTag) { |
144 | // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls. |
145 | HandleScope scope(isolate); |
146 | DCHECK_EQ(1, args.length()); |
147 | DCHECK(isolate->context().is_null()); |
148 | isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate)); |
149 | CONVERT_ARG_CHECKED(Object, except_obj_raw, 0); |
150 | // TODO(mstarzinger): Manually box because parameters are not visited yet. |
151 | Handle<Object> except_obj(except_obj_raw, isolate); |
152 | return *WasmExceptionPackage::GetExceptionTag(isolate, except_obj); |
153 | } |
154 | |
155 | RUNTIME_FUNCTION(Runtime_WasmExceptionGetValues) { |
156 | // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls. |
157 | HandleScope scope(isolate); |
158 | DCHECK_EQ(1, args.length()); |
159 | DCHECK(isolate->context().is_null()); |
160 | isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate)); |
161 | CONVERT_ARG_CHECKED(Object, except_obj_raw, 0); |
162 | // TODO(mstarzinger): Manually box because parameters are not visited yet. |
163 | Handle<Object> except_obj(except_obj_raw, isolate); |
164 | return *WasmExceptionPackage::GetExceptionValues(isolate, except_obj); |
165 | } |
166 | |
167 | RUNTIME_FUNCTION(Runtime_WasmRunInterpreter) { |
168 | DCHECK_EQ(2, args.length()); |
169 | HandleScope scope(isolate); |
170 | CONVERT_NUMBER_CHECKED(int32_t, func_index, Int32, args[0]); |
171 | CONVERT_ARG_HANDLE_CHECKED(Object, arg_buffer_obj, 1); |
172 | |
173 | // The arg buffer is the raw pointer to the caller's stack. It looks like a |
174 | // Smi (lowest bit not set, as checked by IsSmi), but is no valid Smi. We just |
175 | // cast it back to the raw pointer. |
176 | CHECK(!arg_buffer_obj->IsHeapObject()); |
177 | CHECK(arg_buffer_obj->IsSmi()); |
178 | Address arg_buffer = arg_buffer_obj->ptr(); |
179 | |
180 | ClearThreadInWasmScope wasm_flag; |
181 | |
182 | // Find the frame pointer and instance of the interpreter frame on the stack. |
183 | Handle<WasmInstanceObject> instance; |
184 | Address frame_pointer = 0; |
185 | { |
186 | StackFrameIterator it(isolate, isolate->thread_local_top()); |
187 | // On top: C entry stub. |
188 | DCHECK_EQ(StackFrame::EXIT, it.frame()->type()); |
189 | it.Advance(); |
190 | // Next: the wasm interpreter entry. |
191 | DCHECK_EQ(StackFrame::WASM_INTERPRETER_ENTRY, it.frame()->type()); |
192 | instance = handle( |
193 | WasmInterpreterEntryFrame::cast(it.frame())->wasm_instance(), isolate); |
194 | frame_pointer = it.frame()->fp(); |
195 | } |
196 | |
197 | // Reserve buffers for argument and return values. |
198 | DCHECK_GE(instance->module()->functions.size(), func_index); |
199 | wasm::FunctionSig* sig = instance->module()->functions[func_index].sig; |
200 | DCHECK_GE(kMaxInt, sig->parameter_count()); |
201 | int num_params = static_cast<int>(sig->parameter_count()); |
202 | ScopedVector<wasm::WasmValue> wasm_args(num_params); |
203 | DCHECK_GE(kMaxInt, sig->return_count()); |
204 | int num_returns = static_cast<int>(sig->return_count()); |
205 | ScopedVector<wasm::WasmValue> wasm_rets(num_returns); |
206 | |
207 | // Copy the arguments for the {arg_buffer} into a vector of {WasmValue}. This |
208 | // also boxes reference types into handles, which needs to happen before any |
209 | // methods that could trigger a GC are being called. |
210 | Address arg_buf_ptr = arg_buffer; |
211 | for (int i = 0; i < num_params; ++i) { |
212 | #define CASE_ARG_TYPE(type, ctype) \ |
213 | case wasm::type: \ |
214 | DCHECK_EQ(wasm::ValueTypes::ElementSizeInBytes(sig->GetParam(i)), \ |
215 | sizeof(ctype)); \ |
216 | wasm_args[i] = wasm::WasmValue(ReadUnalignedValue<ctype>(arg_buf_ptr)); \ |
217 | arg_buf_ptr += sizeof(ctype); \ |
218 | break; |
219 | switch (sig->GetParam(i)) { |
220 | CASE_ARG_TYPE(kWasmI32, uint32_t) |
221 | CASE_ARG_TYPE(kWasmI64, uint64_t) |
222 | CASE_ARG_TYPE(kWasmF32, float) |
223 | CASE_ARG_TYPE(kWasmF64, double) |
224 | #undef CASE_ARG_TYPE |
225 | case wasm::kWasmAnyRef: |
226 | case wasm::kWasmAnyFunc: |
227 | case wasm::kWasmExceptRef: { |
228 | DCHECK_EQ(wasm::ValueTypes::ElementSizeInBytes(sig->GetParam(i)), |
229 | kSystemPointerSize); |
230 | Handle<Object> ref(ReadUnalignedValue<Object>(arg_buf_ptr), isolate); |
231 | wasm_args[i] = wasm::WasmValue(ref); |
232 | arg_buf_ptr += kSystemPointerSize; |
233 | break; |
234 | } |
235 | default: |
236 | UNREACHABLE(); |
237 | } |
238 | } |
239 | |
240 | // Set the current isolate's context. |
241 | DCHECK(isolate->context().is_null()); |
242 | isolate->set_context(instance->native_context()); |
243 | |
244 | // Run the function in the interpreter. Note that neither the {WasmDebugInfo} |
245 | // nor the {InterpreterHandle} have to exist, because interpretation might |
246 | // have been triggered by another Isolate sharing the same WasmEngine. |
247 | Handle<WasmDebugInfo> debug_info = |
248 | WasmInstanceObject::GetOrCreateDebugInfo(instance); |
249 | bool success = WasmDebugInfo::RunInterpreter( |
250 | isolate, debug_info, frame_pointer, func_index, wasm_args, wasm_rets); |
251 | |
252 | // Early return on failure. |
253 | if (!success) { |
254 | DCHECK(isolate->has_pending_exception()); |
255 | return ReadOnlyRoots(isolate).exception(); |
256 | } |
257 | |
258 | // Copy return values from the vector of {WasmValue} into {arg_buffer}. This |
259 | // also un-boxes reference types from handles into raw pointers. |
260 | arg_buf_ptr = arg_buffer; |
261 | for (int i = 0; i < num_returns; ++i) { |
262 | #define CASE_RET_TYPE(type, ctype) \ |
263 | case wasm::type: \ |
264 | DCHECK_EQ(wasm::ValueTypes::ElementSizeInBytes(sig->GetReturn(i)), \ |
265 | sizeof(ctype)); \ |
266 | WriteUnalignedValue<ctype>(arg_buf_ptr, wasm_rets[i].to<ctype>()); \ |
267 | arg_buf_ptr += sizeof(ctype); \ |
268 | break; |
269 | switch (sig->GetReturn(i)) { |
270 | CASE_RET_TYPE(kWasmI32, uint32_t) |
271 | CASE_RET_TYPE(kWasmI64, uint64_t) |
272 | CASE_RET_TYPE(kWasmF32, float) |
273 | CASE_RET_TYPE(kWasmF64, double) |
274 | #undef CASE_RET_TYPE |
275 | case wasm::kWasmAnyRef: |
276 | case wasm::kWasmAnyFunc: |
277 | case wasm::kWasmExceptRef: { |
278 | DCHECK_EQ(wasm::ValueTypes::ElementSizeInBytes(sig->GetReturn(i)), |
279 | kSystemPointerSize); |
280 | WriteUnalignedValue<Object>(arg_buf_ptr, *wasm_rets[i].to_anyref()); |
281 | arg_buf_ptr += kSystemPointerSize; |
282 | break; |
283 | } |
284 | default: |
285 | UNREACHABLE(); |
286 | } |
287 | } |
288 | |
289 | return ReadOnlyRoots(isolate).undefined_value(); |
290 | } |
291 | |
292 | RUNTIME_FUNCTION(Runtime_WasmStackGuard) { |
293 | SealHandleScope shs(isolate); |
294 | DCHECK_EQ(0, args.length()); |
295 | DCHECK(!trap_handler::IsTrapHandlerEnabled() || |
296 | trap_handler::IsThreadInWasm()); |
297 | |
298 | ClearThreadInWasmScope wasm_flag; |
299 | |
300 | // Check if this is a real stack overflow. |
301 | StackLimitCheck check(isolate); |
302 | if (check.JsHasOverflowed()) return isolate->StackOverflow(); |
303 | |
304 | return isolate->stack_guard()->HandleInterrupts(); |
305 | } |
306 | |
307 | RUNTIME_FUNCTION(Runtime_WasmCompileLazy) { |
308 | HandleScope scope(isolate); |
309 | DCHECK_EQ(2, args.length()); |
310 | CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0); |
311 | CONVERT_SMI_ARG_CHECKED(func_index, 1); |
312 | |
313 | ClearThreadInWasmScope wasm_flag; |
314 | |
315 | #ifdef DEBUG |
316 | StackFrameIterator it(isolate, isolate->thread_local_top()); |
317 | // On top: C entry stub. |
318 | DCHECK_EQ(StackFrame::EXIT, it.frame()->type()); |
319 | it.Advance(); |
320 | // Next: the wasm lazy compile frame. |
321 | DCHECK_EQ(StackFrame::WASM_COMPILE_LAZY, it.frame()->type()); |
322 | DCHECK_EQ(*instance, WasmCompileLazyFrame::cast(it.frame())->wasm_instance()); |
323 | #endif |
324 | |
325 | auto* native_module = instance->module_object()->native_module(); |
326 | wasm::CompileLazy(isolate, native_module, func_index); |
327 | |
328 | Address entrypoint = native_module->GetCallTargetForFunction(func_index); |
329 | return Object(entrypoint); |
330 | } |
331 | |
332 | // Should be called from within a handle scope |
333 | Handle<JSArrayBuffer> getSharedArrayBuffer(Handle<WasmInstanceObject> instance, |
334 | Isolate* isolate, uint32_t address) { |
335 | DCHECK(instance->has_memory_object()); |
336 | Handle<JSArrayBuffer> array_buffer(instance->memory_object()->array_buffer(), |
337 | isolate); |
338 | |
339 | // Validation should have failed if the memory was not shared. |
340 | DCHECK(array_buffer->is_shared()); |
341 | |
342 | // Should have trapped if address was OOB |
343 | DCHECK_LT(address, array_buffer->byte_length()); |
344 | return array_buffer; |
345 | } |
346 | |
347 | RUNTIME_FUNCTION(Runtime_WasmAtomicNotify) { |
348 | HandleScope scope(isolate); |
349 | DCHECK_EQ(3, args.length()); |
350 | CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0); |
351 | CONVERT_NUMBER_CHECKED(uint32_t, address, Uint32, args[1]); |
352 | CONVERT_NUMBER_CHECKED(uint32_t, count, Uint32, args[2]); |
353 | Handle<JSArrayBuffer> array_buffer = |
354 | getSharedArrayBuffer(instance, isolate, address); |
355 | return FutexEmulation::Wake(array_buffer, address, count); |
356 | } |
357 | |
358 | double WaitTimeoutInMs(double timeout_ns) { |
359 | return timeout_ns < 0 |
360 | ? V8_INFINITY |
361 | : timeout_ns / (base::Time::kNanosecondsPerMicrosecond * |
362 | base::Time::kMicrosecondsPerMillisecond); |
363 | } |
364 | |
365 | RUNTIME_FUNCTION(Runtime_WasmI32AtomicWait) { |
366 | HandleScope scope(isolate); |
367 | DCHECK_EQ(4, args.length()); |
368 | CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0); |
369 | CONVERT_NUMBER_CHECKED(uint32_t, address, Uint32, args[1]); |
370 | CONVERT_NUMBER_CHECKED(int32_t, expected_value, Int32, args[2]); |
371 | CONVERT_DOUBLE_ARG_CHECKED(timeout_ns, 3); |
372 | double timeout_ms = WaitTimeoutInMs(timeout_ns); |
373 | Handle<JSArrayBuffer> array_buffer = |
374 | getSharedArrayBuffer(instance, isolate, address); |
375 | return FutexEmulation::Wait32(isolate, array_buffer, address, expected_value, |
376 | timeout_ms); |
377 | } |
378 | |
379 | RUNTIME_FUNCTION(Runtime_WasmI64AtomicWait) { |
380 | HandleScope scope(isolate); |
381 | DCHECK_EQ(5, args.length()); |
382 | CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0); |
383 | CONVERT_NUMBER_CHECKED(uint32_t, address, Uint32, args[1]); |
384 | CONVERT_NUMBER_CHECKED(uint32_t, expected_value_high, Uint32, args[2]); |
385 | CONVERT_NUMBER_CHECKED(uint32_t, expected_value_low, Uint32, args[3]); |
386 | CONVERT_DOUBLE_ARG_CHECKED(timeout_ns, 4); |
387 | int64_t expected_value = (static_cast<uint64_t>(expected_value_high) << 32) | |
388 | static_cast<uint64_t>(expected_value_low); |
389 | double timeout_ms = WaitTimeoutInMs(timeout_ns); |
390 | Handle<JSArrayBuffer> array_buffer = |
391 | getSharedArrayBuffer(instance, isolate, address); |
392 | return FutexEmulation::Wait64(isolate, array_buffer, address, expected_value, |
393 | timeout_ms); |
394 | } |
395 | |
396 | namespace { |
397 | Object ThrowTableOutOfBounds(Isolate* isolate, |
398 | Handle<WasmInstanceObject> instance) { |
399 | // Handle out-of-bounds access here in the runtime call, rather |
400 | // than having the lower-level layers deal with JS exceptions. |
401 | if (isolate->context().is_null()) { |
402 | isolate->set_context(instance->native_context()); |
403 | } |
404 | Handle<Object> error_obj = isolate->factory()->NewWasmRuntimeError( |
405 | MessageTemplate::kWasmTrapTableOutOfBounds); |
406 | return isolate->Throw(*error_obj); |
407 | } |
408 | } // namespace |
409 | |
410 | RUNTIME_FUNCTION(Runtime_WasmFunctionTableGet) { |
411 | // This runtime function is always being called from wasm code. |
412 | ClearThreadInWasmScope flag_scope; |
413 | |
414 | HandleScope scope(isolate); |
415 | DCHECK_EQ(3, args.length()); |
416 | CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0); |
417 | CONVERT_UINT32_ARG_CHECKED(table_index, 1); |
418 | CONVERT_UINT32_ARG_CHECKED(entry_index, 2); |
419 | DCHECK_LT(table_index, instance->tables()->length()); |
420 | auto table = handle( |
421 | WasmTableObject::cast(instance->tables()->get(table_index)), isolate); |
422 | |
423 | if (!WasmTableObject::IsInBounds(isolate, table, entry_index)) { |
424 | return ThrowWasmError(isolate, MessageTemplate::kWasmTrapTableOutOfBounds); |
425 | } |
426 | |
427 | return *WasmTableObject::Get(isolate, table, entry_index); |
428 | } |
429 | |
430 | RUNTIME_FUNCTION(Runtime_WasmFunctionTableSet) { |
431 | // This runtime function is always being called from wasm code. |
432 | ClearThreadInWasmScope flag_scope; |
433 | |
434 | HandleScope scope(isolate); |
435 | DCHECK_EQ(4, args.length()); |
436 | CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0); |
437 | CONVERT_UINT32_ARG_CHECKED(table_index, 1); |
438 | CONVERT_UINT32_ARG_CHECKED(entry_index, 2); |
439 | CONVERT_ARG_CHECKED(Object, element_raw, 3); |
440 | // TODO(mstarzinger): Manually box because parameters are not visited yet. |
441 | Handle<Object> element(element_raw, isolate); |
442 | DCHECK_LT(table_index, instance->tables()->length()); |
443 | auto table = handle( |
444 | WasmTableObject::cast(instance->tables()->get(table_index)), isolate); |
445 | |
446 | if (!WasmTableObject::IsInBounds(isolate, table, entry_index)) { |
447 | return ThrowWasmError(isolate, MessageTemplate::kWasmTrapTableOutOfBounds); |
448 | } |
449 | WasmTableObject::Set(isolate, table, entry_index, element); |
450 | return ReadOnlyRoots(isolate).undefined_value(); |
451 | } |
452 | |
453 | RUNTIME_FUNCTION(Runtime_WasmIndirectCallCheckSignatureAndGetTargetInstance) { |
454 | HandleScope scope(isolate); |
455 | DCHECK_EQ(3, args.length()); |
456 | auto instance = |
457 | Handle<WasmInstanceObject>(GetWasmInstanceOnStackTop(isolate), isolate); |
458 | CONVERT_UINT32_ARG_CHECKED(table_index, 0); |
459 | CONVERT_UINT32_ARG_CHECKED(entry_index, 1); |
460 | CONVERT_UINT32_ARG_CHECKED(sig_index, 2); |
461 | DCHECK(isolate->context().is_null()); |
462 | isolate->set_context(instance->native_context()); |
463 | |
464 | DCHECK_LT(table_index, instance->tables()->length()); |
465 | auto table_obj = handle( |
466 | WasmTableObject::cast(instance->tables()->get(table_index)), isolate); |
467 | |
468 | // This check is already done in generated code. |
469 | DCHECK(WasmTableObject::IsInBounds(isolate, table_obj, entry_index)); |
470 | |
471 | bool is_valid; |
472 | bool is_null; |
473 | MaybeHandle<WasmInstanceObject> maybe_target_instance; |
474 | int function_index; |
475 | WasmTableObject::GetFunctionTableEntry( |
476 | isolate, table_obj, entry_index, &is_valid, &is_null, |
477 | &maybe_target_instance, &function_index); |
478 | |
479 | CHECK(is_valid); |
480 | if (is_null) { |
481 | // We throw a signature mismatch trap to be in sync with the generated |
482 | // code. There we do a signature check instead of a null-check. Trap |
483 | // reasons are not defined in the spec. Otherwise, a null-check is |
484 | // performed before a signature, according to the spec. |
485 | return ThrowWasmError(isolate, MessageTemplate::kWasmTrapFuncSigMismatch); |
486 | } |
487 | |
488 | // Now we do the signature check. |
489 | Handle<WasmInstanceObject> target_instance = |
490 | maybe_target_instance.ToHandleChecked(); |
491 | |
492 | const wasm::WasmModule* target_module = |
493 | target_instance->module_object()->native_module()->module(); |
494 | |
495 | wasm::FunctionSig* target_sig = target_module->functions[function_index].sig; |
496 | |
497 | auto target_sig_id = instance->module()->signature_map.Find(*target_sig); |
498 | uint32_t expected_sig_id = instance->module()->signature_ids[sig_index]; |
499 | |
500 | if (expected_sig_id != static_cast<uint32_t>(target_sig_id)) { |
501 | return ThrowWasmError(isolate, MessageTemplate::kWasmTrapFuncSigMismatch); |
502 | } |
503 | |
504 | if (function_index < |
505 | static_cast<int>(target_instance->module()->num_imported_functions)) { |
506 | // The function in the target instance was imported. Use its imports table, |
507 | // which contains a tuple needed by the import wrapper. |
508 | ImportedFunctionEntry entry(target_instance, function_index); |
509 | return entry.object_ref(); |
510 | } |
511 | return *target_instance; |
512 | } |
513 | |
514 | RUNTIME_FUNCTION(Runtime_WasmIndirectCallGetTargetAddress) { |
515 | HandleScope scope(isolate); |
516 | DCHECK_EQ(2, args.length()); |
517 | auto instance = |
518 | Handle<WasmInstanceObject>(GetWasmInstanceOnStackTop(isolate), isolate); |
519 | CONVERT_UINT32_ARG_CHECKED(table_index, 0); |
520 | CONVERT_UINT32_ARG_CHECKED(entry_index, 1); |
521 | |
522 | DCHECK_LT(table_index, instance->tables()->length()); |
523 | auto table_obj = handle( |
524 | WasmTableObject::cast(instance->tables()->get(table_index)), isolate); |
525 | |
526 | DCHECK(WasmTableObject::IsInBounds(isolate, table_obj, entry_index)); |
527 | |
528 | bool is_valid; |
529 | bool is_null; |
530 | MaybeHandle<WasmInstanceObject> maybe_target_instance; |
531 | int function_index; |
532 | WasmTableObject::GetFunctionTableEntry( |
533 | isolate, table_obj, entry_index, &is_valid, &is_null, |
534 | &maybe_target_instance, &function_index); |
535 | |
536 | CHECK(is_valid); |
537 | // The null-check should already have been done in |
538 | // Runtime_WasmIndirectCallCheckSignatureAndGetTargetInstance. That runtime |
539 | // function should always be called first. |
540 | CHECK(!is_null); |
541 | |
542 | Handle<WasmInstanceObject> target_instance = |
543 | maybe_target_instance.ToHandleChecked(); |
544 | |
545 | Address call_target = 0; |
546 | if (function_index < |
547 | static_cast<int>(target_instance->module()->num_imported_functions)) { |
548 | // The function in the target instance was imported. Use its imports table, |
549 | // which contains a tuple needed by the import wrapper. |
550 | ImportedFunctionEntry entry(target_instance, function_index); |
551 | call_target = entry.target(); |
552 | } else { |
553 | // The function in the target instance was not imported. |
554 | call_target = target_instance->GetCallTarget(function_index); |
555 | } |
556 | |
557 | // The return value is an address and not a SMI. However, the address is |
558 | // always aligned, and a SMI uses the same space as {Address}. |
559 | CHECK(HAS_SMI_TAG(call_target)); |
560 | return Smi(call_target); |
561 | } |
562 | |
563 | RUNTIME_FUNCTION(Runtime_WasmTableInit) { |
564 | HandleScope scope(isolate); |
565 | DCHECK_EQ(5, args.length()); |
566 | auto instance = |
567 | Handle<WasmInstanceObject>(GetWasmInstanceOnStackTop(isolate), isolate); |
568 | CONVERT_UINT32_ARG_CHECKED(table_index, 0); |
569 | CONVERT_UINT32_ARG_CHECKED(elem_segment_index, 1); |
570 | CONVERT_UINT32_ARG_CHECKED(dst, 2); |
571 | CONVERT_UINT32_ARG_CHECKED(src, 3); |
572 | CONVERT_UINT32_ARG_CHECKED(count, 4); |
573 | |
574 | DCHECK(isolate->context().is_null()); |
575 | isolate->set_context(instance->native_context()); |
576 | |
577 | bool oob = !WasmInstanceObject::InitTableEntries( |
578 | isolate, instance, table_index, elem_segment_index, dst, src, count); |
579 | if (oob) return ThrowTableOutOfBounds(isolate, instance); |
580 | return ReadOnlyRoots(isolate).undefined_value(); |
581 | } |
582 | |
583 | RUNTIME_FUNCTION(Runtime_WasmTableCopy) { |
584 | HandleScope scope(isolate); |
585 | DCHECK_EQ(5, args.length()); |
586 | auto instance = |
587 | Handle<WasmInstanceObject>(GetWasmInstanceOnStackTop(isolate), isolate); |
588 | CONVERT_UINT32_ARG_CHECKED(table_src_index, 0); |
589 | CONVERT_UINT32_ARG_CHECKED(table_dst_index, 1); |
590 | CONVERT_UINT32_ARG_CHECKED(dst, 2); |
591 | CONVERT_UINT32_ARG_CHECKED(src, 3); |
592 | CONVERT_UINT32_ARG_CHECKED(count, 4); |
593 | |
594 | bool oob = !WasmInstanceObject::CopyTableEntries( |
595 | isolate, instance, table_src_index, table_dst_index, dst, src, count); |
596 | if (oob) return ThrowTableOutOfBounds(isolate, instance); |
597 | return ReadOnlyRoots(isolate).undefined_value(); |
598 | } |
599 | } // namespace internal |
600 | } // namespace v8 |
601 | |