1 // Copyright 2015 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/wasm/wasm-objects.h"
6 
7 #include "src/base/iterator.h"
8 #include "src/codegen/assembler-inl.h"
9 #include "src/codegen/code-factory.h"
10 #include "src/compiler/wasm-compiler.h"
11 #include "src/debug/debug-interface.h"
12 #include "src/logging/counters.h"
13 #include "src/objects/debug-objects-inl.h"
14 #include "src/objects/objects-inl.h"
15 #include "src/objects/shared-function-info.h"
16 #include "src/objects/struct-inl.h"
17 #include "src/trap-handler/trap-handler.h"
18 #include "src/utils/utils.h"
19 #include "src/utils/vector.h"
20 #include "src/wasm/jump-table-assembler.h"
21 #include "src/wasm/module-compiler.h"
22 #include "src/wasm/module-decoder.h"
23 #include "src/wasm/module-instantiate.h"
24 #include "src/wasm/value-type.h"
25 #include "src/wasm/wasm-code-manager.h"
26 #include "src/wasm/wasm-engine.h"
27 #include "src/wasm/wasm-limits.h"
28 #include "src/wasm/wasm-module.h"
29 #include "src/wasm/wasm-objects-inl.h"
30 #include "src/wasm/wasm-subtyping.h"
31 #include "src/wasm/wasm-value.h"
32 
33 #define TRACE_IFT(...)              \
34   do {                              \
35     if (false) PrintF(__VA_ARGS__); \
36   } while (false)
37 
38 namespace v8 {
39 namespace internal {
40 
41 // Import a few often used types from the wasm namespace.
42 using WasmFunction = wasm::WasmFunction;
43 using WasmModule = wasm::WasmModule;
44 
45 namespace {
46 
47 // Manages the natively-allocated memory for a WasmInstanceObject. Since
48 // an instance finalizer is not guaranteed to run upon isolate shutdown,
49 // we must use a Managed<WasmInstanceNativeAllocations> to guarantee
50 // it is freed.
51 // Native allocations are the signature ids and targets for indirect call
52 // targets, as well as the call targets for imported functions.
53 class WasmInstanceNativeAllocations {
54  public:
55 // Helper macro to set an internal field and the corresponding field
56 // on an instance.
57 #define SET(instance, field, value) \
58   instance->set_##field((this->field##_ = value).get());
59 
60   // Allocates initial native storage for a given instance.
WasmInstanceNativeAllocations(Handle<WasmInstanceObject> instance,size_t num_imported_functions,size_t num_imported_mutable_globals,size_t num_data_segments,size_t num_elem_segments)61   WasmInstanceNativeAllocations(Handle<WasmInstanceObject> instance,
62                                 size_t num_imported_functions,
63                                 size_t num_imported_mutable_globals,
64                                 size_t num_data_segments,
65                                 size_t num_elem_segments) {
66     SET(instance, imported_function_targets,
67         std::make_unique<Address[]>(num_imported_functions));
68     SET(instance, imported_mutable_globals,
69         std::make_unique<Address[]>(num_imported_mutable_globals));
70     SET(instance, data_segment_starts,
71         std::make_unique<Address[]>(num_data_segments));
72     SET(instance, data_segment_sizes,
73         std::make_unique<uint32_t[]>(num_data_segments));
74     SET(instance, dropped_elem_segments,
75         std::make_unique<uint8_t[]>(num_elem_segments));
76   }
77 
indirect_function_table_capacity() const78   uint32_t indirect_function_table_capacity() const {
79     return indirect_function_table_capacity_;
80   }
81 
82   // Resizes the indirect function table.
resize_indirect_function_table(Isolate * isolate,Handle<WasmInstanceObject> instance,uint32_t new_capacity)83   void resize_indirect_function_table(Isolate* isolate,
84                                       Handle<WasmInstanceObject> instance,
85                                       uint32_t new_capacity) {
86     uint32_t old_capacity = indirect_function_table_capacity_;
87     DCHECK_LT(old_capacity, new_capacity);
88     // Grow exponentially to support repeated re-allocation.
89     new_capacity = std::max(new_capacity, 2 * old_capacity);
90     CHECK_GE(kMaxInt, old_capacity);
91     CHECK_GE(kMaxInt, new_capacity);
92 
93     SET(instance, indirect_function_table_sig_ids,
94         grow(indirect_function_table_sig_ids_.get(), old_capacity,
95              new_capacity));
96     SET(instance, indirect_function_table_targets,
97         grow(indirect_function_table_targets_.get(), old_capacity,
98              new_capacity));
99 
100     Handle<FixedArray> old_refs(instance->indirect_function_table_refs(),
101                                 isolate);
102     Handle<FixedArray> new_refs = isolate->factory()->CopyFixedArrayAndGrow(
103         old_refs, static_cast<int>(new_capacity - old_capacity));
104     instance->set_indirect_function_table_refs(*new_refs);
105     indirect_function_table_capacity_ = new_capacity;
106   }
107 
108  private:
109   template <typename T>
grow(T * old_arr,size_t old_size,size_t new_size)110   std::unique_ptr<T[]> grow(T* old_arr, size_t old_size, size_t new_size) {
111     std::unique_ptr<T[]> new_arr = std::make_unique<T[]>(new_size);
112     std::copy_n(old_arr, old_size, new_arr.get());
113     return new_arr;
114   }
115 
116   uint32_t indirect_function_table_capacity_ = 0;
117   std::unique_ptr<uint32_t[]> indirect_function_table_sig_ids_;
118   std::unique_ptr<Address[]> indirect_function_table_targets_;
119   std::unique_ptr<Address[]> imported_function_targets_;
120   std::unique_ptr<Address[]> imported_mutable_globals_;
121   std::unique_ptr<Address[]> data_segment_starts_;
122   std::unique_ptr<uint32_t[]> data_segment_sizes_;
123   std::unique_ptr<uint8_t[]> dropped_elem_segments_;
124 #undef SET
125 };
126 
EstimateNativeAllocationsSize(const WasmModule * module)127 size_t EstimateNativeAllocationsSize(const WasmModule* module) {
128   size_t estimate =
129       sizeof(WasmInstanceNativeAllocations) +
130       (1 * kSystemPointerSize * module->num_imported_mutable_globals) +
131       (2 * kSystemPointerSize * module->num_imported_functions) +
132       ((kSystemPointerSize + sizeof(uint32_t) + sizeof(uint8_t)) *
133        module->num_declared_data_segments);
134   for (auto& table : module->tables) {
135     estimate += 3 * kSystemPointerSize * table.initial_size;
136   }
137   return estimate;
138 }
139 
GetNativeAllocations(WasmInstanceObject instance)140 WasmInstanceNativeAllocations* GetNativeAllocations(
141     WasmInstanceObject instance) {
142   return Managed<WasmInstanceNativeAllocations>::cast(
143              instance.managed_native_allocations())
144       .raw();
145 }
146 
147 enum DispatchTableElements : int {
148   kDispatchTableInstanceOffset,
149   kDispatchTableIndexOffset,
150   kDispatchTableFunctionTableOffset,
151   // Marker:
152   kDispatchTableNumElements
153 };
154 
155 }  // namespace
156 
157 // static
New(Isolate * isolate,std::shared_ptr<wasm::NativeModule> native_module,Handle<Script> script)158 Handle<WasmModuleObject> WasmModuleObject::New(
159     Isolate* isolate, std::shared_ptr<wasm::NativeModule> native_module,
160     Handle<Script> script) {
161   Handle<FixedArray> export_wrappers = isolate->factory()->NewFixedArray(0);
162   return New(isolate, std::move(native_module), script, export_wrappers);
163 }
164 
165 // static
New(Isolate * isolate,std::shared_ptr<wasm::NativeModule> native_module,Handle<Script> script,Handle<FixedArray> export_wrappers)166 Handle<WasmModuleObject> WasmModuleObject::New(
167     Isolate* isolate, std::shared_ptr<wasm::NativeModule> native_module,
168     Handle<Script> script, Handle<FixedArray> export_wrappers) {
169   Handle<Managed<wasm::NativeModule>> managed_native_module;
170   if (script->type() == Script::TYPE_WASM) {
171     managed_native_module = handle(
172         Managed<wasm::NativeModule>::cast(script->wasm_managed_native_module()),
173         isolate);
174   } else {
175     const WasmModule* module = native_module->module();
176     size_t memory_estimate =
177         native_module->committed_code_space() +
178         wasm::WasmCodeManager::EstimateNativeModuleMetaDataSize(module);
179     managed_native_module = Managed<wasm::NativeModule>::FromSharedPtr(
180         isolate, memory_estimate, std::move(native_module));
181   }
182   Handle<WasmModuleObject> module_object = Handle<WasmModuleObject>::cast(
183       isolate->factory()->NewJSObject(isolate->wasm_module_constructor()));
184   module_object->set_export_wrappers(*export_wrappers);
185   module_object->set_managed_native_module(*managed_native_module);
186   module_object->set_script(*script);
187   return module_object;
188 }
189 
ExtractUtf8StringFromModuleBytes(Isolate * isolate,Handle<WasmModuleObject> module_object,wasm::WireBytesRef ref,InternalizeString internalize)190 Handle<String> WasmModuleObject::ExtractUtf8StringFromModuleBytes(
191     Isolate* isolate, Handle<WasmModuleObject> module_object,
192     wasm::WireBytesRef ref, InternalizeString internalize) {
193   Vector<const uint8_t> wire_bytes =
194       module_object->native_module()->wire_bytes();
195   return ExtractUtf8StringFromModuleBytes(isolate, wire_bytes, ref,
196                                           internalize);
197 }
198 
ExtractUtf8StringFromModuleBytes(Isolate * isolate,Vector<const uint8_t> wire_bytes,wasm::WireBytesRef ref,InternalizeString internalize)199 Handle<String> WasmModuleObject::ExtractUtf8StringFromModuleBytes(
200     Isolate* isolate, Vector<const uint8_t> wire_bytes, wasm::WireBytesRef ref,
201     InternalizeString internalize) {
202   Vector<const uint8_t> name_vec =
203       wire_bytes.SubVector(ref.offset(), ref.end_offset());
204   // UTF8 validation happens at decode time.
205   DCHECK(unibrow::Utf8::ValidateEncoding(name_vec.begin(), name_vec.length()));
206   auto* factory = isolate->factory();
207   return internalize
208              ? factory->InternalizeUtf8String(
209                    Vector<const char>::cast(name_vec))
210              : factory->NewStringFromUtf8(Vector<const char>::cast(name_vec))
211                    .ToHandleChecked();
212 }
213 
GetModuleNameOrNull(Isolate * isolate,Handle<WasmModuleObject> module_object)214 MaybeHandle<String> WasmModuleObject::GetModuleNameOrNull(
215     Isolate* isolate, Handle<WasmModuleObject> module_object) {
216   const WasmModule* module = module_object->module();
217   if (!module->name.is_set()) return {};
218   return ExtractUtf8StringFromModuleBytes(isolate, module_object, module->name,
219                                           kNoInternalize);
220 }
221 
GetFunctionNameOrNull(Isolate * isolate,Handle<WasmModuleObject> module_object,uint32_t func_index)222 MaybeHandle<String> WasmModuleObject::GetFunctionNameOrNull(
223     Isolate* isolate, Handle<WasmModuleObject> module_object,
224     uint32_t func_index) {
225   DCHECK_LT(func_index, module_object->module()->functions.size());
226   wasm::WireBytesRef name =
227       module_object->module()->lazily_generated_names.LookupFunctionName(
228           wasm::ModuleWireBytes(module_object->native_module()->wire_bytes()),
229           func_index, VectorOf(module_object->module()->export_table));
230   if (!name.is_set()) return {};
231   return ExtractUtf8StringFromModuleBytes(isolate, module_object, name,
232                                           kNoInternalize);
233 }
234 
GetFunctionName(Isolate * isolate,Handle<WasmModuleObject> module_object,uint32_t func_index)235 Handle<String> WasmModuleObject::GetFunctionName(
236     Isolate* isolate, Handle<WasmModuleObject> module_object,
237     uint32_t func_index) {
238   MaybeHandle<String> name =
239       GetFunctionNameOrNull(isolate, module_object, func_index);
240   if (!name.is_null()) return name.ToHandleChecked();
241   EmbeddedVector<char, 32> buffer;
242   DCHECK_GE(func_index, module_object->module()->num_imported_functions);
243   int length = SNPrintF(buffer, "func%u", func_index);
244   return isolate->factory()
245       ->NewStringFromOneByte(Vector<uint8_t>::cast(buffer.SubVector(0, length)))
246       .ToHandleChecked();
247 }
248 
GetRawFunctionName(uint32_t func_index)249 Vector<const uint8_t> WasmModuleObject::GetRawFunctionName(
250     uint32_t func_index) {
251   DCHECK_GT(module()->functions.size(), func_index);
252   wasm::ModuleWireBytes wire_bytes(native_module()->wire_bytes());
253   wasm::WireBytesRef name_ref =
254       module()->lazily_generated_names.LookupFunctionName(
255           wire_bytes, func_index, VectorOf(module()->export_table));
256   wasm::WasmName name = wire_bytes.GetNameOrNull(name_ref);
257   return Vector<const uint8_t>::cast(name);
258 }
259 
New(Isolate * isolate,Handle<WasmInstanceObject> instance,wasm::ValueType type,uint32_t initial,bool has_maximum,uint32_t maximum,Handle<FixedArray> * entries)260 Handle<WasmTableObject> WasmTableObject::New(
261     Isolate* isolate, Handle<WasmInstanceObject> instance, wasm::ValueType type,
262     uint32_t initial, bool has_maximum, uint32_t maximum,
263     Handle<FixedArray>* entries) {
264   // TODO(7748): Make this work with other types when spec clears up.
265   {
266     const WasmModule* module =
267         instance.is_null() ? nullptr : instance->module();
268     CHECK(wasm::WasmTable::IsValidTableType(type, module));
269   }
270 
271   Handle<FixedArray> backing_store = isolate->factory()->NewFixedArray(initial);
272   Object null = ReadOnlyRoots(isolate).null_value();
273   for (int i = 0; i < static_cast<int>(initial); ++i) {
274     backing_store->set(i, null);
275   }
276 
277   Handle<Object> max;
278   if (has_maximum) {
279     max = isolate->factory()->NewNumberFromUint(maximum);
280   } else {
281     max = isolate->factory()->undefined_value();
282   }
283 
284   Handle<JSFunction> table_ctor(
285       isolate->native_context()->wasm_table_constructor(), isolate);
286   auto table_obj = Handle<WasmTableObject>::cast(
287       isolate->factory()->NewJSObject(table_ctor));
288   DisallowHeapAllocation no_gc;
289 
290   if (!instance.is_null()) table_obj->set_instance(*instance);
291   table_obj->set_entries(*backing_store);
292   table_obj->set_current_length(initial);
293   table_obj->set_maximum_length(*max);
294   table_obj->set_raw_type(static_cast<int>(type.heap_representation()));
295 
296   table_obj->set_dispatch_tables(ReadOnlyRoots(isolate).empty_fixed_array());
297   if (entries != nullptr) {
298     *entries = backing_store;
299   }
300   return Handle<WasmTableObject>::cast(table_obj);
301 }
302 
AddDispatchTable(Isolate * isolate,Handle<WasmTableObject> table_obj,Handle<WasmInstanceObject> instance,int table_index)303 void WasmTableObject::AddDispatchTable(Isolate* isolate,
304                                        Handle<WasmTableObject> table_obj,
305                                        Handle<WasmInstanceObject> instance,
306                                        int table_index) {
307   Handle<FixedArray> dispatch_tables(table_obj->dispatch_tables(), isolate);
308   int old_length = dispatch_tables->length();
309   DCHECK_EQ(0, old_length % kDispatchTableNumElements);
310 
311   if (instance.is_null()) return;
312   // TODO(titzer): use weak cells here to avoid leaking instances.
313 
314   // Grow the dispatch table and add a new entry at the end.
315   Handle<FixedArray> new_dispatch_tables =
316       isolate->factory()->CopyFixedArrayAndGrow(dispatch_tables,
317                                                 kDispatchTableNumElements);
318 
319   new_dispatch_tables->set(old_length + kDispatchTableInstanceOffset,
320                            *instance);
321   new_dispatch_tables->set(old_length + kDispatchTableIndexOffset,
322                            Smi::FromInt(table_index));
323 
324   table_obj->set_dispatch_tables(*new_dispatch_tables);
325 }
326 
Grow(Isolate * isolate,Handle<WasmTableObject> table,uint32_t count,Handle<Object> init_value)327 int WasmTableObject::Grow(Isolate* isolate, Handle<WasmTableObject> table,
328                           uint32_t count, Handle<Object> init_value) {
329   uint32_t old_size = table->current_length();
330   if (count == 0) return old_size;  // Degenerate case: nothing to do.
331 
332   // Check if growing by {count} is valid.
333   uint32_t max_size;
334   if (!table->maximum_length().ToUint32(&max_size)) {
335     max_size = FLAG_wasm_max_table_size;
336   }
337   max_size = std::min(max_size, FLAG_wasm_max_table_size);
338   DCHECK_LE(old_size, max_size);
339   if (max_size - old_size < count) return -1;
340 
341   uint32_t new_size = old_size + count;
342   // Even with 2x over-allocation, there should not be an integer overflow.
343   STATIC_ASSERT(wasm::kV8MaxWasmTableSize <= kMaxInt / 2);
344   DCHECK_GE(kMaxInt, new_size);
345   int old_capacity = table->entries().length();
346   if (new_size > static_cast<uint32_t>(old_capacity)) {
347     int grow = static_cast<int>(new_size) - old_capacity;
348     // Grow at least by the old capacity, to implement exponential growing.
349     grow = std::max(grow, old_capacity);
350     // Never grow larger than the max size.
351     grow = std::min(grow, static_cast<int>(max_size - old_capacity));
352     auto new_store = isolate->factory()->CopyFixedArrayAndGrow(
353         handle(table->entries(), isolate), grow);
354     table->set_entries(*new_store, WriteBarrierMode::UPDATE_WRITE_BARRIER);
355   }
356   table->set_current_length(new_size);
357 
358   Handle<FixedArray> dispatch_tables(table->dispatch_tables(), isolate);
359   DCHECK_EQ(0, dispatch_tables->length() % kDispatchTableNumElements);
360   // Tables are stored in the instance object, no code patching is
361   // necessary. We simply have to grow the raw tables in each instance
362   // that has imported this table.
363 
364   // TODO(titzer): replace the dispatch table with a weak list of all
365   // the instances that import a given table.
366   for (int i = 0; i < dispatch_tables->length();
367        i += kDispatchTableNumElements) {
368     int table_index =
369         Smi::cast(dispatch_tables->get(i + kDispatchTableIndexOffset)).value();
370 
371     Handle<WasmInstanceObject> instance(
372         WasmInstanceObject::cast(dispatch_tables->get(i)), isolate);
373 
374     DCHECK_EQ(old_size, WasmInstanceObject::IndirectFunctionTableSize(
375                             isolate, instance, table_index));
376     WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize(
377         instance, table_index, new_size);
378   }
379 
380   for (uint32_t entry = old_size; entry < new_size; ++entry) {
381     WasmTableObject::Set(isolate, table, entry, init_value);
382   }
383   return old_size;
384 }
385 
IsInBounds(Isolate * isolate,Handle<WasmTableObject> table,uint32_t entry_index)386 bool WasmTableObject::IsInBounds(Isolate* isolate,
387                                  Handle<WasmTableObject> table,
388                                  uint32_t entry_index) {
389   return entry_index < static_cast<uint32_t>(table->current_length());
390 }
391 
IsValidElement(Isolate * isolate,Handle<WasmTableObject> table,Handle<Object> entry)392 bool WasmTableObject::IsValidElement(Isolate* isolate,
393                                      Handle<WasmTableObject> table,
394                                      Handle<Object> entry) {
395   const char* error_message;
396   const WasmModule* module =
397       !table->instance().IsUndefined()
398           ? WasmInstanceObject::cast(table->instance()).module()
399           : nullptr;
400   return wasm::TypecheckJSObject(isolate, module, entry, table->type(),
401                                  &error_message);
402 }
403 
SetFunctionTableEntry(Isolate * isolate,Handle<WasmTableObject> table,Handle<FixedArray> entries,int entry_index,Handle<Object> entry)404 void WasmTableObject::SetFunctionTableEntry(Isolate* isolate,
405                                             Handle<WasmTableObject> table,
406                                             Handle<FixedArray> entries,
407                                             int entry_index,
408                                             Handle<Object> entry) {
409   if (entry->IsNull(isolate)) {
410     ClearDispatchTables(isolate, table, entry_index);  // Degenerate case.
411     entries->set(entry_index, ReadOnlyRoots(isolate).null_value());
412     return;
413   }
414 
415   if (WasmExportedFunction::IsWasmExportedFunction(*entry)) {
416     auto exported_function = Handle<WasmExportedFunction>::cast(entry);
417     Handle<WasmInstanceObject> target_instance(exported_function->instance(),
418                                                isolate);
419     int func_index = exported_function->function_index();
420     auto* wasm_function = &target_instance->module()->functions[func_index];
421     DCHECK_NOT_NULL(wasm_function);
422     DCHECK_NOT_NULL(wasm_function->sig);
423     UpdateDispatchTables(isolate, table, entry_index, wasm_function->sig,
424                          target_instance, func_index);
425   } else if (WasmJSFunction::IsWasmJSFunction(*entry)) {
426     UpdateDispatchTables(isolate, table, entry_index,
427                          Handle<WasmJSFunction>::cast(entry));
428   } else {
429     DCHECK(WasmCapiFunction::IsWasmCapiFunction(*entry));
430     UpdateDispatchTables(isolate, table, entry_index,
431                          Handle<WasmCapiFunction>::cast(entry));
432   }
433   entries->set(entry_index, *entry);
434 }
435 
Set(Isolate * isolate,Handle<WasmTableObject> table,uint32_t index,Handle<Object> entry)436 void WasmTableObject::Set(Isolate* isolate, Handle<WasmTableObject> table,
437                           uint32_t index, Handle<Object> entry) {
438   // Callers need to perform bounds checks, type check, and error handling.
439   DCHECK(IsInBounds(isolate, table, index));
440   DCHECK(IsValidElement(isolate, table, entry));
441 
442   Handle<FixedArray> entries(table->entries(), isolate);
443   // The FixedArray is addressed with int's.
444   int entry_index = static_cast<int>(index);
445 
446   switch (table->type().heap_representation()) {
447     case wasm::HeapType::kExtern:
448     case wasm::HeapType::kExn:
449       entries->set(entry_index, *entry);
450       return;
451     case wasm::HeapType::kFunc:
452       SetFunctionTableEntry(isolate, table, entries, entry_index, entry);
453       return;
454     case wasm::HeapType::kEq:
455     case wasm::HeapType::kI31:
456       // TODO(7748): Implement once we have a story for struct/arrays/i31ref in
457       // JS.
458       UNIMPLEMENTED();
459     case wasm::HeapType::kBottom:
460       UNREACHABLE();
461     default:
462       DCHECK(!table->instance().IsUndefined());
463       if (WasmInstanceObject::cast(table->instance())
464               .module()
465               ->has_signature(entry_index)) {
466         SetFunctionTableEntry(isolate, table, entries, entry_index, entry);
467         return;
468       }
469       // TODO(7748): Implement once we have a story for struct/arrays in JS.
470       UNIMPLEMENTED();
471   }
472 }
473 
Get(Isolate * isolate,Handle<WasmTableObject> table,uint32_t index)474 Handle<Object> WasmTableObject::Get(Isolate* isolate,
475                                     Handle<WasmTableObject> table,
476                                     uint32_t index) {
477   Handle<FixedArray> entries(table->entries(), isolate);
478   // Callers need to perform bounds checks and error handling.
479   DCHECK(IsInBounds(isolate, table, index));
480 
481   // The FixedArray is addressed with int's.
482   int entry_index = static_cast<int>(index);
483 
484   Handle<Object> entry(entries->get(entry_index), isolate);
485 
486   if (entry->IsNull(isolate)) {
487     return entry;
488   }
489 
490   switch (table->type().heap_representation()) {
491     case wasm::HeapType::kExtern:
492     case wasm::HeapType::kExn:
493       return entry;
494     case wasm::HeapType::kFunc:
495       if (WasmExportedFunction::IsWasmExportedFunction(*entry) ||
496           WasmJSFunction::IsWasmJSFunction(*entry) ||
497           WasmCapiFunction::IsWasmCapiFunction(*entry)) {
498         return entry;
499       }
500       break;
501     case wasm::HeapType::kEq:
502     case wasm::HeapType::kI31:
503       // TODO(7748): Implement once we have a story for struct/arrays/i31ref in
504       // JS.
505       UNIMPLEMENTED();
506     case wasm::HeapType::kBottom:
507       UNREACHABLE();
508     default:
509       DCHECK(!table->instance().IsUndefined());
510       if (WasmInstanceObject::cast(table->instance())
511               .module()
512               ->has_signature(entry_index)) {
513         if (WasmExportedFunction::IsWasmExportedFunction(*entry) ||
514             WasmJSFunction::IsWasmJSFunction(*entry) ||
515             WasmCapiFunction::IsWasmCapiFunction(*entry)) {
516           return entry;
517         }
518         break;
519       }
520       // TODO(7748): Implement once we have a story for struct/arrays in JS.
521       UNIMPLEMENTED();
522   }
523 
524   // {entry} is not a valid entry in the table. It has to be a placeholder
525   // for lazy initialization.
526   Handle<Tuple2> tuple = Handle<Tuple2>::cast(entry);
527   auto instance = handle(WasmInstanceObject::cast(tuple->value1()), isolate);
528   int function_index = Smi::cast(tuple->value2()).value();
529 
530   // Check if we already compiled a wrapper for the function but did not store
531   // it in the table slot yet.
532   entry = WasmInstanceObject::GetOrCreateWasmExternalFunction(isolate, instance,
533                                                               function_index);
534   entries->set(entry_index, *entry);
535   return entry;
536 }
537 
Fill(Isolate * isolate,Handle<WasmTableObject> table,uint32_t start,Handle<Object> entry,uint32_t count)538 void WasmTableObject::Fill(Isolate* isolate, Handle<WasmTableObject> table,
539                            uint32_t start, Handle<Object> entry,
540                            uint32_t count) {
541   // Bounds checks must be done by the caller.
542   DCHECK_LE(start, table->current_length());
543   DCHECK_LE(count, table->current_length());
544   DCHECK_LE(start + count, table->current_length());
545 
546   for (uint32_t i = 0; i < count; i++) {
547     WasmTableObject::Set(isolate, table, start + i, entry);
548   }
549 }
550 
UpdateDispatchTables(Isolate * isolate,Handle<WasmTableObject> table,int entry_index,const wasm::FunctionSig * sig,Handle<WasmInstanceObject> target_instance,int target_func_index)551 void WasmTableObject::UpdateDispatchTables(
552     Isolate* isolate, Handle<WasmTableObject> table, int entry_index,
553     const wasm::FunctionSig* sig, Handle<WasmInstanceObject> target_instance,
554     int target_func_index) {
555   // We simply need to update the IFTs for each instance that imports
556   // this table.
557   Handle<FixedArray> dispatch_tables(table->dispatch_tables(), isolate);
558   DCHECK_EQ(0, dispatch_tables->length() % kDispatchTableNumElements);
559 
560   for (int i = 0; i < dispatch_tables->length();
561        i += kDispatchTableNumElements) {
562     int table_index =
563         Smi::cast(dispatch_tables->get(i + kDispatchTableIndexOffset)).value();
564     Handle<WasmInstanceObject> instance(
565         WasmInstanceObject::cast(
566             dispatch_tables->get(i + kDispatchTableInstanceOffset)),
567         isolate);
568     // Note that {SignatureMap::Find} may return {-1} if the signature is
569     // not found; it will simply never match any check.
570     auto sig_id = instance->module()->signature_map.Find(*sig);
571     IndirectFunctionTableEntry(instance, table_index, entry_index)
572         .Set(sig_id, target_instance, target_func_index);
573   }
574 }
575 
UpdateDispatchTables(Isolate * isolate,Handle<WasmTableObject> table,int entry_index,Handle<WasmJSFunction> function)576 void WasmTableObject::UpdateDispatchTables(Isolate* isolate,
577                                            Handle<WasmTableObject> table,
578                                            int entry_index,
579                                            Handle<WasmJSFunction> function) {
580   // We simply need to update the IFTs for each instance that imports
581   // this table.
582   Handle<FixedArray> dispatch_tables(table->dispatch_tables(), isolate);
583   DCHECK_EQ(0, dispatch_tables->length() % kDispatchTableNumElements);
584 
585   for (int i = 0; i < dispatch_tables->length();
586        i += kDispatchTableNumElements) {
587     int table_index =
588         Smi::cast(dispatch_tables->get(i + kDispatchTableIndexOffset)).value();
589     Handle<WasmInstanceObject> instance(
590         WasmInstanceObject::cast(
591             dispatch_tables->get(i + kDispatchTableInstanceOffset)),
592         isolate);
593     WasmInstanceObject::ImportWasmJSFunctionIntoTable(
594         isolate, instance, table_index, entry_index, function);
595   }
596 }
597 
UpdateDispatchTables(Isolate * isolate,Handle<WasmTableObject> table,int entry_index,Handle<WasmCapiFunction> capi_function)598 void WasmTableObject::UpdateDispatchTables(
599     Isolate* isolate, Handle<WasmTableObject> table, int entry_index,
600     Handle<WasmCapiFunction> capi_function) {
601   // We simply need to update the IFTs for each instance that imports
602   // this table.
603   Handle<FixedArray> dispatch_tables(table->dispatch_tables(), isolate);
604   DCHECK_EQ(0, dispatch_tables->length() % kDispatchTableNumElements);
605 
606   // Reconstruct signature.
607   // TODO(jkummerow): Unify with "SignatureHelper" in c-api.cc.
608   PodArray<wasm::ValueType> serialized_sig =
609       capi_function->GetSerializedSignature();
610   int total_count = serialized_sig.length() - 1;
611   std::unique_ptr<wasm::ValueType[]> reps(new wasm::ValueType[total_count]);
612   int result_count;
613   static const wasm::ValueType kMarker = wasm::kWasmStmt;
614   for (int i = 0, j = 0; i <= total_count; i++) {
615     if (serialized_sig.get(i) == kMarker) {
616       result_count = i;
617       continue;
618     }
619     reps[j++] = serialized_sig.get(i);
620   }
621   int param_count = total_count - result_count;
622   wasm::FunctionSig sig(result_count, param_count, reps.get());
623 
624   for (int i = 0; i < dispatch_tables->length();
625        i += kDispatchTableNumElements) {
626     int table_index =
627         Smi::cast(dispatch_tables->get(i + kDispatchTableIndexOffset)).value();
628     Handle<WasmInstanceObject> instance(
629         WasmInstanceObject::cast(
630             dispatch_tables->get(i + kDispatchTableInstanceOffset)),
631         isolate);
632     // TODO(jkummerow): Find a way to avoid recompiling wrappers.
633     wasm::NativeModule* native_module =
634         instance->module_object().native_module();
635     Address host_address = capi_function->GetHostCallTarget();
636     wasm::WasmCodeRefScope code_ref_scope;
637     wasm::WasmCode* wasm_code = compiler::CompileWasmCapiCallWrapper(
638         isolate->wasm_engine(), native_module, &sig, host_address);
639     isolate->counters()->wasm_generated_code_size()->Increment(
640         wasm_code->instructions().length());
641     isolate->counters()->wasm_reloc_size()->Increment(
642         wasm_code->reloc_info().length());
643     Handle<Tuple2> tuple = isolate->factory()->NewTuple2(
644         instance, capi_function, AllocationType::kOld);
645     // Note that {SignatureMap::Find} may return {-1} if the signature is
646     // not found; it will simply never match any check.
647     auto sig_id = instance->module()->signature_map.Find(sig);
648     IndirectFunctionTableEntry(instance, table_index, entry_index)
649         .Set(sig_id, wasm_code->instruction_start(), *tuple);
650   }
651 }
652 
ClearDispatchTables(Isolate * isolate,Handle<WasmTableObject> table,int index)653 void WasmTableObject::ClearDispatchTables(Isolate* isolate,
654                                           Handle<WasmTableObject> table,
655                                           int index) {
656   Handle<FixedArray> dispatch_tables(table->dispatch_tables(), isolate);
657   DCHECK_EQ(0, dispatch_tables->length() % kDispatchTableNumElements);
658   for (int i = 0; i < dispatch_tables->length();
659        i += kDispatchTableNumElements) {
660     int table_index =
661         Smi::cast(dispatch_tables->get(i + kDispatchTableIndexOffset)).value();
662     Handle<WasmInstanceObject> target_instance(
663         WasmInstanceObject::cast(
664             dispatch_tables->get(i + kDispatchTableInstanceOffset)),
665         isolate);
666     DCHECK_LT(index, WasmInstanceObject::IndirectFunctionTableSize(
667                          isolate, target_instance, table_index));
668     IndirectFunctionTableEntry(target_instance, table_index, index).clear();
669   }
670 }
671 
SetFunctionTablePlaceholder(Isolate * isolate,Handle<WasmTableObject> table,int entry_index,Handle<WasmInstanceObject> instance,int func_index)672 void WasmTableObject::SetFunctionTablePlaceholder(
673     Isolate* isolate, Handle<WasmTableObject> table, int entry_index,
674     Handle<WasmInstanceObject> instance, int func_index) {
675   // Put (instance, func_index) as a Tuple2 into the table_index.
676   // The {WasmExportedFunction} will be created lazily.
677   Handle<Tuple2> tuple = isolate->factory()->NewTuple2(
678       instance, Handle<Smi>(Smi::FromInt(func_index), isolate),
679       AllocationType::kYoung);
680   table->entries().set(entry_index, *tuple);
681 }
682 
GetFunctionTableEntry(Isolate * isolate,const WasmModule * module,Handle<WasmTableObject> table,int entry_index,bool * is_valid,bool * is_null,MaybeHandle<WasmInstanceObject> * instance,int * function_index,MaybeHandle<WasmJSFunction> * maybe_js_function)683 void WasmTableObject::GetFunctionTableEntry(
684     Isolate* isolate, const WasmModule* module, Handle<WasmTableObject> table,
685     int entry_index, bool* is_valid, bool* is_null,
686     MaybeHandle<WasmInstanceObject>* instance, int* function_index,
687     MaybeHandle<WasmJSFunction>* maybe_js_function) {
688   DCHECK(wasm::IsSubtypeOf(table->type(), wasm::kWasmFuncRef, module));
689   DCHECK_LT(entry_index, table->current_length());
690   // We initialize {is_valid} with {true}. We may change it later.
691   *is_valid = true;
692   Handle<Object> element(table->entries().get(entry_index), isolate);
693 
694   *is_null = element->IsNull(isolate);
695   if (*is_null) return;
696 
697   if (WasmExportedFunction::IsWasmExportedFunction(*element)) {
698     auto target_func = Handle<WasmExportedFunction>::cast(element);
699     *instance = handle(target_func->instance(), isolate);
700     *function_index = target_func->function_index();
701     *maybe_js_function = MaybeHandle<WasmJSFunction>();
702     return;
703   }
704   if (WasmJSFunction::IsWasmJSFunction(*element)) {
705     *instance = MaybeHandle<WasmInstanceObject>();
706     *maybe_js_function = Handle<WasmJSFunction>::cast(element);
707     return;
708   }
709   if (element->IsTuple2()) {
710     auto tuple = Handle<Tuple2>::cast(element);
711     *instance = handle(WasmInstanceObject::cast(tuple->value1()), isolate);
712     *function_index = Smi::cast(tuple->value2()).value();
713     *maybe_js_function = MaybeHandle<WasmJSFunction>();
714     return;
715   }
716   *is_valid = false;
717 }
718 
719 namespace {
720 class IftNativeAllocations {
721  public:
IftNativeAllocations(Handle<WasmIndirectFunctionTable> table,uint32_t size)722   IftNativeAllocations(Handle<WasmIndirectFunctionTable> table, uint32_t size)
723       : sig_ids_(size), targets_(size) {
724     table->set_sig_ids(sig_ids_.data());
725     table->set_targets(targets_.data());
726   }
727 
SizeInMemory(uint32_t size)728   static size_t SizeInMemory(uint32_t size) {
729     return size * (sizeof(Address) + sizeof(uint32_t));
730   }
731 
resize(Handle<WasmIndirectFunctionTable> table,uint32_t new_size)732   void resize(Handle<WasmIndirectFunctionTable> table, uint32_t new_size) {
733     DCHECK_GE(new_size, sig_ids_.size());
734     DCHECK_EQ(this, Managed<IftNativeAllocations>::cast(
735                         table->managed_native_allocations())
736                         .raw());
737     sig_ids_.resize(new_size);
738     targets_.resize(new_size);
739     table->set_sig_ids(sig_ids_.data());
740     table->set_targets(targets_.data());
741   }
742 
743  private:
744   std::vector<uint32_t> sig_ids_;
745   std::vector<Address> targets_;
746 };
747 }  // namespace
748 
New(Isolate * isolate,uint32_t size)749 Handle<WasmIndirectFunctionTable> WasmIndirectFunctionTable::New(
750     Isolate* isolate, uint32_t size) {
751   auto refs = isolate->factory()->NewFixedArray(static_cast<int>(size));
752   auto table = Handle<WasmIndirectFunctionTable>::cast(
753       isolate->factory()->NewStruct(WASM_INDIRECT_FUNCTION_TABLE_TYPE));
754   table->set_size(size);
755   table->set_refs(*refs);
756   auto native_allocations = Managed<IftNativeAllocations>::Allocate(
757       isolate, IftNativeAllocations::SizeInMemory(size), table, size);
758   table->set_managed_native_allocations(*native_allocations);
759   for (uint32_t i = 0; i < size; ++i) {
760     IndirectFunctionTableEntry(table, static_cast<int>(i)).clear();
761   }
762   return table;
763 }
764 
Resize(Isolate * isolate,Handle<WasmIndirectFunctionTable> table,uint32_t new_size)765 void WasmIndirectFunctionTable::Resize(Isolate* isolate,
766                                        Handle<WasmIndirectFunctionTable> table,
767                                        uint32_t new_size) {
768   uint32_t old_size = table->size();
769   if (old_size >= new_size) return;  // Nothing to do.
770 
771   Managed<IftNativeAllocations>::cast(table->managed_native_allocations())
772       .raw()
773       ->resize(table, new_size);
774 
775   Handle<FixedArray> old_refs(table->refs(), isolate);
776   Handle<FixedArray> new_refs = isolate->factory()->CopyFixedArrayAndGrow(
777       old_refs, static_cast<int>(new_size - old_size));
778   table->set_refs(*new_refs);
779   table->set_size(new_size);
780   for (uint32_t i = old_size; i < new_size; ++i) {
781     IndirectFunctionTableEntry(table, static_cast<int>(i)).clear();
782   }
783 }
784 
785 namespace {
786 
SetInstanceMemory(Handle<WasmInstanceObject> instance,Handle<JSArrayBuffer> buffer)787 void SetInstanceMemory(Handle<WasmInstanceObject> instance,
788                        Handle<JSArrayBuffer> buffer) {
789   bool is_wasm_module = instance->module()->origin == wasm::kWasmOrigin;
790   bool use_trap_handler =
791       instance->module_object().native_module()->use_trap_handler();
792   // Wasm modules compiled to use the trap handler don't have bounds checks,
793   // so they must have a memory that has guard regions.
794   CHECK_IMPLIES(is_wasm_module && use_trap_handler,
795                 buffer->GetBackingStore()->has_guard_regions());
796 
797   instance->SetRawMemory(reinterpret_cast<byte*>(buffer->backing_store()),
798                          buffer->byte_length());
799 #if DEBUG
800   if (!FLAG_mock_arraybuffer_allocator) {
801     // To flush out bugs earlier, in DEBUG mode, check that all pages of the
802     // memory are accessible by reading and writing one byte on each page.
803     // Don't do this if the mock ArrayBuffer allocator is enabled.
804     byte* mem_start = instance->memory_start();
805     size_t mem_size = instance->memory_size();
806     for (size_t offset = 0; offset < mem_size; offset += wasm::kWasmPageSize) {
807       byte val = mem_start[offset];
808       USE(val);
809       mem_start[offset] = val;
810     }
811   }
812 #endif
813 }
814 }  // namespace
815 
New(Isolate * isolate,MaybeHandle<JSArrayBuffer> maybe_buffer,uint32_t maximum)816 Handle<WasmMemoryObject> WasmMemoryObject::New(
817     Isolate* isolate, MaybeHandle<JSArrayBuffer> maybe_buffer,
818     uint32_t maximum) {
819   Handle<JSArrayBuffer> buffer;
820   if (!maybe_buffer.ToHandle(&buffer)) {
821     // If no buffer was provided, create a zero-length one.
822     auto backing_store =
823         BackingStore::AllocateWasmMemory(isolate, 0, 0, SharedFlag::kNotShared);
824     buffer = isolate->factory()->NewJSArrayBuffer(std::move(backing_store));
825   }
826 
827   Handle<JSFunction> memory_ctor(
828       isolate->native_context()->wasm_memory_constructor(), isolate);
829 
830   auto memory_object = Handle<WasmMemoryObject>::cast(
831       isolate->factory()->NewJSObject(memory_ctor, AllocationType::kOld));
832   memory_object->set_array_buffer(*buffer);
833   memory_object->set_maximum_pages(maximum);
834 
835   if (buffer->is_shared()) {
836     auto backing_store = buffer->GetBackingStore();
837     backing_store->AttachSharedWasmMemoryObject(isolate, memory_object);
838   }
839 
840   return memory_object;
841 }
842 
New(Isolate * isolate,uint32_t initial,uint32_t maximum,SharedFlag shared)843 MaybeHandle<WasmMemoryObject> WasmMemoryObject::New(Isolate* isolate,
844                                                     uint32_t initial,
845                                                     uint32_t maximum,
846                                                     SharedFlag shared) {
847   auto heuristic_maximum = maximum;
848 #ifdef V8_TARGET_ARCH_32_BIT
849   // TODO(wasm): use a better heuristic for reserving more than the initial
850   // number of pages on 32-bit systems. Being too greedy in reserving capacity
851   // limits the number of memories that can be allocated, causing OOMs in many
852   // tests. For now, on 32-bit we never reserve more than initial, unless the
853   // memory is shared.
854   if (shared == SharedFlag::kNotShared || !FLAG_wasm_grow_shared_memory) {
855     heuristic_maximum = initial;
856   }
857 #endif
858 
859   auto backing_store = BackingStore::AllocateWasmMemory(
860       isolate, initial, heuristic_maximum, shared);
861 
862   if (!backing_store) return {};
863 
864   Handle<JSArrayBuffer> buffer =
865       (shared == SharedFlag::kShared)
866           ? isolate->factory()->NewJSSharedArrayBuffer(std::move(backing_store))
867           : isolate->factory()->NewJSArrayBuffer(std::move(backing_store));
868 
869   return New(isolate, buffer, maximum);
870 }
871 
AddInstance(Isolate * isolate,Handle<WasmMemoryObject> memory,Handle<WasmInstanceObject> instance)872 void WasmMemoryObject::AddInstance(Isolate* isolate,
873                                    Handle<WasmMemoryObject> memory,
874                                    Handle<WasmInstanceObject> instance) {
875   Handle<WeakArrayList> old_instances =
876       memory->has_instances()
877           ? Handle<WeakArrayList>(memory->instances(), isolate)
878           : handle(ReadOnlyRoots(isolate->heap()).empty_weak_array_list(),
879                    isolate);
880   Handle<WeakArrayList> new_instances = WeakArrayList::AddToEnd(
881       isolate, old_instances, MaybeObjectHandle::Weak(instance));
882   memory->set_instances(*new_instances);
883   Handle<JSArrayBuffer> buffer(memory->array_buffer(), isolate);
884   SetInstanceMemory(instance, buffer);
885 }
886 
update_instances(Isolate * isolate,Handle<JSArrayBuffer> buffer)887 void WasmMemoryObject::update_instances(Isolate* isolate,
888                                         Handle<JSArrayBuffer> buffer) {
889   if (has_instances()) {
890     Handle<WeakArrayList> instances(this->instances(), isolate);
891     for (int i = 0; i < instances->length(); i++) {
892       MaybeObject elem = instances->Get(i);
893       HeapObject heap_object;
894       if (elem->GetHeapObjectIfWeak(&heap_object)) {
895         Handle<WasmInstanceObject> instance(
896             WasmInstanceObject::cast(heap_object), isolate);
897         SetInstanceMemory(instance, buffer);
898       } else {
899         DCHECK(elem->IsCleared());
900       }
901     }
902   }
903   set_array_buffer(*buffer);
904 }
905 
906 // static
Grow(Isolate * isolate,Handle<WasmMemoryObject> memory_object,uint32_t pages)907 int32_t WasmMemoryObject::Grow(Isolate* isolate,
908                                Handle<WasmMemoryObject> memory_object,
909                                uint32_t pages) {
910   TRACE_EVENT0("v8.wasm", "wasm.GrowMemory");
911   Handle<JSArrayBuffer> old_buffer(memory_object->array_buffer(), isolate);
912   // Any buffer used as an asmjs memory cannot be detached, and
913   // therefore this memory cannot be grown.
914   if (old_buffer->is_asmjs_memory()) return -1;
915 
916   // Checks for maximum memory size.
917   uint32_t maximum_pages = wasm::max_mem_pages();
918   if (memory_object->has_maximum_pages()) {
919     maximum_pages = std::min(
920         maximum_pages, static_cast<uint32_t>(memory_object->maximum_pages()));
921   }
922   size_t old_size = old_buffer->byte_length();
923   DCHECK_EQ(0, old_size % wasm::kWasmPageSize);
924   size_t old_pages = old_size / wasm::kWasmPageSize;
925   CHECK_GE(wasm::max_mem_pages(), old_pages);
926   if (pages > maximum_pages - old_pages) return -1;
927   std::shared_ptr<BackingStore> backing_store = old_buffer->GetBackingStore();
928   if (!backing_store) return -1;
929 
930   // Try to handle shared memory first.
931   if (old_buffer->is_shared()) {
932     if (FLAG_wasm_grow_shared_memory) {
933       base::Optional<size_t> result =
934           backing_store->GrowWasmMemoryInPlace(isolate, pages, maximum_pages);
935       // Shared memories can only be grown in place; no copying.
936       if (result.has_value()) {
937         BackingStore::BroadcastSharedWasmMemoryGrow(isolate, backing_store);
938         // Broadcasting the update should update this memory object too.
939         CHECK_NE(*old_buffer, memory_object->array_buffer());
940         size_t new_pages = result.value() + pages;
941         // If the allocation succeeded, then this can't possibly overflow:
942         size_t new_byte_length = new_pages * wasm::kWasmPageSize;
943         // This is a less than check, as it is not guaranteed that the SAB
944         // length here will be equal to the stashed length above as calls to
945         // grow the same memory object can come in from different workers.
946         // It is also possible that a call to Grow was in progress when
947         // handling this call.
948         CHECK_LE(new_byte_length, memory_object->array_buffer().byte_length());
949         // As {old_pages} was read racefully, we return here the synchronized
950         // value provided by {GrowWasmMemoryInPlace}, to provide the atomic
951         // read-modify-write behavior required by the spec.
952         return static_cast<int32_t>(result.value());  // success
953       }
954     }
955     return -1;
956   }
957 
958   base::Optional<size_t> result =
959       backing_store->GrowWasmMemoryInPlace(isolate, pages, maximum_pages);
960   // Try to grow non-shared memory in-place.
961   if (result.has_value()) {
962     // Detach old and create a new one with the grown backing store.
963     old_buffer->Detach(true);
964     Handle<JSArrayBuffer> new_buffer =
965         isolate->factory()->NewJSArrayBuffer(std::move(backing_store));
966     memory_object->update_instances(isolate, new_buffer);
967     DCHECK_EQ(result.value(), old_pages);
968     return static_cast<int32_t>(result.value());  // success
969   }
970 
971   size_t new_pages = old_pages + pages;
972   // Try allocating a new backing store and copying.
973   std::unique_ptr<BackingStore> new_backing_store =
974       backing_store->CopyWasmMemory(isolate, new_pages);
975   if (!new_backing_store) {
976     // Crash on out-of-memory if the correctness fuzzer is running.
977     if (FLAG_correctness_fuzzer_suppressions) {
978       FATAL("could not grow wasm memory");
979     }
980     return -1;
981   }
982 
983   // Detach old and create a new one with the new backing store.
984   old_buffer->Detach(true);
985   Handle<JSArrayBuffer> new_buffer =
986       isolate->factory()->NewJSArrayBuffer(std::move(new_backing_store));
987   memory_object->update_instances(isolate, new_buffer);
988   return static_cast<int32_t>(old_pages);  // success
989 }
990 
991 // static
New(Isolate * isolate,Handle<WasmInstanceObject> instance,MaybeHandle<JSArrayBuffer> maybe_untagged_buffer,MaybeHandle<FixedArray> maybe_tagged_buffer,wasm::ValueType type,int32_t offset,bool is_mutable)992 MaybeHandle<WasmGlobalObject> WasmGlobalObject::New(
993     Isolate* isolate, Handle<WasmInstanceObject> instance,
994     MaybeHandle<JSArrayBuffer> maybe_untagged_buffer,
995     MaybeHandle<FixedArray> maybe_tagged_buffer, wasm::ValueType type,
996     int32_t offset, bool is_mutable) {
997   Handle<JSFunction> global_ctor(
998       isolate->native_context()->wasm_global_constructor(), isolate);
999   auto global_obj = Handle<WasmGlobalObject>::cast(
1000       isolate->factory()->NewJSObject(global_ctor));
1001   {
1002     // Disallow GC until all fields have acceptable types.
1003     DisallowHeapAllocation no_gc;
1004     if (!instance.is_null()) global_obj->set_instance(*instance);
1005     global_obj->set_type(type);
1006     global_obj->set_offset(offset);
1007     global_obj->set_is_mutable(is_mutable);
1008   }
1009 
1010   if (type.is_reference_type()) {
1011     DCHECK(maybe_untagged_buffer.is_null());
1012     Handle<FixedArray> tagged_buffer;
1013     if (!maybe_tagged_buffer.ToHandle(&tagged_buffer)) {
1014       // If no buffer was provided, create one.
1015       tagged_buffer =
1016           isolate->factory()->NewFixedArray(1, AllocationType::kOld);
1017       CHECK_EQ(offset, 0);
1018     }
1019     global_obj->set_tagged_buffer(*tagged_buffer);
1020   } else {
1021     DCHECK(maybe_tagged_buffer.is_null());
1022     uint32_t type_size = type.element_size_bytes();
1023 
1024     Handle<JSArrayBuffer> untagged_buffer;
1025     if (!maybe_untagged_buffer.ToHandle(&untagged_buffer)) {
1026       MaybeHandle<JSArrayBuffer> result =
1027           isolate->factory()->NewJSArrayBufferAndBackingStore(
1028               offset + type_size, InitializedFlag::kZeroInitialized);
1029 
1030       if (!result.ToHandle(&untagged_buffer)) return {};
1031     }
1032 
1033     // Check that the offset is in bounds.
1034     CHECK_LE(offset + type_size, untagged_buffer->byte_length());
1035 
1036     global_obj->set_untagged_buffer(*untagged_buffer);
1037   }
1038 
1039   return global_obj;
1040 }
1041 
clear()1042 void IndirectFunctionTableEntry::clear() {
1043   if (!instance_.is_null()) {
1044     instance_->indirect_function_table_sig_ids()[index_] = -1;
1045     instance_->indirect_function_table_targets()[index_] = 0;
1046     instance_->indirect_function_table_refs().set(
1047         index_, ReadOnlyRoots(instance_->GetIsolate()).undefined_value());
1048   } else {
1049     DCHECK(!table_.is_null());
1050     table_->sig_ids()[index_] = -1;
1051     table_->targets()[index_] = 0;
1052     table_->refs().set(
1053         index_,
1054         ReadOnlyRoots(GetIsolateFromWritableObject(*table_)).undefined_value());
1055   }
1056 }
1057 
Set(int sig_id,Handle<WasmInstanceObject> target_instance,int target_func_index)1058 void IndirectFunctionTableEntry::Set(int sig_id,
1059                                      Handle<WasmInstanceObject> target_instance,
1060                                      int target_func_index) {
1061   TRACE_IFT("IFT entry 0x%" PRIxPTR
1062             "[%d] = {sig_id=%d, target_instance=0x%" PRIxPTR
1063             ", target_func_index=%d}\n",
1064             instance_->ptr(), index_, sig_id, target_instance->ptr(),
1065             target_func_index);
1066 
1067   Object ref;
1068   Address call_target = 0;
1069   if (target_func_index <
1070       static_cast<int>(target_instance->module()->num_imported_functions)) {
1071     // The function in the target instance was imported. Use its imports table,
1072     // which contains a tuple needed by the import wrapper.
1073     ImportedFunctionEntry entry(target_instance, target_func_index);
1074     ref = entry.object_ref();
1075     call_target = entry.target();
1076   } else {
1077     // The function in the target instance was not imported.
1078     ref = *target_instance;
1079     call_target = target_instance->GetCallTarget(target_func_index);
1080   }
1081   Set(sig_id, call_target, ref);
1082 }
1083 
Set(int sig_id,Address call_target,Object ref)1084 void IndirectFunctionTableEntry::Set(int sig_id, Address call_target,
1085                                      Object ref) {
1086   if (!instance_.is_null()) {
1087     instance_->indirect_function_table_sig_ids()[index_] = sig_id;
1088     instance_->indirect_function_table_targets()[index_] = call_target;
1089     instance_->indirect_function_table_refs().set(index_, ref);
1090   } else {
1091     DCHECK(!table_.is_null());
1092     table_->sig_ids()[index_] = sig_id;
1093     table_->targets()[index_] = call_target;
1094     table_->refs().set(index_, ref);
1095   }
1096 }
1097 
object_ref() const1098 Object IndirectFunctionTableEntry::object_ref() const {
1099   return !instance_.is_null()
1100              ? instance_->indirect_function_table_refs().get(index_)
1101              : table_->refs().get(index_);
1102 }
1103 
sig_id() const1104 int IndirectFunctionTableEntry::sig_id() const {
1105   return !instance_.is_null()
1106              ? instance_->indirect_function_table_sig_ids()[index_]
1107              : table_->sig_ids()[index_];
1108 }
1109 
target() const1110 Address IndirectFunctionTableEntry::target() const {
1111   return !instance_.is_null()
1112              ? instance_->indirect_function_table_targets()[index_]
1113              : table_->targets()[index_];
1114 }
1115 
SetWasmToJs(Isolate * isolate,Handle<JSReceiver> callable,const wasm::WasmCode * wasm_to_js_wrapper)1116 void ImportedFunctionEntry::SetWasmToJs(
1117     Isolate* isolate, Handle<JSReceiver> callable,
1118     const wasm::WasmCode* wasm_to_js_wrapper) {
1119   TRACE_IFT("Import callable 0x%" PRIxPTR "[%d] = {callable=0x%" PRIxPTR
1120             ", target=%p}\n",
1121             instance_->ptr(), index_, callable->ptr(),
1122             wasm_to_js_wrapper->instructions().begin());
1123   DCHECK(wasm_to_js_wrapper->kind() == wasm::WasmCode::kWasmToJsWrapper ||
1124          wasm_to_js_wrapper->kind() == wasm::WasmCode::kWasmToCapiWrapper);
1125   Handle<Tuple2> tuple =
1126       isolate->factory()->NewTuple2(instance_, callable, AllocationType::kOld);
1127   instance_->imported_function_refs().set(index_, *tuple);
1128   instance_->imported_function_targets()[index_] =
1129       wasm_to_js_wrapper->instruction_start();
1130 }
1131 
SetWasmToWasm(WasmInstanceObject instance,Address call_target)1132 void ImportedFunctionEntry::SetWasmToWasm(WasmInstanceObject instance,
1133                                           Address call_target) {
1134   TRACE_IFT("Import Wasm 0x%" PRIxPTR "[%d] = {instance=0x%" PRIxPTR
1135             ", target=0x%" PRIxPTR "}\n",
1136             instance_->ptr(), index_, instance.ptr(), call_target);
1137   instance_->imported_function_refs().set(index_, instance);
1138   instance_->imported_function_targets()[index_] = call_target;
1139 }
1140 
instance()1141 WasmInstanceObject ImportedFunctionEntry::instance() {
1142   // The imported reference entry is either a target instance or a tuple
1143   // of this instance and the target callable.
1144   Object value = object_ref();
1145   if (value.IsWasmInstanceObject()) {
1146     return WasmInstanceObject::cast(value);
1147   }
1148   Tuple2 tuple = Tuple2::cast(value);
1149   return WasmInstanceObject::cast(tuple.value1());
1150 }
1151 
1152 // Returns an empty Object() if no callable is available, a JSReceiver
1153 // otherwise.
maybe_callable()1154 Object ImportedFunctionEntry::maybe_callable() {
1155   Object value = object_ref();
1156   if (!value.IsTuple2()) return Object();
1157   Tuple2 tuple = Tuple2::cast(value);
1158   return JSReceiver::cast(tuple.value2());
1159 }
1160 
callable()1161 JSReceiver ImportedFunctionEntry::callable() {
1162   return JSReceiver::cast(Tuple2::cast(object_ref()).value2());
1163 }
1164 
object_ref()1165 Object ImportedFunctionEntry::object_ref() {
1166   return instance_->imported_function_refs().get(index_);
1167 }
1168 
target()1169 Address ImportedFunctionEntry::target() {
1170   return instance_->imported_function_targets()[index_];
1171 }
1172 
1173 // static
1174 constexpr uint16_t WasmInstanceObject::kTaggedFieldOffsets[];
1175 
1176 // static
EnsureIndirectFunctionTableWithMinimumSize(Handle<WasmInstanceObject> instance,int table_index,uint32_t minimum_size)1177 bool WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize(
1178     Handle<WasmInstanceObject> instance, int table_index,
1179     uint32_t minimum_size) {
1180   Isolate* isolate = instance->GetIsolate();
1181   if (table_index > 0) {
1182     DCHECK_LT(table_index, instance->indirect_function_tables().length());
1183     auto table =
1184         handle(WasmIndirectFunctionTable::cast(
1185                    instance->indirect_function_tables().get(table_index)),
1186                isolate);
1187     WasmIndirectFunctionTable::Resize(isolate, table, minimum_size);
1188     return true;
1189   }
1190 
1191   uint32_t old_size = instance->indirect_function_table_size();
1192   if (old_size >= minimum_size) return false;  // Nothing to do.
1193 
1194   auto native_allocations = GetNativeAllocations(*instance);
1195   if (native_allocations->indirect_function_table_capacity() < minimum_size) {
1196     HandleScope scope(isolate);
1197     native_allocations->resize_indirect_function_table(isolate, instance,
1198                                                        minimum_size);
1199     DCHECK_GE(native_allocations->indirect_function_table_capacity(),
1200               minimum_size);
1201   }
1202   instance->set_indirect_function_table_size(minimum_size);
1203   for (uint32_t j = old_size; j < minimum_size; j++) {
1204     // {WasmInstanceNativeAllocations} only manages the memory of table 0.
1205     // Therefore we pass the {table_index} as a constant here.
1206     IndirectFunctionTableEntry(instance, 0, static_cast<int>(j)).clear();
1207   }
1208 
1209   return true;
1210 }
1211 
SetRawMemory(byte * mem_start,size_t mem_size)1212 void WasmInstanceObject::SetRawMemory(byte* mem_start, size_t mem_size) {
1213   CHECK_LE(mem_size, wasm::max_mem_bytes());
1214 #if V8_HOST_ARCH_64_BIT
1215   uint64_t mem_mask64 = base::bits::RoundUpToPowerOfTwo64(mem_size) - 1;
1216   set_memory_start(mem_start);
1217   set_memory_size(mem_size);
1218   set_memory_mask(mem_mask64);
1219 #else
1220   // Must handle memory > 2GiB specially.
1221   CHECK_LE(mem_size, size_t{kMaxUInt32});
1222   uint32_t mem_mask32 =
1223       (mem_size > 2 * size_t{GB})
1224           ? 0xFFFFFFFFu
1225           : base::bits::RoundUpToPowerOfTwo32(static_cast<uint32_t>(mem_size)) -
1226                 1;
1227   set_memory_start(mem_start);
1228   set_memory_size(mem_size);
1229   set_memory_mask(mem_mask32);
1230 #endif
1231 }
1232 
module()1233 const WasmModule* WasmInstanceObject::module() {
1234   return module_object().module();
1235 }
1236 
New(Isolate * isolate,Handle<WasmModuleObject> module_object)1237 Handle<WasmInstanceObject> WasmInstanceObject::New(
1238     Isolate* isolate, Handle<WasmModuleObject> module_object) {
1239   Handle<JSFunction> instance_cons(
1240       isolate->native_context()->wasm_instance_constructor(), isolate);
1241   Handle<JSObject> instance_object =
1242       isolate->factory()->NewJSObject(instance_cons, AllocationType::kOld);
1243 
1244   Handle<WasmInstanceObject> instance(
1245       WasmInstanceObject::cast(*instance_object), isolate);
1246   instance->clear_padding();
1247 
1248   // Initialize the imported function arrays.
1249   auto module = module_object->module();
1250   auto num_imported_functions = module->num_imported_functions;
1251   auto num_imported_mutable_globals = module->num_imported_mutable_globals;
1252   auto num_data_segments = module->num_declared_data_segments;
1253   size_t native_allocations_size = EstimateNativeAllocationsSize(module);
1254   auto native_allocations = Managed<WasmInstanceNativeAllocations>::Allocate(
1255       isolate, native_allocations_size, instance, num_imported_functions,
1256       num_imported_mutable_globals, num_data_segments,
1257       module->elem_segments.size());
1258   instance->set_managed_native_allocations(*native_allocations);
1259 
1260   Handle<FixedArray> imported_function_refs =
1261       isolate->factory()->NewFixedArray(num_imported_functions);
1262   instance->set_imported_function_refs(*imported_function_refs);
1263 
1264   instance->SetRawMemory(nullptr, 0);
1265   instance->set_isolate_root(isolate->isolate_root());
1266   instance->set_stack_limit_address(
1267       isolate->stack_guard()->address_of_jslimit());
1268   instance->set_real_stack_limit_address(
1269       isolate->stack_guard()->address_of_real_jslimit());
1270   instance->set_globals_start(nullptr);
1271   instance->set_indirect_function_table_size(0);
1272   instance->set_indirect_function_table_refs(
1273       ReadOnlyRoots(isolate).empty_fixed_array());
1274   instance->set_indirect_function_table_sig_ids(nullptr);
1275   instance->set_indirect_function_table_targets(nullptr);
1276   instance->set_native_context(*isolate->native_context());
1277   instance->set_module_object(*module_object);
1278   instance->set_jump_table_start(
1279       module_object->native_module()->jump_table_start());
1280   instance->set_hook_on_function_call_address(
1281       isolate->debug()->hook_on_function_call_address());
1282   instance->set_managed_object_maps(*isolate->factory()->empty_fixed_array());
1283   instance->set_num_liftoff_function_calls_array(
1284       module_object->native_module()->num_liftoff_function_calls_array());
1285 
1286   // Insert the new instance into the scripts weak list of instances. This list
1287   // is used for breakpoints affecting all instances belonging to the script.
1288   // TODO(wasm): Allow to reuse holes in the {WeakArrayList} below.
1289   if (module_object->script().type() == Script::TYPE_WASM) {
1290     Handle<WeakArrayList> weak_instance_list(
1291         module_object->script().wasm_weak_instance_list(), isolate);
1292     weak_instance_list = WeakArrayList::AddToEnd(
1293         isolate, weak_instance_list, MaybeObjectHandle::Weak(instance));
1294     module_object->script().set_wasm_weak_instance_list(*weak_instance_list);
1295   }
1296 
1297   InitDataSegmentArrays(instance, module_object);
1298   InitElemSegmentArrays(instance, module_object);
1299 
1300   return instance;
1301 }
1302 
1303 // static
InitDataSegmentArrays(Handle<WasmInstanceObject> instance,Handle<WasmModuleObject> module_object)1304 void WasmInstanceObject::InitDataSegmentArrays(
1305     Handle<WasmInstanceObject> instance,
1306     Handle<WasmModuleObject> module_object) {
1307   auto module = module_object->module();
1308   auto wire_bytes = module_object->native_module()->wire_bytes();
1309   auto num_data_segments = module->num_declared_data_segments;
1310   // The number of declared data segments will be zero if there is no DataCount
1311   // section. These arrays will not be allocated nor initialized in that case,
1312   // since they cannot be used (since the validator checks that number of
1313   // declared data segments when validating the memory.init and memory.drop
1314   // instructions).
1315   DCHECK(num_data_segments == 0 ||
1316          num_data_segments == module->data_segments.size());
1317   for (size_t i = 0; i < num_data_segments; ++i) {
1318     const wasm::WasmDataSegment& segment = module->data_segments[i];
1319     // Initialize the pointer and size of passive segments.
1320     auto source_bytes = wire_bytes.SubVector(segment.source.offset(),
1321                                              segment.source.end_offset());
1322     instance->data_segment_starts()[i] =
1323         reinterpret_cast<Address>(source_bytes.begin());
1324     // Set the active segments to being already dropped, since memory.init on
1325     // a dropped passive segment and an active segment have the same
1326     // behavior.
1327     instance->data_segment_sizes()[i] =
1328         segment.active ? 0 : source_bytes.length();
1329   }
1330 }
1331 
InitElemSegmentArrays(Handle<WasmInstanceObject> instance,Handle<WasmModuleObject> module_object)1332 void WasmInstanceObject::InitElemSegmentArrays(
1333     Handle<WasmInstanceObject> instance,
1334     Handle<WasmModuleObject> module_object) {
1335   auto module = module_object->module();
1336   auto num_elem_segments = module->elem_segments.size();
1337   for (size_t i = 0; i < num_elem_segments; ++i) {
1338     instance->dropped_elem_segments()[i] =
1339         module->elem_segments[i].status ==
1340                 wasm::WasmElemSegment::kStatusDeclarative
1341             ? 1
1342             : 0;
1343   }
1344 }
1345 
GetCallTarget(uint32_t func_index)1346 Address WasmInstanceObject::GetCallTarget(uint32_t func_index) {
1347   wasm::NativeModule* native_module = module_object().native_module();
1348   if (func_index < native_module->num_imported_functions()) {
1349     return imported_function_targets()[func_index];
1350   }
1351   return native_module->GetCallTargetForFunction(func_index);
1352 }
1353 
IndirectFunctionTableSize(Isolate * isolate,Handle<WasmInstanceObject> instance,uint32_t table_index)1354 int WasmInstanceObject::IndirectFunctionTableSize(
1355     Isolate* isolate, Handle<WasmInstanceObject> instance,
1356     uint32_t table_index) {
1357   if (table_index == 0) {
1358     return instance->indirect_function_table_size();
1359   }
1360   auto table =
1361       handle(WasmIndirectFunctionTable::cast(
1362                  instance->indirect_function_tables().get(table_index)),
1363              isolate);
1364   return table->size();
1365 }
1366 
1367 // static
CopyTableEntries(Isolate * isolate,Handle<WasmInstanceObject> instance,uint32_t table_dst_index,uint32_t table_src_index,uint32_t dst,uint32_t src,uint32_t count)1368 bool WasmInstanceObject::CopyTableEntries(Isolate* isolate,
1369                                           Handle<WasmInstanceObject> instance,
1370                                           uint32_t table_dst_index,
1371                                           uint32_t table_src_index,
1372                                           uint32_t dst, uint32_t src,
1373                                           uint32_t count) {
1374   CHECK_LT(table_dst_index, instance->tables().length());
1375   CHECK_LT(table_src_index, instance->tables().length());
1376   auto table_dst = handle(
1377       WasmTableObject::cast(instance->tables().get(table_dst_index)), isolate);
1378   auto table_src = handle(
1379       WasmTableObject::cast(instance->tables().get(table_src_index)), isolate);
1380   uint32_t max_dst = table_dst->current_length();
1381   uint32_t max_src = table_src->current_length();
1382   bool copy_backward = src < dst;
1383   if (!base::IsInBounds(dst, count, max_dst) ||
1384       !base::IsInBounds(src, count, max_src)) {
1385     return false;
1386   }
1387 
1388   // no-op
1389   if ((dst == src && table_dst_index == table_src_index) || count == 0) {
1390     return true;
1391   }
1392 
1393   for (uint32_t i = 0; i < count; ++i) {
1394     uint32_t src_index = copy_backward ? (src + count - i - 1) : src + i;
1395     uint32_t dst_index = copy_backward ? (dst + count - i - 1) : dst + i;
1396     auto value = WasmTableObject::Get(isolate, table_src, src_index);
1397     WasmTableObject::Set(isolate, table_dst, dst_index, value);
1398   }
1399   return true;
1400 }
1401 
1402 // static
InitTableEntries(Isolate * isolate,Handle<WasmInstanceObject> instance,uint32_t table_index,uint32_t segment_index,uint32_t dst,uint32_t src,uint32_t count)1403 bool WasmInstanceObject::InitTableEntries(Isolate* isolate,
1404                                           Handle<WasmInstanceObject> instance,
1405                                           uint32_t table_index,
1406                                           uint32_t segment_index, uint32_t dst,
1407                                           uint32_t src, uint32_t count) {
1408   // Note that this implementation just calls through to module instantiation.
1409   // This is intentional, so that the runtime only depends on the object
1410   // methods, and not the module instantiation logic.
1411   return wasm::LoadElemSegment(isolate, instance, table_index, segment_index,
1412                                dst, src, count);
1413 }
1414 
GetWasmExternalFunction(Isolate * isolate,Handle<WasmInstanceObject> instance,int index)1415 MaybeHandle<WasmExternalFunction> WasmInstanceObject::GetWasmExternalFunction(
1416     Isolate* isolate, Handle<WasmInstanceObject> instance, int index) {
1417   MaybeHandle<WasmExternalFunction> result;
1418   if (instance->has_wasm_external_functions()) {
1419     Object val = instance->wasm_external_functions().get(index);
1420     if (!val.IsUndefined(isolate)) {
1421       result = Handle<WasmExternalFunction>(WasmExternalFunction::cast(val),
1422                                             isolate);
1423     }
1424   }
1425   return result;
1426 }
1427 
1428 Handle<WasmExternalFunction>
GetOrCreateWasmExternalFunction(Isolate * isolate,Handle<WasmInstanceObject> instance,int function_index)1429 WasmInstanceObject::GetOrCreateWasmExternalFunction(
1430     Isolate* isolate, Handle<WasmInstanceObject> instance, int function_index) {
1431   MaybeHandle<WasmExternalFunction> maybe_result =
1432       WasmInstanceObject::GetWasmExternalFunction(isolate, instance,
1433                                                   function_index);
1434 
1435   Handle<WasmExternalFunction> result;
1436   if (maybe_result.ToHandle(&result)) {
1437     return result;
1438   }
1439 
1440   Handle<WasmModuleObject> module_object(instance->module_object(), isolate);
1441   const WasmModule* module = module_object->module();
1442   const WasmFunction& function = module->functions[function_index];
1443   int wrapper_index =
1444       GetExportWrapperIndex(module, function.sig, function.imported);
1445 
1446   Handle<Object> entry =
1447       FixedArray::get(module_object->export_wrappers(), wrapper_index, isolate);
1448 
1449   Handle<Code> wrapper;
1450   if (entry->IsCode()) {
1451     wrapper = Handle<Code>::cast(entry);
1452   } else {
1453     // The wrapper may not exist yet if no function in the exports section has
1454     // this signature. We compile it and store the wrapper in the module for
1455     // later use.
1456     wrapper = wasm::JSToWasmWrapperCompilationUnit::CompileJSToWasmWrapper(
1457         isolate, function.sig, instance->module(), function.imported);
1458     module_object->export_wrappers().set(wrapper_index, *wrapper);
1459   }
1460   result = Handle<WasmExternalFunction>::cast(WasmExportedFunction::New(
1461       isolate, instance, function_index,
1462       static_cast<int>(function.sig->parameter_count()), wrapper));
1463 
1464   WasmInstanceObject::SetWasmExternalFunction(isolate, instance, function_index,
1465                                               result);
1466   return result;
1467 }
1468 
SetWasmExternalFunction(Isolate * isolate,Handle<WasmInstanceObject> instance,int index,Handle<WasmExternalFunction> val)1469 void WasmInstanceObject::SetWasmExternalFunction(
1470     Isolate* isolate, Handle<WasmInstanceObject> instance, int index,
1471     Handle<WasmExternalFunction> val) {
1472   Handle<FixedArray> functions;
1473   if (!instance->has_wasm_external_functions()) {
1474     // Lazily allocate the wasm external functions array.
1475     functions = isolate->factory()->NewFixedArray(
1476         static_cast<int>(instance->module()->functions.size()));
1477     instance->set_wasm_external_functions(*functions);
1478   } else {
1479     functions =
1480         Handle<FixedArray>(instance->wasm_external_functions(), isolate);
1481   }
1482   functions->set(index, *val);
1483 }
1484 
1485 // static
ImportWasmJSFunctionIntoTable(Isolate * isolate,Handle<WasmInstanceObject> instance,int table_index,int entry_index,Handle<WasmJSFunction> js_function)1486 void WasmInstanceObject::ImportWasmJSFunctionIntoTable(
1487     Isolate* isolate, Handle<WasmInstanceObject> instance, int table_index,
1488     int entry_index, Handle<WasmJSFunction> js_function) {
1489   // Deserialize the signature encapsulated with the {WasmJSFunction}.
1490   // Note that {SignatureMap::Find} may return {-1} if the signature is
1491   // not found; it will simply never match any check.
1492   Zone zone(isolate->allocator(), ZONE_NAME);
1493   const wasm::FunctionSig* sig = js_function->GetSignature(&zone);
1494   auto sig_id = instance->module()->signature_map.Find(*sig);
1495 
1496   // Compile a wrapper for the target callable.
1497   Handle<JSReceiver> callable(js_function->GetCallable(), isolate);
1498   wasm::WasmCodeRefScope code_ref_scope;
1499   Address call_target = kNullAddress;
1500   if (sig_id >= 0) {
1501     wasm::NativeModule* native_module =
1502         instance->module_object().native_module();
1503     // TODO(wasm): Cache and reuse wrapper code.
1504     const wasm::WasmFeatures enabled = native_module->enabled_features();
1505     auto resolved = compiler::ResolveWasmImportCall(
1506         callable, sig, instance->module(), enabled);
1507     compiler::WasmImportCallKind kind = resolved.first;
1508     callable = resolved.second;  // Update to ultimate target.
1509     DCHECK_NE(compiler::WasmImportCallKind::kLinkError, kind);
1510     wasm::CompilationEnv env = native_module->CreateCompilationEnv();
1511     // {expected_arity} should only be used if kind != kJSFunctionArityMismatch.
1512     int expected_arity = -1;
1513     if (kind == compiler::WasmImportCallKind ::kJSFunctionArityMismatch) {
1514       expected_arity = Handle<JSFunction>::cast(callable)
1515                            ->shared()
1516                            .internal_formal_parameter_count();
1517     }
1518     wasm::WasmCompilationResult result = compiler::CompileWasmImportCallWrapper(
1519         isolate->wasm_engine(), &env, kind, sig, false, expected_arity);
1520     std::unique_ptr<wasm::WasmCode> wasm_code = native_module->AddCode(
1521         result.func_index, result.code_desc, result.frame_slot_count,
1522         result.tagged_parameter_slots,
1523         result.protected_instructions_data.as_vector(),
1524         result.source_positions.as_vector(), GetCodeKind(result),
1525         wasm::ExecutionTier::kNone, wasm::kNoDebugging);
1526     wasm::WasmCode* published_code =
1527         native_module->PublishCode(std::move(wasm_code));
1528     isolate->counters()->wasm_generated_code_size()->Increment(
1529         published_code->instructions().length());
1530     isolate->counters()->wasm_reloc_size()->Increment(
1531         published_code->reloc_info().length());
1532     call_target = published_code->instruction_start();
1533   }
1534 
1535   // Update the dispatch table.
1536   Handle<Tuple2> tuple =
1537       isolate->factory()->NewTuple2(instance, callable, AllocationType::kOld);
1538   IndirectFunctionTableEntry(instance, table_index, entry_index)
1539       .Set(sig_id, call_target, *tuple);
1540 }
1541 
1542 // static
GetGlobalStorage(Handle<WasmInstanceObject> instance,const wasm::WasmGlobal & global)1543 uint8_t* WasmInstanceObject::GetGlobalStorage(
1544     Handle<WasmInstanceObject> instance, const wasm::WasmGlobal& global) {
1545   DCHECK(!global.type.is_reference_type());
1546   if (global.mutability && global.imported) {
1547     return reinterpret_cast<byte*>(
1548         instance->imported_mutable_globals()[global.index]);
1549   } else {
1550     return instance->globals_start() + global.offset;
1551   }
1552 }
1553 
1554 // static
1555 std::pair<Handle<FixedArray>, uint32_t>
GetGlobalBufferAndIndex(Handle<WasmInstanceObject> instance,const wasm::WasmGlobal & global)1556 WasmInstanceObject::GetGlobalBufferAndIndex(Handle<WasmInstanceObject> instance,
1557                                             const wasm::WasmGlobal& global) {
1558   DCHECK(global.type.is_reference_type());
1559   Isolate* isolate = instance->GetIsolate();
1560   if (global.mutability && global.imported) {
1561     Handle<FixedArray> buffer(
1562         FixedArray::cast(
1563             instance->imported_mutable_globals_buffers().get(global.index)),
1564         isolate);
1565     Address idx = instance->imported_mutable_globals()[global.index];
1566     DCHECK_LE(idx, std::numeric_limits<uint32_t>::max());
1567     return {buffer, static_cast<uint32_t>(idx)};
1568   }
1569   return {handle(instance->tagged_globals_buffer(), isolate), global.offset};
1570 }
1571 
1572 // static
GetGlobalNameOrNull(Isolate * isolate,Handle<WasmInstanceObject> instance,uint32_t global_index)1573 MaybeHandle<String> WasmInstanceObject::GetGlobalNameOrNull(
1574     Isolate* isolate, Handle<WasmInstanceObject> instance,
1575     uint32_t global_index) {
1576   return WasmInstanceObject::GetNameFromImportsAndExportsOrNull(
1577       isolate, instance, wasm::ImportExportKindCode::kExternalGlobal,
1578       global_index);
1579 }
1580 
1581 // static
GetMemoryNameOrNull(Isolate * isolate,Handle<WasmInstanceObject> instance,uint32_t memory_index)1582 MaybeHandle<String> WasmInstanceObject::GetMemoryNameOrNull(
1583     Isolate* isolate, Handle<WasmInstanceObject> instance,
1584     uint32_t memory_index) {
1585   return WasmInstanceObject::GetNameFromImportsAndExportsOrNull(
1586       isolate, instance, wasm::ImportExportKindCode::kExternalMemory,
1587       memory_index);
1588 }
1589 
1590 // static
GetTableNameOrNull(Isolate * isolate,Handle<WasmInstanceObject> instance,uint32_t table_index)1591 MaybeHandle<String> WasmInstanceObject::GetTableNameOrNull(
1592     Isolate* isolate, Handle<WasmInstanceObject> instance,
1593     uint32_t table_index) {
1594   return WasmInstanceObject::GetNameFromImportsAndExportsOrNull(
1595       isolate, instance, wasm::ImportExportKindCode::kExternalTable,
1596       table_index);
1597 }
1598 
1599 // static
GetNameFromImportsAndExportsOrNull(Isolate * isolate,Handle<WasmInstanceObject> instance,wasm::ImportExportKindCode kind,uint32_t index)1600 MaybeHandle<String> WasmInstanceObject::GetNameFromImportsAndExportsOrNull(
1601     Isolate* isolate, Handle<WasmInstanceObject> instance,
1602     wasm::ImportExportKindCode kind, uint32_t index) {
1603   DCHECK(kind == wasm::ImportExportKindCode::kExternalGlobal ||
1604          kind == wasm::ImportExportKindCode::kExternalMemory ||
1605          kind == wasm::ImportExportKindCode::kExternalTable);
1606   wasm::ModuleWireBytes wire_bytes(
1607       instance->module_object().native_module()->wire_bytes());
1608 
1609   // This is pair of <module_name, field_name>.
1610   // If field_name is not set then we don't generate a name. Else if module_name
1611   // is set then it is an imported one. Otherwise it is an exported one.
1612   std::pair<wasm::WireBytesRef, wasm::WireBytesRef> name_ref =
1613       instance->module()
1614           ->lazily_generated_names.LookupNameFromImportsAndExports(
1615               kind, index, VectorOf(instance->module()->import_table),
1616               VectorOf(instance->module()->export_table));
1617   if (!name_ref.second.is_set()) return {};
1618   Vector<const char> field_name = wire_bytes.GetNameOrNull(name_ref.second);
1619   if (!name_ref.first.is_set()) {
1620     return isolate->factory()->NewStringFromUtf8(VectorOf(field_name));
1621   }
1622   Vector<const char> module_name = wire_bytes.GetNameOrNull(name_ref.first);
1623   std::string full_name;
1624   full_name.append(module_name.begin(), module_name.end());
1625   full_name.append(".");
1626   full_name.append(field_name.begin(), field_name.end());
1627   return isolate->factory()->NewStringFromUtf8(VectorOf(full_name));
1628 }
1629 
1630 // static
GetGlobalValue(Handle<WasmInstanceObject> instance,const wasm::WasmGlobal & global)1631 wasm::WasmValue WasmInstanceObject::GetGlobalValue(
1632     Handle<WasmInstanceObject> instance, const wasm::WasmGlobal& global) {
1633   Isolate* isolate = instance->GetIsolate();
1634   if (global.type.is_reference_type()) {
1635     Handle<FixedArray> global_buffer;  // The buffer of the global.
1636     uint32_t global_index = 0;         // The index into the buffer.
1637     std::tie(global_buffer, global_index) =
1638         GetGlobalBufferAndIndex(instance, global);
1639     return wasm::WasmValue(handle(global_buffer->get(global_index), isolate));
1640   }
1641   Address ptr = reinterpret_cast<Address>(GetGlobalStorage(instance, global));
1642   using wasm::Simd128;
1643   switch (global.type.kind()) {
1644 #define CASE_TYPE(valuetype, ctype) \
1645   case wasm::ValueType::valuetype:  \
1646     return wasm::WasmValue(base::ReadLittleEndianValue<ctype>(ptr));
1647     FOREACH_WASMVALUE_CTYPES(CASE_TYPE)
1648 #undef CASE_TYPE
1649     default:
1650       UNREACHABLE();
1651   }
1652 }
1653 
1654 // static
New(Isolate * isolate,const wasm::FunctionSig * sig,Handle<HeapObject> exception_tag)1655 Handle<WasmExceptionObject> WasmExceptionObject::New(
1656     Isolate* isolate, const wasm::FunctionSig* sig,
1657     Handle<HeapObject> exception_tag) {
1658   Handle<JSFunction> exception_cons(
1659       isolate->native_context()->wasm_exception_constructor(), isolate);
1660 
1661   // Serialize the signature.
1662   DCHECK_EQ(0, sig->return_count());
1663   DCHECK_LE(sig->parameter_count(), std::numeric_limits<int>::max());
1664   int sig_size = static_cast<int>(sig->parameter_count());
1665   Handle<PodArray<wasm::ValueType>> serialized_sig =
1666       PodArray<wasm::ValueType>::New(isolate, sig_size, AllocationType::kOld);
1667   int index = 0;  // Index into the {PodArray} above.
1668   for (wasm::ValueType param : sig->parameters()) {
1669     serialized_sig->set(index++, param);
1670   }
1671 
1672   Handle<JSObject> exception_object =
1673       isolate->factory()->NewJSObject(exception_cons, AllocationType::kOld);
1674   Handle<WasmExceptionObject> exception =
1675       Handle<WasmExceptionObject>::cast(exception_object);
1676   exception->set_serialized_signature(*serialized_sig);
1677   exception->set_exception_tag(*exception_tag);
1678 
1679   return exception;
1680 }
1681 
1682 // TODO(9495): Update this if function type variance is introduced.
MatchesSignature(const wasm::FunctionSig * sig)1683 bool WasmExceptionObject::MatchesSignature(const wasm::FunctionSig* sig) {
1684   DCHECK_EQ(0, sig->return_count());
1685   DCHECK_LE(sig->parameter_count(), std::numeric_limits<int>::max());
1686   int sig_size = static_cast<int>(sig->parameter_count());
1687   if (sig_size != serialized_signature().length()) return false;
1688   for (int index = 0; index < sig_size; ++index) {
1689     if (sig->GetParam(index) != serialized_signature().get(index)) {
1690       return false;
1691     }
1692   }
1693   return true;
1694 }
1695 
1696 // TODO(9495): Update this if function type variance is introduced.
MatchesSignature(const wasm::FunctionSig * sig) const1697 bool WasmCapiFunction::MatchesSignature(const wasm::FunctionSig* sig) const {
1698   // TODO(jkummerow): Unify with "SignatureHelper" in c-api.cc.
1699   int param_count = static_cast<int>(sig->parameter_count());
1700   int result_count = static_cast<int>(sig->return_count());
1701   PodArray<wasm::ValueType> serialized_sig =
1702       shared().wasm_capi_function_data().serialized_signature();
1703   if (param_count + result_count + 1 != serialized_sig.length()) return false;
1704   int serialized_index = 0;
1705   for (int i = 0; i < result_count; i++, serialized_index++) {
1706     if (sig->GetReturn(i) != serialized_sig.get(serialized_index)) {
1707       return false;
1708     }
1709   }
1710   if (serialized_sig.get(serialized_index) != wasm::kWasmStmt) return false;
1711   serialized_index++;
1712   for (int i = 0; i < param_count; i++, serialized_index++) {
1713     if (sig->GetParam(i) != serialized_sig.get(serialized_index)) return false;
1714   }
1715   return true;
1716 }
1717 
1718 // static
New(Isolate * isolate,Handle<WasmExceptionTag> exception_tag,int size)1719 Handle<WasmExceptionPackage> WasmExceptionPackage::New(
1720     Isolate* isolate, Handle<WasmExceptionTag> exception_tag, int size) {
1721   Handle<Object> exception = isolate->factory()->NewWasmRuntimeError(
1722       MessageTemplate::kWasmExceptionError);
1723   CHECK(!Object::SetProperty(isolate, exception,
1724                              isolate->factory()->wasm_exception_tag_symbol(),
1725                              exception_tag, StoreOrigin::kMaybeKeyed,
1726                              Just(ShouldThrow::kThrowOnError))
1727              .is_null());
1728   Handle<FixedArray> values = isolate->factory()->NewFixedArray(size);
1729   CHECK(!Object::SetProperty(isolate, exception,
1730                              isolate->factory()->wasm_exception_values_symbol(),
1731                              values, StoreOrigin::kMaybeKeyed,
1732                              Just(ShouldThrow::kThrowOnError))
1733              .is_null());
1734   return Handle<WasmExceptionPackage>::cast(exception);
1735 }
1736 
1737 // static
GetExceptionTag(Isolate * isolate,Handle<WasmExceptionPackage> exception_package)1738 Handle<Object> WasmExceptionPackage::GetExceptionTag(
1739     Isolate* isolate, Handle<WasmExceptionPackage> exception_package) {
1740   Handle<Object> tag;
1741   if (JSReceiver::GetProperty(isolate, exception_package,
1742                               isolate->factory()->wasm_exception_tag_symbol())
1743           .ToHandle(&tag)) {
1744     return tag;
1745   }
1746   return ReadOnlyRoots(isolate).undefined_value_handle();
1747 }
1748 
1749 // static
GetExceptionValues(Isolate * isolate,Handle<WasmExceptionPackage> exception_package)1750 Handle<Object> WasmExceptionPackage::GetExceptionValues(
1751     Isolate* isolate, Handle<WasmExceptionPackage> exception_package) {
1752   Handle<Object> values;
1753   if (JSReceiver::GetProperty(
1754           isolate, exception_package,
1755           isolate->factory()->wasm_exception_values_symbol())
1756           .ToHandle(&values)) {
1757     DCHECK(values->IsFixedArray());
1758     return values;
1759   }
1760   return ReadOnlyRoots(isolate).undefined_value_handle();
1761 }
1762 
1763 #ifdef DEBUG
1764 
1765 namespace {
1766 
1767 constexpr uint32_t kBytesPerExceptionValuesArrayElement = 2;
1768 
ComputeEncodedElementSize(wasm::ValueType type)1769 size_t ComputeEncodedElementSize(wasm::ValueType type) {
1770   size_t byte_size = type.element_size_bytes();
1771   DCHECK_EQ(byte_size % kBytesPerExceptionValuesArrayElement, 0);
1772   DCHECK_LE(1, byte_size / kBytesPerExceptionValuesArrayElement);
1773   return byte_size / kBytesPerExceptionValuesArrayElement;
1774 }
1775 
1776 }  // namespace
1777 
1778 #endif  // DEBUG
1779 
1780 // static
GetEncodedSize(const wasm::WasmException * exception)1781 uint32_t WasmExceptionPackage::GetEncodedSize(
1782     const wasm::WasmException* exception) {
1783   const wasm::WasmExceptionSig* sig = exception->sig;
1784   uint32_t encoded_size = 0;
1785   for (size_t i = 0; i < sig->parameter_count(); ++i) {
1786     switch (sig->GetParam(i).kind()) {
1787       case wasm::ValueType::kI32:
1788       case wasm::ValueType::kF32:
1789         DCHECK_EQ(2, ComputeEncodedElementSize(sig->GetParam(i)));
1790         encoded_size += 2;
1791         break;
1792       case wasm::ValueType::kI64:
1793       case wasm::ValueType::kF64:
1794         DCHECK_EQ(4, ComputeEncodedElementSize(sig->GetParam(i)));
1795         encoded_size += 4;
1796         break;
1797       case wasm::ValueType::kS128:
1798         DCHECK_EQ(8, ComputeEncodedElementSize(sig->GetParam(i)));
1799         encoded_size += 8;
1800         break;
1801       case wasm::ValueType::kRef:
1802       case wasm::ValueType::kOptRef:
1803         encoded_size += 1;
1804         break;
1805       case wasm::ValueType::kRtt:
1806       case wasm::ValueType::kStmt:
1807       case wasm::ValueType::kBottom:
1808       case wasm::ValueType::kI8:
1809       case wasm::ValueType::kI16:
1810         UNREACHABLE();
1811     }
1812   }
1813   return encoded_size;
1814 }
1815 
IsWasmExportedFunction(Object object)1816 bool WasmExportedFunction::IsWasmExportedFunction(Object object) {
1817   if (!object.IsJSFunction()) return false;
1818   JSFunction js_function = JSFunction::cast(object);
1819   if (CodeKind::JS_TO_WASM_FUNCTION != js_function.code().kind() &&
1820       js_function.code().builtin_index() != Builtins::kGenericJSToWasmWrapper) {
1821     return false;
1822   }
1823   DCHECK(js_function.shared().HasWasmExportedFunctionData());
1824   return true;
1825 }
1826 
IsWasmCapiFunction(Object object)1827 bool WasmCapiFunction::IsWasmCapiFunction(Object object) {
1828   if (!object.IsJSFunction()) return false;
1829   JSFunction js_function = JSFunction::cast(object);
1830   // TODO(jkummerow): Enable this when there is a JavaScript wrapper
1831   // able to call this function.
1832   // if (js_function->code()->kind() != CodeKind::WASM_TO_CAPI_FUNCTION) {
1833   //   return false;
1834   // }
1835   // DCHECK(js_function->shared()->HasWasmCapiFunctionData());
1836   // return true;
1837   return js_function.shared().HasWasmCapiFunctionData();
1838 }
1839 
New(Isolate * isolate,Address call_target,Handle<Foreign> embedder_data,Handle<PodArray<wasm::ValueType>> serialized_signature)1840 Handle<WasmCapiFunction> WasmCapiFunction::New(
1841     Isolate* isolate, Address call_target, Handle<Foreign> embedder_data,
1842     Handle<PodArray<wasm::ValueType>> serialized_signature) {
1843   // TODO(jkummerow): Install a JavaScript wrapper. For now, calling
1844   // these functions directly is unsupported; they can only be called
1845   // from Wasm code.
1846   Handle<WasmCapiFunctionData> fun_data =
1847       isolate->factory()->NewWasmCapiFunctionData(
1848           call_target, embedder_data,
1849           isolate->builtins()->builtin_handle(Builtins::kIllegal),
1850           serialized_signature, AllocationType::kOld);
1851   Handle<SharedFunctionInfo> shared =
1852       isolate->factory()->NewSharedFunctionInfoForWasmCapiFunction(fun_data);
1853   return Handle<WasmCapiFunction>::cast(
1854       isolate->factory()->NewFunctionFromSharedFunctionInfo(
1855           shared, isolate->native_context()));
1856 }
1857 
instance()1858 WasmInstanceObject WasmExportedFunction::instance() {
1859   return shared().wasm_exported_function_data().instance();
1860 }
1861 
function_index()1862 int WasmExportedFunction::function_index() {
1863   return shared().wasm_exported_function_data().function_index();
1864 }
1865 
New(Isolate * isolate,Handle<WasmInstanceObject> instance,int func_index,int arity,Handle<Code> export_wrapper)1866 Handle<WasmExportedFunction> WasmExportedFunction::New(
1867     Isolate* isolate, Handle<WasmInstanceObject> instance, int func_index,
1868     int arity, Handle<Code> export_wrapper) {
1869   DCHECK(
1870       CodeKind::JS_TO_WASM_FUNCTION == export_wrapper->kind() ||
1871       (export_wrapper->is_builtin() &&
1872        export_wrapper->builtin_index() == Builtins::kGenericJSToWasmWrapper));
1873   int num_imported_functions = instance->module()->num_imported_functions;
1874   int jump_table_offset = -1;
1875   if (func_index >= num_imported_functions) {
1876     uint32_t jump_table_diff =
1877         instance->module_object().native_module()->GetJumpTableOffset(
1878             func_index);
1879     DCHECK_GE(kMaxInt, jump_table_diff);
1880     jump_table_offset = static_cast<int>(jump_table_diff);
1881   }
1882   const wasm::FunctionSig* sig = instance->module()->functions[func_index].sig;
1883   Handle<Foreign> sig_foreign =
1884       isolate->factory()->NewForeign(reinterpret_cast<Address>(sig));
1885   Handle<WasmExportedFunctionData> function_data =
1886       Handle<WasmExportedFunctionData>::cast(isolate->factory()->NewStruct(
1887           WASM_EXPORTED_FUNCTION_DATA_TYPE, AllocationType::kOld));
1888   function_data->set_wrapper_code(*export_wrapper);
1889   function_data->set_instance(*instance);
1890   function_data->set_jump_table_offset(jump_table_offset);
1891   function_data->set_function_index(func_index);
1892   function_data->set_signature(*sig_foreign);
1893   function_data->set_call_count(0);
1894   function_data->set_c_wrapper_code(Smi::zero(), SKIP_WRITE_BARRIER);
1895   function_data->set_wasm_call_target(Smi::zero(), SKIP_WRITE_BARRIER);
1896   function_data->set_packed_args_size(0);
1897 
1898   MaybeHandle<String> maybe_name;
1899   bool is_asm_js_module = instance->module_object().is_asm_js();
1900   if (is_asm_js_module) {
1901     // We can use the function name only for asm.js. For WebAssembly, the
1902     // function name is specified as the function_index.toString().
1903     maybe_name = WasmModuleObject::GetFunctionNameOrNull(
1904         isolate, handle(instance->module_object(), isolate), func_index);
1905   }
1906   Handle<String> name;
1907   if (!maybe_name.ToHandle(&name)) {
1908     EmbeddedVector<char, 16> buffer;
1909     int length = SNPrintF(buffer, "%d", func_index);
1910     name = isolate->factory()
1911                ->NewStringFromOneByte(
1912                    Vector<uint8_t>::cast(buffer.SubVector(0, length)))
1913                .ToHandleChecked();
1914   }
1915   Handle<Map> function_map;
1916   switch (instance->module()->origin) {
1917     case wasm::kWasmOrigin:
1918       if (instance->module_object()
1919               .native_module()
1920               ->enabled_features()
1921               .has_gc()) {
1922         uint32_t sig_index =
1923             instance->module()->functions[func_index].sig_index;
1924         function_map = handle(
1925             Map::cast(instance->managed_object_maps().get(sig_index)), isolate);
1926       } else {
1927         function_map = isolate->wasm_exported_function_map();
1928       }
1929       break;
1930     case wasm::kAsmJsSloppyOrigin:
1931       function_map = isolate->sloppy_function_map();
1932       break;
1933     case wasm::kAsmJsStrictOrigin:
1934       function_map = isolate->strict_function_map();
1935       break;
1936   }
1937   NewFunctionArgs args =
1938       NewFunctionArgs::ForWasm(name, function_data, function_map);
1939   Handle<JSFunction> js_function = isolate->factory()->NewFunction(args);
1940   // According to the spec, exported functions should not have a [[Construct]]
1941   // method. This does not apply to functions exported from asm.js however.
1942   DCHECK_EQ(is_asm_js_module, js_function->IsConstructor());
1943   js_function->shared().set_length(arity);
1944   js_function->shared().set_internal_formal_parameter_count(arity);
1945   js_function->shared().set_script(instance->module_object().script());
1946   return Handle<WasmExportedFunction>::cast(js_function);
1947 }
1948 
GetWasmCallTarget()1949 Address WasmExportedFunction::GetWasmCallTarget() {
1950   return instance().GetCallTarget(function_index());
1951 }
1952 
sig()1953 const wasm::FunctionSig* WasmExportedFunction::sig() {
1954   return instance().module()->functions[function_index()].sig;
1955 }
1956 
MatchesSignature(const WasmModule * other_module,const wasm::FunctionSig * other_sig)1957 bool WasmExportedFunction::MatchesSignature(
1958     const WasmModule* other_module, const wasm::FunctionSig* other_sig) {
1959   const wasm::FunctionSig* sig = this->sig();
1960   if (sig->parameter_count() != other_sig->parameter_count() ||
1961       sig->return_count() != other_sig->return_count()) {
1962     return false;
1963   }
1964 
1965   for (int i = 0; i < sig->all().size(); i++) {
1966     if (!wasm::EquivalentTypes(sig->all()[i], other_sig->all()[i],
1967                                this->instance().module(), other_module)) {
1968       return false;
1969     }
1970   }
1971   return true;
1972 }
1973 
1974 // static
IsWasmJSFunction(Object object)1975 bool WasmJSFunction::IsWasmJSFunction(Object object) {
1976   if (!object.IsJSFunction()) return false;
1977   JSFunction js_function = JSFunction::cast(object);
1978   return js_function.shared().HasWasmJSFunctionData();
1979 }
1980 
New(Isolate * isolate,const wasm::FunctionSig * sig,Handle<JSReceiver> callable)1981 Handle<WasmJSFunction> WasmJSFunction::New(Isolate* isolate,
1982                                            const wasm::FunctionSig* sig,
1983                                            Handle<JSReceiver> callable) {
1984   DCHECK_LE(sig->all().size(), kMaxInt);
1985   int sig_size = static_cast<int>(sig->all().size());
1986   int return_count = static_cast<int>(sig->return_count());
1987   int parameter_count = static_cast<int>(sig->parameter_count());
1988   Handle<PodArray<wasm::ValueType>> serialized_sig =
1989       PodArray<wasm::ValueType>::New(isolate, sig_size, AllocationType::kOld);
1990   if (sig_size > 0) {
1991     serialized_sig->copy_in(0, sig->all().begin(), sig_size);
1992   }
1993   // TODO(wasm): Think about caching and sharing the JS-to-JS wrappers per
1994   // signature instead of compiling a new one for every instantiation.
1995   Handle<Code> wrapper_code =
1996       compiler::CompileJSToJSWrapper(isolate, sig, nullptr).ToHandleChecked();
1997 
1998   Handle<WasmJSFunctionData> function_data =
1999       Handle<WasmJSFunctionData>::cast(isolate->factory()->NewStruct(
2000           WASM_JS_FUNCTION_DATA_TYPE, AllocationType::kOld));
2001   function_data->set_serialized_return_count(return_count);
2002   function_data->set_serialized_parameter_count(parameter_count);
2003   function_data->set_serialized_signature(*serialized_sig);
2004   function_data->set_callable(*callable);
2005   function_data->set_wrapper_code(*wrapper_code);
2006   // Use Abort() as a default value (it will never be called if not overwritten
2007   // below).
2008   function_data->set_wasm_to_js_wrapper_code(
2009       isolate->heap()->builtin(Builtins::kAbort));
2010 
2011   if (wasm::WasmFeatures::FromIsolate(isolate).has_typed_funcref()) {
2012     using CK = compiler::WasmImportCallKind;
2013     int expected_arity = parameter_count;
2014     CK kind = compiler::kDefaultImportCallKind;
2015     if (callable->IsJSFunction()) {
2016       SharedFunctionInfo shared = Handle<JSFunction>::cast(callable)->shared();
2017       expected_arity = shared.internal_formal_parameter_count();
2018       if (expected_arity != parameter_count) {
2019         kind = CK::kJSFunctionArityMismatch;
2020       }
2021     }
2022     // TODO(wasm): Think about caching and sharing the wasm-to-JS wrappers per
2023     // signature instead of compiling a new one for every instantiation.
2024     Handle<Code> wasm_to_js_wrapper_code =
2025         compiler::CompileWasmToJSWrapper(isolate, sig, kind, expected_arity)
2026             .ToHandleChecked();
2027     function_data->set_wasm_to_js_wrapper_code(*wasm_to_js_wrapper_code);
2028   }
2029 
2030   Handle<String> name = isolate->factory()->Function_string();
2031   if (callable->IsJSFunction()) {
2032     name = JSFunction::GetName(Handle<JSFunction>::cast(callable));
2033     name = String::Flatten(isolate, name);
2034   }
2035   Handle<Map> function_map =
2036       Map::Copy(isolate, isolate->wasm_exported_function_map(),
2037                 "fresh function map for WasmJSFunction::New");
2038   NewFunctionArgs args =
2039       NewFunctionArgs::ForWasm(name, function_data, function_map);
2040   Handle<JSFunction> js_function = isolate->factory()->NewFunction(args);
2041   js_function->shared().set_internal_formal_parameter_count(parameter_count);
2042   return Handle<WasmJSFunction>::cast(js_function);
2043 }
2044 
GetCallable() const2045 JSReceiver WasmJSFunction::GetCallable() const {
2046   return shared().wasm_js_function_data().callable();
2047 }
2048 
GetSignature(Zone * zone)2049 const wasm::FunctionSig* WasmJSFunction::GetSignature(Zone* zone) {
2050   WasmJSFunctionData function_data = shared().wasm_js_function_data();
2051   int sig_size = function_data.serialized_signature().length();
2052   wasm::ValueType* types = zone->NewArray<wasm::ValueType>(sig_size);
2053   if (sig_size > 0) {
2054     function_data.serialized_signature().copy_out(0, types, sig_size);
2055   }
2056   int return_count = function_data.serialized_return_count();
2057   int parameter_count = function_data.serialized_parameter_count();
2058   return zone->New<wasm::FunctionSig>(return_count, parameter_count, types);
2059 }
2060 
2061 // TODO(9495): Update this if function type variance is introduced.
MatchesSignature(const wasm::FunctionSig * sig)2062 bool WasmJSFunction::MatchesSignature(const wasm::FunctionSig* sig) {
2063   DCHECK_LE(sig->all().size(), kMaxInt);
2064   int sig_size = static_cast<int>(sig->all().size());
2065   int return_count = static_cast<int>(sig->return_count());
2066   int parameter_count = static_cast<int>(sig->parameter_count());
2067   WasmJSFunctionData function_data = shared().wasm_js_function_data();
2068   if (return_count != function_data.serialized_return_count() ||
2069       parameter_count != function_data.serialized_parameter_count()) {
2070     return false;
2071   }
2072   if (sig_size == 0) return true;  // Prevent undefined behavior.
2073   const wasm::ValueType* expected = sig->all().begin();
2074   return function_data.serialized_signature().matches(expected, sig_size);
2075 }
2076 
GetHostCallTarget() const2077 Address WasmCapiFunction::GetHostCallTarget() const {
2078   return shared().wasm_capi_function_data().call_target();
2079 }
2080 
GetSerializedSignature() const2081 PodArray<wasm::ValueType> WasmCapiFunction::GetSerializedSignature() const {
2082   return shared().wasm_capi_function_data().serialized_signature();
2083 }
2084 
IsWasmExternalFunction(Object object)2085 bool WasmExternalFunction::IsWasmExternalFunction(Object object) {
2086   return WasmExportedFunction::IsWasmExportedFunction(object) ||
2087          WasmJSFunction::IsWasmJSFunction(object);
2088 }
2089 
New(Isolate * isolate,int index)2090 Handle<WasmExceptionTag> WasmExceptionTag::New(Isolate* isolate, int index) {
2091   Handle<WasmExceptionTag> result =
2092       Handle<WasmExceptionTag>::cast(isolate->factory()->NewStruct(
2093           WASM_EXCEPTION_TAG_TYPE, AllocationType::kOld));
2094   result->set_index(index);
2095   return result;
2096 }
2097 
New(Isolate * isolate,std::shared_ptr<wasm::NativeModule> native_module,Handle<FixedArray> export_wrappers,Handle<HeapNumber> uses_bitset)2098 Handle<AsmWasmData> AsmWasmData::New(
2099     Isolate* isolate, std::shared_ptr<wasm::NativeModule> native_module,
2100     Handle<FixedArray> export_wrappers, Handle<HeapNumber> uses_bitset) {
2101   const WasmModule* module = native_module->module();
2102   const bool kUsesLiftoff = false;
2103   size_t memory_estimate =
2104       wasm::WasmCodeManager::EstimateNativeModuleCodeSize(module,
2105                                                           kUsesLiftoff) +
2106       wasm::WasmCodeManager::EstimateNativeModuleMetaDataSize(module);
2107   Handle<Managed<wasm::NativeModule>> managed_native_module =
2108       Managed<wasm::NativeModule>::FromSharedPtr(isolate, memory_estimate,
2109                                                  std::move(native_module));
2110   Handle<AsmWasmData> result = Handle<AsmWasmData>::cast(
2111       isolate->factory()->NewStruct(ASM_WASM_DATA_TYPE, AllocationType::kOld));
2112   result->set_managed_native_module(*managed_native_module);
2113   result->set_export_wrappers(*export_wrappers);
2114   result->set_uses_bitset(*uses_bitset);
2115   return result;
2116 }
2117 
2118 namespace wasm {
2119 
TypecheckJSObject(Isolate * isolate,const WasmModule * module,Handle<Object> value,ValueType expected,const char ** error_message)2120 bool TypecheckJSObject(Isolate* isolate, const WasmModule* module,
2121                        Handle<Object> value, ValueType expected,
2122                        const char** error_message) {
2123   DCHECK(expected.is_reference_type());
2124   switch (expected.kind()) {
2125     case ValueType::kOptRef:
2126       if (value->IsNull(isolate)) return true;
2127       V8_FALLTHROUGH;
2128     case ValueType::kRef:
2129       switch (expected.heap_representation()) {
2130         case HeapType::kFunc: {
2131           if (!(WasmExternalFunction::IsWasmExternalFunction(*value) ||
2132                 WasmCapiFunction::IsWasmCapiFunction(*value))) {
2133             *error_message =
2134                 "function-typed object must be null (if nullable) or a Wasm "
2135                 "function object";
2136             return false;
2137           }
2138           return true;
2139         }
2140         case HeapType::kExtern:
2141         case HeapType::kExn:
2142           return true;
2143         case HeapType::kEq: {
2144           // TODO(7748): Change this when we have a decision on the JS API for
2145           // structs/arrays.
2146           Handle<Name> key = isolate->factory()->wasm_wrapped_object_symbol();
2147           LookupIterator it(isolate, value, key,
2148                             LookupIterator::OWN_SKIP_INTERCEPTOR);
2149           if (it.state() == LookupIterator::DATA) return true;
2150           *error_message =
2151               "eqref object must be null (if nullable) or wrapped with wasm "
2152               "object wrapper";
2153           return false;
2154         }
2155         case HeapType::kI31:
2156           // TODO(7748): Implement when the JS API for i31ref is decided on.
2157           *error_message = "Assigning JS objects to i31ref not supported yet.";
2158           return false;
2159         default:
2160           // Tables defined outside a module can't refer to user-defined types.
2161           if (module == nullptr) return false;
2162           DCHECK(module->has_type(expected.ref_index()));
2163           if (module->has_signature(expected.ref_index())) {
2164             if (WasmExportedFunction::IsWasmExportedFunction(*value)) {
2165               WasmExportedFunction function =
2166                   WasmExportedFunction::cast(*value);
2167               const WasmModule* exporting_module = function.instance().module();
2168               ValueType real_type = ValueType::Ref(
2169                   exporting_module->functions[function.function_index()]
2170                       .sig_index,
2171                   kNonNullable);
2172               if (!IsSubtypeOf(real_type, expected, exporting_module, module)) {
2173                 *error_message =
2174                     "assigned exported function has to be a subtype of the "
2175                     "expected type";
2176                 return false;
2177               }
2178               return true;
2179             }
2180 
2181             if (WasmJSFunction::IsWasmJSFunction(*value)) {
2182               // Since a WasmJSFunction cannot refer to indexed types (definable
2183               // only in a module), we do not need to use EquivalentTypes().
2184               if (!WasmJSFunction::cast(*value).MatchesSignature(
2185                       module->signature(expected.ref_index()))) {
2186                 *error_message =
2187                     "assigned WasmJSFunction has to be a subtype of the "
2188                     "expected type";
2189                 return false;
2190               }
2191               return true;
2192             }
2193 
2194             if (WasmCapiFunction::IsWasmCapiFunction(*value)) {
2195               if (!WasmCapiFunction::cast(*value).MatchesSignature(
2196                       module->signature(expected.ref_index()))) {
2197                 // Since a WasmCapiFunction cannot refer to indexed types
2198                 // (definable in a module), we don't need to invoke
2199                 // IsEquivalentType();
2200                 *error_message =
2201                     "assigned WasmCapiFunction has to be a subtype of the "
2202                     "expected type";
2203                 return false;
2204               }
2205               return true;
2206             }
2207 
2208             *error_message =
2209                 "function-typed object must be null (if nullable) or a Wasm "
2210                 "function object";
2211 
2212             return false;
2213           }
2214           // TODO(7748): Implement when the JS API for structs/arrays is decided
2215           // on.
2216           *error_message =
2217               "Assigning to struct/array globals not supported yet.";
2218           return false;
2219       }
2220     case ValueType::kRtt:
2221       // TODO(7748): Implement when the JS API for rtts is decided on.
2222       *error_message = "Assigning to rtt globals not supported yet.";
2223       return false;
2224     default:
2225       UNREACHABLE();
2226   }
2227 }
2228 
2229 }  // namespace wasm
2230 
2231 }  // namespace internal
2232 }  // namespace v8
2233 
2234 #undef TRACE_IFT
2235