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