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
25namespace v8 {
26namespace internal {
27
28namespace {
29
30WasmInstanceObject 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
41Context GetNativeContextFromWasmInstanceOnStackTop(Isolate* isolate) {
42 return GetWasmInstanceOnStackTop(isolate)->native_context();
43}
44
45class 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
58Object 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
65RUNTIME_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
79RUNTIME_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
97RUNTIME_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
104RUNTIME_FUNCTION(Runtime_ThrowWasmStackOverflow) {
105 SealHandleScope shs(isolate);
106 DCHECK_LE(0, args.length());
107 return isolate->StackOverflow();
108}
109
110RUNTIME_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
117RUNTIME_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
143RUNTIME_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
155RUNTIME_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
167RUNTIME_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
292RUNTIME_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
307RUNTIME_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
333Handle<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
347RUNTIME_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
358double WaitTimeoutInMs(double timeout_ns) {
359 return timeout_ns < 0
360 ? V8_INFINITY
361 : timeout_ns / (base::Time::kNanosecondsPerMicrosecond *
362 base::Time::kMicrosecondsPerMillisecond);
363}
364
365RUNTIME_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
379RUNTIME_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
396namespace {
397Object 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
410RUNTIME_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
430RUNTIME_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
453RUNTIME_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
514RUNTIME_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
563RUNTIME_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
583RUNTIME_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