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