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