From ec83c5918aa47619a7c47120751c322104446db0 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Tue, 2 Dec 2025 07:05:40 -0800 Subject: [PATCH] [dart2wasm] Prune library imports after TFA for better deferred module splits This significantly prunes the imports in the library graph which in return significantly reduces code size of main module. - gallery: -27% (-677 KB) - essentials: -45% (-8.7 MB) The main insight here is that for the purpose of splitting up Dart libraries into wasm modules we only have a dependency of one dart library on another if the former uses something from the latter "statically". Other uses of `Reference`s from other libraries (e.g. interface targets, dart types, etc) don't require an import. The other insight is that a library A may import library B which may re-export library C1, C2, C3. If A only uses things from C2 then we can remove import B and inject import C2 instead. That means library A no longer needs B, C1, C2. For dart types specifically: Compared to other backends, dart2wasm stores all RTI (runtime type information) in the main module - i.e. deferred modules don't bring extra RTI - which is why we don't have to load deferred module before we can access it's types. Change-Id: I9e486bb4de165e4b23644a84838ace06d2f35bce Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/464901 Commit-Queue: Martin Kustermann Reviewed-by: Slava Egorov --- pkg/dart2wasm/lib/compile.dart | 25 ++ pkg/dart2wasm/lib/deferred_loading.dart | 35 +- .../lib/library_dependencies_pruner.dart | 365 ++++++++++++++++++ ...rred.constant.multi_module_use_module4.wat | 2 + .../ir_tests/deferred.constant.type_use.dart | 39 ++ .../deferred.constant.type_use.h.0.dart | 10 + .../deferred.constant.type_use.h.1.dart | 12 + .../ir_tests/deferred.constant.type_use.wat | 41 ++ .../deferred.constant.type_use_module1.wat | 28 ++ .../deferred.constant.type_use_module2.wat | 2 + .../ir_tests/deferred.constant_module3.wat | 2 + .../test/ir_tests/import_name_module2.wat | 2 + 12 files changed, 560 insertions(+), 3 deletions(-) create mode 100644 pkg/dart2wasm/lib/library_dependencies_pruner.dart create mode 100644 pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use_module4.wat create mode 100644 pkg/dart2wasm/test/ir_tests/deferred.constant.type_use.dart create mode 100644 pkg/dart2wasm/test/ir_tests/deferred.constant.type_use.h.0.dart create mode 100644 pkg/dart2wasm/test/ir_tests/deferred.constant.type_use.h.1.dart create mode 100644 pkg/dart2wasm/test/ir_tests/deferred.constant.type_use.wat create mode 100644 pkg/dart2wasm/test/ir_tests/deferred.constant.type_use_module1.wat create mode 100644 pkg/dart2wasm/test/ir_tests/deferred.constant.type_use_module2.wat create mode 100644 pkg/dart2wasm/test/ir_tests/deferred.constant_module3.wat create mode 100644 pkg/dart2wasm/test/ir_tests/import_name_module2.wat diff --git a/pkg/dart2wasm/lib/compile.dart b/pkg/dart2wasm/lib/compile.dart index a76fa34ad18..643a26945e9 100644 --- a/pkg/dart2wasm/lib/compile.dart +++ b/pkg/dart2wasm/lib/compile.dart @@ -39,6 +39,7 @@ import 'dynamic_modules.dart'; import 'io_util.dart'; import 'js/method_collector.dart' show JSMethods; import 'js/runtime_generator.dart' as js; +import 'library_dependencies_pruner.dart'; import 'modules.dart'; import 'record_class_generator.dart'; import 'records.dart'; @@ -548,6 +549,30 @@ Future _runTfaPhase( libraryIndex = LibraryIndex(component, _librariesToIndex); } + // NOTE: The [Library.dependencies] will be in a weird state after TFA: + // + // * a library may use members of other libraries without an import that + // provides the member + // + // * a library may have many unused imports + // + // -> See https://dartbug.com/62112 & https://dartbug.com/62111 for details. + // + // At this point dart2wasm uses library dependencies only for one purpose, + // namely for partitioning all libraries into deferred wasm modules. So we now + // perform a dart2wasm specific pruning of library imports. + // + // See [pruneLibraryDependencies] for more information. + // + // NOTE: In stress test mode the component is manually split into one wasm + // module per library without making imports of libraries deferred. To ensure + // all modules get loaded before main it injects dummy [LoadLibrary] + // expressions that would be optimized out by [pruneLibraryDependencies]. So + // we disable the pruning in this case. + if (!options.translatorOptions.enableMultiModuleStressTestMode) { + pruneLibraryDependencies(libraryIndex, component); + } + if (options.emitTfa) { // Store metadata needed for codegen so that it can be serialized. final recordClassesRepo = _RecordClassesRepository(); diff --git a/pkg/dart2wasm/lib/deferred_loading.dart b/pkg/dart2wasm/lib/deferred_loading.dart index be1e4a8c5a9..cc5e59115e7 100644 --- a/pkg/dart2wasm/lib/deferred_loading.dart +++ b/pkg/dart2wasm/lib/deferred_loading.dart @@ -14,6 +14,7 @@ import 'package:kernel/core_types.dart'; import 'await_transformer.dart' as await_transformer; import 'compiler_options.dart'; +import 'library_dependencies_pruner.dart'; import 'modules.dart'; import 'target.dart'; import 'util.dart' show addPragma; @@ -139,8 +140,32 @@ class DeferredLoadingModuleStrategy extends ModuleStrategy { }); }); - moduleOutputData = - ModuleOutputData([mainModule, ...rootSetToModule.values]); + // Some libraries may not have gotten a module assigned in the above + // procedure. This can have a varity of reasons: + // + // - A class that's never really used but still in the program because TFA + // left it there (this happens occasionally because we enable RTA before + // TFA, RTA is less precised and may mark a class as allocated but TFA + // later on optimizes usages away which leave the class as non-abstract + // but unused). + // - A class is only used in type expressions + // - ... + // + // The code generator still requires every library to have a corresponding + // module, so we make an artificial one here. + final assignedLibraries = { + ...mainModule.libraries, + for (final module in rootSetToModule.values) ...module.libraries, + }; + final unassignedLibraries = component.libraries.toSet() + ..removeAll(assignedLibraries); + + moduleOutputData = ModuleOutputData([ + mainModule, + ...rootSetToModule.values, + if (unassignedLibraries.isNotEmpty) + builder.buildModuleMetadata()..libraries.addAll(unassignedLibraries) + ]); } @override @@ -195,6 +220,10 @@ class DeferredLoadingModuleStrategy extends ModuleStrategy { for (final dependency in currentLibrary.dependencies) { final targetLibrary = dependency.importedLibraryReference.asLibrary; if (dependency.isDeferred) { + if (dependency.name!.startsWith(unusedDeferredLibraryPrefix)) { + continue; + } + newDeferredRoots.add(targetLibrary); (importTargetMap[currentLibrary] ??= {})[dependency.name!] = targetLibrary; @@ -401,7 +430,7 @@ class DeferredLoadingLowering extends Transformer { _checkLibraryIsLoadedFromLoadId, Arguments([IntLiteral(loadId)])); } - static void addEntryPointPragma(CoreTypes coreTypes, Procedure node) { + static void addEntryPointPragma(CoreTypes coreTypes, Annotatable node) { addPragma(node, 'wasm:entry-point', coreTypes, value: BoolConstant(true)); } } diff --git a/pkg/dart2wasm/lib/library_dependencies_pruner.dart b/pkg/dart2wasm/lib/library_dependencies_pruner.dart new file mode 100644 index 00000000000..9e01f62e69a --- /dev/null +++ b/pkg/dart2wasm/lib/library_dependencies_pruner.dart @@ -0,0 +1,365 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:kernel/kernel.dart'; +import 'package:kernel/library_index.dart'; + +/// If a deferred library import has this name prefix it isn't used to load +/// anything. It only serves to maintain that any [CheckLibraryIsLoaded] throws +/// if the [LoadLibrary] call was not called. +const unusedDeferredLibraryPrefix = 'unused-'; + +/// Prunes [Library.dependencies] to contain precisely those imports needed. +/// +/// Dart2wasm only uses library dependencies for one purpose, namely for +/// computing deferred loading units. This computation is done based on the +/// import graph and the granularity is on a library level. +/// +/// We make the following overvations: +/// +/// a) Using a type from a import +/// -> The main wasm module has all Dart runtime type information atm +/// -> No need to import a library to use classes from it in types. +/// +/// b) Using a constant from an import +/// -> If we use a [InstanceConstant] / [TearOffConstant] in a library then +/// we have to ensure the enclosing library of the +/// [InstanceConstant.classNode] / [TearOffConstant.target] is imported. +/// -> This ensures that the code for closure/method is available when +/// invoking it. +/// -> Any other constant doesn't require an import. +/// +/// c) Using static elements from an import +/// -> If we invoke a constructor, a static method, static getter, super +/// constructor, etc we need to import the target's enclosing library. +/// -> This will guarantee we have the code for target loaded when we perform +/// the call. +/// +/// d) Instance/Dynamic invocations, other uses of [Reference]s +/// -> Does not require an import of the [Reference]s enclosing library. +/// -> We have sound type system: If the call is executed we know that the +/// receiver was allocated (and whoever allocated it has ensured - via c) +/// above - that the code for methods of the receiver is loaded). +/// +/// So we establish the following invariants +/// +/// * We only have imports for [Reference]s which are used for "static"-like +/// calls +/// +/// * We import the library containing the definition of a [Reference] and not +/// e.g. a library that may re-export it +/// +/// * If a [Reference] was usable via a deferred import (possibly via +/// a deferred library that re-exported the [Reference]) we ensure the newly +/// inserted import will also be deferred. +/// +/// The transform will +/// +/// * prune [Library.dependencies] to be exact, i.e. have a import iff +/// something is used from the imported library +/// +/// * will remove all exports +/// +/// * may insert more precise [LoadLibrary]/[CheckLibraryIsLoaded] if we don't +/// use a deferred library directly but things it re-exported +/// +void pruneLibraryDependencies(LibraryIndex libraryIndex, Component component) { + final constantToLibrarySet = _ConstantToLibrarySet(); + for (final library in component.libraries) { + final usedLibraries = + _Collector(library, constantToLibrarySet).usedLibraries; + _ImportPruner(libraryIndex, usedLibraries, library); + } + for (final library in component.libraries) { + library.dependencies.removeWhere((dep) { + if (dep.isExport) { + dep.parent = null; + return true; + } + return false; + }); + } +} + +class _ImportPruner extends Transformer { + final LibraryIndex libraryIndex; + + final Set usedLibraries; + final Library library; + final additionalDeferredImports = + >{}; + + late final futureImmediate = + libraryIndex.getConstructor('dart:async', '_Future', 'immediate'); + + _ImportPruner(this.libraryIndex, this.usedLibraries, this.library) { + // Step 1) Prune existing library imports. + + // The set of libraries that we need to import and are already covered via + // an existing library import. + final librariesOfExistingImports = {}; + + // Maps a library to all the deferred imports that made this library + // available. + final libraryToDeferredImports = >{}; + + // The new set of imports (possibly smaller - removing unused dependencies, + // possibly larger - adding used but not yet imported dependencies). + final prunedDependencies = []; + for (final dep in library.dependencies) { + if (dep.isExport) { + prunedDependencies.add(dep); + continue; + } + if (usedLibraries.contains(dep.targetLibrary)) { + librariesOfExistingImports.add(dep.targetLibrary); + prunedDependencies.add(dep); + continue; + } + + if (dep.isDeferred) { + // Although the deferred dependency isn't used, for making sure + // [CheckLibraryIsLoaded] nodes throw if no preceding + // [LoadLibrary] was called we have to maintain a dummy import. This + // will also ensure the exception mentions the right name. + prunedDependencies.add(dep); + dep.name = '$unusedDeferredLibraryPrefix${dep.name!}'; + + // Loop over all libraries available via the deferred import that are + // used. The transformer will then issue individual [LoadLibrary] calls + // to them. + for (final available in _transitiveLibrarySet(dep.targetLibrary)) { + if (usedLibraries.contains(available)) { + libraryToDeferredImports.putIfAbsent(available, () => []).add(dep); + } + } + continue; + } + + // The [dep] isn't directly used, remove it. + assert(!dep.isDeferred); + dep.parent = null; + } + library.dependencies = prunedDependencies; + + // Step 2) Add missing imports. + for (final used in usedLibraries) { + // Maybe we already import the [used] library. + if (librariesOfExistingImports.contains(used)) { + continue; + } + + // Never emit a library import to `dart:core`, it's special. + if (used.importUri.scheme == 'dart' && used.importUri.path == 'core') { + continue; + } + + // We need to inject a new import of the [used] library. + final oldDeferredImports = libraryToDeferredImports[used]; + if (oldDeferredImports == null) { + // This library was not accessible via old deferred imports, so we emit + // a normal import. + library.addDependency(LibraryDependency.import(used)); + continue; + } + + // The library was accessible (via a re-export) from an deferred + // import. Let's make a new deferred import for that particular library. + final newDep = LibraryDependency.deferredImport( + used, 'PreciseDeferredDep-${used.dependencies.length}'); + library.addDependency(newDep); + for (final oldImport in oldDeferredImports) { + // Any [LoadLibrary] or [CheckLibraryIsLoaded] node that operated on the + // old (unused) deferred import needs to cover the [newDep] (possibly in + // addition to the existing dep (if used) and others). + additionalDeferredImports.putIfAbsent(oldImport, () => []).add(newDep); + } + } + + // We only have to transform the body of the library if any [LoadLibrary] or + // [CheckLibraryIsLoaded] has to be modified. + if (additionalDeferredImports.isNotEmpty) { + library.transformChildren(this); + } + } + + @override + TreeNode visitLoadLibrary(LoadLibrary node) { + node = super.visitLoadLibrary(node) as LoadLibrary; + final additional = additionalDeferredImports[node.import]; + if (additional == null) return node; + return BlockExpression( + Block([ + // This may be a dummy/unused which we only omit for throwing correct + // errors if a access (e.g. of a type) is used before the load call. + ExpressionStatement(node), + + for (final replacement in additional.skip(1)) + ExpressionStatement(LoadLibrary(replacement)), + ]), + LoadLibrary(additional.last)); + } + + @override + TreeNode visitCheckLibraryIsLoaded(CheckLibraryIsLoaded node) { + node = super.visitCheckLibraryIsLoaded(node) as CheckLibraryIsLoaded; + final additional = additionalDeferredImports[node.import]; + if (additional == null) return node; + return BlockExpression( + Block([ + // This may be a dummy/unused which we only omit for throwing correct + // errors if a access (e.g. of a type) is used before the load call. + ExpressionStatement(node), + + for (final replacement in additional.skip(1)) + ExpressionStatement(CheckLibraryIsLoaded(replacement)), + ]), + CheckLibraryIsLoaded(additional.last)); + } +} + +/// Traverses the AST of a [Library] and collects the set of libraries we need +/// to import due to accessing elements "statically" (see +/// [pruneLibraryDependencies] for more information) +class _Collector extends RecursiveVisitor { + final Library library; + final _ConstantToLibrarySet constantToLibrarySet; + + /// The libraries that need to be imported. + final Set usedLibraries = {}; + + _Collector(this.library, this.constantToLibrarySet) { + library.accept(this); + } + + @override + void visitStaticGet(StaticGet node) { + super.visitStaticGet(node); + addLibrary(node.target.enclosingLibrary); + } + + @override + void visitStaticSet(StaticSet node) { + super.visitStaticSet(node); + addLibrary(node.target.enclosingLibrary); + } + + @override + void visitStaticInvocation(StaticInvocation node) { + super.visitStaticInvocation(node); + addLibrary(node.target.enclosingLibrary); + } + + @override + void visitConstructorInvocation(ConstructorInvocation node) { + super.visitConstructorInvocation(node); + addLibrary(node.target.enclosingLibrary); + } + + @override + void visitSuperInitializer(SuperInitializer node) { + super.visitSuperInitializer(node); + addLibrary(node.target.enclosingLibrary); + } + + @override + void visitRedirectingInitializer(RedirectingInitializer node) { + super.visitRedirectingInitializer(node); + addLibrary(node.target.enclosingLibrary); + } + + @override + void visitStaticTearOff(StaticTearOff node) { + super.visitStaticTearOff(node); + addLibrary(node.target.enclosingLibrary); + } + + @override + void defaultDartType(DartType node) { + // Ignore due to compiler always able to construct runtime type objects when + // needed (see also [pruneLibraryDependencies]). + } + + @override + void visitSupertype(Supertype node) { + // Ignore due to compiler always able to construct runtime type objects when + // needed (see also [pruneLibraryDependencies]). + } + + @override + void visitConstantExpression(ConstantExpression node) { + usedLibraries.addAll(constantToLibrarySet.librariesFor(node.constant)); + } + + void addLibrary(Library used) { + if (used != library) { + usedLibraries.add(used); + } + } +} + +class _ConstantToLibrarySet { + final _constantToTransitiveLibraries = >{}; + + /// Collects the set of libraries one needs to import when accessing + /// [constant]. + /// + /// This include enclosing libraries of all [InstanceConstant]s + /// and [TearOffConstant]s of the transitive constant graph of [constant]. + Set librariesFor(Constant constant) { + final existing = _constantToTransitiveLibraries[constant]; + if (existing != null) return existing; + + final transitiveLibraries = { + if (constant is InstanceConstant) constant.classNode.enclosingLibrary, + if (constant is TearOffConstant) constant.target.enclosingLibrary, + // Collect all transitive libraries for direct child constants. + for (final childConstant + in _ChildConstantCollector.directChildrenOf(constant)) + ...librariesFor(childConstant), + }; + + return _constantToTransitiveLibraries[constant] = + transitiveLibraries.isEmpty ? const {} : transitiveLibraries; + } +} + +class _ChildConstantCollector extends RecursiveVisitor { + /// Returns the set of constants referred to by the (possibly composed) + /// [constant]. + static Set directChildrenOf(Constant constant) { + final children = {}; + constant.visitChildren(_ChildConstantCollector._(children)); + return children; + } + + final Set _directChildren; + _ChildConstantCollector._(this._directChildren); + + @override + void defaultConstantReference(Constant node) { + _directChildren.add(node); + } +} + +/// Collects the set of libraries transitively imported via importing [library]. +/// +/// This includes [library] and any other library it transitively re-exports. +Set _transitiveLibrarySet(Library library) { + final transitiveLibrarySet = {library}; + final worklist = [library]; + while (worklist.isNotEmpty) { + final toBeExpanded = worklist.removeLast(); + assert(transitiveLibrarySet.contains(toBeExpanded)); + for (final dep in toBeExpanded.dependencies) { + if (dep.isExport) { + final reExportedLibrary = dep.targetLibrary; + if (transitiveLibrarySet.add(reExportedLibrary)) { + worklist.add(reExportedLibrary); + } + } + } + } + return transitiveLibrarySet; +} diff --git a/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use_module4.wat b/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use_module4.wat new file mode 100644 index 00000000000..285347635d0 --- /dev/null +++ b/pkg/dart2wasm/test/ir_tests/deferred.constant.multi_module_use_module4.wat @@ -0,0 +1,2 @@ +(module $module4 +) \ No newline at end of file diff --git a/pkg/dart2wasm/test/ir_tests/deferred.constant.type_use.dart b/pkg/dart2wasm/test/ir_tests/deferred.constant.type_use.dart new file mode 100644 index 00000000000..27e51b6e192 --- /dev/null +++ b/pkg/dart2wasm/test/ir_tests/deferred.constant.type_use.dart @@ -0,0 +1,39 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// functionFilter=useFoo +// tableFilter=static[0-9]+ +// globalFilter=Foo +// typeFilter=Foo +// compilerOption=--enable-deferred-loading +// compilerOption=--no-minify + +import 'deferred.constant.type_use.h.0.dart'; +import 'deferred.constant.type_use.h.1.dart' deferred as h1; + +void main() async { + // Ensure the deferred libraries are loaded. + await h1.loadLibrary(); + + useFoo(); +} + +@pragma('wasm:never-inline') +void useFoo() { + // We use `Foo` as type but that doesn't mean + // => This shouldn't require the code for `Foo` to be bundled with the main + // module. + useFooAsType(); + + // We use `Foo` code, but only via deferred library which uses `Foo` + // directly. + // => This should require the code for `Foo` to end up in the deferred + // library. + h1.useFooAsObject(); +} + +@pragma('wasm:never-inline') +void useFooAsType() { + print(Foo); +} diff --git a/pkg/dart2wasm/test/ir_tests/deferred.constant.type_use.h.0.dart b/pkg/dart2wasm/test/ir_tests/deferred.constant.type_use.h.0.dart new file mode 100644 index 00000000000..9f59eae3446 --- /dev/null +++ b/pkg/dart2wasm/test/ir_tests/deferred.constant.type_use.h.0.dart @@ -0,0 +1,10 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +class Foo { + int i = 0; + void printFoo() { + print('Foo called ${i++}'); + } +} diff --git a/pkg/dart2wasm/test/ir_tests/deferred.constant.type_use.h.1.dart b/pkg/dart2wasm/test/ir_tests/deferred.constant.type_use.h.1.dart new file mode 100644 index 00000000000..297b96c258f --- /dev/null +++ b/pkg/dart2wasm/test/ir_tests/deferred.constant.type_use.h.1.dart @@ -0,0 +1,12 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'deferred.constant.type_use.h.0.dart'; + +@pragma('wasm:never-inline') +void useFooAsObject() { + final f = Foo(); + f.printFoo(); + f.printFoo(); +} diff --git a/pkg/dart2wasm/test/ir_tests/deferred.constant.type_use.wat b/pkg/dart2wasm/test/ir_tests/deferred.constant.type_use.wat new file mode 100644 index 00000000000..93ab2d13e2c --- /dev/null +++ b/pkg/dart2wasm/test/ir_tests/deferred.constant.type_use.wat @@ -0,0 +1,41 @@ +(module $module0 + (type $#Top <...>) + (type $DefaultSet <...>) + (type $DeferredLoadIdNotLoadedError <...>) + (type $_InterfaceType <...>) + (type $type0 <...>) + (table $static0-0 (export "static0-0") 1 (ref null $type0)) + (global $"C414 _InterfaceType" (ref $_InterfaceType) <...>) + (global $_loaded (mut (ref null $DefaultSet)) <...>) + (func $_loaded implicit getter (result (ref $DefaultSet)) <...>) + (func $"useFoo " + call $"useFooAsType " + block $label0 (result (ref $DefaultSet)) + global.get $_loaded + br_on_non_null $label0 + call $"_loaded implicit getter" + end $label0 + call $_DefaultSet&_HashFieldBase&SetMixin&_HashBase&_OperatorEqualsAndHashCode&_LinkedHashSetMixin.contains + i32.eqz + if + i32.const 49 + i32.const 0 + ref.null none + i64.const 0 + struct.new $DeferredLoadIdNotLoadedError + call $Error._throwWithCurrentStackTrace + unreachable + end + i32.const 0 + call_indirect $static0-0 (result (ref null $#Top)) + drop + ) + (func $"useFooAsType " + global.get $"C414 _InterfaceType" + call $print + drop + ) + (func $Error._throwWithCurrentStackTrace (param $var0 (ref $#Top)) <...>) + (func $_DefaultSet&_HashFieldBase&SetMixin&_HashBase&_OperatorEqualsAndHashCode&_LinkedHashSetMixin.contains (param $var0 (ref $DefaultSet)) (result i32) <...>) + (func $print (param $var0 (ref null $#Top)) (result (ref null $#Top)) <...>) +) \ No newline at end of file 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 new file mode 100644 index 00000000000..49ade7575e9 --- /dev/null +++ b/pkg/dart2wasm/test/ir_tests/deferred.constant.type_use_module1.wat @@ -0,0 +1,28 @@ +(module $module1 + (type $#Top <...>) + (type $Foo (sub final $Object (struct + (field $field0 i32) + (field $field1 (mut i32)) + (field $i (mut i64))))) + (type $JSStringImpl <...>) + (type $Object <...>) + (global $".Foo called " (import "" "Foo called ") (ref extern)) + (global $"C462 \"Foo called \"" (ref $JSStringImpl) + (i32.const 4) + (i32.const 0) + (global.get $".Foo called ") + (struct.new $JSStringImpl)) + (func $"useFooAsObject " (result (ref null $#Top)) + (local $var0 (ref $Foo)) + i32.const 114 + i32.const 0 + i64.const 0 + struct.new $Foo + local.tee $var0 + call $Foo.printFoo + local.get $var0 + call $Foo.printFoo + ref.null none + ) + (func $Foo.printFoo (param $var0 (ref $Foo)) <...>) +) \ No newline at end of file diff --git a/pkg/dart2wasm/test/ir_tests/deferred.constant.type_use_module2.wat b/pkg/dart2wasm/test/ir_tests/deferred.constant.type_use_module2.wat new file mode 100644 index 00000000000..e7a73780556 --- /dev/null +++ b/pkg/dart2wasm/test/ir_tests/deferred.constant.type_use_module2.wat @@ -0,0 +1,2 @@ +(module $module2 +) \ No newline at end of file diff --git a/pkg/dart2wasm/test/ir_tests/deferred.constant_module3.wat b/pkg/dart2wasm/test/ir_tests/deferred.constant_module3.wat new file mode 100644 index 00000000000..8457f80982c --- /dev/null +++ b/pkg/dart2wasm/test/ir_tests/deferred.constant_module3.wat @@ -0,0 +1,2 @@ +(module $module3 +) \ No newline at end of file diff --git a/pkg/dart2wasm/test/ir_tests/import_name_module2.wat b/pkg/dart2wasm/test/ir_tests/import_name_module2.wat new file mode 100644 index 00000000000..e7a73780556 --- /dev/null +++ b/pkg/dart2wasm/test/ir_tests/import_name_module2.wat @@ -0,0 +1,2 @@ +(module $module2 +) \ No newline at end of file