diff --git a/pkg/dart2wasm/lib/compile.dart b/pkg/dart2wasm/lib/compile.dart index abeeedc1452..0b4456e9db9 100644 --- a/pkg/dart2wasm/lib/compile.dart +++ b/pkg/dart2wasm/lib/compile.dart @@ -40,6 +40,7 @@ import 'compiler_options.dart' as compiler; import 'constant_evaluator.dart'; import 'deferred_loading.dart'; import 'dry_run.dart'; +import 'generate_wasm.dart'; import 'io_util.dart'; import 'js/runtime_generator.dart' as js; import 'modules.dart'; @@ -666,7 +667,7 @@ Future _runCodegenPhase( final wasmOutputFilename = path.basename(options.outputFile); final moduleIds = modules.keys .map( - (moduleMetadata) => options.idForModuleName( + (moduleMetadata) => WasmCompilerOptions.idForModuleName( wasmOutputFilename, moduleMetadata.moduleName, )!, diff --git a/pkg/dart2wasm/lib/compiler_options.dart b/pkg/dart2wasm/lib/compiler_options.dart index 9642d0d77e3..e92c61a0044 100644 --- a/pkg/dart2wasm/lib/compiler_options.dart +++ b/pkg/dart2wasm/lib/compiler_options.dart @@ -75,12 +75,19 @@ class WasmCompilerOptions { translatorOptions.enableDeferredLoading || translatorOptions.enableMultiModuleStressTestMode; - String moduleNameForId(String filePath, int id, {bool emitAsMain = false}) => - emitAsMain || id == mainModuleId - ? path.basename(filePath) - : path.basename(path.setExtension(filePath, '_module$id.wasm')); + static String moduleNameForId( + String filePath, + int id, { + bool emitAsMain = false, + }) { + final basename = path.basename(filePath); + if (emitAsMain || id == mainModuleId) { + return basename; + } + return '${deferredModuleFilenamePrefix(basename)}$id.wasm'; + } - int? idForModuleName(String mainWasmFilename, String moduleFilename) { + static int? idForModuleName(String mainWasmFilename, String moduleFilename) { assert( mainWasmFilename.endsWith('.wasm') && !mainWasmFilename.contains(path.separator), @@ -93,14 +100,28 @@ class WasmCompilerOptions { !moduleFilename.endsWith('.wasm')) { return null; } - return int.tryParse( - moduleFilename.substring( - prefix.length, - moduleFilename.length - '.wasm'.length, - ), + return idFromDeferredModuleFilename(moduleFilename); + } + + /// Given a deferred module filename returns the module id. + /// + /// (i.e. returns `` for `test_module.wasm`). + static int idFromDeferredModuleFilename(String moduleName) { + // The name has pattern: "..._module.wasm" + assert(moduleName.endsWith('.wasm') && moduleName.contains('_module')); + final offset = moduleName.lastIndexOf('_module') + '_module'.length; + return int.parse( + moduleName.substring(offset, moduleName.length - '.wasm'.length), ); } + /// The prefix of all deferred module filenames. + /// + /// (i.e. returns `test_module` for main module `test.wasm` and + /// deferred modules `test_module.wasm`). + static String deferredModuleFilenamePrefix(String mainModuleFilename) => + path.basename(path.setExtension(mainModuleFilename, '_module')); + static int _defaultMaxActiveWasmOptProcesses() { try { return Platform.numberOfProcessors; diff --git a/pkg/dart2wasm/lib/io_util.dart b/pkg/dart2wasm/lib/io_util.dart index 9f470382102..5209fc19e7e 100644 --- a/pkg/dart2wasm/lib/io_util.dart +++ b/pkg/dart2wasm/lib/io_util.dart @@ -135,9 +135,12 @@ class CompilerPhaseInputOutputManager { int moduleId, List flags, ) async { - final inputModuleName = options.moduleNameForId(mainWasmModule, moduleId); + final inputModuleName = WasmCompilerOptions.moduleNameForId( + mainWasmModule, + moduleId, + ); - final outputModuleName = options.moduleNameForId( + final outputModuleName = WasmCompilerOptions.moduleNameForId( options.outputFile, moduleId, ); @@ -224,7 +227,7 @@ class CompilerPhaseInputOutputManager { final moduleIds = {}; for (final file in files) { if (file is! File) continue; - final moduleId = options.idForModuleName( + final moduleId = WasmCompilerOptions.idForModuleName( mainWasmFilename, path.basename(file.path), ); diff --git a/pkg/dart2wasm/lib/kernel_nodes.dart b/pkg/dart2wasm/lib/kernel_nodes.dart index d72abd0fd9b..fc089b429ca 100644 --- a/pkg/dart2wasm/lib/kernel_nodes.dart +++ b/pkg/dart2wasm/lib/kernel_nodes.dart @@ -774,6 +774,12 @@ mixin KernelNodes { LibraryIndex.topLevel, 'get:_loadingMap', ); + late final Procedure? dartInternalModuleNamePrefixGetter = index + .tryGetProcedure( + 'dart:_internal', + LibraryIndex.topLevel, + 'get:_moduleNamePrefix', + ); late final Procedure? dartInternalLoadingMapNamesGetter = index .tryGetProcedure( 'dart:_internal', diff --git a/pkg/dart2wasm/lib/modules.dart b/pkg/dart2wasm/lib/modules.dart index 89d4ec0087b..cbb43298b14 100644 --- a/pkg/dart2wasm/lib/modules.dart +++ b/pkg/dart2wasm/lib/modules.dart @@ -37,7 +37,11 @@ class ModuleMetadataBuilder { : 'module$id'; return ModuleMetadata._( moduleImportName, - options.moduleNameForId(options.outputFile, id, emitAsMain: emitAsMain), + WasmCompilerOptions.moduleNameForId( + options.outputFile, + id, + emitAsMain: emitAsMain, + ), skipEmit: skipEmit, isMain: id == WasmCompilerOptions.mainModuleId, ); diff --git a/pkg/dart2wasm/lib/translator.dart b/pkg/dart2wasm/lib/translator.dart index 06ac6521be6..728a7470474 100644 --- a/pkg/dart2wasm/lib/translator.dart +++ b/pkg/dart2wasm/lib/translator.dart @@ -28,6 +28,7 @@ import 'dispatch_table.dart'; import 'dynamic_dispatch_table.dart'; import 'dynamic_dispatchers.dart'; import 'functions.dart'; +import 'generate_wasm.dart'; import 'globals.dart'; import 'kernel_nodes.dart'; import 'modules.dart'; @@ -645,40 +646,85 @@ class Translator with KernelNodes { // NOTE: We do this after code generation is complete. So the code generation // phase has the opportunity to generate more wasm modules and add them to the // loading map. + // + // Keep in sync with sdk/lib/_internal/wasm/js_common/deferred_patch.dart's + // `_decodeEncodedModuleIds` and `_loadLibraryViaEmbedderModuleNames` void _patchLoadingMapGetter(w.FunctionBuilder function) { - final externRef = w.RefType.extern(nullable: false); - final arrayExternRef = wasmArrayType( - externRef, - externRef.toString(), - mutable: false, - ); - final arrayArrayString = wasmArrayType( - w.RefType(arrayExternRef, nullable: false), - arrayExternRef.toString(), - mutable: false, + final moduleMap = loadingMap.moduleMap; + final byteArrayType = wasmArrayType(w.PackedType.i8, 'WasmI8'); + final arrayOfNullableByteArray = wasmArrayType( + w.RefType(byteArrayType, nullable: true), + 'WasmArray', ); - _lazyInitializeGlobal( - function, - w.RefType(arrayArrayString, nullable: false), - 'loadIdModuleNames', - (b) { - final moduleMap = loadingMap.moduleMap; - for (int i = 0; i < moduleMap.length; ++i) { - final moduleNames = moduleMap[i]; - for (int k = 0; k < moduleNames.length; ++k) { - b.global_get( - getInternalizedStringGlobal( - function.moduleBuilder, - moduleNames[k].moduleName, - ), - ); - } - b.array_new_fixed(arrayExternRef, moduleNames.length); - } - b.array_new_fixed(arrayArrayString, moduleMap.length); - }, + // Make a global containing the load id -> module id list table. + final loadingMapGlobal = mainModule.globals.define( + w.GlobalType(w.RefType(arrayOfNullableByteArray, nullable: false)), ); + loadingMapGlobal.initializer + ..i32_const(moduleMap.length) + ..array_new_default(arrayOfNullableByteArray) + ..end(); + + // Make the getter return that array. + _replaceBody(function) + ..global_get(loadingMapGlobal) + ..end(); + + // Emit code to initialize the load id -> module id list table. + final startFunction = mainModule.startFunction.body; + final encodedSegment = mainModule.dataSegments.define(); + for (int i = 0; i < moduleMap.length; ++i) { + final moduleNames = moduleMap[i]; + if (moduleNames.isEmpty) continue; + + // We sort the module ids increasingly, thereby allowing us to encode them + // via delta to previous module id. + final moduleIds = []; + for (int k = 0; k < moduleNames.length; ++k) { + final moduleId = WasmCompilerOptions.idFromDeferredModuleFilename( + moduleNames[k].moduleName, + ); + moduleIds.add(moduleId); + } + moduleIds.sort(); + + // Make the encoded list of module ids. + final moduleIdsEncoded = BytesBuilder(); + moduleIdsEncoded.writeULEB128(moduleNames.length); + int lastId = 0; + for (int i = 0; i < moduleIds.length; ++i) { + final moduleId = moduleIds[i]; + final diff = moduleId - lastId; + moduleIdsEncoded.writeULEB128(diff); + lastId = moduleId; + } + + // Append the encoded module id list to the data segment & make start + // function patch the runtime with the list. + startFunction.global_get(loadingMapGlobal); + startFunction.i32_const(i); + { + startFunction.i32_const(encodedSegment.length); + startFunction.i32_const(moduleIdsEncoded.length); + startFunction.array_new_data(byteArrayType, encodedSegment); + encodedSegment.append(moduleIdsEncoded.takeBytes()); + } + startFunction.array_set(arrayOfNullableByteArray); + } + + final mainModuleOutput = _builderToOutput[mainModule]!; + final prefix = WasmCompilerOptions.deferredModuleFilenamePrefix( + mainModuleOutput.moduleName, + ); + final prefixGetter = + functions.getExistingFunction( + dartInternalModuleNamePrefixGetter!.reference, + ) + as w.FunctionBuilder; + _replaceBody(prefixGetter) + ..global_get(getInternalizedStringGlobal(mainModule, prefix)) + ..end(); } void _patchLoadingMapNamesGetter(w.FunctionBuilder function) { @@ -724,12 +770,7 @@ class Translator with KernelNodes { ..ref_null(w.HeapType.none) ..end(); - final b = w.InstructionsBuilder( - f.moduleBuilder, - f.type.inputs, - f.type.outputs, - ); - f.replaceBody(b); + final b = _replaceBody(f); final label = b.block(const [], [type]); b.global_get(global); @@ -743,6 +784,16 @@ class Translator with KernelNodes { b.end(); } + w.InstructionsBuilder _replaceBody(w.FunctionBuilder function) { + final newBody = w.InstructionsBuilder( + function.moduleBuilder, + function.type.inputs, + function.type.outputs, + ); + function.replaceBody(newBody); + return newBody; + } + void _printFunction(w.BaseFunction function, Object name) { if (options.printWasm) { print("#${function.name}: $name"); @@ -3968,3 +4019,15 @@ class SingleClosureTarget { SingleClosureTarget._(this.callTarget, this.paramInfo); } + +extension on BytesBuilder { + void writeULEB128(int value) { + assert(value >= 0); + do { + int byte = value & 0x7F; + value >>>= 7; + if (value != 0) byte |= 0x80; + addByte(byte); + } while (value != 0); + } +} diff --git a/pkg/dart2wasm/test/deferred_loading/partition_tests_dart2js/custom_split/fuse_with_and.constraints.txt b/pkg/dart2wasm/test/deferred_loading/partition_tests_dart2js/custom_split/fuse_with_and.constraints.txt index 06027de75a4..db7cc27a906 100644 --- a/pkg/dart2wasm/test/deferred_loading/partition_tests_dart2js/custom_split/fuse_with_and.constraints.txt +++ b/pkg/dart2wasm/test/deferred_loading/partition_tests_dart2js/custom_split/fuse_with_and.constraints.txt @@ -55,7 +55,6 @@ Part 0 - pkg/compiler/test/custom_split/data/fuse_with_and/lib_010_0.dart::@methods::g_010_0 - pkg/compiler/test/custom_split/data/fuse_with_and/lib_100_0.dart::@methods::g_100_0 Constants - - IntConstant(127) - IntConstant(20) - IntConstant(256) - IntConstant(36) diff --git a/pkg/dart2wasm/test/deferred_loading/partition_tests_dart2js/custom_split/fuse_with_and.default.txt b/pkg/dart2wasm/test/deferred_loading/partition_tests_dart2js/custom_split/fuse_with_and.default.txt index 949df457003..a9be2a91bcc 100644 --- a/pkg/dart2wasm/test/deferred_loading/partition_tests_dart2js/custom_split/fuse_with_and.default.txt +++ b/pkg/dart2wasm/test/deferred_loading/partition_tests_dart2js/custom_split/fuse_with_and.default.txt @@ -39,7 +39,6 @@ Part 0 - pkg/compiler/test/custom_split/data/fuse_with_and/libImport.dart::@methods::f_111_1 - pkg/compiler/test/custom_split/data/fuse_with_and/libImport.dart::@methods::v Constants - - IntConstant(127) - IntConstant(20) - IntConstant(256) - IntConstant(36) diff --git a/pkg/dart2wasm/test/deferred_loading/partition_tests_dart2js/custom_split/fuse_with_or.constraints.txt b/pkg/dart2wasm/test/deferred_loading/partition_tests_dart2js/custom_split/fuse_with_or.constraints.txt index e8a0bd51f83..454a2915094 100644 --- a/pkg/dart2wasm/test/deferred_loading/partition_tests_dart2js/custom_split/fuse_with_or.constraints.txt +++ b/pkg/dart2wasm/test/deferred_loading/partition_tests_dart2js/custom_split/fuse_with_or.constraints.txt @@ -49,7 +49,6 @@ Part 0 - pkg/compiler/test/custom_split/data/fuse_with_or/libImport.dart::@methods::v - pkg/compiler/test/custom_split/data/fuse_with_or/lib_001_0.dart::@methods::g_001_0 Constants - - IntConstant(127) - IntConstant(20) - IntConstant(256) - IntConstant(36) diff --git a/pkg/dart2wasm/test/deferred_loading/partition_tests_dart2js/custom_split/fuse_with_or.default.txt b/pkg/dart2wasm/test/deferred_loading/partition_tests_dart2js/custom_split/fuse_with_or.default.txt index 64fe2643f8c..a5aa9fec2e1 100644 --- a/pkg/dart2wasm/test/deferred_loading/partition_tests_dart2js/custom_split/fuse_with_or.default.txt +++ b/pkg/dart2wasm/test/deferred_loading/partition_tests_dart2js/custom_split/fuse_with_or.default.txt @@ -39,7 +39,6 @@ Part 0 - pkg/compiler/test/custom_split/data/fuse_with_or/libImport.dart::@methods::f_111_1 - pkg/compiler/test/custom_split/data/fuse_with_or/libImport.dart::@methods::v Constants - - IntConstant(127) - IntConstant(20) - IntConstant(256) - IntConstant(36) diff --git a/pkg/dart2wasm/test/deferred_loading/partition_tests_dart2js/custom_split/just_fuse.constraints.txt b/pkg/dart2wasm/test/deferred_loading/partition_tests_dart2js/custom_split/just_fuse.constraints.txt index d8533f0f1cd..c6a25c84ec3 100644 --- a/pkg/dart2wasm/test/deferred_loading/partition_tests_dart2js/custom_split/just_fuse.constraints.txt +++ b/pkg/dart2wasm/test/deferred_loading/partition_tests_dart2js/custom_split/just_fuse.constraints.txt @@ -41,7 +41,6 @@ Part 0 - pkg/compiler/test/custom_split/data/just_fuse/libImport.dart::@methods::f_111_1 - pkg/compiler/test/custom_split/data/just_fuse/libImport.dart::@methods::v Constants - - IntConstant(127) - IntConstant(20) - IntConstant(256) - IntConstant(36) diff --git a/pkg/dart2wasm/test/deferred_loading/partition_tests_dart2js/custom_split/just_fuse.default.txt b/pkg/dart2wasm/test/deferred_loading/partition_tests_dart2js/custom_split/just_fuse.default.txt index fa1755cd53b..99b0495ef3e 100644 --- a/pkg/dart2wasm/test/deferred_loading/partition_tests_dart2js/custom_split/just_fuse.default.txt +++ b/pkg/dart2wasm/test/deferred_loading/partition_tests_dart2js/custom_split/just_fuse.default.txt @@ -39,7 +39,6 @@ Part 0 - pkg/compiler/test/custom_split/data/just_fuse/libImport.dart::@methods::f_111_1 - pkg/compiler/test/custom_split/data/just_fuse/libImport.dart::@methods::v Constants - - IntConstant(127) - IntConstant(20) - IntConstant(256) - IntConstant(36) diff --git a/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use_module1.wat b/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use_module1.wat index fcb344a4470..84f5203cfbf 100644 --- a/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use_module1.wat +++ b/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use_module1.wat @@ -16,7 +16,7 @@ (table $module0.cross-module-funcs-0 (import "module0" "cross-module-funcs-0") 17 funcref) (global $"\"bad\"" (ref $JSExternWrapper) <...>) (global $MyConstClass (ref $MyConstClass) - (i32.const 108) + (i32.const 109) (i32.const 0) (i32.const 60) (i32.const 0) diff --git a/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use_module2.wat b/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use_module2.wat index 7da3ac8a14b..0c371d831ba 100644 --- a/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use_module2.wat +++ b/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use_module2.wat @@ -15,7 +15,7 @@ (global $.h0-nonshared-const (import "" "h0-nonshared-const") (ref extern)) (table $module0.cross-module-funcs-0 (import "module0" "cross-module-funcs-0") 17 funcref) (global $MyConstClass (ref $MyConstClass) - (i32.const 108) + (i32.const 109) (i32.const 0) (i32.const 60) (i32.const 0) diff --git a/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use_module3.wat b/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use_module3.wat index 1c54c08596b..31caaef9927 100644 --- a/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use_module3.wat +++ b/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use_module3.wat @@ -14,7 +14,7 @@ (field $field1 (mut i32))))) (global $.shared-const (import "" "shared-const") (ref extern)) (global $MyConstClass (ref $MyConstClass) - (i32.const 108) + (i32.const 109) (i32.const 0) (i32.const 60) (i32.const 0) diff --git a/pkg/dart2wasm/test/ir_tests/deferred.constant.type_use_module1.wat b/pkg/dart2wasm/test/ir_tests/deferred.constant.type_use_module1.wat index 87304e7697e..6f979101a1e 100644 --- a/pkg/dart2wasm/test/ir_tests/deferred.constant.type_use_module1.wat +++ b/pkg/dart2wasm/test/ir_tests/deferred.constant.type_use_module1.wat @@ -10,7 +10,7 @@ (global $".Foo called " (import "" "Foo called ") (ref extern)) (table $module0.cross-module-funcs-0 (import "module0" "cross-module-funcs-0") 4 funcref) (global $"\"Foo called \"" (ref $JSExternWrapper) - (i32.const 58) + (i32.const 59) (i32.const 0) (global.get $".Foo called ") (struct.new $JSExternWrapper)) @@ -27,7 +27,7 @@ ) (func $"useFooAsObject " (local $var0 (ref $Foo)) - i32.const 106 + i32.const 107 i32.const 0 i64.const 0 struct.new $Foo diff --git a/pkg/dart2wasm/test/ir_tests/deferred.constant_module1.wat b/pkg/dart2wasm/test/ir_tests/deferred.constant_module1.wat index 4f931842ac4..9c2ca81fd74 100644 --- a/pkg/dart2wasm/test/ir_tests/deferred.constant_module1.wat +++ b/pkg/dart2wasm/test/ir_tests/deferred.constant_module1.wat @@ -58,7 +58,7 @@ (local $var1 (ref $_FunctionType)) (local $var2 (ref $#Closure-0-1)) (local $var3 (ref $H1)) - i32.const 106 + i32.const 107 i32.const 0 block $label0 (result (ref $#Closure-0-1)) global.get $"InstantiationConstant(globalH1Foo)" @@ -144,7 +144,7 @@ struct.get $H1 $fun local.tee $var0 struct.get $#Closure-0-1 $context - i32.const 86 + i32.const 88 i64.const 1 struct.new $BoxedInt local.get $var0 diff --git a/pkg/dart2wasm/test/ir_tests/deferred.constant_module2.wat b/pkg/dart2wasm/test/ir_tests/deferred.constant_module2.wat index f8fc0efc04e..e9b9009d8c9 100644 --- a/pkg/dart2wasm/test/ir_tests/deferred.constant_module2.wat +++ b/pkg/dart2wasm/test/ir_tests/deferred.constant_module2.wat @@ -39,7 +39,7 @@ (local $var0 (ref $_FunctionType)) (local $var1 (ref $#Closure-0-1)) (local $var2 (ref $H0)) - i32.const 107 + i32.const 108 i32.const 0 block $label0 (result (ref $#Closure-0-1)) global.get $"globalH0Foo tear-off" diff --git a/pkg/dart2wasm/test/ir_tests/deferred.fine_grained.devirtualized_module1.wat b/pkg/dart2wasm/test/ir_tests/deferred.fine_grained.devirtualized_module1.wat index 51a13f85e41..71e479d1ff1 100644 --- a/pkg/dart2wasm/test/ir_tests/deferred.fine_grained.devirtualized_module1.wat +++ b/pkg/dart2wasm/test/ir_tests/deferred.fine_grained.devirtualized_module1.wat @@ -11,7 +11,7 @@ (global $1 (import "module0" "global2") (ref $BoxedInt)) (global $2 (import "module0" "global3") (ref $BoxedInt)) (table $module0.cross-module-funcs-0 (import "module0" "cross-module-funcs-0") 18 funcref) - (table $module0.dispatch0 (import "module0" "dispatch0") 654 funcref) + (table $module0.dispatch0 (import "module0" "dispatch0") 657 funcref) (global $"\"Foo0.doitDispatch(\"" (ref $JSExternWrapper) (i32.const 60) (i32.const 0) @@ -42,7 +42,7 @@ (func $"foo0 " call $"runtimeTrue implicit getter" if (result (ref $Object)) - i32.const 108 + i32.const 109 i32.const 0 struct.new $Object else @@ -71,7 +71,7 @@ global.get $1 local.get $var0 struct.get $Object $field0 - i32.const 365 + i32.const 399 i32.add call_indirect $module0.dispatch0 (param (ref $Object) (ref null $#Top)) block $label2 (result (ref $Object)) diff --git a/pkg/dart2wasm/test/ir_tests/deferred.fine_grained.wat b/pkg/dart2wasm/test/ir_tests/deferred.fine_grained.wat index 49689cd4414..8a3076f7b5b 100644 --- a/pkg/dart2wasm/test/ir_tests/deferred.fine_grained.wat +++ b/pkg/dart2wasm/test/ir_tests/deferred.fine_grained.wat @@ -22,7 +22,7 @@ (global $"\"foo0Code(\"" (ref $JSExternWrapper) <...>) (global $0 (ref $BoxedInt) <...>) (global $FooConst0 (ref $Object) - (i32.const 108) + (i32.const 109) (i32.const 0) (struct.new $Object)) (global $fooGlobal0 (mut (ref null $#Top)) diff --git a/pkg/dart2wasm/test/ir_tests/deferred.fine_grained_module2.wat b/pkg/dart2wasm/test/ir_tests/deferred.fine_grained_module2.wat index 702a07795ab..32ab0f730d0 100644 --- a/pkg/dart2wasm/test/ir_tests/deferred.fine_grained_module2.wat +++ b/pkg/dart2wasm/test/ir_tests/deferred.fine_grained_module2.wat @@ -8,7 +8,7 @@ (table $module0.cross-module-funcs-0 (import "module0" "cross-module-funcs-0") 45 funcref) (global $"\"foo1Code(\"" (ref $JSExternWrapper) <...>) (global $FooConst1 (ref $Object) - (i32.const 109) + (i32.const 110) (i32.const 0) (struct.new $Object)) (global $fooGlobal1 (mut (ref null $#Top)) diff --git a/pkg/dart2wasm/test/ir_tests/deferred.fine_grained_module3.wat b/pkg/dart2wasm/test/ir_tests/deferred.fine_grained_module3.wat index bdc638c970d..2c8761e2395 100644 --- a/pkg/dart2wasm/test/ir_tests/deferred.fine_grained_module3.wat +++ b/pkg/dart2wasm/test/ir_tests/deferred.fine_grained_module3.wat @@ -22,7 +22,7 @@ (global $FooConst0 (import "module0" "global7") (ref $Object)) (global $fooGlobal0 (import "module0" "global16") (ref null $#Top)) (table $module0.cross-module-funcs-0 (import "module0" "cross-module-funcs-0") 45 funcref) - (table $module0.dispatch0 (import "module0" "dispatch0") 670 funcref) + (table $module0.dispatch0 (import "module0" "dispatch0") 673 funcref) (global $"\"0\"" (ref $JSExternWrapper) <...>) (global $"\"1\"" (ref $JSExternWrapper) <...>) (global $"\"2\"" (ref $JSExternWrapper) <...>) @@ -65,7 +65,7 @@ (struct.new $JSExternWrapper)) (global $"\"foo5Code(\"" (ref $JSExternWrapper) <...>) (global $FooConst5 (ref $Object) - (i32.const 113) + (i32.const 114) (i32.const 0) (struct.new $Object)) (global $_InterfaceType (ref $_InterfaceType) <...>) @@ -222,7 +222,7 @@ call $"fooGlobal5 implicit getter" local.get $var2 struct.get $Object $field0 - i32.const 378 + i32.const 382 i32.add call_indirect $module0.dispatch0 (param (ref $Object) (ref null $#Top)) ) diff --git a/pkg/dart2wasm/test/ir_tests/deferred.fine_grained_module5.wat b/pkg/dart2wasm/test/ir_tests/deferred.fine_grained_module5.wat index 3bd1dfd66cd..5a945546f5d 100644 --- a/pkg/dart2wasm/test/ir_tests/deferred.fine_grained_module5.wat +++ b/pkg/dart2wasm/test/ir_tests/deferred.fine_grained_module5.wat @@ -8,7 +8,7 @@ (table $module0.cross-module-funcs-0 (import "module0" "cross-module-funcs-0") 45 funcref) (global $"\"foo2Code(\"" (ref $JSExternWrapper) <...>) (global $FooConst2 (ref $Object) - (i32.const 110) + (i32.const 111) (i32.const 0) (struct.new $Object)) (global $fooGlobal2 (mut (ref null $#Top)) diff --git a/pkg/dart2wasm/test/ir_tests/deferred.fine_grained_module7.wat b/pkg/dart2wasm/test/ir_tests/deferred.fine_grained_module7.wat index 91d84286c92..1c5535f048f 100644 --- a/pkg/dart2wasm/test/ir_tests/deferred.fine_grained_module7.wat +++ b/pkg/dart2wasm/test/ir_tests/deferred.fine_grained_module7.wat @@ -8,7 +8,7 @@ (table $module0.cross-module-funcs-0 (import "module0" "cross-module-funcs-0") 45 funcref) (global $"\"foo3Code(\"" (ref $JSExternWrapper) <...>) (global $FooConst3 (ref $Object) - (i32.const 111) + (i32.const 112) (i32.const 0) (struct.new $Object)) (global $fooGlobal3 (mut (ref null $#Top)) diff --git a/pkg/dart2wasm/test/ir_tests/deferred.fine_grained_module9.wat b/pkg/dart2wasm/test/ir_tests/deferred.fine_grained_module9.wat index 52ff5f56959..661f8fb5d1c 100644 --- a/pkg/dart2wasm/test/ir_tests/deferred.fine_grained_module9.wat +++ b/pkg/dart2wasm/test/ir_tests/deferred.fine_grained_module9.wat @@ -8,7 +8,7 @@ (table $module0.cross-module-funcs-0 (import "module0" "cross-module-funcs-0") 45 funcref) (global $"\"foo4Code(\"" (ref $JSExternWrapper) <...>) (global $FooConst4 (ref $Object) - (i32.const 112) + (i32.const 113) (i32.const 0) (struct.new $Object)) (global $fooGlobal4 (mut (ref null $#Top)) diff --git a/pkg/dart2wasm/test/ir_tests/deferred.init_at_startup.wat b/pkg/dart2wasm/test/ir_tests/deferred.init_at_startup.wat index 87dcbc86c7d..29b2ce9b1fa 100644 --- a/pkg/dart2wasm/test/ir_tests/deferred.init_at_startup.wat +++ b/pkg/dart2wasm/test/ir_tests/deferred.init_at_startup.wat @@ -3,6 +3,8 @@ (type $ArgumentError <...>) (type $Array <...>) (type $Array <...>) + (type $Array?> <...>) + (type $Array <...>) (type $Array <...>) (type $JSExternWrapper <...>) (type $Object <...>) @@ -16,6 +18,7 @@ (global $BoxedDouble._cacheKeys (mut (ref $Array)) <...>) (global $BoxedDouble._cacheValues (mut (ref $Array)) <...>) (global $_deletedDataMarker (mut (ref $#Top)) <...>) + (global $global0 (ref $Array?>) <...>) (elem $cross-module-funcs-0 (set 3 (ref.func $"wasm:js-string.length (import)")) (set 4 (ref.func $JSStringImpl._interpolate)) @@ -49,6 +52,12 @@ i32.const 0 struct.new $Object global.set $_deletedDataMarker + global.get $global0 + i32.const 0 + i32.const 0 + i32.const 2 + array.new_data $Array$data0 + array.set $Array?> ) (func $ArgumentError (param $var0 (ref null $#Top)) (param $var1 (ref null $JSExternWrapper)) (result (ref $ArgumentError)) <...>) (func $IntegerDivisionByZeroException (result (ref $Object)) <...>) @@ -58,4 +67,5 @@ (func $JSStringImpl.fromRefUnchecked (param $var0 externref) (result (ref $JSExternWrapper)) <...>) (func $JSStringImpl.substring (param $var0 (ref $JSExternWrapper)) (param $var1 i64) (param $var2 i64) (result (ref $JSExternWrapper)) <...>) (func $_jsBigIntToString (param $var0 i64) (param $var1 i64) (result (ref $JSExternWrapper)) <...>) + (data $data0 <... 2 bytes ...>) ) \ No newline at end of file diff --git a/pkg/dart2wasm/test/ir_tests/dispatch_table_reuse_module1.wat b/pkg/dart2wasm/test/ir_tests/dispatch_table_reuse_module1.wat index bfa645d89b8..aee296059d5 100644 --- a/pkg/dart2wasm/test/ir_tests/dispatch_table_reuse_module1.wat +++ b/pkg/dart2wasm/test/ir_tests/dispatch_table_reuse_module1.wat @@ -7,7 +7,7 @@ (type $WasmListBase <...>) (type $_Type <...>) (table $module0.cross-module-funcs-0 (import "module0" "cross-module-funcs-0") 12 funcref) - (table $module0.dispatch0 (import "module0" "dispatch0") 670 funcref) + (table $module0.dispatch0 (import "module0" "dispatch0") 673 funcref) (elem $module0.cross-module-funcs-0 (set 0 (ref.func $"runTest "))) (func $"runTest " @@ -83,19 +83,19 @@ ref.cast $Object local.tee $var2 struct.get $Object $field0 - i32.const 109 + i32.const 110 i32.eq if local.get $var2 local.get $var5 - i32.const 487 + i32.const 492 call_indirect $module0.dispatch0 (param (ref $Object) i64) else local.get $var2 local.get $var5 local.get $var2 struct.get $Object $field0 - i32.const 378 + i32.const 382 i32.add call_indirect $module0.dispatch0 (param (ref $Object) i64) end diff --git a/pkg/dart2wasm/test/ir_tests/import_name_module1.wat b/pkg/dart2wasm/test/ir_tests/import_name_module1.wat index 051bb9de685..a2c22eb15f0 100644 --- a/pkg/dart2wasm/test/ir_tests/import_name_module1.wat +++ b/pkg/dart2wasm/test/ir_tests/import_name_module1.wat @@ -11,7 +11,7 @@ (global $".hello world" (import "" "hello world") (ref extern)) (table $module0.cross-module-funcs-0 (import "module0" "cross-module-funcs-0") 2 funcref) (global $"\"hello world\"" (ref $JSExternWrapper) - (i32.const 57) + (i32.const 59) (i32.const 0) (global.get $".hello world") (struct.new $JSExternWrapper)) diff --git a/sdk/lib/_internal/wasm/js_common/deferred_patch.dart b/sdk/lib/_internal/wasm/js_common/deferred_patch.dart index 78e5b72d97f..a0041efcc28 100644 --- a/sdk/lib/_internal/wasm/js_common/deferred_patch.dart +++ b/sdk/lib/_internal/wasm/js_common/deferred_patch.dart @@ -22,11 +22,19 @@ final Map> _loading = {}; final Set _loaded = {}; /// Only used when loading modules directly, will get populated by the compiler. -external ImmutableWasmArray> get _loadingMap; +/// +/// Maps a loading id (aka deferred prefix) to the set of module ids that have +/// to be loaded. +external WasmArray?> get _loadingMap; /// Maps load id to (import uri, import prefix). external ImmutableWasmArray get _loadingMapNames; +/// The prefix of all module names. +/// +/// For `test_module.wasm` it will be `test_module`. +external WasmExternRef get _moduleNamePrefix; + @pragma("wasm:import", "moduleLoadingHelper.loadDeferredModules") external WasmExternRef _loadDeferredModules(WasmExternRef moduleNames); @@ -94,8 +102,10 @@ Future loadLibraryFromLoadId(int loadId) { 'Error loading load ID: ${_loadIdInJson(loadId)}\n$e', ); } - return 'Error loading ${_prefixName(loadId)} of library ' - '${_importUri(loadId)}\n$e'; + throw DeferredLoadException( + 'Error loading ${_prefixName(loadId)} of library ' + '${_importUri(loadId)}\n$e', + ); }, ); } @@ -108,18 +118,47 @@ Future _loadLibraryViaEmbedderLoadId(int loadId) { Future _loadLibraryViaEmbedderModuleNames(int loadId) { assert(loadId < _loadingMap.length); - - final ImmutableWasmArray moduleNames = _loadingMap[loadId]; - if (moduleNames.length == 0) { + final WasmArray? encodedModuleIds = _loadingMap[loadId]; + if (encodedModuleIds == null) { // No modules to load. return Future.value(); } - final moduleNamesAsList = []; - for (int i = 0; i < moduleNames.length; ++i) { - moduleNamesAsList.add(JSValue(moduleNames[i]) as JSString); - } + final moduleNamesAsList = _decodeEncodedModuleIds('test', encodedModuleIds); + final promise = (_loadDeferredModules(moduleNamesAsList.toJS.toExternRef!).toJS as JSPromise); return promise.toDart; } + +/// Keep in sync with pkg/dart2wasm/lib/translator.dart:Translator._patchLoadingMapGetter` +List _decodeEncodedModuleIds( + String prefix, + WasmArray encoded, +) { + int offset = 0; + + int nextULEB128() { + int result = 0; + int shift = 0; + while (true) { + final byte = encoded.readUnsigned(offset++); + result |= (byte & 0x7F) << shift; + shift += 7; + if ((byte & 0x80) == 0) break; + } + return result; + } + + final length = nextULEB128(); + final moduleIds = []; + int previousModuleId = 0; + final prefix = JSStringImpl.fromRefUnchecked(_moduleNamePrefix); + for (int i = 0; i < length; ++i) { + int diff = nextULEB128(); + final moduleId = previousModuleId + diff; + moduleIds.add('$prefix${moduleId.toString()}.wasm'.toJS); + previousModuleId = moduleId; + } + return moduleIds; +}