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