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