[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:
committed by
Commit Queue
parent
3cb8ec842a
commit
92f500b7b7
@@ -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).');
|
||||
|
||||
@@ -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
|
||||
)
|
||||
)
|
||||
@@ -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')
|
||||
|
||||
@@ -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)) <...>)
|
||||
)
|
||||
@@ -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) <...>)
|
||||
)
|
||||
@@ -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) <...>)
|
||||
)
|
||||
@@ -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) <...>)
|
||||
)
|
||||
@@ -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) <...>)
|
||||
)
|
||||
@@ -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);
|
||||
|
||||
@@ -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(')');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(' ');
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user