[dart2wasm] More compact encoding of deferred load lists
Measured on size of e main module (baseline is we don't embed it in application code): * embedding before: +16.5% uncompressed / +9.1% compressed * embedding with this CL: +4% uncompressed / +4.3% compressed When embeddeding deferred load list information into the app (as opposed to a separate json file) we now use a more compact encoding. Specifically: Instead of encoding it as an array of an array of strings (which are module names), we encode it as an array of an array of module ids and construct the module name from the id. To make the array of module ids more compact we utilize the fact that we can sort them and encode in delta encoding (i.e. instead of absolute module ids, encode the diff between previous module id in the list). We put the encoded module id lists in a data section and create `WasmArray<WasmI8>`s from them at startup. When we trigger a load we then decode them into the list of module names. There's more opportunity to optimize it, but it's good to do this as a first step. Change-Id: I293fb8879d992fc370786f6c9b258ccd27e1559b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/508980 Reviewed-by: Srujan Gaddam <srujzs@google.com> Commit-Queue: Martin Kustermann <kustermann@google.com>
This commit is contained in:
@@ -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<CompilationResult> _runCodegenPhase(
|
||||
final wasmOutputFilename = path.basename(options.outputFile);
|
||||
final moduleIds = modules.keys
|
||||
.map<int>(
|
||||
(moduleMetadata) => options.idForModuleName(
|
||||
(moduleMetadata) => WasmCompilerOptions.idForModuleName(
|
||||
wasmOutputFilename,
|
||||
moduleMetadata.moduleName,
|
||||
)!,
|
||||
|
||||
@@ -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 `<id>` for `test_module<id>.wasm`).
|
||||
static int idFromDeferredModuleFilename(String moduleName) {
|
||||
// The name has pattern: "..._module<moduleId>.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<id>.wasm`).
|
||||
static String deferredModuleFilenamePrefix(String mainModuleFilename) =>
|
||||
path.basename(path.setExtension(mainModuleFilename, '_module'));
|
||||
|
||||
static int _defaultMaxActiveWasmOptProcesses() {
|
||||
try {
|
||||
return Platform.numberOfProcessors;
|
||||
|
||||
@@ -135,9 +135,12 @@ class CompilerPhaseInputOutputManager {
|
||||
int moduleId,
|
||||
List<String> 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 = <int>{};
|
||||
for (final file in files) {
|
||||
if (file is! File) continue;
|
||||
final moduleId = options.idForModuleName(
|
||||
final moduleId = WasmCompilerOptions.idForModuleName(
|
||||
mainWasmFilename,
|
||||
path.basename(file.path),
|
||||
);
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
@@ -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<WasmI8>',
|
||||
);
|
||||
|
||||
_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 = <int>[];
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
-1
@@ -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)
|
||||
|
||||
-1
@@ -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)
|
||||
|
||||
-1
@@ -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)
|
||||
|
||||
-1
@@ -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)
|
||||
|
||||
-1
@@ -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)
|
||||
|
||||
-1
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 <noInline>"
|
||||
(local $var0 (ref $Foo))
|
||||
i32.const 106
|
||||
i32.const 107
|
||||
i32.const 0
|
||||
i64.const 0
|
||||
struct.new $Foo
|
||||
|
||||
@@ -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<int>)"
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 <noInline>"
|
||||
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))
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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))
|
||||
)
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
(type $ArgumentError <...>)
|
||||
(type $Array<Object?> <...>)
|
||||
(type $Array<String> <...>)
|
||||
(type $Array<WasmArray<WasmI8>?> <...>)
|
||||
(type $Array<WasmI8> <...>)
|
||||
(type $Array<int> <...>)
|
||||
(type $JSExternWrapper <...>)
|
||||
(type $Object <...>)
|
||||
@@ -16,6 +18,7 @@
|
||||
(global $BoxedDouble._cacheKeys (mut (ref $Array<int>)) <...>)
|
||||
(global $BoxedDouble._cacheValues (mut (ref $Array<String>)) <...>)
|
||||
(global $_deletedDataMarker (mut (ref $#Top)) <...>)
|
||||
(global $global0 (ref $Array<WasmArray<WasmI8>?>) <...>)
|
||||
(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<WasmI8>$data0
|
||||
array.set $Array<WasmArray<WasmI8>?>
|
||||
)
|
||||
(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 ...>)
|
||||
)
|
||||
@@ -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 <noInline>")))
|
||||
(func $"runTest <noInline>"
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -22,11 +22,19 @@ final Map<int, Future<void>> _loading = {};
|
||||
final Set<int> _loaded = {};
|
||||
|
||||
/// Only used when loading modules directly, will get populated by the compiler.
|
||||
external ImmutableWasmArray<ImmutableWasmArray<WasmExternRef>> get _loadingMap;
|
||||
///
|
||||
/// Maps a loading id (aka deferred prefix) to the set of module ids that have
|
||||
/// to be loaded.
|
||||
external WasmArray<WasmArray<WasmI8>?> get _loadingMap;
|
||||
|
||||
/// Maps load id to (import uri, import prefix).
|
||||
external ImmutableWasmArray<WasmExternRef> get _loadingMapNames;
|
||||
|
||||
/// The prefix of all module names.
|
||||
///
|
||||
/// For `test_module<id>.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<void> 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<void> _loadLibraryViaEmbedderLoadId(int loadId) {
|
||||
|
||||
Future<void> _loadLibraryViaEmbedderModuleNames(int loadId) {
|
||||
assert(loadId < _loadingMap.length);
|
||||
|
||||
final ImmutableWasmArray<WasmExternRef> moduleNames = _loadingMap[loadId];
|
||||
if (moduleNames.length == 0) {
|
||||
final WasmArray<WasmI8>? encodedModuleIds = _loadingMap[loadId];
|
||||
if (encodedModuleIds == null) {
|
||||
// No modules to load.
|
||||
return Future.value();
|
||||
}
|
||||
final moduleNamesAsList = <JSString>[];
|
||||
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<JSString> _decodeEncodedModuleIds(
|
||||
String prefix,
|
||||
WasmArray<WasmI8> 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 = <JSString>[];
|
||||
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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user