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