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