[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 <kustermann@google.com>
Reviewed-by: Nate Biggs <natebiggs@google.com>
This commit is contained in:
Martin Kustermann
2025-10-23 01:11:22 -07:00
committed by Commit Queue
parent 3cb8ec842a
commit 92f500b7b7
23 changed files with 1092 additions and 310 deletions
+56 -2
View File
@@ -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<String> args) {
final input = args[0];
final result = argParser.parse(args);
if (result.flag('help')) {
print('Usage: wasm2wat.dart [...options...] <input.wasm>');
print(argParser.usage);
exit(0);
}
final input = result.rest.single;
final output = result['output'] as String?;
List<RegExp> getFilter(String optionName) {
final filterStrings = result[optionName] as List<String>;
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).');
+87 -43
View File
@@ -36,6 +36,12 @@ void main(List<String> 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<String> 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<String> 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<File>()
.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<RegExp> 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<RegExp> parseNameFilters(String dartCode) {
(ModulePrintSettings, List<String>) parseSettings(String dartCode) {
const functionFilter = '// functionFilter=';
final filters = <RegExp>[];
const tableFilter = '// tableFilter=';
const globalFilter = '// globalFilter=';
const typeFilter = '// typeFilter=';
const compilerOption = '// compilerOption=';
final functionFilters = <RegExp>[];
final tableFilters = <RegExp>[];
final globalFilters = <RegExp>[];
final typeFilters = <RegExp>[];
final compilerOptions = <String>[];
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
);
}
@@ -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);
}
@@ -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);
@@ -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>(T a) => print('globalH1Bar<$T>($a)');
const constH1 = H1(globalH1Foo);
@pragma('wasm:never-inline')
void modH1UseH1() {
print(constH1);
constH1.fun(1);
}
@@ -0,0 +1,183 @@
(module $module0
(type $#Top <...>)
(type $Object <...>)
(type $JSStringImpl <...>)
(type $Array<Object?> <...>)
(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<int>)" (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 <noInline>"
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<Object?>)) (param $var3 (ref $Array<Object?>)) (result (ref null $#Top))
local.get $var1
i32.const 0
array.get $Array<_Type>
local.get $var2
i32.const 0
array.get $Array<Object?>
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<Object?>)) (param $var3 (ref $Array<Object?>)) (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<int>)"
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<int>)"
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<Object?>)) (param $var3 (ref $Array<Object?>)) (result (ref null $#Top))
local.get $var2
i32.const 0
array.get $Array<Object?>
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
)
)
@@ -0,0 +1,59 @@
(module $module1
(type $#Top <...>)
(type $Object <...>)
(type $JSStringImpl <...>)
(type $Array<Object?> <...>)
(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<Object?>)) (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 <noInline>" (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<Object?> 5
call $JSStringImpl._interpolate
call $print
)
)
@@ -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
)
)
+4
View File
@@ -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')
+10 -4
View File
@@ -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 <noInline>"
global.get $"C327 \"hello world\""
call $print
)
(func $print (param $var0 (ref $#Top)))
(func $print (param $var0 (ref $#Top)) <...>)
)
+31 -20
View File
@@ -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 <noInline>"
(local $var0 externref)
i32.const 1
@@ -118,11 +129,11 @@
end
call $"sinkBoolNullable <noInline>"
)
(func $jsifyRaw (param $var0 (ref null $#Top)) (result externref))
(func $isDartNull (param $var0 externref) (result i32))
(func $sinkBoolNullable <noInline> (param $var0 (ref null $#Top)))
(func $_throwArgumentNullError <noInline> )
(func $ktrue implicit getter (result i32))
(func $boolValue implicit getter (result i32))
(func $sinkBool <noInline> (param $var0 i32))
(func $jsifyRaw (param $var0 (ref null $#Top)) (result externref) <...>)
(func $isDartNull (param $var0 externref) (result i32) <...>)
(func $sinkBoolNullable <noInline> (param $var0 (ref null $#Top)) <...>)
(func $_throwArgumentNullError <noInline> <...>)
(func $ktrue implicit getter (result i32) <...>)
(func $boolValue implicit getter (result i32) <...>)
(func $sinkBool <noInline> (param $var0 i32) <...>)
)
+38 -22
View File
@@ -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 <noInline>"
(local $var0 externref)
f64.const 1.1
@@ -170,10 +186,10 @@
end
call $"sinkDoubleNullable <noInline>"
)
(func $isDartNull (param $var0 externref) (result i32))
(func $sinkDoubleNullable <noInline> (param $var0 (ref null $BoxedDouble)))
(func $_throwArgumentNullError <noInline> )
(func $ktrue implicit getter (result i32))
(func $doubleValue implicit getter (result f64))
(func $sinkDouble <noInline> (param $var0 f64))
(func $isDartNull (param $var0 externref) (result i32) <...>)
(func $sinkDoubleNullable <noInline> (param $var0 (ref null $BoxedDouble)) <...>)
(func $_throwArgumentNullError <noInline> <...>)
(func $ktrue implicit getter (result i32) <...>)
(func $doubleValue implicit getter (result f64) <...>)
(func $sinkDouble <noInline> (param $var0 f64) <...>)
)
+28 -19
View File
@@ -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 <noInline>"
(local $var0 externref)
i64.const 1
@@ -112,13 +121,13 @@
end
call $"sinkIntNullable <noInline>"
)
(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 <noInline> (param $var0 (ref null $BoxedInt)))
(func $jsifyInt (param $var0 i64) (result externref))
(func $_throwArgumentNullError <noInline> )
(func $ktrue implicit getter (result i32))
(func $intValue implicit getter (result i64))
(func $sinkInt <noInline> (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 <noInline> (param $var0 (ref null $BoxedInt)) <...>)
(func $jsifyInt (param $var0 i64) (result externref) <...>)
(func $_throwArgumentNullError <noInline> <...>)
(func $ktrue implicit getter (result i32) <...>)
(func $intValue implicit getter (result i64) <...>)
(func $sinkInt <noInline> (param $var0 i64) <...>)
)
+38 -23
View File
@@ -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 <noInline>"
(local $var0 externref)
i64.const 1
@@ -196,12 +211,12 @@
end
call $"sinkNumNullable <noInline>"
)
(func $isDartNull (param $var0 externref) (result i32))
(func $sinkNumNullable <noInline> (param $var0 (ref null $BoxedDouble)))
(func $jsifyNum (param $var0 (ref $#Top)) (result externref))
(func $jsifyInt (param $var0 i64) (result externref))
(func $_throwArgumentNullError <noInline> )
(func $ktrue implicit getter (result i32))
(func $numValue implicit getter (result (ref $#Top)))
(func $sinkNum <noInline> (param $var0 f64))
(func $isDartNull (param $var0 externref) (result i32) <...>)
(func $sinkNumNullable <noInline> (param $var0 (ref null $BoxedDouble)) <...>)
(func $jsifyNum (param $var0 (ref $#Top)) (result externref) <...>)
(func $jsifyInt (param $var0 i64) (result externref) <...>)
(func $_throwArgumentNullError <noInline> <...>)
(func $ktrue implicit getter (result i32) <...>)
(func $numValue implicit getter (result (ref $#Top)) <...>)
(func $sinkNum <noInline> (param $var0 f64) <...>)
)
+29 -18
View File
@@ -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 <noInline>"
(local $var0 externref)
global.get $"C358 \"a\""
@@ -106,11 +117,11 @@
end
call $"sinkStringNullable <noInline>"
)
(func $jsifyRaw (param $var0 (ref null $#Top)) (result externref))
(func $isDartNull (param $var0 externref) (result i32))
(func $sinkStringNullable <noInline> (param $var0 (ref null $JSStringImpl)))
(func $_throwArgumentNullError <noInline> )
(func $ktrue implicit getter (result i32))
(func $stringValue implicit getter (result (ref $JSStringImpl)))
(func $sinkString <noInline> (param $var0 (ref $JSStringImpl)))
(func $jsifyRaw (param $var0 (ref null $#Top)) (result externref) <...>)
(func $isDartNull (param $var0 externref) (result i32) <...>)
(func $sinkStringNullable <noInline> (param $var0 (ref null $JSStringImpl)) <...>)
(func $_throwArgumentNullError <noInline> <...>)
(func $ktrue implicit getter (result i32) <...>)
(func $stringValue implicit getter (result (ref $JSStringImpl)) <...>)
(func $sinkString <noInline> (param $var0 (ref $JSStringImpl)) <...>)
)
@@ -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);
+21 -6
View File
@@ -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(' ');
@@ -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(')');
}
}
}
+62 -16
View File
@@ -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();
}
+57 -13
View File
@@ -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 {
+13 -6
View File
@@ -875,15 +875,21 @@ abstract class DefType extends HeapType {
void deserializeFillInner(Deserializer d, List<DefType> 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(' ');
+194 -55
View File
@@ -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 = <ir.DefType, String>{};
final _tags = <ir.Tag, String>{};
@@ -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<RegExp> functionFilters;
final List<RegExp> tableFilters;
final List<RegExp> globalFilters;
final List<RegExp> 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<int, String> 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<T> {
final ir.Module _module;
final bool _scrubAbsoluteFileUris;
int _nextId = 0;
final Map<T, String> _names = {};
final void Function(T) _onReference;
_Namer(this._onReference);
late final Map<ir.Exportable, String> _exportNames = (() {
final map = <ir.Exportable, String>{};
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<ir.BaseFunction> {
_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<ir.Tag> {
_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<ir.Table> {
_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<ir.DefType> {
_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<int> {
final Map<int, String> _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<ir.Global> {
_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) {
@@ -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 <value>) (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 <value>) (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 = <ir.BaseFunction>[];
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";
}
}