From e63dcafc0ccaaa21559125e5818fa53fd0a3d301 Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Wed, 15 Oct 2025 12:36:50 -0700 Subject: [PATCH] [dart2wasm] Add wasm module printing functionality. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds support for printing module IR as text format. For convenience we add a `pkg/dart2wasm/bin/wami.dart` that produces very similar output to V8's `wami`. The goal is to use this to write size/perf optimization tests by dumping IR into expectation files (will add this infrastructure in a future CL) Issue https://github.com/dart-lang/sdk/issues/60928 Change-Id: I42d19c2b8c6242f55693d6ed5d844a5c1ecb1f39 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/454600 Commit-Queue: Martin Kustermann Reviewed-by: Ömer Ağacan --- pkg/dart2wasm/bin/wasm2wat.dart | 16 + pkg/dart2wasm/test/wasm2wat_test.dart | 56 + pkg/wasm_builder/lib/src/ir/function.dart | 52 + pkg/wasm_builder/lib/src/ir/global.dart | 29 + pkg/wasm_builder/lib/src/ir/instruction.dart | 1293 +++++++++++++++++ pkg/wasm_builder/lib/src/ir/instructions.dart | 74 + pkg/wasm_builder/lib/src/ir/module.dart | 25 + pkg/wasm_builder/lib/src/ir/table.dart | 22 + pkg/wasm_builder/lib/src/ir/tables.dart | 2 + pkg/wasm_builder/lib/src/ir/tags.dart | 23 + pkg/wasm_builder/lib/src/ir/type.dart | 137 +- .../lib/src/serialize/printer.dart | 663 +++++++++ 12 files changed, 2390 insertions(+), 2 deletions(-) create mode 100644 pkg/dart2wasm/bin/wasm2wat.dart create mode 100644 pkg/dart2wasm/test/wasm2wat_test.dart create mode 100644 pkg/wasm_builder/lib/src/serialize/printer.dart diff --git a/pkg/dart2wasm/bin/wasm2wat.dart b/pkg/dart2wasm/bin/wasm2wat.dart new file mode 100644 index 00000000000..99e3ad549d6 --- /dev/null +++ b/pkg/dart2wasm/bin/wasm2wat.dart @@ -0,0 +1,16 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:io'; + +import 'package:wasm_builder/wasm_builder.dart'; + +void main(List args) { + final input = args[0]; + final wasmBytes = File(input).readAsBytesSync(); + + final deserializer = Deserializer(wasmBytes); + final module = Module.deserialize(deserializer); + print(module.printAsWat()); +} diff --git a/pkg/dart2wasm/test/wasm2wat_test.dart b/pkg/dart2wasm/test/wasm2wat_test.dart new file mode 100644 index 00000000000..94fb59d81e3 --- /dev/null +++ b/pkg/dart2wasm/test/wasm2wat_test.dart @@ -0,0 +1,56 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:path/path.dart' as path; +import 'package:wasm_builder/wasm_builder.dart'; + +import 'self_compile_test.dart' show withTempDir, run; + +Future main() async { + if (!Platform.isLinux && !Platform.isMacOS) return; + + await withTempDir((String tempDir) async { + final dartFilename = 'third_party/flute/benchmarks/lib/complex.dart'; + + final wasmFilename = path.join(tempDir, 'flute.wasm'); + final optWasmFilename = path.join(tempDir, 'flute.opt.wasm'); + final wasmFile = File(wasmFilename); + final optWasmFile = File(wasmFilename); + + // Ensure we can print unoptimized dart2wasm modules + await run([ + Platform.executable, + 'compile', + 'wasm', + '-O0', + dartFilename, + '-o', + wasmFilename, + ]); + wasmPrint(wasmFile.readAsBytesSync()); + + // Ensure we can print wasm-opt optimized wasm modules + await run([ + Platform.executable, + 'compile', + 'wasm', + '-O3', + dartFilename, + '-o', + optWasmFilename, + ]); + wasmPrint(optWasmFile.readAsBytesSync()); + + // Temporary files will be deleted when returning to [withTempDir]. + }); +} + +void wasmPrint(Uint8List wasmBytes) { + final deserializer = Deserializer(wasmBytes); + final module = Module.deserialize(deserializer); + print('len = ${module.printAsWat().length}'); +} diff --git a/pkg/wasm_builder/lib/src/ir/function.dart b/pkg/wasm_builder/lib/src/ir/function.dart index 42701b44869..675501cdbaa 100644 --- a/pkg/wasm_builder/lib/src/ir/function.dart +++ b/pkg/wasm_builder/lib/src/ir/function.dart @@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import '../serialize/serialize.dart'; +import '../serialize/printer.dart'; import 'ir.dart'; /// A local variable defined in a function. @@ -12,6 +13,15 @@ class Local { Local(this.index, this.type); + void printTo(IrPrinter p, Map localNames, + {bool isParam = false}) { + p.write(isParam ? 'param' : 'local'); + p.write(' '); + p.writeLocalIndexReference(index); + p.write(' '); + p.writeValueType(type); + } + @override String toString() => "$index"; } @@ -82,6 +92,39 @@ class DefinedFunction extends BaseFunction implements Serializable { s.writeData(localS); } + void printTo(IrPrinter p) { + p.write('(func '); + p.writeFunctionReference(this); + String? exportName; + for (final f in enclosingModule.exports.exported) { + if (f is FunctionExport && f.function == this) { + exportName = f.name; + break; + } + } + if (exportName != null) { + p.write(' '); + p.writeExport(exportName); + } + + p.withLocalNames(localNames, () { + if (type.inputs.isNotEmpty || type.outputs.isNotEmpty) { + p.write(' '); + type.printSignatureWithNamesTo(p, oneLine: true); + } + p.writeln(''); + p.withIndent(() { + for (int i = type.inputs.length; i < locals.length; ++i) { + p.write('('); + locals[i].printTo(p, localNames); + p.writeln(')'); + } + body.printTo(p); + }); + }); + p.write(')'); + } + @override String toString() => functionName ?? "#$finalizableIndex"; } @@ -105,6 +148,15 @@ class ImportedFunction extends BaseFunction implements Import { s.writeUnsigned(type.index); } + void printTo(IrPrinter p) { + p.write('(func '); + p.writeFunctionReference(this); + p.writeImport(module, name); + p.write(' '); + type.printOneLineSignatureTo(p); + p.write(')'); + } + @override String toString() => "$module.$name"; } diff --git a/pkg/wasm_builder/lib/src/ir/global.dart b/pkg/wasm_builder/lib/src/ir/global.dart index cc4db79e488..95ac4777800 100644 --- a/pkg/wasm_builder/lib/src/ir/global.dart +++ b/pkg/wasm_builder/lib/src/ir/global.dart @@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import '../serialize/serialize.dart'; +import '../serialize/printer.dart'; import 'ir.dart'; /// An (imported or defined) global variable. @@ -26,6 +27,8 @@ abstract class Global with Indexable, Exportable { Export buildExport(String name) { return GlobalExport(name, this); } + + void printTo(IrPrinter p) => throw 'not implemented'; } /// A global variable defined in a module. @@ -41,6 +44,21 @@ class DefinedGlobal extends Global implements Serializable { s.write(type); s.write(initializer); } + + @override + void printTo(IrPrinter p) { + // This may generate globals this one refers to. + final ip = p.dup(); + initializer.printInitializerTo(ip); + + p.write('(global '); + p.writeGlobalReference(this); + p.write(' '); + type.printTo(p); + p.write(' '); + p.write(ip.getText().trim()); + p.write(')'); + } } /// An imported global variable. @@ -62,6 +80,17 @@ class ImportedGlobal extends Global implements Import { s.writeByte(0x03); s.write(type); } + + @override + void printTo(IrPrinter p) { + p.write('(global '); + p.writeGlobalReference(this); + p.write(' '); + p.writeImport(module, name); + p.write(' '); + p.writeValueType(type.type); + p.write(')'); + } } class GlobalExport extends Export { diff --git a/pkg/wasm_builder/lib/src/ir/instruction.dart b/pkg/wasm_builder/lib/src/ir/instruction.dart index a1b578a01f6..1b5f617d1e7 100644 --- a/pkg/wasm_builder/lib/src/ir/instruction.dart +++ b/pkg/wasm_builder/lib/src/ir/instruction.dart @@ -2,6 +2,7 @@ // 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 '../serialize/printer.dart'; import '../serialize/serialize.dart'; import 'ir.dart'; @@ -25,6 +26,16 @@ abstract class Instruction implements Serializable { /// segments, data segments. bool get isConstant => false; + /// The name of the instruction. + String get name; + + /// Prints the text representation of this instruction to [p]. + /// + /// Instructions that have fields should override this. + void printTo(IrPrinter p) { + p.write(name); + } + static Instruction deserializeConst( Deserializer d, Types types, Functions functions, Globals globals, {bool isConstOnlyUse = true}) { @@ -600,12 +611,18 @@ class Unreachable extends SingleByteInstruction { const Unreachable() : super(0x00); static Unreachable deserialize(Deserializer d) => const Unreachable(); + + @override + String get name => 'unreachable'; } class Nop extends SingleByteInstruction { const Nop() : super(0x01); static Nop deserialize(Deserializer d) => const Nop(); + + @override + String get name => 'nop'; } class BeginNoEffectBlock extends Instruction { @@ -621,6 +638,16 @@ class BeginNoEffectBlock extends Instruction { s.writeByte(0x02); s.writeByte(0x40); } + + @override + String get name => 'block'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeLabelDefinition(0); + } } class BeginOneOutputBlock extends Instruction { @@ -642,6 +669,20 @@ class BeginOneOutputBlock extends Instruction { s.writeByte(0x02); s.write(type); } + + @override + String get name => 'block'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeLabelDefinition(0); + p.write(' '); + p.write('(result '); + p.writeValueType(type); + p.write(')'); + } } class BeginFunctionBlock extends Instruction { @@ -661,6 +702,18 @@ class BeginFunctionBlock extends Instruction { s.writeByte(0x02); s.write(type); } + + @override + String get name => 'block'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeLabelDefinition(0); + p.write(' '); + p.writeFunctionType(type); + } } class BeginNoEffectLoop extends Instruction { @@ -676,6 +729,16 @@ class BeginNoEffectLoop extends Instruction { d.readByte(); return const BeginNoEffectLoop(); } + + @override + String get name => 'loop'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeLabelDefinition(0); + } } class BeginOneOutputLoop extends Instruction { @@ -696,6 +759,18 @@ class BeginOneOutputLoop extends Instruction { s.writeByte(0x03); s.write(type); } + + @override + String get name => 'loop'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeLabelDefinition(0); + p.write(' '); + p.writeValueType(type); + } } class BeginFunctionLoop extends Instruction { @@ -711,6 +786,18 @@ class BeginFunctionLoop extends Instruction { s.writeByte(0x03); s.write(type); } + + @override + String get name => 'loop'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeLabelDefinition(0); + p.write(' '); + p.writeFunctionType(type); + } } class BeginNoEffectIf extends Instruction { @@ -726,6 +813,9 @@ class BeginNoEffectIf extends Instruction { d.readByte(); return const BeginNoEffectIf(); } + + @override + String get name => 'if'; } class BeginOneOutputIf extends Instruction { @@ -746,6 +836,17 @@ class BeginOneOutputIf extends Instruction { s.writeByte(0x04); s.write(type); } + + @override + String get name => 'if'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' (result '); + p.writeValueType(type); + p.write(')'); + } } class BeginFunctionIf extends Instruction { @@ -761,12 +862,25 @@ class BeginFunctionIf extends Instruction { s.writeByte(0x04); s.write(type); } + + @override + String get name => 'if'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeFunctionType(type); + } } class Else extends SingleByteInstruction { const Else() : super(0x05); static Else deserialize(Deserializer d) => const Else(); + + @override + String get name => 'else'; } class BeginNoEffectTry extends Instruction { @@ -782,6 +896,16 @@ class BeginNoEffectTry extends Instruction { s.writeByte(0x06); s.writeByte(0x40); } + + @override + String get name => 'try'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeLabelDefinition(0); + } } class BeginOneOutputTry extends Instruction { @@ -803,6 +927,18 @@ class BeginOneOutputTry extends Instruction { s.writeByte(0x06); s.write(type); } + + @override + String get name => 'try'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeLabelDefinition(0); + p.write(' '); + p.writeValueType(type); + } } class BeginFunctionTry extends Instruction { @@ -822,6 +958,18 @@ class BeginFunctionTry extends Instruction { s.writeByte(0x06); s.write(type); } + + @override + String get name => 'try'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeLabelDefinition(0); + p.write(' '); + p.writeFunctionType(type); + } } class CatchLegacy extends Instruction { @@ -838,12 +986,25 @@ class CatchLegacy extends Instruction { s.writeByte(0x07); s.writeUnsigned(tag.index); } + + @override + String get name => 'catch'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeTagReference(tag); + } } class CatchAllLegacy extends SingleByteInstruction { const CatchAllLegacy() : super(0x19); static CatchAllLegacy deserialize(Deserializer d) => const CatchAllLegacy(); + + @override + String get name => 'catch_all'; } class Throw extends Instruction { @@ -860,6 +1021,16 @@ class Throw extends Instruction { s.writeByte(0x08); s.writeUnsigned(tag.index); } + + @override + String get name => 'throw'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeTagReference(tag); + } } class ThrowRef extends Instruction { @@ -871,6 +1042,9 @@ class ThrowRef extends Instruction { void serialize(Serializer s) { s.writeByte(0x0a); } + + @override + String get name => 'throw_ref'; } class Rethrow extends Instruction { @@ -885,6 +1059,16 @@ class Rethrow extends Instruction { s.writeByte(0x09); s.writeUnsigned(labelIndex); } + + @override + String get name => 'rethrow'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeLabelReference(labelIndex); + } } class End extends SingleByteInstruction { @@ -896,6 +1080,9 @@ class End extends SingleByteInstruction { static End deserialize(Deserializer d) { return const End(); } + + @override + String get name => 'end'; } class Br extends Instruction { @@ -910,6 +1097,16 @@ class Br extends Instruction { s.writeByte(0x0C); s.writeUnsigned(labelIndex); } + + @override + String get name => 'br'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeLabelReference(labelIndex); + } } class BrIf extends Instruction { @@ -924,6 +1121,16 @@ class BrIf extends Instruction { s.writeByte(0x0D); s.writeUnsigned(labelIndex); } + + @override + String get name => 'br_if'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeLabelReference(labelIndex); + } } class BrTable extends Instruction { @@ -945,12 +1152,29 @@ class BrTable extends Instruction { } s.writeUnsigned(defaultLabelIndex); } + + @override + String get name => 'br_table'; + + @override + void printTo(IrPrinter p) { + p.write(name); + for (final labelIndex in labelIndices) { + p.write(' '); + p.writeLabelReference(labelIndex); + } + p.write(' '); + p.writeLabelReference(defaultLabelIndex); + } } class Return extends SingleByteInstruction { const Return() : super(0x0F); static Return deserialize(Deserializer d) => const Return(); + + @override + String get name => 'return'; } class Call extends Instruction { @@ -967,6 +1191,16 @@ class Call extends Instruction { s.writeByte(0x10); s.writeUnsigned(function.index); } + + @override + String get name => 'call'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeFunctionReference(function); + } } class CallIndirect extends Instruction { @@ -990,6 +1224,17 @@ class CallIndirect extends Instruction { s.writeTypeIndex(type); s.writeUnsigned(table?.index ?? 0); } + + @override + String get name => 'call_indirect'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.writeTableReference(table); + p.write(' '); + p.writeFunctionType(type); + } } class CallRef extends Instruction { @@ -1009,12 +1254,25 @@ class CallRef extends Instruction { s.writeByte(0x14); s.writeTypeIndex(type); } + + @override + String get name => 'call_ref'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeDefTypeReference(type); + } } class Drop extends SingleByteInstruction { const Drop() : super(0x1A); static Drop deserialize(Deserializer d) => const Drop(); + + @override + String get name => 'drop'; } class Select extends Instruction { @@ -1031,6 +1289,9 @@ class Select extends Instruction { void serialize(Serializer s) { s.writeByte(0x1B); } + + @override + String get name => 'select'; } class SelectWithType extends Instruction { @@ -1052,6 +1313,16 @@ class SelectWithType extends Instruction { s.writeUnsigned(1); s.write(type); } + + @override + String get name => 'select'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeValueType(type); + } } class LocalGet extends Instruction { @@ -1069,6 +1340,16 @@ class LocalGet extends Instruction { s.writeByte(0x20); s.writeUnsigned(local.index); } + + @override + String get name => 'local.get'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeLocalReference(local); + } } class LocalSet extends Instruction { @@ -1085,6 +1366,16 @@ class LocalSet extends Instruction { s.writeByte(0x21); s.writeUnsigned(local.index); } + + @override + String get name => 'local.set'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeLocalReference(local); + } } class LocalTee extends Instruction { @@ -1101,6 +1392,16 @@ class LocalTee extends Instruction { s.writeByte(0x22); s.writeUnsigned(local.index); } + + @override + String get name => 'local.tee'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeLocalReference(local); + } } class GlobalGet extends Instruction { @@ -1120,6 +1421,16 @@ class GlobalGet extends Instruction { s.writeByte(0x23); s.writeUnsigned(global.index); } + + @override + String get name => 'global.get'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeGlobalReference(global); + } } class GlobalSet extends Instruction { @@ -1136,6 +1447,16 @@ class GlobalSet extends Instruction { s.writeByte(0x24); s.writeUnsigned(global.index); } + + @override + String get name => 'global.set'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeGlobalReference(global); + } } class TableSet extends Instruction { @@ -1152,6 +1473,15 @@ class TableSet extends Instruction { s.writeByte(0x26); s.writeUnsigned(table.index); } + + @override + String get name => 'table.set'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.writeTableReference(table); + } } class TableGet extends Instruction { @@ -1168,6 +1498,15 @@ class TableGet extends Instruction { s.writeByte(0x25); s.writeUnsigned(table.index); } + + @override + String get name => 'table.get'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.writeTableReference(table); + } } class TableSize extends Instruction { @@ -1185,6 +1524,15 @@ class TableSize extends Instruction { s.writeByte(0x10); s.writeUnsigned(table.index); } + + @override + String get name => 'table.size'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.writeTableReference(table); + } } class MemoryOffsetAlign implements Serializable { @@ -1214,6 +1562,18 @@ class MemoryOffsetAlign implements Serializable { return MemoryOffsetAlign(memories[memoryIndex], offset: offset, align: align); } + + void printTo(IrPrinter p) { + if (memory.index != 0) { + p.writeMemoryReference(memory); + } + if (offset != 0) { + p.write(' offset=$offset'); + } + if (align != 0) { + p.write(' align=${1 << align}'); + } + } } abstract class MemoryInstruction extends Instruction { @@ -1235,6 +1595,15 @@ class I32Load extends MemoryInstruction { static I32Load deserialize(Deserializer d, Memories memories) { return I32Load(MemoryOffsetAlign.deserialize(d, memories)); } + + @override + String get name => 'i32.load'; + + @override + void printTo(IrPrinter p) { + p.write(name); + memory.printTo(p); + } } class I64Load extends MemoryInstruction { @@ -1243,6 +1612,15 @@ class I64Load extends MemoryInstruction { static I64Load deserialize(Deserializer d, Memories memories) { return I64Load(MemoryOffsetAlign.deserialize(d, memories)); } + + @override + String get name => 'i64.load'; + + @override + void printTo(IrPrinter p) { + p.write(name); + memory.printTo(p); + } } class F32Load extends MemoryInstruction { @@ -1251,6 +1629,15 @@ class F32Load extends MemoryInstruction { static F32Load deserialize(Deserializer d, Memories memories) { return F32Load(MemoryOffsetAlign.deserialize(d, memories)); } + + @override + String get name => 'f32.load'; + + @override + void printTo(IrPrinter p) { + p.write(name); + memory.printTo(p); + } } class F64Load extends MemoryInstruction { @@ -1259,6 +1646,15 @@ class F64Load extends MemoryInstruction { static F64Load deserialize(Deserializer d, Memories memories) { return F64Load(MemoryOffsetAlign.deserialize(d, memories)); } + + @override + String get name => 'f64.load'; + + @override + void printTo(IrPrinter p) { + p.write(name); + memory.printTo(p); + } } class I32Load8S extends MemoryInstruction { @@ -1267,6 +1663,15 @@ class I32Load8S extends MemoryInstruction { static I32Load8S deserialize(Deserializer d, Memories memories) { return I32Load8S(MemoryOffsetAlign.deserialize(d, memories)); } + + @override + String get name => 'i32.load8_s'; + + @override + void printTo(IrPrinter p) { + p.write(name); + memory.printTo(p); + } } class I32Load8U extends MemoryInstruction { @@ -1275,6 +1680,15 @@ class I32Load8U extends MemoryInstruction { static I32Load8U deserialize(Deserializer d, Memories memories) { return I32Load8U(MemoryOffsetAlign.deserialize(d, memories)); } + + @override + String get name => 'i32.load8_u'; + + @override + void printTo(IrPrinter p) { + p.write(name); + memory.printTo(p); + } } class I32Load16S extends MemoryInstruction { @@ -1283,6 +1697,15 @@ class I32Load16S extends MemoryInstruction { static I32Load16S deserialize(Deserializer d, Memories memories) { return I32Load16S(MemoryOffsetAlign.deserialize(d, memories)); } + + @override + String get name => 'i32.load16_s'; + + @override + void printTo(IrPrinter p) { + p.write(name); + memory.printTo(p); + } } class I32Load16U extends MemoryInstruction { @@ -1291,6 +1714,15 @@ class I32Load16U extends MemoryInstruction { static I32Load16U deserialize(Deserializer d, Memories memories) { return I32Load16U(MemoryOffsetAlign.deserialize(d, memories)); } + + @override + String get name => 'i32.load16_u'; + + @override + void printTo(IrPrinter p) { + p.write(name); + memory.printTo(p); + } } class I64Load8S extends MemoryInstruction { @@ -1299,6 +1731,15 @@ class I64Load8S extends MemoryInstruction { static I64Load8S deserialize(Deserializer d, Memories memories) { return I64Load8S(MemoryOffsetAlign.deserialize(d, memories)); } + + @override + String get name => 'i64.load8_s'; + + @override + void printTo(IrPrinter p) { + p.write(name); + memory.printTo(p); + } } class I64Load8U extends MemoryInstruction { @@ -1307,6 +1748,15 @@ class I64Load8U extends MemoryInstruction { static I64Load8U deserialize(Deserializer d, Memories memories) { return I64Load8U(MemoryOffsetAlign.deserialize(d, memories)); } + + @override + String get name => 'i64.load8_u'; + + @override + void printTo(IrPrinter p) { + p.write(name); + memory.printTo(p); + } } class I64Load16S extends MemoryInstruction { @@ -1315,6 +1765,15 @@ class I64Load16S extends MemoryInstruction { static I64Load16S deserialize(Deserializer d, Memories memories) { return I64Load16S(MemoryOffsetAlign.deserialize(d, memories)); } + + @override + String get name => 'i64.load16_s'; + + @override + void printTo(IrPrinter p) { + p.write(name); + memory.printTo(p); + } } class I64Load16U extends MemoryInstruction { @@ -1323,6 +1782,15 @@ class I64Load16U extends MemoryInstruction { static I64Load16U deserialize(Deserializer d, Memories memories) { return I64Load16U(MemoryOffsetAlign.deserialize(d, memories)); } + + @override + String get name => 'i64.load16_u'; + + @override + void printTo(IrPrinter p) { + p.write(name); + memory.printTo(p); + } } class I64Load32S extends MemoryInstruction { @@ -1331,6 +1799,15 @@ class I64Load32S extends MemoryInstruction { static I64Load32S deserialize(Deserializer d, Memories memories) { return I64Load32S(MemoryOffsetAlign.deserialize(d, memories)); } + + @override + String get name => 'i64.load32_s'; + + @override + void printTo(IrPrinter p) { + p.write(name); + memory.printTo(p); + } } class I64Load32U extends MemoryInstruction { @@ -1339,6 +1816,15 @@ class I64Load32U extends MemoryInstruction { static I64Load32U deserialize(Deserializer d, Memories memories) { return I64Load32U(MemoryOffsetAlign.deserialize(d, memories)); } + + @override + String get name => 'i64.load32_u'; + + @override + void printTo(IrPrinter p) { + p.write(name); + memory.printTo(p); + } } class I32Store extends MemoryInstruction { @@ -1347,6 +1833,15 @@ class I32Store extends MemoryInstruction { static I32Store deserialize(Deserializer d, Memories memories) { return I32Store(MemoryOffsetAlign.deserialize(d, memories)); } + + @override + String get name => 'i32.store'; + + @override + void printTo(IrPrinter p) { + p.write(name); + memory.printTo(p); + } } class I64Store extends MemoryInstruction { @@ -1355,6 +1850,15 @@ class I64Store extends MemoryInstruction { static I64Store deserialize(Deserializer d, Memories memories) { return I64Store(MemoryOffsetAlign.deserialize(d, memories)); } + + @override + String get name => 'i64.store'; + + @override + void printTo(IrPrinter p) { + p.write(name); + memory.printTo(p); + } } class F32Store extends MemoryInstruction { @@ -1363,6 +1867,15 @@ class F32Store extends MemoryInstruction { static F32Store deserialize(Deserializer d, Memories memories) { return F32Store(MemoryOffsetAlign.deserialize(d, memories)); } + + @override + String get name => 'f32.store'; + + @override + void printTo(IrPrinter p) { + p.write(name); + memory.printTo(p); + } } class F64Store extends MemoryInstruction { @@ -1371,6 +1884,15 @@ class F64Store extends MemoryInstruction { static F64Store deserialize(Deserializer d, Memories memories) { return F64Store(MemoryOffsetAlign.deserialize(d, memories)); } + + @override + String get name => 'f64.store'; + + @override + void printTo(IrPrinter p) { + p.write(name); + memory.printTo(p); + } } class I32Store8 extends MemoryInstruction { @@ -1379,6 +1901,15 @@ class I32Store8 extends MemoryInstruction { static I32Store8 deserialize(Deserializer d, Memories memories) { return I32Store8(MemoryOffsetAlign.deserialize(d, memories)); } + + @override + String get name => 'i32.store8'; + + @override + void printTo(IrPrinter p) { + p.write(name); + memory.printTo(p); + } } class I32Store16 extends MemoryInstruction { @@ -1387,6 +1918,15 @@ class I32Store16 extends MemoryInstruction { static I32Store16 deserialize(Deserializer d, Memories memories) { return I32Store16(MemoryOffsetAlign.deserialize(d, memories)); } + + @override + String get name => 'i32.store16'; + + @override + void printTo(IrPrinter p) { + p.write(name); + memory.printTo(p); + } } class I64Store8 extends MemoryInstruction { @@ -1395,6 +1935,15 @@ class I64Store8 extends MemoryInstruction { static I64Store8 deserialize(Deserializer d, Memories memories) { return I64Store8(MemoryOffsetAlign.deserialize(d, memories)); } + + @override + String get name => 'i64.store8'; + + @override + void printTo(IrPrinter p) { + p.write(name); + memory.printTo(p); + } } class I64Store16 extends MemoryInstruction { @@ -1403,6 +1952,15 @@ class I64Store16 extends MemoryInstruction { static I64Store16 deserialize(Deserializer d, Memories memories) { return I64Store16(MemoryOffsetAlign.deserialize(d, memories)); } + + @override + String get name => 'i64.store16'; + + @override + void printTo(IrPrinter p) { + p.write(name); + memory.printTo(p); + } } class I64Store32 extends MemoryInstruction { @@ -1411,6 +1969,15 @@ class I64Store32 extends MemoryInstruction { static I64Store32 deserialize(Deserializer d, Memories memories) { return I64Store32(MemoryOffsetAlign.deserialize(d, memories)); } + + @override + String get name => 'i64.store32'; + + @override + void printTo(IrPrinter p) { + p.write(name); + memory.printTo(p); + } } class MemorySize extends Instruction { @@ -1427,6 +1994,15 @@ class MemorySize extends Instruction { s.writeByte(0x3F); s.writeUnsigned(memory.index); } + + @override + String get name => 'memory.size'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.writeMemoryReference(memory); + } } class MemoryGrow extends Instruction { @@ -1443,6 +2019,15 @@ class MemoryGrow extends Instruction { s.writeByte(0x40); s.writeUnsigned(memory.index); } + + @override + String get name => 'memory.grow'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.writeMemoryReference(memory); + } } class RefNull extends Instruction { @@ -1468,12 +2053,25 @@ class RefNull extends Instruction { s.writeByte(0xD0); s.write(heapType); } + + @override + String get name => 'ref.null'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeHeapTypeReference(heapType); + } } class RefIsNull extends SingleByteInstruction { const RefIsNull() : super(0xD1); static RefIsNull deserialize(Deserializer d) => const RefIsNull(); + + @override + String get name => 'ref.is_null'; } class RefFunc extends Instruction { @@ -1495,12 +2093,25 @@ class RefFunc extends Instruction { s.writeByte(0xD2); s.writeUnsigned(function.index); } + + @override + String get name => 'ref.func'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeFunctionReference(function); + } } class RefAsNonNull extends SingleByteInstruction { const RefAsNonNull() : super(0xD4); static RefAsNonNull deserialize(Deserializer d) => const RefAsNonNull(); + + @override + String get name => 'ref.as_non_null'; } class BrOnNull extends Instruction { @@ -1515,12 +2126,25 @@ class BrOnNull extends Instruction { s.writeByte(0xD5); s.writeUnsigned(labelIndex); } + + @override + String get name => 'br_on_null'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeLabelReference(labelIndex); + } } class RefEq extends SingleByteInstruction { const RefEq() : super(0xD3); static RefEq deserialize(Deserializer d) => const RefEq(); + + @override + String get name => 'ref.eq'; } class BrOnNonNull extends Instruction { @@ -1536,6 +2160,16 @@ class BrOnNonNull extends Instruction { s.writeByte(0xD6); s.writeUnsigned(labelIndex); } + + @override + String get name => 'br_on_non_null'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeLabelReference(labelIndex); + } } class StructGet extends Instruction { @@ -1559,6 +2193,16 @@ class StructGet extends Instruction { s.writeTypeIndex(structType); s.writeUnsigned(fieldIndex); } + + @override + String get name => 'struct.get'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeFieldReference(structType, fieldIndex); + } } class StructGetS extends Instruction { @@ -1582,6 +2226,16 @@ class StructGetS extends Instruction { s.writeTypeIndex(structType); s.writeUnsigned(fieldIndex); } + + @override + String get name => 'struct.get_s'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeFieldReference(structType, fieldIndex); + } } class StructGetU extends Instruction { @@ -1605,6 +2259,16 @@ class StructGetU extends Instruction { s.writeTypeIndex(structType); s.writeUnsigned(fieldIndex); } + + @override + String get name => 'struct.get_u'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeFieldReference(structType, fieldIndex); + } } class StructSet extends Instruction { @@ -1628,6 +2292,16 @@ class StructSet extends Instruction { s.writeTypeIndex(structType); s.writeUnsigned(fieldIndex); } + + @override + String get name => 'struct.set'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeFieldReference(structType, fieldIndex); + } } class StructNew extends Instruction { @@ -1651,6 +2325,16 @@ class StructNew extends Instruction { s.writeByte(0x00); s.writeTypeIndex(structType); } + + @override + String get name => 'struct.new'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeDefTypeReference(structType); + } } class StructNewDefault extends Instruction { @@ -1674,6 +2358,16 @@ class StructNewDefault extends Instruction { s.writeByte(0x01); s.writeTypeIndex(structType); } + + @override + String get name => 'struct.new_default'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeDefTypeReference(structType); + } } class ArrayGet extends Instruction { @@ -1694,6 +2388,16 @@ class ArrayGet extends Instruction { s.writeByte(0x0b); s.writeTypeIndex(arrayType); } + + @override + String get name => 'array.get'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeDefTypeReference(arrayType); + } } class ArrayGetS extends Instruction { @@ -1714,6 +2418,16 @@ class ArrayGetS extends Instruction { s.writeByte(0x0c); s.writeTypeIndex(arrayType); } + + @override + String get name => 'array.get_s'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeDefTypeReference(arrayType); + } } class ArrayGetU extends Instruction { @@ -1734,6 +2448,16 @@ class ArrayGetU extends Instruction { s.writeByte(0x0d); s.writeTypeIndex(arrayType); } + + @override + String get name => 'array.get_u'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeDefTypeReference(arrayType); + } } class ArraySet extends Instruction { @@ -1754,6 +2478,16 @@ class ArraySet extends Instruction { s.writeByte(0x0E); s.writeTypeIndex(arrayType); } + + @override + String get name => 'array.set'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeDefTypeReference(arrayType); + } } class ArrayLen extends Instruction { @@ -1766,6 +2500,9 @@ class ArrayLen extends Instruction { s.writeByte(0xFB); s.writeByte(0x0F); } + + @override + String get name => 'array.len'; } class ArrayNewFixed extends Instruction { @@ -1792,6 +2529,17 @@ class ArrayNewFixed extends Instruction { s.writeTypeIndex(arrayType); s.writeUnsigned(length); } + + @override + String get name => 'array.new_fixed'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeDefTypeReference(arrayType); + p.write(' $length'); + } } class ArrayNew extends Instruction { @@ -1815,6 +2563,16 @@ class ArrayNew extends Instruction { s.writeByte(0x06); s.writeTypeIndex(arrayType); } + + @override + String get name => 'array.new'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeDefTypeReference(arrayType); + } } class ArrayNewDefault extends Instruction { @@ -1838,6 +2596,16 @@ class ArrayNewDefault extends Instruction { s.writeByte(0x07); s.writeTypeIndex(arrayType); } + + @override + String get name => 'array.new_default'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeDefTypeReference(arrayType); + } } class ArrayNewData extends Instruction { @@ -1862,6 +2630,17 @@ class ArrayNewData extends Instruction { s.writeTypeIndex(arrayType); s.writeUnsigned(data.index); } + + @override + String get name => 'array.new_data'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeDefTypeReference(arrayType); + p.writeDataReference(data); + } } class ArrayCopy extends Instruction { @@ -1886,6 +2665,18 @@ class ArrayCopy extends Instruction { s.writeTypeIndex(destArrayType); s.writeTypeIndex(sourceArrayType); } + + @override + String get name => 'array.copy'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeDefTypeReference(destArrayType); + p.write(' '); + p.writeDefTypeReference(sourceArrayType); + } } class ArrayFill extends Instruction { @@ -1906,6 +2697,16 @@ class ArrayFill extends Instruction { s.writeByte(0x10); s.writeTypeIndex(arrayType); } + + @override + String get name => 'array.fill'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeDefTypeReference(arrayType); + } } class I31New extends Instruction { @@ -1918,6 +2719,9 @@ class I31New extends Instruction { s.writeByte(0xFB); s.writeByte(0x1C); } + + @override + String get name => 'i31.new'; } class I31GetS extends Instruction { @@ -1930,6 +2734,9 @@ class I31GetS extends Instruction { s.writeByte(0xFB); s.writeByte(0x1D); } + + @override + String get name => 'i31.get_s'; } class I31GetU extends Instruction { @@ -1942,6 +2749,9 @@ class I31GetU extends Instruction { s.writeByte(0xFB); s.writeByte(0x1E); } + + @override + String get name => 'i31.get_u'; } class RefTest extends Instruction { @@ -1960,6 +2770,16 @@ class RefTest extends Instruction { s.writeByte(targetType.nullable ? 0x15 : 0x14); s.write(targetType.heapType); } + + @override + String get name => 'ref.test'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeRefTypeReference(targetType); + } } class RefCast extends Instruction { @@ -1981,6 +2801,16 @@ class RefCast extends Instruction { s.writeByte(targetType.nullable ? 0x17 : 0x16); s.write(targetType.heapType); } + + @override + String get name => 'ref.cast'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeRefTypeReference(targetType); + } } class BrOnCast extends Instruction { @@ -2014,6 +2844,20 @@ class BrOnCast extends Instruction { s.write(inputType.heapType); s.write(targetType.heapType); } + + @override + String get name => 'br_on_cast'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeLabelReference(labelIndex); + p.write(' '); + p.writeValueType(inputType); + p.write(' '); + p.writeValueType(targetType); + } } class BrOnCastFail extends Instruction { @@ -2049,6 +2893,20 @@ class BrOnCastFail extends Instruction { s.write(inputType.heapType); s.write(targetType.heapType); } + + @override + String get name => 'br_on_cast_fail'; + + @override + void printTo(IrPrinter p) { + p.write(name); + p.write(' '); + p.writeLabelReference(labelIndex); + p.write(' '); + p.writeValueType(inputType); + p.write(' '); + p.writeValueType(targetType); + } } class ExternInternalize extends Instruction { @@ -2066,6 +2924,9 @@ class ExternInternalize extends Instruction { @override bool get isConstant => true; + + @override + String get name => 'any.convert_extern'; } class ExternExternalize extends Instruction { @@ -2083,6 +2944,9 @@ class ExternExternalize extends Instruction { @override bool get isConstant => true; + + @override + String get name => 'extern.externalize'; } class I32Const extends Instruction { @@ -2102,6 +2966,9 @@ class I32Const extends Instruction { s.writeByte(0x41); s.writeSigned(value); } + + @override + String get name => 'i32.const $value'; } class I64Const extends Instruction { @@ -2121,6 +2988,9 @@ class I64Const extends Instruction { s.writeByte(0x42); s.writeSigned(value); } + + @override + String get name => 'i64.const $value'; } class F32Const extends Instruction { @@ -2140,6 +3010,9 @@ class F32Const extends Instruction { s.writeByte(0x43); s.writeF32(value); } + + @override + String get name => 'f32.const $value'; } class F64Const extends Instruction { @@ -2159,720 +3032,1080 @@ class F64Const extends Instruction { s.writeByte(0x44); s.writeF64(value); } + + @override + String get name => 'f64.const $value'; } class I32Eqz extends SingleByteInstruction { const I32Eqz() : super(0x45); static I32Eqz deserialize(Deserializer d) => const I32Eqz(); + + @override + String get name => 'i32.eqz'; } class I32Eq extends SingleByteInstruction { const I32Eq() : super(0x46); static I32Eq deserialize(Deserializer d) => const I32Eq(); + + @override + String get name => 'i32.eq'; } class I32Ne extends SingleByteInstruction { const I32Ne() : super(0x47); static I32Ne deserialize(Deserializer d) => const I32Ne(); + + @override + String get name => 'i32.ne'; } class I32LtS extends SingleByteInstruction { const I32LtS() : super(0x48); static I32LtS deserialize(Deserializer d) => const I32LtS(); + + @override + String get name => 'i32.lt_s'; } class I32LtU extends SingleByteInstruction { const I32LtU() : super(0x49); static I32LtU deserialize(Deserializer d) => const I32LtU(); + + @override + String get name => 'i32.lt_u'; } class I32GtS extends SingleByteInstruction { const I32GtS() : super(0x4A); static I32GtS deserialize(Deserializer d) => const I32GtS(); + + @override + String get name => 'i32.gt_s'; } class I32GtU extends SingleByteInstruction { const I32GtU() : super(0x4B); static I32GtU deserialize(Deserializer d) => const I32GtU(); + + @override + String get name => 'i32.gt_u'; } class I32LeS extends SingleByteInstruction { const I32LeS() : super(0x4C); static I32LeS deserialize(Deserializer d) => const I32LeS(); + + @override + String get name => 'i32.le_s'; } class I32LeU extends SingleByteInstruction { const I32LeU() : super(0x4D); static I32LeU deserialize(Deserializer d) => const I32LeU(); + + @override + String get name => 'i32.le_u'; } class I32GeS extends SingleByteInstruction { const I32GeS() : super(0x4E); static I32GeS deserialize(Deserializer d) => const I32GeS(); + + @override + String get name => 'i32.ge_s'; } class I32GeU extends SingleByteInstruction { const I32GeU() : super(0x4F); static I32GeU deserialize(Deserializer d) => const I32GeU(); + + @override + String get name => 'i32.ge_u'; } class I64Eqz extends SingleByteInstruction { const I64Eqz() : super(0x50); static I64Eqz deserialize(Deserializer d) => const I64Eqz(); + + @override + String get name => 'i64.eqz'; } class I64Eq extends SingleByteInstruction { const I64Eq() : super(0x51); static I64Eq deserialize(Deserializer d) => const I64Eq(); + + @override + String get name => 'i64.eq'; } class I64Ne extends SingleByteInstruction { const I64Ne() : super(0x52); static I64Ne deserialize(Deserializer d) => const I64Ne(); + + @override + String get name => 'i64.ne'; } class I64LtS extends SingleByteInstruction { const I64LtS() : super(0x53); static I64LtS deserialize(Deserializer d) => const I64LtS(); + + @override + String get name => 'i64.lt_s'; } class I64LtU extends SingleByteInstruction { const I64LtU() : super(0x54); static I64LtU deserialize(Deserializer d) => const I64LtU(); + + @override + String get name => 'i64.lt_u'; } class I64GtS extends SingleByteInstruction { const I64GtS() : super(0x55); static I64GtS deserialize(Deserializer d) => const I64GtS(); + + @override + String get name => 'i64.gt_s'; } class I64GtU extends SingleByteInstruction { const I64GtU() : super(0x56); static I64GtU deserialize(Deserializer d) => const I64GtU(); + + @override + String get name => 'i64.gt_u'; } class I64LeS extends SingleByteInstruction { const I64LeS() : super(0x57); static I64LeS deserialize(Deserializer d) => const I64LeS(); + + @override + String get name => 'i64.le_s'; } class I64LeU extends SingleByteInstruction { const I64LeU() : super(0x58); static I64LeU deserialize(Deserializer d) => const I64LeU(); + + @override + String get name => 'i64.le_u'; } class I64GeS extends SingleByteInstruction { const I64GeS() : super(0x59); static I64GeS deserialize(Deserializer d) => const I64GeS(); + + @override + String get name => 'i64.ge_s'; } class I64GeU extends SingleByteInstruction { const I64GeU() : super(0x5A); static I64GeU deserialize(Deserializer d) => const I64GeU(); + + @override + String get name => 'i64.ge_u'; } class F32Eq extends SingleByteInstruction { const F32Eq() : super(0x5B); static F32Eq deserialize(Deserializer d) => const F32Eq(); + + @override + String get name => 'f32.eq'; } class F32Ne extends SingleByteInstruction { const F32Ne() : super(0x5C); static F32Ne deserialize(Deserializer d) => const F32Ne(); + + @override + String get name => 'f32.ne'; } class F32Lt extends SingleByteInstruction { const F32Lt() : super(0x5D); static F32Lt deserialize(Deserializer d) => const F32Lt(); + + @override + String get name => 'f32.lt'; } class F32Gt extends SingleByteInstruction { const F32Gt() : super(0x5E); static F32Gt deserialize(Deserializer d) => const F32Gt(); + + @override + String get name => 'f32.gt'; } class F32Le extends SingleByteInstruction { const F32Le() : super(0x5F); static F32Le deserialize(Deserializer d) => const F32Le(); + + @override + String get name => 'f32.le'; } class F32Ge extends SingleByteInstruction { const F32Ge() : super(0x60); static F32Ge deserialize(Deserializer d) => const F32Ge(); + + @override + String get name => 'f32.ge'; } class F64Eq extends SingleByteInstruction { const F64Eq() : super(0x61); static F64Eq deserialize(Deserializer d) => const F64Eq(); + + @override + String get name => 'f64.eq'; } class F64Ne extends SingleByteInstruction { const F64Ne() : super(0x62); static F64Ne deserialize(Deserializer d) => const F64Ne(); + + @override + String get name => 'f64.ne'; } class F64Lt extends SingleByteInstruction { const F64Lt() : super(0x63); static F64Lt deserialize(Deserializer d) => const F64Lt(); + + @override + String get name => 'f64.lt'; } class F64Gt extends SingleByteInstruction { const F64Gt() : super(0x64); static F64Gt deserialize(Deserializer d) => const F64Gt(); + + @override + String get name => 'f64.gt'; } class F64Le extends SingleByteInstruction { const F64Le() : super(0x65); static F64Le deserialize(Deserializer d) => const F64Le(); + + @override + String get name => 'f64.le'; } class F64Ge extends SingleByteInstruction { const F64Ge() : super(0x66); static F64Ge deserialize(Deserializer d) => const F64Ge(); + + @override + String get name => 'f64.ge'; } class I32Clz extends SingleByteInstruction { const I32Clz() : super(0x67); static I32Clz deserialize(Deserializer d) => const I32Clz(); + + @override + String get name => 'i32.clz'; } class I32Ctz extends SingleByteInstruction { const I32Ctz() : super(0x68); static I32Ctz deserialize(Deserializer d) => const I32Ctz(); + + @override + String get name => 'i32.ctz'; } class I32Popcnt extends SingleByteInstruction { const I32Popcnt() : super(0x69); static I32Popcnt deserialize(Deserializer d) => const I32Popcnt(); + + @override + String get name => 'i32.popcnt'; } class I32Add extends SingleByteInstruction { const I32Add() : super(0x6A); static I32Add deserialize(Deserializer d) => const I32Add(); + + @override + String get name => 'i32.add'; } class I32Sub extends SingleByteInstruction { const I32Sub() : super(0x6B); static I32Sub deserialize(Deserializer d) => const I32Sub(); + + @override + String get name => 'i32.sub'; } class I32Mul extends SingleByteInstruction { const I32Mul() : super(0x6C); static I32Mul deserialize(Deserializer d) => const I32Mul(); + + @override + String get name => 'i32.mul'; } class I32DivS extends SingleByteInstruction { const I32DivS() : super(0x6D); static I32DivS deserialize(Deserializer d) => const I32DivS(); + + @override + String get name => 'i32.div_s'; } class I32DivU extends SingleByteInstruction { const I32DivU() : super(0x6E); static I32DivU deserialize(Deserializer d) => const I32DivU(); + + @override + String get name => 'i32.div_u'; } class I32RemS extends SingleByteInstruction { const I32RemS() : super(0x6F); static I32RemS deserialize(Deserializer d) => const I32RemS(); + + @override + String get name => 'i32.rem_s'; } class I32RemU extends SingleByteInstruction { const I32RemU() : super(0x70); static I32RemU deserialize(Deserializer d) => const I32RemU(); + + @override + String get name => 'i32.rem_u'; } class I32And extends SingleByteInstruction { const I32And() : super(0x71); static I32And deserialize(Deserializer d) => const I32And(); + + @override + String get name => 'i32.and'; } class I32Or extends SingleByteInstruction { const I32Or() : super(0x72); static I32Or deserialize(Deserializer d) => const I32Or(); + + @override + String get name => 'i32.or'; } class I32Xor extends SingleByteInstruction { const I32Xor() : super(0x73); static I32Xor deserialize(Deserializer d) => const I32Xor(); + + @override + String get name => 'i32.xor'; } class I32Shl extends SingleByteInstruction { const I32Shl() : super(0x74); static I32Shl deserialize(Deserializer d) => const I32Shl(); + + @override + String get name => 'i32.shl'; } class I32ShrS extends SingleByteInstruction { const I32ShrS() : super(0x75); static I32ShrS deserialize(Deserializer d) => const I32ShrS(); + + @override + String get name => 'i32.shr_s'; } class I32ShrU extends SingleByteInstruction { const I32ShrU() : super(0x76); static I32ShrU deserialize(Deserializer d) => const I32ShrU(); + + @override + String get name => 'i32.shr_u'; } class I32Rotl extends SingleByteInstruction { const I32Rotl() : super(0x77); static I32Rotl deserialize(Deserializer d) => const I32Rotl(); + + @override + String get name => 'i32.rotl'; } class I32Rotr extends SingleByteInstruction { const I32Rotr() : super(0x78); static I32Rotr deserialize(Deserializer d) => const I32Rotr(); + + @override + String get name => 'i32.rotr'; } class I64Clz extends SingleByteInstruction { const I64Clz() : super(0x79); static I64Clz deserialize(Deserializer d) => const I64Clz(); + + @override + String get name => 'i64.clz'; } class I64Ctz extends SingleByteInstruction { const I64Ctz() : super(0x7A); static I64Ctz deserialize(Deserializer d) => const I64Ctz(); + + @override + String get name => 'i64.ctz'; } class I64Popcnt extends SingleByteInstruction { const I64Popcnt() : super(0x7B); static I64Popcnt deserialize(Deserializer d) => const I64Popcnt(); + + @override + String get name => 'i64.popcnt'; } class I64Add extends SingleByteInstruction { const I64Add() : super(0x7C); static I64Add deserialize(Deserializer d) => const I64Add(); + + @override + String get name => 'i64.add'; } class I64Sub extends SingleByteInstruction { const I64Sub() : super(0x7D); static I64Sub deserialize(Deserializer d) => const I64Sub(); + + @override + String get name => 'i64.sub'; } class I64Mul extends SingleByteInstruction { const I64Mul() : super(0x7E); static I64Mul deserialize(Deserializer d) => const I64Mul(); + + @override + String get name => 'i64.mul'; } class I64DivS extends SingleByteInstruction { const I64DivS() : super(0x7F); static I64DivS deserialize(Deserializer d) => const I64DivS(); + + @override + String get name => 'i64.div_s'; } class I64DivU extends SingleByteInstruction { const I64DivU() : super(0x80); static I64DivU deserialize(Deserializer d) => const I64DivU(); + + @override + String get name => 'i64.div_u'; } class I64RemS extends SingleByteInstruction { const I64RemS() : super(0x81); static I64RemS deserialize(Deserializer d) => const I64RemS(); + + @override + String get name => 'i64.rem_s'; } class I64RemU extends SingleByteInstruction { const I64RemU() : super(0x82); static I64RemU deserialize(Deserializer d) => const I64RemU(); + + @override + String get name => 'i64.rem_u'; } class I64And extends SingleByteInstruction { const I64And() : super(0x83); static I64And deserialize(Deserializer d) => const I64And(); + + @override + String get name => 'i64.and'; } class I64Or extends SingleByteInstruction { const I64Or() : super(0x84); static I64Or deserialize(Deserializer d) => const I64Or(); + + @override + String get name => 'i64.or'; } class I64Xor extends SingleByteInstruction { const I64Xor() : super(0x85); static I64Xor deserialize(Deserializer d) => const I64Xor(); + + @override + String get name => 'i64.xor'; } class I64Shl extends SingleByteInstruction { const I64Shl() : super(0x86); static I64Shl deserialize(Deserializer d) => const I64Shl(); + + @override + String get name => 'i64.shl'; } class I64ShrS extends SingleByteInstruction { const I64ShrS() : super(0x87); static I64ShrS deserialize(Deserializer d) => const I64ShrS(); + + @override + String get name => 'i64.shr_s'; } class I64ShrU extends SingleByteInstruction { const I64ShrU() : super(0x88); static I64ShrU deserialize(Deserializer d) => const I64ShrU(); + + @override + String get name => 'i64.shr_u'; } class I64Rotl extends SingleByteInstruction { const I64Rotl() : super(0x89); static I64Rotl deserialize(Deserializer d) => const I64Rotl(); + + @override + String get name => 'i64.rotl'; } class I64Rotr extends SingleByteInstruction { const I64Rotr() : super(0x8A); static I64Rotr deserialize(Deserializer d) => const I64Rotr(); + + @override + String get name => 'i64.rotr'; } class F32Abs extends SingleByteInstruction { const F32Abs() : super(0x8B); static F32Abs deserialize(Deserializer d) => const F32Abs(); + + @override + String get name => 'f32.abs'; } class F32Neg extends SingleByteInstruction { const F32Neg() : super(0x8C); static F32Neg deserialize(Deserializer d) => const F32Neg(); + + @override + String get name => 'f32.neg'; } class F32Ceil extends SingleByteInstruction { const F32Ceil() : super(0x8D); static F32Ceil deserialize(Deserializer d) => const F32Ceil(); + + @override + String get name => 'f32.ceil'; } class F32Floor extends SingleByteInstruction { const F32Floor() : super(0x8E); static F32Floor deserialize(Deserializer d) => const F32Floor(); + + @override + String get name => 'f32.floor'; } class F32Trunc extends SingleByteInstruction { const F32Trunc() : super(0x8F); static F32Trunc deserialize(Deserializer d) => const F32Trunc(); + + @override + String get name => 'f32.trunc'; } class F32Nearest extends SingleByteInstruction { const F32Nearest() : super(0x90); static F32Nearest deserialize(Deserializer d) => const F32Nearest(); + + @override + String get name => 'f32.nearest'; } class F32Sqrt extends SingleByteInstruction { const F32Sqrt() : super(0x91); static F32Sqrt deserialize(Deserializer d) => const F32Sqrt(); + + @override + String get name => 'f32.sqrt'; } class F32Add extends SingleByteInstruction { const F32Add() : super(0x92); static F32Add deserialize(Deserializer d) => const F32Add(); + + @override + String get name => 'f32.add'; } class F32Sub extends SingleByteInstruction { const F32Sub() : super(0x93); static F32Sub deserialize(Deserializer d) => const F32Sub(); + + @override + String get name => 'f32.sub'; } class F32Mul extends SingleByteInstruction { const F32Mul() : super(0x94); static F32Mul deserialize(Deserializer d) => const F32Mul(); + + @override + String get name => 'f32.mul'; } class F32Div extends SingleByteInstruction { const F32Div() : super(0x95); static F32Div deserialize(Deserializer d) => const F32Div(); + + @override + String get name => 'f32.div'; } class F32Min extends SingleByteInstruction { const F32Min() : super(0x96); static F32Min deserialize(Deserializer d) => const F32Min(); + + @override + String get name => 'f32.min'; } class F32Max extends SingleByteInstruction { const F32Max() : super(0x97); static F32Max deserialize(Deserializer d) => const F32Max(); + + @override + String get name => 'f32.max'; } class F32Copysign extends SingleByteInstruction { const F32Copysign() : super(0x98); static F32Copysign deserialize(Deserializer d) => const F32Copysign(); + + @override + String get name => 'f32.copysign'; } class F64Abs extends SingleByteInstruction { const F64Abs() : super(0x99); static F64Abs deserialize(Deserializer d) => const F64Abs(); + + @override + String get name => 'f64.abs'; } class F64Neg extends SingleByteInstruction { const F64Neg() : super(0x9A); static F64Neg deserialize(Deserializer d) => const F64Neg(); + + @override + String get name => 'f64.neg'; } class F64Ceil extends SingleByteInstruction { const F64Ceil() : super(0x9B); static F64Ceil deserialize(Deserializer d) => const F64Ceil(); + + @override + String get name => 'f64.ceil'; } class F64Floor extends SingleByteInstruction { const F64Floor() : super(0x9C); static F64Floor deserialize(Deserializer d) => const F64Floor(); + + @override + String get name => 'f64.floor'; } class F64Trunc extends SingleByteInstruction { const F64Trunc() : super(0x9D); static F64Trunc deserialize(Deserializer d) => const F64Trunc(); + + @override + String get name => 'f64.trunc'; } class F64Nearest extends SingleByteInstruction { const F64Nearest() : super(0x9E); static F64Nearest deserialize(Deserializer d) => const F64Nearest(); + + @override + String get name => 'f64.nearest'; } class F64Sqrt extends SingleByteInstruction { const F64Sqrt() : super(0x9F); static F64Sqrt deserialize(Deserializer d) => const F64Sqrt(); + + @override + String get name => 'f64.sqrt'; } class F64Add extends SingleByteInstruction { const F64Add() : super(0xA0); static F64Add deserialize(Deserializer d) => const F64Add(); + + @override + String get name => 'f64.add'; } class F64Sub extends SingleByteInstruction { const F64Sub() : super(0xA1); static F64Sub deserialize(Deserializer d) => const F64Sub(); + + @override + String get name => 'f64.sub'; } class F64Mul extends SingleByteInstruction { const F64Mul() : super(0xA2); static F64Mul deserialize(Deserializer d) => const F64Mul(); + + @override + String get name => 'f64.mul'; } class F64Div extends SingleByteInstruction { const F64Div() : super(0xA3); static F64Div deserialize(Deserializer d) => const F64Div(); + + @override + String get name => 'f64.div'; } class F64Min extends SingleByteInstruction { const F64Min() : super(0xA4); static F64Min deserialize(Deserializer d) => const F64Min(); + + @override + String get name => 'f64.min'; } class F64Max extends SingleByteInstruction { const F64Max() : super(0xA5); static F64Max deserialize(Deserializer d) => const F64Max(); + + @override + String get name => 'f64.max'; } class F64Copysign extends SingleByteInstruction { const F64Copysign() : super(0xA6); static F64Copysign deserialize(Deserializer d) => const F64Copysign(); + + @override + String get name => 'f64.copysign'; } class I32WrapI64 extends SingleByteInstruction { const I32WrapI64() : super(0xA7); static I32WrapI64 deserialize(Deserializer d) => const I32WrapI64(); + + @override + String get name => 'i32.wrap_i64'; } class I32TruncF32S extends SingleByteInstruction { const I32TruncF32S() : super(0xA8); static I32TruncF32S deserialize(Deserializer d) => const I32TruncF32S(); + + @override + String get name => 'i32.trunc_f32_s'; } class I32TruncF32U extends SingleByteInstruction { const I32TruncF32U() : super(0xA9); static I32TruncF32U deserialize(Deserializer d) => const I32TruncF32U(); + + @override + String get name => 'i32.trunc_f32_u'; } class I32TruncF64S extends SingleByteInstruction { const I32TruncF64S() : super(0xAA); static I32TruncF64S deserialize(Deserializer d) => const I32TruncF64S(); + + @override + String get name => 'i32.trunc_f64_s'; } class I32TruncF64U extends SingleByteInstruction { const I32TruncF64U() : super(0xAB); static I32TruncF64U deserialize(Deserializer d) => const I32TruncF64U(); + + @override + String get name => 'i32.trunc_f64_u'; } class I64ExtendI32S extends SingleByteInstruction { const I64ExtendI32S() : super(0xAC); static I64ExtendI32S deserialize(Deserializer d) => const I64ExtendI32S(); + + @override + String get name => 'i64.extend_i32_s'; } class I64ExtendI32U extends SingleByteInstruction { const I64ExtendI32U() : super(0xAD); static I64ExtendI32U deserialize(Deserializer d) => const I64ExtendI32U(); + + @override + String get name => 'i64.extend_i32_u'; } class I64TruncF32S extends SingleByteInstruction { const I64TruncF32S() : super(0xAE); static I64TruncF32S deserialize(Deserializer d) => const I64TruncF32S(); + + @override + String get name => 'i64.trunc_f32_s'; } class I64TruncF32U extends SingleByteInstruction { const I64TruncF32U() : super(0xAF); static I64TruncF32U deserialize(Deserializer d) => const I64TruncF32U(); + + @override + String get name => 'i64.trunc_f32_u'; } class I64TruncF64S extends SingleByteInstruction { const I64TruncF64S() : super(0xB0); static I64TruncF64S deserialize(Deserializer d) => const I64TruncF64S(); + + @override + String get name => 'i64.trunc_f64_s'; } class I64TruncF64U extends SingleByteInstruction { const I64TruncF64U() : super(0xB1); static I64TruncF64U deserialize(Deserializer d) => const I64TruncF64U(); + + @override + String get name => 'i64.trunc_f64_u'; } class F32ConvertI32S extends SingleByteInstruction { const F32ConvertI32S() : super(0xB2); static F32ConvertI32S deserialize(Deserializer d) => const F32ConvertI32S(); + + @override + String get name => 'f32.convert_i32_s'; } class F32ConvertI32U extends SingleByteInstruction { const F32ConvertI32U() : super(0xB3); static F32ConvertI32U deserialize(Deserializer d) => const F32ConvertI32U(); + + @override + String get name => 'f32.convert_i32_u'; } class F32ConvertI64S extends SingleByteInstruction { const F32ConvertI64S() : super(0xB4); static F32ConvertI64S deserialize(Deserializer d) => const F32ConvertI64S(); + + @override + String get name => 'f32.convert_i64_s'; } class F32ConvertI64U extends SingleByteInstruction { const F32ConvertI64U() : super(0xB5); static F32ConvertI64U deserialize(Deserializer d) => const F32ConvertI64U(); + + @override + String get name => 'f32.convert_i64_u'; } class F32DemoteF64 extends SingleByteInstruction { const F32DemoteF64() : super(0xB6); static F32DemoteF64 deserialize(Deserializer d) => const F32DemoteF64(); + + @override + String get name => 'f32.demote_f64'; } class F64ConvertI32S extends SingleByteInstruction { const F64ConvertI32S() : super(0xB7); static F64ConvertI32S deserialize(Deserializer d) => const F64ConvertI32S(); + + @override + String get name => 'f64.convert_i32_s'; } class F64ConvertI32U extends SingleByteInstruction { const F64ConvertI32U() : super(0xB8); static F64ConvertI32U deserialize(Deserializer d) => const F64ConvertI32U(); + + @override + String get name => 'f64.convert_i32_u'; } class F64ConvertI64S extends SingleByteInstruction { const F64ConvertI64S() : super(0xB9); static F64ConvertI64S deserialize(Deserializer d) => const F64ConvertI64S(); + + @override + String get name => 'f64.convert_i64_s'; } class F64ConvertI64U extends SingleByteInstruction { const F64ConvertI64U() : super(0xBA); static F64ConvertI64U deserialize(Deserializer d) => const F64ConvertI64U(); + + @override + String get name => 'f64.convert_i64_u'; } class F64PromoteF32 extends SingleByteInstruction { const F64PromoteF32() : super(0xBB); static F64PromoteF32 deserialize(Deserializer d) => const F64PromoteF32(); + + @override + String get name => 'f64.promote_f32'; } class I32ReinterpretF32 extends SingleByteInstruction { @@ -2880,6 +4113,9 @@ class I32ReinterpretF32 extends SingleByteInstruction { static I32ReinterpretF32 deserialize(Deserializer d) => const I32ReinterpretF32(); + + @override + String get name => 'i32.reinterpret_f32'; } class I64ReinterpretF64 extends SingleByteInstruction { @@ -2887,6 +4123,9 @@ class I64ReinterpretF64 extends SingleByteInstruction { static I64ReinterpretF64 deserialize(Deserializer d) => const I64ReinterpretF64(); + + @override + String get name => 'i64.reinterpret_f64'; } class F32ReinterpretI32 extends SingleByteInstruction { @@ -2894,6 +4133,9 @@ class F32ReinterpretI32 extends SingleByteInstruction { static F32ReinterpretI32 deserialize(Deserializer d) => const F32ReinterpretI32(); + + @override + String get name => 'f32.reinterpret_i32'; } class F64ReinterpretI64 extends SingleByteInstruction { @@ -2901,36 +4143,54 @@ class F64ReinterpretI64 extends SingleByteInstruction { static F64ReinterpretI64 deserialize(Deserializer d) => const F64ReinterpretI64(); + + @override + String get name => 'f64.reinterpret_i64'; } class I32Extend8S extends SingleByteInstruction { const I32Extend8S() : super(0xC0); static I32Extend8S deserialize(Deserializer d) => const I32Extend8S(); + + @override + String get name => 'i32.extend8_s'; } class I32Extend16S extends SingleByteInstruction { const I32Extend16S() : super(0xC1); static I32Extend16S deserialize(Deserializer d) => const I32Extend16S(); + + @override + String get name => 'i32.extend16_s'; } class I64Extend8S extends SingleByteInstruction { const I64Extend8S() : super(0xC2); static I64Extend8S deserialize(Deserializer d) => const I64Extend8S(); + + @override + String get name => 'i64.extend8_s'; } class I64Extend16S extends SingleByteInstruction { const I64Extend16S() : super(0xC3); static I64Extend16S deserialize(Deserializer d) => const I64Extend16S(); + + @override + String get name => 'i64.extend16_s'; } class I64Extend32S extends SingleByteInstruction { const I64Extend32S() : super(0xC4); static I64Extend32S deserialize(Deserializer d) => const I64Extend32S(); + + @override + String get name => 'i64.extend32_s'; } class I32TruncSatF32S extends Instruction { @@ -2943,6 +4203,9 @@ class I32TruncSatF32S extends Instruction { } static I32TruncSatF32S deserialize(Deserializer d) => const I32TruncSatF32S(); + + @override + String get name => 'i32.trunc_sat_f32_s'; } class I32TruncSatF32U extends Instruction { @@ -2955,6 +4218,9 @@ class I32TruncSatF32U extends Instruction { } static I32TruncSatF32U deserialize(Deserializer d) => const I32TruncSatF32U(); + + @override + String get name => 'i32.trunc_sat_f32_u'; } class I32TruncSatF64S extends Instruction { @@ -2967,6 +4233,9 @@ class I32TruncSatF64S extends Instruction { } static I32TruncSatF64S deserialize(Deserializer d) => const I32TruncSatF64S(); + + @override + String get name => 'i32.trunc_sat_f64_s'; } class I32TruncSatF64U extends Instruction { @@ -2979,6 +4248,9 @@ class I32TruncSatF64U extends Instruction { } static I32TruncSatF64U deserialize(Deserializer d) => const I32TruncSatF64U(); + + @override + String get name => 'i32.trunc_sat_f64_u'; } class I64TruncSatF32S extends Instruction { @@ -2991,6 +4263,9 @@ class I64TruncSatF32S extends Instruction { } static I64TruncSatF32S deserialize(Deserializer d) => const I64TruncSatF32S(); + + @override + String get name => 'i64.trunc_sat_f32_s'; } class I64TruncSatF32U extends Instruction { @@ -3003,6 +4278,9 @@ class I64TruncSatF32U extends Instruction { } static I64TruncSatF32U deserialize(Deserializer d) => const I64TruncSatF32U(); + + @override + String get name => 'i64.trunc_sat_f32_u'; } class I64TruncSatF64S extends Instruction { @@ -3015,6 +4293,9 @@ class I64TruncSatF64S extends Instruction { } static I64TruncSatF64S deserialize(Deserializer d) => const I64TruncSatF64S(); + + @override + String get name => 'i64.trunc_sat_f64_s'; } class I64TruncSatF64U extends Instruction { @@ -3027,6 +4308,9 @@ class I64TruncSatF64U extends Instruction { } static I64TruncSatF64U deserialize(Deserializer d) => const I64TruncSatF64U(); + + @override + String get name => 'i64.trunc_sat_f64_u'; } class BeginNoEffectTryTable extends Instruction { @@ -3049,6 +4333,9 @@ class BeginNoEffectTryTable extends Instruction { catch_.serialize(s); } } + + @override + String get name => 'try_table'; } class BeginOneOutputTryTable extends Instruction { @@ -3076,6 +4363,9 @@ class BeginOneOutputTryTable extends Instruction { catch_.serialize(s); } } + + @override + String get name => 'try_table'; } class BeginFunctionTryTable extends Instruction { @@ -3096,6 +4386,9 @@ class BeginFunctionTryTable extends Instruction { catch_.serialize(s); } } + + @override + String get name => 'try_table'; } abstract class TryTableCatch { diff --git a/pkg/wasm_builder/lib/src/ir/instructions.dart b/pkg/wasm_builder/lib/src/ir/instructions.dart index 13bc1fb207c..812e05002dd 100644 --- a/pkg/wasm_builder/lib/src/ir/instructions.dart +++ b/pkg/wasm_builder/lib/src/ir/instructions.dart @@ -4,6 +4,7 @@ import '../../source_map.dart'; import '../serialize/serialize.dart'; +import '../serialize/printer.dart'; import 'ir.dart'; class Instructions implements Serializable { @@ -69,6 +70,79 @@ class Instructions implements Serializable { s.sourceMapSerializer.addMapping(s.offset, null); } + void printInitializerTo(IrPrinter p) { + 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(')'); + } + } + + void printTo(IrPrinter p) { + p.beginLabeledBlock(null); + for (int k = 0; k < instructions.length; ++k) { + final i = instructions[k]; + + final isTry = i is BeginNoEffectTry || + i is BeginOneOutputTry || + i is BeginFunctionTry; + final isTryTable = i is BeginNoEffectTryTable || + i is BeginOneOutputTryTable || + i is BeginFunctionTryTable; + final isIf = + i is BeginNoEffectIf || i is BeginOneOutputIf || i is BeginFunctionIf; + final isBlock = i is BeginNoEffectBlock || + i is BeginOneOutputBlock || + i is BeginFunctionBlock; + final isLoop = i is BeginNoEffectLoop || + i is BeginOneOutputLoop || + i is BeginFunctionLoop; + if (isTry || isIf || isBlock || isTryTable || isLoop) { + p.beginLabeledBlock(i); + i.printTo(p); + p.writeln(); + p.indent(); + continue; + } + + final isCatch = i is CatchLegacy || i is CatchAllLegacy; + final isElse = i is Else; + if (isCatch || isElse) { + p.deindent(); + i.printTo(p); + p.writeln(); + p.indent(); + continue; + } + + final isEnd = i is End; + if (isEnd) { + final labelInfo = p.endLabeledBlock(); + if (labelInfo?.target != null) { + // The outermost label belongs to the function and it wasn't indented + // so we don't have to deindent either. + p.deindent(); + } + final isLast = k == (instructions.length - 1); + if (!isLast) { + i.printTo(p); + if (labelInfo != null && labelInfo.used) { + p.write(' '); + p.write(labelInfo.name!); + } + p.writeln(); + } + continue; + } + + i.printTo(p); + p.writeln(); + } + p.endLabeledBlock(); + } + static Instructions deserializeConst( Deserializer d, Types types, diff --git a/pkg/wasm_builder/lib/src/ir/module.dart b/pkg/wasm_builder/lib/src/ir/module.dart index bbbc170f677..112e514c8fa 100644 --- a/pkg/wasm_builder/lib/src/ir/module.dart +++ b/pkg/wasm_builder/lib/src/ir/module.dart @@ -5,6 +5,7 @@ import 'dart:typed_data'; import '../serialize/serialize.dart'; +import '../serialize/printer.dart'; import 'ir.dart'; /// A logically const wasm module ready to encode. Created with `ModuleBuilder`. @@ -233,4 +234,28 @@ class Module implements Serializable { sourceMapUrl, ); } + + String printAsWat() { + final mp = ModulePrinter(this); + + // 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 function in [...functions.imported, ...functions.defined]) { + mp.enqueueFunction(function); + } + return mp.print(); + } } diff --git a/pkg/wasm_builder/lib/src/ir/table.dart b/pkg/wasm_builder/lib/src/ir/table.dart index 14510978922..abfe9cfaf40 100644 --- a/pkg/wasm_builder/lib/src/ir/table.dart +++ b/pkg/wasm_builder/lib/src/ir/table.dart @@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import '../serialize/serialize.dart'; +import '../serialize/printer.dart'; import 'ir.dart'; /// An (imported or defined) table. @@ -44,6 +45,27 @@ class DefinedTable extends Table { DefinedTable(super.enclosingModule, this.elements, super.finalizableIndex, super.type, super.minSize, super.maxSize); + + void printTo(IrPrinter p) { + // 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(')'); + } + } + }); + p.write(')'); + } } /// An imported table. diff --git a/pkg/wasm_builder/lib/src/ir/tables.dart b/pkg/wasm_builder/lib/src/ir/tables.dart index cd58c984843..b1c9263cbf8 100644 --- a/pkg/wasm_builder/lib/src/ir/tables.dart +++ b/pkg/wasm_builder/lib/src/ir/tables.dart @@ -17,4 +17,6 @@ class Tables { Table operator [](int index) => index < imported.length ? imported[index] : defined[index - imported.length]; + + int get length => imported.length + defined.length; } diff --git a/pkg/wasm_builder/lib/src/ir/tags.dart b/pkg/wasm_builder/lib/src/ir/tags.dart index 035e2e6b5b5..5b095e34587 100644 --- a/pkg/wasm_builder/lib/src/ir/tags.dart +++ b/pkg/wasm_builder/lib/src/ir/tags.dart @@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import '../serialize/serialize.dart'; +import '../serialize/printer.dart'; import 'ir.dart'; /// An exported tag from the current module. @@ -36,6 +37,8 @@ abstract class Tag with Indexable, Exportable { Export buildExport(String name) { return TagExport(name, this); } + + void printTo(IrPrinter p); } /// A tag defined in the current module. @@ -48,6 +51,15 @@ class DefinedTag extends Tag implements Serializable { s.writeByte(0x00); s.write(type); } + + @override + void printTo(IrPrinter p) { + p.write('(tag '); + p.writeTagReference(this); + p.write(' '); + type.printOneLineSignatureTo(p); + p.write(')'); + } } /// A tag imported from another module. @@ -70,6 +82,17 @@ class ImportedTag extends Tag implements Import { s.writeByte(0x00); s.write(type); } + + @override + void printTo(IrPrinter p) { + p.write('(tag '); + p.writeTagReference(this); + p.write(' '); + p.writeImport(module, name); + p.write(' '); + type.printOneLineSignatureTo(p); + p.write(')'); + } } class Tags { diff --git a/pkg/wasm_builder/lib/src/ir/type.dart b/pkg/wasm_builder/lib/src/ir/type.dart index d8993e71883..e38596da509 100644 --- a/pkg/wasm_builder/lib/src/ir/type.dart +++ b/pkg/wasm_builder/lib/src/ir/type.dart @@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import '../serialize/serialize.dart'; +import '../serialize/printer.dart'; // Representations of all Wasm types. @@ -298,7 +299,7 @@ class RefType extends ValueType { if (nullable == heapType.nullableByDefault) { return "${heapType.shorthandName}ref"; } - return "ref${nullable ? " null " : " "}$heapType"; + return "ref ${nullable ? "null " : ""}$heapType"; } @override @@ -685,7 +686,7 @@ class NoExternHeapType extends HeapType { void serialize(Serializer s) => s.writeByte(0x72); // -0x0e @override - String toString() => "extern"; + String toString() => "noextern"; @override String get shorthandName => "nullextern"; @@ -873,6 +874,20 @@ abstract class DefType extends HeapType { } void deserializeFillInner(Deserializer d, List existing); + + void printTypeDefTo(IrPrinter p) { + // This may generate other types that this one refers to. + final ip = p.dup(); + printTypeDefToInternal(ip); + + p.write('(type '); + p.writeDefTypeReference(this); + p.write(' '); + p.write(ip.getText()); + p.write(')'); + } + + void printTypeDefToInternal(IrPrinter p); } class UnresolvedDefType extends DefType { @@ -906,6 +921,10 @@ class UnresolvedDefType extends DefType { @override void deserializeFillInner(Deserializer d, List existing) => throw 'Cannot deserialize unresolved type'; + + @override + void printTypeDefToInternal(IrPrinter p) => + throw 'Cannot print unresolved type'; } /// The `exn` heap type. @@ -1030,6 +1049,59 @@ class FunctionType extends DefType { outputs.addAll(d.readList((d) => ValueType.deserialize(d, existing))); } + @override + void printTypeDefToInternal(IrPrinter p) { + p.withLocalNames({}, () { + p.write('(func '); + printSignatureWithNamesTo(p, oneLine: false); + p.write(')'); + }); + } + + void printOneLineSignatureTo(IrPrinter p) { + if (inputs.isNotEmpty) { + p.write('(param '); + for (int i = 0; i < inputs.length; ++i) { + if (i > 0) p.write(' '); + p.writeValueType(inputs[i]); + } + p.write(')'); + } + if (inputs.isNotEmpty && outputs.isNotEmpty) { + p.write(' '); + } + for (int i = 0; i < outputs.length; ++i) { + if (i > 0) p.write(' '); + p.write('(result '); + p.writeValueType(outputs[i]); + p.write(')'); + } + } + + void printSignatureWithNamesTo(IrPrinter p, {bool oneLine = true}) { + final indent = !oneLine && (inputs.length + outputs.length) > 2; + final sep = indent ? '\n ' : ' '; + + if (indent) p.write(sep); + for (int i = 0; i < inputs.length; ++i) { + if (i > 0) p.write(sep); + p.write('(param '); + p.writeLocalIndexReference(i); + p.write(' '); + p.writeValueType(inputs[i]); + p.write(')'); + } + if (inputs.isNotEmpty && outputs.isNotEmpty) { + p.write(sep); + } + for (int i = 0; i < outputs.length; ++i) { + if (i > 0) p.write(sep); + p.write('(result '); + p.writeValueType(outputs[i]); + p.write(')'); + } + } + @override String toString() => "(${inputs.join(", ")}) -> (${outputs.join(", ")})"; } @@ -1113,6 +1185,47 @@ class StructType extends DataType { void deserializeFillInner(Deserializer d, List existing) { fields.addAll(d.readList((d) => FieldType.deserialize(d, existing))); } + + @override + void printTypeDefToInternal(IrPrinter p) { + final sup = superType; + + p.write('('); + if (sup != null) { + p.write('sub '); + if (!hasAnySubtypes) { + p.write('final '); + } + p.writeDefTypeReference(sup); + p.write(' ('); + } + p.write('struct'); + p.withIndent(() { + for (int i = 0; i < fields.length; ++i) { + if (fields.length > 2) { + p.writeln(); + } else { + p.write(' '); + } + + final field = fields[i]; + final name = '\$${fieldNames[i] ?? 'field$i'}'; + p.write('(field $name '); + if (field.mutable) { + p.write('(mut '); + p.writeStorageTypeTypeReference(field.type); + p.write(')'); + } else { + p.writeStorageTypeTypeReference(field.type); + } + p.write(')'); + } + }); + if (sup != null) { + p.write(')'); + } + p.write(')'); + } } /// A custom `array` type. @@ -1157,6 +1270,20 @@ class ArrayType extends DataType { void deserializeFillInner(Deserializer d, List existing) { elementType = FieldType.deserialize(d, existing); } + + @override + void printTypeDefToInternal(IrPrinter p) { + p.write('(array '); + p.write('(field '); + if (elementType.mutable) { + p.write('(mut '); + } + p.writeStorageTypeTypeReference(elementType.type); + if (elementType.mutable) { + p.write(')'); + } + p.write('))'); + } } class _WithMutability implements Serializable { @@ -1193,6 +1320,12 @@ class GlobalType extends _WithMutability { _WithMutability.deserialize(d, (d) => ValueType.deserialize(d, types)); return GlobalType(type, mutable: mutable); } + + void printTo(IrPrinter p) { + if (mutable) p.write('(mut '); + p.writeValueType(type); + if (mutable) p.write(')'); + } } /// A type for a struct field or an array element. diff --git a/pkg/wasm_builder/lib/src/serialize/printer.dart b/pkg/wasm_builder/lib/src/serialize/printer.dart new file mode 100644 index 00000000000..bf3bff26b77 --- /dev/null +++ b/pkg/wasm_builder/lib/src/serialize/printer.dart @@ -0,0 +1,663 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:core'; +import 'dart:typed_data'; +import 'dart:collection'; + +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); + + final _types = {}; + final _tags = {}; + final _tables = {}; + final _globals = {}; + final _functions = {}; + + final _typeQueue = Queue(); + final _functionsQueue = Queue(); + + ModulePrinter(this._module); + + IrPrinter newIrPrinter() => IrPrinter._(_module, _typeNamer, _globalNamer, + _functionNamer, _tagNamer, _tableNamer); + + void enqueueType(ir.DefType type) { + if (!_types.containsKey(type)) { + _types[type] = ''; + _generateType(type); + } + } + + void enqueueGlobal(ir.Global global) { + if (!_globals.containsKey(global)) { + _globals[global] = ''; + _generateGlobal(global); + } + } + + void enqueueFunction(ir.BaseFunction fun) { + if (!_functions.containsKey(fun)) { + _functions[fun] = ''; + if (fun is ir.ImportedFunction) { + _generateImportedFunction(fun); + } else { + _functionsQueue.add(fun as ir.DefinedFunction); + } + } + } + + void enqueueTag(ir.Tag tag) { + if (!_tags.containsKey(tag)) { + _tags[tag] = ''; + _generateTag(tag); + } + } + + void enqueueTable(ir.Table table) { + if (!_tables.containsKey(table)) { + _tables[table] = ''; + _generateTable(table); + } + } + + String print() { + while (_functionsQueue.isNotEmpty || _typeQueue.isNotEmpty) { + while (_functionsQueue.isNotEmpty) { + final fun = _functionsQueue.removeFirst(); + _generateFunction(fun); + } + } + + final mp = IndentPrinter(); + mp.writeln('(module \$${_module.moduleName}'); + mp.withIndent(() { + for (final group in _module.types.recursionGroups) { + final filtered = group.where((t) => _types.containsKey(t)).toList(); + if (filtered.isNotEmpty) { + if (filtered.length == 1) { + mp.write(_types[filtered.single]!); + mp.writeln(); + } else { + mp.writeln('(rec'); + mp.withIndent(() { + for (final type in filtered) { + mp.write(_types[type]!); + mp.writeln(); + } + }); + mp.writeln(')'); + } + } + } + for (final fun in _module.functions.imported) { + final s = _functions[fun]; + if (s != null) { + mp.write(s); + mp.writeln(); + } + } + for (final global in _module.globals.imported) { + final s = _globals[global]; + if (s != null) { + mp.write(s); + mp.writeln(); + } + } + for (final tag in _module.tags.defined) { + final s = _tags[tag]; + if (s != null) { + mp.write(s); + mp.writeln(); + } + } + for (final global in _module.globals.defined) { + final s = _globals[global]; + if (s != null) { + mp.write(s); + mp.writeln(); + } + } + for (final table in _module.tables.defined) { + final s = _tables[table]; + if (s != null) { + mp.write(s); + mp.writeln(); + } + } + for (final fun in _module.functions.defined) { + final s = _functions[fun]; + if (s != null) { + mp.write(s); + mp.writeln(); + } + } + }); + mp.write(')'); + return mp.getText(); + } + + void _generateTag(ir.Tag tag) { + final p = newIrPrinter(); + tag.printTo(p); + _tags[tag] = p.getText(); + } + + void _generateTable(ir.Table table) { + if (table is! ir.DefinedTable) return; + + final p = newIrPrinter(); + table.printTo(p); + _tables[table] = p.getText(); + } + + void _generateGlobal(ir.Global global) { + final p = newIrPrinter(); + global.printTo(p); + _globals[global] = p.getText(); + } + + void _generateImportedFunction(ir.ImportedFunction fun) { + final p = newIrPrinter(); + fun.printTo(p); + _functions[fun] = p.getText(); + } + + void _generateFunction(ir.DefinedFunction fun) { + final p = newIrPrinter(); + fun.printTo(p); + _functions[fun] = p.getText(); + } + + void _generateType(ir.DefType type) { + final p = newIrPrinter(); + type.printTypeDefTo(p); + _types[type] = p.getText(); + } +} + +class IndentPrinter { + final _buffer = StringBuffer(); + int _indent = 0; + bool _startOfLine = true; + + void write(String s) { + final lines = s.split('\n'); + for (int i = 0; i < lines.length; ++i) { + _writePartOfLine(lines[i]); + if (i < (lines.length - 1)) { + _writeNewLine(); + } + } + } + + void writeImport(String module, String name) { + write('(import "'); + write(_escapeString(module)); + write('" "'); + write(_escapeString(name)); + write('")'); + } + + void writeExport(String name) { + write('(export "${_escapeString(name)}")'); + } + + void writeln([String? s]) { + if (s != null) { + write(s); + } + write('\n'); + } + + void withIndent(void Function() fun) { + final before = _indent; + _indent++; + fun(); + assert(before == (_indent - 1)); + _indent = before; + } + + void indent() { + _indent++; + } + + void deindent() { + _indent--; + assert(_indent >= 0); + } + + void _writePartOfLine(String text) { + assert(!text.contains('\n')); + if (text.isEmpty) return; + if (_startOfLine) { + _buffer.write(' ' * _indent); + _startOfLine = false; + } + _buffer.write(text); + } + + void _writeNewLine() { + _buffer.write('\n'); + _startOfLine = true; + } + + String getText() => '$_buffer'; +} + +class IrPrinter extends IndentPrinter { + final ir.Module module; + + final _TypeNamer _typeNamer; + final _GlobalNamer _globalNamer; + final _FunctionNamer _functionNamer; + final _TagNamer _tagNamer; + final _TableNamer _tableNamer; + + _LocalNamer? _localNamer; + final _labelNamer = _LabelNamer(); + + IrPrinter._(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); + + void beginLabeledBlock(ir.Instruction? instruction) { + _labelNamer.stack.add(LabelInfo(instruction)); + } + + LabelInfo? endLabeledBlock() { + if (_labelNamer.stack.isEmpty) return null; + final last = _labelNamer.stack.removeLast(); + return last; + } + + void writeLabelDefinition(int labelIndex) { + write(_labelNamer.nameLabel(labelIndex, use: false)); + } + + void writeLabelReference(int labelIndex) { + write(_labelNamer.nameLabel(labelIndex)); + } + + void writeLocalReference(ir.Local local) { + write(_localNamer!.nameLocal(local.index)); + } + + void writeLocalIndexReference(int localIndex) { + write(_localNamer!.nameLocal(localIndex)); + } + + void withLocalNames(Map names, void Function() fun) { + _localNamer = _LocalNamer(names); + fun(); + _localNamer = null; + } + + void writeStorageTypeTypeReference(ir.StorageType type, {bool ref = true}) { + if (type is ir.PackedType) { + write('$type'); + return; + } + writeValueType(type as ir.ValueType); + } + + String _defTypeName(ir.DefType type, bool nullable, bool ref) { + final name = _typeNamer.nameDefType(type); + if (ref) { + return nullable ? '(ref null $name)' : '(ref $name)'; + } + return nullable ? 'null $name' : name; + } + + void writeRefTypeReference(ir.RefType type) { + writeValueType(type, ref: false); + } + + void writeValueType(ir.ValueType type, {bool ref = true}) { + if (type is ir.NumType) { + write('$type'); + return; + } + if (type is ir.RefType) { + final heapType = type.heapType; + if (heapType is ir.DefType) { + write(_defTypeName(heapType, type.nullable, ref)); + return; + } + + if (heapType.nullableByDefault == true && type.nullable) { + write('$type'); + return; + } + if (heapType.nullableByDefault != true && !type.nullable) { + write('$type'); + return; + } + } + + write('($type)'); + } + + void writeDefTypeReference(ir.DefType type) { + write(_typeNamer.nameDefType(type)); + } + + void writeFunctionType(ir.FunctionType type) { + type.printOneLineSignatureTo(this); + } + + void writeHeapTypeReference(ir.HeapType heapType) { + if (heapType is ir.DefType) { + write(_defTypeName(heapType, false, false)); + return; + } + write('$heapType'); + } + + void writeTableReference(ir.Table? table, {bool alwaysPrint = false}) { + final name = _tableNamer.nameTable(table); + if (alwaysPrint) { + write(name); + return; + } + if (module.tables.length > 1) { + write(' '); + write(name); + } + } + + void writeFieldReference(ir.StructType type, int fieldIndex) { + final name = type.fieldNames[fieldIndex] ?? 'field$fieldIndex'; + write(_defTypeName(type, false, false)); + write(' \$$name'); + } + + void writeGlobalReference(ir.Global global) { + write(_globalNamer.nameGlobal(global)); + } + + void writeFunctionReference(ir.BaseFunction function) { + write(_functionNamer.nameFunction(function)); + } + + void writeTagReference(ir.Tag tag) { + write(_tagNamer.nameTag(tag)); + } + + void writeDataReference(ir.BaseDataSegment dataSegment) { + throw UnimplementedError(); + } + + void writeMemoryReference(ir.Memory memory) { + throw UnimplementedError(); + } +} + +class _Namer { + int _nextId = 0; + + final Map _names = {}; + final void Function(T) _onReference; + + _Namer(this._onReference); + + String _name(T key, String? name, String unnamedPrefix) { + final existing = _names[key]; + if (existing != null) return existing; + + _onReference(key); + final sanitizedName = + name != null ? _sanitizeName(name) : '$unnamedPrefix${_nextId++}'; + return _names[key] ??= '\$$sanitizedName'; + } +} + +class _FunctionNamer extends _Namer { + _FunctionNamer(super.onReference); + + String nameFunction(ir.BaseFunction function) { + return super._name(function, function.functionName, ''); + } +} + +class _TagNamer extends _Namer { + _TagNamer(super.onReference); + + String nameTag(ir.Tag tag) { + return super._name(tag, null, 'tag'); + } +} + +class _TableNamer extends _Namer { + _TableNamer(super.onReference); + + String nameTable(ir.Table? table) { + if (table == null) { + return '\$table0'; + } + return super._name(table, null, 'table'); + } +} + +class _TypeNamer extends _Namer { + _TypeNamer(super.onReference); + + String nameDefType(ir.DefType type) { + return super._name(type, type is ir.DataType ? type.name : null, 'type'); + } +} + +class _LocalNamer extends _Namer { + final Map _namedVariables; + + _LocalNamer(this._namedVariables) : super((_) {}); + + String nameLocal(int index) { + return super._name(index, _namedVariables[index], 'var'); + } +} + +class _GlobalNamer extends _Namer { + _GlobalNamer(super.onReference); + + String nameGlobal(ir.Global global) { + String? gn = global.globalName; + if (gn == null && global is ir.ImportedGlobal) { + gn = '${global.module}.${global.name}'; + } + return super._name(global, gn, 'global'); + } +} + +class _LabelNamer { + int _nextId = 0; + final stack = []; + _LabelNamer(); + + String nameLabel(int n, {bool use = true}) { + final index = stack.length - 1 - n; + final info = stack[index]; + if (use) info.used = true; + return info.name ??= '\$label${_nextId++}'; + } +} + +class LabelInfo { + // This is optional as the function (body) itself introduces a label that a + // branch instruction can break to (aka return). + final ir.Instruction? target; + + String? name; + bool used = false; + + LabelInfo(this.target); +} + +String _escapeString(String s) { + final units = s.codeUnits; + final sb = StringBuffer(); + int startIndex = 0; + while (startIndex < units.length) { + int endIndex = units.length; + int endUnit = 0; + for (int i = startIndex; i < units.length; ++i) { + final unit = units[i]; + if (unit < 0x20 || + 0x7e < unit || + unit == _backslash || + unit == _doubleQuote) { + endUnit = unit; + endIndex = i; + break; + } + } + sb.write(s.substring(startIndex, endIndex)); + startIndex = endIndex + 1; + if (endIndex < units.length) { + if (endUnit == _backslash) { + sb.write('\\\\'); + continue; + } + if (endUnit == _doubleQuote) { + sb.write('\\"'); + continue; + } + if (endUnit == _newline) { + sb.write('\\n'); + continue; + } + if (endUnit == _cr) { + sb.write('\\r'); + continue; + } + if (endUnit == _tab) { + sb.write('\\t'); + continue; + } + if (endUnit.isLeadSurrogate) { + if ((endIndex + 1) < units.length) { + final tail = units[endIndex + 1]; + if (tail.isTailSurrogate) { + startIndex++; + sb.writeEscapedPairedRune(endUnit, tail); + continue; + } + } + } + sb.writeEscapedUnpairedRune(endUnit); + continue; + } + } + return '$sb'; +} + +String _sanitizeName(String s) { + final units = s.codeUnits; + for (int i = 0; i < units.length; ++i) { + final unit = units[i]; + final sanitizedUnit = _nameAsciiMapping[unit & 0x7f]; + if (unit != sanitizedUnit) { + return '''"${_escapeString(s)}"'''; + } + } + return s; +} + +final Uint8List _nameAsciiMapping = (() { + // Names are only allowed to have certain characters in them in the wat + // format. See allowed characters at + // https://webassembly.github.io/spec/core/text/values.html#text-id + final map = Uint8List(128); + for (int unit = 'a'.ordinal; unit <= 'z'.ordinal; ++unit) { + map[unit] = unit; + } + for (int unit = 'A'.ordinal; unit <= 'Z'.ordinal; ++unit) { + map[unit] = unit; + } + for (int unit = '0'.ordinal; unit <= '9'.ordinal; ++unit) { + map[unit] = unit; + } + map['!'.ordinal] = '!'.ordinal; + map['#'.ordinal] = '#'.ordinal; + map['\$'.ordinal] = '\$'.ordinal; + map['%'.ordinal] = '%'.ordinal; + map['&'.ordinal] = '&'.ordinal; + map['\''.ordinal] = '\''.ordinal; + map['*'.ordinal] = '*'.ordinal; + map['+'.ordinal] = '+'.ordinal; + map['-'.ordinal] = '-'.ordinal; + map['.'.ordinal] = '.'.ordinal; + map['/'.ordinal] = '/'.ordinal; + map[':'.ordinal] = ':'.ordinal; + map['<'.ordinal] = '<'.ordinal; + map['='.ordinal] = '='.ordinal; + map['>'.ordinal] = '>'.ordinal; + map['?'.ordinal] = '?'.ordinal; + map['@'.ordinal] = '@'.ordinal; + map['\\'.ordinal] = '\\'.ordinal; + map['^'.ordinal] = '^'.ordinal; + map['_'.ordinal] = '_'.ordinal; + map['`'.ordinal] = '`'.ordinal; + map['|'.ordinal] = '|'.ordinal; + map['~'.ordinal] = '~'.ordinal; + return map; +})(); + +extension on String { + int get ordinal => codeUnitAt(0); +} + +extension on StringBuffer { + void writeEscapedUnpairedRune(int rune) { + if (rune < 0x7ff) { + writeHex(0xC0 | (rune >> 6)); + writeHex(0x80 | (rune & 0x3f)); + return; + } + writeHex(0xE0 | (rune >> 12)); + writeHex(0x80 | ((rune >> 6) & 0x3f)); + writeHex(0x80 | (rune & 0x3f)); + } + + void writeEscapedPairedRune(int leadSurrogate, int tailSurrogate) { + final rune = _combineSurrogatePair(leadSurrogate, tailSurrogate); + writeHex(0xF0 | (rune >> 18)); + writeHex(0x80 | ((rune >> 12) & 0x3f)); + writeHex(0x80 | ((rune >> 6) & 0x3f)); + writeHex(0x80 | (rune & 0x3f)); + } + + void writeHex(int value) { + write('\\'); + write((value >> 4).toRadixString(16)); + write((value & 0xf).toRadixString(16)); + } +} + +extension on int { + bool get isLeadSurrogate => (this & 0xFC00) == 0xD800; + bool get isTailSurrogate => (this & 0xFC00) == 0xDC00; +} + +int _combineSurrogatePair(int lead, int tail) { + return 0x10000 + ((lead & 0x3ff) << 10) + (tail & 0x3ff); +} + +const _doubleQuote = 0x22; +const _tab = 0x9; +const _newline = 0x0a; +const _cr = 0xd; +const _backslash = 0x5c;