From 92f500b7b7e68df11ea89fd61890d2cf502d1570 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Thu, 23 Oct 2025 01:11:22 -0700 Subject: [PATCH] [dart2wasm] Make IR test framework more flexible, add deferred loading baseline test Changes to IR printing implementation: * Emit omitted `<...>` marker to make difference between function without body and function with omitted body clear. * Extend omtting capability to globals, types and tables * Extend filtering capability to globals, types, tables * Better table names: Try to use import or export names if available. * Option to print globals & types always multiline * Option to scrub absolute file uris (which aren't stable across machines) Changes to IR binary / text: * Extend binary parsing of element section to support imported tables * Add printing of those (which shows how imported tables are patched) (The patching of importing tables is used in deferred modules) Changes to IR test framework: * Allow more filters (see above) in the test files. * Allow tests to use helper libraries (containing `.h.` in their name) without them being considered tests themselves. * Allow using deferred loading in tests and write expectation files for main & deferred modules as wat files. Other things: * Share more code between `pkg/dart2wasm/bin/wasm2dart.dart` and `pkg/dart2wasm/test/ir_test.dart` * Add baseline test for deferred loading using tear off constants Change-Id: Ica6666f23f5aa6fb2174f414c9082039940f8dc5 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/456460 Commit-Queue: Martin Kustermann Reviewed-by: Nate Biggs --- pkg/dart2wasm/bin/wasm2wat.dart | 58 +++- pkg/dart2wasm/test/ir_test.dart | 130 ++++++--- .../test/ir_tests/deferred.constant.dart | 32 +++ .../test/ir_tests/deferred.constant.h.0.dart | 12 + .../test/ir_tests/deferred.constant.h.1.dart | 18 ++ .../test/ir_tests/deferred.constant.wat | 183 +++++++++++++ .../ir_tests/deferred.constant_module1.wat | 59 +++++ .../ir_tests/deferred.constant_module2.wat | 10 + pkg/dart2wasm/test/ir_tests/hello.dart | 4 + pkg/dart2wasm/test/ir_tests/hello.wat | 14 +- pkg/dart2wasm/test/ir_tests/interop.bool.wat | 51 ++-- .../test/ir_tests/interop.double.wat | 60 +++-- pkg/dart2wasm/test/ir_tests/interop.int.wat | 47 ++-- pkg/dart2wasm/test/ir_tests/interop.num.wat | 61 +++-- .../test/ir_tests/interop.string.wat | 47 ++-- pkg/wasm_builder/lib/src/ir/function.dart | 2 + pkg/wasm_builder/lib/src/ir/global.dart | 27 +- pkg/wasm_builder/lib/src/ir/instructions.dart | 12 +- pkg/wasm_builder/lib/src/ir/module.dart | 78 ++++-- pkg/wasm_builder/lib/src/ir/table.dart | 70 ++++- pkg/wasm_builder/lib/src/ir/type.dart | 19 +- .../lib/src/serialize/printer.dart | 249 ++++++++++++++---- .../lib/src/serialize/sections.dart | 159 ++++++----- 23 files changed, 1092 insertions(+), 310 deletions(-) create mode 100644 pkg/dart2wasm/test/ir_tests/deferred.constant.dart create mode 100644 pkg/dart2wasm/test/ir_tests/deferred.constant.h.0.dart create mode 100644 pkg/dart2wasm/test/ir_tests/deferred.constant.h.1.dart create mode 100644 pkg/dart2wasm/test/ir_tests/deferred.constant.wat create mode 100644 pkg/dart2wasm/test/ir_tests/deferred.constant_module1.wat create mode 100644 pkg/dart2wasm/test/ir_tests/deferred.constant_module2.wat diff --git a/pkg/dart2wasm/bin/wasm2wat.dart b/pkg/dart2wasm/bin/wasm2wat.dart index 99e3ad549d6..1747cc1b3c7 100644 --- a/pkg/dart2wasm/bin/wasm2wat.dart +++ b/pkg/dart2wasm/bin/wasm2wat.dart @@ -4,13 +4,67 @@ import 'dart:io'; +import 'package:args/args.dart'; + +import 'package:wasm_builder/src/serialize/printer.dart'; import 'package:wasm_builder/wasm_builder.dart'; void main(List args) { - final input = args[0]; + final result = argParser.parse(args); + if (result.flag('help')) { + print('Usage: wasm2wat.dart [...options...] '); + print(argParser.usage); + exit(0); + } + + final input = result.rest.single; + final output = result['output'] as String?; + + List getFilter(String optionName) { + final filterStrings = result[optionName] as List; + return [for (final f in filterStrings) RegExp(f)]; + } + + final functionFilters = getFilter('function-name-filter'); + final typeFilters = getFilter('type-name-filter'); + final globalFilters = getFilter('global-name-filter'); + final settings = ModulePrintSettings( + functionFilters: functionFilters, + typeFilters: typeFilters, + globalFilters: globalFilters); + final wasmBytes = File(input).readAsBytesSync(); final deserializer = Deserializer(wasmBytes); final module = Module.deserialize(deserializer); - print(module.printAsWat()); + final wat = module.printAsWat(settings: settings); + if (output != null) { + File(output).writeAsStringSync(wat); + } else { + print(wat); + } } + +final argParser = ArgParser() + ..addMultiOption('function-name-filter', + abbr: 'f', + help: 'Only print function bodies if the function name matches. ' + 'The name filter is interpreted as a Dart `RegExp`.') + ..addMultiOption('type-name-filter', + abbr: 't', + help: 'Only print type constituents if the type name matches. ' + 'The name filter is interpreted as a Dart `RegExp`.') + ..addMultiOption('global-name-filter', + abbr: 'g', + help: 'Only print global initializers if the global name matches. ' + 'The name filter is interpreted as a Dart `RegExp`.') + ..addFlag('help', abbr: 'h', help: 'Print the help of this tool.') + ..addFlag('prefer-multiline', + abbr: 'm', + help: + 'Prefer to print global initializers & type definitions as multi line.', + defaultsTo: /* wami equivalent is false */ false) + ..addOption('output', + abbr: 'o', + help: + 'The filepath where the output will be written to (default: stdout).'); diff --git a/pkg/dart2wasm/test/ir_test.dart b/pkg/dart2wasm/test/ir_test.dart index 25dd4f84fb8..bba54edc7a1 100644 --- a/pkg/dart2wasm/test/ir_test.dart +++ b/pkg/dart2wasm/test/ir_test.dart @@ -36,6 +36,12 @@ void main(List args) async { await withTempDir((String tempDir) async { for (final dartFilename in listIrTests()) { + // Ignore helper files (e.g. tests may use deferred modules which requires + // multiple dart files to test). + if (dartFilename.contains('.h.')) { + continue; + } + if (filterRegExp != null && !filterRegExp.hasMatch(dartFilename)) { continue; } @@ -46,15 +52,17 @@ void main(List args) async { } final dartCode = File(dartFilename).readAsStringSync(); - final watFile = File(path.setExtension(dartFilename, '.wat')); final wasmFile = File(path.join( tempDir, path.setExtension(path.basename(dartFilename), '.wasm'))); + final (settings, compilerOptions) = parseSettings(dartCode); + print('\nTesting $dartFilename'); final result = await Process.run('/usr/bin/env', [ 'bash', 'pkg/dart2wasm/tool/compile_benchmark', + for (final option in compilerOptions) '--extra-compiler-option=$option', if (runFromSource) '--src', '--no-strip-wasm', '-o', @@ -69,26 +77,42 @@ void main(List args) async { continue; } - final wasmBytes = wasmFile.readAsBytesSync(); - final wat = - moduleToString(parseModule(wasmBytes), parseNameFilters(dartCode)); - if (write) { - print('-> Updated expectation file: ${watFile.path}'); - watFile.writeAsStringSync(wat); - continue; - } - if (!watFile.existsSync()) { - print('Expected "${watFile.path}" to exist.'); - failTest(); - continue; - } + final deferredModulePrefix = + '${path.withoutExtension(wasmFile.path)}_mod'; + final deferredModuleWasmFiles = wasmFile.parent + .listSync() + .whereType() + .where((fse) => + fse.path.endsWith('.wasm') && + fse.path.startsWith(deferredModulePrefix)) + .toList(); - final oldWat = watFile.readAsStringSync(); - if (oldWat != wat) { - print( - '-> Expectation mismatch. Run with `-w` to update expectation file.'); - failTest(); - continue; + for (final file in [wasmFile, ...deferredModuleWasmFiles]) { + final module = parseModule(file.readAsBytesSync()); + final wat = module.printAsWat(settings: settings); + final watFile = File(path.join(path.dirname(dartFilename), + path.setExtension(path.basename(file.path), '.wat'))); + + if (write) { + print('-> Updated expectation file: ${watFile.path}'); + watFile.writeAsStringSync(wat); + continue; + } + if (!watFile.existsSync()) { + print('Expected "${watFile.path}" to exist.'); + failTest(); + continue; + } + + final oldWat = watFile.readAsStringSync(); + if (oldWat != wat) { + print('-> Expectation of ${path.basename(watFile.path)} mismatch: '); + print('Expected:\n ${oldWat.split('\n').join('\n ')}'); + print('Actual:\n ${wat.split('\n').join('\n ')}'); + print('-> Run with `-w` to update expectation file.'); + failTest(); + continue; + } } } }); @@ -116,32 +140,52 @@ Module parseModule(Uint8List wasmBytes) { return Module.deserialize(deserializer); } -String moduleToString(Module module, List functionNameFilters) { - bool printFunctionBody(BaseFunction function) { - final name = function.functionName; - if (name == null) return false; - return functionNameFilters.any((pattern) => name.contains(pattern)); - } - - final mp = ModulePrinter(module, printFunctionBody: printFunctionBody); - for (final function in module.functions.defined) { - if (printFunctionBody(function)) { - mp.enqueueFunction(function); - } - } - return mp.print(); -} - -List parseNameFilters(String dartCode) { +(ModulePrintSettings, List) parseSettings(String dartCode) { const functionFilter = '// functionFilter='; - final filters = []; + const tableFilter = '// tableFilter='; + const globalFilter = '// globalFilter='; + const typeFilter = '// typeFilter='; + const compilerOption = '// compilerOption='; + + final functionFilters = []; + final tableFilters = []; + final globalFilters = []; + final typeFilters = []; + final compilerOptions = []; + for (final line in dartCode.split('\n')) { - if (line.startsWith(functionFilter)) { - final filter = line.substring(functionFilter.length).trim(); - if (filter.isNotEmpty) { - filters.add(RegExp(filter)); + for (final (prefix, regexpList) in [ + (functionFilter, functionFilters), + (tableFilter, tableFilters), + (globalFilter, globalFilters), + (typeFilter, typeFilters), + ]) { + if (line.startsWith(prefix)) { + final value = line.substring(prefix.length).trim(); + if (value.isNotEmpty) { + regexpList.add(RegExp(value)); + } + } + } + for (final (prefix, list) in [ + (compilerOption, compilerOptions), + ]) { + if (line.startsWith(prefix)) { + final value = line.substring(prefix.length).trim(); + if (value.isNotEmpty) { + list.add(value); + } } } } - return filters; + return ( + ModulePrintSettings( + functionFilters: functionFilters, + tableFilters: tableFilters, + globalFilters: globalFilters, + typeFilters: typeFilters, + preferMultiline: true, + scrubAbsoluteUris: true), + compilerOptions + ); } diff --git a/pkg/dart2wasm/test/ir_tests/deferred.constant.dart b/pkg/dart2wasm/test/ir_tests/deferred.constant.dart new file mode 100644 index 00000000000..c2f73bd0422 --- /dev/null +++ b/pkg/dart2wasm/test/ir_tests/deferred.constant.dart @@ -0,0 +1,32 @@ +// 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=mod.*Use +// functionFilter=H[0-1] +// tableFilter=static[0-9]+ +// globalFilter=H[0-1] +// typeFilter=H[0-1] +// compilerOption=--enable-deferred-loading +// compilerOption=--no-minify + +import 'deferred.constant.h.0.dart' deferred as h0; +import 'deferred.constant.h.1.dart' deferred as h1; + +void main() async { + // Ensure the deferred libraries are loaded. + await h0.loadLibrary(); + await h1.loadLibrary(); + + // Directly use the H0 constant in the main module. + modMainUseH0(); + + // Call to H1 module to use the constants in H1 module. + h1.modH1UseH1(); +} + +@pragma('wasm:never-inline') +void modMainUseH0() { + print(h0.constH0); + h0.constH0.fun(1); +} diff --git a/pkg/dart2wasm/test/ir_tests/deferred.constant.h.0.dart b/pkg/dart2wasm/test/ir_tests/deferred.constant.h.0.dart new file mode 100644 index 00000000000..abfcc3c90ad --- /dev/null +++ b/pkg/dart2wasm/test/ir_tests/deferred.constant.h.0.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. + +class H0 { + final void Function(int) fun; + const H0(this.fun); +} + +void globalH0Foo(int a) => print('globalH0Foo'); + +const constH0 = H0(globalH0Foo); diff --git a/pkg/dart2wasm/test/ir_tests/deferred.constant.h.1.dart b/pkg/dart2wasm/test/ir_tests/deferred.constant.h.1.dart new file mode 100644 index 00000000000..cfd211dcb61 --- /dev/null +++ b/pkg/dart2wasm/test/ir_tests/deferred.constant.h.1.dart @@ -0,0 +1,18 @@ +// 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 H1 { + final void Function(int) fun; + const H1(this.fun); +} + +void globalH1Foo(T a) => print('globalH1Bar<$T>($a)'); + +const constH1 = H1(globalH1Foo); + +@pragma('wasm:never-inline') +void modH1UseH1() { + print(constH1); + constH1.fun(1); +} diff --git a/pkg/dart2wasm/test/ir_tests/deferred.constant.wat b/pkg/dart2wasm/test/ir_tests/deferred.constant.wat new file mode 100644 index 00000000000..8abd4bf728d --- /dev/null +++ b/pkg/dart2wasm/test/ir_tests/deferred.constant.wat @@ -0,0 +1,183 @@ +(module $module0 + (type $#Top <...>) + (type $Object <...>) + (type $JSStringImpl <...>) + (type $Array <...>) + (type $BoxedInt <...>) + (type $_Type <...>) + (type $Array<_Type> <...>) + (type $_InterfaceType <...>) + (type $_FunctionType <...>) + (type $#ClosureBase <...>) + (type $#Vtable-0-1 <...>) + (type $#Closure-0-1 <...>) + (type $H1 (sub final $Object (struct + (field $field0 i32) + (field $field1 (mut i32)) + (field $fun (ref $#Closure-0-1))))) + (type $#Vtable-1-1 <...>) + (type $#Closure-1-1 <...>) + (type $#InstantiationContext-1-1 <...>) + (type $type253 <...>) + (type $H0 (sub final $Object (struct + (field $field0 i32) + (field $field1 (mut i32)) + (field $fun (ref $#Closure-0-1))))) + (type $type256 <...>) + (type $#DummyStruct <...>) + (global $S.globalH1Bar< (import "S" "globalH1Bar<") externref) + (global $S.globalH0Foo (import "S" "globalH0Foo") externref) + (global $global29 (ref $#DummyStruct) <...>) + (global $"C28 _InterfaceType" (ref $_InterfaceType) <...>) + (global $"C333 \"h0\"" (ref $JSStringImpl) <...>) + (global $"C372 _FunctionType" (ref $_FunctionType) <...>) + (global $global32 (ref $#Vtable-1-1) <...>) + (global $"C376 _FunctionType" (ref $_FunctionType) <...>) + (global $"C377 globalH1Foo tear-off" (mut (ref null $#Closure-1-1)) + (ref.null none)) + (global $"C378 InstantiationConstant(globalH1Foo)" (mut (ref null $#Closure-0-1)) + (ref.null none)) + (global $"C379 H1" (mut (ref null $H1)) + (ref.null none)) + (global $"C386 \"globalH1Bar<\"" (ref $JSStringImpl) + (i32.const 4) + (i32.const 0) + (global.get $S.globalH1Bar<) + (struct.new $JSStringImpl)) + (global $global35 (ref $#Vtable-0-1) <...>) + (global $"C397 globalH0Foo tear-off" (mut (ref null $#Closure-0-1)) + (ref.null none)) + (global $"C398 H0" (mut (ref null $H0)) + (ref.null none)) + (global $"C399 \"globalH0Foo\"" (ref $JSStringImpl) + (i32.const 4) + (i32.const 0) + (global.get $S.globalH0Foo) + (struct.new $JSStringImpl)) + (table $static1-0 (export "static1-0") 1 (ref null $type256)) + (table $static2-0 (export "static2-0") 1 (ref null $type253)) + (func $#dummy function (ref struct) -> (ref null #Top) (param $var0 (ref struct)) (result (ref null $#Top)) <...>) + (func $print (param $var0 (ref null $#Top)) (result (ref null $#Top)) <...>) + (func $"modMainUseH0 " + global.get $"C333 \"h0\"" + call $checkLibraryIsLoaded + block $label0 (result (ref $H0)) + global.get $"C398 H0" + br_on_non_null $label0 + call $"C398 H0 (lazy initializer)}" + end $label0 + call $print + drop + global.get $"C333 \"h0\"" + call $checkLibraryIsLoaded + block $label1 (result (ref $H0)) + global.get $"C398 H0" + br_on_non_null $label1 + call $"C398 H0 (lazy initializer)}" + end $label1 + drop + i64.const 1 + i32.const 0 + call_indirect $static2-0 (param i64) (result (ref null $#Top)) + drop + ) + (func $checkLibraryIsLoaded (param $var0 (ref $JSStringImpl)) <...>) + (func $"globalH1Foo tear-off dynamic call entry" (param $var0 (ref $#ClosureBase)) (param $var1 (ref $Array<_Type>)) (param $var2 (ref $Array)) (param $var3 (ref $Array)) (result (ref null $#Top)) + local.get $var1 + i32.const 0 + array.get $Array<_Type> + local.get $var2 + i32.const 0 + array.get $Array + i32.const 0 + call_indirect $static1-0 (param (ref $_Type) (ref null $#Top)) (result (ref null $#Top)) + ) + (func $"globalH1Foo tear-off trampoline" (param $var0 (ref struct)) (param $var1 (ref $_Type)) (param $var2 (ref null $#Top)) (result (ref null $#Top)) + local.get $var1 + local.get $var2 + i32.const 0 + call_indirect $static1-0 (param (ref $_Type) (ref null $#Top)) (result (ref null $#Top)) + ) + (func $dynamic call entry (param $var0 (ref $#ClosureBase)) (param $var1 (ref $Array<_Type>)) (param $var2 (ref $Array)) (param $var3 (ref $Array)) (result (ref null $#Top)) <...>) + (func $instantiation constant trampoline (param $var0 (ref struct)) (param $var1 (ref null $#Top)) (result (ref null $#Top)) <...>) + (func $"C379 H1 (lazy initializer)}" (export "func0") (result (ref $H1)) + (local $var0 (ref $#Closure-1-1)) + (local $var1 (ref $#Closure-0-1)) + (local $var2 (ref $H1)) + i32.const 106 + i32.const 0 + block $label0 (result (ref $#Closure-0-1)) + global.get $"C378 InstantiationConstant(globalH1Foo)" + br_on_non_null $label0 + i32.const 37 + i32.const 0 + block $label1 (result (ref $#Closure-1-1)) + global.get $"C377 globalH1Foo tear-off" + br_on_non_null $label1 + i32.const 37 + i32.const 0 + global.get $global29 + global.get $global32 + global.get $"C376 _FunctionType" + struct.new $#Closure-1-1 + local.tee $var0 + global.set $"C377 globalH1Foo tear-off" + local.get $var0 + end $label1 + global.get $"C28 _InterfaceType" + struct.new $#InstantiationContext-1-1 + ref.func $"dynamic call entry" + ref.func $"#dummy function (ref struct) -> (ref null #Top)" + ref.func $"instantiation constant trampoline" + struct.new $#Vtable-0-1 + global.get $"C372 _FunctionType" + struct.new $#Closure-0-1 + local.tee $var1 + global.set $"C378 InstantiationConstant(globalH1Foo)" + local.get $var1 + end $label0 + struct.new $H1 + local.tee $var2 + global.set $"C379 H1" + local.get $var2 + ) + (func $"globalH0Foo tear-off dynamic call entry" (param $var0 (ref $#ClosureBase)) (param $var1 (ref $Array<_Type>)) (param $var2 (ref $Array)) (param $var3 (ref $Array)) (result (ref null $#Top)) + local.get $var2 + i32.const 0 + array.get $Array + ref.cast $BoxedInt + struct.get $BoxedInt $value + i32.const 0 + call_indirect $static2-0 (param i64) (result (ref null $#Top)) + ) + (func $"globalH0Foo tear-off trampoline" (param $var0 (ref struct)) (param $var1 (ref null $#Top)) (result (ref null $#Top)) + local.get $var1 + ref.cast $BoxedInt + struct.get $BoxedInt $value + i32.const 0 + call_indirect $static2-0 (param i64) (result (ref null $#Top)) + ) + (func $"C398 H0 (lazy initializer)}" (result (ref $H0)) + (local $var0 (ref $#Closure-0-1)) + (local $var1 (ref $H0)) + i32.const 107 + i32.const 0 + block $label0 (result (ref $#Closure-0-1)) + global.get $"C397 globalH0Foo tear-off" + br_on_non_null $label0 + i32.const 37 + i32.const 0 + global.get $global29 + global.get $global35 + global.get $"C372 _FunctionType" + struct.new $#Closure-0-1 + local.tee $var0 + global.set $"C397 globalH0Foo tear-off" + local.get $var0 + end $label0 + struct.new $H0 + local.tee $var1 + global.set $"C398 H0" + local.get $var1 + ) +) \ No newline at end of file diff --git a/pkg/dart2wasm/test/ir_tests/deferred.constant_module1.wat b/pkg/dart2wasm/test/ir_tests/deferred.constant_module1.wat new file mode 100644 index 00000000000..dc73c20bdc8 --- /dev/null +++ b/pkg/dart2wasm/test/ir_tests/deferred.constant_module1.wat @@ -0,0 +1,59 @@ +(module $module1 + (type $#Top <...>) + (type $Object <...>) + (type $JSStringImpl <...>) + (type $Array <...>) + (type $_Type <...>) + (type $type9 <...>) + (type $#Vtable-0-1 <...>) + (type $#Closure-0-1 <...>) + (type $H1 (sub final $Object (struct + (field $field0 i32) + (field $field1 (mut i32)) + (field $fun (ref $#Closure-0-1))))) + (type $BoxedInt <...>) + (func $"C379 H1 (lazy initializer)}" (import "module0" "func0") (result (ref $H1))) + (func $print (import "module0" "func1") (param (ref null $#Top)) (result (ref null $#Top))) + (func $JSStringImpl._interpolate (import "module0" "func2") (param (ref $Array)) (result (ref $JSStringImpl))) + (global $module0.global0 (import "module0" "global0") (ref null $H1)) + (global $module0.global1 (import "module0" "global1") (ref $JSStringImpl)) + (global $module0.global2 (import "module0" "global2") (ref $JSStringImpl)) + (global $module0.global3 (import "module0" "global3") (ref $JSStringImpl)) + (func $"modH1UseH1 " (result (ref null $#Top)) + (local $var0 (ref $#Closure-0-1)) + block $label0 (result (ref $H1)) + global.get $module0.global0 + br_on_non_null $label0 + call $"C379 H1 (lazy initializer)}" + end $label0 + call $print + drop + block $label1 (result (ref $H1)) + global.get $module0.global0 + br_on_non_null $label1 + call $"C379 H1 (lazy initializer)}" + end $label1 + struct.get $H1 $fun + local.tee $var0 + struct.get $#Closure-0-1 $context + i32.const 84 + i64.const 1 + struct.new $BoxedInt + local.get $var0 + struct.get $#Closure-0-1 $vtable + struct.get $#Vtable-0-1 $closureCallEntry-0-1 + call_ref $type9 + drop + ref.null none + ) + (func $globalH1Foo (param $var0 (ref $_Type)) (param $var1 (ref null $#Top)) (result (ref null $#Top)) + global.get $module0.global1 + local.get $var0 + global.get $module0.global2 + local.get $var1 + global.get $module0.global3 + array.new_fixed $Array 5 + call $JSStringImpl._interpolate + call $print + ) +) \ No newline at end of file diff --git a/pkg/dart2wasm/test/ir_tests/deferred.constant_module2.wat b/pkg/dart2wasm/test/ir_tests/deferred.constant_module2.wat new file mode 100644 index 00000000000..5084b428c46 --- /dev/null +++ b/pkg/dart2wasm/test/ir_tests/deferred.constant_module2.wat @@ -0,0 +1,10 @@ +(module $module2 + (type $#Top <...>) + (type $JSStringImpl <...>) + (func $print (import "module0" "func1") (param (ref null $#Top)) (result (ref null $#Top))) + (global $module0.global4 (import "module0" "global4") (ref $JSStringImpl)) + (func $globalH0Foo (param $var0 i64) (result (ref null $#Top)) + global.get $module0.global4 + call $print + ) +) \ No newline at end of file diff --git a/pkg/dart2wasm/test/ir_tests/hello.dart b/pkg/dart2wasm/test/ir_tests/hello.dart index c614601b11b..ae2c23d5101 100644 --- a/pkg/dart2wasm/test/ir_tests/hello.dart +++ b/pkg/dart2wasm/test/ir_tests/hello.dart @@ -1,3 +1,7 @@ +// 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=main @pragma('wasm:never-inline') diff --git a/pkg/dart2wasm/test/ir_tests/hello.wat b/pkg/dart2wasm/test/ir_tests/hello.wat index a614a416f10..2bec6426d4f 100644 --- a/pkg/dart2wasm/test/ir_tests/hello.wat +++ b/pkg/dart2wasm/test/ir_tests/hello.wat @@ -1,11 +1,17 @@ (module $module0 - (type $#Top (struct (field $field0 i32))) - (type $JSStringImpl (sub final $#Top (struct (field $field0 i32) (field $field1 externref)))) + (type $#Top (struct + (field $field0 i32))) + (type $JSStringImpl (sub final $#Top (struct + (field $field0 i32) + (field $field1 externref)))) (global $"S.hello world" (import "S" "hello world") externref) - (global $"C327 \"hello world\"" (ref $JSStringImpl) (i32.const 4) (global.get $"S.hello world") (struct.new $JSStringImpl)) + (global $"C327 \"hello world\"" (ref $JSStringImpl) + (i32.const 4) + (global.get $"S.hello world") + (struct.new $JSStringImpl)) (func $"main " global.get $"C327 \"hello world\"" call $print ) - (func $print (param $var0 (ref $#Top))) + (func $print (param $var0 (ref $#Top)) <...>) ) \ No newline at end of file diff --git a/pkg/dart2wasm/test/ir_tests/interop.bool.wat b/pkg/dart2wasm/test/ir_tests/interop.bool.wat index 970a918c1e2..6f099b8314b 100644 --- a/pkg/dart2wasm/test/ir_tests/interop.bool.wat +++ b/pkg/dart2wasm/test/ir_tests/interop.bool.wat @@ -1,17 +1,28 @@ (module $module0 - (type $#Top (struct (field $field0 i32))) - (func $"dart2wasm._274 (import)"(import "dart2wasm" "_274") (param externref) (result externref)) - (func $"dart2wasm._275 (import)"(import "dart2wasm" "_275") (param externref) (result externref)) - (func $"dart2wasm._149 (import)"(import "dart2wasm" "_149") (param externref) (result i32)) - (func $"dart2wasm._150 (import)"(import "dart2wasm" "_150") (param i32) (result externref)) - (global $"C2 false" (ref $#Top) (i32.const 3) (struct.new $#Top)) - (global $"C40 true" (ref $#Top) (i32.const 3) (struct.new $#Top)) - (global $"boolValueNullable initialized" (mut i32) (i32.const 0)) - (global $boolValueNullable (mut (ref null $#Top)) (ref.null none)) - (global $"ktrue initialized" (mut i32) (i32.const 0)) - (global $ktrue (mut i32) (i32.const 0)) - (global $"boolValue initialized" (mut i32) (i32.const 0)) - (global $boolValue (mut i32) (i32.const 0)) + (type $#Top (struct + (field $field0 i32))) + (func $"dart2wasm._274 (import)" (import "dart2wasm" "_274") (param externref) (result externref)) + (func $"dart2wasm._275 (import)" (import "dart2wasm" "_275") (param externref) (result externref)) + (func $"dart2wasm._149 (import)" (import "dart2wasm" "_149") (param externref) (result i32)) + (func $"dart2wasm._150 (import)" (import "dart2wasm" "_150") (param i32) (result externref)) + (global $"C2 false" (ref $#Top) + (i32.const 3) + (struct.new $#Top)) + (global $"C40 true" (ref $#Top) + (i32.const 3) + (struct.new $#Top)) + (global $"boolValueNullable initialized" (mut i32) + (i32.const 0)) + (global $boolValueNullable (mut (ref null $#Top)) + (ref.null none)) + (global $"ktrue initialized" (mut i32) + (i32.const 0)) + (global $ktrue (mut i32) + (i32.const 0)) + (global $"boolValue initialized" (mut i32) + (i32.const 0)) + (global $boolValue (mut i32) + (i32.const 0)) (func $"testBoolConstant " (local $var0 externref) i32.const 1 @@ -118,11 +129,11 @@ end call $"sinkBoolNullable " ) - (func $jsifyRaw (param $var0 (ref null $#Top)) (result externref)) - (func $isDartNull (param $var0 externref) (result i32)) - (func $sinkBoolNullable (param $var0 (ref null $#Top))) - (func $_throwArgumentNullError ) - (func $ktrue implicit getter (result i32)) - (func $boolValue implicit getter (result i32)) - (func $sinkBool (param $var0 i32)) + (func $jsifyRaw (param $var0 (ref null $#Top)) (result externref) <...>) + (func $isDartNull (param $var0 externref) (result i32) <...>) + (func $sinkBoolNullable (param $var0 (ref null $#Top)) <...>) + (func $_throwArgumentNullError <...>) + (func $ktrue implicit getter (result i32) <...>) + (func $boolValue implicit getter (result i32) <...>) + (func $sinkBool (param $var0 i32) <...>) ) \ No newline at end of file diff --git a/pkg/dart2wasm/test/ir_tests/interop.double.wat b/pkg/dart2wasm/test/ir_tests/interop.double.wat index 9acbd0061a6..519704710df 100644 --- a/pkg/dart2wasm/test/ir_tests/interop.double.wat +++ b/pkg/dart2wasm/test/ir_tests/interop.double.wat @@ -1,20 +1,36 @@ (module $module0 - (type $#Top (struct (field $field0 i32))) - (type $_Type (sub $#Top (struct (field $field0 i32) (field $field1 i32)))) - (type $BoxedDouble (sub final $#Top (struct (field $field0 i32) (field $value f64)))) - (type $_TopType (sub final $_Type (struct (field $field0 i32) (field $field1 i32)))) - (func $"dart2wasm._274 (import)"(import "dart2wasm" "_274") (param f64) (result externref)) - (func $"dart2wasm._275 (import)"(import "dart2wasm" "_275") (param externref) (result externref)) - (func $"dart2wasm._147 (import)"(import "dart2wasm" "_147") (param externref) (result f64)) - (func $"dart2wasm._148 (import)"(import "dart2wasm" "_148") (param f64) (result externref)) - (global $"C311 _TopType" (ref $_TopType) (i32.const 6) (i32.const 1) (struct.new $_TopType)) - (global $"doubleValueNullable initialized" (mut i32) (i32.const 0)) - (global $doubleValueNullable (mut (ref null $BoxedDouble)) (ref.null none)) - (global $"ktrue initialized" (mut i32) (i32.const 0)) - (global $ktrue (mut i32) (i32.const 0)) - (global $"doubleValue initialized" (mut i32) (i32.const 0)) - (global $doubleValue (mut f64) (f64.const 0.0)) - (func $_TypeUniverse.isObjectInterfaceSubtype1 (param $var0 (ref $#Top)) (param $var1 i32) (param $var2 (ref $_Type)) (result i32)) + (type $#Top (struct + (field $field0 i32))) + (type $_Type (sub $#Top (struct + (field $field0 i32) + (field $field1 i32)))) + (type $BoxedDouble (sub final $#Top (struct + (field $field0 i32) + (field $value f64)))) + (type $_TopType (sub final $_Type (struct + (field $field0 i32) + (field $field1 i32)))) + (func $"dart2wasm._274 (import)" (import "dart2wasm" "_274") (param f64) (result externref)) + (func $"dart2wasm._275 (import)" (import "dart2wasm" "_275") (param externref) (result externref)) + (func $"dart2wasm._147 (import)" (import "dart2wasm" "_147") (param externref) (result f64)) + (func $"dart2wasm._148 (import)" (import "dart2wasm" "_148") (param f64) (result externref)) + (global $"C311 _TopType" (ref $_TopType) + (i32.const 6) + (i32.const 1) + (struct.new $_TopType)) + (global $"doubleValueNullable initialized" (mut i32) + (i32.const 0)) + (global $doubleValueNullable (mut (ref null $BoxedDouble)) + (ref.null none)) + (global $"ktrue initialized" (mut i32) + (i32.const 0)) + (global $ktrue (mut i32) + (i32.const 0)) + (global $"doubleValue initialized" (mut i32) + (i32.const 0)) + (global $doubleValue (mut f64) + (f64.const 0.0)) + (func $_TypeUniverse.isObjectInterfaceSubtype1 (param $var0 (ref $#Top)) (param $var1 i32) (param $var2 (ref $_Type)) (result i32) <...>) (func $"testDoubleConstant " (local $var0 externref) f64.const 1.1 @@ -170,10 +186,10 @@ end call $"sinkDoubleNullable " ) - (func $isDartNull (param $var0 externref) (result i32)) - (func $sinkDoubleNullable (param $var0 (ref null $BoxedDouble))) - (func $_throwArgumentNullError ) - (func $ktrue implicit getter (result i32)) - (func $doubleValue implicit getter (result f64)) - (func $sinkDouble (param $var0 f64)) + (func $isDartNull (param $var0 externref) (result i32) <...>) + (func $sinkDoubleNullable (param $var0 (ref null $BoxedDouble)) <...>) + (func $_throwArgumentNullError <...>) + (func $ktrue implicit getter (result i32) <...>) + (func $doubleValue implicit getter (result f64) <...>) + (func $sinkDouble (param $var0 f64) <...>) ) \ No newline at end of file diff --git a/pkg/dart2wasm/test/ir_tests/interop.int.wat b/pkg/dart2wasm/test/ir_tests/interop.int.wat index 020cc86112c..d6a6e3e5d49 100644 --- a/pkg/dart2wasm/test/ir_tests/interop.int.wat +++ b/pkg/dart2wasm/test/ir_tests/interop.int.wat @@ -1,14 +1,23 @@ (module $module0 - (type $#Top (struct (field $field0 i32))) - (type $BoxedInt (sub $#Top (struct (field $field0 i32) (field $value i64)))) - (func $"dart2wasm._274 (import)"(import "dart2wasm" "_274") (param externref) (result externref)) - (func $"dart2wasm._275 (import)"(import "dart2wasm" "_275") (param externref) (result externref)) - (global $"intValueNullable initialized" (mut i32) (i32.const 0)) - (global $intValueNullable (mut (ref null $BoxedInt)) (ref.null none)) - (global $"ktrue initialized" (mut i32) (i32.const 0)) - (global $ktrue (mut i32) (i32.const 0)) - (global $"intValue initialized" (mut i32) (i32.const 0)) - (global $intValue (mut i64) (i64.const 0)) + (type $#Top (struct + (field $field0 i32))) + (type $BoxedInt (sub $#Top (struct + (field $field0 i32) + (field $value i64)))) + (func $"dart2wasm._274 (import)" (import "dart2wasm" "_274") (param externref) (result externref)) + (func $"dart2wasm._275 (import)" (import "dart2wasm" "_275") (param externref) (result externref)) + (global $"intValueNullable initialized" (mut i32) + (i32.const 0)) + (global $intValueNullable (mut (ref null $BoxedInt)) + (ref.null none)) + (global $"ktrue initialized" (mut i32) + (i32.const 0)) + (global $ktrue (mut i32) + (i32.const 0)) + (global $"intValue initialized" (mut i32) + (i32.const 0)) + (global $intValue (mut i64) + (i64.const 0)) (func $"testIntConstant " (local $var0 externref) i64.const 1 @@ -112,13 +121,13 @@ end call $"sinkIntNullable " ) - (func $jsifyRaw (param $var0 (ref null $#Top)) (result externref)) - (func $isDartNull (param $var0 externref) (result i32)) - (func $dartifyInt (param $var0 externref) (result i64)) - (func $sinkIntNullable (param $var0 (ref null $BoxedInt))) - (func $jsifyInt (param $var0 i64) (result externref)) - (func $_throwArgumentNullError ) - (func $ktrue implicit getter (result i32)) - (func $intValue implicit getter (result i64)) - (func $sinkInt (param $var0 i64)) + (func $jsifyRaw (param $var0 (ref null $#Top)) (result externref) <...>) + (func $isDartNull (param $var0 externref) (result i32) <...>) + (func $dartifyInt (param $var0 externref) (result i64) <...>) + (func $sinkIntNullable (param $var0 (ref null $BoxedInt)) <...>) + (func $jsifyInt (param $var0 i64) (result externref) <...>) + (func $_throwArgumentNullError <...>) + (func $ktrue implicit getter (result i32) <...>) + (func $intValue implicit getter (result i64) <...>) + (func $sinkInt (param $var0 i64) <...>) ) \ No newline at end of file diff --git a/pkg/dart2wasm/test/ir_tests/interop.num.wat b/pkg/dart2wasm/test/ir_tests/interop.num.wat index 37ad3d30f1d..f5fd2b357ce 100644 --- a/pkg/dart2wasm/test/ir_tests/interop.num.wat +++ b/pkg/dart2wasm/test/ir_tests/interop.num.wat @@ -1,19 +1,34 @@ (module $module0 - (type $#Top (struct (field $field0 i32))) - (type $_Type (sub $#Top (struct (field $field0 i32) (field $field1 i32)))) - (type $BoxedDouble (sub final $#Top (struct (field $field0 i32) (field $value f64)))) - (type $_TopType (sub final $_Type (struct (field $field0 i32) (field $field1 i32)))) - (func $"dart2wasm._274 (import)"(import "dart2wasm" "_274") (param externref) (result externref)) - (func $"dart2wasm._275 (import)"(import "dart2wasm" "_275") (param externref) (result externref)) - (func $"dart2wasm._147 (import)"(import "dart2wasm" "_147") (param externref) (result f64)) - (func $"dart2wasm._148 (import)"(import "dart2wasm" "_148") (param f64) (result externref)) - (global $"C311 _TopType" (ref $_TopType) (i32.const 6) (i32.const 1) (struct.new $_TopType)) - (global $"numValueNullable initialized" (mut i32) (i32.const 0)) - (global $numValueNullable (mut (ref null $#Top)) (ref.null none)) - (global $"ktrue initialized" (mut i32) (i32.const 0)) - (global $ktrue (mut i32) (i32.const 0)) - (global $numValue (mut (ref null $#Top)) (ref.null none)) - (func $_TypeUniverse.isObjectInterfaceSubtype1 (param $var0 (ref $#Top)) (param $var1 i32) (param $var2 (ref $_Type)) (result i32)) + (type $#Top (struct + (field $field0 i32))) + (type $_Type (sub $#Top (struct + (field $field0 i32) + (field $field1 i32)))) + (type $BoxedDouble (sub final $#Top (struct + (field $field0 i32) + (field $value f64)))) + (type $_TopType (sub final $_Type (struct + (field $field0 i32) + (field $field1 i32)))) + (func $"dart2wasm._274 (import)" (import "dart2wasm" "_274") (param externref) (result externref)) + (func $"dart2wasm._275 (import)" (import "dart2wasm" "_275") (param externref) (result externref)) + (func $"dart2wasm._147 (import)" (import "dart2wasm" "_147") (param externref) (result f64)) + (func $"dart2wasm._148 (import)" (import "dart2wasm" "_148") (param f64) (result externref)) + (global $"C311 _TopType" (ref $_TopType) + (i32.const 6) + (i32.const 1) + (struct.new $_TopType)) + (global $"numValueNullable initialized" (mut i32) + (i32.const 0)) + (global $numValueNullable (mut (ref null $#Top)) + (ref.null none)) + (global $"ktrue initialized" (mut i32) + (i32.const 0)) + (global $ktrue (mut i32) + (i32.const 0)) + (global $numValue (mut (ref null $#Top)) + (ref.null none)) + (func $_TypeUniverse.isObjectInterfaceSubtype1 (param $var0 (ref $#Top)) (param $var1 i32) (param $var2 (ref $_Type)) (result i32) <...>) (func $"testNumConstant " (local $var0 externref) i64.const 1 @@ -196,12 +211,12 @@ end call $"sinkNumNullable " ) - (func $isDartNull (param $var0 externref) (result i32)) - (func $sinkNumNullable (param $var0 (ref null $BoxedDouble))) - (func $jsifyNum (param $var0 (ref $#Top)) (result externref)) - (func $jsifyInt (param $var0 i64) (result externref)) - (func $_throwArgumentNullError ) - (func $ktrue implicit getter (result i32)) - (func $numValue implicit getter (result (ref $#Top))) - (func $sinkNum (param $var0 f64)) + (func $isDartNull (param $var0 externref) (result i32) <...>) + (func $sinkNumNullable (param $var0 (ref null $BoxedDouble)) <...>) + (func $jsifyNum (param $var0 (ref $#Top)) (result externref) <...>) + (func $jsifyInt (param $var0 i64) (result externref) <...>) + (func $_throwArgumentNullError <...>) + (func $ktrue implicit getter (result i32) <...>) + (func $numValue implicit getter (result (ref $#Top)) <...>) + (func $sinkNum (param $var0 f64) <...>) ) \ No newline at end of file diff --git a/pkg/dart2wasm/test/ir_tests/interop.string.wat b/pkg/dart2wasm/test/ir_tests/interop.string.wat index 15d47df74eb..d5cac7fff87 100644 --- a/pkg/dart2wasm/test/ir_tests/interop.string.wat +++ b/pkg/dart2wasm/test/ir_tests/interop.string.wat @@ -1,16 +1,27 @@ (module $module0 - (type $#Top (struct (field $field0 i32))) - (type $JSStringImpl (sub final $#Top (struct (field $field0 i32) (field $field1 externref)))) - (func $"dart2wasm._274 (import)"(import "dart2wasm" "_274") (param externref) (result externref)) - (func $"dart2wasm._275 (import)"(import "dart2wasm" "_275") (param externref) (result externref)) + (type $#Top (struct + (field $field0 i32))) + (type $JSStringImpl (sub final $#Top (struct + (field $field0 i32) + (field $field1 externref)))) + (func $"dart2wasm._274 (import)" (import "dart2wasm" "_274") (param externref) (result externref)) + (func $"dart2wasm._275 (import)" (import "dart2wasm" "_275") (param externref) (result externref)) (global $S.a (import "S" "a") externref) - (global $"stringValueNullable initialized" (mut i32) (i32.const 0)) - (global $stringValueNullable (mut (ref null $JSStringImpl)) (ref.null none)) - (global $"ktrue initialized" (mut i32) (i32.const 0)) - (global $ktrue (mut i32) (i32.const 0)) - (global $stringValue (mut (ref null $JSStringImpl)) (ref.null none)) - (global $"C358 \"a\"" (ref $JSStringImpl) (i32.const 4) (global.get $S.a) (struct.new $JSStringImpl)) - (func $new JSStringImpl.fromRef (param $var0 externref) (result (ref $JSStringImpl))) + (global $"stringValueNullable initialized" (mut i32) + (i32.const 0)) + (global $stringValueNullable (mut (ref null $JSStringImpl)) + (ref.null none)) + (global $"ktrue initialized" (mut i32) + (i32.const 0)) + (global $ktrue (mut i32) + (i32.const 0)) + (global $stringValue (mut (ref null $JSStringImpl)) + (ref.null none)) + (global $"C358 \"a\"" (ref $JSStringImpl) + (i32.const 4) + (global.get $S.a) + (struct.new $JSStringImpl)) + (func $new JSStringImpl.fromRef (param $var0 externref) (result (ref $JSStringImpl)) <...>) (func $"testStringConstant " (local $var0 externref) global.get $"C358 \"a\"" @@ -106,11 +117,11 @@ end call $"sinkStringNullable " ) - (func $jsifyRaw (param $var0 (ref null $#Top)) (result externref)) - (func $isDartNull (param $var0 externref) (result i32)) - (func $sinkStringNullable (param $var0 (ref null $JSStringImpl))) - (func $_throwArgumentNullError ) - (func $ktrue implicit getter (result i32)) - (func $stringValue implicit getter (result (ref $JSStringImpl))) - (func $sinkString (param $var0 (ref $JSStringImpl))) + (func $jsifyRaw (param $var0 (ref null $#Top)) (result externref) <...>) + (func $isDartNull (param $var0 externref) (result i32) <...>) + (func $sinkStringNullable (param $var0 (ref null $JSStringImpl)) <...>) + (func $_throwArgumentNullError <...>) + (func $ktrue implicit getter (result i32) <...>) + (func $stringValue implicit getter (result (ref $JSStringImpl)) <...>) + (func $sinkString (param $var0 (ref $JSStringImpl)) <...>) ) \ No newline at end of file diff --git a/pkg/wasm_builder/lib/src/ir/function.dart b/pkg/wasm_builder/lib/src/ir/function.dart index 2b80945f87c..bc1cbd2315f 100644 --- a/pkg/wasm_builder/lib/src/ir/function.dart +++ b/pkg/wasm_builder/lib/src/ir/function.dart @@ -130,6 +130,7 @@ class DefinedFunction extends BaseFunction implements Serializable { p.withLocalNames(localNames, () { type.printSignatureWithNamesTo(p, oneLine: true); }); + p.write(' <...>'); p.writeln(')'); } @@ -159,6 +160,7 @@ class ImportedFunction extends BaseFunction implements Import { void printTo(IrPrinter p) { p.write('(func '); p.writeFunctionReference(this); + p.write(' '); p.writeImport(module, name); p.write(' '); type.printOneLineSignatureTo(p); diff --git a/pkg/wasm_builder/lib/src/ir/global.dart b/pkg/wasm_builder/lib/src/ir/global.dart index 95ac4777800..e654db3779a 100644 --- a/pkg/wasm_builder/lib/src/ir/global.dart +++ b/pkg/wasm_builder/lib/src/ir/global.dart @@ -28,7 +28,8 @@ abstract class Global with Indexable, Exportable { return GlobalExport(name, this); } - void printTo(IrPrinter p) => throw 'not implemented'; + void printTo(IrPrinter p, {bool includeInitializer = true}) => + throw 'not implemented'; } /// A global variable defined in a module. @@ -46,17 +47,31 @@ class DefinedGlobal extends Global implements Serializable { } @override - void printTo(IrPrinter p) { + void printTo(IrPrinter p, {bool includeInitializer = true}) { // This may generate globals this one refers to. final ip = p.dup(); - initializer.printInitializerTo(ip); + if (includeInitializer) { + initializer.printInitializerTo(ip); + } p.write('(global '); p.writeGlobalReference(this); p.write(' '); type.printTo(p); - p.write(' '); - p.write(ip.getText().trim()); + if (includeInitializer) { + if (p.preferMultiline) { + p.indent(); + p.writeln(); + } else { + p.write(' '); + } + p.write(ip.getText().trim()); + if (p.preferMultiline) { + p.deindent(); + } + } else { + p.write(' <...>'); + } p.write(')'); } } @@ -82,7 +97,7 @@ class ImportedGlobal extends Global implements Import { } @override - void printTo(IrPrinter p) { + void printTo(IrPrinter p, {bool includeInitializer = true}) { p.write('(global '); p.writeGlobalReference(this); p.write(' '); diff --git a/pkg/wasm_builder/lib/src/ir/instructions.dart b/pkg/wasm_builder/lib/src/ir/instructions.dart index 812e05002dd..13d038481d9 100644 --- a/pkg/wasm_builder/lib/src/ir/instructions.dart +++ b/pkg/wasm_builder/lib/src/ir/instructions.dart @@ -74,9 +74,15 @@ class Instructions implements Serializable { for (int k = 0; k < instructions.length; ++k) { final i = instructions[k]; if (i is End) return; - p.write(k > 0 ? ' (' : '('); - i.printTo(p); - p.write(')'); + if (p.preferMultiline) { + p.write('('); + i.printTo(p); + p.writeln(')'); + } else { + p.write(k > 0 ? ' (' : '('); + i.printTo(p); + p.write(')'); + } } } diff --git a/pkg/wasm_builder/lib/src/ir/module.dart b/pkg/wasm_builder/lib/src/ir/module.dart index 000e3d93fa1..29cda4c7b76 100644 --- a/pkg/wasm_builder/lib/src/ir/module.dart +++ b/pkg/wasm_builder/lib/src/ir/module.dart @@ -250,26 +250,72 @@ class Module implements Serializable { return sourceMapUrl; } - String printAsWat() { - final mp = ModulePrinter(this); + String printAsWat( + {ModulePrintSettings settings = const ModulePrintSettings()}) { + final mp = ModulePrinter(this, settings: settings); - // Enqueue all types, tags, globals, functions thereby making the - // printed module contain most things we care about. - for (final type in types.defined) { - if (type is! FunctionType) { - mp.enqueueType(type); + if (settings.hasFilters) { + // If we have any filters, we treat those as roots. + if (settings.typeFilters.isNotEmpty) { + for (final type in types.defined) { + final name = mp.typeNamer + .nameDefType(type, activateOnReferenceCallback: false); + if (settings.printTypeConstituents(name)) { + mp.enqueueType(type); + } + } + } + if (settings.globalFilters.isNotEmpty) { + for (final global in globals.defined) { + final name = mp.globalNamer + .nameGlobal(global, activateOnReferenceCallback: false); + if (settings.printGlobalInitializer(name)) { + mp.enqueueGlobal(global); + } + } + } + if (settings.functionFilters.isNotEmpty) { + for (final function in functions.defined) { + final name = mp.functionNamer + .nameFunction(function, activateOnReferenceCallback: false); + if (settings.printFunctionBody(name)) { + mp.enqueueFunction(function); + } + } + } + if (settings.tableFilters.isNotEmpty) { + for (final table in tables.defined) { + final name = mp.tableNamer + .nameTable(table, activateOnReferenceCallback: false); + if (settings.printFunctionBody(name)) { + mp.enqueueTable(table); + } + } + } + } else { + // Enqueue all types, tags, globals, functions thereby making the + // printed module contain most things we care about. + for (final type in types.defined) { + if (type is! FunctionType) { + mp.enqueueType(type); + } } - } - for (final tag in [...tags.imported, ...tags.defined]) { - mp.enqueueTag(tag); - } - for (final global in [...globals.imported, ...globals.defined]) { - mp.enqueueGlobal(global); - } + for (final table in [...tables.imported, ...tables.defined]) { + mp.enqueueTable(table); + } - for (final function in [...functions.imported, ...functions.defined]) { - mp.enqueueFunction(function); + for (final tag in [...tags.imported, ...tags.defined]) { + mp.enqueueTag(tag); + } + + for (final global in [...globals.imported, ...globals.defined]) { + mp.enqueueGlobal(global); + } + + for (final function in [...functions.imported, ...functions.defined]) { + mp.enqueueFunction(function); + } } return mp.print(); } diff --git a/pkg/wasm_builder/lib/src/ir/table.dart b/pkg/wasm_builder/lib/src/ir/table.dart index abfe9cfaf40..dc760d806d0 100644 --- a/pkg/wasm_builder/lib/src/ir/table.dart +++ b/pkg/wasm_builder/lib/src/ir/table.dart @@ -46,24 +46,42 @@ class DefinedTable extends Table { DefinedTable(super.enclosingModule, this.elements, super.finalizableIndex, super.type, super.minSize, super.maxSize); - void printTo(IrPrinter p) { + void printTo(IrPrinter p, {bool includeElements = true}) { // NOTE: This format differs from what V8's `wami` will print. // It makes it easier to see the exact values of the table. p.write('(table '); p.writeTableReference(this, alwaysPrint: true); - p.write(' ${elements.length} '); - p.writeValueType(type); - p.writeln(); - p.withIndent(() { - for (int i = 0; i < elements.length; ++i) { - final function = elements[i]; - if (function != null) { - p.write('(at $i '); - p.writeFunctionReference(function); - p.writeln(')'); - } + String? exportName; + for (final e in enclosingModule.exports.exported) { + if (e is TableExport && e.table == this) { + exportName = e.name; + break; } - }); + } + if (exportName != null) { + p.write(' '); + p.writeExport(exportName); + } + + p.write(' $minSize '); + p.writeValueType(type); + if (includeElements) { + if (elements.any((e) => e != null)) { + p.writeln(); + p.withIndent(() { + for (int i = 0; i < elements.length; ++i) { + final function = elements[i]; + if (function != null) { + p.write('(def $i '); + p.writeFunctionReference(function); + p.writeln(')'); + } + } + }); + } + } else { + p.write(' <...>'); + } p.write(')'); } } @@ -88,6 +106,32 @@ class ImportedTable extends Table implements Import { s.writeByte(0x01); super.serialize(s); } + + void printTo(IrPrinter p, {bool includeElements = true}) { + // NOTE: This format differs from what V8's `wami` will print. + // It makes it easier to see the exact values of the table. + p.write('(table '); + p.writeTableReference(this, alwaysPrint: true); + p.write(' '); + p.writeImport(module, name); + p.write(' $minSize '); + p.writeValueType(type); + if (includeElements) { + if (setElements.isNotEmpty) { + p.writeln(); + p.withIndent(() { + setElements.forEach((int i, function) { + p.write('(set $i '); + p.writeFunctionReference(function); + p.writeln(')'); + }); + }); + } + } else { + p.write(' <...>'); + } + p.write(')'); + } } class TableExport extends Export { diff --git a/pkg/wasm_builder/lib/src/ir/type.dart b/pkg/wasm_builder/lib/src/ir/type.dart index e38596da509..1e6969e98bb 100644 --- a/pkg/wasm_builder/lib/src/ir/type.dart +++ b/pkg/wasm_builder/lib/src/ir/type.dart @@ -875,15 +875,21 @@ abstract class DefType extends HeapType { void deserializeFillInner(Deserializer d, List existing); - void printTypeDefTo(IrPrinter p) { + void printTypeDefTo(IrPrinter p, {bool includeConstituents = true}) { // This may generate other types that this one refers to. final ip = p.dup(); - printTypeDefToInternal(ip); + if (includeConstituents) { + printTypeDefToInternal(ip); + } p.write('(type '); p.writeDefTypeReference(this); - p.write(' '); - p.write(ip.getText()); + if (includeConstituents) { + p.write(' '); + p.write(ip.getText()); + } else { + p.write(' <...>'); + } p.write(')'); } @@ -1079,7 +1085,8 @@ class FunctionType extends DefType { } void printSignatureWithNamesTo(IrPrinter p, {bool oneLine = true}) { - final indent = !oneLine && (inputs.length + outputs.length) > 2; + final indent = + !oneLine && (p.preferMultiline || (inputs.length + outputs.length) > 2); final sep = indent ? '\n ' : ' '; if (indent) p.write(sep); @@ -1202,7 +1209,7 @@ class StructType extends DataType { p.write('struct'); p.withIndent(() { for (int i = 0; i < fields.length; ++i) { - if (fields.length > 2) { + if (p.preferMultiline || fields.length > 2) { p.writeln(); } else { p.write(' '); diff --git a/pkg/wasm_builder/lib/src/serialize/printer.dart b/pkg/wasm_builder/lib/src/serialize/printer.dart index 4ac910ec48f..3bdf46da040 100644 --- a/pkg/wasm_builder/lib/src/serialize/printer.dart +++ b/pkg/wasm_builder/lib/src/serialize/printer.dart @@ -2,7 +2,6 @@ // 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 'dart:core'; import 'dart:typed_data'; import 'dart:collection'; @@ -11,11 +10,16 @@ import '../ir/ir.dart' as ir; class ModulePrinter { final ir.Module _module; - late final _typeNamer = _TypeNamer(enqueueType); - late final _globalNamer = _GlobalNamer(enqueueGlobal); - late final _functionNamer = _FunctionNamer(enqueueFunction); - late final _tagNamer = _TagNamer(enqueueTag); - late final _tableNamer = _TableNamer(enqueueTable); + late final typeNamer = + _TypeNamer(settings.scrubAbsoluteUris, _module, enqueueType); + late final globalNamer = + _GlobalNamer(settings.scrubAbsoluteUris, _module, enqueueGlobal); + late final functionNamer = + _FunctionNamer(settings.scrubAbsoluteUris, _module, enqueueFunction); + late final tagNamer = + _TagNamer(settings.scrubAbsoluteUris, _module, enqueueTag); + late final tableNamer = + _TableNamer(settings.scrubAbsoluteUris, _module, enqueueTable); final _types = {}; final _tags = {}; @@ -28,27 +32,28 @@ class ModulePrinter { /// Closure that tells us whether the body of a function should be printed or /// not. - late final bool Function(ir.BaseFunction) _printFunctionBody; + final ModulePrintSettings settings; - ModulePrinter(this._module, - {bool Function(ir.BaseFunction)? printFunctionBody}) { - _printFunctionBody = printFunctionBody ?? (_) => true; - } + ModulePrinter(this._module, {this.settings = const ModulePrintSettings()}); - IrPrinter newIrPrinter() => IrPrinter._(_module, _typeNamer, _globalNamer, - _functionNamer, _tagNamer, _tableNamer); + IrPrinter newIrPrinter() => IrPrinter._(settings.preferMultiline, _module, + typeNamer, globalNamer, functionNamer, tagNamer, tableNamer); void enqueueType(ir.DefType type) { if (!_types.containsKey(type)) { _types[type] = ''; - _generateType(type); + _generateDefType(type, + includeConstituents: settings.printTypeConstituents( + typeNamer.nameDefType(type, activateOnReferenceCallback: false))); } } void enqueueGlobal(ir.Global global) { if (!_globals.containsKey(global)) { _globals[global] = ''; - _generateGlobal(global); + _generateGlobal(global, + includeInitializer: settings.printGlobalInitializer(globalNamer + .nameGlobal(global, activateOnReferenceCallback: false))); } } @@ -73,7 +78,9 @@ class ModulePrinter { void enqueueTable(ir.Table table) { if (!_tables.containsKey(table)) { _tables[table] = ''; - _generateTable(table); + _generateTable(table, + includeElements: settings.printTableElements( + tableNamer.nameTable(table, activateOnReferenceCallback: false))); } } @@ -82,7 +89,9 @@ class ModulePrinter { while (_functionsQueue.isNotEmpty) { final fun = _functionsQueue.removeFirst(); - _generateFunction(fun, includingBody: _printFunctionBody(fun)); + _generateFunction(fun, + includingBody: settings.printFunctionBody(functionNamer + .nameFunction(fun, activateOnReferenceCallback: false))); } } @@ -121,6 +130,13 @@ class ModulePrinter { mp.writeln(); } } + for (final table in _module.tables.imported) { + final s = _tables[table]; + if (s != null) { + mp.write(s); + mp.writeln(); + } + } for (final tag in _module.tags.defined) { final s = _tags[tag]; if (s != null) { @@ -160,17 +176,21 @@ class ModulePrinter { _tags[tag] = p.getText(); } - void _generateTable(ir.Table table) { - if (table is! ir.DefinedTable) return; - + void _generateTable(ir.Table table, {required bool includeElements}) { final p = newIrPrinter(); - table.printTo(p); + if (table is ir.DefinedTable) { + table.printTo(p, includeElements: includeElements); + } else if (table is ir.ImportedTable) { + table.printTo(p, includeElements: includeElements); + } else { + return; + } _tables[table] = p.getText(); } - void _generateGlobal(ir.Global global) { + void _generateGlobal(ir.Global global, {required bool includeInitializer}) { final p = newIrPrinter(); - global.printTo(p); + global.printTo(p, includeInitializer: includeInitializer); _globals[global] = p.getText(); } @@ -191,13 +211,60 @@ class ModulePrinter { _functions[fun] = p.getText().trimRight(); } - void _generateType(ir.DefType type) { + void _generateDefType(ir.DefType type, {required bool includeConstituents}) { final p = newIrPrinter(); - type.printTypeDefTo(p); + type.printTypeDefTo(p, includeConstituents: includeConstituents); _types[type] = p.getText(); } } +class ModulePrintSettings { + final List functionFilters; + final List tableFilters; + final List globalFilters; + final List typeFilters; + final bool preferMultiline; + final bool scrubAbsoluteUris; + + const ModulePrintSettings( + {this.functionFilters = const [], + this.tableFilters = const [], + this.globalFilters = const [], + this.typeFilters = const [], + this.preferMultiline = false, + this.scrubAbsoluteUris = false}); + + bool printFunctionBody(String name) { + if (functionFilters.isEmpty) return true; + if (name.isEmpty) return false; + return functionFilters.any((pattern) => name.contains(pattern)); + } + + bool printTableElements(String name) { + if (tableFilters.isEmpty) return true; + if (name.isEmpty) return false; + return tableFilters.any((pattern) => name.contains(pattern)); + } + + bool printGlobalInitializer(String name) { + if (globalFilters.isEmpty) return true; + if (name.isEmpty) return false; + return globalFilters.any((pattern) => name.contains(pattern)); + } + + bool printTypeConstituents(String name) { + if (typeFilters.isEmpty) return true; + if (name.isEmpty) return false; + return typeFilters.any((pattern) => name.contains(pattern)); + } + + bool get hasFilters => + functionFilters.isNotEmpty || + tableFilters.isNotEmpty || + globalFilters.isNotEmpty || + typeFilters.isNotEmpty; +} + class IndentPrinter { final _buffer = StringBuffer(); int _indent = 0; @@ -268,6 +335,7 @@ class IndentPrinter { } class IrPrinter extends IndentPrinter { + final bool preferMultiline; final ir.Module module; final _TypeNamer _typeNamer; @@ -279,13 +347,13 @@ class IrPrinter extends IndentPrinter { _LocalNamer? _localNamer; final _labelNamer = _LabelNamer(); - IrPrinter._(this.module, this._typeNamer, this._globalNamer, - this._functionNamer, this._tagNamer, this._tableNamer); + IrPrinter._(this.preferMultiline, this.module, this._typeNamer, + this._globalNamer, this._functionNamer, this._tagNamer, this._tableNamer); /// Returns a new [IrPrinter] with same settings, but empty indentation, /// empty text content and no local namer. - IrPrinter dup() => IrPrinter._( - module, _typeNamer, _globalNamer, _functionNamer, _tagNamer, _tableNamer); + IrPrinter dup() => IrPrinter._(preferMultiline, module, _typeNamer, + _globalNamer, _functionNamer, _tagNamer, _tableNamer); void beginLabeledBlock(ir.Instruction? instruction) { _labelNamer.stack.add(LabelInfo(instruction)); @@ -314,7 +382,8 @@ class IrPrinter extends IndentPrinter { } void withLocalNames(Map names, void Function() fun) { - _localNamer = _LocalNamer(names); + _localNamer = + _LocalNamer(_functionNamer._scrubAbsoluteFileUris, module, names); fun(); _localNamer = null; } @@ -420,78 +489,126 @@ class IrPrinter extends IndentPrinter { } class _Namer { + final ir.Module _module; + final bool _scrubAbsoluteFileUris; + int _nextId = 0; final Map _names = {}; final void Function(T) _onReference; - _Namer(this._onReference); + late final Map _exportNames = (() { + final map = {}; + for (final export in _module.exports.exported) { + final ir.Exportable? key = switch (export) { + ir.TableExport(table: var table) => table, + ir.TagExport(tag: var tag) => tag, + ir.GlobalExport(global: var global) => global, + ir.MemoryExport(memory: var memory) => memory, + ir.FunctionExport(function: var function) => function, + _ => null, + }; + if (key != null) { + map[key] = export.name; + } + } + return map; + })(); - String _name(T key, String? name, String unnamedPrefix) { + _Namer(this._scrubAbsoluteFileUris, this._module, this._onReference); + + String _name(T key, String? name, String unnamedPrefix, + bool activateOnReferenceCallback) { final existing = _names[key]; if (existing != null) return existing; - _onReference(key); + if (activateOnReferenceCallback) { + _onReference(key); + } + if (name == null) { + if (key is ir.Import) { + name = '${key.module}.${key.name}'; + } else if (key is ir.Exportable) { + name = _exportNames[key]; + } + } + if (name != null && _scrubAbsoluteFileUris) { + name = _sanitizeAbsoluteFileUris(name); + } final sanitizedName = name != null ? _sanitizeName(name) : '$unnamedPrefix${_nextId++}'; - return _names[key] ??= '\$$sanitizedName'; + final quotedName = '\$$sanitizedName'; + return activateOnReferenceCallback + ? _names[key] ??= quotedName + : quotedName; } } class _FunctionNamer extends _Namer { - _FunctionNamer(super.onReference); + _FunctionNamer(super.scrubAbsoluteUris, super.module, super.onReference); - String nameFunction(ir.BaseFunction function) { - return super._name(function, function.functionName, ''); + String nameFunction(ir.BaseFunction function, + {bool activateOnReferenceCallback = true}) { + return super._name( + function, function.functionName, '', activateOnReferenceCallback); } } class _TagNamer extends _Namer { - _TagNamer(super.onReference); + _TagNamer(super.scrubAbsoluteUris, super.module, super.onReference); - String nameTag(ir.Tag tag) { - return super._name(tag, null, 'tag'); + String nameTag(ir.Tag tag, {bool activateOnReferenceCallback = true}) { + return super._name(tag, null, 'tag', activateOnReferenceCallback); } } class _TableNamer extends _Namer { - _TableNamer(super.onReference); + _TableNamer(super.scubUris, super.module, super.onReference); - String nameTable(ir.Table? table) { - if (table == null) { - return '\$table0'; - } - return super._name(table, null, 'table'); + String nameTable(ir.Table? table, {bool activateOnReferenceCallback = true}) { + table ??= _module.tables.defined.first; + + // Try to use cache first to avoid O(n) scan in the exports. + final existing = _names[table]; + if (existing != null) return existing; + + final prefix = table is ir.ImportedTable ? 'itable' : 'dtable'; + return super._name(table, null, prefix, activateOnReferenceCallback); } } class _TypeNamer extends _Namer { - _TypeNamer(super.onReference); + _TypeNamer(super.scrubAbsoluteUris, super.module, super.onReference); - String nameDefType(ir.DefType type) { - return super._name(type, type is ir.DataType ? type.name : null, 'type'); + String nameDefType(ir.DefType type, + {bool activateOnReferenceCallback = true}) { + return super._name(type, type is ir.DataType ? type.name : null, 'type', + activateOnReferenceCallback); } } class _LocalNamer extends _Namer { final Map _namedVariables; - _LocalNamer(this._namedVariables) : super((_) {}); + _LocalNamer(bool scrubAbsoluteUris, ir.Module module, this._namedVariables) + : super(scrubAbsoluteUris, module, (_) {}); - String nameLocal(int index) { - return super._name(index, _namedVariables[index], 'var'); + String nameLocal(int index, {bool activateOnReferenceCallback = true}) { + return super._name( + index, _namedVariables[index], 'var', activateOnReferenceCallback); } } class _GlobalNamer extends _Namer { - _GlobalNamer(super.onReference); + _GlobalNamer(super.scrubAbsoluteUris, super.module, super.onReference); - String nameGlobal(ir.Global global) { + String nameGlobal(ir.Global global, + {bool activateOnReferenceCallback = true}) { String? gn = global.globalName; if (gn == null && global is ir.ImportedGlobal) { gn = '${global.module}.${global.name}'; } - return super._name(global, gn, 'global'); + return super._name(global, gn, 'global', activateOnReferenceCallback); } } @@ -577,6 +694,28 @@ String _escapeString(String s) { return '$sb'; } +String _sanitizeAbsoluteFileUris(String name) { + int globalStart = 0; + while (true) { + final start = name.indexOf('file:///', globalStart); + if (start < 0) { + break; + } + final end = name.indexOf('.dart', start); + if (end < 0) { + break; + } + final uri = name.substring(start, end); + final slash = uri.lastIndexOf('/'); + final first = name.substring(0, start); + final filename = name.substring(start + slash + 1, end); + final last = name.substring(end + '.dart'.length); + name = '${first}file:///.../$filename.dart$last'; + globalStart = name.length - last.length; + } + return name; +} + String _sanitizeName(String s) { final units = s.codeUnits; for (int i = 0; i < units.length; ++i) { diff --git a/pkg/wasm_builder/lib/src/serialize/sections.dart b/pkg/wasm_builder/lib/src/serialize/sections.dart index dcd0e1a9a6b..7077a61c9b8 100644 --- a/pkg/wasm_builder/lib/src/serialize/sections.dart +++ b/pkg/wasm_builder/lib/src/serialize/sections.dart @@ -506,29 +506,88 @@ class _TableElement implements _Element { @override void serialize(Serializer s) { - if (table.index != 0) { - s.writeByte(0x06); - s.writeUnsigned(table.index); + final int kind; + if (table.index == 0) { + kind = 0x00; + s.writeByte(kind); } else { - s.writeByte(0x00); + kind = 0x06; + s.writeByte(kind); + s.writeUnsigned(table.index); } - s.writeByte(0x41); // i32.const - s.writeSigned(startIndex); - s.writeByte(0x0B); // end - if (table.index != 0) { + + ir.I32Const(startIndex).serialize(s); + ir.End().serialize(s); + + if (kind == 0x06) { s.write(table.type); } s.writeUnsigned(entries.length); for (var entry in entries) { - if (table.index == 0) { + if (kind == 0x0) { s.writeUnsigned(entry.index); } else { - s.writeByte(0xD2); // ref.func - s.writeSigned(entry.index); - s.writeByte(0x0B); // end + ir.RefFunc(entry).serialize(s); + ir.End().serialize(s); } } } + + static _TableElement deserialize( + Deserializer d, + ir.Module module, + ir.Types types, + ir.Functions functions, + ir.Tables tables, + ir.Globals globals, + ) { + final int tableIndex; + final kind = d.readByte(); + switch (kind) { + case 0x00: + tableIndex = 0; + break; + case 0x06: + tableIndex = d.readUnsigned(); + break; + default: + throw "unsupported element segment kind $kind"; + } + + final i0 = ir.Instruction.deserializeConst(d, types, functions, globals); + final i1 = ir.Instruction.deserializeConst(d, types, functions, globals); + if (i0 is! ir.I32Const || i1 is! ir.End) { + throw StateError('Expected offset to be encoded as ' + '`(i32.const ) (end)`. ' + 'Got instead: (${i0.name}) (${i1.name})'); + } + final offset = i0.value; + + if (kind == 0x06) { + ir.RefType.deserialize(d, types.defined); + } + + final table = tables[tableIndex]; + final tableElement = _TableElement(table, offset); + final count = d.readUnsigned(); + for (int i = 0; i < count; i++) { + if (kind == 0x0) { + tableElement.entries.add(functions[d.readUnsigned()]); + } else { + final i0 = + ir.Instruction.deserializeConst(d, types, functions, globals); + final i1 = + ir.Instruction.deserializeConst(d, types, functions, globals); + if (i0 is! ir.RefFunc || i1 is! ir.End) { + throw StateError('Expected function reference to be encoded as ' + '`(ref.func ) (end)`. ' + 'Got instead: (${i0.name}) (${i1.name})'); + } + tableElement.entries.add(i0.function); + } + } + return tableElement; + } } class _DeclaredElement implements _Element { @@ -547,6 +606,16 @@ class _DeclaredElement implements _Element { s.writeUnsigned(entry.index); } } + + static _DeclaredElement deserialize(Deserializer d, ir.Functions functions) { + if (d.readByte() != 0x03) throw 'bad encoding'; + + final elemkind = d.readByte(); + if (elemkind != 0x00) throw "unsupported elemkind"; + + final declaredFunctions = d.readList((d) => functions[d.readUnsigned()]); + return _DeclaredElement(declaredFunctions); + } } class ElementSection extends Section { @@ -622,58 +691,28 @@ class ElementSection extends Section { final declaredFunctions = []; final count = d.readUnsigned(); for (int i = 0; i < count; i++) { - final kind = d.readByte(); - int tableIndex; - switch (kind) { - case 0x00: - tableIndex = 0; - break; - case 0x06: - tableIndex = d.readUnsigned(); - break; - case 0x03: - final elemkind = d.readByte(); - if (elemkind != 0x00) throw "unsupported elemkind"; - final funcs = d.readList((d) => functions[d.readUnsigned()]); - declaredFunctions.addAll(funcs); - continue; - default: - throw "unsupported element segment kind $kind"; + if (d.peekByte() == 0x03) { + final declaredElement = _DeclaredElement.deserialize(d, functions); + declaredFunctions.addAll(declaredElement.entries); + continue; } + final tableElement = _TableElement.deserialize( + d, module, types, functions, tables, globals); + for (int i = 0; i < tableElement.entries.length; i++) { + final table = tableElement.table; + final offset = tableElement.startIndex; + final function = tableElement.entries[i]; - final offsetInitializer = - ir.Instructions.deserializeConst(d, types, functions, globals); - final instructions = offsetInitializer.instructions; - assert(instructions.length == 2 && - instructions[0] is ir.I32Const && - instructions[1] is ir.End); - final offset = (instructions[0] as ir.I32Const).value; - - if (kind == 0x06) { - ir.RefType.deserialize(d, types.defined); - } - - final table = tables[tableIndex]; - if (table is ir.DefinedTable) { - final count = d.readUnsigned(); - for (int j = 0; j < count; j++) { - late ir.BaseFunction func; - if (tableIndex == 0) { - final funcIndex = d.readUnsigned(); - func = functions[funcIndex]; - } else { - final funcInitializer = - ir.Instructions.deserializeConst(d, types, functions, globals); - final refFunc = funcInitializer.instructions.single as ir.RefFunc; - func = refFunc.function; + if (table is ir.DefinedTable) { + if (table.elements.length <= offset + i) { + table.elements.length = offset + i + 1; } - if (table.elements.length <= offset + j) { - table.elements.length = offset + j + 1; - } - table.elements[offset + j] = func; + table.elements[offset + i] = function; + } else if (table is ir.ImportedTable) { + table.setElements[offset + i] = function; + } else { + throw "unsupported table type $table"; } - } else { - throw "unsupported table type"; } }