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