Line data Source code
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/asmjs/asm-js.h"
8 : #include "src/conversions-inl.h"
9 : #include "src/counters.h"
10 : #include "src/property-descriptor.h"
11 : #include "src/tracing/trace-event.h"
12 : #include "src/utils.h"
13 : #include "src/wasm/module-compiler.h"
14 : #include "src/wasm/wasm-external-refs.h"
15 : #include "src/wasm/wasm-import-wrapper-cache.h"
16 : #include "src/wasm/wasm-module.h"
17 : #include "src/wasm/wasm-objects-inl.h"
18 :
19 : #define TRACE(...) \
20 : do { \
21 : if (FLAG_trace_wasm_instances) PrintF(__VA_ARGS__); \
22 : } while (false)
23 :
24 : namespace v8 {
25 : namespace internal {
26 : namespace wasm {
27 :
28 : namespace {
29 : byte* raw_buffer_ptr(MaybeHandle<JSArrayBuffer> buffer, int offset) {
30 8402 : return static_cast<byte*>(buffer.ToHandleChecked()->backing_store()) + offset;
31 : }
32 :
33 3126 : uint32_t EvalUint32InitExpr(Handle<WasmInstanceObject> instance,
34 : const WasmInitExpr& expr) {
35 3126 : switch (expr.kind) {
36 : case WasmInitExpr::kI32Const:
37 2414 : return expr.val.i32_const;
38 : case WasmInitExpr::kGlobalIndex: {
39 : uint32_t offset =
40 2136 : instance->module()->globals[expr.val.global_index].offset;
41 : auto raw_addr =
42 : reinterpret_cast<Address>(
43 712 : instance->untagged_globals_buffer()->backing_store()) +
44 712 : offset;
45 : return ReadLittleEndianValue<uint32_t>(raw_addr);
46 : }
47 : default:
48 0 : UNREACHABLE();
49 : }
50 : }
51 : } // namespace
52 :
53 : // A helper class to simplify instantiating a module from a module object.
54 : // It closes over the {Isolate}, the {ErrorThrower}, etc.
55 272937 : class InstanceBuilder {
56 : public:
57 : InstanceBuilder(Isolate* isolate, ErrorThrower* thrower,
58 : Handle<WasmModuleObject> module_object,
59 : MaybeHandle<JSReceiver> ffi,
60 : MaybeHandle<JSArrayBuffer> memory);
61 :
62 : // Build an instance, in all of its glory.
63 : MaybeHandle<WasmInstanceObject> Build();
64 : // Run the start function, if any.
65 : bool ExecuteStartFunction();
66 :
67 : private:
68 : // A pre-evaluated value to use in import binding.
69 : struct SanitizedImport {
70 : Handle<String> module_name;
71 : Handle<String> import_name;
72 : Handle<Object> value;
73 : };
74 :
75 : Isolate* isolate_;
76 : const WasmFeatures enabled_;
77 : const WasmModule* const module_;
78 : ErrorThrower* thrower_;
79 : Handle<WasmModuleObject> module_object_;
80 : MaybeHandle<JSReceiver> ffi_;
81 : MaybeHandle<JSArrayBuffer> memory_;
82 : Handle<JSArrayBuffer> untagged_globals_;
83 : Handle<FixedArray> tagged_globals_;
84 : std::vector<Handle<WasmExceptionObject>> exception_wrappers_;
85 : Handle<WasmExportedFunction> start_function_;
86 : std::vector<SanitizedImport> sanitized_imports_;
87 :
88 : // Helper routines to print out errors with imports.
89 : #define ERROR_THROWER_WITH_MESSAGE(TYPE) \
90 : void Report##TYPE(const char* error, uint32_t index, \
91 : Handle<String> module_name, Handle<String> import_name) { \
92 : thrower_->TYPE("Import #%d module=\"%s\" function=\"%s\" error: %s", \
93 : index, module_name->ToCString().get(), \
94 : import_name->ToCString().get(), error); \
95 : } \
96 : \
97 : MaybeHandle<Object> Report##TYPE(const char* error, uint32_t index, \
98 : Handle<String> module_name) { \
99 : thrower_->TYPE("Import #%d module=\"%s\" error: %s", index, \
100 : module_name->ToCString().get(), error); \
101 : return MaybeHandle<Object>(); \
102 : }
103 :
104 10686 : ERROR_THROWER_WITH_MESSAGE(LinkError)
105 1536 : ERROR_THROWER_WITH_MESSAGE(TypeError)
106 :
107 : #undef ERROR_THROWER_WITH_MESSAGE
108 :
109 : // Look up an import value in the {ffi_} object.
110 : MaybeHandle<Object> LookupImport(uint32_t index, Handle<String> module_name,
111 : Handle<String> import_name);
112 :
113 : // Look up an import value in the {ffi_} object specifically for linking an
114 : // asm.js module. This only performs non-observable lookups, which allows
115 : // falling back to JavaScript proper (and hence re-executing all lookups) if
116 : // module instantiation fails.
117 : MaybeHandle<Object> LookupImportAsm(uint32_t index,
118 : Handle<String> import_name);
119 :
120 : // Load data segments into the memory.
121 : void LoadDataSegments(Handle<WasmInstanceObject> instance);
122 :
123 : void WriteGlobalValue(const WasmGlobal& global, double value);
124 : void WriteGlobalValue(const WasmGlobal& global, int64_t num);
125 : void WriteGlobalValue(const WasmGlobal& global,
126 : Handle<WasmGlobalObject> value);
127 :
128 : void WriteGlobalAnyRef(const WasmGlobal& global, Handle<Object> value);
129 :
130 : void SanitizeImports();
131 :
132 : // Find the imported memory buffer if there is one. This is used to see if we
133 : // need to recompile with bounds checks before creating the instance.
134 : MaybeHandle<JSArrayBuffer> FindImportedMemoryBuffer() const;
135 :
136 : // Processes a single imported function.
137 : bool ProcessImportedFunction(Handle<WasmInstanceObject> instance,
138 : int import_index, int func_index,
139 : Handle<String> module_name,
140 : Handle<String> import_name,
141 : Handle<Object> value);
142 :
143 : // Process a single imported table.
144 : bool ProcessImportedTable(Handle<WasmInstanceObject> instance,
145 : int import_index, int table_index,
146 : Handle<String> module_name,
147 : Handle<String> import_name, Handle<Object> value);
148 :
149 : // Process a single imported memory.
150 : bool ProcessImportedMemory(Handle<WasmInstanceObject> instance,
151 : int import_index, Handle<String> module_name,
152 : Handle<String> import_name, Handle<Object> value);
153 :
154 : // Process a single imported global.
155 : bool ProcessImportedGlobal(Handle<WasmInstanceObject> instance,
156 : int import_index, int global_index,
157 : Handle<String> module_name,
158 : Handle<String> import_name, Handle<Object> value);
159 :
160 : // Process a single imported WasmGlobalObject.
161 : bool ProcessImportedWasmGlobalObject(Handle<WasmInstanceObject> instance,
162 : int import_index,
163 : Handle<String> module_name,
164 : Handle<String> import_name,
165 : const WasmGlobal& global,
166 : Handle<WasmGlobalObject> global_object);
167 :
168 : // Process the imports, including functions, tables, globals, and memory, in
169 : // order, loading them from the {ffi_} object. Returns the number of imported
170 : // functions.
171 : int ProcessImports(Handle<WasmInstanceObject> instance);
172 :
173 : template <typename T>
174 : T* GetRawGlobalPtr(const WasmGlobal& global);
175 :
176 : // Process initialization of globals.
177 : void InitGlobals();
178 :
179 : // Allocate memory for a module instance as a new JSArrayBuffer.
180 : Handle<JSArrayBuffer> AllocateMemory(uint32_t initial_pages,
181 : uint32_t maximum_pages);
182 :
183 : bool NeedsWrappers() const;
184 :
185 : // Process the exports, creating wrappers for functions, tables, memories,
186 : // and globals.
187 : void ProcessExports(Handle<WasmInstanceObject> instance);
188 :
189 : void InitializeTables(Handle<WasmInstanceObject> instance);
190 :
191 : void LoadTableSegments(Handle<WasmInstanceObject> instance);
192 :
193 : // Creates new exception tags for all exceptions. Note that some tags might
194 : // already exist if they were imported, those tags will be re-used.
195 : void InitializeExceptions(Handle<WasmInstanceObject> instance);
196 : };
197 :
198 136466 : MaybeHandle<WasmInstanceObject> InstantiateToInstanceObject(
199 : Isolate* isolate, ErrorThrower* thrower,
200 : Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports,
201 : MaybeHandle<JSArrayBuffer> memory) {
202 272934 : InstanceBuilder builder(isolate, thrower, module_object, imports, memory);
203 136470 : auto instance = builder.Build();
204 136468 : if (!instance.is_null() && builder.ExecuteStartFunction()) {
205 133680 : return instance;
206 : }
207 : DCHECK(isolate->has_pending_exception() || thrower->error());
208 2788 : return {};
209 : }
210 :
211 136466 : InstanceBuilder::InstanceBuilder(Isolate* isolate, ErrorThrower* thrower,
212 : Handle<WasmModuleObject> module_object,
213 : MaybeHandle<JSReceiver> ffi,
214 : MaybeHandle<JSArrayBuffer> memory)
215 : : isolate_(isolate),
216 272932 : enabled_(module_object->native_module()->enabled_features()),
217 272934 : module_(module_object->module()),
218 : thrower_(thrower),
219 : module_object_(module_object),
220 : ffi_(ffi),
221 545868 : memory_(memory) {
222 136468 : sanitized_imports_.reserve(module_->import_table.size());
223 136470 : }
224 :
225 : // Build an instance, in all of its glory.
226 136467 : MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
227 409402 : TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"), "InstanceBuilder::Build");
228 : // Check that an imports argument was provided, if the module requires it.
229 : // No point in continuing otherwise.
230 257861 : if (!module_->import_table.empty() && ffi_.is_null()) {
231 93 : thrower_->TypeError(
232 93 : "Imports argument must be present and must be an object");
233 93 : return {};
234 : }
235 :
236 136374 : SanitizeImports();
237 136377 : if (thrower_->error()) return {};
238 :
239 : // From here on, we expect the build pipeline to run without exiting to JS.
240 271948 : DisallowJavascriptExecution no_js(isolate_);
241 : // Record build time into correct bucket, then build instance.
242 271946 : TimedHistogramScope wasm_instantiate_module_time_scope(SELECT_WASM_COUNTER(
243 135973 : isolate_->counters(), module_->origin, wasm_instantiate, module_time));
244 135975 : NativeModule* native_module = module_object_->native_module();
245 :
246 : //--------------------------------------------------------------------------
247 : // Allocate the memory array buffer.
248 : //--------------------------------------------------------------------------
249 135975 : uint32_t initial_pages = module_->initial_pages;
250 140839 : auto initial_pages_counter = SELECT_WASM_COUNTER(
251 : isolate_->counters(), module_->origin, wasm, min_mem_pages_count);
252 135975 : initial_pages_counter->AddSample(initial_pages);
253 135975 : if (module_->has_maximum_pages) {
254 : DCHECK_EQ(kWasmOrigin, module_->origin);
255 : auto max_pages_counter =
256 3745 : isolate_->counters()->wasm_wasm_max_mem_pages_count();
257 3745 : max_pages_counter->AddSample(module_->maximum_pages);
258 : }
259 : // Asm.js has memory_ already set at this point, so we don't want to
260 : // overwrite it.
261 135975 : if (memory_.is_null()) {
262 134735 : memory_ = FindImportedMemoryBuffer();
263 : }
264 135975 : if (!memory_.is_null()) {
265 : // Set externally passed ArrayBuffer non detachable.
266 : Handle<JSArrayBuffer> memory = memory_.ToHandleChecked();
267 : memory->set_is_detachable(false);
268 :
269 : DCHECK_IMPLIES(native_module->use_trap_handler(),
270 : module_->origin == kAsmJsOrigin ||
271 : memory->is_wasm_memory() ||
272 : memory->backing_store() == nullptr);
273 131273 : } else if (initial_pages > 0 || native_module->use_trap_handler()) {
274 : // We need to unconditionally create a guard region if using trap handlers,
275 : // even when the size is zero to prevent null-dereference issues
276 : // (e.g. https://crbug.com/769637).
277 : // Allocate memory if the initial size is more than 0 pages.
278 131211 : memory_ = AllocateMemory(initial_pages, module_->maximum_pages);
279 131211 : if (memory_.is_null()) {
280 : // failed to allocate memory
281 : DCHECK(isolate_->has_pending_exception() || thrower_->error());
282 16 : return {};
283 : }
284 : }
285 :
286 : //--------------------------------------------------------------------------
287 : // Create the WebAssembly.Instance object.
288 : //--------------------------------------------------------------------------
289 : TRACE("New module instantiation for %p\n", native_module);
290 : Handle<WasmInstanceObject> instance =
291 135959 : WasmInstanceObject::New(isolate_, module_object_);
292 271913 : NativeModuleModificationScope native_modification_scope(native_module);
293 :
294 : //--------------------------------------------------------------------------
295 : // Set up the globals for the new instance.
296 : //--------------------------------------------------------------------------
297 135957 : uint32_t untagged_globals_buffer_size = module_->untagged_globals_buffer_size;
298 135957 : if (untagged_globals_buffer_size > 0) {
299 6534 : void* backing_store = isolate_->array_buffer_allocator()->Allocate(
300 6534 : untagged_globals_buffer_size);
301 3267 : if (backing_store == nullptr) {
302 0 : thrower_->RangeError("Out of memory: wasm globals");
303 0 : return {};
304 : }
305 3267 : untagged_globals_ = isolate_->factory()->NewJSArrayBuffer(
306 3267 : SharedFlag::kNotShared, AllocationType::kOld);
307 : constexpr bool is_external = false;
308 : constexpr bool is_wasm_memory = false;
309 3267 : JSArrayBuffer::Setup(untagged_globals_, isolate_, is_external,
310 : backing_store, untagged_globals_buffer_size,
311 3267 : SharedFlag::kNotShared, is_wasm_memory);
312 3267 : if (untagged_globals_.is_null()) {
313 0 : thrower_->RangeError("Out of memory: wasm globals");
314 0 : return {};
315 : }
316 : instance->set_globals_start(
317 : reinterpret_cast<byte*>(untagged_globals_->backing_store()));
318 3267 : instance->set_untagged_globals_buffer(*untagged_globals_);
319 : }
320 :
321 135957 : uint32_t tagged_globals_buffer_size = module_->tagged_globals_buffer_size;
322 135957 : if (tagged_globals_buffer_size > 0) {
323 400 : tagged_globals_ = isolate_->factory()->NewFixedArray(
324 800 : static_cast<int>(tagged_globals_buffer_size));
325 400 : instance->set_tagged_globals_buffer(*tagged_globals_);
326 : }
327 :
328 : //--------------------------------------------------------------------------
329 : // Set up the array of references to imported globals' array buffers.
330 : //--------------------------------------------------------------------------
331 135957 : if (module_->num_imported_mutable_globals > 0) {
332 : // TODO(binji): This allocates one slot for each mutable global, which is
333 : // more than required if multiple globals are imported from the same
334 : // module.
335 264 : Handle<FixedArray> buffers_array = isolate_->factory()->NewFixedArray(
336 528 : module_->num_imported_mutable_globals, AllocationType::kOld);
337 264 : instance->set_imported_mutable_globals_buffers(*buffers_array);
338 : }
339 :
340 : //--------------------------------------------------------------------------
341 : // Set up the exception table used for exception tag checks.
342 : //--------------------------------------------------------------------------
343 271914 : int exceptions_count = static_cast<int>(module_->exceptions.size());
344 135957 : if (exceptions_count > 0) {
345 616 : Handle<FixedArray> exception_table = isolate_->factory()->NewFixedArray(
346 616 : exceptions_count, AllocationType::kOld);
347 616 : instance->set_exceptions_table(*exception_table);
348 616 : exception_wrappers_.resize(exceptions_count);
349 : }
350 :
351 : //--------------------------------------------------------------------------
352 : // Set up table storage space.
353 : //--------------------------------------------------------------------------
354 271914 : int table_count = static_cast<int>(module_->tables.size());
355 135957 : Handle<FixedArray> tables = isolate_->factory()->NewFixedArray(table_count);
356 137325 : for (int i = module_->num_imported_tables; i < table_count; i++) {
357 1369 : const WasmTable& table = module_->tables[i];
358 : Handle<WasmTableObject> table_obj = WasmTableObject::New(
359 2738 : isolate_, table.type, table.initial_size, table.has_maximum_size,
360 4107 : table.maximum_size, nullptr);
361 2738 : tables->set(i, *table_obj);
362 : }
363 135956 : instance->set_tables(*tables);
364 :
365 : //--------------------------------------------------------------------------
366 : // Process the imports for the module.
367 : //--------------------------------------------------------------------------
368 135956 : int num_imported_functions = ProcessImports(instance);
369 135956 : if (num_imported_functions < 0) return {};
370 :
371 : //--------------------------------------------------------------------------
372 : // Process the initialization for the module's globals.
373 : //--------------------------------------------------------------------------
374 133995 : InitGlobals();
375 :
376 : //--------------------------------------------------------------------------
377 : // Initialize the indirect tables.
378 : //--------------------------------------------------------------------------
379 133995 : if (table_count > 0) {
380 2345 : InitializeTables(instance);
381 : }
382 :
383 : //--------------------------------------------------------------------------
384 : // Initialize the exceptions table.
385 : //--------------------------------------------------------------------------
386 133996 : if (exceptions_count > 0) {
387 576 : InitializeExceptions(instance);
388 : }
389 :
390 : //--------------------------------------------------------------------------
391 : // Create the WebAssembly.Memory object.
392 : //--------------------------------------------------------------------------
393 133996 : if (module_->has_memory) {
394 12606 : if (!instance->has_memory_object()) {
395 : // No memory object exists. Create one.
396 : Handle<WasmMemoryObject> memory_object = WasmMemoryObject::New(
397 : isolate_, memory_,
398 9234 : module_->maximum_pages != 0 ? module_->maximum_pages : -1);
399 9234 : instance->set_memory_object(*memory_object);
400 : }
401 :
402 : // Add the instance object to the list of instances for this memory.
403 12606 : Handle<WasmMemoryObject> memory_object(instance->memory_object(), isolate_);
404 12606 : WasmMemoryObject::AddInstance(isolate_, memory_object, instance);
405 :
406 12608 : if (!memory_.is_null()) {
407 : // Double-check the {memory} array buffer matches the instance.
408 : Handle<JSArrayBuffer> memory = memory_.ToHandleChecked();
409 12596 : CHECK_EQ(instance->memory_size(), memory->byte_length());
410 12596 : CHECK_EQ(instance->memory_start(), memory->backing_store());
411 : }
412 : }
413 :
414 : // The bulk memory proposal changes the MVP behavior here; the segments are
415 : // written as if `memory.init` and `table.init` are executed directly, and
416 : // not bounds checked ahead of time.
417 133998 : if (!enabled_.bulk_memory) {
418 : //--------------------------------------------------------------------------
419 : // Check that indirect function table segments are within bounds.
420 : //--------------------------------------------------------------------------
421 4851 : for (const WasmElemSegment& elem_segment : module_->elem_segments) {
422 101 : if (!elem_segment.active) continue;
423 : DCHECK_LT(elem_segment.table_index, table_count);
424 101 : uint32_t base = EvalUint32InitExpr(instance, elem_segment.offset);
425 : // Because of imported tables, {table_size} has to come from the table
426 : // object itself.
427 : auto table_object = handle(WasmTableObject::cast(instance->tables()->get(
428 101 : elem_segment.table_index)),
429 202 : isolate_);
430 101 : size_t table_size = table_object->elements()->length();
431 202 : if (!IsInBounds(base, elem_segment.entries.size(), table_size)) {
432 0 : thrower_->LinkError("table initializer is out of bounds");
433 0 : return {};
434 : }
435 : }
436 :
437 : //--------------------------------------------------------------------------
438 : // Check that memory segments are within bounds.
439 : //--------------------------------------------------------------------------
440 4851 : for (const WasmDataSegment& seg : module_->data_segments) {
441 0 : if (!seg.active) continue;
442 0 : uint32_t base = EvalUint32InitExpr(instance, seg.dest_addr);
443 0 : if (!IsInBounds(base, seg.source.length(), instance->memory_size())) {
444 0 : thrower_->LinkError("data segment is out of bounds");
445 0 : return {};
446 : }
447 : }
448 : }
449 :
450 : //--------------------------------------------------------------------------
451 : // Set up the exports object for the new instance.
452 : //--------------------------------------------------------------------------
453 133998 : ProcessExports(instance);
454 133995 : if (thrower_->error()) return {};
455 :
456 : //--------------------------------------------------------------------------
457 : // Initialize the indirect function tables.
458 : //--------------------------------------------------------------------------
459 133995 : if (table_count > 0) {
460 2345 : LoadTableSegments(instance);
461 2345 : if (thrower_->error()) return {};
462 : }
463 :
464 : //--------------------------------------------------------------------------
465 : // Initialize the memory by loading data segments.
466 : //--------------------------------------------------------------------------
467 267750 : if (module_->data_segments.size() > 0) {
468 724 : LoadDataSegments(instance);
469 724 : if (thrower_->error()) return {};
470 : }
471 :
472 : //--------------------------------------------------------------------------
473 : // Debugging support.
474 : //--------------------------------------------------------------------------
475 : // Set all breakpoints that were set on the shared module.
476 133747 : WasmModuleObject::SetBreakpointsOnNewInstance(module_object_, instance);
477 :
478 : //--------------------------------------------------------------------------
479 : // Create a wrapper for the start function.
480 : //--------------------------------------------------------------------------
481 133746 : if (module_->start_function_index >= 0) {
482 : int start_index = module_->start_function_index;
483 5027 : auto& function = module_->functions[start_index];
484 10054 : Handle<Code> wrapper_code = compiler::CompileJSToWasmWrapper(
485 5027 : isolate_, function.sig, function.imported)
486 5027 : .ToHandleChecked();
487 : // TODO(clemensh): Don't generate an exported function for the start
488 : // function. Use CWasmEntry instead.
489 : start_function_ = WasmExportedFunction::New(
490 : isolate_, instance, MaybeHandle<String>(), start_index,
491 5027 : static_cast<int>(function.sig->parameter_count()), wrapper_code);
492 : }
493 :
494 : DCHECK(!isolate_->has_pending_exception());
495 : TRACE("Successfully built instance for module %p\n",
496 : module_object_->native_module());
497 133746 : return instance;
498 : }
499 :
500 133746 : bool InstanceBuilder::ExecuteStartFunction() {
501 401240 : TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"),
502 : "InstanceBuilder::ExecuteStartFunction");
503 133747 : if (start_function_.is_null()) return true; // No start function.
504 :
505 5027 : HandleScope scope(isolate_);
506 : // Call the JS function.
507 5027 : Handle<Object> undefined = isolate_->factory()->undefined_value();
508 : MaybeHandle<Object> retval =
509 5027 : Execution::Call(isolate_, start_function_, undefined, 0, nullptr);
510 :
511 5027 : if (retval.is_null()) {
512 : DCHECK(isolate_->has_pending_exception());
513 : return false;
514 : }
515 4959 : return true;
516 : }
517 :
518 : // Look up an import value in the {ffi_} object.
519 122875 : MaybeHandle<Object> InstanceBuilder::LookupImport(uint32_t index,
520 : Handle<String> module_name,
521 :
522 : Handle<String> import_name) {
523 : // We pre-validated in the js-api layer that the ffi object is present, and
524 : // a JSObject, if the module has imports.
525 : DCHECK(!ffi_.is_null());
526 : // Look up the module first.
527 : MaybeHandle<Object> result = Object::GetPropertyOrElement(
528 122875 : isolate_, ffi_.ToHandleChecked(), module_name);
529 122874 : if (result.is_null()) {
530 24 : return ReportTypeError("module not found", index, module_name);
531 : }
532 :
533 : Handle<Object> module = result.ToHandleChecked();
534 :
535 : // Look up the value in the module.
536 122850 : if (!module->IsJSReceiver()) {
537 : return ReportTypeError("module is not an object or function", index,
538 360 : module_name);
539 : }
540 :
541 122490 : result = Object::GetPropertyOrElement(isolate_, module, import_name);
542 122493 : if (result.is_null()) {
543 0 : ReportLinkError("import not found", index, module_name, import_name);
544 0 : return MaybeHandle<JSFunction>();
545 : }
546 :
547 122493 : return result;
548 : }
549 :
550 : // Look up an import value in the {ffi_} object specifically for linking an
551 : // asm.js module. This only performs non-observable lookups, which allows
552 : // falling back to JavaScript proper (and hence re-executing all lookups) if
553 : // module instantiation fails.
554 4787 : MaybeHandle<Object> InstanceBuilder::LookupImportAsm(
555 : uint32_t index, Handle<String> import_name) {
556 : // Check that a foreign function interface object was provided.
557 4787 : if (ffi_.is_null()) {
558 0 : return ReportLinkError("missing imports object", index, import_name);
559 : }
560 :
561 : // Perform lookup of the given {import_name} without causing any observable
562 : // side-effect. We only accept accesses that resolve to data properties,
563 : // which is indicated by the asm.js spec in section 7 ("Linking") as well.
564 : Handle<Object> result;
565 : LookupIterator it = LookupIterator::PropertyOrElement(
566 4787 : isolate_, ffi_.ToHandleChecked(), import_name);
567 : switch (it.state()) {
568 : case LookupIterator::ACCESS_CHECK:
569 : case LookupIterator::INTEGER_INDEXED_EXOTIC:
570 : case LookupIterator::INTERCEPTOR:
571 : case LookupIterator::JSPROXY:
572 : case LookupIterator::ACCESSOR:
573 : case LookupIterator::TRANSITION:
574 18 : return ReportLinkError("not a data property", index, import_name);
575 : case LookupIterator::NOT_FOUND:
576 : // Accepting missing properties as undefined does not cause any
577 : // observable difference from JavaScript semantics, we are lenient.
578 72 : result = isolate_->factory()->undefined_value();
579 72 : break;
580 : case LookupIterator::DATA:
581 4697 : result = it.GetDataValue();
582 4697 : break;
583 : }
584 :
585 4769 : return result;
586 : }
587 :
588 : // Load data segments into the memory.
589 724 : void InstanceBuilder::LoadDataSegments(Handle<WasmInstanceObject> instance) {
590 : Vector<const uint8_t> wire_bytes =
591 1448 : module_object_->native_module()->wire_bytes();
592 724 : for (const WasmDataSegment& segment : module_->data_segments) {
593 : uint32_t size = segment.source.length();
594 :
595 964 : if (enabled_.bulk_memory) {
596 : // Passive segments are not copied during instantiation.
597 964 : if (!segment.active) continue;
598 :
599 916 : uint32_t dest_offset = EvalUint32InitExpr(instance, segment.dest_addr);
600 916 : bool ok = ClampToBounds(dest_offset, &size,
601 : static_cast<uint32_t>(instance->memory_size()));
602 : Address dest_addr =
603 916 : reinterpret_cast<Address>(instance->memory_start()) + dest_offset;
604 916 : Address src_addr = reinterpret_cast<Address>(wire_bytes.start()) +
605 916 : segment.source.offset();
606 916 : memory_copy_wrapper(dest_addr, src_addr, size);
607 916 : if (!ok) {
608 128 : thrower_->LinkError("data segment is out of bounds");
609 : return;
610 : }
611 : } else {
612 : DCHECK(segment.active);
613 : // Segments of size == 0 are just nops.
614 0 : if (size == 0) continue;
615 :
616 0 : uint32_t dest_offset = EvalUint32InitExpr(instance, segment.dest_addr);
617 : DCHECK(IsInBounds(dest_offset, size, instance->memory_size()));
618 0 : byte* dest = instance->memory_start() + dest_offset;
619 0 : const byte* src = wire_bytes.start() + segment.source.offset();
620 0 : memcpy(dest, src, size);
621 : }
622 : }
623 : }
624 :
625 2219 : void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global, double num) {
626 : TRACE("init [globals_start=%p + %u] = %lf, type = %s\n",
627 : reinterpret_cast<void*>(raw_buffer_ptr(untagged_globals_, 0)),
628 : global.offset, num, ValueTypes::TypeName(global.type));
629 2219 : switch (global.type) {
630 : case kWasmI32:
631 1807 : WriteLittleEndianValue<int32_t>(GetRawGlobalPtr<int32_t>(global),
632 : DoubleToInt32(num));
633 : break;
634 : case kWasmI64:
635 : // The Wasm-BigInt proposal currently says that i64 globals may
636 : // only be initialized with BigInts. See:
637 : // https://github.com/WebAssembly/JS-BigInt-integration/issues/12
638 0 : UNREACHABLE();
639 : break;
640 : case kWasmF32:
641 40 : WriteLittleEndianValue<float>(GetRawGlobalPtr<float>(global),
642 : DoubleToFloat32(num));
643 : break;
644 : case kWasmF64:
645 372 : WriteLittleEndianValue<double>(GetRawGlobalPtr<double>(global), num);
646 : break;
647 : default:
648 0 : UNREACHABLE();
649 : }
650 2219 : }
651 :
652 24 : void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global, int64_t num) {
653 : TRACE("init [globals_start=%p + %u] = %" PRId64 ", type = %s\n",
654 : reinterpret_cast<void*>(raw_buffer_ptr(untagged_globals_, 0)),
655 : global.offset, num, ValueTypes::TypeName(global.type));
656 : DCHECK_EQ(kWasmI64, global.type);
657 24 : WriteLittleEndianValue<int64_t>(GetRawGlobalPtr<int64_t>(global), num);
658 24 : }
659 :
660 40 : void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global,
661 : Handle<WasmGlobalObject> value) {
662 : TRACE("init [globals_start=%p + %u] = ",
663 : reinterpret_cast<void*>(raw_buffer_ptr(untagged_globals_, 0)),
664 : global.offset);
665 40 : switch (global.type) {
666 : case kWasmI32: {
667 : int32_t num = value->GetI32();
668 24 : WriteLittleEndianValue<int32_t>(GetRawGlobalPtr<int32_t>(global), num);
669 : TRACE("%d", num);
670 : break;
671 : }
672 : case kWasmI64: {
673 : int64_t num = value->GetI64();
674 8 : WriteLittleEndianValue<int64_t>(GetRawGlobalPtr<int64_t>(global), num);
675 : TRACE("%" PRId64, num);
676 : break;
677 : }
678 : case kWasmF32: {
679 : float num = value->GetF32();
680 8 : WriteLittleEndianValue<float>(GetRawGlobalPtr<float>(global), num);
681 : TRACE("%f", num);
682 : break;
683 : }
684 : case kWasmF64: {
685 : double num = value->GetF64();
686 0 : WriteLittleEndianValue<double>(GetRawGlobalPtr<double>(global), num);
687 : TRACE("%lf", num);
688 : break;
689 : }
690 : default:
691 0 : UNREACHABLE();
692 : }
693 : TRACE(", type = %s (from WebAssembly.Global)\n",
694 : ValueTypes::TypeName(global.type));
695 40 : }
696 :
697 0 : void InstanceBuilder::WriteGlobalAnyRef(const WasmGlobal& global,
698 : Handle<Object> value) {
699 320 : tagged_globals_->set(global.offset, *value, UPDATE_WRITE_BARRIER);
700 0 : }
701 :
702 136373 : void InstanceBuilder::SanitizeImports() {
703 : Vector<const uint8_t> wire_bytes =
704 272750 : module_object_->native_module()->wire_bytes();
705 654543 : for (size_t index = 0; index < module_->import_table.size(); ++index) {
706 : const WasmImport& import = module_->import_table[index];
707 :
708 : Handle<String> module_name;
709 : MaybeHandle<String> maybe_module_name =
710 : WasmModuleObject::ExtractUtf8StringFromModuleBytes(isolate_, wire_bytes,
711 127665 : import.module_name);
712 127665 : if (!maybe_module_name.ToHandle(&module_name)) {
713 0 : thrower_->LinkError("Could not resolve module name for import %zu",
714 0 : index);
715 0 : return;
716 : }
717 :
718 : Handle<String> import_name;
719 : MaybeHandle<String> maybe_import_name =
720 : WasmModuleObject::ExtractUtf8StringFromModuleBytes(isolate_, wire_bytes,
721 127665 : import.field_name);
722 127664 : if (!maybe_import_name.ToHandle(&import_name)) {
723 0 : thrower_->LinkError("Could not resolve import name for import %zu",
724 0 : index);
725 0 : return;
726 : }
727 :
728 : int int_index = static_cast<int>(index);
729 : MaybeHandle<Object> result =
730 127664 : module_->origin == kAsmJsOrigin
731 4787 : ? LookupImportAsm(int_index, import_name)
732 132451 : : LookupImport(int_index, module_name, import_name);
733 127665 : if (thrower_->error()) {
734 402 : thrower_->LinkError("Could not find value for import %zu", index);
735 402 : return;
736 : }
737 : Handle<Object> value = result.ToHandleChecked();
738 254526 : sanitized_imports_.push_back({module_name, import_name, value});
739 : }
740 : }
741 :
742 134735 : MaybeHandle<JSArrayBuffer> InstanceBuilder::FindImportedMemoryBuffer() const {
743 : DCHECK_EQ(module_->import_table.size(), sanitized_imports_.size());
744 627445 : for (size_t index = 0; index < module_->import_table.size(); index++) {
745 : const WasmImport& import = module_->import_table[index];
746 :
747 123199 : if (import.kind == kExternalMemory) {
748 : const auto& value = sanitized_imports_[index].value;
749 3874 : if (!value->IsWasmMemoryObject()) {
750 412 : return {};
751 : }
752 : auto memory = Handle<WasmMemoryObject>::cast(value);
753 3462 : Handle<JSArrayBuffer> buffer(memory->array_buffer(), isolate_);
754 3462 : return buffer;
755 : }
756 : }
757 130861 : return {};
758 : }
759 :
760 118437 : bool InstanceBuilder::ProcessImportedFunction(
761 : Handle<WasmInstanceObject> instance, int import_index, int func_index,
762 : Handle<String> module_name, Handle<String> import_name,
763 : Handle<Object> value) {
764 : // Function imports must be callable.
765 118437 : if (!value->IsCallable()) {
766 400 : ReportLinkError("function import requires a callable", import_index,
767 400 : module_name, import_name);
768 400 : return false;
769 : }
770 118037 : auto js_receiver = Handle<JSReceiver>::cast(value);
771 236074 : FunctionSig* expected_sig = module_->functions[func_index].sig;
772 118037 : auto kind = compiler::GetWasmImportCallKind(js_receiver, expected_sig,
773 236074 : enabled_.bigint);
774 118037 : switch (kind) {
775 : case compiler::WasmImportCallKind::kLinkError:
776 152 : ReportLinkError("imported function does not match the expected type",
777 152 : import_index, module_name, import_name);
778 152 : return false;
779 : case compiler::WasmImportCallKind::kWasmToWasm: {
780 : // The imported function is a WASM function from another instance.
781 : auto imported_function = Handle<WasmExportedFunction>::cast(value);
782 : Handle<WasmInstanceObject> imported_instance(
783 321192 : imported_function->instance(), isolate_);
784 : // The import reference is the instance object itself.
785 107064 : Address imported_target = imported_function->GetWasmCallTarget();
786 : ImportedFunctionEntry entry(instance, func_index);
787 107064 : entry.SetWasmToWasm(*imported_instance, imported_target);
788 : break;
789 : }
790 : default: {
791 : // The imported function is a callable.
792 10821 : NativeModule* native_module = instance->module_object()->native_module();
793 : WasmCode* wasm_code = native_module->import_wrapper_cache()->GetOrCompile(
794 21642 : isolate_->wasm_engine(), isolate_->counters(), kind, expected_sig);
795 : ImportedFunctionEntry entry(instance, func_index);
796 10821 : if (wasm_code->kind() == WasmCode::kWasmToJsWrapper) {
797 : // Wasm to JS wrappers are treated specially in the import table.
798 10597 : entry.SetWasmToJs(isolate_, js_receiver, wasm_code);
799 : } else {
800 : // Wasm math intrinsics are compiled as regular Wasm functions.
801 : DCHECK(kind >= compiler::WasmImportCallKind::kFirstMathIntrinsic &&
802 : kind <= compiler::WasmImportCallKind::kLastMathIntrinsic);
803 224 : entry.SetWasmToWasm(*instance, wasm_code->instruction_start());
804 : }
805 : break;
806 : }
807 : }
808 : return true;
809 : }
810 :
811 1508 : bool InstanceBuilder::ProcessImportedTable(Handle<WasmInstanceObject> instance,
812 : int import_index, int table_index,
813 : Handle<String> module_name,
814 : Handle<String> import_name,
815 : Handle<Object> value) {
816 1508 : if (!value->IsWasmTableObject()) {
817 336 : ReportLinkError("table import requires a WebAssembly.Table", import_index,
818 336 : module_name, import_name);
819 336 : return false;
820 : }
821 1172 : const WasmTable& table = module_->tables[table_index];
822 :
823 1172 : instance->tables()->set(table_index, *value);
824 : auto table_object = Handle<WasmTableObject>::cast(value);
825 :
826 : int imported_table_size = table_object->elements().length();
827 1172 : if (imported_table_size < static_cast<int>(table.initial_size)) {
828 40 : thrower_->LinkError("table import %d is smaller than initial %d, got %u",
829 40 : import_index, table.initial_size, imported_table_size);
830 40 : return false;
831 : }
832 :
833 1132 : if (table.has_maximum_size) {
834 1712 : if (table_object->maximum_length()->IsUndefined(isolate_)) {
835 16 : thrower_->LinkError("table import %d has no maximum length, expected %d",
836 32 : import_index, table.maximum_size);
837 16 : return false;
838 : }
839 840 : int64_t imported_maximum_size = table_object->maximum_length()->Number();
840 840 : if (imported_maximum_size < 0) {
841 0 : thrower_->LinkError("table import %d has no maximum length, expected %d",
842 0 : import_index, table.maximum_size);
843 0 : return false;
844 : }
845 840 : if (imported_maximum_size > table.maximum_size) {
846 48 : thrower_->LinkError("table import %d has a larger maximum size %" PRIx64
847 : " than the module's declared maximum %u",
848 : import_index, imported_maximum_size,
849 48 : table.maximum_size);
850 48 : return false;
851 : }
852 : }
853 :
854 : // Allocate a new dispatch table.
855 1068 : if (!instance->has_indirect_function_table()) {
856 1044 : WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize(
857 1044 : instance, imported_table_size);
858 : }
859 : // Initialize the dispatch table with the (foreign) JS functions
860 : // that are already in the table.
861 4220044 : for (int i = 0; i < imported_table_size; ++i) {
862 : bool is_valid;
863 : bool is_null;
864 2109488 : MaybeHandle<WasmInstanceObject> maybe_target_instance;
865 : int function_index;
866 2109488 : WasmTableObject::GetFunctionTableEntry(isolate_, table_object, i, &is_valid,
867 : &is_null, &maybe_target_instance,
868 2109488 : &function_index);
869 2109488 : if (!is_valid) {
870 0 : thrower_->LinkError("table import %d[%d] is not a wasm function",
871 0 : import_index, i);
872 0 : return false;
873 : }
874 4216316 : if (is_null) continue;
875 :
876 : Handle<WasmInstanceObject> target_instance =
877 : maybe_target_instance.ToHandleChecked();
878 5320 : FunctionSig* sig = target_instance->module_object()
879 : ->module()
880 2660 : ->functions[function_index]
881 2660 : .sig;
882 :
883 : // Look up the signature's canonical id. If there is no canonical
884 : // id, then the signature does not appear at all in this module,
885 : // so putting {-1} in the table will cause checks to always fail.
886 : IndirectFunctionTableEntry(instance, i)
887 5320 : .Set(module_->signature_map.Find(*sig), target_instance,
888 5320 : function_index);
889 : }
890 : return true;
891 : }
892 :
893 3865 : bool InstanceBuilder::ProcessImportedMemory(Handle<WasmInstanceObject> instance,
894 : int import_index,
895 : Handle<String> module_name,
896 : Handle<String> import_name,
897 : Handle<Object> value) {
898 : // Validation should have failed if more than one memory object was
899 : // provided.
900 : DCHECK(!instance->has_memory_object());
901 3865 : if (!value->IsWasmMemoryObject()) {
902 404 : ReportLinkError("memory import must be a WebAssembly.Memory object",
903 404 : import_index, module_name, import_name);
904 404 : return false;
905 : }
906 : auto memory = Handle<WasmMemoryObject>::cast(value);
907 3461 : instance->set_memory_object(*memory);
908 3461 : Handle<JSArrayBuffer> buffer(memory->array_buffer(), isolate_);
909 : // memory_ should have already been assigned in Build().
910 : DCHECK_EQ(*memory_.ToHandleChecked(), *buffer);
911 : uint32_t imported_cur_pages =
912 3462 : static_cast<uint32_t>(buffer->byte_length() / kWasmPageSize);
913 3462 : if (imported_cur_pages < module_->initial_pages) {
914 24 : thrower_->LinkError("memory import %d is smaller than initial %u, got %u",
915 : import_index, module_->initial_pages,
916 24 : imported_cur_pages);
917 24 : return false;
918 : }
919 : int32_t imported_maximum_pages = memory->maximum_pages();
920 3438 : if (module_->has_maximum_pages) {
921 1454 : if (imported_maximum_pages < 0) {
922 8 : thrower_->LinkError(
923 : "memory import %d has no maximum limit, expected at most %u",
924 8 : import_index, imported_maximum_pages);
925 8 : return false;
926 : }
927 1446 : if (static_cast<uint32_t>(imported_maximum_pages) >
928 1446 : module_->maximum_pages) {
929 40 : thrower_->LinkError(
930 : "memory import %d has a larger maximum size %u than the "
931 : "module's declared maximum %u",
932 40 : import_index, imported_maximum_pages, module_->maximum_pages);
933 40 : return false;
934 : }
935 : }
936 6780 : if (module_->has_shared_memory != buffer->is_shared()) {
937 16 : thrower_->LinkError(
938 : "mismatch in shared state of memory, declared = %d, imported = %d",
939 16 : module_->has_shared_memory, buffer->is_shared());
940 16 : return false;
941 : }
942 :
943 : return true;
944 : }
945 :
946 436 : bool InstanceBuilder::ProcessImportedWasmGlobalObject(
947 : Handle<WasmInstanceObject> instance, int import_index,
948 : Handle<String> module_name, Handle<String> import_name,
949 : const WasmGlobal& global, Handle<WasmGlobalObject> global_object) {
950 436 : if (global_object->type() != global.type) {
951 8 : ReportLinkError("imported global does not match the expected type",
952 8 : import_index, module_name, import_name);
953 8 : return false;
954 : }
955 428 : if (global_object->is_mutable() != global.mutability) {
956 24 : ReportLinkError("imported global does not match the expected mutability",
957 24 : import_index, module_name, import_name);
958 24 : return false;
959 : }
960 404 : if (global.mutability) {
961 : DCHECK_LT(global.index, module_->num_imported_mutable_globals);
962 : Handle<Object> buffer;
963 : Address address_or_offset;
964 364 : if (ValueTypes::IsReferenceType(global.type)) {
965 : static_assert(sizeof(global_object->offset()) <= sizeof(Address),
966 : "The offset into the globals buffer does not fit into "
967 : "the imported_mutable_globals array");
968 224 : buffer = handle(global_object->tagged_buffer(), isolate_);
969 : // For anyref globals we use a relative offset, not an absolute address.
970 224 : address_or_offset = static_cast<Address>(global_object->offset());
971 : } else {
972 140 : buffer = handle(global_object->untagged_buffer(), isolate_);
973 : // It is safe in this case to store the raw pointer to the buffer
974 : // since the backing store of the JSArrayBuffer will not be
975 : // relocated.
976 : address_or_offset = reinterpret_cast<Address>(raw_buffer_ptr(
977 140 : Handle<JSArrayBuffer>::cast(buffer), global_object->offset()));
978 : }
979 728 : instance->imported_mutable_globals_buffers()->set(global.index, *buffer);
980 364 : instance->imported_mutable_globals()[global.index] = address_or_offset;
981 : return true;
982 : }
983 :
984 40 : WriteGlobalValue(global, global_object);
985 40 : return true;
986 : }
987 :
988 3244 : bool InstanceBuilder::ProcessImportedGlobal(Handle<WasmInstanceObject> instance,
989 : int import_index, int global_index,
990 : Handle<String> module_name,
991 : Handle<String> import_name,
992 : Handle<Object> value) {
993 : // Immutable global imports are converted to numbers and written into
994 : // the {untagged_globals_} array buffer.
995 : //
996 : // Mutable global imports instead have their backing array buffers
997 : // referenced by this instance, and store the address of the imported
998 : // global in the {imported_mutable_globals_} array.
999 3244 : const WasmGlobal& global = module_->globals[global_index];
1000 :
1001 : // The mutable-global proposal allows importing i64 values, but only if
1002 : // they are passed as a WebAssembly.Global object.
1003 : //
1004 : // However, the bigint proposal allows importing constant i64 values,
1005 : // as non WebAssembly.Global object.
1006 3292 : if (global.type == kWasmI64 && !enabled_.bigint &&
1007 : !value->IsWasmGlobalObject()) {
1008 32 : ReportLinkError("global import cannot have type i64", import_index,
1009 32 : module_name, import_name);
1010 32 : return false;
1011 : }
1012 3212 : if (module_->origin == kAsmJsOrigin) {
1013 : // Accepting {JSFunction} on top of just primitive values here is a
1014 : // workaround to support legacy asm.js code with broken binding. Note
1015 : // that using {NaN} (or Smi::kZero) here is what using the observable
1016 : // conversion via {ToPrimitive} would produce as well.
1017 : // TODO(mstarzinger): Still observable if Function.prototype.valueOf
1018 : // or friends are patched, we might need to check for that as well.
1019 1208 : if (value->IsJSFunction()) value = isolate_->factory()->nan_value();
1020 2407 : if (value->IsPrimitive() && !value->IsSymbol()) {
1021 1195 : if (global.type == kWasmI32) {
1022 855 : value = Object::ToInt32(isolate_, value).ToHandleChecked();
1023 : } else {
1024 680 : value = Object::ToNumber(isolate_, value).ToHandleChecked();
1025 : }
1026 : }
1027 : }
1028 :
1029 3212 : if (value->IsWasmGlobalObject()) {
1030 436 : auto global_object = Handle<WasmGlobalObject>::cast(value);
1031 : return ProcessImportedWasmGlobalObject(instance, import_index, module_name,
1032 436 : import_name, global, global_object);
1033 : }
1034 :
1035 2776 : if (global.mutability) {
1036 16 : ReportLinkError(
1037 : "imported mutable global must be a WebAssembly.Global object",
1038 16 : import_index, module_name, import_name);
1039 16 : return false;
1040 : }
1041 :
1042 5520 : if (ValueTypes::IsReferenceType(global.type)) {
1043 : // There shouldn't be any null-ref globals.
1044 : DCHECK_NE(ValueType::kWasmNullRef, global.type);
1045 176 : if (global.type == ValueType::kWasmAnyFunc) {
1046 128 : if (!value->IsNull(isolate_) &&
1047 32 : !WasmExportedFunction::IsWasmExportedFunction(*value)) {
1048 16 : ReportLinkError(
1049 : "imported anyfunc global must be null or an exported function",
1050 16 : import_index, module_name, import_name);
1051 16 : return false;
1052 : }
1053 : }
1054 : WriteGlobalAnyRef(global, value);
1055 160 : return true;
1056 : }
1057 :
1058 2584 : if (value->IsNumber() && global.type != kWasmI64) {
1059 2219 : WriteGlobalValue(global, value->Number());
1060 2219 : return true;
1061 : }
1062 :
1063 365 : if (enabled_.bigint && global.type == kWasmI64) {
1064 : Handle<BigInt> bigint;
1065 :
1066 48 : if (!BigInt::FromObject(isolate_, value).ToHandle(&bigint)) {
1067 : return false;
1068 : }
1069 24 : WriteGlobalValue(global, bigint->AsInt64());
1070 24 : return true;
1071 : }
1072 :
1073 341 : ReportLinkError("global import must be a number or WebAssembly.Global object",
1074 341 : import_index, module_name, import_name);
1075 341 : return false;
1076 : }
1077 :
1078 : // Process the imports, including functions, tables, globals, and memory, in
1079 : // order, loading them from the {ffi_} object. Returns the number of imported
1080 : // functions.
1081 135955 : int InstanceBuilder::ProcessImports(Handle<WasmInstanceObject> instance) {
1082 : int num_imported_functions = 0;
1083 : int num_imported_tables = 0;
1084 :
1085 : DCHECK_EQ(module_->import_table.size(), sanitized_imports_.size());
1086 271910 : int num_imports = static_cast<int>(module_->import_table.size());
1087 386313 : for (int index = 0; index < num_imports; ++index) {
1088 127139 : const WasmImport& import = module_->import_table[index];
1089 :
1090 127139 : Handle<String> module_name = sanitized_imports_[index].module_name;
1091 127139 : Handle<String> import_name = sanitized_imports_[index].import_name;
1092 127139 : Handle<Object> value = sanitized_imports_[index].value;
1093 :
1094 127139 : switch (import.kind) {
1095 : case kExternalFunction: {
1096 118437 : uint32_t func_index = import.index;
1097 : DCHECK_EQ(num_imported_functions, func_index);
1098 118437 : if (!ProcessImportedFunction(instance, index, func_index, module_name,
1099 : import_name, value)) {
1100 1961 : return -1;
1101 : }
1102 117885 : num_imported_functions++;
1103 117885 : break;
1104 : }
1105 : case kExternalTable: {
1106 1508 : uint32_t table_index = import.index;
1107 : DCHECK_EQ(table_index, num_imported_tables);
1108 1508 : if (!ProcessImportedTable(instance, index, table_index, module_name,
1109 : import_name, value)) {
1110 : return -1;
1111 : }
1112 : num_imported_tables++;
1113 : break;
1114 : }
1115 : case kExternalMemory: {
1116 3862 : if (!ProcessImportedMemory(instance, index, module_name, import_name,
1117 : value)) {
1118 : return -1;
1119 : }
1120 : break;
1121 : }
1122 : case kExternalGlobal: {
1123 3244 : if (!ProcessImportedGlobal(instance, index, import.index, module_name,
1124 : import_name, value)) {
1125 : return -1;
1126 : }
1127 : break;
1128 : }
1129 : case kExternalException: {
1130 88 : if (!value->IsWasmExceptionObject()) {
1131 32 : ReportLinkError("exception import requires a WebAssembly.Exception",
1132 32 : index, module_name, import_name);
1133 32 : return -1;
1134 : }
1135 : Handle<WasmExceptionObject> imported_exception =
1136 : Handle<WasmExceptionObject>::cast(value);
1137 56 : if (!imported_exception->IsSignatureEqual(
1138 112 : module_->exceptions[import.index].sig)) {
1139 8 : ReportLinkError("imported exception does not match the expected type",
1140 8 : index, module_name, import_name);
1141 8 : return -1;
1142 : }
1143 48 : Object exception_tag = imported_exception->exception_tag();
1144 : DCHECK(instance->exceptions_table()->get(import.index)->IsUndefined());
1145 96 : instance->exceptions_table()->set(import.index, exception_tag);
1146 96 : exception_wrappers_[import.index] = imported_exception;
1147 48 : break;
1148 : }
1149 : default:
1150 0 : UNREACHABLE();
1151 : break;
1152 : }
1153 : }
1154 : return num_imported_functions;
1155 : }
1156 :
1157 : template <typename T>
1158 8158 : T* InstanceBuilder::GetRawGlobalPtr(const WasmGlobal& global) {
1159 16316 : return reinterpret_cast<T*>(raw_buffer_ptr(untagged_globals_, global.offset));
1160 : }
1161 :
1162 : // Process initialization of globals.
1163 133995 : void InstanceBuilder::InitGlobals() {
1164 133995 : for (auto global : module_->globals) {
1165 9214 : if (global.mutability && global.imported) {
1166 : continue;
1167 : }
1168 :
1169 8850 : switch (global.init.kind) {
1170 : case WasmInitExpr::kI32Const:
1171 4745 : WriteLittleEndianValue<int32_t>(GetRawGlobalPtr<int32_t>(global),
1172 : global.init.val.i32_const);
1173 : break;
1174 : case WasmInitExpr::kI64Const:
1175 88 : WriteLittleEndianValue<int64_t>(GetRawGlobalPtr<int64_t>(global),
1176 : global.init.val.i64_const);
1177 : break;
1178 : case WasmInitExpr::kF32Const:
1179 188 : WriteLittleEndianValue<float>(GetRawGlobalPtr<float>(global),
1180 : global.init.val.f32_const);
1181 : break;
1182 : case WasmInitExpr::kF64Const:
1183 854 : WriteLittleEndianValue<double>(GetRawGlobalPtr<double>(global),
1184 : global.init.val.f64_const);
1185 : break;
1186 : case WasmInitExpr::kRefNullConst:
1187 : DCHECK(enabled_.anyref || enabled_.eh);
1188 464 : if (global.imported) break; // We already initialized imported globals.
1189 :
1190 1392 : tagged_globals_->set(global.offset,
1191 1392 : ReadOnlyRoots(isolate_).null_value(),
1192 464 : SKIP_WRITE_BARRIER);
1193 464 : break;
1194 : case WasmInitExpr::kGlobalIndex: {
1195 : // Initialize with another global.
1196 : uint32_t new_offset = global.offset;
1197 : uint32_t old_offset =
1198 136 : module_->globals[global.init.val.global_index].offset;
1199 : TRACE("init [globals+%u] = [globals+%d]\n", global.offset, old_offset);
1200 68 : if (ValueTypes::IsReferenceType(global.type)) {
1201 : DCHECK(enabled_.anyref || enabled_.eh);
1202 48 : tagged_globals_->set(new_offset, tagged_globals_->get(old_offset));
1203 : } else {
1204 52 : size_t size = (global.type == kWasmI64 || global.type == kWasmF64)
1205 : ? sizeof(double)
1206 52 : : sizeof(int32_t);
1207 52 : memcpy(raw_buffer_ptr(untagged_globals_, new_offset),
1208 52 : raw_buffer_ptr(untagged_globals_, old_offset), size);
1209 : }
1210 : break;
1211 : }
1212 : case WasmInitExpr::kNone:
1213 : // Happens with imported globals.
1214 : break;
1215 : default:
1216 0 : UNREACHABLE();
1217 : break;
1218 : }
1219 : }
1220 133995 : }
1221 :
1222 : // Allocate memory for a module instance as a new JSArrayBuffer.
1223 131211 : Handle<JSArrayBuffer> InstanceBuilder::AllocateMemory(uint32_t initial_pages,
1224 : uint32_t maximum_pages) {
1225 131211 : if (initial_pages > max_mem_pages()) {
1226 16 : thrower_->RangeError("Out of memory: wasm memory too large");
1227 : return Handle<JSArrayBuffer>::null();
1228 : }
1229 131195 : const bool is_shared_memory = module_->has_shared_memory && enabled_.threads;
1230 : Handle<JSArrayBuffer> mem_buffer;
1231 131195 : if (is_shared_memory) {
1232 512 : if (!NewSharedArrayBuffer(isolate_, initial_pages * kWasmPageSize,
1233 256 : maximum_pages * kWasmPageSize)
1234 : .ToHandle(&mem_buffer)) {
1235 0 : thrower_->RangeError("Out of memory: wasm shared memory");
1236 : }
1237 : } else {
1238 261878 : if (!NewArrayBuffer(isolate_, initial_pages * kWasmPageSize)
1239 : .ToHandle(&mem_buffer)) {
1240 0 : thrower_->RangeError("Out of memory: wasm memory");
1241 : }
1242 : }
1243 131195 : return mem_buffer;
1244 : }
1245 :
1246 0 : bool InstanceBuilder::NeedsWrappers() const {
1247 133995 : if (module_->num_exported_functions > 0) return true;
1248 4556 : for (auto& table : module_->tables) {
1249 900 : if (table.type == kWasmAnyFunc) return true;
1250 : }
1251 : return false;
1252 : }
1253 :
1254 : // Process the exports, creating wrappers for functions, tables, memories,
1255 : // globals, and exceptions.
1256 133996 : void InstanceBuilder::ProcessExports(Handle<WasmInstanceObject> instance) {
1257 : Handle<FixedArray> export_wrappers(module_object_->export_wrappers(),
1258 133996 : isolate_);
1259 133995 : if (NeedsWrappers()) {
1260 : // If an imported WebAssembly function gets exported, the exported function
1261 : // has to be identical to to imported function. Therefore we cache all
1262 : // imported WebAssembly functions in the instance.
1263 254679 : for (int index = 0, end = static_cast<int>(module_->import_table.size());
1264 254679 : index < end; ++index) {
1265 124340 : const WasmImport& import = module_->import_table[index];
1266 124340 : if (import.kind == kExternalFunction) {
1267 117605 : Handle<Object> value = sanitized_imports_[index].value;
1268 117605 : if (WasmExportedFunction::IsWasmExportedFunction(*value)) {
1269 107024 : WasmInstanceObject::SetWasmExportedFunction(
1270 107024 : isolate_, instance, import.index,
1271 107024 : Handle<WasmExportedFunction>::cast(value));
1272 : }
1273 : }
1274 : }
1275 : }
1276 :
1277 : Handle<JSObject> exports_object;
1278 : bool is_asm_js = false;
1279 133995 : switch (module_->origin) {
1280 : case kWasmOrigin: {
1281 : // Create the "exports" object.
1282 129144 : exports_object = isolate_->factory()->NewJSObjectWithNullProto();
1283 129147 : break;
1284 : }
1285 : case kAsmJsOrigin: {
1286 : Handle<JSFunction> object_function = Handle<JSFunction>(
1287 14553 : isolate_->native_context()->object_function(), isolate_);
1288 4851 : exports_object = isolate_->factory()->NewJSObject(object_function);
1289 : is_asm_js = true;
1290 : break;
1291 : }
1292 : default:
1293 0 : UNREACHABLE();
1294 : }
1295 133998 : instance->set_exports_object(*exports_object);
1296 :
1297 : Handle<String> single_function_name =
1298 133997 : isolate_->factory()->InternalizeUtf8String(AsmJs::kSingleFunctionName);
1299 :
1300 : PropertyDescriptor desc;
1301 : desc.set_writable(is_asm_js);
1302 : desc.set_enumerable(true);
1303 : desc.set_configurable(is_asm_js);
1304 :
1305 : // Process each export in the export table.
1306 : int export_index = 0; // Index into {export_wrappers}.
1307 133995 : for (const WasmExport& exp : module_->export_table) {
1308 459151 : Handle<String> name = WasmModuleObject::ExtractUtf8StringFromModuleBytes(
1309 229575 : isolate_, module_object_, exp.name)
1310 : .ToHandleChecked();
1311 : Handle<JSObject> export_to;
1312 237822 : if (is_asm_js && exp.kind == kExternalFunction &&
1313 8246 : String::Equals(isolate_, name, single_function_name)) {
1314 : export_to = instance;
1315 : } else {
1316 : export_to = exports_object;
1317 : }
1318 :
1319 229576 : switch (exp.kind) {
1320 : case kExternalFunction: {
1321 : // Wrap and export the code as a JSFunction.
1322 : // TODO(wasm): reduce duplication with LoadElemSegment() further below
1323 226448 : const WasmFunction& function = module_->functions[exp.index];
1324 : MaybeHandle<WasmExportedFunction> wasm_exported_function =
1325 : WasmInstanceObject::GetWasmExportedFunction(isolate_, instance,
1326 226448 : exp.index);
1327 226447 : if (wasm_exported_function.is_null()) {
1328 : // Wrap the exported code as a JSFunction.
1329 : Handle<Code> export_code =
1330 452366 : export_wrappers->GetValueChecked<Code>(isolate_, export_index);
1331 226185 : MaybeHandle<String> func_name;
1332 226185 : if (is_asm_js) {
1333 : // For modules arising from asm.js, honor the names section.
1334 8226 : WireBytesRef func_name_ref = module_->LookupFunctionName(
1335 : ModuleWireBytes(module_object_->native_module()->wire_bytes()),
1336 24678 : function.func_index);
1337 16452 : func_name = WasmModuleObject::ExtractUtf8StringFromModuleBytes(
1338 8226 : isolate_, module_object_, func_name_ref)
1339 8226 : .ToHandleChecked();
1340 : }
1341 : wasm_exported_function = WasmExportedFunction::New(
1342 226185 : isolate_, instance, func_name, function.func_index,
1343 452370 : static_cast<int>(function.sig->parameter_count()), export_code);
1344 226184 : WasmInstanceObject::SetWasmExportedFunction(
1345 226184 : isolate_, instance, exp.index,
1346 226184 : wasm_exported_function.ToHandleChecked());
1347 : }
1348 : desc.set_value(wasm_exported_function.ToHandleChecked());
1349 226451 : export_index++;
1350 : break;
1351 : }
1352 : case kExternalTable: {
1353 540 : desc.set_value(handle(instance->tables()->get(exp.index), isolate_));
1354 540 : break;
1355 : }
1356 : case kExternalMemory: {
1357 : // Export the memory as a WebAssembly.Memory object. A WasmMemoryObject
1358 : // should already be available if the module has memory, since we always
1359 : // create or import it when building an WasmInstanceObject.
1360 : DCHECK(instance->has_memory_object());
1361 1640 : desc.set_value(
1362 : Handle<WasmMemoryObject>(instance->memory_object(), isolate_));
1363 1640 : break;
1364 : }
1365 : case kExternalGlobal: {
1366 836 : const WasmGlobal& global = module_->globals[exp.index];
1367 : Handle<JSArrayBuffer> untagged_buffer;
1368 : Handle<FixedArray> tagged_buffer;
1369 : uint32_t offset;
1370 :
1371 836 : if (global.mutability && global.imported) {
1372 : Handle<FixedArray> buffers_array(
1373 148 : instance->imported_mutable_globals_buffers(), isolate_);
1374 296 : if (ValueTypes::IsReferenceType(global.type)) {
1375 : tagged_buffer = buffers_array->GetValueChecked<FixedArray>(
1376 192 : isolate_, global.index);
1377 : // For anyref globals we store the relative offset in the
1378 : // imported_mutable_globals array instead of an absolute address.
1379 96 : Address addr = instance->imported_mutable_globals()[global.index];
1380 : DCHECK_LE(addr, static_cast<Address>(
1381 : std::numeric_limits<uint32_t>::max()));
1382 96 : offset = static_cast<uint32_t>(addr);
1383 : } else {
1384 : untagged_buffer = buffers_array->GetValueChecked<JSArrayBuffer>(
1385 104 : isolate_, global.index);
1386 : Address global_addr =
1387 52 : instance->imported_mutable_globals()[global.index];
1388 :
1389 : size_t buffer_size = untagged_buffer->byte_length();
1390 : Address backing_store =
1391 52 : reinterpret_cast<Address>(untagged_buffer->backing_store());
1392 52 : CHECK(global_addr >= backing_store &&
1393 : global_addr < backing_store + buffer_size);
1394 52 : offset = static_cast<uint32_t>(global_addr - backing_store);
1395 : }
1396 : } else {
1397 1376 : if (ValueTypes::IsReferenceType(global.type)) {
1398 208 : tagged_buffer = handle(instance->tagged_globals_buffer(), isolate_);
1399 : } else {
1400 : untagged_buffer =
1401 480 : handle(instance->untagged_globals_buffer(), isolate_);
1402 : }
1403 688 : offset = global.offset;
1404 : }
1405 :
1406 : // Since the global's array untagged_buffer is always provided,
1407 : // allocation should never fail.
1408 : Handle<WasmGlobalObject> global_obj =
1409 1672 : WasmGlobalObject::New(isolate_, untagged_buffer, tagged_buffer,
1410 2508 : global.type, offset, global.mutability)
1411 : .ToHandleChecked();
1412 : desc.set_value(global_obj);
1413 : break;
1414 : }
1415 : case kExternalException: {
1416 112 : const WasmException& exception = module_->exceptions[exp.index];
1417 112 : Handle<WasmExceptionObject> wrapper = exception_wrappers_[exp.index];
1418 112 : if (wrapper.is_null()) {
1419 : Handle<HeapObject> exception_tag(
1420 : HeapObject::cast(instance->exceptions_table()->get(exp.index)),
1421 96 : isolate_);
1422 : wrapper =
1423 96 : WasmExceptionObject::New(isolate_, exception.sig, exception_tag);
1424 192 : exception_wrappers_[exp.index] = wrapper;
1425 : }
1426 : desc.set_value(wrapper);
1427 : break;
1428 : }
1429 : default:
1430 0 : UNREACHABLE();
1431 : break;
1432 : }
1433 :
1434 : v8::Maybe<bool> status = JSReceiver::DefineOwnProperty(
1435 229579 : isolate_, export_to, name, &desc, Just(kThrowOnError));
1436 229573 : if (!status.IsJust()) {
1437 : DisallowHeapAllocation no_gc;
1438 0 : TruncatedUserString<> trunc_name(name->GetCharVector<uint8_t>(no_gc));
1439 0 : thrower_->LinkError("export of %.*s failed.", trunc_name.length(),
1440 0 : trunc_name.start());
1441 : return;
1442 : }
1443 : }
1444 : DCHECK_EQ(export_index, export_wrappers->length());
1445 :
1446 133993 : if (module_->origin == kWasmOrigin) {
1447 : v8::Maybe<bool> success =
1448 129142 : JSReceiver::SetIntegrityLevel(exports_object, FROZEN, kDontThrow);
1449 : DCHECK(success.FromMaybe(false));
1450 : USE(success);
1451 : }
1452 : }
1453 :
1454 2345 : void InstanceBuilder::InitializeTables(Handle<WasmInstanceObject> instance) {
1455 2345 : size_t table_count = module_->tables.size();
1456 7211 : for (size_t index = 0; index < table_count; ++index) {
1457 2433 : const WasmTable& table = module_->tables[index];
1458 :
1459 3746 : if (!instance->has_indirect_function_table() &&
1460 1313 : table.type == kWasmAnyFunc) {
1461 : WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize(
1462 1313 : instance, table.initial_size);
1463 : }
1464 : }
1465 2345 : }
1466 :
1467 2445 : bool LoadElemSegmentImpl(Isolate* isolate, Handle<WasmInstanceObject> instance,
1468 : Handle<WasmTableObject> table_object,
1469 : const WasmElemSegment& elem_segment, uint32_t dst,
1470 : uint32_t src, size_t count) {
1471 : // TODO(wasm): Move this functionality into wasm-objects, since it is used
1472 : // for both instantiation and in the implementation of the table.init
1473 : // instruction.
1474 : bool ok =
1475 2445 : ClampToBounds<size_t>(dst, &count, table_object->elements()->length());
1476 : // Use & instead of && so the clamp is not short-circuited.
1477 2445 : ok &= ClampToBounds<size_t>(src, &count, elem_segment.entries.size());
1478 :
1479 2445 : const WasmModule* module = instance->module();
1480 22727 : for (size_t i = 0; i < count; ++i) {
1481 20282 : uint32_t func_index = elem_segment.entries[src + i];
1482 10141 : int entry_index = static_cast<int>(dst + i);
1483 :
1484 10141 : if (func_index == WasmElemSegment::kNullIndex) {
1485 0 : IndirectFunctionTableEntry(instance, entry_index).clear();
1486 0 : WasmTableObject::Set(isolate, table_object, entry_index,
1487 0 : isolate->factory()->null_value());
1488 : continue;
1489 : }
1490 :
1491 10141 : const WasmFunction* function = &module->functions[func_index];
1492 :
1493 : // Update the local dispatch table first.
1494 20282 : uint32_t sig_id = module->signature_ids[function->sig_index];
1495 : IndirectFunctionTableEntry(instance, entry_index)
1496 20282 : .Set(sig_id, instance, func_index);
1497 :
1498 : // Update the table object's other dispatch tables.
1499 : MaybeHandle<WasmExportedFunction> wasm_exported_function =
1500 : WasmInstanceObject::GetWasmExportedFunction(isolate, instance,
1501 10141 : func_index);
1502 10141 : if (wasm_exported_function.is_null()) {
1503 : // No JSFunction entry yet exists for this function. Create a {Tuple2}
1504 : // holding the information to lazily allocate one.
1505 : WasmTableObject::SetFunctionTablePlaceholder(
1506 9489 : isolate, table_object, entry_index, instance, func_index);
1507 : } else {
1508 1956 : table_object->elements()->set(entry_index,
1509 652 : *wasm_exported_function.ToHandleChecked());
1510 : }
1511 : // UpdateDispatchTables() updates all other dispatch tables, since
1512 : // we have not yet added the dispatch table we are currently building.
1513 : WasmTableObject::UpdateDispatchTables(isolate, table_object, entry_index,
1514 10141 : function->sig, instance, func_index);
1515 : }
1516 2445 : return ok;
1517 : }
1518 :
1519 2345 : void InstanceBuilder::LoadTableSegments(Handle<WasmInstanceObject> instance) {
1520 2345 : for (auto& elem_segment : module_->elem_segments) {
1521 : // Passive segments are not copied during instantiation.
1522 2125 : if (!elem_segment.active) continue;
1523 :
1524 2109 : uint32_t dst = EvalUint32InitExpr(instance, elem_segment.offset);
1525 : uint32_t src = 0;
1526 : size_t count = elem_segment.entries.size();
1527 :
1528 6327 : bool success = LoadElemSegmentImpl(
1529 : isolate_, instance,
1530 : handle(WasmTableObject::cast(
1531 2109 : instance->tables()->get(elem_segment.table_index)),
1532 : isolate_),
1533 2109 : elem_segment, dst, src, count);
1534 2109 : if (enabled_.bulk_memory) {
1535 2008 : if (!success) {
1536 120 : thrower_->LinkError("table initializer is out of bounds");
1537 : // Break out instead of returning; we don't want to continue to
1538 : // initialize any further element segments, but still need to add
1539 : // dispatch tables below.
1540 120 : break;
1541 : }
1542 : } else {
1543 101 : CHECK(success);
1544 : }
1545 : }
1546 :
1547 4690 : int table_count = static_cast<int>(module_->tables.size());
1548 7211 : for (int index = 0; index < table_count; ++index) {
1549 4866 : if (module_->tables[index].type == kWasmAnyFunc) {
1550 : auto table_object = handle(
1551 4834 : WasmTableObject::cast(instance->tables()->get(index)), isolate_);
1552 :
1553 : // Add the new dispatch table at the end to avoid redundant lookups.
1554 2417 : WasmTableObject::AddDispatchTable(isolate_, table_object, instance,
1555 2417 : index);
1556 : }
1557 : }
1558 2345 : }
1559 :
1560 576 : void InstanceBuilder::InitializeExceptions(
1561 : Handle<WasmInstanceObject> instance) {
1562 576 : Handle<FixedArray> exceptions_table(instance->exceptions_table(), isolate_);
1563 1984 : for (int index = 0; index < exceptions_table->length(); ++index) {
1564 1456 : if (!exceptions_table->get(index)->IsUndefined(isolate_)) continue;
1565 : Handle<WasmExceptionTag> exception_tag =
1566 656 : WasmExceptionTag::New(isolate_, index);
1567 1312 : exceptions_table->set(index, *exception_tag);
1568 : }
1569 576 : }
1570 :
1571 336 : bool LoadElemSegment(Isolate* isolate, Handle<WasmInstanceObject> instance,
1572 : uint32_t table_index, uint32_t segment_index, uint32_t dst,
1573 : uint32_t src, uint32_t count) {
1574 1008 : auto& elem_segment = instance->module()->elem_segments[segment_index];
1575 336 : return LoadElemSegmentImpl(
1576 : isolate, instance,
1577 : handle(WasmTableObject::cast(instance->tables()->get(table_index)),
1578 : isolate),
1579 336 : elem_segment, dst, src, count);
1580 : }
1581 :
1582 : } // namespace wasm
1583 : } // namespace internal
1584 121996 : } // namespace v8
1585 :
1586 : #undef TRACE
|