1 // Copyright 2019 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/module-instantiate.h"
6
7 #include "src/api/api.h"
8 #include "src/asmjs/asm-js.h"
9 #include "src/logging/counters.h"
10 #include "src/logging/metrics.h"
11 #include "src/numbers/conversions-inl.h"
12 #include "src/objects/property-descriptor.h"
13 #include "src/tracing/trace-event.h"
14 #include "src/utils/utils.h"
15 #include "src/wasm/module-compiler.h"
16 #include "src/wasm/wasm-constants.h"
17 #include "src/wasm/wasm-external-refs.h"
18 #include "src/wasm/wasm-import-wrapper-cache.h"
19 #include "src/wasm/wasm-module.h"
20 #include "src/wasm/wasm-objects-inl.h"
21 #include "src/wasm/wasm-subtyping.h"
22
23 #define TRACE(...) \
24 do { \
25 if (FLAG_trace_wasm_instances) PrintF(__VA_ARGS__); \
26 } while (false)
27
28 namespace v8 {
29 namespace internal {
30 namespace wasm {
31
32 using base::ReadLittleEndianValue;
33 using base::WriteLittleEndianValue;
34
35 namespace {
raw_buffer_ptr(MaybeHandle<JSArrayBuffer> buffer,int offset)36 byte* raw_buffer_ptr(MaybeHandle<JSArrayBuffer> buffer, int offset) {
37 return static_cast<byte*>(buffer.ToHandleChecked()->backing_store()) + offset;
38 }
39
EvalUint32InitExpr(Handle<WasmInstanceObject> instance,const WasmInitExpr & expr)40 uint32_t EvalUint32InitExpr(Handle<WasmInstanceObject> instance,
41 const WasmInitExpr& expr) {
42 switch (expr.kind()) {
43 case WasmInitExpr::kI32Const:
44 return expr.immediate().i32_const;
45 case WasmInitExpr::kGlobalGet: {
46 uint32_t offset =
47 instance->module()->globals[expr.immediate().index].offset;
48 auto raw_addr = reinterpret_cast<Address>(
49 instance->untagged_globals_buffer().backing_store()) +
50 offset;
51 return ReadLittleEndianValue<uint32_t>(raw_addr);
52 }
53 default:
54 UNREACHABLE();
55 }
56 }
57
58 using ImportWrapperQueue = WrapperQueue<WasmImportWrapperCache::CacheKey,
59 WasmImportWrapperCache::CacheKeyHash>;
60
61 class CompileImportWrapperJob final : public JobTask {
62 public:
CompileImportWrapperJob(WasmEngine * engine,Counters * counters,NativeModule * native_module,ImportWrapperQueue * queue,WasmImportWrapperCache::ModificationScope * cache_scope)63 CompileImportWrapperJob(
64 WasmEngine* engine, Counters* counters, NativeModule* native_module,
65 ImportWrapperQueue* queue,
66 WasmImportWrapperCache::ModificationScope* cache_scope)
67 : engine_(engine),
68 counters_(counters),
69 native_module_(native_module),
70 queue_(queue),
71 cache_scope_(cache_scope) {}
72
GetMaxConcurrency(size_t worker_count) const73 size_t GetMaxConcurrency(size_t worker_count) const override {
74 size_t flag_limit =
75 static_cast<size_t>(std::max(1, FLAG_wasm_num_compilation_tasks));
76 // Add {worker_count} to the queue size because workers might still be
77 // processing units that have already been popped from the queue.
78 return std::min(flag_limit, worker_count + queue_->size());
79 }
80
Run(JobDelegate * delegate)81 void Run(JobDelegate* delegate) override {
82 while (base::Optional<WasmImportWrapperCache::CacheKey> key =
83 queue_->pop()) {
84 CompileImportWrapper(engine_, native_module_, counters_, key->kind,
85 key->signature, key->expected_arity, cache_scope_);
86 if (delegate->ShouldYield()) return;
87 }
88 }
89
90 private:
91 WasmEngine* const engine_;
92 Counters* const counters_;
93 NativeModule* const native_module_;
94 ImportWrapperQueue* const queue_;
95 WasmImportWrapperCache::ModificationScope* const cache_scope_;
96 };
97
98 } // namespace
99
100 // TODO(jkummerow): Move these elsewhere.
CreateStructMap(Isolate * isolate,const WasmModule * module,int struct_index,Handle<Map> rtt_parent)101 Handle<Map> CreateStructMap(Isolate* isolate, const WasmModule* module,
102 int struct_index, Handle<Map> rtt_parent) {
103 const wasm::StructType* type = module->struct_type(struct_index);
104 const int inobject_properties = 0;
105 DCHECK_LE(type->total_fields_size(), kMaxInt - WasmStruct::kHeaderSize);
106 const int instance_size =
107 WasmStruct::kHeaderSize + static_cast<int>(type->total_fields_size());
108 const InstanceType instance_type = WASM_STRUCT_TYPE;
109 // TODO(jkummerow): If NO_ELEMENTS were supported, we could use that here.
110 const ElementsKind elements_kind = TERMINAL_FAST_ELEMENTS_KIND;
111 Handle<WasmTypeInfo> type_info = isolate->factory()->NewWasmTypeInfo(
112 reinterpret_cast<Address>(type), rtt_parent);
113 Handle<Map> map = isolate->factory()->NewMap(
114 instance_type, instance_size, elements_kind, inobject_properties);
115 map->set_wasm_type_info(*type_info);
116 return map;
117 }
118
CreateArrayMap(Isolate * isolate,const WasmModule * module,int array_index,Handle<Map> rtt_parent)119 Handle<Map> CreateArrayMap(Isolate* isolate, const WasmModule* module,
120 int array_index, Handle<Map> rtt_parent) {
121 const wasm::ArrayType* type = module->array_type(array_index);
122 const int inobject_properties = 0;
123 const int instance_size = kVariableSizeSentinel;
124 const InstanceType instance_type = WASM_ARRAY_TYPE;
125 const ElementsKind elements_kind = TERMINAL_FAST_ELEMENTS_KIND;
126 Handle<WasmTypeInfo> type_info = isolate->factory()->NewWasmTypeInfo(
127 reinterpret_cast<Address>(type), rtt_parent);
128 Handle<Map> map = isolate->factory()->NewMap(
129 instance_type, instance_size, elements_kind, inobject_properties);
130 map->set_wasm_type_info(*type_info);
131 return map;
132 }
133
CreateGenericRtt(Isolate * isolate,const WasmModule * module,Handle<Map> rtt_parent)134 Handle<Map> CreateGenericRtt(Isolate* isolate, const WasmModule* module,
135 Handle<Map> rtt_parent) {
136 const int inobject_properties = 0;
137 const int instance_size = 0;
138 const InstanceType instance_type = WASM_STRUCT_TYPE; // Fake; good enough.
139 const ElementsKind elements_kind = TERMINAL_FAST_ELEMENTS_KIND;
140 Handle<WasmTypeInfo> type_info =
141 isolate->factory()->NewWasmTypeInfo(0, rtt_parent);
142 Handle<Map> map = isolate->factory()->NewMap(
143 instance_type, instance_size, elements_kind, inobject_properties);
144 map->set_wasm_type_info(*type_info);
145 return map;
146 }
147
148 namespace {
149
150 // TODO(7748): Consider storing this array in Maps'
151 // "transitions_or_prototype_info" slot.
152 // Also consider being more memory-efficient, e.g. use inline storage for
153 // single entries, and/or adapt the growth strategy.
154 class RttSubtypes : public ArrayList {
155 public:
Insert(Isolate * isolate,Handle<ArrayList> array,uint32_t type_index,Handle<Map> sub_rtt)156 static Handle<ArrayList> Insert(Isolate* isolate, Handle<ArrayList> array,
157 uint32_t type_index, Handle<Map> sub_rtt) {
158 Handle<Smi> key = handle(Smi::FromInt(type_index), isolate);
159 return Add(isolate, array, key, sub_rtt);
160 }
161
SearchSubtype(Handle<ArrayList> array,uint32_t type_index)162 static Map SearchSubtype(Handle<ArrayList> array, uint32_t type_index) {
163 // Linear search for now.
164 // TODO(7748): Consider keeping the array sorted and using binary search
165 // here, if empirical data indicates that that would be worthwhile.
166 int count = array->Length();
167 for (int i = 0; i < count; i += 2) {
168 if (Smi::cast(array->Get(i)).value() == static_cast<int>(type_index)) {
169 return Map::cast(array->Get(i + 1));
170 }
171 }
172 return {};
173 }
174 };
175
176 } // namespace
177
AllocateSubRtt(Isolate * isolate,Handle<WasmInstanceObject> instance,uint32_t type,Handle<Map> parent)178 Handle<Map> AllocateSubRtt(Isolate* isolate,
179 Handle<WasmInstanceObject> instance, uint32_t type,
180 Handle<Map> parent) {
181 // Check for an existing RTT first.
182 DCHECK(parent->IsWasmStructMap() || parent->IsWasmArrayMap());
183 Handle<ArrayList> cache(parent->wasm_type_info().subtypes(), isolate);
184 Map maybe_cached = RttSubtypes::SearchSubtype(cache, type);
185 if (!maybe_cached.is_null()) return handle(maybe_cached, isolate);
186
187 // Allocate a fresh RTT otherwise.
188 const wasm::WasmModule* module = instance->module();
189 Handle<Map> rtt;
190 if (wasm::HeapType(type).is_generic()) {
191 rtt = wasm::CreateGenericRtt(isolate, module, parent);
192 } else if (module->has_struct(type)) {
193 rtt = wasm::CreateStructMap(isolate, module, type, parent);
194 } else if (module->has_array(type)) {
195 rtt = wasm::CreateArrayMap(isolate, module, type, parent);
196 } else {
197 DCHECK(module->has_signature(type));
198 // Currently, parent rtts for functions are meaningless,
199 // since (rtt.test func rtt) iff (func.map == rtt).
200 // Therefore, we simply create a fresh function map here.
201 // TODO(7748): Canonicalize rtts to make them work for identical function
202 // types.
203 rtt = Map::Copy(isolate, isolate->wasm_exported_function_map(),
204 "fresh function map for AllocateSubRtt");
205 }
206 cache = RttSubtypes::Insert(isolate, cache, type, rtt);
207 parent->wasm_type_info().set_subtypes(*cache);
208 return rtt;
209 }
210
211 // A helper class to simplify instantiating a module from a module object.
212 // It closes over the {Isolate}, the {ErrorThrower}, etc.
213 class InstanceBuilder {
214 public:
215 InstanceBuilder(Isolate* isolate, v8::metrics::Recorder::ContextId context_id,
216 ErrorThrower* thrower, Handle<WasmModuleObject> module_object,
217 MaybeHandle<JSReceiver> ffi,
218 MaybeHandle<JSArrayBuffer> memory_buffer);
219
220 // Build an instance, in all of its glory.
221 MaybeHandle<WasmInstanceObject> Build();
222 // Run the start function, if any.
223 bool ExecuteStartFunction();
224
225 private:
226 // A pre-evaluated value to use in import binding.
227 struct SanitizedImport {
228 Handle<String> module_name;
229 Handle<String> import_name;
230 Handle<Object> value;
231 };
232
233 Isolate* isolate_;
234 v8::metrics::Recorder::ContextId context_id_;
235 const WasmFeatures enabled_;
236 const WasmModule* const module_;
237 ErrorThrower* thrower_;
238 Handle<WasmModuleObject> module_object_;
239 MaybeHandle<JSReceiver> ffi_;
240 MaybeHandle<JSArrayBuffer> memory_buffer_;
241 Handle<WasmMemoryObject> memory_object_;
242 Handle<JSArrayBuffer> untagged_globals_;
243 Handle<FixedArray> tagged_globals_;
244 std::vector<Handle<WasmExceptionObject>> exception_wrappers_;
245 Handle<WasmExportedFunction> start_function_;
246 std::vector<SanitizedImport> sanitized_imports_;
247
248 // Helper routines to print out errors with imports.
249 #define ERROR_THROWER_WITH_MESSAGE(TYPE) \
250 void Report##TYPE(const char* error, uint32_t index, \
251 Handle<String> module_name, Handle<String> import_name) { \
252 thrower_->TYPE("Import #%d module=\"%s\" function=\"%s\" error: %s", \
253 index, module_name->ToCString().get(), \
254 import_name->ToCString().get(), error); \
255 } \
256 \
257 MaybeHandle<Object> Report##TYPE(const char* error, uint32_t index, \
258 Handle<String> module_name) { \
259 thrower_->TYPE("Import #%d module=\"%s\" error: %s", index, \
260 module_name->ToCString().get(), error); \
261 return MaybeHandle<Object>(); \
262 }
263
264 ERROR_THROWER_WITH_MESSAGE(LinkError)
265 ERROR_THROWER_WITH_MESSAGE(TypeError)
266
267 #undef ERROR_THROWER_WITH_MESSAGE
268
269 // Look up an import value in the {ffi_} object.
270 MaybeHandle<Object> LookupImport(uint32_t index, Handle<String> module_name,
271 Handle<String> import_name);
272
273 // Look up an import value in the {ffi_} object specifically for linking an
274 // asm.js module. This only performs non-observable lookups, which allows
275 // falling back to JavaScript proper (and hence re-executing all lookups) if
276 // module instantiation fails.
277 MaybeHandle<Object> LookupImportAsm(uint32_t index,
278 Handle<String> import_name);
279
280 // Load data segments into the memory.
281 void LoadDataSegments(Handle<WasmInstanceObject> instance);
282
283 void WriteGlobalValue(const WasmGlobal& global, double value);
284 void WriteGlobalValue(const WasmGlobal& global, int64_t num);
285 void WriteGlobalValue(const WasmGlobal& global,
286 Handle<WasmGlobalObject> value);
287
288 void WriteGlobalExternRef(const WasmGlobal& global, Handle<Object> value);
289
290 void SanitizeImports();
291
292 // Find the imported memory if there is one.
293 bool FindImportedMemory();
294
295 // Allocate the memory.
296 bool AllocateMemory();
297
298 // Processes a single imported function.
299 bool ProcessImportedFunction(Handle<WasmInstanceObject> instance,
300 int import_index, int func_index,
301 Handle<String> module_name,
302 Handle<String> import_name,
303 Handle<Object> value);
304
305 // Initialize imported tables of type funcref.
306 bool InitializeImportedIndirectFunctionTable(
307 Handle<WasmInstanceObject> instance, int table_index, int import_index,
308 Handle<WasmTableObject> table_object);
309
310 // Process a single imported table.
311 bool ProcessImportedTable(Handle<WasmInstanceObject> instance,
312 int import_index, int table_index,
313 Handle<String> module_name,
314 Handle<String> import_name, Handle<Object> value);
315
316 // Process a single imported memory.
317 bool ProcessImportedMemory(Handle<WasmInstanceObject> instance,
318 int import_index, Handle<String> module_name,
319 Handle<String> import_name, Handle<Object> value);
320
321 // Process a single imported global.
322 bool ProcessImportedGlobal(Handle<WasmInstanceObject> instance,
323 int import_index, int global_index,
324 Handle<String> module_name,
325 Handle<String> import_name, Handle<Object> value);
326
327 // Process a single imported WasmGlobalObject.
328 bool ProcessImportedWasmGlobalObject(Handle<WasmInstanceObject> instance,
329 int import_index,
330 Handle<String> module_name,
331 Handle<String> import_name,
332 const WasmGlobal& global,
333 Handle<WasmGlobalObject> global_object);
334
335 // Compile import wrappers in parallel. The result goes into the native
336 // module's import_wrapper_cache.
337 void CompileImportWrappers(Handle<WasmInstanceObject> instance);
338
339 // Process the imports, including functions, tables, globals, and memory, in
340 // order, loading them from the {ffi_} object. Returns the number of imported
341 // functions.
342 int ProcessImports(Handle<WasmInstanceObject> instance);
343
344 template <typename T>
345 T* GetRawGlobalPtr(const WasmGlobal& global);
346
347 // Process initialization of globals.
348 void InitGlobals(Handle<WasmInstanceObject> instance);
349
350 Handle<Object> RecursivelyEvaluateGlobalInitializer(
351 const WasmInitExpr& init, Handle<WasmInstanceObject> instance);
352
353 // Process the exports, creating wrappers for functions, tables, memories,
354 // and globals.
355 void ProcessExports(Handle<WasmInstanceObject> instance);
356
357 void InitializeIndirectFunctionTables(Handle<WasmInstanceObject> instance);
358
359 void LoadTableSegments(Handle<WasmInstanceObject> instance);
360
361 // Creates new exception tags for all exceptions. Note that some tags might
362 // already exist if they were imported, those tags will be re-used.
363 void InitializeExceptions(Handle<WasmInstanceObject> instance);
364 };
365
InstantiateToInstanceObject(Isolate * isolate,ErrorThrower * thrower,Handle<WasmModuleObject> module_object,MaybeHandle<JSReceiver> imports,MaybeHandle<JSArrayBuffer> memory_buffer)366 MaybeHandle<WasmInstanceObject> InstantiateToInstanceObject(
367 Isolate* isolate, ErrorThrower* thrower,
368 Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports,
369 MaybeHandle<JSArrayBuffer> memory_buffer) {
370 v8::metrics::Recorder::ContextId context_id =
371 isolate->GetOrRegisterRecorderContextId(isolate->native_context());
372 InstanceBuilder builder(isolate, context_id, thrower, module_object, imports,
373 memory_buffer);
374 auto instance = builder.Build();
375 if (!instance.is_null() && builder.ExecuteStartFunction()) {
376 return instance;
377 }
378 DCHECK(isolate->has_pending_exception() || thrower->error());
379 return {};
380 }
381
InstanceBuilder(Isolate * isolate,v8::metrics::Recorder::ContextId context_id,ErrorThrower * thrower,Handle<WasmModuleObject> module_object,MaybeHandle<JSReceiver> ffi,MaybeHandle<JSArrayBuffer> memory_buffer)382 InstanceBuilder::InstanceBuilder(Isolate* isolate,
383 v8::metrics::Recorder::ContextId context_id,
384 ErrorThrower* thrower,
385 Handle<WasmModuleObject> module_object,
386 MaybeHandle<JSReceiver> ffi,
387 MaybeHandle<JSArrayBuffer> memory_buffer)
388 : isolate_(isolate),
389 context_id_(context_id),
390 enabled_(module_object->native_module()->enabled_features()),
391 module_(module_object->module()),
392 thrower_(thrower),
393 module_object_(module_object),
394 ffi_(ffi),
395 memory_buffer_(memory_buffer) {
396 sanitized_imports_.reserve(module_->import_table.size());
397 }
398
399 // Build an instance, in all of its glory.
Build()400 MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
401 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm.detailed"),
402 "wasm.InstanceBuilder.Build");
403 // Check that an imports argument was provided, if the module requires it.
404 // No point in continuing otherwise.
405 if (!module_->import_table.empty() && ffi_.is_null()) {
406 thrower_->TypeError(
407 "Imports argument must be present and must be an object");
408 return {};
409 }
410
411 SanitizeImports();
412 if (thrower_->error()) return {};
413
414 // From here on, we expect the build pipeline to run without exiting to JS.
415 DisallowJavascriptExecution no_js(isolate_);
416 // Record build time into correct bucket, then build instance.
417 TimedHistogramScope wasm_instantiate_module_time_scope(SELECT_WASM_COUNTER(
418 isolate_->counters(), module_->origin, wasm_instantiate, module_time));
419 v8::metrics::WasmModuleInstantiated wasm_module_instantiated;
420 base::ElapsedTimer timer;
421 timer.Start();
422 NativeModule* native_module = module_object_->native_module();
423
424 //--------------------------------------------------------------------------
425 // Set up the memory buffer and memory objects.
426 //--------------------------------------------------------------------------
427 uint32_t initial_pages = module_->initial_pages;
428 auto initial_pages_counter = SELECT_WASM_COUNTER(
429 isolate_->counters(), module_->origin, wasm, min_mem_pages_count);
430 initial_pages_counter->AddSample(initial_pages);
431 if (module_->has_maximum_pages) {
432 DCHECK_EQ(kWasmOrigin, module_->origin);
433 auto max_pages_counter =
434 isolate_->counters()->wasm_wasm_max_mem_pages_count();
435 max_pages_counter->AddSample(module_->maximum_pages);
436 }
437
438 if (is_asmjs_module(module_)) {
439 Handle<JSArrayBuffer> buffer;
440 if (memory_buffer_.ToHandle(&buffer)) {
441 // asm.js instantiation should have changed the state of the buffer.
442 CHECK(!buffer->is_detachable());
443 CHECK(buffer->is_asmjs_memory());
444 } else {
445 // Use an empty JSArrayBuffer for degenerate asm.js modules.
446 memory_buffer_ = isolate_->factory()->NewJSArrayBufferAndBackingStore(
447 0, InitializedFlag::kUninitialized);
448 if (!memory_buffer_.ToHandle(&buffer)) {
449 thrower_->RangeError("Out of memory: asm.js memory");
450 return {};
451 }
452 buffer->set_is_asmjs_memory(true);
453 buffer->set_is_detachable(false);
454 }
455
456 // The maximum number of pages isn't strictly necessary for memory
457 // objects used for asm.js, as they are never visible, but we might
458 // as well make it accurate.
459 auto maximum_pages = static_cast<uint32_t>(
460 RoundUp(buffer->byte_length(), wasm::kWasmPageSize) /
461 wasm::kWasmPageSize);
462 memory_object_ =
463 WasmMemoryObject::New(isolate_, memory_buffer_, maximum_pages);
464 } else {
465 // Actual wasm module must have either imported or created memory.
466 CHECK(memory_buffer_.is_null());
467 if (!FindImportedMemory()) {
468 if (module_->has_memory && !AllocateMemory()) {
469 DCHECK(isolate_->has_pending_exception() || thrower_->error());
470 return {};
471 }
472 }
473 }
474
475 //--------------------------------------------------------------------------
476 // Create the WebAssembly.Instance object.
477 //--------------------------------------------------------------------------
478 TRACE("New module instantiation for %p\n", native_module);
479 Handle<WasmInstanceObject> instance =
480 WasmInstanceObject::New(isolate_, module_object_);
481
482 //--------------------------------------------------------------------------
483 // Attach the memory to the instance.
484 //--------------------------------------------------------------------------
485 if (module_->has_memory) {
486 DCHECK(!memory_object_.is_null());
487 if (!instance->has_memory_object()) {
488 instance->set_memory_object(*memory_object_);
489 }
490 // Add the instance object to the list of instances for this memory.
491 WasmMemoryObject::AddInstance(isolate_, memory_object_, instance);
492
493 // Double-check the {memory} array buffer matches the instance.
494 Handle<JSArrayBuffer> memory = memory_buffer_.ToHandleChecked();
495 CHECK_EQ(instance->memory_size(), memory->byte_length());
496 CHECK_EQ(instance->memory_start(), memory->backing_store());
497 }
498
499 //--------------------------------------------------------------------------
500 // Set up the globals for the new instance.
501 //--------------------------------------------------------------------------
502 uint32_t untagged_globals_buffer_size = module_->untagged_globals_buffer_size;
503 if (untagged_globals_buffer_size > 0) {
504 MaybeHandle<JSArrayBuffer> result =
505 isolate_->factory()->NewJSArrayBufferAndBackingStore(
506 untagged_globals_buffer_size, InitializedFlag::kZeroInitialized,
507 AllocationType::kOld);
508
509 if (!result.ToHandle(&untagged_globals_)) {
510 thrower_->RangeError("Out of memory: wasm globals");
511 return {};
512 }
513
514 instance->set_untagged_globals_buffer(*untagged_globals_);
515 instance->set_globals_start(
516 reinterpret_cast<byte*>(untagged_globals_->backing_store()));
517 }
518
519 uint32_t tagged_globals_buffer_size = module_->tagged_globals_buffer_size;
520 if (tagged_globals_buffer_size > 0) {
521 tagged_globals_ = isolate_->factory()->NewFixedArray(
522 static_cast<int>(tagged_globals_buffer_size));
523 instance->set_tagged_globals_buffer(*tagged_globals_);
524 }
525
526 //--------------------------------------------------------------------------
527 // Set up the array of references to imported globals' array buffers.
528 //--------------------------------------------------------------------------
529 if (module_->num_imported_mutable_globals > 0) {
530 // TODO(binji): This allocates one slot for each mutable global, which is
531 // more than required if multiple globals are imported from the same
532 // module.
533 Handle<FixedArray> buffers_array = isolate_->factory()->NewFixedArray(
534 module_->num_imported_mutable_globals, AllocationType::kOld);
535 instance->set_imported_mutable_globals_buffers(*buffers_array);
536 }
537
538 //--------------------------------------------------------------------------
539 // Set up the exception table used for exception tag checks.
540 //--------------------------------------------------------------------------
541 int exceptions_count = static_cast<int>(module_->exceptions.size());
542 if (exceptions_count > 0) {
543 Handle<FixedArray> exception_table = isolate_->factory()->NewFixedArray(
544 exceptions_count, AllocationType::kOld);
545 instance->set_exceptions_table(*exception_table);
546 exception_wrappers_.resize(exceptions_count);
547 }
548
549 //--------------------------------------------------------------------------
550 // Set up table storage space.
551 //--------------------------------------------------------------------------
552 int table_count = static_cast<int>(module_->tables.size());
553 {
554 for (int i = 0; i < table_count; i++) {
555 const WasmTable& table = module_->tables[i];
556 if (table.initial_size > FLAG_wasm_max_table_size) {
557 thrower_->RangeError(
558 "initial table size (%u elements) is larger than implementation "
559 "limit (%u elements)",
560 table.initial_size, FLAG_wasm_max_table_size);
561 return {};
562 }
563 }
564
565 Handle<FixedArray> tables = isolate_->factory()->NewFixedArray(table_count);
566 for (int i = module_->num_imported_tables; i < table_count; i++) {
567 const WasmTable& table = module_->tables[i];
568 Handle<WasmTableObject> table_obj = WasmTableObject::New(
569 isolate_, instance, table.type, table.initial_size,
570 table.has_maximum_size, table.maximum_size, nullptr);
571 tables->set(i, *table_obj);
572 }
573 instance->set_tables(*tables);
574 }
575
576 {
577 Handle<FixedArray> tables = isolate_->factory()->NewFixedArray(table_count);
578 // Table 0 is handled specially. See {InitializeIndirectFunctionTable} for
579 // the initilization. All generated and runtime code will use this optimized
580 // shortcut in the instance. Hence it is safe to start with table 1 in the
581 // iteration below.
582 for (int i = 1; i < table_count; ++i) {
583 const WasmTable& table = module_->tables[i];
584 if (IsSubtypeOf(table.type, kWasmFuncRef, module_)) {
585 Handle<WasmIndirectFunctionTable> table_obj =
586 WasmIndirectFunctionTable::New(isolate_, table.initial_size);
587 tables->set(i, *table_obj);
588 }
589 }
590 instance->set_indirect_function_tables(*tables);
591 }
592
593 NativeModuleModificationScope native_modification_scope(native_module);
594
595 //--------------------------------------------------------------------------
596 // Process the imports for the module.
597 //--------------------------------------------------------------------------
598 int num_imported_functions = ProcessImports(instance);
599 if (num_imported_functions < 0) return {};
600 wasm_module_instantiated.imported_function_count = num_imported_functions;
601
602 //--------------------------------------------------------------------------
603 // Create maps for managed objects (GC proposal).
604 // Must happen before {InitGlobals} because globals can refer to these maps.
605 //--------------------------------------------------------------------------
606 if (enabled_.has_gc()) {
607 Handle<FixedArray> maps = isolate_->factory()->NewUninitializedFixedArray(
608 static_cast<int>(module_->type_kinds.size()));
609 // TODO(7748): Do we want a different sentinel here?
610 Handle<Map> anyref_sentinel_map = isolate_->factory()->null_map();
611 for (int map_index = 0;
612 map_index < static_cast<int>(module_->type_kinds.size());
613 map_index++) {
614 Handle<Map> map;
615 switch (module_->type_kinds[map_index]) {
616 case kWasmStructTypeCode:
617 map = CreateStructMap(isolate_, module_, map_index,
618 anyref_sentinel_map);
619 break;
620 case kWasmArrayTypeCode:
621 map =
622 CreateArrayMap(isolate_, module_, map_index, anyref_sentinel_map);
623 break;
624 case kWasmFunctionTypeCode:
625 // TODO(7748): Think about canonicalizing rtts to make them work for
626 // identical function types.
627 map = Map::Copy(isolate_, isolate_->wasm_exported_function_map(),
628 "fresh function map for function type canonical rtt "
629 "initialization");
630 break;
631 }
632 maps->set(map_index, *map);
633 }
634 instance->set_managed_object_maps(*maps);
635 }
636
637 //--------------------------------------------------------------------------
638 // Process the initialization for the module's globals.
639 //--------------------------------------------------------------------------
640 InitGlobals(instance);
641
642 //--------------------------------------------------------------------------
643 // Initialize the indirect tables.
644 //--------------------------------------------------------------------------
645 if (table_count > 0) {
646 InitializeIndirectFunctionTables(instance);
647 }
648
649 //--------------------------------------------------------------------------
650 // Initialize the exceptions table.
651 //--------------------------------------------------------------------------
652 if (exceptions_count > 0) {
653 InitializeExceptions(instance);
654 }
655
656 // The bulk memory proposal changes the MVP behavior here; the segments are
657 // written as if `memory.init` and `table.init` are executed directly, and
658 // not bounds checked ahead of time.
659 if (!enabled_.has_bulk_memory()) {
660 //--------------------------------------------------------------------------
661 // Check that indirect function table segments are within bounds.
662 //--------------------------------------------------------------------------
663 for (const WasmElemSegment& elem_segment : module_->elem_segments) {
664 if (elem_segment.status != WasmElemSegment::kStatusActive) continue;
665 DCHECK_LT(elem_segment.table_index, table_count);
666 uint32_t base = EvalUint32InitExpr(instance, elem_segment.offset);
667 // Because of imported tables, {table_size} has to come from the table
668 // object itself.
669 auto table_object = handle(WasmTableObject::cast(instance->tables().get(
670 elem_segment.table_index)),
671 isolate_);
672 uint32_t table_size = table_object->current_length();
673 if (!base::IsInBounds<uint32_t>(
674 base, static_cast<uint32_t>(elem_segment.entries.size()),
675 table_size)) {
676 thrower_->LinkError("table initializer is out of bounds");
677 return {};
678 }
679 }
680
681 //--------------------------------------------------------------------------
682 // Check that memory segments are within bounds.
683 //--------------------------------------------------------------------------
684 for (const WasmDataSegment& seg : module_->data_segments) {
685 if (!seg.active) continue;
686 uint32_t base = EvalUint32InitExpr(instance, seg.dest_addr);
687 if (!base::IsInBounds<uint64_t>(base, seg.source.length(),
688 instance->memory_size())) {
689 thrower_->LinkError("data segment is out of bounds");
690 return {};
691 }
692 }
693 }
694
695 //--------------------------------------------------------------------------
696 // Set up the exports object for the new instance.
697 //--------------------------------------------------------------------------
698 ProcessExports(instance);
699 if (thrower_->error()) return {};
700
701 //--------------------------------------------------------------------------
702 // Initialize the indirect function tables.
703 //--------------------------------------------------------------------------
704 if (table_count > 0) {
705 LoadTableSegments(instance);
706 if (thrower_->error()) return {};
707 }
708
709 //--------------------------------------------------------------------------
710 // Initialize the memory by loading data segments.
711 //--------------------------------------------------------------------------
712 if (module_->data_segments.size() > 0) {
713 LoadDataSegments(instance);
714 if (thrower_->error()) return {};
715 }
716
717 //--------------------------------------------------------------------------
718 // Create a wrapper for the start function.
719 //--------------------------------------------------------------------------
720 if (module_->start_function_index >= 0) {
721 int start_index = module_->start_function_index;
722 auto& function = module_->functions[start_index];
723 Handle<Code> wrapper_code =
724 JSToWasmWrapperCompilationUnit::CompileJSToWasmWrapper(
725 isolate_, function.sig, module_, function.imported);
726 // TODO(clemensb): Don't generate an exported function for the start
727 // function. Use CWasmEntry instead.
728 start_function_ = WasmExportedFunction::New(
729 isolate_, instance, start_index,
730 static_cast<int>(function.sig->parameter_count()), wrapper_code);
731
732 if (function.imported) {
733 ImportedFunctionEntry entry(instance, module_->start_function_index);
734 Object callable = entry.maybe_callable();
735 if (callable.IsJSFunction()) {
736 // If the start function was imported and calls into Blink, we have
737 // to pretend that the V8 API was used to enter its correct context.
738 // To get that context to {ExecuteStartFunction} below, we install it
739 // as the context of the wrapper we just compiled. That's a bit of a
740 // hack because it's not really the wrapper's context, only its wrapped
741 // target's context, but the end result is the same, and since the
742 // start function wrapper doesn't leak, neither does this
743 // implementation detail.
744 start_function_->set_context(JSFunction::cast(callable).context());
745 }
746 }
747 }
748
749 DCHECK(!isolate_->has_pending_exception());
750 TRACE("Successfully built instance for module %p\n",
751 module_object_->native_module());
752 wasm_module_instantiated.success = true;
753 wasm_module_instantiated.wall_clock_duration_in_us =
754 timer.Elapsed().InMicroseconds();
755 timer.Stop();
756 isolate_->metrics_recorder()->DelayMainThreadEvent(wasm_module_instantiated,
757 context_id_);
758 return instance;
759 }
760
ExecuteStartFunction()761 bool InstanceBuilder::ExecuteStartFunction() {
762 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm.detailed"),
763 "wasm.ExecuteStartFunction");
764 if (start_function_.is_null()) return true; // No start function.
765
766 HandleScope scope(isolate_);
767 // In case the start function calls out to Blink, we have to make sure that
768 // the correct "entered context" is available. This is the equivalent of
769 // v8::Context::Enter() and must happen in addition to the function call
770 // sequence doing the compiled version of "isolate->set_context(...)".
771 HandleScopeImplementer* hsi = isolate_->handle_scope_implementer();
772 hsi->EnterContext(start_function_->context());
773
774 // Call the JS function.
775 Handle<Object> undefined = isolate_->factory()->undefined_value();
776 MaybeHandle<Object> retval =
777 Execution::Call(isolate_, start_function_, undefined, 0, nullptr);
778 hsi->LeaveContext();
779
780 if (retval.is_null()) {
781 DCHECK(isolate_->has_pending_exception());
782 return false;
783 }
784 return true;
785 }
786
787 // Look up an import value in the {ffi_} object.
LookupImport(uint32_t index,Handle<String> module_name,Handle<String> import_name)788 MaybeHandle<Object> InstanceBuilder::LookupImport(uint32_t index,
789 Handle<String> module_name,
790 Handle<String> import_name) {
791 // We pre-validated in the js-api layer that the ffi object is present, and
792 // a JSObject, if the module has imports.
793 DCHECK(!ffi_.is_null());
794 // Look up the module first.
795 MaybeHandle<Object> result = Object::GetPropertyOrElement(
796 isolate_, ffi_.ToHandleChecked(), module_name);
797 if (result.is_null()) {
798 return ReportTypeError("module not found", index, module_name);
799 }
800
801 Handle<Object> module = result.ToHandleChecked();
802
803 // Look up the value in the module.
804 if (!module->IsJSReceiver()) {
805 return ReportTypeError("module is not an object or function", index,
806 module_name);
807 }
808
809 result = Object::GetPropertyOrElement(isolate_, module, import_name);
810 if (result.is_null()) {
811 ReportLinkError("import not found", index, module_name, import_name);
812 return MaybeHandle<JSFunction>();
813 }
814
815 return result;
816 }
817
818 // Look up an import value in the {ffi_} object specifically for linking an
819 // asm.js module. This only performs non-observable lookups, which allows
820 // falling back to JavaScript proper (and hence re-executing all lookups) if
821 // module instantiation fails.
LookupImportAsm(uint32_t index,Handle<String> import_name)822 MaybeHandle<Object> InstanceBuilder::LookupImportAsm(
823 uint32_t index, Handle<String> import_name) {
824 // Check that a foreign function interface object was provided.
825 if (ffi_.is_null()) {
826 return ReportLinkError("missing imports object", index, import_name);
827 }
828
829 // Perform lookup of the given {import_name} without causing any observable
830 // side-effect. We only accept accesses that resolve to data properties,
831 // which is indicated by the asm.js spec in section 7 ("Linking") as well.
832 Handle<Object> result;
833 LookupIterator::Key key(isolate_, Handle<Name>::cast(import_name));
834 LookupIterator it(isolate_, ffi_.ToHandleChecked(), key);
835 switch (it.state()) {
836 case LookupIterator::ACCESS_CHECK:
837 case LookupIterator::INTEGER_INDEXED_EXOTIC:
838 case LookupIterator::INTERCEPTOR:
839 case LookupIterator::JSPROXY:
840 case LookupIterator::ACCESSOR:
841 case LookupIterator::TRANSITION:
842 return ReportLinkError("not a data property", index, import_name);
843 case LookupIterator::NOT_FOUND:
844 // Accepting missing properties as undefined does not cause any
845 // observable difference from JavaScript semantics, we are lenient.
846 result = isolate_->factory()->undefined_value();
847 break;
848 case LookupIterator::DATA:
849 result = it.GetDataValue();
850 break;
851 }
852
853 return result;
854 }
855
856 // Load data segments into the memory.
LoadDataSegments(Handle<WasmInstanceObject> instance)857 void InstanceBuilder::LoadDataSegments(Handle<WasmInstanceObject> instance) {
858 Vector<const uint8_t> wire_bytes =
859 module_object_->native_module()->wire_bytes();
860 for (const WasmDataSegment& segment : module_->data_segments) {
861 uint32_t size = segment.source.length();
862
863 if (enabled_.has_bulk_memory()) {
864 // Passive segments are not copied during instantiation.
865 if (!segment.active) continue;
866
867 uint32_t dest_offset = EvalUint32InitExpr(instance, segment.dest_addr);
868 bool ok = base::ClampToBounds(
869 dest_offset, &size, static_cast<uint32_t>(instance->memory_size()));
870 if (!ok) {
871 thrower_->RuntimeError("data segment is out of bounds");
872 return;
873 }
874 // No need to copy empty segments.
875 if (size == 0) continue;
876 std::memcpy(instance->memory_start() + dest_offset,
877 wire_bytes.begin() + segment.source.offset(), size);
878 } else {
879 DCHECK(segment.active);
880 // Segments of size == 0 are just nops.
881 if (size == 0) continue;
882
883 uint32_t dest_offset = EvalUint32InitExpr(instance, segment.dest_addr);
884 DCHECK(base::IsInBounds<uint64_t>(dest_offset, size,
885 instance->memory_size()));
886 byte* dest = instance->memory_start() + dest_offset;
887 const byte* src = wire_bytes.begin() + segment.source.offset();
888 memcpy(dest, src, size);
889 }
890 }
891 }
892
WriteGlobalValue(const WasmGlobal & global,double num)893 void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global, double num) {
894 TRACE("init [globals_start=%p + %u] = %lf, type = %s\n",
895 raw_buffer_ptr(untagged_globals_, 0), global.offset, num,
896 global.type.name().c_str());
897 switch (global.type.kind()) {
898 case ValueType::kI32:
899 WriteLittleEndianValue<int32_t>(GetRawGlobalPtr<int32_t>(global),
900 DoubleToInt32(num));
901 break;
902 case ValueType::kI64:
903 // The Wasm-BigInt proposal currently says that i64 globals may
904 // only be initialized with BigInts. See:
905 // https://github.com/WebAssembly/JS-BigInt-integration/issues/12
906 UNREACHABLE();
907 case ValueType::kF32:
908 WriteLittleEndianValue<float>(GetRawGlobalPtr<float>(global),
909 DoubleToFloat32(num));
910 break;
911 case ValueType::kF64:
912 WriteLittleEndianValue<double>(GetRawGlobalPtr<double>(global), num);
913 break;
914 default:
915 UNREACHABLE();
916 }
917 }
918
WriteGlobalValue(const WasmGlobal & global,int64_t num)919 void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global, int64_t num) {
920 TRACE("init [globals_start=%p + %u] = %" PRId64 ", type = %s\n",
921 raw_buffer_ptr(untagged_globals_, 0), global.offset, num,
922 global.type.name().c_str());
923 DCHECK_EQ(kWasmI64, global.type);
924 WriteLittleEndianValue<int64_t>(GetRawGlobalPtr<int64_t>(global), num);
925 }
926
WriteGlobalValue(const WasmGlobal & global,Handle<WasmGlobalObject> value)927 void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global,
928 Handle<WasmGlobalObject> value) {
929 TRACE("init [globals_start=%p + %u] = ", raw_buffer_ptr(untagged_globals_, 0),
930 global.offset);
931 switch (global.type.kind()) {
932 case ValueType::kI32: {
933 int32_t num = value->GetI32();
934 WriteLittleEndianValue<int32_t>(GetRawGlobalPtr<int32_t>(global), num);
935 TRACE("%d", num);
936 break;
937 }
938 case ValueType::kI64: {
939 int64_t num = value->GetI64();
940 WriteLittleEndianValue<int64_t>(GetRawGlobalPtr<int64_t>(global), num);
941 TRACE("%" PRId64, num);
942 break;
943 }
944 case ValueType::kF32: {
945 float num = value->GetF32();
946 WriteLittleEndianValue<float>(GetRawGlobalPtr<float>(global), num);
947 TRACE("%f", num);
948 break;
949 }
950 case ValueType::kF64: {
951 double num = value->GetF64();
952 WriteLittleEndianValue<double>(GetRawGlobalPtr<double>(global), num);
953 TRACE("%lf", num);
954 break;
955 }
956 case ValueType::kRtt:
957 case ValueType::kRef:
958 case ValueType::kOptRef: {
959 tagged_globals_->set(global.offset, *value->GetRef());
960 break;
961 }
962 case ValueType::kStmt:
963 case ValueType::kS128:
964 case ValueType::kBottom:
965 case ValueType::kI8:
966 case ValueType::kI16:
967 UNREACHABLE();
968 }
969 TRACE(", type = %s (from WebAssembly.Global)\n", global.type.name().c_str());
970 }
971
WriteGlobalExternRef(const WasmGlobal & global,Handle<Object> value)972 void InstanceBuilder::WriteGlobalExternRef(const WasmGlobal& global,
973 Handle<Object> value) {
974 tagged_globals_->set(global.offset, *value, UPDATE_WRITE_BARRIER);
975 }
976
SanitizeImports()977 void InstanceBuilder::SanitizeImports() {
978 Vector<const uint8_t> wire_bytes =
979 module_object_->native_module()->wire_bytes();
980 for (size_t index = 0; index < module_->import_table.size(); ++index) {
981 const WasmImport& import = module_->import_table[index];
982
983 Handle<String> module_name =
984 WasmModuleObject::ExtractUtf8StringFromModuleBytes(
985 isolate_, wire_bytes, import.module_name, kInternalize);
986
987 Handle<String> import_name =
988 WasmModuleObject::ExtractUtf8StringFromModuleBytes(
989 isolate_, wire_bytes, import.field_name, kInternalize);
990
991 int int_index = static_cast<int>(index);
992 MaybeHandle<Object> result =
993 is_asmjs_module(module_)
994 ? LookupImportAsm(int_index, import_name)
995 : LookupImport(int_index, module_name, import_name);
996 if (thrower_->error()) {
997 thrower_->LinkError("Could not find value for import %zu", index);
998 return;
999 }
1000 Handle<Object> value = result.ToHandleChecked();
1001 sanitized_imports_.push_back({module_name, import_name, value});
1002 }
1003 }
1004
FindImportedMemory()1005 bool InstanceBuilder::FindImportedMemory() {
1006 DCHECK_EQ(module_->import_table.size(), sanitized_imports_.size());
1007 for (size_t index = 0; index < module_->import_table.size(); index++) {
1008 WasmImport import = module_->import_table[index];
1009
1010 if (import.kind == kExternalMemory) {
1011 auto& value = sanitized_imports_[index].value;
1012 if (!value->IsWasmMemoryObject()) return false;
1013 memory_object_ = Handle<WasmMemoryObject>::cast(value);
1014 memory_buffer_ =
1015 Handle<JSArrayBuffer>(memory_object_->array_buffer(), isolate_);
1016 return true;
1017 }
1018 }
1019 return false;
1020 }
1021
ProcessImportedFunction(Handle<WasmInstanceObject> instance,int import_index,int func_index,Handle<String> module_name,Handle<String> import_name,Handle<Object> value)1022 bool InstanceBuilder::ProcessImportedFunction(
1023 Handle<WasmInstanceObject> instance, int import_index, int func_index,
1024 Handle<String> module_name, Handle<String> import_name,
1025 Handle<Object> value) {
1026 // Function imports must be callable.
1027 if (!value->IsCallable()) {
1028 ReportLinkError("function import requires a callable", import_index,
1029 module_name, import_name);
1030 return false;
1031 }
1032 // Store any {WasmExternalFunction} callable in the instance before the call
1033 // is resolved to preserve its identity. This handles exported functions as
1034 // well as functions constructed via other means (e.g. WebAssembly.Function).
1035 if (WasmExternalFunction::IsWasmExternalFunction(*value)) {
1036 WasmInstanceObject::SetWasmExternalFunction(
1037 isolate_, instance, func_index,
1038 Handle<WasmExternalFunction>::cast(value));
1039 }
1040 auto js_receiver = Handle<JSReceiver>::cast(value);
1041 const FunctionSig* expected_sig = module_->functions[func_index].sig;
1042 auto resolved = compiler::ResolveWasmImportCall(js_receiver, expected_sig,
1043 module_, enabled_);
1044 compiler::WasmImportCallKind kind = resolved.first;
1045 js_receiver = resolved.second;
1046 switch (kind) {
1047 case compiler::WasmImportCallKind::kLinkError:
1048 ReportLinkError("imported function does not match the expected type",
1049 import_index, module_name, import_name);
1050 return false;
1051 case compiler::WasmImportCallKind::kWasmToWasm: {
1052 // The imported function is a Wasm function from another instance.
1053 auto imported_function = Handle<WasmExportedFunction>::cast(js_receiver);
1054 Handle<WasmInstanceObject> imported_instance(
1055 imported_function->instance(), isolate_);
1056 // The import reference is the instance object itself.
1057 Address imported_target = imported_function->GetWasmCallTarget();
1058 ImportedFunctionEntry entry(instance, func_index);
1059 entry.SetWasmToWasm(*imported_instance, imported_target);
1060 break;
1061 }
1062 case compiler::WasmImportCallKind::kWasmToCapi: {
1063 NativeModule* native_module = instance->module_object().native_module();
1064 Address host_address =
1065 WasmCapiFunction::cast(*js_receiver).GetHostCallTarget();
1066 WasmCodeRefScope code_ref_scope;
1067 WasmCode* wasm_code = compiler::CompileWasmCapiCallWrapper(
1068 isolate_->wasm_engine(), native_module, expected_sig, host_address);
1069 isolate_->counters()->wasm_generated_code_size()->Increment(
1070 wasm_code->instructions().length());
1071 isolate_->counters()->wasm_reloc_size()->Increment(
1072 wasm_code->reloc_info().length());
1073
1074 ImportedFunctionEntry entry(instance, func_index);
1075 // We re-use the SetWasmToJs infrastructure because it passes the
1076 // callable to the wrapper, which we need to get the function data.
1077 entry.SetWasmToJs(isolate_, js_receiver, wasm_code);
1078 break;
1079 }
1080 default: {
1081 // The imported function is a callable.
1082
1083 int expected_arity = static_cast<int>(expected_sig->parameter_count());
1084 if (kind == compiler::WasmImportCallKind::kJSFunctionArityMismatch) {
1085 Handle<JSFunction> function = Handle<JSFunction>::cast(js_receiver);
1086 SharedFunctionInfo shared = function->shared();
1087 expected_arity = shared.internal_formal_parameter_count();
1088 }
1089
1090 NativeModule* native_module = instance->module_object().native_module();
1091 WasmCode* wasm_code = native_module->import_wrapper_cache()->Get(
1092 kind, expected_sig, expected_arity);
1093 DCHECK_NOT_NULL(wasm_code);
1094 ImportedFunctionEntry entry(instance, func_index);
1095 if (wasm_code->kind() == WasmCode::kWasmToJsWrapper) {
1096 // Wasm to JS wrappers are treated specially in the import table.
1097 entry.SetWasmToJs(isolate_, js_receiver, wasm_code);
1098 } else {
1099 // Wasm math intrinsics are compiled as regular Wasm functions.
1100 DCHECK(kind >= compiler::WasmImportCallKind::kFirstMathIntrinsic &&
1101 kind <= compiler::WasmImportCallKind::kLastMathIntrinsic);
1102 entry.SetWasmToWasm(*instance, wasm_code->instruction_start());
1103 }
1104 break;
1105 }
1106 }
1107 return true;
1108 }
1109
InitializeImportedIndirectFunctionTable(Handle<WasmInstanceObject> instance,int table_index,int import_index,Handle<WasmTableObject> table_object)1110 bool InstanceBuilder::InitializeImportedIndirectFunctionTable(
1111 Handle<WasmInstanceObject> instance, int table_index, int import_index,
1112 Handle<WasmTableObject> table_object) {
1113 int imported_table_size = table_object->current_length();
1114 // Allocate a new dispatch table.
1115 WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize(
1116 instance, table_index, imported_table_size);
1117 // Initialize the dispatch table with the (foreign) JS functions
1118 // that are already in the table.
1119 for (int i = 0; i < imported_table_size; ++i) {
1120 bool is_valid;
1121 bool is_null;
1122 MaybeHandle<WasmInstanceObject> maybe_target_instance;
1123 int function_index;
1124 MaybeHandle<WasmJSFunction> maybe_js_function;
1125 WasmTableObject::GetFunctionTableEntry(
1126 isolate_, module_, table_object, i, &is_valid, &is_null,
1127 &maybe_target_instance, &function_index, &maybe_js_function);
1128 if (!is_valid) {
1129 thrower_->LinkError("table import %d[%d] is not a wasm function",
1130 import_index, i);
1131 return false;
1132 }
1133 if (is_null) continue;
1134 Handle<WasmJSFunction> js_function;
1135 if (maybe_js_function.ToHandle(&js_function)) {
1136 WasmInstanceObject::ImportWasmJSFunctionIntoTable(
1137 isolate_, instance, table_index, i, js_function);
1138 continue;
1139 }
1140
1141 Handle<WasmInstanceObject> target_instance =
1142 maybe_target_instance.ToHandleChecked();
1143 const FunctionSig* sig = target_instance->module_object()
1144 .module()
1145 ->functions[function_index]
1146 .sig;
1147
1148 // Look up the signature's canonical id. If there is no canonical
1149 // id, then the signature does not appear at all in this module,
1150 // so putting {-1} in the table will cause checks to always fail.
1151 IndirectFunctionTableEntry(instance, table_index, i)
1152 .Set(module_->signature_map.Find(*sig), target_instance,
1153 function_index);
1154 }
1155 return true;
1156 }
1157
ProcessImportedTable(Handle<WasmInstanceObject> instance,int import_index,int table_index,Handle<String> module_name,Handle<String> import_name,Handle<Object> value)1158 bool InstanceBuilder::ProcessImportedTable(Handle<WasmInstanceObject> instance,
1159 int import_index, int table_index,
1160 Handle<String> module_name,
1161 Handle<String> import_name,
1162 Handle<Object> value) {
1163 if (!value->IsWasmTableObject()) {
1164 ReportLinkError("table import requires a WebAssembly.Table", import_index,
1165 module_name, import_name);
1166 return false;
1167 }
1168 const WasmTable& table = module_->tables[table_index];
1169
1170 auto table_object = Handle<WasmTableObject>::cast(value);
1171
1172 uint32_t imported_table_size =
1173 static_cast<uint32_t>(table_object->current_length());
1174 if (imported_table_size < table.initial_size) {
1175 thrower_->LinkError("table import %d is smaller than initial %u, got %u",
1176 import_index, table.initial_size, imported_table_size);
1177 return false;
1178 }
1179
1180 if (table.has_maximum_size) {
1181 if (table_object->maximum_length().IsUndefined(isolate_)) {
1182 thrower_->LinkError("table import %d has no maximum length, expected %u",
1183 import_index, table.maximum_size);
1184 return false;
1185 }
1186 int64_t imported_maximum_size = table_object->maximum_length().Number();
1187 if (imported_maximum_size < 0) {
1188 thrower_->LinkError("table import %d has no maximum length, expected %u",
1189 import_index, table.maximum_size);
1190 return false;
1191 }
1192 if (imported_maximum_size > table.maximum_size) {
1193 thrower_->LinkError("table import %d has a larger maximum size %" PRIx64
1194 " than the module's declared maximum %u",
1195 import_index, imported_maximum_size,
1196 table.maximum_size);
1197 return false;
1198 }
1199 }
1200
1201 const WasmModule* table_type_module =
1202 !table_object->instance().IsUndefined()
1203 ? WasmInstanceObject::cast(table_object->instance()).module()
1204 : instance->module();
1205
1206 if (!EquivalentTypes(table.type, table_object->type(), module_,
1207 table_type_module)) {
1208 ReportLinkError("imported table does not match the expected type",
1209 import_index, module_name, import_name);
1210 return false;
1211 }
1212
1213 if (IsSubtypeOf(table.type, kWasmFuncRef, module_) &&
1214 !InitializeImportedIndirectFunctionTable(instance, table_index,
1215 import_index, table_object)) {
1216 return false;
1217 }
1218
1219 instance->tables().set(table_index, *value);
1220 return true;
1221 }
1222
ProcessImportedMemory(Handle<WasmInstanceObject> instance,int import_index,Handle<String> module_name,Handle<String> import_name,Handle<Object> value)1223 bool InstanceBuilder::ProcessImportedMemory(Handle<WasmInstanceObject> instance,
1224 int import_index,
1225 Handle<String> module_name,
1226 Handle<String> import_name,
1227 Handle<Object> value) {
1228 if (!value->IsWasmMemoryObject()) {
1229 ReportLinkError("memory import must be a WebAssembly.Memory object",
1230 import_index, module_name, import_name);
1231 return false;
1232 }
1233 auto memory_object = Handle<WasmMemoryObject>::cast(value);
1234
1235 // The imported memory should have been already set up early.
1236 CHECK_EQ(instance->memory_object(), *memory_object);
1237
1238 Handle<JSArrayBuffer> buffer(memory_object_->array_buffer(), isolate_);
1239 // memory_ should have already been assigned in Build().
1240 DCHECK_EQ(*memory_buffer_.ToHandleChecked(), *buffer);
1241 uint32_t imported_cur_pages =
1242 static_cast<uint32_t>(buffer->byte_length() / kWasmPageSize);
1243 if (imported_cur_pages < module_->initial_pages) {
1244 thrower_->LinkError("memory import %d is smaller than initial %u, got %u",
1245 import_index, module_->initial_pages,
1246 imported_cur_pages);
1247 return false;
1248 }
1249 int32_t imported_maximum_pages = memory_object_->maximum_pages();
1250 if (module_->has_maximum_pages) {
1251 if (imported_maximum_pages < 0) {
1252 thrower_->LinkError(
1253 "memory import %d has no maximum limit, expected at most %u",
1254 import_index, imported_maximum_pages);
1255 return false;
1256 }
1257 if (static_cast<uint32_t>(imported_maximum_pages) >
1258 module_->maximum_pages) {
1259 thrower_->LinkError(
1260 "memory import %d has a larger maximum size %u than the "
1261 "module's declared maximum %u",
1262 import_index, imported_maximum_pages, module_->maximum_pages);
1263 return false;
1264 }
1265 }
1266 if (module_->has_shared_memory != buffer->is_shared()) {
1267 thrower_->LinkError(
1268 "mismatch in shared state of memory, declared = %d, imported = %d",
1269 module_->has_shared_memory, buffer->is_shared());
1270 return false;
1271 }
1272
1273 return true;
1274 }
1275
ProcessImportedWasmGlobalObject(Handle<WasmInstanceObject> instance,int import_index,Handle<String> module_name,Handle<String> import_name,const WasmGlobal & global,Handle<WasmGlobalObject> global_object)1276 bool InstanceBuilder::ProcessImportedWasmGlobalObject(
1277 Handle<WasmInstanceObject> instance, int import_index,
1278 Handle<String> module_name, Handle<String> import_name,
1279 const WasmGlobal& global, Handle<WasmGlobalObject> global_object) {
1280 if (static_cast<bool>(global_object->is_mutable()) != global.mutability) {
1281 ReportLinkError("imported global does not match the expected mutability",
1282 import_index, module_name, import_name);
1283 return false;
1284 }
1285
1286 const WasmModule* global_type_module =
1287 !global_object->instance().IsUndefined()
1288 ? WasmInstanceObject::cast(global_object->instance()).module()
1289 : instance->module();
1290
1291 bool valid_type =
1292 global.mutability
1293 ? EquivalentTypes(global_object->type(), global.type,
1294 global_type_module, instance->module())
1295 : IsSubtypeOf(global_object->type(), global.type, global_type_module,
1296 instance->module());
1297
1298 if (!valid_type) {
1299 ReportLinkError("imported global does not match the expected type",
1300 import_index, module_name, import_name);
1301 return false;
1302 }
1303 if (global.mutability) {
1304 DCHECK_LT(global.index, module_->num_imported_mutable_globals);
1305 Handle<Object> buffer;
1306 Address address_or_offset;
1307 if (global.type.is_reference_type()) {
1308 static_assert(sizeof(global_object->offset()) <= sizeof(Address),
1309 "The offset into the globals buffer does not fit into "
1310 "the imported_mutable_globals array");
1311 buffer = handle(global_object->tagged_buffer(), isolate_);
1312 // For externref globals we use a relative offset, not an absolute
1313 // address.
1314 address_or_offset = static_cast<Address>(global_object->offset());
1315 } else {
1316 buffer = handle(global_object->untagged_buffer(), isolate_);
1317 // It is safe in this case to store the raw pointer to the buffer
1318 // since the backing store of the JSArrayBuffer will not be
1319 // relocated.
1320 address_or_offset = reinterpret_cast<Address>(raw_buffer_ptr(
1321 Handle<JSArrayBuffer>::cast(buffer), global_object->offset()));
1322 }
1323 instance->imported_mutable_globals_buffers().set(global.index, *buffer);
1324 instance->imported_mutable_globals()[global.index] = address_or_offset;
1325 return true;
1326 }
1327
1328 WriteGlobalValue(global, global_object);
1329 return true;
1330 }
1331
ProcessImportedGlobal(Handle<WasmInstanceObject> instance,int import_index,int global_index,Handle<String> module_name,Handle<String> import_name,Handle<Object> value)1332 bool InstanceBuilder::ProcessImportedGlobal(Handle<WasmInstanceObject> instance,
1333 int import_index, int global_index,
1334 Handle<String> module_name,
1335 Handle<String> import_name,
1336 Handle<Object> value) {
1337 // Immutable global imports are converted to numbers and written into
1338 // the {untagged_globals_} array buffer.
1339 //
1340 // Mutable global imports instead have their backing array buffers
1341 // referenced by this instance, and store the address of the imported
1342 // global in the {imported_mutable_globals_} array.
1343 const WasmGlobal& global = module_->globals[global_index];
1344
1345 // The mutable-global proposal allows importing i64 values, but only if
1346 // they are passed as a WebAssembly.Global object.
1347 //
1348 // However, the bigint proposal allows importing constant i64 values,
1349 // as non WebAssembly.Global object.
1350 if (global.type == kWasmI64 && !enabled_.has_bigint() &&
1351 !value->IsWasmGlobalObject()) {
1352 ReportLinkError("global import cannot have type i64", import_index,
1353 module_name, import_name);
1354 return false;
1355 }
1356
1357 // SIMD proposal allows modules to define an imported v128 global, and only
1358 // supports importing a WebAssembly.Global object for this global, but also
1359 // defines constructing a WebAssembly.Global of v128 to be a TypeError.
1360 // We *should* never hit this case in the JS API, but the module should should
1361 // be allowed to declare such a global (no validation error).
1362 if (global.type == kWasmS128 && !value->IsWasmGlobalObject()) {
1363 ReportLinkError("global import of type v128 must be a WebAssembly.Global",
1364 import_index, module_name, import_name);
1365 return false;
1366 }
1367
1368 if (is_asmjs_module(module_)) {
1369 // Accepting {JSFunction} on top of just primitive values here is a
1370 // workaround to support legacy asm.js code with broken binding. Note
1371 // that using {NaN} (or Smi::zero()) here is what using the observable
1372 // conversion via {ToPrimitive} would produce as well.
1373 // TODO(wasm): Still observable if Function.prototype.valueOf or friends
1374 // are patched, we might need to check for that as well.
1375 if (value->IsJSFunction()) value = isolate_->factory()->nan_value();
1376 if (value->IsPrimitive() && !value->IsSymbol()) {
1377 if (global.type == kWasmI32) {
1378 value = Object::ToInt32(isolate_, value).ToHandleChecked();
1379 } else {
1380 value = Object::ToNumber(isolate_, value).ToHandleChecked();
1381 }
1382 }
1383 }
1384
1385 if (value->IsWasmGlobalObject()) {
1386 auto global_object = Handle<WasmGlobalObject>::cast(value);
1387 return ProcessImportedWasmGlobalObject(instance, import_index, module_name,
1388 import_name, global, global_object);
1389 }
1390
1391 if (global.mutability) {
1392 ReportLinkError(
1393 "imported mutable global must be a WebAssembly.Global object",
1394 import_index, module_name, import_name);
1395 return false;
1396 }
1397
1398 if (global.type.is_reference_type()) {
1399 const char* error_message;
1400 if (!wasm::TypecheckJSObject(isolate_, module_, value, global.type,
1401 &error_message)) {
1402 ReportLinkError(error_message, global_index, module_name, import_name);
1403 return false;
1404 }
1405 WriteGlobalExternRef(global, value);
1406 return true;
1407 }
1408
1409 if (value->IsNumber() && global.type != kWasmI64) {
1410 WriteGlobalValue(global, value->Number());
1411 return true;
1412 }
1413
1414 if (enabled_.has_bigint() && global.type == kWasmI64 && value->IsBigInt()) {
1415 WriteGlobalValue(global, BigInt::cast(*value).AsInt64());
1416 return true;
1417 }
1418
1419 ReportLinkError(
1420 "global import must be a number, valid Wasm reference, or "
1421 "WebAssembly.Global object",
1422 import_index, module_name, import_name);
1423 return false;
1424 }
1425
CompileImportWrappers(Handle<WasmInstanceObject> instance)1426 void InstanceBuilder::CompileImportWrappers(
1427 Handle<WasmInstanceObject> instance) {
1428 int num_imports = static_cast<int>(module_->import_table.size());
1429 NativeModule* native_module = instance->module_object().native_module();
1430 WasmImportWrapperCache::ModificationScope cache_scope(
1431 native_module->import_wrapper_cache());
1432
1433 // Compilation is done in two steps:
1434 // 1) Insert nullptr entries in the cache for wrappers that need to be
1435 // compiled. 2) Compile wrappers in background tasks using the
1436 // ImportWrapperQueue. This way the cache won't invalidate other iterators
1437 // when inserting a new WasmCode, since the key will already be there.
1438 ImportWrapperQueue import_wrapper_queue;
1439 for (int index = 0; index < num_imports; ++index) {
1440 Handle<Object> value = sanitized_imports_[index].value;
1441 if (module_->import_table[index].kind != kExternalFunction ||
1442 !value->IsCallable()) {
1443 continue;
1444 }
1445 auto js_receiver = Handle<JSReceiver>::cast(value);
1446 uint32_t func_index = module_->import_table[index].index;
1447 const FunctionSig* sig = module_->functions[func_index].sig;
1448 auto resolved =
1449 compiler::ResolveWasmImportCall(js_receiver, sig, module_, enabled_);
1450 compiler::WasmImportCallKind kind = resolved.first;
1451 if (kind == compiler::WasmImportCallKind::kWasmToWasm ||
1452 kind == compiler::WasmImportCallKind::kLinkError ||
1453 kind == compiler::WasmImportCallKind::kWasmToCapi) {
1454 continue;
1455 }
1456
1457 int expected_arity = static_cast<int>(sig->parameter_count());
1458 if (resolved.first ==
1459 compiler::WasmImportCallKind::kJSFunctionArityMismatch) {
1460 Handle<JSFunction> function = Handle<JSFunction>::cast(resolved.second);
1461 SharedFunctionInfo shared = function->shared();
1462 expected_arity = shared.internal_formal_parameter_count();
1463 }
1464
1465 WasmImportWrapperCache::CacheKey key(kind, sig, expected_arity);
1466 if (cache_scope[key] != nullptr) {
1467 // Cache entry already exists, no need to compile it again.
1468 continue;
1469 }
1470 import_wrapper_queue.insert(key);
1471 }
1472
1473 auto compile_job_task = std::make_unique<CompileImportWrapperJob>(
1474 isolate_->wasm_engine(), isolate_->counters(), native_module,
1475 &import_wrapper_queue, &cache_scope);
1476 auto compile_job = V8::GetCurrentPlatform()->PostJob(
1477 TaskPriority::kUserVisible, std::move(compile_job_task));
1478
1479 // Wait for the job to finish, while contributing in this thread.
1480 compile_job->Join();
1481 }
1482
1483 // Process the imports, including functions, tables, globals, and memory, in
1484 // order, loading them from the {ffi_} object. Returns the number of imported
1485 // functions.
ProcessImports(Handle<WasmInstanceObject> instance)1486 int InstanceBuilder::ProcessImports(Handle<WasmInstanceObject> instance) {
1487 int num_imported_functions = 0;
1488 int num_imported_tables = 0;
1489
1490 DCHECK_EQ(module_->import_table.size(), sanitized_imports_.size());
1491
1492 CompileImportWrappers(instance);
1493 int num_imports = static_cast<int>(module_->import_table.size());
1494 for (int index = 0; index < num_imports; ++index) {
1495 const WasmImport& import = module_->import_table[index];
1496
1497 Handle<String> module_name = sanitized_imports_[index].module_name;
1498 Handle<String> import_name = sanitized_imports_[index].import_name;
1499 Handle<Object> value = sanitized_imports_[index].value;
1500
1501 switch (import.kind) {
1502 case kExternalFunction: {
1503 uint32_t func_index = import.index;
1504 DCHECK_EQ(num_imported_functions, func_index);
1505 if (!ProcessImportedFunction(instance, index, func_index, module_name,
1506 import_name, value)) {
1507 return -1;
1508 }
1509 num_imported_functions++;
1510 break;
1511 }
1512 case kExternalTable: {
1513 uint32_t table_index = import.index;
1514 DCHECK_EQ(table_index, num_imported_tables);
1515 if (!ProcessImportedTable(instance, index, table_index, module_name,
1516 import_name, value)) {
1517 return -1;
1518 }
1519 num_imported_tables++;
1520 break;
1521 }
1522 case kExternalMemory: {
1523 if (!ProcessImportedMemory(instance, index, module_name, import_name,
1524 value)) {
1525 return -1;
1526 }
1527 break;
1528 }
1529 case kExternalGlobal: {
1530 if (!ProcessImportedGlobal(instance, index, import.index, module_name,
1531 import_name, value)) {
1532 return -1;
1533 }
1534 break;
1535 }
1536 case kExternalException: {
1537 if (!value->IsWasmExceptionObject()) {
1538 ReportLinkError("exception import requires a WebAssembly.Exception",
1539 index, module_name, import_name);
1540 return -1;
1541 }
1542 Handle<WasmExceptionObject> imported_exception =
1543 Handle<WasmExceptionObject>::cast(value);
1544 if (!imported_exception->MatchesSignature(
1545 module_->exceptions[import.index].sig)) {
1546 ReportLinkError("imported exception does not match the expected type",
1547 index, module_name, import_name);
1548 return -1;
1549 }
1550 Object exception_tag = imported_exception->exception_tag();
1551 DCHECK(instance->exceptions_table().get(import.index).IsUndefined());
1552 instance->exceptions_table().set(import.index, exception_tag);
1553 exception_wrappers_[import.index] = imported_exception;
1554 break;
1555 }
1556 default:
1557 UNREACHABLE();
1558 break;
1559 }
1560 }
1561 return num_imported_functions;
1562 }
1563
1564 template <typename T>
GetRawGlobalPtr(const WasmGlobal & global)1565 T* InstanceBuilder::GetRawGlobalPtr(const WasmGlobal& global) {
1566 return reinterpret_cast<T*>(raw_buffer_ptr(untagged_globals_, global.offset));
1567 }
1568
RecursivelyEvaluateGlobalInitializer(const WasmInitExpr & init,Handle<WasmInstanceObject> instance)1569 Handle<Object> InstanceBuilder::RecursivelyEvaluateGlobalInitializer(
1570 const WasmInitExpr& init, Handle<WasmInstanceObject> instance) {
1571 switch (init.kind()) {
1572 case WasmInitExpr::kI32Const:
1573 case WasmInitExpr::kI64Const:
1574 case WasmInitExpr::kF32Const:
1575 case WasmInitExpr::kF64Const:
1576 case WasmInitExpr::kS128Const:
1577 case WasmInitExpr::kRefNullConst:
1578 case WasmInitExpr::kRefFuncConst:
1579 case WasmInitExpr::kNone:
1580 // Handled directly by {InitGlobals()}, can't occur as recursive case.
1581 UNREACHABLE();
1582 break;
1583 case WasmInitExpr::kGlobalGet: {
1584 // We can only get here for reference-type globals, but we don't have
1585 // enough information to DCHECK that directly.
1586 DCHECK(enabled_.has_reftypes() || enabled_.has_eh());
1587 uint32_t old_offset = module_->globals[init.immediate().index].offset;
1588 DCHECK(static_cast<int>(old_offset) < tagged_globals_->length());
1589 return handle(tagged_globals_->get(old_offset), isolate_);
1590 }
1591 case WasmInitExpr::kRttCanon: {
1592 switch (init.immediate().heap_type) {
1593 case wasm::HeapType::kEq:
1594 return isolate_->root_handle(RootIndex::kWasmRttEqrefMap);
1595 case wasm::HeapType::kExtern:
1596 return isolate_->root_handle(RootIndex::kWasmRttExternrefMap);
1597 case wasm::HeapType::kFunc:
1598 return isolate_->root_handle(RootIndex::kWasmRttFuncrefMap);
1599 case wasm::HeapType::kI31:
1600 return isolate_->root_handle(RootIndex::kWasmRttI31refMap);
1601 case wasm::HeapType::kExn:
1602 UNIMPLEMENTED(); // TODO(jkummerow): This is going away?
1603 case wasm::HeapType::kBottom:
1604 UNREACHABLE();
1605 }
1606 // Non-generic types fall through.
1607 int map_index = init.immediate().heap_type;
1608 return handle(instance->managed_object_maps().get(map_index), isolate_);
1609 }
1610 case WasmInitExpr::kRttSub: {
1611 uint32_t type = static_cast<uint32_t>(init.immediate().heap_type);
1612 Handle<Object> parent =
1613 RecursivelyEvaluateGlobalInitializer(*init.operand(), instance);
1614 return AllocateSubRtt(isolate_, instance, type,
1615 Handle<Map>::cast(parent));
1616 }
1617 }
1618 }
1619
1620 // Process initialization of globals.
InitGlobals(Handle<WasmInstanceObject> instance)1621 void InstanceBuilder::InitGlobals(Handle<WasmInstanceObject> instance) {
1622 for (const WasmGlobal& global : module_->globals) {
1623 if (global.mutability && global.imported) {
1624 continue;
1625 }
1626
1627 switch (global.init.kind()) {
1628 case WasmInitExpr::kI32Const:
1629 WriteLittleEndianValue<int32_t>(GetRawGlobalPtr<int32_t>(global),
1630 global.init.immediate().i32_const);
1631 break;
1632 case WasmInitExpr::kI64Const:
1633 WriteLittleEndianValue<int64_t>(GetRawGlobalPtr<int64_t>(global),
1634 global.init.immediate().i64_const);
1635 break;
1636 case WasmInitExpr::kF32Const:
1637 WriteLittleEndianValue<float>(GetRawGlobalPtr<float>(global),
1638 global.init.immediate().f32_const);
1639 break;
1640 case WasmInitExpr::kF64Const:
1641 WriteLittleEndianValue<double>(GetRawGlobalPtr<double>(global),
1642 global.init.immediate().f64_const);
1643 break;
1644 case WasmInitExpr::kS128Const:
1645 DCHECK(enabled_.has_simd());
1646 WriteLittleEndianValue<std::array<uint8_t, kSimd128Size>>(
1647 GetRawGlobalPtr<std::array<uint8_t, kSimd128Size>>(global),
1648 global.init.immediate().s128_const);
1649 break;
1650 case WasmInitExpr::kRefNullConst:
1651 DCHECK(enabled_.has_reftypes() || enabled_.has_eh());
1652 if (global.imported) break; // We already initialized imported globals.
1653
1654 tagged_globals_->set(global.offset,
1655 ReadOnlyRoots(isolate_).null_value(),
1656 SKIP_WRITE_BARRIER);
1657 break;
1658 case WasmInitExpr::kRefFuncConst: {
1659 DCHECK(enabled_.has_reftypes());
1660 auto function = WasmInstanceObject::GetOrCreateWasmExternalFunction(
1661 isolate_, instance, global.init.immediate().index);
1662 tagged_globals_->set(global.offset, *function);
1663 break;
1664 }
1665 case WasmInitExpr::kGlobalGet: {
1666 // Initialize with another global.
1667 uint32_t new_offset = global.offset;
1668 uint32_t old_offset =
1669 module_->globals[global.init.immediate().index].offset;
1670 TRACE("init [globals+%u] = [globals+%d]\n", global.offset, old_offset);
1671 if (global.type.is_reference_type()) {
1672 DCHECK(enabled_.has_reftypes() || enabled_.has_eh());
1673 tagged_globals_->set(new_offset, tagged_globals_->get(old_offset));
1674 } else {
1675 size_t size = (global.type == kWasmI64 || global.type == kWasmF64)
1676 ? sizeof(double)
1677 : sizeof(int32_t);
1678 memcpy(raw_buffer_ptr(untagged_globals_, new_offset),
1679 raw_buffer_ptr(untagged_globals_, old_offset), size);
1680 }
1681 break;
1682 }
1683 case WasmInitExpr::kRttCanon:
1684 case WasmInitExpr::kRttSub:
1685 tagged_globals_->set(
1686 global.offset,
1687 *RecursivelyEvaluateGlobalInitializer(global.init, instance));
1688 break;
1689 case WasmInitExpr::kNone:
1690 // Happens with imported globals.
1691 break;
1692 }
1693 }
1694 }
1695
1696 // Allocate memory for a module instance as a new JSArrayBuffer.
AllocateMemory()1697 bool InstanceBuilder::AllocateMemory() {
1698 uint32_t initial_pages = module_->initial_pages;
1699 uint32_t maximum_pages =
1700 module_->has_maximum_pages ? module_->maximum_pages : max_mem_pages();
1701 if (initial_pages > max_mem_pages()) {
1702 thrower_->RangeError("Out of memory: wasm memory too large");
1703 return false;
1704 }
1705 auto shared = (module_->has_shared_memory && enabled_.has_threads())
1706 ? SharedFlag::kShared
1707 : SharedFlag::kNotShared;
1708
1709 MaybeHandle<WasmMemoryObject> result =
1710 WasmMemoryObject::New(isolate_, initial_pages, maximum_pages, shared);
1711
1712 if (!result.ToHandle(&memory_object_)) {
1713 thrower_->RangeError("Out of memory: wasm memory");
1714 return false;
1715 }
1716 memory_buffer_ =
1717 Handle<JSArrayBuffer>(memory_object_->array_buffer(), isolate_);
1718 return true;
1719 }
1720
1721 // Process the exports, creating wrappers for functions, tables, memories,
1722 // globals, and exceptions.
ProcessExports(Handle<WasmInstanceObject> instance)1723 void InstanceBuilder::ProcessExports(Handle<WasmInstanceObject> instance) {
1724 std::unordered_map<int, Handle<Object>> imported_globals;
1725
1726 // If an imported WebAssembly function or global gets exported, the export
1727 // has to be identical to to import. Therefore we cache all imported
1728 // WebAssembly functions in the instance, and all imported globals in a map
1729 // here.
1730 for (int index = 0, end = static_cast<int>(module_->import_table.size());
1731 index < end; ++index) {
1732 const WasmImport& import = module_->import_table[index];
1733 if (import.kind == kExternalFunction) {
1734 Handle<Object> value = sanitized_imports_[index].value;
1735 if (WasmExternalFunction::IsWasmExternalFunction(*value)) {
1736 WasmInstanceObject::SetWasmExternalFunction(
1737 isolate_, instance, import.index,
1738 Handle<WasmExternalFunction>::cast(value));
1739 }
1740 } else if (import.kind == kExternalGlobal) {
1741 Handle<Object> value = sanitized_imports_[index].value;
1742 if (value->IsWasmGlobalObject()) {
1743 imported_globals[import.index] = value;
1744 }
1745 }
1746 }
1747
1748 Handle<JSObject> exports_object;
1749 MaybeHandle<String> single_function_name;
1750 bool is_asm_js = is_asmjs_module(module_);
1751 if (is_asm_js) {
1752 Handle<JSFunction> object_function = Handle<JSFunction>(
1753 isolate_->native_context()->object_function(), isolate_);
1754 exports_object = isolate_->factory()->NewJSObject(object_function);
1755 single_function_name =
1756 isolate_->factory()->InternalizeUtf8String(AsmJs::kSingleFunctionName);
1757 } else {
1758 exports_object = isolate_->factory()->NewJSObjectWithNullProto();
1759 }
1760 instance->set_exports_object(*exports_object);
1761
1762 PropertyDescriptor desc;
1763 desc.set_writable(is_asm_js);
1764 desc.set_enumerable(true);
1765 desc.set_configurable(is_asm_js);
1766
1767 // Process each export in the export table.
1768 for (const WasmExport& exp : module_->export_table) {
1769 Handle<String> name = WasmModuleObject::ExtractUtf8StringFromModuleBytes(
1770 isolate_, module_object_, exp.name, kInternalize);
1771 Handle<JSObject> export_to = exports_object;
1772 switch (exp.kind) {
1773 case kExternalFunction: {
1774 // Wrap and export the code as a JSFunction.
1775 // TODO(wasm): reduce duplication with LoadElemSegment() further below
1776 Handle<WasmExternalFunction> wasm_external_function =
1777 WasmInstanceObject::GetOrCreateWasmExternalFunction(
1778 isolate_, instance, exp.index);
1779 desc.set_value(wasm_external_function);
1780
1781 if (is_asm_js &&
1782 String::Equals(isolate_, name,
1783 single_function_name.ToHandleChecked())) {
1784 export_to = instance;
1785 }
1786 break;
1787 }
1788 case kExternalTable: {
1789 desc.set_value(handle(instance->tables().get(exp.index), isolate_));
1790 break;
1791 }
1792 case kExternalMemory: {
1793 // Export the memory as a WebAssembly.Memory object. A WasmMemoryObject
1794 // should already be available if the module has memory, since we always
1795 // create or import it when building an WasmInstanceObject.
1796 DCHECK(instance->has_memory_object());
1797 desc.set_value(
1798 Handle<WasmMemoryObject>(instance->memory_object(), isolate_));
1799 break;
1800 }
1801 case kExternalGlobal: {
1802 const WasmGlobal& global = module_->globals[exp.index];
1803 if (global.imported) {
1804 auto cached_global = imported_globals.find(exp.index);
1805 if (cached_global != imported_globals.end()) {
1806 desc.set_value(cached_global->second);
1807 break;
1808 }
1809 }
1810 Handle<JSArrayBuffer> untagged_buffer;
1811 Handle<FixedArray> tagged_buffer;
1812 uint32_t offset;
1813
1814 if (global.mutability && global.imported) {
1815 Handle<FixedArray> buffers_array(
1816 instance->imported_mutable_globals_buffers(), isolate_);
1817 if (global.type.is_reference_type()) {
1818 tagged_buffer = handle(
1819 FixedArray::cast(buffers_array->get(global.index)), isolate_);
1820 // For externref globals we store the relative offset in the
1821 // imported_mutable_globals array instead of an absolute address.
1822 Address addr = instance->imported_mutable_globals()[global.index];
1823 DCHECK_LE(addr, static_cast<Address>(
1824 std::numeric_limits<uint32_t>::max()));
1825 offset = static_cast<uint32_t>(addr);
1826 } else {
1827 untagged_buffer =
1828 handle(JSArrayBuffer::cast(buffers_array->get(global.index)),
1829 isolate_);
1830 Address global_addr =
1831 instance->imported_mutable_globals()[global.index];
1832
1833 size_t buffer_size = untagged_buffer->byte_length();
1834 Address backing_store =
1835 reinterpret_cast<Address>(untagged_buffer->backing_store());
1836 CHECK(global_addr >= backing_store &&
1837 global_addr < backing_store + buffer_size);
1838 offset = static_cast<uint32_t>(global_addr - backing_store);
1839 }
1840 } else {
1841 if (global.type.is_reference_type()) {
1842 tagged_buffer = handle(instance->tagged_globals_buffer(), isolate_);
1843 } else {
1844 untagged_buffer =
1845 handle(instance->untagged_globals_buffer(), isolate_);
1846 }
1847 offset = global.offset;
1848 }
1849
1850 // Since the global's array untagged_buffer is always provided,
1851 // allocation should never fail.
1852 Handle<WasmGlobalObject> global_obj =
1853 WasmGlobalObject::New(isolate_, instance, untagged_buffer,
1854 tagged_buffer, global.type, offset,
1855 global.mutability)
1856 .ToHandleChecked();
1857 desc.set_value(global_obj);
1858 break;
1859 }
1860 case kExternalException: {
1861 const WasmException& exception = module_->exceptions[exp.index];
1862 Handle<WasmExceptionObject> wrapper = exception_wrappers_[exp.index];
1863 if (wrapper.is_null()) {
1864 Handle<HeapObject> exception_tag(
1865 HeapObject::cast(instance->exceptions_table().get(exp.index)),
1866 isolate_);
1867 wrapper =
1868 WasmExceptionObject::New(isolate_, exception.sig, exception_tag);
1869 exception_wrappers_[exp.index] = wrapper;
1870 }
1871 desc.set_value(wrapper);
1872 break;
1873 }
1874 default:
1875 UNREACHABLE();
1876 break;
1877 }
1878
1879 v8::Maybe<bool> status = JSReceiver::DefineOwnProperty(
1880 isolate_, export_to, name, &desc, Just(kThrowOnError));
1881 if (!status.IsJust()) {
1882 DisallowHeapAllocation no_gc;
1883 TruncatedUserString<> trunc_name(name->GetCharVector<uint8_t>(no_gc));
1884 thrower_->LinkError("export of %.*s failed.", trunc_name.length(),
1885 trunc_name.start());
1886 return;
1887 }
1888 }
1889
1890 if (module_->origin == kWasmOrigin) {
1891 v8::Maybe<bool> success =
1892 JSReceiver::SetIntegrityLevel(exports_object, FROZEN, kDontThrow);
1893 DCHECK(success.FromMaybe(false));
1894 USE(success);
1895 }
1896 }
1897
InitializeIndirectFunctionTables(Handle<WasmInstanceObject> instance)1898 void InstanceBuilder::InitializeIndirectFunctionTables(
1899 Handle<WasmInstanceObject> instance) {
1900 for (int i = 0; i < static_cast<int>(module_->tables.size()); ++i) {
1901 const WasmTable& table = module_->tables[i];
1902
1903 if (IsSubtypeOf(table.type, kWasmFuncRef, module_)) {
1904 WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize(
1905 instance, i, table.initial_size);
1906 }
1907 }
1908 }
1909
LoadElemSegmentImpl(Isolate * isolate,Handle<WasmInstanceObject> instance,Handle<WasmTableObject> table_object,uint32_t table_index,uint32_t segment_index,uint32_t dst,uint32_t src,size_t count)1910 bool LoadElemSegmentImpl(Isolate* isolate, Handle<WasmInstanceObject> instance,
1911 Handle<WasmTableObject> table_object,
1912 uint32_t table_index, uint32_t segment_index,
1913 uint32_t dst, uint32_t src, size_t count) {
1914 DCHECK_LT(segment_index, instance->module()->elem_segments.size());
1915 auto& elem_segment = instance->module()->elem_segments[segment_index];
1916 // TODO(wasm): Move this functionality into wasm-objects, since it is used
1917 // for both instantiation and in the implementation of the table.init
1918 // instruction.
1919 if (!base::IsInBounds<uint64_t>(dst, count, table_object->current_length()) ||
1920 !base::IsInBounds<uint64_t>(
1921 src, count,
1922 instance->dropped_elem_segments()[segment_index] == 0
1923 ? elem_segment.entries.size()
1924 : 0)) {
1925 return false;
1926 }
1927
1928 const WasmModule* module = instance->module();
1929 for (size_t i = 0; i < count; ++i) {
1930 uint32_t func_index = elem_segment.entries[src + i];
1931 int entry_index = static_cast<int>(dst + i);
1932
1933 if (func_index == WasmElemSegment::kNullIndex) {
1934 if (IsSubtypeOf(table_object->type(), kWasmFuncRef, module)) {
1935 IndirectFunctionTableEntry(instance, table_index, entry_index).clear();
1936 }
1937 WasmTableObject::Set(isolate, table_object, entry_index,
1938 isolate->factory()->null_value());
1939 continue;
1940 }
1941
1942 const WasmFunction* function = &module->functions[func_index];
1943
1944 // Update the local dispatch table first if necessary.
1945 if (IsSubtypeOf(table_object->type(), kWasmFuncRef, module)) {
1946 uint32_t sig_id = module->canonicalized_type_ids[function->sig_index];
1947 IndirectFunctionTableEntry(instance, table_index, entry_index)
1948 .Set(sig_id, instance, func_index);
1949 }
1950
1951 // For ExternRef tables, we have to generate the WasmExternalFunction
1952 // eagerly. Later we cannot know if an entry is a placeholder or not.
1953 if (table_object->type().is_reference_to(HeapType::kExtern)) {
1954 Handle<WasmExternalFunction> wasm_external_function =
1955 WasmInstanceObject::GetOrCreateWasmExternalFunction(isolate, instance,
1956 func_index);
1957 WasmTableObject::Set(isolate, table_object, entry_index,
1958 wasm_external_function);
1959 } else {
1960 // Update the table object's other dispatch tables.
1961 MaybeHandle<WasmExternalFunction> wasm_external_function =
1962 WasmInstanceObject::GetWasmExternalFunction(isolate, instance,
1963 func_index);
1964 if (wasm_external_function.is_null()) {
1965 // No JSFunction entry yet exists for this function. Create a {Tuple2}
1966 // holding the information to lazily allocate one.
1967 WasmTableObject::SetFunctionTablePlaceholder(
1968 isolate, table_object, entry_index, instance, func_index);
1969 } else {
1970 table_object->entries().set(entry_index,
1971 *wasm_external_function.ToHandleChecked());
1972 }
1973 // UpdateDispatchTables() updates all other dispatch tables, since
1974 // we have not yet added the dispatch table we are currently building.
1975 WasmTableObject::UpdateDispatchTables(isolate, table_object, entry_index,
1976 function->sig, instance,
1977 func_index);
1978 }
1979 }
1980 return true;
1981 }
1982
LoadTableSegments(Handle<WasmInstanceObject> instance)1983 void InstanceBuilder::LoadTableSegments(Handle<WasmInstanceObject> instance) {
1984 for (uint32_t segment_index = 0;
1985 segment_index < module_->elem_segments.size(); ++segment_index) {
1986 auto& elem_segment = instance->module()->elem_segments[segment_index];
1987 // Passive segments are not copied during instantiation.
1988 if (elem_segment.status != WasmElemSegment::kStatusActive) continue;
1989
1990 uint32_t table_index = elem_segment.table_index;
1991 uint32_t dst = EvalUint32InitExpr(instance, elem_segment.offset);
1992 uint32_t src = 0;
1993 size_t count = elem_segment.entries.size();
1994
1995 bool success = LoadElemSegmentImpl(
1996 isolate_, instance,
1997 handle(WasmTableObject::cast(
1998 instance->tables().get(elem_segment.table_index)),
1999 isolate_),
2000 table_index, segment_index, dst, src, count);
2001 // Set the active segments to being already dropped, since memory.init on
2002 // a dropped passive segment and an active segment have the same
2003 // behavior.
2004 instance->dropped_elem_segments()[segment_index] = 1;
2005 if (enabled_.has_bulk_memory()) {
2006 if (!success) {
2007 thrower_->RuntimeError("table initializer is out of bounds");
2008 // Break out instead of returning; we don't want to continue to
2009 // initialize any further element segments, but still need to add
2010 // dispatch tables below.
2011 break;
2012 }
2013 } else {
2014 CHECK(success);
2015 }
2016 }
2017
2018 int table_count = static_cast<int>(module_->tables.size());
2019 for (int index = 0; index < table_count; ++index) {
2020 if (IsSubtypeOf(module_->tables[index].type, kWasmFuncRef, module_)) {
2021 auto table_object = handle(
2022 WasmTableObject::cast(instance->tables().get(index)), isolate_);
2023
2024 // Add the new dispatch table at the end to avoid redundant lookups.
2025 WasmTableObject::AddDispatchTable(isolate_, table_object, instance,
2026 index);
2027 }
2028 }
2029 }
2030
InitializeExceptions(Handle<WasmInstanceObject> instance)2031 void InstanceBuilder::InitializeExceptions(
2032 Handle<WasmInstanceObject> instance) {
2033 Handle<FixedArray> exceptions_table(instance->exceptions_table(), isolate_);
2034 for (int index = 0; index < exceptions_table->length(); ++index) {
2035 if (!exceptions_table->get(index).IsUndefined(isolate_)) continue;
2036 Handle<WasmExceptionTag> exception_tag =
2037 WasmExceptionTag::New(isolate_, index);
2038 exceptions_table->set(index, *exception_tag);
2039 }
2040 }
2041
LoadElemSegment(Isolate * isolate,Handle<WasmInstanceObject> instance,uint32_t table_index,uint32_t segment_index,uint32_t dst,uint32_t src,uint32_t count)2042 bool LoadElemSegment(Isolate* isolate, Handle<WasmInstanceObject> instance,
2043 uint32_t table_index, uint32_t segment_index, uint32_t dst,
2044 uint32_t src, uint32_t count) {
2045 return LoadElemSegmentImpl(
2046 isolate, instance,
2047 handle(WasmTableObject::cast(instance->tables().get(table_index)),
2048 isolate),
2049 table_index, segment_index, dst, src, count);
2050 }
2051
2052 } // namespace wasm
2053 } // namespace internal
2054 } // namespace v8
2055
2056 #undef TRACE
2057