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/base/memory.h"
6 #include "src/common/message-template.h"
7 #include "src/compiler/wasm-compiler.h"
8 #include "src/debug/debug.h"
9 #include "src/execution/arguments-inl.h"
10 #include "src/execution/frame-constants.h"
11 #include "src/execution/frames.h"
12 #include "src/heap/factory.h"
13 #include "src/logging/counters.h"
14 #include "src/numbers/conversions.h"
15 #include "src/objects/objects-inl.h"
16 #include "src/runtime/runtime-utils.h"
17 #include "src/trap-handler/trap-handler.h"
18 #include "src/wasm/module-compiler.h"
19 #include "src/wasm/value-type.h"
20 #include "src/wasm/wasm-code-manager.h"
21 #include "src/wasm/wasm-constants.h"
22 #include "src/wasm/wasm-debug.h"
23 #include "src/wasm/wasm-engine.h"
24 #include "src/wasm/wasm-objects.h"
25 #include "src/wasm/wasm-subtyping.h"
26 #include "src/wasm/wasm-value.h"
27
28 namespace v8 {
29 namespace internal {
30
31 namespace {
32
33 template <typename FrameType>
34 class FrameFinder {
35 public:
FrameFinder(Isolate * isolate,std::initializer_list<StackFrame::Type> skipped_frame_types={StackFrame::EXIT})36 explicit FrameFinder(Isolate* isolate,
37 std::initializer_list<StackFrame::Type>
38 skipped_frame_types = {StackFrame::EXIT})
39 : frame_iterator_(isolate, isolate->thread_local_top()) {
40 // We skip at least one frame.
41 DCHECK_LT(0, skipped_frame_types.size());
42
43 for (auto type : skipped_frame_types) {
44 DCHECK_EQ(type, frame_iterator_.frame()->type());
45 USE(type);
46 frame_iterator_.Advance();
47 }
48 // Type check the frame where the iterator stopped now.
49 DCHECK_NOT_NULL(frame());
50 }
51
frame()52 FrameType* frame() { return FrameType::cast(frame_iterator_.frame()); }
53
54 private:
55 StackFrameIterator frame_iterator_;
56 };
57
GetWasmInstanceOnStackTop(Isolate * isolate)58 WasmInstanceObject GetWasmInstanceOnStackTop(Isolate* isolate) {
59 return FrameFinder<WasmFrame>(isolate).frame()->wasm_instance();
60 }
61
GetNativeContextFromWasmInstanceOnStackTop(Isolate * isolate)62 Context GetNativeContextFromWasmInstanceOnStackTop(Isolate* isolate) {
63 return GetWasmInstanceOnStackTop(isolate).native_context();
64 }
65
66 class V8_NODISCARD ClearThreadInWasmScope {
67 public:
ClearThreadInWasmScope(Isolate * isolate)68 explicit ClearThreadInWasmScope(Isolate* isolate) : isolate_(isolate) {
69 DCHECK_IMPLIES(trap_handler::IsTrapHandlerEnabled(),
70 trap_handler::IsThreadInWasm());
71 trap_handler::ClearThreadInWasm();
72 }
~ClearThreadInWasmScope()73 ~ClearThreadInWasmScope() {
74 DCHECK_IMPLIES(trap_handler::IsTrapHandlerEnabled(),
75 !trap_handler::IsThreadInWasm());
76 if (!isolate_->has_pending_exception()) {
77 trap_handler::SetThreadInWasm();
78 }
79 // Otherwise we only want to set the flag if the exception is caught in
80 // wasm. This is handled by the unwinder.
81 }
82
83 private:
84 Isolate* isolate_;
85 };
86
ThrowWasmError(Isolate * isolate,MessageTemplate message)87 Object ThrowWasmError(Isolate* isolate, MessageTemplate message) {
88 Handle<JSObject> error_obj = isolate->factory()->NewWasmRuntimeError(message);
89 JSObject::AddProperty(isolate, error_obj,
90 isolate->factory()->wasm_uncatchable_symbol(),
91 isolate->factory()->true_value(), NONE);
92 return isolate->Throw(*error_obj);
93 }
94 } // namespace
95
RUNTIME_FUNCTION(Runtime_WasmIsValidRefValue)96 RUNTIME_FUNCTION(Runtime_WasmIsValidRefValue) {
97 // This code is called from wrappers, so the "thread is wasm" flag is not set.
98 DCHECK_IMPLIES(trap_handler::IsTrapHandlerEnabled(),
99 !trap_handler::IsThreadInWasm());
100 HandleScope scope(isolate);
101 DCHECK_EQ(3, args.length());
102 CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0)
103 CONVERT_ARG_HANDLE_CHECKED(Object, value, 1);
104 // Make sure ValueType fits properly in a Smi.
105 STATIC_ASSERT(wasm::ValueType::kLastUsedBit + 1 <= kSmiValueSize);
106 CONVERT_SMI_ARG_CHECKED(raw_type, 2);
107
108 wasm::ValueType type = wasm::ValueType::FromRawBitField(raw_type);
109 const char* error_message;
110
111 bool result = internal::wasm::TypecheckJSObject(isolate, instance->module(),
112 value, type, &error_message);
113 return Smi::FromInt(result);
114 }
115
RUNTIME_FUNCTION(Runtime_WasmMemoryGrow)116 RUNTIME_FUNCTION(Runtime_WasmMemoryGrow) {
117 ClearThreadInWasmScope flag_scope(isolate);
118 HandleScope scope(isolate);
119 DCHECK_EQ(2, args.length());
120 CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
121 // {delta_pages} is checked to be a positive smi in the WasmMemoryGrow builtin
122 // which calls this runtime function.
123 CONVERT_UINT32_ARG_CHECKED(delta_pages, 1);
124
125 int ret = WasmMemoryObject::Grow(
126 isolate, handle(instance->memory_object(), isolate), delta_pages);
127 // The WasmMemoryGrow builtin which calls this runtime function expects us to
128 // always return a Smi.
129 DCHECK(!isolate->has_pending_exception());
130 return Smi::FromInt(ret);
131 }
132
RUNTIME_FUNCTION(Runtime_ThrowWasmError)133 RUNTIME_FUNCTION(Runtime_ThrowWasmError) {
134 ClearThreadInWasmScope flag_scope(isolate);
135 HandleScope scope(isolate);
136 DCHECK_EQ(1, args.length());
137 CONVERT_SMI_ARG_CHECKED(message_id, 0);
138 return ThrowWasmError(isolate, MessageTemplateFromInt(message_id));
139 }
140
RUNTIME_FUNCTION(Runtime_ThrowWasmStackOverflow)141 RUNTIME_FUNCTION(Runtime_ThrowWasmStackOverflow) {
142 ClearThreadInWasmScope clear_wasm_flag(isolate);
143 SealHandleScope shs(isolate);
144 DCHECK_LE(0, args.length());
145 return isolate->StackOverflow();
146 }
147
RUNTIME_FUNCTION(Runtime_WasmThrowJSTypeError)148 RUNTIME_FUNCTION(Runtime_WasmThrowJSTypeError) {
149 // The caller may be wasm or JS. Only clear the thread_in_wasm flag if the
150 // caller is wasm, and let the unwinder set it back depending on the handler.
151 if (trap_handler::IsTrapHandlerEnabled() && trap_handler::IsThreadInWasm()) {
152 trap_handler::ClearThreadInWasm();
153 }
154 HandleScope scope(isolate);
155 DCHECK_EQ(0, args.length());
156 THROW_NEW_ERROR_RETURN_FAILURE(
157 isolate, NewTypeError(MessageTemplate::kWasmTrapJSTypeError));
158 }
159
RUNTIME_FUNCTION(Runtime_WasmThrow)160 RUNTIME_FUNCTION(Runtime_WasmThrow) {
161 ClearThreadInWasmScope clear_wasm_flag(isolate);
162 HandleScope scope(isolate);
163 DCHECK_EQ(2, args.length());
164 isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
165
166 CONVERT_ARG_CHECKED(WasmExceptionTag, tag_raw, 0);
167 CONVERT_ARG_CHECKED(FixedArray, values_raw, 1);
168 // TODO(wasm): Manually box because parameters are not visited yet.
169 Handle<WasmExceptionTag> tag(tag_raw, isolate);
170 Handle<FixedArray> values(values_raw, isolate);
171 Handle<WasmExceptionPackage> exception =
172 WasmExceptionPackage::New(isolate, tag, values);
173 wasm::GetWasmEngine()->SampleThrowEvent(isolate);
174 return isolate->Throw(*exception);
175 }
176
RUNTIME_FUNCTION(Runtime_WasmReThrow)177 RUNTIME_FUNCTION(Runtime_WasmReThrow) {
178 ClearThreadInWasmScope clear_wasm_flag(isolate);
179 HandleScope scope(isolate);
180 DCHECK_EQ(1, args.length());
181 wasm::GetWasmEngine()->SampleRethrowEvent(isolate);
182 return isolate->ReThrow(args[0]);
183 }
184
RUNTIME_FUNCTION(Runtime_WasmStackGuard)185 RUNTIME_FUNCTION(Runtime_WasmStackGuard) {
186 ClearThreadInWasmScope wasm_flag(isolate);
187 SealHandleScope shs(isolate);
188 DCHECK_EQ(0, args.length());
189
190 // Check if this is a real stack overflow.
191 StackLimitCheck check(isolate);
192 if (check.JsHasOverflowed()) return isolate->StackOverflow();
193
194 return isolate->stack_guard()->HandleInterrupts();
195 }
196
RUNTIME_FUNCTION(Runtime_WasmCompileLazy)197 RUNTIME_FUNCTION(Runtime_WasmCompileLazy) {
198 ClearThreadInWasmScope wasm_flag(isolate);
199 HandleScope scope(isolate);
200 DCHECK_EQ(2, args.length());
201 CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
202 CONVERT_SMI_ARG_CHECKED(func_index, 1);
203
204 #ifdef DEBUG
205 FrameFinder<WasmCompileLazyFrame> frame_finder(isolate);
206 DCHECK_EQ(*instance, frame_finder.frame()->wasm_instance());
207 #endif
208
209 DCHECK(isolate->context().is_null());
210 isolate->set_context(instance->native_context());
211 Handle<WasmModuleObject> module_object{instance->module_object(), isolate};
212 bool success = wasm::CompileLazy(isolate, module_object, func_index);
213 if (!success) {
214 DCHECK(isolate->has_pending_exception());
215 return ReadOnlyRoots(isolate).exception();
216 }
217
218 Address entrypoint =
219 module_object->native_module()->GetCallTargetForFunction(func_index);
220
221 return Object(entrypoint);
222 }
223
224 namespace {
ReplaceWrapper(Isolate * isolate,Handle<WasmInstanceObject> instance,int function_index,Handle<Code> wrapper_code)225 void ReplaceWrapper(Isolate* isolate, Handle<WasmInstanceObject> instance,
226 int function_index, Handle<Code> wrapper_code) {
227 Handle<WasmExternalFunction> exported_function =
228 WasmInstanceObject::GetWasmExternalFunction(isolate, instance,
229 function_index)
230 .ToHandleChecked();
231 exported_function->set_code(*wrapper_code, kReleaseStore);
232 WasmExportedFunctionData function_data =
233 exported_function->shared().wasm_exported_function_data();
234 function_data.set_wrapper_code(*wrapper_code);
235 }
236 } // namespace
237
RUNTIME_FUNCTION(Runtime_WasmCompileWrapper)238 RUNTIME_FUNCTION(Runtime_WasmCompileWrapper) {
239 HandleScope scope(isolate);
240 DCHECK_EQ(2, args.length());
241 CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
242 CONVERT_ARG_HANDLE_CHECKED(WasmExportedFunctionData, function_data, 1);
243 DCHECK(isolate->context().is_null());
244 isolate->set_context(instance->native_context());
245
246 const wasm::WasmModule* module = instance->module();
247 const int function_index = function_data->function_index();
248 const wasm::WasmFunction& function = module->functions[function_index];
249 const wasm::FunctionSig* sig = function.sig;
250
251 // The start function is not guaranteed to be registered as
252 // an exported function (although it is called as one).
253 // If there is no entry for the start function,
254 // the tier-up is abandoned.
255 MaybeHandle<WasmExternalFunction> maybe_exported_function =
256 WasmInstanceObject::GetWasmExternalFunction(isolate, instance,
257 function_index);
258 Handle<WasmExternalFunction> exported_function;
259 if (!maybe_exported_function.ToHandle(&exported_function)) {
260 DCHECK_EQ(function_index, module->start_function_index);
261 return ReadOnlyRoots(isolate).undefined_value();
262 }
263
264 Handle<Code> wrapper_code =
265 wasm::JSToWasmWrapperCompilationUnit::CompileSpecificJSToWasmWrapper(
266 isolate, sig, module);
267
268 // Replace the wrapper for the function that triggered the tier-up.
269 // This is to verify that the wrapper is replaced, even if the function
270 // is implicitly exported and is not part of the export_table.
271 ReplaceWrapper(isolate, instance, function_index, wrapper_code);
272
273 // Iterate over all exports to replace eagerly the wrapper for all functions
274 // that share the signature of the function that tiered up.
275 for (wasm::WasmExport exp : module->export_table) {
276 if (exp.kind != wasm::kExternalFunction) {
277 continue;
278 }
279 int index = static_cast<int>(exp.index);
280 const wasm::WasmFunction& exp_function = module->functions[index];
281 if (exp_function.sig == sig && index != function_index) {
282 ReplaceWrapper(isolate, instance, index, wrapper_code);
283 }
284 }
285
286 return ReadOnlyRoots(isolate).undefined_value();
287 }
288
RUNTIME_FUNCTION(Runtime_WasmTriggerTierUp)289 RUNTIME_FUNCTION(Runtime_WasmTriggerTierUp) {
290 HandleScope scope(isolate);
291 DCHECK_EQ(1, args.length());
292 CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
293
294 FrameFinder<WasmFrame> frame_finder(isolate);
295 int func_index = frame_finder.frame()->function_index();
296 auto* native_module = instance->module_object().native_module();
297
298 wasm::TriggerTierUp(isolate, native_module, func_index);
299
300 return ReadOnlyRoots(isolate).undefined_value();
301 }
302
RUNTIME_FUNCTION(Runtime_WasmAtomicNotify)303 RUNTIME_FUNCTION(Runtime_WasmAtomicNotify) {
304 ClearThreadInWasmScope clear_wasm_flag(isolate);
305 HandleScope scope(isolate);
306 DCHECK_EQ(3, args.length());
307 CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
308 CONVERT_DOUBLE_ARG_CHECKED(offset_double, 1);
309 uintptr_t offset = static_cast<uintptr_t>(offset_double);
310 CONVERT_NUMBER_CHECKED(uint32_t, count, Uint32, args[2]);
311 Handle<JSArrayBuffer> array_buffer{instance->memory_object().array_buffer(),
312 isolate};
313 // Should have trapped if address was OOB.
314 DCHECK_LT(offset, array_buffer->byte_length());
315 if (!array_buffer->is_shared()) return Smi::FromInt(0);
316 return FutexEmulation::Wake(array_buffer, offset, count);
317 }
318
RUNTIME_FUNCTION(Runtime_WasmI32AtomicWait)319 RUNTIME_FUNCTION(Runtime_WasmI32AtomicWait) {
320 ClearThreadInWasmScope clear_wasm_flag(isolate);
321 HandleScope scope(isolate);
322 DCHECK_EQ(4, args.length());
323 CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
324 CONVERT_DOUBLE_ARG_CHECKED(offset_double, 1);
325 uintptr_t offset = static_cast<uintptr_t>(offset_double);
326 CONVERT_NUMBER_CHECKED(int32_t, expected_value, Int32, args[2]);
327 CONVERT_ARG_HANDLE_CHECKED(BigInt, timeout_ns, 3);
328
329 Handle<JSArrayBuffer> array_buffer{instance->memory_object().array_buffer(),
330 isolate};
331 // Should have trapped if address was OOB.
332 DCHECK_LT(offset, array_buffer->byte_length());
333
334 // Trap if memory is not shared, or wait is not allowed on the isolate
335 if (!array_buffer->is_shared() || !isolate->allow_atomics_wait()) {
336 return ThrowWasmError(isolate, MessageTemplate::kAtomicsWaitNotAllowed);
337 }
338 return FutexEmulation::WaitWasm32(isolate, array_buffer, offset,
339 expected_value, timeout_ns->AsInt64());
340 }
341
RUNTIME_FUNCTION(Runtime_WasmI64AtomicWait)342 RUNTIME_FUNCTION(Runtime_WasmI64AtomicWait) {
343 ClearThreadInWasmScope clear_wasm_flag(isolate);
344 HandleScope scope(isolate);
345 DCHECK_EQ(4, args.length());
346 CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
347 CONVERT_DOUBLE_ARG_CHECKED(offset_double, 1);
348 uintptr_t offset = static_cast<uintptr_t>(offset_double);
349 CONVERT_ARG_HANDLE_CHECKED(BigInt, expected_value, 2);
350 CONVERT_ARG_HANDLE_CHECKED(BigInt, timeout_ns, 3);
351
352 Handle<JSArrayBuffer> array_buffer{instance->memory_object().array_buffer(),
353 isolate};
354 // Should have trapped if address was OOB.
355 DCHECK_LT(offset, array_buffer->byte_length());
356
357 // Trap if memory is not shared, or if wait is not allowed on the isolate
358 if (!array_buffer->is_shared() || !isolate->allow_atomics_wait()) {
359 return ThrowWasmError(isolate, MessageTemplate::kAtomicsWaitNotAllowed);
360 }
361 return FutexEmulation::WaitWasm64(isolate, array_buffer, offset,
362 expected_value->AsInt64(),
363 timeout_ns->AsInt64());
364 }
365
366 namespace {
ThrowTableOutOfBounds(Isolate * isolate,Handle<WasmInstanceObject> instance)367 Object ThrowTableOutOfBounds(Isolate* isolate,
368 Handle<WasmInstanceObject> instance) {
369 // Handle out-of-bounds access here in the runtime call, rather
370 // than having the lower-level layers deal with JS exceptions.
371 if (isolate->context().is_null()) {
372 isolate->set_context(instance->native_context());
373 }
374 return ThrowWasmError(isolate, MessageTemplate::kWasmTrapTableOutOfBounds);
375 }
376 } // namespace
377
RUNTIME_FUNCTION(Runtime_WasmRefFunc)378 RUNTIME_FUNCTION(Runtime_WasmRefFunc) {
379 ClearThreadInWasmScope flag_scope(isolate);
380 HandleScope scope(isolate);
381 DCHECK_EQ(2, args.length());
382 CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
383 CONVERT_UINT32_ARG_CHECKED(function_index, 1);
384
385 Handle<WasmExternalFunction> function =
386 WasmInstanceObject::GetOrCreateWasmExternalFunction(isolate, instance,
387 function_index);
388
389 return *function;
390 }
391
RUNTIME_FUNCTION(Runtime_WasmFunctionTableGet)392 RUNTIME_FUNCTION(Runtime_WasmFunctionTableGet) {
393 ClearThreadInWasmScope flag_scope(isolate);
394 HandleScope scope(isolate);
395 DCHECK_EQ(3, args.length());
396 CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
397 CONVERT_UINT32_ARG_CHECKED(table_index, 1);
398 CONVERT_UINT32_ARG_CHECKED(entry_index, 2);
399 DCHECK_LT(table_index, instance->tables().length());
400 auto table = handle(
401 WasmTableObject::cast(instance->tables().get(table_index)), isolate);
402 // We only use the runtime call for lazily initialized function references.
403 DCHECK(
404 table->instance().IsUndefined()
405 ? table->type() == wasm::kWasmFuncRef
406 : IsSubtypeOf(table->type(), wasm::kWasmFuncRef,
407 WasmInstanceObject::cast(table->instance()).module()));
408
409 if (!WasmTableObject::IsInBounds(isolate, table, entry_index)) {
410 return ThrowWasmError(isolate, MessageTemplate::kWasmTrapTableOutOfBounds);
411 }
412
413 return *WasmTableObject::Get(isolate, table, entry_index);
414 }
415
RUNTIME_FUNCTION(Runtime_WasmFunctionTableSet)416 RUNTIME_FUNCTION(Runtime_WasmFunctionTableSet) {
417 ClearThreadInWasmScope flag_scope(isolate);
418 HandleScope scope(isolate);
419 DCHECK_EQ(4, args.length());
420 CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
421 CONVERT_UINT32_ARG_CHECKED(table_index, 1);
422 CONVERT_UINT32_ARG_CHECKED(entry_index, 2);
423 CONVERT_ARG_CHECKED(Object, element_raw, 3);
424 // TODO(wasm): Manually box because parameters are not visited yet.
425 Handle<Object> element(element_raw, isolate);
426 DCHECK_LT(table_index, instance->tables().length());
427 auto table = handle(
428 WasmTableObject::cast(instance->tables().get(table_index)), isolate);
429 // We only use the runtime call for function references.
430 DCHECK(
431 table->instance().IsUndefined()
432 ? table->type() == wasm::kWasmFuncRef
433 : IsSubtypeOf(table->type(), wasm::kWasmFuncRef,
434 WasmInstanceObject::cast(table->instance()).module()));
435
436 if (!WasmTableObject::IsInBounds(isolate, table, entry_index)) {
437 return ThrowWasmError(isolate, MessageTemplate::kWasmTrapTableOutOfBounds);
438 }
439 WasmTableObject::Set(isolate, table, entry_index, element);
440 return ReadOnlyRoots(isolate).undefined_value();
441 }
442
RUNTIME_FUNCTION(Runtime_WasmTableInit)443 RUNTIME_FUNCTION(Runtime_WasmTableInit) {
444 ClearThreadInWasmScope flag_scope(isolate);
445 HandleScope scope(isolate);
446 DCHECK_EQ(6, args.length());
447 CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
448 CONVERT_UINT32_ARG_CHECKED(table_index, 1);
449 CONVERT_UINT32_ARG_CHECKED(elem_segment_index, 2);
450 static_assert(
451 wasm::kV8MaxWasmTableSize < kSmiMaxValue,
452 "Make sure clamping to Smi range doesn't make an invalid call valid");
453 CONVERT_UINT32_ARG_CHECKED(dst, 3);
454 CONVERT_UINT32_ARG_CHECKED(src, 4);
455 CONVERT_UINT32_ARG_CHECKED(count, 5);
456
457 DCHECK(!isolate->context().is_null());
458
459 bool oob = !WasmInstanceObject::InitTableEntries(
460 isolate, instance, table_index, elem_segment_index, dst, src, count);
461 if (oob) return ThrowTableOutOfBounds(isolate, instance);
462 return ReadOnlyRoots(isolate).undefined_value();
463 }
464
RUNTIME_FUNCTION(Runtime_WasmTableCopy)465 RUNTIME_FUNCTION(Runtime_WasmTableCopy) {
466 ClearThreadInWasmScope flag_scope(isolate);
467 HandleScope scope(isolate);
468 DCHECK_EQ(6, args.length());
469 CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
470 CONVERT_UINT32_ARG_CHECKED(table_dst_index, 1);
471 CONVERT_UINT32_ARG_CHECKED(table_src_index, 2);
472 static_assert(
473 wasm::kV8MaxWasmTableSize < kSmiMaxValue,
474 "Make sure clamping to Smi range doesn't make an invalid call valid");
475 CONVERT_UINT32_ARG_CHECKED(dst, 3);
476 CONVERT_UINT32_ARG_CHECKED(src, 4);
477 CONVERT_UINT32_ARG_CHECKED(count, 5);
478
479 DCHECK(!isolate->context().is_null());
480
481 bool oob = !WasmInstanceObject::CopyTableEntries(
482 isolate, instance, table_dst_index, table_src_index, dst, src, count);
483 if (oob) return ThrowTableOutOfBounds(isolate, instance);
484 return ReadOnlyRoots(isolate).undefined_value();
485 }
486
RUNTIME_FUNCTION(Runtime_WasmTableGrow)487 RUNTIME_FUNCTION(Runtime_WasmTableGrow) {
488 ClearThreadInWasmScope flag_scope(isolate);
489 HandleScope scope(isolate);
490 DCHECK_EQ(4, args.length());
491 CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
492 CONVERT_UINT32_ARG_CHECKED(table_index, 1);
493 CONVERT_ARG_CHECKED(Object, value_raw, 2);
494 // TODO(wasm): Manually box because parameters are not visited yet.
495 Handle<Object> value(value_raw, isolate);
496 CONVERT_UINT32_ARG_CHECKED(delta, 3);
497
498 Handle<WasmTableObject> table(
499 WasmTableObject::cast(instance->tables().get(table_index)), isolate);
500 int result = WasmTableObject::Grow(isolate, table, delta, value);
501
502 return Smi::FromInt(result);
503 }
504
RUNTIME_FUNCTION(Runtime_WasmTableFill)505 RUNTIME_FUNCTION(Runtime_WasmTableFill) {
506 ClearThreadInWasmScope flag_scope(isolate);
507 HandleScope scope(isolate);
508 DCHECK_EQ(5, args.length());
509 CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
510 CONVERT_UINT32_ARG_CHECKED(table_index, 1);
511 CONVERT_UINT32_ARG_CHECKED(start, 2);
512 CONVERT_ARG_CHECKED(Object, value_raw, 3);
513 // TODO(wasm): Manually box because parameters are not visited yet.
514 Handle<Object> value(value_raw, isolate);
515 CONVERT_UINT32_ARG_CHECKED(count, 4);
516
517 Handle<WasmTableObject> table(
518 WasmTableObject::cast(instance->tables().get(table_index)), isolate);
519
520 uint32_t table_size = table->current_length();
521
522 if (start > table_size) {
523 return ThrowTableOutOfBounds(isolate, instance);
524 }
525
526 // Even when table.fill goes out-of-bounds, as many entries as possible are
527 // put into the table. Only afterwards we trap.
528 uint32_t fill_count = std::min(count, table_size - start);
529 if (fill_count < count) {
530 return ThrowTableOutOfBounds(isolate, instance);
531 }
532 WasmTableObject::Fill(isolate, table, start, value, fill_count);
533
534 return ReadOnlyRoots(isolate).undefined_value();
535 }
536
RUNTIME_FUNCTION(Runtime_WasmDebugBreak)537 RUNTIME_FUNCTION(Runtime_WasmDebugBreak) {
538 ClearThreadInWasmScope flag_scope(isolate);
539 HandleScope scope(isolate);
540 DCHECK_EQ(0, args.length());
541 FrameFinder<WasmFrame> frame_finder(
542 isolate, {StackFrame::EXIT, StackFrame::WASM_DEBUG_BREAK});
543 WasmFrame* frame = frame_finder.frame();
544 auto instance = handle(frame->wasm_instance(), isolate);
545 auto script = handle(instance->module_object().script(), isolate);
546 auto* debug_info = instance->module_object().native_module()->GetDebugInfo();
547 isolate->set_context(instance->native_context());
548
549 // Stepping can repeatedly create code, and code GC requires stack guards to
550 // be executed on all involved isolates. Proactively do this here.
551 StackLimitCheck check(isolate);
552 if (check.InterruptRequested()) {
553 Object interrupt_object = isolate->stack_guard()->HandleInterrupts();
554 // Interrupt handling can create an exception, including the
555 // termination exception.
556 if (interrupt_object.IsException(isolate)) return interrupt_object;
557 DCHECK(interrupt_object.IsUndefined(isolate));
558 }
559
560 // Enter the debugger.
561 DebugScope debug_scope(isolate->debug());
562
563 // Check for instrumentation breakpoint.
564 DCHECK_EQ(script->break_on_entry(), !!instance->break_on_entry());
565 if (script->break_on_entry()) {
566 MaybeHandle<FixedArray> maybe_on_entry_breakpoints =
567 WasmScript::CheckBreakPoints(isolate, script,
568 WasmScript::kOnEntryBreakpointPosition,
569 frame->id());
570 script->set_break_on_entry(false);
571 // Update the "break_on_entry" flag on all live instances.
572 i::WeakArrayList weak_instance_list = script->wasm_weak_instance_list();
573 for (int i = 0; i < weak_instance_list.length(); ++i) {
574 if (weak_instance_list.Get(i)->IsCleared()) continue;
575 i::WasmInstanceObject::cast(weak_instance_list.Get(i)->GetHeapObject())
576 .set_break_on_entry(false);
577 }
578 DCHECK(!instance->break_on_entry());
579 Handle<FixedArray> on_entry_breakpoints;
580 if (maybe_on_entry_breakpoints.ToHandle(&on_entry_breakpoints)) {
581 debug_info->ClearStepping(isolate);
582 StepAction step_action = isolate->debug()->last_step_action();
583 isolate->debug()->ClearStepping();
584 isolate->debug()->OnDebugBreak(on_entry_breakpoints, step_action);
585 // Don't process regular breakpoints.
586 return ReadOnlyRoots(isolate).undefined_value();
587 }
588 }
589
590 if (debug_info->IsStepping(frame)) {
591 debug_info->ClearStepping(isolate);
592 StepAction step_action = isolate->debug()->last_step_action();
593 isolate->debug()->ClearStepping();
594 isolate->debug()->OnDebugBreak(isolate->factory()->empty_fixed_array(),
595 step_action);
596 return ReadOnlyRoots(isolate).undefined_value();
597 }
598
599 // Check whether we hit a breakpoint.
600 Handle<FixedArray> breakpoints;
601 if (WasmScript::CheckBreakPoints(isolate, script, frame->position(),
602 frame->id())
603 .ToHandle(&breakpoints)) {
604 debug_info->ClearStepping(isolate);
605 StepAction step_action = isolate->debug()->last_step_action();
606 isolate->debug()->ClearStepping();
607 if (isolate->debug()->break_points_active()) {
608 // We hit one or several breakpoints. Notify the debug listeners.
609 isolate->debug()->OnDebugBreak(breakpoints, step_action);
610 }
611 return ReadOnlyRoots(isolate).undefined_value();
612 }
613
614 // We did not hit a breakpoint. If we are in stepping code, but the user did
615 // not request stepping, clear this (to save further calls into this runtime
616 // function).
617 debug_info->ClearStepping(frame);
618
619 return ReadOnlyRoots(isolate).undefined_value();
620 }
621
RUNTIME_FUNCTION(Runtime_WasmAllocateRtt)622 RUNTIME_FUNCTION(Runtime_WasmAllocateRtt) {
623 ClearThreadInWasmScope flag_scope(isolate);
624 HandleScope scope(isolate);
625 DCHECK_EQ(3, args.length());
626 CONVERT_UINT32_ARG_CHECKED(type_index, 0);
627 CONVERT_ARG_HANDLE_CHECKED(Map, parent, 1);
628 CONVERT_SMI_ARG_CHECKED(raw_mode, 2);
629 Handle<WasmInstanceObject> instance(GetWasmInstanceOnStackTop(isolate),
630 isolate);
631 return *wasm::AllocateSubRtt(isolate, instance, type_index, parent,
632 static_cast<WasmRttSubMode>(raw_mode));
633 }
634
635 namespace {
ArrayElementAddress(Handle<WasmArray> array,uint32_t index,int element_size_bytes)636 inline void* ArrayElementAddress(Handle<WasmArray> array, uint32_t index,
637 int element_size_bytes) {
638 return reinterpret_cast<void*>(array->ptr() + WasmArray::kHeaderSize -
639 kHeapObjectTag + index * element_size_bytes);
640 }
641 } // namespace
642
643 // Assumes copy ranges are in-bounds and copy length > 0.
RUNTIME_FUNCTION(Runtime_WasmArrayCopy)644 RUNTIME_FUNCTION(Runtime_WasmArrayCopy) {
645 ClearThreadInWasmScope flag_scope(isolate);
646 HandleScope scope(isolate);
647 DCHECK_EQ(5, args.length());
648 CONVERT_ARG_HANDLE_CHECKED(WasmArray, dst_array, 0);
649 CONVERT_UINT32_ARG_CHECKED(dst_index, 1);
650 CONVERT_ARG_HANDLE_CHECKED(WasmArray, src_array, 2);
651 CONVERT_UINT32_ARG_CHECKED(src_index, 3);
652 CONVERT_UINT32_ARG_CHECKED(length, 4);
653 DCHECK_GT(length, 0);
654 bool overlapping_ranges =
655 dst_array->ptr() == src_array->ptr() &&
656 (dst_index < src_index ? dst_index + length > src_index
657 : src_index + length > dst_index);
658 wasm::ValueType element_type = src_array->type()->element_type();
659 if (element_type.is_reference()) {
660 ObjectSlot dst_slot = dst_array->ElementSlot(dst_index);
661 ObjectSlot src_slot = src_array->ElementSlot(src_index);
662 if (overlapping_ranges) {
663 isolate->heap()->MoveRange(*dst_array, dst_slot, src_slot, length,
664 UPDATE_WRITE_BARRIER);
665 } else {
666 isolate->heap()->CopyRange(*dst_array, dst_slot, src_slot, length,
667 UPDATE_WRITE_BARRIER);
668 }
669 } else {
670 int element_size_bytes = element_type.element_size_bytes();
671 void* dst = ArrayElementAddress(dst_array, dst_index, element_size_bytes);
672 void* src = ArrayElementAddress(src_array, src_index, element_size_bytes);
673 size_t copy_size = length * element_size_bytes;
674 if (overlapping_ranges) {
675 MemMove(dst, src, copy_size);
676 } else {
677 MemCopy(dst, src, copy_size);
678 }
679 }
680 return ReadOnlyRoots(isolate).undefined_value();
681 }
682
683 } // namespace internal
684 } // namespace v8
685