From 8322e6af370375548baaed24b0f523ae98f224fb Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Wed, 1 Oct 2025 03:37:31 -0700 Subject: [PATCH] [dart2wasm] Add support for reading wasm files to `package:wasm_builder` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds a wasm binary reader that produces an `ir.Module`. We also make a few changes to existing code * Represent the import section with an `ir.Imports` object (similar to `ir.Exports`, `ir.Functions`, ...) * We make a bunch of data structures allocatable in uninitialized state (the fields being usually uninitialized `late final` fields) where the deserializer can create those objects and then fill in details later. => This comes partly due to the way wasm binaries are structured themselves: The "data count" section comes first so a reader knows how many data sections there will be, then the "code section" can refer to those data sections. Then afterwards the actual "data segment" comes that fills in the data of the section. * We make names consistently optional: Wasm objects don't have to have names, so the names should be optional, so we make them `String?`. We also make them non-final as that's consistent with other names. * We make the `ir.Types`, `ir.Functions`, ... objects have `operator[]` and the index used is the same index used e.g. in wasm instructions. * We make static constants for section ids and custom section names. Issue https://github.com/dart-lang/sdk/issues/60928 Change-Id: I5394d6b82cf4dc68d24cea1dee66c5b33eb2f60f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/452144 Commit-Queue: Martin Kustermann Reviewed-by: Ömer Ağacan --- pkg/dart2wasm/lib/dynamic_modules.dart | 2 +- pkg/dart2wasm/lib/translator.dart | 2 +- pkg/dart2wasm/test/self_compile_test.dart | 21 +- pkg/dart2wasm/test/wasm_read_write_test.dart | 44 + .../lib/src/builder/functions.dart | 9 +- .../lib/src/builder/instructions.dart | 2 +- pkg/wasm_builder/lib/src/builder/module.dart | 22 +- pkg/wasm_builder/lib/src/ir/data_segment.dart | 10 +- pkg/wasm_builder/lib/src/ir/function.dart | 10 +- pkg/wasm_builder/lib/src/ir/functions.dart | 15 +- pkg/wasm_builder/lib/src/ir/global.dart | 6 +- pkg/wasm_builder/lib/src/ir/globals.dart | 6 + pkg/wasm_builder/lib/src/ir/imports.dart | 26 + pkg/wasm_builder/lib/src/ir/instruction.dart | 1321 ++++++++++++++++- pkg/wasm_builder/lib/src/ir/instructions.dart | 37 + pkg/wasm_builder/lib/src/ir/ir.dart | 12 +- pkg/wasm_builder/lib/src/ir/memories.dart | 4 + pkg/wasm_builder/lib/src/ir/module.dart | 134 +- pkg/wasm_builder/lib/src/ir/tables.dart | 4 + pkg/wasm_builder/lib/src/ir/tags.dart | 4 + pkg/wasm_builder/lib/src/ir/type.dart | 304 ++++ pkg/wasm_builder/lib/src/ir/types.dart | 9 +- .../lib/src/serialize/deserializer.dart | 86 ++ .../lib/src/serialize/sections.dart | 697 ++++++++- .../lib/src/serialize/serialize.dart | 19 +- tools/bots/test_matrix.json | 1 + 26 files changed, 2672 insertions(+), 135 deletions(-) create mode 100644 pkg/dart2wasm/test/wasm_read_write_test.dart create mode 100644 pkg/wasm_builder/lib/src/serialize/deserializer.dart diff --git a/pkg/dart2wasm/lib/dynamic_modules.dart b/pkg/dart2wasm/lib/dynamic_modules.dart index 4d7ee8ee7e8..5af3b3c6f38 100644 --- a/pkg/dart2wasm/lib/dynamic_modules.dart +++ b/pkg/dart2wasm/lib/dynamic_modules.dart @@ -395,7 +395,7 @@ class DynamicModuleInfo { DynamicModuleInfo(this.translator, this.metadata); void initSubmodule() { - submodule.functions.start = initFunction = submodule.functions.define( + submodule.startFunction = initFunction = submodule.functions.define( translator.typesBuilder.defineFunction(const [], const []), "#init"); // Make sure the exception tag is exported from the main module. diff --git a/pkg/dart2wasm/lib/translator.dart b/pkg/dart2wasm/lib/translator.dart index 74f4ac80c9c..158863efd45 100644 --- a/pkg/dart2wasm/lib/translator.dart +++ b/pkg/dart2wasm/lib/translator.dart @@ -549,7 +549,7 @@ class Translator with KernelNodes { _initModules(sourceMapUrlGenerator); initFunction = mainModule.functions .define(typesBuilder.defineFunction(const [], const []), "#init"); - mainModule.functions.start = initFunction; + mainModule.startFunction = initFunction; closureLayouter.collect(); classInfoCollector.collect(); diff --git a/pkg/dart2wasm/test/self_compile_test.dart b/pkg/dart2wasm/test/self_compile_test.dart index add127e0aba..cdb8097c255 100644 --- a/pkg/dart2wasm/test/self_compile_test.dart +++ b/pkg/dart2wasm/test/self_compile_test.dart @@ -3,6 +3,7 @@ // 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; @@ -39,14 +40,7 @@ Future main() async { final wasmBytes = outFile.readAsBytesSync(); outFile.renameSync(outDart2WasmFilename); - if (vmBytes.length != wasmBytes.length) { - throw 'Mismatch in length ${vmBytes.length} vs ${wasmBytes.length}'; - } - for (int i = 0; i < vmBytes.length; ++i) { - if (vmBytes[i] != wasmBytes[i]) { - throw 'Mismatch at offset $i ${vmBytes[i]} vs ${wasmBytes[i]}'; - } - } + expectEqualBytes(vmBytes, wasmBytes); }); } @@ -61,6 +55,17 @@ Future run(List command) async { } } +void expectEqualBytes(Uint8List a, Uint8List b) { + if (a.length != b.length) { + throw 'Mismatch in length ${a.length} vs ${b.length}'; + } + for (int i = 0; i < a.length; ++i) { + if (a[i] != b[i]) { + throw 'Mismatch at offset $i ${a[i]} vs ${b[i]}'; + } + } +} + Future withTempDir(Future Function(String directory) fun) async { final dir = Directory.systemTemp.createTempSync('dart2wasm_self_compile'); try { diff --git a/pkg/dart2wasm/test/wasm_read_write_test.dart b/pkg/dart2wasm/test/wasm_read_write_test.dart new file mode 100644 index 00000000000..a005f792a32 --- /dev/null +++ b/pkg/dart2wasm/test/wasm_read_write_test.dart @@ -0,0 +1,44 @@ +// 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, expectEqualBytes; + +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 wasmFile = File(wasmFilename); + + await run([ + Platform.executable, + 'compile', + 'wasm', + '-O0', + dartFilename, + '-o', + wasmFilename, + ]); + final wasmBytes = wasmFile.readAsBytesSync(); + expectEqualBytes(wasmBytes, readWrite(wasmBytes)); + // Temporary files will be deleted when returning to [withTempDir]. + }); +} + +Uint8List readWrite(Uint8List wasmBytes) { + final deserializer = Deserializer(wasmBytes); + final module = Module.deserialize(deserializer); + + final serializer = Serializer(); + module.serialize(serializer); + return serializer.data; +} diff --git a/pkg/wasm_builder/lib/src/builder/functions.dart b/pkg/wasm_builder/lib/src/builder/functions.dart index 22bbe19851f..6e957d5a0ed 100644 --- a/pkg/wasm_builder/lib/src/builder/functions.dart +++ b/pkg/wasm_builder/lib/src/builder/functions.dart @@ -12,15 +12,9 @@ class FunctionsBuilder with Builder { final _functionBuilders = []; final _importedFunctions = []; final _declaredFunctions = {}; - ir.BaseFunction? _start; FunctionsBuilder(this._moduleBuilder); - set start(ir.BaseFunction init) { - assert(_start == null); - _start = init; - } - void collectUsedTypes(Set usedTypes) { for (final f in _functionBuilders) { usedTypes.add(f.type); @@ -62,7 +56,6 @@ class FunctionsBuilder with Builder { ir.Functions forceBuild() { final built = finalizeImportsAndBuilders( _importedFunctions, _functionBuilders); - return ir.Functions( - _start, _importedFunctions, built, [..._declaredFunctions]); + return ir.Functions(_importedFunctions, built, [..._declaredFunctions]); } } diff --git a/pkg/wasm_builder/lib/src/builder/instructions.dart b/pkg/wasm_builder/lib/src/builder/instructions.dart index 1995aaeef2c..fac4b649247 100644 --- a/pkg/wasm_builder/lib/src/builder/instructions.dart +++ b/pkg/wasm_builder/lib/src/builder/instructions.dart @@ -789,7 +789,7 @@ class InstructionsBuilder with Builder { void select(ir.ValueType type) { assert(_verifyTypes([type, type, ir.NumType.i32], [type], trace: ['select', type])); - _add(ir.Select(type)); + _add(type is ir.NumType ? ir.Select() : ir.SelectWithType(type)); } // Variable instructions diff --git a/pkg/wasm_builder/lib/src/builder/module.dart b/pkg/wasm_builder/lib/src/builder/module.dart index ad0c37d086b..088db5c9dbc 100644 --- a/pkg/wasm_builder/lib/src/builder/module.dart +++ b/pkg/wasm_builder/lib/src/builder/module.dart @@ -32,6 +32,7 @@ class ModuleBuilder with Builder { final dataSegments = DataSegmentsBuilder(); late final globals = GlobalsBuilder(this); final exports = ExportsBuilder(); + ir.BaseFunction? _startFunction; /// Create a new, initially empty, module. /// @@ -44,6 +45,11 @@ class ModuleBuilder with Builder { types = TypesBuilder(this, parent: parent?.types); } + set startFunction(ir.BaseFunction init) { + assert(_startFunction == null); + _startFunction = init; + } + @override ir.Module forceBuild() { final finalFunctions = functions.build(); @@ -51,10 +57,18 @@ class ModuleBuilder with Builder { final finalMemories = memories.build(); final finalGlobals = globals.build(); final finalTags = tags.build(); + final imports = ir.Imports( + finalFunctions.imported, + finalTags.imported, + finalGlobals.imported, + finalTables.imported, + finalMemories.imported, + ); return module ..initialize( moduleName, finalFunctions, + _startFunction, finalTables, finalTags, finalMemories, @@ -62,13 +76,7 @@ class ModuleBuilder with Builder { finalGlobals, types.build(), dataSegments.build(), - [ - ...finalFunctions.imported, - ...finalTables.imported, - ...finalMemories.imported, - ...finalGlobals.imported, - ...finalTags.imported, - ], + imports, watchPoints, sourceMapUrl); } diff --git a/pkg/wasm_builder/lib/src/ir/data_segment.dart b/pkg/wasm_builder/lib/src/ir/data_segment.dart index b71e580f261..d6b9d345a8a 100644 --- a/pkg/wasm_builder/lib/src/ir/data_segment.dart +++ b/pkg/wasm_builder/lib/src/ir/data_segment.dart @@ -8,18 +8,20 @@ import '../serialize/serialize.dart'; import 'ir.dart'; class BaseDataSegment { - final int index; - final Memory? memory; - final int? offset; + late final int index; + late final Memory? memory; + late final int? offset; BaseDataSegment(this.index, this.memory, this.offset); + BaseDataSegment.uninitialized(); } /// A data segment in a module. class DataSegment extends BaseDataSegment implements Serializable { - final Uint8List content; + late final Uint8List content; DataSegment(super.index, this.content, super.memory, super.offset); + DataSegment.uninitialized() : super.uninitialized(); @override void serialize(Serializer s) { diff --git a/pkg/wasm_builder/lib/src/ir/function.dart b/pkg/wasm_builder/lib/src/ir/function.dart index 388c86f79d9..42701b44869 100644 --- a/pkg/wasm_builder/lib/src/ir/function.dart +++ b/pkg/wasm_builder/lib/src/ir/function.dart @@ -21,12 +21,12 @@ abstract class BaseFunction with Indexable, Exportable { @override final FinalizableIndex finalizableIndex; final FunctionType type; - final String? functionName; + String? functionName; @override final Module enclosingModule; BaseFunction(this.enclosingModule, this.finalizableIndex, this.type, - this.functionName); + [this.functionName]); @override String get name => functionName ?? super.name; @@ -40,7 +40,7 @@ abstract class BaseFunction with Indexable, Exportable { /// A function defined in a module. class DefinedFunction extends BaseFunction implements Serializable { - final Instructions body; + late final Instructions body; /// All local variables defined in the function, including its inputs. List get locals => body.locals; @@ -51,6 +51,10 @@ class DefinedFunction extends BaseFunction implements Serializable { super.enclosingModule, this.body, super.finalizableIndex, super.type, [super.functionName]); + DefinedFunction.withoutBody( + super.enclosingModule, super.finalizableIndex, super.type, + [super.functionName]); + @override void serialize(Serializer s) { // Serialize locals internally first in order to compute the total size of diff --git a/pkg/wasm_builder/lib/src/ir/functions.dart b/pkg/wasm_builder/lib/src/ir/functions.dart index 80578e786cc..6ea06ddc5c6 100644 --- a/pkg/wasm_builder/lib/src/ir/functions.dart +++ b/pkg/wasm_builder/lib/src/ir/functions.dart @@ -6,9 +6,6 @@ import 'function.dart'; /// The interface for the functions in a module. class Functions { - /// The start function. - final BaseFunction? start; - /// Imported functions. final List imported; @@ -16,7 +13,15 @@ class Functions { final List defined; /// Declared functions. - final List declared; + late final List declared; - Functions(this.start, this.imported, this.defined, this.declared); + Functions(this.imported, this.defined, this.declared); + + Functions.withoutDeclared(this.imported, this.defined); + + BaseFunction 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/global.dart b/pkg/wasm_builder/lib/src/ir/global.dart index a0f43a1c7a9..cc4db79e488 100644 --- a/pkg/wasm_builder/lib/src/ir/global.dart +++ b/pkg/wasm_builder/lib/src/ir/global.dart @@ -14,10 +14,10 @@ abstract class Global with Indexable, Exportable { final Module enclosingModule; /// Name of the global in the names section. - final String? globalName; + String? globalName; - Global( - this.enclosingModule, this.finalizableIndex, this.type, this.globalName); + Global(this.enclosingModule, this.finalizableIndex, this.type, + [this.globalName]); @override String toString() => globalName ?? "$finalizableIndex"; diff --git a/pkg/wasm_builder/lib/src/ir/globals.dart b/pkg/wasm_builder/lib/src/ir/globals.dart index da3e4151809..9990f174b18 100644 --- a/pkg/wasm_builder/lib/src/ir/globals.dart +++ b/pkg/wasm_builder/lib/src/ir/globals.dart @@ -12,4 +12,10 @@ class Globals { final List defined; Globals(this.imported, this.defined); + + Global 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/imports.dart b/pkg/wasm_builder/lib/src/ir/imports.dart index 31374594c4c..1492beb128d 100644 --- a/pkg/wasm_builder/lib/src/ir/imports.dart +++ b/pkg/wasm_builder/lib/src/ir/imports.dart @@ -5,6 +5,32 @@ import '../serialize/serialize.dart'; import 'ir.dart'; +class Imports { + late final List all; + + final List functions; + final List tags; + final List globals; + final List tables; + final List memories; + + Imports(this.functions, this.tags, this.globals, this.tables, this.memories) { + all = [ + ...functions, + ...tags, + ...globals, + ...tables, + ...memories, + ]; + } + + Imports.deserialized(this.all, this.functions, this.tags, this.globals, + this.tables, this.memories) { + assert(all.length == + (functions.length + tags.length + globals.length + tables.length)); + } +} + /// Any import (function, table, memory or global). abstract class Import implements Indexable, Serializable { String get module; diff --git a/pkg/wasm_builder/lib/src/ir/instruction.dart b/pkg/wasm_builder/lib/src/ir/instruction.dart index 26a8beae6e4..a1b578a01f6 100644 --- a/pkg/wasm_builder/lib/src/ir/instruction.dart +++ b/pkg/wasm_builder/lib/src/ir/instruction.dart @@ -24,6 +24,567 @@ abstract class Instruction implements Serializable { /// Constant instructions can be used in global initializers, element /// segments, data segments. bool get isConstant => false; + + static Instruction deserializeConst( + Deserializer d, Types types, Functions functions, Globals globals, + {bool isConstOnlyUse = true}) { + final byte = d.readByte(); + switch (byte) { + case 0x0B: + return End.deserialize(d); + case 0x23: + return GlobalGet.deserialize(d, globals); + case 0x41: + return I32Const.deserialize(d); + case 0x42: + return I64Const.deserialize(d); + case 0x43: + return F32Const.deserialize(d); + case 0x44: + return F64Const.deserialize(d); + case 0xD0: + return RefNull.deserialize(d, types); + case 0xD2: + return RefFunc.deserialize(d, functions); + case 0xFB: + { + final byte2 = d.readByte(); + switch (byte2) { + case 0x00: + return StructNew.deserialize(d, types); + case 0x01: + return StructNewDefault.deserialize(d, types); + case 0x06: + return ArrayNew.deserialize(d, types); + case 0x07: + return ArrayNewDefault.deserialize(d, types); + case 0x08: + return ArrayNewFixed.deserialize(d, types); + case 0x1A: + return ExternInternalize.deserialize(d); + case 0x1B: + return ExternExternalize.deserialize(d); + default: + throw "Invalid ${isConstOnlyUse ? 'const ' : ''}instruction byte: $byte $byte2"; + } + } + default: + throw "Invalid ${isConstOnlyUse ? 'const ' : ''}instruction byte: $byte"; + } + } + + static Instruction deserialize( + Deserializer d, + Types types, + Tables tables, + Tags tags, + Globals globals, + DataSegments dataSegments, + Memories memories, + Functions functions, + ) { + final instructionStart = d.offset; + final byte = d.readByte(); + switch (byte) { + case 0x00: + return Unreachable.deserialize(d); + case 0x01: + return Nop.deserialize(d); + case 0x02: + { + if (d.peekByte() == 0x40) { + return BeginNoEffectBlock.deserialize(d); + } + + final oldOffset = d.offset; + final value = d.readSigned(); + d.offset = oldOffset; + if (value >= 0) { + return BeginFunctionBlock.deserialize(d, types); + } + + return BeginOneOutputBlock.deserialize(d, types); + } + case 0x03: + { + if (d.peekByte() == 0x40) { + return BeginNoEffectLoop.deserialize(d); + } + return BeginOneOutputLoop.deserialize(d, types); + } + case 0x04: + { + if (d.peekByte() == 0x40) { + return BeginNoEffectIf.deserialize(d); + } + return BeginOneOutputIf.deserialize(d, types); + } + case 0x05: + return Else.deserialize(d); + case 0x06: + { + if (d.peekByte() == 0x40) { + return BeginNoEffectTry.deserialize(d); + } + + final oldOffset = d.offset; + final value = d.readSigned(); + d.offset = oldOffset; + if (value >= 0) { + return BeginFunctionTry.deserialize(d, types); + } + + return BeginOneOutputTry.deserialize(d, types); + } + case 0x07: + return CatchLegacy.deserialize(d, tags); + case 0x08: + return Throw.deserialize(d, tags); + case 0x09: + return Rethrow.deserialize(d); + case 0x0A: + return ThrowRef.deserialize(d); + case 0x0C: + return Br.deserialize(d); + case 0x0D: + return BrIf.deserialize(d); + case 0x0E: + return BrTable.deserialize(d); + case 0x0F: + return Return.deserialize(d); + case 0x10: + return Call.deserialize(d, functions); + case 0x11: + return CallIndirect.deserialize(d, types, tables); + case 0x14: + return CallRef.deserialize(d, types); + case 0x19: + return CatchAllLegacy.deserialize(d); + case 0x1A: + return Drop.deserialize(d); + case 0x1B: + return Select.deserialize(d); + case 0x1C: + return SelectWithType.deserialize(d, types); + case 0x1F: + { + if (d.peekByte() == 0x40) { + return BeginNoEffectTryTable.deserialize(d, tags); + } + return BeginOneOutputTryTable.deserialize(d, types, tags); + } + case 0x20: + return LocalGet.deserialize(d); + case 0x21: + return LocalSet.deserialize(d); + case 0x22: + return LocalTee.deserialize(d); + case 0x24: + return GlobalSet.deserialize(d, globals); + case 0x25: + return TableGet.deserialize(d, tables); + case 0x26: + return TableSet.deserialize(d, tables); + case 0x28: + return I32Load.deserialize(d, memories); + case 0x29: + return I64Load.deserialize(d, memories); + case 0x2A: + return F32Load.deserialize(d, memories); + case 0x2B: + return F64Load.deserialize(d, memories); + case 0x2C: + return I32Load8S.deserialize(d, memories); + case 0x2D: + return I32Load8U.deserialize(d, memories); + case 0x2E: + return I32Load16S.deserialize(d, memories); + case 0x2F: + return I32Load16U.deserialize(d, memories); + case 0x30: + return I64Load8S.deserialize(d, memories); + case 0x31: + return I64Load8U.deserialize(d, memories); + case 0x32: + return I64Load16S.deserialize(d, memories); + case 0x33: + return I64Load16U.deserialize(d, memories); + case 0x34: + return I64Load32S.deserialize(d, memories); + case 0x35: + return I64Load32U.deserialize(d, memories); + case 0x36: + return I32Store.deserialize(d, memories); + case 0x37: + return I64Store.deserialize(d, memories); + case 0x38: + return F32Store.deserialize(d, memories); + case 0x39: + return F64Store.deserialize(d, memories); + case 0x3A: + return I32Store8.deserialize(d, memories); + case 0x3B: + return I32Store16.deserialize(d, memories); + case 0x3C: + return I64Store8.deserialize(d, memories); + case 0x3D: + return I64Store16.deserialize(d, memories); + case 0x3E: + return I64Store32.deserialize(d, memories); + case 0x3F: + return MemorySize.deserialize(d, memories); + case 0x40: + return MemoryGrow.deserialize(d, memories); + case 0x45: + return I32Eqz.deserialize(d); + case 0x46: + return I32Eq.deserialize(d); + case 0x47: + return I32Ne.deserialize(d); + case 0x48: + return I32LtS.deserialize(d); + case 0x49: + return I32LtU.deserialize(d); + case 0x4A: + return I32GtS.deserialize(d); + case 0x4B: + return I32GtU.deserialize(d); + case 0x4C: + return I32LeS.deserialize(d); + case 0x4D: + return I32LeU.deserialize(d); + case 0x4E: + return I32GeS.deserialize(d); + case 0x4F: + return I32GeU.deserialize(d); + case 0x50: + return I64Eqz.deserialize(d); + case 0x51: + return I64Eq.deserialize(d); + case 0x52: + return I64Ne.deserialize(d); + case 0x53: + return I64LtS.deserialize(d); + case 0x54: + return I64LtU.deserialize(d); + case 0x55: + return I64GtS.deserialize(d); + case 0x56: + return I64GtU.deserialize(d); + case 0x57: + return I64LeS.deserialize(d); + case 0x58: + return I64LeU.deserialize(d); + case 0x59: + return I64GeS.deserialize(d); + case 0x5A: + return I64GeU.deserialize(d); + case 0x5B: + return F32Eq.deserialize(d); + case 0x5C: + return F32Ne.deserialize(d); + case 0x5D: + return F32Lt.deserialize(d); + case 0x5E: + return F32Gt.deserialize(d); + case 0x5F: + return F32Le.deserialize(d); + case 0x60: + return F32Ge.deserialize(d); + case 0x61: + return F64Eq.deserialize(d); + case 0x62: + return F64Ne.deserialize(d); + case 0x63: + return F64Lt.deserialize(d); + case 0x64: + return F64Gt.deserialize(d); + case 0x65: + return F64Le.deserialize(d); + case 0x66: + return F64Ge.deserialize(d); + case 0x67: + return I32Clz.deserialize(d); + case 0x68: + return I32Ctz.deserialize(d); + case 0x69: + return I32Popcnt.deserialize(d); + case 0x6A: + return I32Add.deserialize(d); + case 0x6B: + return I32Sub.deserialize(d); + case 0x6C: + return I32Mul.deserialize(d); + case 0x6D: + return I32DivS.deserialize(d); + case 0x6E: + return I32DivU.deserialize(d); + case 0x6F: + return I32RemS.deserialize(d); + case 0x70: + return I32RemU.deserialize(d); + case 0x71: + return I32And.deserialize(d); + case 0x72: + return I32Or.deserialize(d); + case 0x73: + return I32Xor.deserialize(d); + case 0x74: + return I32Shl.deserialize(d); + case 0x75: + return I32ShrS.deserialize(d); + case 0x76: + return I32ShrU.deserialize(d); + case 0x77: + return I32Rotl.deserialize(d); + case 0x78: + return I32Rotr.deserialize(d); + case 0x79: + return I64Clz.deserialize(d); + case 0x7A: + return I64Ctz.deserialize(d); + case 0x7B: + return I64Popcnt.deserialize(d); + case 0x7C: + return I64Add.deserialize(d); + case 0x7D: + return I64Sub.deserialize(d); + case 0x7E: + return I64Mul.deserialize(d); + case 0x7F: + return I64DivS.deserialize(d); + case 0x80: + return I64DivU.deserialize(d); + case 0x81: + return I64RemS.deserialize(d); + case 0x82: + return I64RemU.deserialize(d); + case 0x83: + return I64And.deserialize(d); + case 0x84: + return I64Or.deserialize(d); + case 0x85: + return I64Xor.deserialize(d); + case 0x86: + return I64Shl.deserialize(d); + case 0x87: + return I64ShrS.deserialize(d); + case 0x88: + return I64ShrU.deserialize(d); + case 0x89: + return I64Rotl.deserialize(d); + case 0x8A: + return I64Rotr.deserialize(d); + case 0x8B: + return F32Abs.deserialize(d); + case 0x8C: + return F32Neg.deserialize(d); + case 0x8D: + return F32Ceil.deserialize(d); + case 0x8E: + return F32Floor.deserialize(d); + case 0x8F: + return F32Trunc.deserialize(d); + case 0x90: + return F32Nearest.deserialize(d); + case 0x91: + return F32Sqrt.deserialize(d); + case 0x92: + return F32Add.deserialize(d); + case 0x93: + return F32Sub.deserialize(d); + case 0x94: + return F32Mul.deserialize(d); + case 0x95: + return F32Div.deserialize(d); + case 0x96: + return F32Min.deserialize(d); + case 0x97: + return F32Max.deserialize(d); + case 0x98: + return F32Copysign.deserialize(d); + case 0x99: + return F64Abs.deserialize(d); + case 0x9A: + return F64Neg.deserialize(d); + case 0x9B: + return F64Ceil.deserialize(d); + case 0x9C: + return F64Floor.deserialize(d); + case 0x9D: + return F64Trunc.deserialize(d); + case 0x9E: + return F64Nearest.deserialize(d); + case 0x9F: + return F64Sqrt.deserialize(d); + case 0xA0: + return F64Add.deserialize(d); + case 0xA1: + return F64Sub.deserialize(d); + case 0xA2: + return F64Mul.deserialize(d); + case 0xA3: + return F64Div.deserialize(d); + case 0xA4: + return F64Min.deserialize(d); + case 0xA5: + return F64Max.deserialize(d); + case 0xA6: + return F64Copysign.deserialize(d); + case 0xA7: + return I32WrapI64.deserialize(d); + case 0xA8: + return I32TruncF32S.deserialize(d); + case 0xA9: + return I32TruncF32U.deserialize(d); + case 0xAA: + return I32TruncF64S.deserialize(d); + case 0xAB: + return I32TruncF64U.deserialize(d); + case 0xAC: + return I64ExtendI32S.deserialize(d); + case 0xAD: + return I64ExtendI32U.deserialize(d); + case 0xAE: + return I64TruncF32S.deserialize(d); + case 0xAF: + return I64TruncF32U.deserialize(d); + case 0xB0: + return I64TruncF64S.deserialize(d); + case 0xB1: + return I64TruncF64U.deserialize(d); + case 0xB2: + return F32ConvertI32S.deserialize(d); + case 0xB3: + return F32ConvertI32U.deserialize(d); + case 0xB4: + return F32ConvertI64S.deserialize(d); + case 0xB5: + return F32ConvertI64U.deserialize(d); + case 0xB6: + return F32DemoteF64.deserialize(d); + case 0xB7: + return F64ConvertI32S.deserialize(d); + case 0xB8: + return F64ConvertI32U.deserialize(d); + case 0xB9: + return F64ConvertI64S.deserialize(d); + case 0xBA: + return F64ConvertI64U.deserialize(d); + case 0xBB: + return F64PromoteF32.deserialize(d); + case 0xBC: + return I32ReinterpretF32.deserialize(d); + case 0xBD: + return I64ReinterpretF64.deserialize(d); + case 0xBE: + return F32ReinterpretI32.deserialize(d); + case 0xBF: + return F64ReinterpretI64.deserialize(d); + case 0xC0: + return I32Extend8S.deserialize(d); + case 0xC1: + return I32Extend16S.deserialize(d); + case 0xC2: + return I64Extend8S.deserialize(d); + case 0xC3: + return I64Extend16S.deserialize(d); + case 0xC4: + return I64Extend32S.deserialize(d); + case 0xD1: + return RefIsNull.deserialize(d); + case 0xD3: + return RefEq.deserialize(d); + case 0xD4: + return RefAsNonNull.deserialize(d); + case 0xD5: + return BrOnNull.deserialize(d); + case 0xD6: + return BrOnNonNull.deserialize(d); + case 0xFB: + { + final opcode = d.readByte(); + switch (opcode) { + case 0x02: + return StructGet.deserialize(d, types); + case 0x03: + return StructGetS.deserialize(d, types); + case 0x04: + return StructGetU.deserialize(d, types); + case 0x05: + return StructSet.deserialize(d, types); + case 0x09: + return ArrayNewData.deserialize(d, types, dataSegments); + case 0x0b: + return ArrayGet.deserialize(d, types); + case 0x0c: + return ArrayGetS.deserialize(d, types); + case 0x0d: + return ArrayGetU.deserialize(d, types); + case 0x0E: + return ArraySet.deserialize(d, types); + case 0x0F: + return ArrayLen.deserialize(d); + case 0x10: + return ArrayFill.deserialize(d, types); + case 0x11: + return ArrayCopy.deserialize(d, types); + case 0x14: + return RefTest.deserialize(d, types, false); + case 0x15: + return RefTest.deserialize(d, types, true); + case 0x16: + return RefCast.deserialize(d, types, false); + case 0x17: + return RefCast.deserialize(d, types, true); + case 0x18: + return BrOnCast.deserialize(d, types); + case 0x19: + return BrOnCastFail.deserialize(d, types); + case 0x1C: + return I31New.deserialize(d); + case 0x1D: + return I31GetS.deserialize(d); + case 0x1E: + return I31GetU.deserialize(d); + default: + d.offset = instructionStart; + return deserializeConst(d, types, functions, globals, + isConstOnlyUse: false); + } + } + case 0xFC: + { + final opcode = d.readByte(); + switch (opcode) { + case 0x00: + return I32TruncSatF32S.deserialize(d); + case 0x01: + return I32TruncSatF32U.deserialize(d); + case 0x02: + return I32TruncSatF64S.deserialize(d); + case 0x03: + return I32TruncSatF64U.deserialize(d); + case 0x04: + return I64TruncSatF32S.deserialize(d); + case 0x05: + return I64TruncSatF32U.deserialize(d); + case 0x06: + return I64TruncSatF64S.deserialize(d); + case 0x07: + return I64TruncSatF64U.deserialize(d); + case 0x10: + return TableSize.deserialize(d, tables); + default: + throw "Invalid instruction byte: 0xFC $opcode"; + } + } + default: + d.offset = instructionStart; + return deserializeConst(d, types, functions, globals, + isConstOnlyUse: false); + } + } } abstract class SingleByteInstruction extends Instruction { @@ -37,13 +598,24 @@ abstract class SingleByteInstruction extends Instruction { class Unreachable extends SingleByteInstruction { const Unreachable() : super(0x00); + + static Unreachable deserialize(Deserializer d) => const Unreachable(); } class Nop extends SingleByteInstruction { const Nop() : super(0x01); + + static Nop deserialize(Deserializer d) => const Nop(); } class BeginNoEffectBlock extends Instruction { + const BeginNoEffectBlock(); + + static BeginNoEffectBlock deserialize(Deserializer d) { + d.readByte(); + return const BeginNoEffectBlock(); + } + @override void serialize(Serializer s) { s.writeByte(0x02); @@ -59,6 +631,12 @@ class BeginOneOutputBlock extends Instruction { BeginOneOutputBlock(this.type); + static BeginOneOutputBlock deserialize(Deserializer d, Types types) { + final type = ValueType.deserialize(d, types.defined); + assert(type is! FunctionType); + return BeginOneOutputBlock(type); + } + @override void serialize(Serializer s) { s.writeByte(0x02); @@ -74,6 +652,10 @@ class BeginFunctionBlock extends Instruction { BeginFunctionBlock(this.type); + static BeginFunctionBlock deserialize(Deserializer d, Types types) { + return BeginFunctionBlock(types.defined[d.readSigned()] as FunctionType); + } + @override void serialize(Serializer s) { s.writeByte(0x02); @@ -82,11 +664,18 @@ class BeginFunctionBlock extends Instruction { } class BeginNoEffectLoop extends Instruction { + const BeginNoEffectLoop(); + @override void serialize(Serializer s) { s.writeByte(0x03); s.writeByte(0x40); } + + static BeginNoEffectLoop deserialize(Deserializer d) { + d.readByte(); + return const BeginNoEffectLoop(); + } } class BeginOneOutputLoop extends Instruction { @@ -97,6 +686,11 @@ class BeginOneOutputLoop extends Instruction { BeginOneOutputLoop(this.type); + static BeginOneOutputLoop deserialize(Deserializer d, Types types) { + final type = ValueType.deserialize(d, types.defined); + return BeginOneOutputLoop(type); + } + @override void serialize(Serializer s) { s.writeByte(0x03); @@ -120,11 +714,18 @@ class BeginFunctionLoop extends Instruction { } class BeginNoEffectIf extends Instruction { + const BeginNoEffectIf(); + @override void serialize(Serializer s) { s.writeByte(0x04); s.writeByte(0x40); } + + static BeginNoEffectIf deserialize(Deserializer d) { + d.readByte(); + return const BeginNoEffectIf(); + } } class BeginOneOutputIf extends Instruction { @@ -135,6 +736,11 @@ class BeginOneOutputIf extends Instruction { BeginOneOutputIf(this.type); + static BeginOneOutputIf deserialize(Deserializer d, Types types) { + final type = ValueType.deserialize(d, types.defined); + return BeginOneOutputIf(type); + } + @override void serialize(Serializer s) { s.writeByte(0x04); @@ -159,9 +765,18 @@ class BeginFunctionIf extends Instruction { class Else extends SingleByteInstruction { const Else() : super(0x05); + + static Else deserialize(Deserializer d) => const Else(); } class BeginNoEffectTry extends Instruction { + const BeginNoEffectTry(); + + static BeginNoEffectTry deserialize(Deserializer d) { + d.readByte(); + return const BeginNoEffectTry(); + } + @override void serialize(Serializer s) { s.writeByte(0x06); @@ -177,6 +792,12 @@ class BeginOneOutputTry extends Instruction { BeginOneOutputTry(this.type); + static BeginOneOutputTry deserialize(Deserializer d, Types types) { + final type = ValueType.deserialize(d, types.defined); + assert(type is! FunctionType); + return BeginOneOutputTry(type); + } + @override void serialize(Serializer s) { s.writeByte(0x06); @@ -192,6 +813,10 @@ class BeginFunctionTry extends Instruction { BeginFunctionTry(this.type); + static BeginFunctionTry deserialize(Deserializer d, Types types) { + return BeginFunctionTry(types.defined[d.readSigned()] as FunctionType); + } + @override void serialize(Serializer s) { s.writeByte(0x06); @@ -204,6 +829,10 @@ class CatchLegacy extends Instruction { CatchLegacy(this.tag); + static CatchLegacy deserialize(Deserializer d, Tags tags) { + return CatchLegacy(tags[d.readUnsigned()]); + } + @override void serialize(Serializer s) { s.writeByte(0x07); @@ -213,6 +842,8 @@ class CatchLegacy extends Instruction { class CatchAllLegacy extends SingleByteInstruction { const CatchAllLegacy() : super(0x19); + + static CatchAllLegacy deserialize(Deserializer d) => const CatchAllLegacy(); } class Throw extends Instruction { @@ -220,6 +851,10 @@ class Throw extends Instruction { Throw(this.tag); + static Throw deserialize(Deserializer d, Tags tags) { + return Throw(tags[d.readUnsigned()]); + } + @override void serialize(Serializer s) { s.writeByte(0x08); @@ -230,6 +865,8 @@ class Throw extends Instruction { class ThrowRef extends Instruction { const ThrowRef(); + static ThrowRef deserialize(Deserializer d) => const ThrowRef(); + @override void serialize(Serializer s) { s.writeByte(0x0a); @@ -241,6 +878,8 @@ class Rethrow extends Instruction { Rethrow(this.labelIndex); + static Rethrow deserialize(Deserializer d) => Rethrow(d.readUnsigned()); + @override void serialize(Serializer s) { s.writeByte(0x09); @@ -253,6 +892,10 @@ class End extends SingleByteInstruction { @override bool get isConstant => true; + + static End deserialize(Deserializer d) { + return const End(); + } } class Br extends Instruction { @@ -260,6 +903,8 @@ class Br extends Instruction { Br(this.labelIndex); + static Br deserialize(Deserializer d) => Br(d.readUnsigned()); + @override void serialize(Serializer s) { s.writeByte(0x0C); @@ -272,6 +917,8 @@ class BrIf extends Instruction { BrIf(this.labelIndex); + static BrIf deserialize(Deserializer d) => BrIf(d.readUnsigned()); + @override void serialize(Serializer s) { s.writeByte(0x0D); @@ -285,6 +932,10 @@ class BrTable extends Instruction { BrTable(this.labelIndices, this.defaultLabelIndex); + static BrTable deserialize(Deserializer d) { + return BrTable(d.readList((d) => d.readUnsigned()), d.readUnsigned()); + } + @override void serialize(Serializer s) { s.writeByte(0x0E); @@ -298,6 +949,8 @@ class BrTable extends Instruction { class Return extends SingleByteInstruction { const Return() : super(0x0F); + + static Return deserialize(Deserializer d) => const Return(); } class Call extends Instruction { @@ -305,6 +958,10 @@ class Call extends Instruction { Call(this.function); + static Call deserialize(Deserializer d, Functions functions) { + return Call(functions[d.readUnsigned()]); + } + @override void serialize(Serializer s) { s.writeByte(0x10); @@ -321,6 +978,12 @@ class CallIndirect extends Instruction { CallIndirect(this.type, this.table); + static CallIndirect deserialize(Deserializer d, Types types, Tables tables) { + final type = types.defined[d.readTypeIndex()] as FunctionType; + final table = tables[d.readUnsigned()]; + return CallIndirect(type, table); + } + @override void serialize(Serializer s) { s.writeByte(0x11); @@ -337,6 +1000,10 @@ class CallRef extends Instruction { CallRef(this.type); + static CallRef deserialize(Deserializer d, Types types) { + return CallRef(types.defined[d.readTypeIndex()] as FunctionType); + } + @override void serialize(Serializer s) { s.writeByte(0x14); @@ -346,25 +1013,44 @@ class CallRef extends Instruction { class Drop extends SingleByteInstruction { const Drop() : super(0x1A); + + static Drop deserialize(Deserializer d) => const Drop(); } class Select extends Instruction { + @override + List get usedValueTypes => []; + + const Select(); + + static Select deserialize(Deserializer d) { + return const Select(); + } + + @override + void serialize(Serializer s) { + s.writeByte(0x1B); + } +} + +class SelectWithType extends Instruction { final ValueType type; @override List get usedValueTypes => [type]; - Select(this.type); + SelectWithType(this.type); + + static SelectWithType deserialize(Deserializer d, Types types) { + d.readUnsigned(); // vec_len + return SelectWithType(ValueType.deserialize(d, types.defined)); + } @override void serialize(Serializer s) { - if (type is NumType) { - s.writeByte(0x1B); - } else { - s.writeByte(0x1C); - s.writeUnsigned(1); - s.write(type); - } + s.writeByte(0x1C); + s.writeUnsigned(1); + s.write(type); } } @@ -373,6 +1059,11 @@ class LocalGet extends Instruction { LocalGet(this.local); + static LocalGet deserialize(Deserializer d) { + // TODO(joshualitt): This is not correct. + return LocalGet(Local(d.readUnsigned(), NumType.i32)); + } + @override void serialize(Serializer s) { s.writeByte(0x20); @@ -385,6 +1076,10 @@ class LocalSet extends Instruction { LocalSet(this.local); + static LocalSet deserialize(Deserializer d) { + return LocalSet(Local(d.readUnsigned(), NumType.i32)); + } + @override void serialize(Serializer s) { s.writeByte(0x21); @@ -397,6 +1092,10 @@ class LocalTee extends Instruction { LocalTee(this.local); + static LocalTee deserialize(Deserializer d) { + return LocalTee(Local(d.readUnsigned(), NumType.i32)); + } + @override void serialize(Serializer s) { s.writeByte(0x22); @@ -409,6 +1108,10 @@ class GlobalGet extends Instruction { GlobalGet(this.global); + static GlobalGet deserialize(Deserializer d, Globals globals) { + return GlobalGet(globals[d.readUnsigned()]); + } + @override bool get isConstant => true; @@ -424,6 +1127,10 @@ class GlobalSet extends Instruction { GlobalSet(this.global); + static GlobalSet deserialize(Deserializer d, Globals globals) { + return GlobalSet(globals[d.readUnsigned()]); + } + @override void serialize(Serializer s) { s.writeByte(0x24); @@ -436,6 +1143,10 @@ class TableSet extends Instruction { TableSet(this.table); + static TableSet deserialize(Deserializer d, Tables tables) { + return TableSet(tables[d.readUnsigned()]); + } + @override void serialize(Serializer s) { s.writeByte(0x26); @@ -448,6 +1159,10 @@ class TableGet extends Instruction { TableGet(this.table); + static TableGet deserialize(Deserializer d, Tables tables) { + return TableGet(tables[d.readUnsigned()]); + } + @override void serialize(Serializer s) { s.writeByte(0x25); @@ -460,6 +1175,10 @@ class TableSize extends Instruction { TableSize(this.table); + static TableSize deserialize(Deserializer d, Tables tables) { + return TableSize(tables[d.readUnsigned()]); + } + @override void serialize(Serializer s) { s.writeByte(0xFC); @@ -486,6 +1205,15 @@ class MemoryOffsetAlign implements Serializable { s.writeUnsigned(memory.index); } } + + static MemoryOffsetAlign deserialize(Deserializer d, Memories memories) { + final alignAndMemory = d.readByte(); + final align = alignAndMemory & 0x3F; + final offset = d.readUnsigned(); + final memoryIndex = (alignAndMemory & 0x40) != 0 ? d.readUnsigned() : 0; + return MemoryOffsetAlign(memories[memoryIndex], + offset: offset, align: align); + } } abstract class MemoryInstruction extends Instruction { @@ -503,94 +1231,186 @@ abstract class MemoryInstruction extends Instruction { class I32Load extends MemoryInstruction { I32Load(super.memory) : super(encoding: 0x28); + + static I32Load deserialize(Deserializer d, Memories memories) { + return I32Load(MemoryOffsetAlign.deserialize(d, memories)); + } } class I64Load extends MemoryInstruction { I64Load(super.memory) : super(encoding: 0x29); + + static I64Load deserialize(Deserializer d, Memories memories) { + return I64Load(MemoryOffsetAlign.deserialize(d, memories)); + } } class F32Load extends MemoryInstruction { F32Load(super.memory) : super(encoding: 0x2A); + + static F32Load deserialize(Deserializer d, Memories memories) { + return F32Load(MemoryOffsetAlign.deserialize(d, memories)); + } } class F64Load extends MemoryInstruction { F64Load(super.memory) : super(encoding: 0x2B); + + static F64Load deserialize(Deserializer d, Memories memories) { + return F64Load(MemoryOffsetAlign.deserialize(d, memories)); + } } class I32Load8S extends MemoryInstruction { I32Load8S(super.memory) : super(encoding: 0x2C); + + static I32Load8S deserialize(Deserializer d, Memories memories) { + return I32Load8S(MemoryOffsetAlign.deserialize(d, memories)); + } } class I32Load8U extends MemoryInstruction { I32Load8U(super.memory) : super(encoding: 0x2D); + + static I32Load8U deserialize(Deserializer d, Memories memories) { + return I32Load8U(MemoryOffsetAlign.deserialize(d, memories)); + } } class I32Load16S extends MemoryInstruction { I32Load16S(super.memory) : super(encoding: 0x2E); + + static I32Load16S deserialize(Deserializer d, Memories memories) { + return I32Load16S(MemoryOffsetAlign.deserialize(d, memories)); + } } class I32Load16U extends MemoryInstruction { I32Load16U(super.memory) : super(encoding: 0x2F); + + static I32Load16U deserialize(Deserializer d, Memories memories) { + return I32Load16U(MemoryOffsetAlign.deserialize(d, memories)); + } } class I64Load8S extends MemoryInstruction { I64Load8S(super.memory) : super(encoding: 0x30); + + static I64Load8S deserialize(Deserializer d, Memories memories) { + return I64Load8S(MemoryOffsetAlign.deserialize(d, memories)); + } } class I64Load8U extends MemoryInstruction { I64Load8U(super.memory) : super(encoding: 0x31); + + static I64Load8U deserialize(Deserializer d, Memories memories) { + return I64Load8U(MemoryOffsetAlign.deserialize(d, memories)); + } } class I64Load16S extends MemoryInstruction { I64Load16S(super.memory) : super(encoding: 0x32); + + static I64Load16S deserialize(Deserializer d, Memories memories) { + return I64Load16S(MemoryOffsetAlign.deserialize(d, memories)); + } } class I64Load16U extends MemoryInstruction { I64Load16U(super.memory) : super(encoding: 0x33); + + static I64Load16U deserialize(Deserializer d, Memories memories) { + return I64Load16U(MemoryOffsetAlign.deserialize(d, memories)); + } } class I64Load32S extends MemoryInstruction { I64Load32S(super.memory) : super(encoding: 0x34); + + static I64Load32S deserialize(Deserializer d, Memories memories) { + return I64Load32S(MemoryOffsetAlign.deserialize(d, memories)); + } } class I64Load32U extends MemoryInstruction { I64Load32U(super.memory) : super(encoding: 0x35); + + static I64Load32U deserialize(Deserializer d, Memories memories) { + return I64Load32U(MemoryOffsetAlign.deserialize(d, memories)); + } } class I32Store extends MemoryInstruction { I32Store(super.memory) : super(encoding: 0x36); + + static I32Store deserialize(Deserializer d, Memories memories) { + return I32Store(MemoryOffsetAlign.deserialize(d, memories)); + } } class I64Store extends MemoryInstruction { I64Store(super.memory) : super(encoding: 0x37); + + static I64Store deserialize(Deserializer d, Memories memories) { + return I64Store(MemoryOffsetAlign.deserialize(d, memories)); + } } class F32Store extends MemoryInstruction { F32Store(super.memory) : super(encoding: 0x38); + + static F32Store deserialize(Deserializer d, Memories memories) { + return F32Store(MemoryOffsetAlign.deserialize(d, memories)); + } } class F64Store extends MemoryInstruction { F64Store(super.memory) : super(encoding: 0x39); + + static F64Store deserialize(Deserializer d, Memories memories) { + return F64Store(MemoryOffsetAlign.deserialize(d, memories)); + } } class I32Store8 extends MemoryInstruction { I32Store8(super.memory) : super(encoding: 0x3A); + + static I32Store8 deserialize(Deserializer d, Memories memories) { + return I32Store8(MemoryOffsetAlign.deserialize(d, memories)); + } } class I32Store16 extends MemoryInstruction { I32Store16(super.memory) : super(encoding: 0x3B); + + static I32Store16 deserialize(Deserializer d, Memories memories) { + return I32Store16(MemoryOffsetAlign.deserialize(d, memories)); + } } class I64Store8 extends MemoryInstruction { I64Store8(super.memory) : super(encoding: 0x3C); + + static I64Store8 deserialize(Deserializer d, Memories memories) { + return I64Store8(MemoryOffsetAlign.deserialize(d, memories)); + } } class I64Store16 extends MemoryInstruction { I64Store16(super.memory) : super(encoding: 0x3D); + + static I64Store16 deserialize(Deserializer d, Memories memories) { + return I64Store16(MemoryOffsetAlign.deserialize(d, memories)); + } } class I64Store32 extends MemoryInstruction { I64Store32(super.memory) : super(encoding: 0x3E); + + static I64Store32 deserialize(Deserializer d, Memories memories) { + return I64Store32(MemoryOffsetAlign.deserialize(d, memories)); + } } class MemorySize extends Instruction { @@ -598,6 +1418,10 @@ class MemorySize extends Instruction { MemorySize(this.memory); + static MemorySize deserialize(Deserializer d, Memories memories) { + return MemorySize(memories[d.readUnsigned()]); + } + @override void serialize(Serializer s) { s.writeByte(0x3F); @@ -610,6 +1434,10 @@ class MemoryGrow extends Instruction { MemoryGrow(this.memory); + static MemoryGrow deserialize(Deserializer d, Memories memories) { + return MemoryGrow(memories[d.readUnsigned()]); + } + @override void serialize(Serializer s) { s.writeByte(0x40); @@ -628,6 +1456,10 @@ class RefNull extends Instruction { RefNull(this.heapType); + static RefNull deserialize(Deserializer d, Types types) { + return RefNull(HeapType.deserialize(d, types.defined)); + } + @override bool get isConstant => true; @@ -640,6 +1472,8 @@ class RefNull extends Instruction { class RefIsNull extends SingleByteInstruction { const RefIsNull() : super(0xD1); + + static RefIsNull deserialize(Deserializer d) => const RefIsNull(); } class RefFunc extends Instruction { @@ -647,6 +1481,12 @@ class RefFunc extends Instruction { RefFunc(this.function); + static RefFunc deserialize(Deserializer d, Functions functions) { + final index = d.readUnsigned(); + final function = functions[index]; + return RefFunc(function); + } + @override bool get isConstant => true; @@ -659,6 +1499,8 @@ class RefFunc extends Instruction { class RefAsNonNull extends SingleByteInstruction { const RefAsNonNull() : super(0xD4); + + static RefAsNonNull deserialize(Deserializer d) => const RefAsNonNull(); } class BrOnNull extends Instruction { @@ -666,6 +1508,8 @@ class BrOnNull extends Instruction { BrOnNull(this.labelIndex); + static BrOnNull deserialize(Deserializer d) => BrOnNull(d.readUnsigned()); + @override void serialize(Serializer s) { s.writeByte(0xD5); @@ -675,6 +1519,8 @@ class BrOnNull extends Instruction { class RefEq extends SingleByteInstruction { const RefEq() : super(0xD3); + + static RefEq deserialize(Deserializer d) => const RefEq(); } class BrOnNonNull extends Instruction { @@ -682,6 +1528,9 @@ class BrOnNonNull extends Instruction { BrOnNonNull(this.labelIndex); + static BrOnNonNull deserialize(Deserializer d) => + BrOnNonNull(d.readUnsigned()); + @override void serialize(Serializer s) { s.writeByte(0xD6); @@ -698,6 +1547,11 @@ class StructGet extends Instruction { StructGet(this.structType, this.fieldIndex); + static StructGet deserialize(Deserializer d, Types types) { + return StructGet( + types.defined[d.readTypeIndex()] as StructType, d.readUnsigned()); + } + @override void serialize(Serializer s) { s.writeByte(0xFB); @@ -716,6 +1570,11 @@ class StructGetS extends Instruction { StructGetS(this.structType, this.fieldIndex); + static StructGetS deserialize(Deserializer d, Types types) { + return StructGetS( + types.defined[d.readTypeIndex()] as StructType, d.readUnsigned()); + } + @override void serialize(Serializer s) { s.writeByte(0xFB); @@ -734,6 +1593,11 @@ class StructGetU extends Instruction { StructGetU(this.structType, this.fieldIndex); + static StructGetU deserialize(Deserializer d, Types types) { + return StructGetU( + types.defined[d.readTypeIndex()] as StructType, d.readUnsigned()); + } + @override void serialize(Serializer s) { s.writeByte(0xFB); @@ -752,6 +1616,11 @@ class StructSet extends Instruction { StructSet(this.structType, this.fieldIndex); + static StructSet deserialize(Deserializer d, Types types) { + return StructSet( + types.defined[d.readTypeIndex()] as StructType, d.readUnsigned()); + } + @override void serialize(Serializer s) { s.writeByte(0xFB); @@ -769,6 +1638,10 @@ class StructNew extends Instruction { StructNew(this.structType); + static StructNew deserialize(Deserializer d, Types types) { + return StructNew(types.defined[d.readTypeIndex()] as StructType); + } + @override bool get isConstant => true; @@ -788,6 +1661,10 @@ class StructNewDefault extends Instruction { StructNewDefault(this.structType); + static StructNewDefault deserialize(Deserializer d, Types types) { + return StructNewDefault(types.defined[d.readTypeIndex()] as StructType); + } + @override bool get isConstant => true; @@ -807,6 +1684,10 @@ class ArrayGet extends Instruction { ArrayGet(this.arrayType); + static ArrayGet deserialize(Deserializer d, Types types) { + return ArrayGet(types.defined[d.readTypeIndex()] as ArrayType); + } + @override void serialize(Serializer s) { s.writeByte(0xFB); @@ -823,6 +1704,10 @@ class ArrayGetS extends Instruction { ArrayGetS(this.arrayType); + static ArrayGetS deserialize(Deserializer d, Types types) { + return ArrayGetS(types.defined[d.readTypeIndex()] as ArrayType); + } + @override void serialize(Serializer s) { s.writeByte(0xFB); @@ -839,6 +1724,10 @@ class ArrayGetU extends Instruction { ArrayGetU(this.arrayType); + static ArrayGetU deserialize(Deserializer d, Types types) { + return ArrayGetU(types.defined[d.readTypeIndex()] as ArrayType); + } + @override void serialize(Serializer s) { s.writeByte(0xFB); @@ -855,6 +1744,10 @@ class ArraySet extends Instruction { ArraySet(this.arrayType); + static ArraySet deserialize(Deserializer d, Types types) { + return ArraySet(types.defined[d.readTypeIndex()] as ArrayType); + } + @override void serialize(Serializer s) { s.writeByte(0xFB); @@ -866,6 +1759,8 @@ class ArraySet extends Instruction { class ArrayLen extends Instruction { const ArrayLen(); + static ArrayLen deserialize(Deserializer d) => const ArrayLen(); + @override void serialize(Serializer s) { s.writeByte(0xFB); @@ -882,6 +1777,11 @@ class ArrayNewFixed extends Instruction { ArrayNewFixed(this.arrayType, this.length); + static ArrayNewFixed deserialize(Deserializer d, Types types) { + return ArrayNewFixed( + types.defined[d.readTypeIndex()] as ArrayType, d.readUnsigned()); + } + @override bool get isConstant => true; @@ -902,6 +1802,10 @@ class ArrayNew extends Instruction { ArrayNew(this.arrayType); + static ArrayNew deserialize(Deserializer d, Types types) { + return ArrayNew(types.defined[d.readTypeIndex()] as ArrayType); + } + @override bool get isConstant => true; @@ -921,6 +1825,10 @@ class ArrayNewDefault extends Instruction { ArrayNewDefault(this.arrayType); + static ArrayNewDefault deserialize(Deserializer d, Types types) { + return ArrayNewDefault(types.defined[d.readTypeIndex()] as ArrayType); + } + @override bool get isConstant => true; @@ -941,6 +1849,12 @@ class ArrayNewData extends Instruction { ArrayNewData(this.arrayType, this.data); + static ArrayNewData deserialize( + Deserializer d, Types types, DataSegments dataSegments) { + return ArrayNewData(types.defined[d.readTypeIndex()] as ArrayType, + dataSegments.defined[d.readUnsigned()]); + } + @override void serialize(Serializer s) { s.writeByte(0xFB); @@ -959,6 +1873,12 @@ class ArrayCopy extends Instruction { ArrayCopy({required this.destArrayType, required this.sourceArrayType}); + static ArrayCopy deserialize(Deserializer d, Types types) { + return ArrayCopy( + destArrayType: types.defined[d.readTypeIndex()] as ArrayType, + sourceArrayType: types.defined[d.readTypeIndex()] as ArrayType); + } + @override void serialize(Serializer s) { s.writeByte(0xFB); @@ -976,6 +1896,10 @@ class ArrayFill extends Instruction { ArrayFill(this.arrayType); + static ArrayFill deserialize(Deserializer d, Types types) { + return ArrayFill(types.defined[d.readTypeIndex()] as ArrayType); + } + @override void serialize(Serializer s) { s.writeByte(0xFB); @@ -987,6 +1911,8 @@ class ArrayFill extends Instruction { class I31New extends Instruction { const I31New(); + static I31New deserialize(Deserializer d) => const I31New(); + @override void serialize(Serializer s) { s.writeByte(0xFB); @@ -997,6 +1923,8 @@ class I31New extends Instruction { class I31GetS extends Instruction { const I31GetS(); + static I31GetS deserialize(Deserializer d) => const I31GetS(); + @override void serialize(Serializer s) { s.writeByte(0xFB); @@ -1007,6 +1935,8 @@ class I31GetS extends Instruction { class I31GetU extends Instruction { const I31GetU(); + static I31GetU deserialize(Deserializer d) => const I31GetU(); + @override void serialize(Serializer s) { s.writeByte(0xFB); @@ -1019,6 +1949,11 @@ class RefTest extends Instruction { RefTest(this.targetType); + static RefTest deserialize(Deserializer d, Types types, bool nullable) { + return RefTest( + RefType(HeapType.deserialize(d, types.defined), nullable: nullable)); + } + @override void serialize(Serializer s) { s.writeByte(0xFB); @@ -1035,6 +1970,11 @@ class RefCast extends Instruction { RefCast(this.targetType); + static RefCast deserialize(Deserializer d, Types types, bool nullable) { + return RefCast( + RefType(HeapType.deserialize(d, types.defined), nullable: nullable)); + } + @override void serialize(Serializer s) { s.writeByte(0xFB); @@ -1053,6 +1993,16 @@ class BrOnCast extends Instruction { BrOnCast(this.labelIndex, this.inputType, this.targetType); + static BrOnCast deserialize(Deserializer d, Types types) { + final flags = d.readByte(); + final labelIndex = d.readUnsigned(); + final inputHeapType = HeapType.deserialize(d, types.defined); + final targetHeapType = HeapType.deserialize(d, types.defined); + final inputType = RefType(inputHeapType, nullable: (flags & 0x01) != 0); + final targetType = RefType(targetHeapType, nullable: (flags & 0x01) != 0); + return BrOnCast(labelIndex, inputType, targetType); + } + @override void serialize(Serializer s) { int flags = (inputType.nullable ? 0x01 : 0x00) | @@ -1076,6 +2026,18 @@ class BrOnCastFail extends Instruction { BrOnCastFail(this.labelIndex, this.inputType, this.targetType); + static BrOnCastFail deserialize(Deserializer d, Types types) { + final flags = d.readByte(); + final labelIndex = d.readUnsigned(); + final inputHeapType = HeapType.deserialize(d, types.defined); + final targetHeapType = HeapType.deserialize(d, types.defined); + + final inputType = RefType(inputHeapType, nullable: (flags & 0x01) != 0); + final targetType = RefType(targetHeapType, nullable: (flags & 0x01) != 0); + + return BrOnCastFail(labelIndex, inputType, targetType); + } + @override void serialize(Serializer s) { int flags = (inputType.nullable ? 0x01 : 0x00) | @@ -1092,6 +2054,10 @@ class BrOnCastFail extends Instruction { class ExternInternalize extends Instruction { const ExternInternalize(); + static ExternInternalize deserialize(Deserializer d) { + return const ExternInternalize(); + } + @override void serialize(Serializer s) { s.writeByte(0xFB); @@ -1105,6 +2071,10 @@ class ExternInternalize extends Instruction { class ExternExternalize extends Instruction { const ExternExternalize(); + static ExternExternalize deserialize(Deserializer d) { + return const ExternExternalize(); + } + @override void serialize(Serializer s) { s.writeByte(0xFB); @@ -1120,6 +2090,10 @@ class I32Const extends Instruction { I32Const(this.value); + static I32Const deserialize(Deserializer d) { + return I32Const(d.readSigned()); + } + @override bool get isConstant => true; @@ -1135,6 +2109,10 @@ class I64Const extends Instruction { I64Const(this.value); + static I64Const deserialize(Deserializer d) { + return I64Const(d.readSigned()); + } + @override bool get isConstant => true; @@ -1150,6 +2128,10 @@ class F32Const extends Instruction { F32Const(this.value); + static F32Const deserialize(Deserializer d) { + return F32Const(d.readF32()); + } + @override bool get isConstant => true; @@ -1165,6 +2147,10 @@ class F64Const extends Instruction { F64Const(this.value); + static F64Const deserialize(Deserializer d) { + return F64Const(d.readF64()); + } + @override bool get isConstant => true; @@ -1177,514 +2163,774 @@ class F64Const extends Instruction { class I32Eqz extends SingleByteInstruction { const I32Eqz() : super(0x45); + + static I32Eqz deserialize(Deserializer d) => const I32Eqz(); } class I32Eq extends SingleByteInstruction { const I32Eq() : super(0x46); + + static I32Eq deserialize(Deserializer d) => const I32Eq(); } class I32Ne extends SingleByteInstruction { const I32Ne() : super(0x47); + + static I32Ne deserialize(Deserializer d) => const I32Ne(); } class I32LtS extends SingleByteInstruction { const I32LtS() : super(0x48); + + static I32LtS deserialize(Deserializer d) => const I32LtS(); } class I32LtU extends SingleByteInstruction { const I32LtU() : super(0x49); + + static I32LtU deserialize(Deserializer d) => const I32LtU(); } class I32GtS extends SingleByteInstruction { const I32GtS() : super(0x4A); + + static I32GtS deserialize(Deserializer d) => const I32GtS(); } class I32GtU extends SingleByteInstruction { const I32GtU() : super(0x4B); + + static I32GtU deserialize(Deserializer d) => const I32GtU(); } class I32LeS extends SingleByteInstruction { const I32LeS() : super(0x4C); + + static I32LeS deserialize(Deserializer d) => const I32LeS(); } class I32LeU extends SingleByteInstruction { const I32LeU() : super(0x4D); + + static I32LeU deserialize(Deserializer d) => const I32LeU(); } class I32GeS extends SingleByteInstruction { const I32GeS() : super(0x4E); + + static I32GeS deserialize(Deserializer d) => const I32GeS(); } class I32GeU extends SingleByteInstruction { const I32GeU() : super(0x4F); + + static I32GeU deserialize(Deserializer d) => const I32GeU(); } class I64Eqz extends SingleByteInstruction { const I64Eqz() : super(0x50); + + static I64Eqz deserialize(Deserializer d) => const I64Eqz(); } class I64Eq extends SingleByteInstruction { const I64Eq() : super(0x51); + + static I64Eq deserialize(Deserializer d) => const I64Eq(); } class I64Ne extends SingleByteInstruction { const I64Ne() : super(0x52); + + static I64Ne deserialize(Deserializer d) => const I64Ne(); } class I64LtS extends SingleByteInstruction { const I64LtS() : super(0x53); + + static I64LtS deserialize(Deserializer d) => const I64LtS(); } class I64LtU extends SingleByteInstruction { const I64LtU() : super(0x54); + + static I64LtU deserialize(Deserializer d) => const I64LtU(); } class I64GtS extends SingleByteInstruction { const I64GtS() : super(0x55); + + static I64GtS deserialize(Deserializer d) => const I64GtS(); } class I64GtU extends SingleByteInstruction { const I64GtU() : super(0x56); + + static I64GtU deserialize(Deserializer d) => const I64GtU(); } class I64LeS extends SingleByteInstruction { const I64LeS() : super(0x57); + + static I64LeS deserialize(Deserializer d) => const I64LeS(); } class I64LeU extends SingleByteInstruction { const I64LeU() : super(0x58); + + static I64LeU deserialize(Deserializer d) => const I64LeU(); } class I64GeS extends SingleByteInstruction { const I64GeS() : super(0x59); + + static I64GeS deserialize(Deserializer d) => const I64GeS(); } class I64GeU extends SingleByteInstruction { const I64GeU() : super(0x5A); + + static I64GeU deserialize(Deserializer d) => const I64GeU(); } class F32Eq extends SingleByteInstruction { const F32Eq() : super(0x5B); + + static F32Eq deserialize(Deserializer d) => const F32Eq(); } class F32Ne extends SingleByteInstruction { const F32Ne() : super(0x5C); + + static F32Ne deserialize(Deserializer d) => const F32Ne(); } class F32Lt extends SingleByteInstruction { const F32Lt() : super(0x5D); + + static F32Lt deserialize(Deserializer d) => const F32Lt(); } class F32Gt extends SingleByteInstruction { const F32Gt() : super(0x5E); + + static F32Gt deserialize(Deserializer d) => const F32Gt(); } class F32Le extends SingleByteInstruction { const F32Le() : super(0x5F); + + static F32Le deserialize(Deserializer d) => const F32Le(); } class F32Ge extends SingleByteInstruction { const F32Ge() : super(0x60); + + static F32Ge deserialize(Deserializer d) => const F32Ge(); } class F64Eq extends SingleByteInstruction { const F64Eq() : super(0x61); + + static F64Eq deserialize(Deserializer d) => const F64Eq(); } class F64Ne extends SingleByteInstruction { const F64Ne() : super(0x62); + + static F64Ne deserialize(Deserializer d) => const F64Ne(); } class F64Lt extends SingleByteInstruction { const F64Lt() : super(0x63); + + static F64Lt deserialize(Deserializer d) => const F64Lt(); } class F64Gt extends SingleByteInstruction { const F64Gt() : super(0x64); + + static F64Gt deserialize(Deserializer d) => const F64Gt(); } class F64Le extends SingleByteInstruction { const F64Le() : super(0x65); + + static F64Le deserialize(Deserializer d) => const F64Le(); } class F64Ge extends SingleByteInstruction { const F64Ge() : super(0x66); + + static F64Ge deserialize(Deserializer d) => const F64Ge(); } class I32Clz extends SingleByteInstruction { const I32Clz() : super(0x67); + + static I32Clz deserialize(Deserializer d) => const I32Clz(); } class I32Ctz extends SingleByteInstruction { const I32Ctz() : super(0x68); + + static I32Ctz deserialize(Deserializer d) => const I32Ctz(); } class I32Popcnt extends SingleByteInstruction { const I32Popcnt() : super(0x69); + + static I32Popcnt deserialize(Deserializer d) => const I32Popcnt(); } class I32Add extends SingleByteInstruction { const I32Add() : super(0x6A); + + static I32Add deserialize(Deserializer d) => const I32Add(); } class I32Sub extends SingleByteInstruction { const I32Sub() : super(0x6B); + + static I32Sub deserialize(Deserializer d) => const I32Sub(); } class I32Mul extends SingleByteInstruction { const I32Mul() : super(0x6C); + + static I32Mul deserialize(Deserializer d) => const I32Mul(); } class I32DivS extends SingleByteInstruction { const I32DivS() : super(0x6D); + + static I32DivS deserialize(Deserializer d) => const I32DivS(); } class I32DivU extends SingleByteInstruction { const I32DivU() : super(0x6E); + + static I32DivU deserialize(Deserializer d) => const I32DivU(); } class I32RemS extends SingleByteInstruction { const I32RemS() : super(0x6F); + + static I32RemS deserialize(Deserializer d) => const I32RemS(); } class I32RemU extends SingleByteInstruction { const I32RemU() : super(0x70); + + static I32RemU deserialize(Deserializer d) => const I32RemU(); } class I32And extends SingleByteInstruction { const I32And() : super(0x71); + + static I32And deserialize(Deserializer d) => const I32And(); } class I32Or extends SingleByteInstruction { const I32Or() : super(0x72); + + static I32Or deserialize(Deserializer d) => const I32Or(); } class I32Xor extends SingleByteInstruction { const I32Xor() : super(0x73); + + static I32Xor deserialize(Deserializer d) => const I32Xor(); } class I32Shl extends SingleByteInstruction { const I32Shl() : super(0x74); + + static I32Shl deserialize(Deserializer d) => const I32Shl(); } class I32ShrS extends SingleByteInstruction { const I32ShrS() : super(0x75); + + static I32ShrS deserialize(Deserializer d) => const I32ShrS(); } class I32ShrU extends SingleByteInstruction { const I32ShrU() : super(0x76); + + static I32ShrU deserialize(Deserializer d) => const I32ShrU(); } class I32Rotl extends SingleByteInstruction { const I32Rotl() : super(0x77); + + static I32Rotl deserialize(Deserializer d) => const I32Rotl(); } class I32Rotr extends SingleByteInstruction { const I32Rotr() : super(0x78); + + static I32Rotr deserialize(Deserializer d) => const I32Rotr(); } class I64Clz extends SingleByteInstruction { const I64Clz() : super(0x79); + + static I64Clz deserialize(Deserializer d) => const I64Clz(); } class I64Ctz extends SingleByteInstruction { const I64Ctz() : super(0x7A); + + static I64Ctz deserialize(Deserializer d) => const I64Ctz(); } class I64Popcnt extends SingleByteInstruction { const I64Popcnt() : super(0x7B); + + static I64Popcnt deserialize(Deserializer d) => const I64Popcnt(); } class I64Add extends SingleByteInstruction { const I64Add() : super(0x7C); + + static I64Add deserialize(Deserializer d) => const I64Add(); } class I64Sub extends SingleByteInstruction { const I64Sub() : super(0x7D); + + static I64Sub deserialize(Deserializer d) => const I64Sub(); } class I64Mul extends SingleByteInstruction { const I64Mul() : super(0x7E); + + static I64Mul deserialize(Deserializer d) => const I64Mul(); } class I64DivS extends SingleByteInstruction { const I64DivS() : super(0x7F); + + static I64DivS deserialize(Deserializer d) => const I64DivS(); } class I64DivU extends SingleByteInstruction { const I64DivU() : super(0x80); + + static I64DivU deserialize(Deserializer d) => const I64DivU(); } class I64RemS extends SingleByteInstruction { const I64RemS() : super(0x81); + + static I64RemS deserialize(Deserializer d) => const I64RemS(); } class I64RemU extends SingleByteInstruction { const I64RemU() : super(0x82); + + static I64RemU deserialize(Deserializer d) => const I64RemU(); } class I64And extends SingleByteInstruction { const I64And() : super(0x83); + + static I64And deserialize(Deserializer d) => const I64And(); } class I64Or extends SingleByteInstruction { const I64Or() : super(0x84); + + static I64Or deserialize(Deserializer d) => const I64Or(); } class I64Xor extends SingleByteInstruction { const I64Xor() : super(0x85); + + static I64Xor deserialize(Deserializer d) => const I64Xor(); } class I64Shl extends SingleByteInstruction { const I64Shl() : super(0x86); + + static I64Shl deserialize(Deserializer d) => const I64Shl(); } class I64ShrS extends SingleByteInstruction { const I64ShrS() : super(0x87); + + static I64ShrS deserialize(Deserializer d) => const I64ShrS(); } class I64ShrU extends SingleByteInstruction { const I64ShrU() : super(0x88); + + static I64ShrU deserialize(Deserializer d) => const I64ShrU(); } class I64Rotl extends SingleByteInstruction { const I64Rotl() : super(0x89); + + static I64Rotl deserialize(Deserializer d) => const I64Rotl(); } class I64Rotr extends SingleByteInstruction { const I64Rotr() : super(0x8A); + + static I64Rotr deserialize(Deserializer d) => const I64Rotr(); } class F32Abs extends SingleByteInstruction { const F32Abs() : super(0x8B); + + static F32Abs deserialize(Deserializer d) => const F32Abs(); } class F32Neg extends SingleByteInstruction { const F32Neg() : super(0x8C); + + static F32Neg deserialize(Deserializer d) => const F32Neg(); } class F32Ceil extends SingleByteInstruction { const F32Ceil() : super(0x8D); + + static F32Ceil deserialize(Deserializer d) => const F32Ceil(); } class F32Floor extends SingleByteInstruction { const F32Floor() : super(0x8E); + + static F32Floor deserialize(Deserializer d) => const F32Floor(); } class F32Trunc extends SingleByteInstruction { const F32Trunc() : super(0x8F); + + static F32Trunc deserialize(Deserializer d) => const F32Trunc(); } class F32Nearest extends SingleByteInstruction { const F32Nearest() : super(0x90); + + static F32Nearest deserialize(Deserializer d) => const F32Nearest(); } class F32Sqrt extends SingleByteInstruction { const F32Sqrt() : super(0x91); + + static F32Sqrt deserialize(Deserializer d) => const F32Sqrt(); } class F32Add extends SingleByteInstruction { const F32Add() : super(0x92); + + static F32Add deserialize(Deserializer d) => const F32Add(); } class F32Sub extends SingleByteInstruction { const F32Sub() : super(0x93); + + static F32Sub deserialize(Deserializer d) => const F32Sub(); } class F32Mul extends SingleByteInstruction { const F32Mul() : super(0x94); + + static F32Mul deserialize(Deserializer d) => const F32Mul(); } class F32Div extends SingleByteInstruction { const F32Div() : super(0x95); + + static F32Div deserialize(Deserializer d) => const F32Div(); } class F32Min extends SingleByteInstruction { const F32Min() : super(0x96); + + static F32Min deserialize(Deserializer d) => const F32Min(); } class F32Max extends SingleByteInstruction { const F32Max() : super(0x97); + + static F32Max deserialize(Deserializer d) => const F32Max(); } class F32Copysign extends SingleByteInstruction { const F32Copysign() : super(0x98); + + static F32Copysign deserialize(Deserializer d) => const F32Copysign(); } class F64Abs extends SingleByteInstruction { const F64Abs() : super(0x99); + + static F64Abs deserialize(Deserializer d) => const F64Abs(); } class F64Neg extends SingleByteInstruction { const F64Neg() : super(0x9A); + + static F64Neg deserialize(Deserializer d) => const F64Neg(); } class F64Ceil extends SingleByteInstruction { const F64Ceil() : super(0x9B); + + static F64Ceil deserialize(Deserializer d) => const F64Ceil(); } class F64Floor extends SingleByteInstruction { const F64Floor() : super(0x9C); + + static F64Floor deserialize(Deserializer d) => const F64Floor(); } class F64Trunc extends SingleByteInstruction { const F64Trunc() : super(0x9D); + + static F64Trunc deserialize(Deserializer d) => const F64Trunc(); } class F64Nearest extends SingleByteInstruction { const F64Nearest() : super(0x9E); + + static F64Nearest deserialize(Deserializer d) => const F64Nearest(); } class F64Sqrt extends SingleByteInstruction { const F64Sqrt() : super(0x9F); + + static F64Sqrt deserialize(Deserializer d) => const F64Sqrt(); } class F64Add extends SingleByteInstruction { const F64Add() : super(0xA0); + + static F64Add deserialize(Deserializer d) => const F64Add(); } class F64Sub extends SingleByteInstruction { const F64Sub() : super(0xA1); + + static F64Sub deserialize(Deserializer d) => const F64Sub(); } class F64Mul extends SingleByteInstruction { const F64Mul() : super(0xA2); + + static F64Mul deserialize(Deserializer d) => const F64Mul(); } class F64Div extends SingleByteInstruction { const F64Div() : super(0xA3); + + static F64Div deserialize(Deserializer d) => const F64Div(); } class F64Min extends SingleByteInstruction { const F64Min() : super(0xA4); + + static F64Min deserialize(Deserializer d) => const F64Min(); } class F64Max extends SingleByteInstruction { const F64Max() : super(0xA5); + + static F64Max deserialize(Deserializer d) => const F64Max(); } class F64Copysign extends SingleByteInstruction { const F64Copysign() : super(0xA6); + + static F64Copysign deserialize(Deserializer d) => const F64Copysign(); } class I32WrapI64 extends SingleByteInstruction { const I32WrapI64() : super(0xA7); + + static I32WrapI64 deserialize(Deserializer d) => const I32WrapI64(); } class I32TruncF32S extends SingleByteInstruction { const I32TruncF32S() : super(0xA8); + + static I32TruncF32S deserialize(Deserializer d) => const I32TruncF32S(); } class I32TruncF32U extends SingleByteInstruction { const I32TruncF32U() : super(0xA9); + + static I32TruncF32U deserialize(Deserializer d) => const I32TruncF32U(); } class I32TruncF64S extends SingleByteInstruction { const I32TruncF64S() : super(0xAA); + + static I32TruncF64S deserialize(Deserializer d) => const I32TruncF64S(); } class I32TruncF64U extends SingleByteInstruction { const I32TruncF64U() : super(0xAB); + + static I32TruncF64U deserialize(Deserializer d) => const I32TruncF64U(); } class I64ExtendI32S extends SingleByteInstruction { const I64ExtendI32S() : super(0xAC); + + static I64ExtendI32S deserialize(Deserializer d) => const I64ExtendI32S(); } class I64ExtendI32U extends SingleByteInstruction { const I64ExtendI32U() : super(0xAD); + + static I64ExtendI32U deserialize(Deserializer d) => const I64ExtendI32U(); } class I64TruncF32S extends SingleByteInstruction { const I64TruncF32S() : super(0xAE); + + static I64TruncF32S deserialize(Deserializer d) => const I64TruncF32S(); } class I64TruncF32U extends SingleByteInstruction { const I64TruncF32U() : super(0xAF); + + static I64TruncF32U deserialize(Deserializer d) => const I64TruncF32U(); } class I64TruncF64S extends SingleByteInstruction { const I64TruncF64S() : super(0xB0); + + static I64TruncF64S deserialize(Deserializer d) => const I64TruncF64S(); } class I64TruncF64U extends SingleByteInstruction { const I64TruncF64U() : super(0xB1); + + static I64TruncF64U deserialize(Deserializer d) => const I64TruncF64U(); } class F32ConvertI32S extends SingleByteInstruction { const F32ConvertI32S() : super(0xB2); + + static F32ConvertI32S deserialize(Deserializer d) => const F32ConvertI32S(); } class F32ConvertI32U extends SingleByteInstruction { const F32ConvertI32U() : super(0xB3); + + static F32ConvertI32U deserialize(Deserializer d) => const F32ConvertI32U(); } class F32ConvertI64S extends SingleByteInstruction { const F32ConvertI64S() : super(0xB4); + + static F32ConvertI64S deserialize(Deserializer d) => const F32ConvertI64S(); } class F32ConvertI64U extends SingleByteInstruction { const F32ConvertI64U() : super(0xB5); + + static F32ConvertI64U deserialize(Deserializer d) => const F32ConvertI64U(); } class F32DemoteF64 extends SingleByteInstruction { const F32DemoteF64() : super(0xB6); + + static F32DemoteF64 deserialize(Deserializer d) => const F32DemoteF64(); } class F64ConvertI32S extends SingleByteInstruction { const F64ConvertI32S() : super(0xB7); + + static F64ConvertI32S deserialize(Deserializer d) => const F64ConvertI32S(); } class F64ConvertI32U extends SingleByteInstruction { const F64ConvertI32U() : super(0xB8); + + static F64ConvertI32U deserialize(Deserializer d) => const F64ConvertI32U(); } class F64ConvertI64S extends SingleByteInstruction { const F64ConvertI64S() : super(0xB9); + + static F64ConvertI64S deserialize(Deserializer d) => const F64ConvertI64S(); } class F64ConvertI64U extends SingleByteInstruction { const F64ConvertI64U() : super(0xBA); + + static F64ConvertI64U deserialize(Deserializer d) => const F64ConvertI64U(); } class F64PromoteF32 extends SingleByteInstruction { const F64PromoteF32() : super(0xBB); + + static F64PromoteF32 deserialize(Deserializer d) => const F64PromoteF32(); } class I32ReinterpretF32 extends SingleByteInstruction { const I32ReinterpretF32() : super(0xBC); + + static I32ReinterpretF32 deserialize(Deserializer d) => + const I32ReinterpretF32(); } class I64ReinterpretF64 extends SingleByteInstruction { const I64ReinterpretF64() : super(0xBD); + + static I64ReinterpretF64 deserialize(Deserializer d) => + const I64ReinterpretF64(); } class F32ReinterpretI32 extends SingleByteInstruction { const F32ReinterpretI32() : super(0xBE); + + static F32ReinterpretI32 deserialize(Deserializer d) => + const F32ReinterpretI32(); } class F64ReinterpretI64 extends SingleByteInstruction { const F64ReinterpretI64() : super(0xBF); + + static F64ReinterpretI64 deserialize(Deserializer d) => + const F64ReinterpretI64(); } class I32Extend8S extends SingleByteInstruction { const I32Extend8S() : super(0xC0); + + static I32Extend8S deserialize(Deserializer d) => const I32Extend8S(); } class I32Extend16S extends SingleByteInstruction { const I32Extend16S() : super(0xC1); + + static I32Extend16S deserialize(Deserializer d) => const I32Extend16S(); } class I64Extend8S extends SingleByteInstruction { const I64Extend8S() : super(0xC2); + + static I64Extend8S deserialize(Deserializer d) => const I64Extend8S(); } class I64Extend16S extends SingleByteInstruction { const I64Extend16S() : super(0xC3); + + static I64Extend16S deserialize(Deserializer d) => const I64Extend16S(); } class I64Extend32S extends SingleByteInstruction { const I64Extend32S() : super(0xC4); + + static I64Extend32S deserialize(Deserializer d) => const I64Extend32S(); } class I32TruncSatF32S extends Instruction { @@ -1695,6 +2941,8 @@ class I32TruncSatF32S extends Instruction { s.writeByte(0xFC); s.writeByte(0x00); } + + static I32TruncSatF32S deserialize(Deserializer d) => const I32TruncSatF32S(); } class I32TruncSatF32U extends Instruction { @@ -1705,6 +2953,8 @@ class I32TruncSatF32U extends Instruction { s.writeByte(0xFC); s.writeByte(0x01); } + + static I32TruncSatF32U deserialize(Deserializer d) => const I32TruncSatF32U(); } class I32TruncSatF64S extends Instruction { @@ -1715,6 +2965,8 @@ class I32TruncSatF64S extends Instruction { s.writeByte(0xFC); s.writeByte(0x02); } + + static I32TruncSatF64S deserialize(Deserializer d) => const I32TruncSatF64S(); } class I32TruncSatF64U extends Instruction { @@ -1725,6 +2977,8 @@ class I32TruncSatF64U extends Instruction { s.writeByte(0xFC); s.writeByte(0x03); } + + static I32TruncSatF64U deserialize(Deserializer d) => const I32TruncSatF64U(); } class I64TruncSatF32S extends Instruction { @@ -1735,6 +2989,8 @@ class I64TruncSatF32S extends Instruction { s.writeByte(0xFC); s.writeByte(0x04); } + + static I64TruncSatF32S deserialize(Deserializer d) => const I64TruncSatF32S(); } class I64TruncSatF32U extends Instruction { @@ -1745,6 +3001,8 @@ class I64TruncSatF32U extends Instruction { s.writeByte(0xFC); s.writeByte(0x05); } + + static I64TruncSatF32U deserialize(Deserializer d) => const I64TruncSatF32U(); } class I64TruncSatF64S extends Instruction { @@ -1755,6 +3013,8 @@ class I64TruncSatF64S extends Instruction { s.writeByte(0xFC); s.writeByte(0x06); } + + static I64TruncSatF64S deserialize(Deserializer d) => const I64TruncSatF64S(); } class I64TruncSatF64U extends Instruction { @@ -1765,6 +3025,8 @@ class I64TruncSatF64U extends Instruction { s.writeByte(0xFC); s.writeByte(0x07); } + + static I64TruncSatF64U deserialize(Deserializer d) => const I64TruncSatF64U(); } class BeginNoEffectTryTable extends Instruction { @@ -1772,6 +3034,12 @@ class BeginNoEffectTryTable extends Instruction { BeginNoEffectTryTable(this.catches); + static BeginNoEffectTryTable deserialize(Deserializer d, Tags tags) { + d.readByte(); + final catches = d.readList((d) => TryTableCatch.deserialize(d, tags)); + return BeginNoEffectTryTable(catches); + } + @override void serialize(Serializer s) { s.writeByte(0x1F); @@ -1787,11 +3055,18 @@ class BeginOneOutputTryTable extends Instruction { final ValueType type; final List catches; - BeginOneOutputTryTable(this.type, this.catches); - @override List get usedValueTypes => [type]; + BeginOneOutputTryTable(this.type, this.catches); + + static BeginOneOutputTryTable deserialize( + Deserializer d, Types types, Tags tags) { + final type = ValueType.deserialize(d, types.defined); + final catches = d.readList((d) => TryTableCatch.deserialize(d, tags)); + return BeginOneOutputTryTable(type, catches); + } + @override void serialize(Serializer s) { s.writeByte(0x1F); @@ -1829,6 +3104,28 @@ abstract class TryTableCatch { TryTableCatch(this.labelIndex); void serialize(Serializer s); + + static TryTableCatch deserialize(Deserializer d, Tags tags) { + final kind = d.readByte(); + switch (kind) { + case 0x00: + final tag = tags[d.readUnsigned()]; + final label = d.readUnsigned(); + return Catch(tag, label); + case 0x01: + final tag = tags[d.readUnsigned()]; + final label = d.readUnsigned(); + return CatchRef(tag, label); + case 0x02: + final label = d.readUnsigned(); + return CatchAll(label); + case 0x03: + final label = d.readUnsigned(); + return CatchAllRef(label); + default: + throw "Invalid TryTableCatch kind: $kind"; + } + } } class Catch extends TryTableCatch { @@ -1880,3 +3177,7 @@ class CatchAllRef extends TryTableCatch { extension on Serializer { void writeTypeIndex(DefType type) => writeUnsigned(type.index); } + +extension on Deserializer { + int readTypeIndex() => readUnsigned(); +} diff --git a/pkg/wasm_builder/lib/src/ir/instructions.dart b/pkg/wasm_builder/lib/src/ir/instructions.dart index 74bc31c5a4f..13bc1fb207c 100644 --- a/pkg/wasm_builder/lib/src/ir/instructions.dart +++ b/pkg/wasm_builder/lib/src/ir/instructions.dart @@ -68,4 +68,41 @@ class Instructions implements Serializable { s.sourceMapSerializer.addMapping(s.offset, null); } + + static Instructions deserializeConst( + Deserializer d, + Types types, + Functions functions, + Globals globals, + ) { + final instructions = []; + while (true) { + final instruction = + Instruction.deserializeConst(d, types, functions, globals); + instructions.add(instruction); + if (instruction is End) break; + } + return Instructions([], {}, instructions, null, [], null); + } + + static Instructions deserialize( + Deserializer d, + Module module, + Types types, + Functions functions, + Tables tables, + Memories memories, + Tags tags, + Globals globals, + DataSegments dataSegments, + ) { + final instructions = []; + while (true) { + final instruction = Instruction.deserialize( + d, types, tables, tags, globals, dataSegments, memories, functions); + instructions.add(instruction); + if (instruction is End) break; + } + return Instructions([], {}, instructions, null, [], null); + } } diff --git a/pkg/wasm_builder/lib/src/ir/ir.dart b/pkg/wasm_builder/lib/src/ir/ir.dart index b277ca2316c..b6cf0a220c1 100644 --- a/pkg/wasm_builder/lib/src/ir/ir.dart +++ b/pkg/wasm_builder/lib/src/ir/ir.dart @@ -11,18 +11,18 @@ export 'data_segment.dart' show BaseDataSegment, DataSegment; export 'exports.dart' show Export, Exportable, Exports; export 'finalizable.dart' show Finalizable, FinalizableIndex; export 'indexable.dart' show Indexable; -export 'imports.dart' show Import; +export 'imports.dart' show Import, Imports; export 'globals.dart' show Globals; -export 'global.dart' show DefinedGlobal, Global, ImportedGlobal; +export 'global.dart' show DefinedGlobal, Global, ImportedGlobal, GlobalExport; export 'functions.dart' show Functions; export 'function.dart' - show BaseFunction, DefinedFunction, ImportedFunction, Local; + show BaseFunction, DefinedFunction, ImportedFunction, Local, FunctionExport; export 'memories.dart' show Memories; -export 'memory.dart' show DefinedMemory, ImportedMemory, Memory; +export 'memory.dart' show DefinedMemory, ImportedMemory, Memory, MemoryExport; export 'module.dart' show Module; export 'tables.dart' show Tables; -export 'table.dart' show DefinedTable, ImportedTable, Table; -export 'tags.dart' show DefinedTag, ImportedTag, Tag, Tags; +export 'table.dart' show DefinedTable, ImportedTable, Table, TableExport; +export 'tags.dart' show DefinedTag, ImportedTag, Tag, Tags, TagExport; export 'types.dart' show Types; export 'instructions.dart' show Instructions; export 'instruction.dart'; diff --git a/pkg/wasm_builder/lib/src/ir/memories.dart b/pkg/wasm_builder/lib/src/ir/memories.dart index 22203190da1..c63bc2b5b85 100644 --- a/pkg/wasm_builder/lib/src/ir/memories.dart +++ b/pkg/wasm_builder/lib/src/ir/memories.dart @@ -12,4 +12,8 @@ class Memories { final List defined; Memories(this.imported, this.defined); + + Memory operator [](int index) => index < imported.length + ? imported[index] + : defined[index - imported.length]; } diff --git a/pkg/wasm_builder/lib/src/ir/module.dart b/pkg/wasm_builder/lib/src/ir/module.dart index fd8b673e4e8..bbbc170f677 100644 --- a/pkg/wasm_builder/lib/src/ir/module.dart +++ b/pkg/wasm_builder/lib/src/ir/module.dart @@ -21,8 +21,9 @@ class Module implements Serializable { // module with the constitutents. bool _initialized = false; - late final String _moduleName; + late final String? _moduleName; late final Functions _functions; + late final BaseFunction? _start; late final Tables _tables; late final Tags _tags; late final Memories _memories; @@ -30,15 +31,16 @@ class Module implements Serializable { late final Globals _globals; late final Types _types; late final DataSegments _dataSegments; - late final List _imports; + late final Imports _imports; late final List _watchPoints; late final Uri? _sourceMapUrl; Module.uninitialized() : _initialized = false; void initialize( - String moduleName, + String? moduleName, Functions functions, + BaseFunction? start, Tables tables, Tags tags, Memories memories, @@ -46,7 +48,7 @@ class Module implements Serializable { Globals globals, Types types, DataSegments dataSegments, - List imports, + Imports imports, List watchPoints, Uri? sourceMapUrl, ) { @@ -55,6 +57,7 @@ class Module implements Serializable { _initialized = true; _moduleName = moduleName; _functions = functions; + _start = start; _tables = tables; _tags = tags; _memories = memories; @@ -67,8 +70,9 @@ class Module implements Serializable { _sourceMapUrl = sourceMapUrl; } - String get moduleName => _moduleName; + String? get moduleName => _moduleName; Functions get functions => _functions; + BaseFunction? get start => _start; Tables get tables => _tables; Tags get tags => _tags; Memories get memories => _memories; @@ -76,7 +80,7 @@ class Module implements Serializable { Globals get globals => _globals; Types get types => _types; DataSegments get dataSegments => _dataSegments; - List get imports => _imports; + Imports get imports => _imports; List get watchPoints => _watchPoints; Uri? get sourceMapUrl => _sourceMapUrl; @@ -97,7 +101,7 @@ class Module implements Serializable { TagSection(tags.defined, watchPoints).serialize(s); GlobalSection(globals.defined, watchPoints).serialize(s); ExportSection(exports.exported, watchPoints).serialize(s); - StartSection(functions.start, watchPoints).serialize(s); + StartSection(start, watchPoints).serialize(s); ElementSection( tables.defined, tables.imported, functions.declared, watchPoints) .serialize(s); @@ -113,4 +117,120 @@ class Module implements Serializable { .serialize(s); SourceMapSection(sourceMapUrl).serialize(s); } + + static Module deserialize(Deserializer d) { + final preamble = d.readBytes(8); + if (preamble[0] != 0x00 || + preamble[1] != 0x61 || + preamble[2] != 0x73 || + preamble[3] != 0x6D || + preamble[4] != 0x01 || + preamble[5] != 0x00 || + preamble[6] != 0x00 || + preamble[7] != 0x00) { + throw 'Invalid Wasm preamble'; + } + + // Although we expect sections in a specific order, we discover all of them + // here. This makes the code below that handles the presence/absense of a + // section easier. + final sections = >{}; + final customSections = >{}; + while (!d.isAtEnd) { + final id = d.readByte(); + final size = d.readUnsigned(); + final deserializer = Deserializer(d.readBytes(size)); + + if (id == CustomSection.sectionId) { + // Custom section + final name = deserializer.readName(); + customSections.putIfAbsent(name, () => []).add(deserializer); + } else { + sections.putIfAbsent(id, () => []).add(deserializer); + } + } + + final Module module = Module.uninitialized(); + + // We read the sections in the order they should be in the binary. + + final typeSections = sections[TypeSection.sectionId]; + final types = TypeSection.deserialize(typeSections?.single); + + final importSections = sections[ImportSection.sectionId]; + final imports = + ImportSection.deserialize(importSections?.single, module, types); + + final functionSections = sections[FunctionSection.sectionId]; + final functions = FunctionSection.deserialize( + functionSections?.single, module, types, imports.functions); + + final tablesSections = sections[TableSection.sectionId]; + final tables = TableSection.deserialize( + tablesSections?.single, module, types, imports.tables); + + final memorySections = sections[MemorySection.sectionId]; + final memories = MemorySection.deserialize( + memorySections?.single, module, imports.memories); + + final tagSections = sections[TagSection.sectionId]; + final tags = TagSection.deserialize( + tagSections?.single, module, types, imports.tags); + + final globalSections = sections[GlobalSection.sectionId]; + final globals = GlobalSection.deserialize( + globalSections?.single, module, types, functions, imports.globals); + + final exportSections = sections[ExportSection.sectionId]; + final exports = ExportSection.deserialize( + exportSections?.single, functions, tables, memories, globals, tags); + + final startFunctionSections = sections[StartSection.sectionId]; + final start = + StartSection.deserialize(startFunctionSections?.single, functions); + + final elementSections = sections[ElementSection.sectionId]; + // As side-effect initializes [Table.elements] + // As side-effect initializes [ImprotedTable.setElements] + // As side-effect initializes [Functions.declaredFunctions] + ElementSection.deserialize( + elementSections?.single, module, types, functions, tables, globals); + + final dataCountSections = sections[DataCountSection.sectionId]; + final dataSegments = + DataCountSection.deserialize(dataCountSections?.single); + + final codeSections = sections[CodeSection.sectionId]; + CodeSection.deserialize(codeSections?.single, functions.defined, module, + types, functions, tables, memories, tags, globals, dataSegments); + + final dataSections = sections[DataSection.sectionId]; + // As side-effect initializes [dataSegments.defined] + DataSection.deserialize(dataSections?.single, dataSegments, memories); + + final moduleName = NameSection.deserialize( + customSections[NameSection.customSectionName]?.single, + functions, + types, + globals); + final sourceMapUrl = SourceMapSection.deserialize( + customSections[SourceMapSection.customSectionName]?.single); + + return module + ..initialize( + moduleName ?? '', + functions, + start, + tables, + tags, + memories, + exports, + globals, + types, + dataSegments, + imports, + [], + sourceMapUrl, + ); + } } diff --git a/pkg/wasm_builder/lib/src/ir/tables.dart b/pkg/wasm_builder/lib/src/ir/tables.dart index 73296d1958f..cd58c984843 100644 --- a/pkg/wasm_builder/lib/src/ir/tables.dart +++ b/pkg/wasm_builder/lib/src/ir/tables.dart @@ -13,4 +13,8 @@ class Tables { final List defined; Tables(this.imported, this.defined); + + Table operator [](int index) => index < imported.length + ? imported[index] + : defined[index - imported.length]; } diff --git a/pkg/wasm_builder/lib/src/ir/tags.dart b/pkg/wasm_builder/lib/src/ir/tags.dart index 627556316b9..035e2e6b5b5 100644 --- a/pkg/wasm_builder/lib/src/ir/tags.dart +++ b/pkg/wasm_builder/lib/src/ir/tags.dart @@ -80,4 +80,8 @@ class Tags { final List imported; Tags(this.defined, this.imported); + + Tag operator [](int index) => index < imported.length + ? imported[index] + : defined[index - imported.length]; } diff --git a/pkg/wasm_builder/lib/src/ir/type.dart b/pkg/wasm_builder/lib/src/ir/type.dart index 28759bddbbc..d8993e71883 100644 --- a/pkg/wasm_builder/lib/src/ir/type.dart +++ b/pkg/wasm_builder/lib/src/ir/type.dart @@ -21,6 +21,17 @@ abstract class StorageType implements Serializable { /// For primitive types: the size in bytes of a value of this type. int get byteSize; + + static StorageType deserialize(Deserializer d, List types) { + final code = d.peekByte(); + switch (code) { + case 0x78: // -0x8 + case 0x77: // -0x9 + return PackedType.deserialize(d); + default: + return ValueType.deserialize(d, types); + } + } } /// A *value type*. @@ -51,6 +62,20 @@ abstract class ValueType implements StorageType { /// Used by the type builder to determine the set of [DefType]s referenced in /// a module. DefType? get containedDefType => null; + + static ValueType deserialize(Deserializer d, List types) { + final code = d.peekByte(); + switch (code) { + case 0x7F: // -0x01 + case 0x7E: // -0x02 + case 0x7D: // -0x03 + case 0x7C: // -0x04 + case 0x7B: // -0x05 + return NumType.deserialize(d); + default: + return RefType.deserialize(d, types); + } + } } enum NumTypeKind { i32, i64, f32, f64, v128 } @@ -117,6 +142,24 @@ class NumType extends ValueType { } } + static NumType deserialize(Deserializer d) { + final code = d.readByte(); + switch (code) { + case 0x7F: // -0x01 + return i32; + case 0x7E: // -0x02 + return i64; + case 0x7D: // -0x03 + return f32; + case 0x7C: // -0x04 + return f64; + case 0x7B: // -0x05 + return v128; + default: + throw "Invalid NumType code: $code"; + } + } + @override String toString() { switch (kind) { @@ -226,6 +269,30 @@ class RefType extends ValueType { s.write(heapType); } + static RefType deserialize(Deserializer d, List types) { + final code = d.peekByte(); + bool nullable; + HeapType heapType; + switch (code) { + case 0x63: // -0x1d + d.readByte(); + nullable = true; + heapType = HeapType.deserialize(d, types); + break; + case 0x64: // -0x1c + d.readByte(); + nullable = false; + heapType = HeapType.deserialize(d, types); + break; + default: + heapType = HeapType.deserialize(d, types); + nullable = heapType.nullableByDefault!; + assert(heapType is! UnresolvedDefType); + break; + } + return RefType._(heapType, nullable); + } + @override String toString() { if (nullable == heapType.nullableByDefault) { @@ -306,6 +373,46 @@ abstract class HeapType implements Serializable { bool isStructuralSubtypeOf(HeapType other) => isSubtypeOf(other); String get shorthandName => toString(); + + static HeapType deserialize(Deserializer d, List types) { + final code = d.readSigned(); + if (code >= 0) { + if (code < types.length) { + return types[code]; + } + // This happens in wasm type section reading if circular types are + // involved. + return UnresolvedDefType(code); + } + switch (code) { + case -0x11: // 0x6F + return extern; + case -0x12: // 0x6E + return any; + case -0x13: // 0x6D + return eq; + case -0x10: // 0x70 + return func; + case -0x15: // 0x6B + return struct; + case -0x16: // 0x6A + return array; + case -0x14: // 0x6C + return i31; + case -0x0f: // 0x71 + return none; + case -0x0e: // 0x72 + return noextern; + case -0x0d: // 0x73 + return nofunc; + case -0x17: // 0x69 + return exn; + case -0x0c: // 0x74 + return noexn; + default: + throw "Invalid heap type code: $code"; + } + } } /// Internal supertype above any, func and extern. This is only used to specify @@ -668,6 +775,137 @@ abstract class DefType extends HeapType { // Serialize the type for the type section, excluding supertype references. void serializeDefinitionInner(Serializer s); + + static DefType deserializeAllocate(Deserializer d, List existing) { + final code = d.peekByte(); + DefType? superType; + bool hasSubtypes; + switch (code) { + case 0x50: // -0x30 + d.readByte(); + hasSubtypes = true; + final count = d.readUnsigned(); + if (count == 1) { + final superTypeIndex = d.readUnsigned(); + superType = existing[superTypeIndex]; + } else { + assert(count == 0); + } + break; + case 0x4F: // -0x31 + d.readByte(); + hasSubtypes = false; + final count = d.readUnsigned(); + if (count == 1) { + final superTypeIndex = d.readUnsigned(); + superType = existing[superTypeIndex]; + } else { + assert(count == 0); + } + break; + default: + hasSubtypes = false; + break; + } + final code2 = d.readByte(); + DefType result; + switch (code2) { + case 0x60: // -0x20 + result = FunctionType.deserializeAllocate(d, superType, existing); + case 0x5F: // -0x21 + result = StructType.deserializeAllocate(d, superType, existing); + case 0x5E: // -0x22 + result = ArrayType.deserializeAllocate(d, superType, existing); + default: + throw "Invalid DefType code: $code2"; + } + result.hasAnySubtypes = hasSubtypes; + return result; + } + + void deserializeFill(Deserializer d, List existing) { + final code = d.peekByte(); + DefType? superType; + bool hasSubtypes; + switch (code) { + case 0x50: // -0x30 + d.readByte(); + hasSubtypes = true; + final count = d.readUnsigned(); + if (count == 1) { + final superTypeIndex = d.readUnsigned(); + superType = existing[superTypeIndex]; + } else { + assert(count == 0); + } + break; + case 0x4F: // -0x31 + d.readByte(); + hasSubtypes = false; + final count = d.readUnsigned(); + if (count == 1) { + final superTypeIndex = d.readUnsigned(); + superType = existing[superTypeIndex]; + } else { + assert(count == 0); + } + break; + default: + hasSubtypes = false; + break; + } + if (!identical(superType, this.superType) || + hasSubtypes != hasAnySubtypes) { + throw 'Mismatch between Allocate+Fill implementation.'; + } + final code2 = d.readByte(); + switch (code2) { + case 0x60: // -0x20 + assert(this is FunctionType); + case 0x5F: // -0x21 + assert(this is StructType); + case 0x5E: // -0x22 + assert(this is ArrayType); + default: + throw "Invalid DefType code: $code2"; + } + deserializeFillInner(d, existing); + } + + void deserializeFillInner(Deserializer d, List existing); +} + +class UnresolvedDefType extends DefType { + final int typeIndex; + + UnresolvedDefType(this.typeIndex); + + @override + bool get nullableByDefault => + throw 'Cannot obtain nullableByDefault of unresolved type'; + + @override + HeapType get abstractSuperType => + throw 'Cannot obtain abstractSuperType of unresolved type'; + + @override + Iterable get constituentTypes => + throw 'Cannot obtain constituentTypes of unresolved type'; + + @override + HeapType get topType => throw 'Cannot obtain topType of unresolved type'; + + @override + HeapType get bottomType => + throw 'Cannot obtain bottomType of unresolved type'; + + @override + void serializeDefinitionInner(Serializer s) => + throw 'Cannot serialize unresolved type'; + + @override + void deserializeFillInner(Deserializer d, List existing) => + throw 'Cannot deserialize unresolved type'; } /// The `exn` heap type. @@ -779,6 +1017,19 @@ class FunctionType extends DefType { s.writeList(outputs); } + static FunctionType deserializeAllocate( + Deserializer d, DefType? superType, List existing) { + d.readList((d) => ValueType.deserialize(d, existing)); + d.readList((d) => ValueType.deserialize(d, existing)); + return FunctionType([], [], superType: superType); + } + + @override + void deserializeFillInner(Deserializer d, List existing) { + inputs.addAll(d.readList((d) => ValueType.deserialize(d, existing))); + outputs.addAll(d.readList((d) => ValueType.deserialize(d, existing))); + } + @override String toString() => "(${inputs.join(", ")}) -> (${outputs.join(", ")})"; } @@ -851,6 +1102,17 @@ class StructType extends DataType { s.writeByte(0x5F); // -0x21 s.writeList(fields); } + + static StructType deserializeAllocate( + Deserializer d, DefType? superType, List existing) { + d.readList((d) => FieldType.deserialize(d, existing)); + return StructType(null, fields: [], superType: superType); + } + + @override + void deserializeFillInner(Deserializer d, List existing) { + fields.addAll(d.readList((d) => FieldType.deserialize(d, existing))); + } } /// A custom `array` type. @@ -884,6 +1146,17 @@ class ArrayType extends DataType { s.writeByte(0x5E); // -0x22 s.write(elementType); } + + static ArrayType deserializeAllocate( + Deserializer d, DefType? superType, List existing) { + FieldType.deserialize(d, existing); + return ArrayType(null, elementType: null, superType: superType); + } + + @override + void deserializeFillInner(Deserializer d, List existing) { + elementType = FieldType.deserialize(d, existing); + } } class _WithMutability implements Serializable { @@ -898,6 +1171,13 @@ class _WithMutability implements Serializable { s.writeByte(mutable ? 0x01 : 0x00); } + static (T, bool) deserialize( + Deserializer d, T Function(Deserializer) fun) { + final type = fun(d); + final mutable = d.readByte() == 0x01; + return (type, mutable); + } + @override String toString() => "${mutable ? "var " : "const "}$type"; } @@ -907,6 +1187,12 @@ class _WithMutability implements Serializable { /// It consists of a type and a mutability. class GlobalType extends _WithMutability { GlobalType(super.type, {super.mutable = true}); + + static GlobalType deserialize(Deserializer d, List types) { + final (type, mutable) = + _WithMutability.deserialize(d, (d) => ValueType.deserialize(d, types)); + return GlobalType(type, mutable: mutable); + } } /// A type for a struct field or an array element. @@ -931,6 +1217,12 @@ class FieldType extends _WithMutability { return type.isSubtypeOf(other.type); } } + + static FieldType deserialize(Deserializer d, List existing) { + final (type, mutable) = _WithMutability.deserialize( + d, (d) => StorageType.deserialize(d, existing)); + return FieldType(type, mutable: mutable); + } } enum PackedTypeKind { i8, i16 } @@ -978,6 +1270,18 @@ class PackedType implements StorageType { } } + static PackedType deserialize(Deserializer d) { + final code = d.readByte(); + switch (code) { + case 0x78: // -0x8 + return i8; + case 0x77: // -0x9 + return i16; + default: + throw "Invalid PackedType code: $code"; + } + } + @override String toString() { switch (kind) { diff --git a/pkg/wasm_builder/lib/src/ir/types.dart b/pkg/wasm_builder/lib/src/ir/types.dart index afeb69db9af..b51e6d2ac78 100644 --- a/pkg/wasm_builder/lib/src/ir/types.dart +++ b/pkg/wasm_builder/lib/src/ir/types.dart @@ -8,5 +8,12 @@ class Types { /// Types defined in this module. final List> recursionGroups; - Types(this.recursionGroups); + late final List defined; + + Types(this.recursionGroups) + : defined = recursionGroups.expand((g) => g).toList(); + + DefType operator [](int index) => defined[index]; + + int get length => defined.length; } diff --git a/pkg/wasm_builder/lib/src/serialize/deserializer.dart b/pkg/wasm_builder/lib/src/serialize/deserializer.dart new file mode 100644 index 00000000000..ad16384f5af --- /dev/null +++ b/pkg/wasm_builder/lib/src/serialize/deserializer.dart @@ -0,0 +1,86 @@ +// 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:convert'; +import 'dart:typed_data'; + +class Deserializer { + final Uint8List _data; + int offset = 0; + + Deserializer(this._data); + + int get length => _data.length; + + bool get isAtEnd => offset >= _data.length; + + int readByte() { + return _data[offset++]; + } + + int peekByte() { + return _data[offset]; + } + + Uint8List readBytes(int length) { + final bytes = Uint8List.sublistView(_data, offset, offset + length); + offset += length; + return bytes; + } + + int readSigned() { + int result = 0; + int shift = 0; + int byte; + do { + byte = readByte(); + result |= (byte & 0x7F) << shift; + shift += 7; + } while ((byte & 0x80) != 0); + + if ((shift < 64) && ((byte & 0x40) != 0)) { + result |= (~0 << shift); + } + + return result; + } + + int readUnsigned() { + int result = 0; + int shift = 0; + int byte; + do { + byte = readByte(); + result |= (byte & 0x7F) << shift; + shift += 7; + } while ((byte & 0x80) != 0); + return result; + } + + double readF32() { + final bd = ByteData.sublistView(_data, offset, offset + 4); + offset += 4; + return bd.getFloat32(0, Endian.little); + } + + double readF64() { + final bd = ByteData.sublistView(_data, offset, offset + 8); + offset += 8; + return bd.getFloat64(0, Endian.little); + } + + String readName() { + final length = readUnsigned(); + return utf8.decode(readBytes(length)); + } + + List readList(T Function(Deserializer) fun) { + final length = readUnsigned(); + final list = []; + for (int i = 0; i < length; i++) { + list.add(fun(this)); + } + return list; + } +} diff --git a/pkg/wasm_builder/lib/src/serialize/sections.dart b/pkg/wasm_builder/lib/src/serialize/sections.dart index 851eb94e991..dcd0e1a9a6b 100644 --- a/pkg/wasm_builder/lib/src/serialize/sections.dart +++ b/pkg/wasm_builder/lib/src/serialize/sections.dart @@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import '../ir/ir.dart' as ir; +import 'deserializer.dart'; import 'serializer.dart'; abstract class Section implements Serializable { @@ -30,6 +31,8 @@ abstract class Section implements Serializable { } class TypeSection extends Section { + static const sectionId = 1; + final ir.Types types; TypeSection(this.types, super.watchPoints); @@ -37,68 +40,187 @@ class TypeSection extends Section { List> get recursionGroups => types.recursionGroups; @override - int get id => 1; + int get id => sectionId; @override void serializeContents(Serializer s) { - if (types.recursionGroups.isNotEmpty) { - s.writeUnsigned(types.recursionGroups.length); - int typeIndex = 0; + if (types.recursionGroups.isEmpty) return; - // Set all the indices first since types can be referenced before they are - // serialized. - for (final group in recursionGroups) { - assert(group.isNotEmpty, 'Empty groups are not allowed.'); + s.writeUnsigned(types.recursionGroups.length); + int typeIndex = 0; - for (final type in group) { - type.index = typeIndex++; - } - } - for (final group in recursionGroups) { - s.writeByte(0x4E); // -0x32 - s.writeUnsigned(group.length); - for (final type in group) { - assert( - type.superType == null || - type.superType!.index <= group.last.index, - "Type '$type' has a supertype in a later recursion group"); - assert( - type.constituentTypes - .whereType() - .map((t) => t.heapType) - .whereType() - .every((d) => d.index <= group.last.index), - "Type '$type' depends on a type in a later recursion group"); - type.serializeDefinition(s); - } + // Set all the indices first since types can be referenced before they are + // serialized. + for (final group in recursionGroups) { + assert(group.isNotEmpty, 'Empty groups are not allowed.'); + + for (final type in group) { + type.index = typeIndex++; } } + for (final group in recursionGroups) { + if (group.length > 1) { + s.writeByte(0x4E); // -0x32 + s.writeUnsigned(group.length); + } + for (final type in group) { + assert( + type.superType == null || type.superType!.index <= group.last.index, + "Type '$type' has a supertype in a later recursion group"); + assert( + type.constituentTypes + .whereType() + .map((t) => t.heapType) + .whereType() + .every((d) => d.index <= group.last.index), + "Type '$type' depends on a type in a later recursion group"); + type.serializeDefinition(s); + } + } + } + + static ir.Types deserialize(Deserializer? d) { + if (d == null) { + return ir.Types([]); + } + + final List definedTypes = []; + final List> recursionGroups = []; + + final count = d.readUnsigned(); + for (int i = 0; i < count; i++) { + late int recursionGroupMemberCount; + if (d.peekByte() == 0x4E) { + d.readByte(); + // We may have more than one type in the recursion group. + recursionGroupMemberCount = d.readUnsigned(); + } else { + // Old type encoding. The type becomes it's own recursion group. + recursionGroupMemberCount = 1; + } + + // As types can form cycles within a recursion group, we construct them in + // two phases: + // + // 1) allocate the type objects and fixed parts of them + // 2) fill in the composite type references + // + // So for example we'd create a [ir.StructType] in phase 1) and then in + // phase 2) we'd populate the struct field types. + final typesInGroup = []; + final startOffset = d.offset; + for (int j = 0; j < recursionGroupMemberCount; j++) { + final type = ir.DefType.deserializeAllocate(d, definedTypes); + typesInGroup.add(type); + definedTypes.add(type); + } + d.offset = startOffset; + for (int j = 0; j < recursionGroupMemberCount; j++) { + typesInGroup[j].deserializeFill(d, definedTypes); + } + recursionGroups.add(typesInGroup); + } + return ir.Types(recursionGroups); } } class ImportSection extends Section { - final List imports; + static const int sectionId = 2; + + final ir.Imports imports; ImportSection(this.imports, super.watchPoints); @override - int get id => 2; + int get id => sectionId; @override void serializeContents(Serializer s) { - if (imports.isNotEmpty) { - s.writeList(imports); + if (imports.all.isNotEmpty) { + s.writeList(imports.all); } } + + static ir.Imports deserialize( + Deserializer? d, ir.Module module, ir.Types types) { + final imports = []; + final importedMemories = []; + final importedGlobals = []; + final importedTags = []; + final importedTables = []; + final importedFunctions = []; + + if (d != null) { + final count = d.readUnsigned(); + for (int i = 0; i < count; i++) { + final moduleName = d.readName(); + final name = d.readName(); + final kind = d.readByte(); + switch (kind) { + case 0x00: // Function + final typeIndex = d.readUnsigned(); + final type = types[typeIndex] as ir.FunctionType; + final import = ir.ImportedFunction( + module, moduleName, name, ir.FinalizableIndex(), type); + import.finalizableIndex.value = importedFunctions.length; + importedFunctions.add(import); + imports.add(import); + break; + case 0x01: // Table + final type = ir.RefType.deserialize(d, types.defined); + final limits = d.readByte(); + final minSize = d.readUnsigned(); + final maxSize = limits == 0x01 ? d.readUnsigned() : null; + final import = ir.ImportedTable(module, moduleName, name, + ir.FinalizableIndex(), type, minSize, maxSize); + import.finalizableIndex.value = importedTables.length; + importedTables.add(import); + imports.add(import); + break; + case 0x02: // Memory + final limits = d.readByte(); + final shared = limits == 0x03; + final minSize = d.readUnsigned(); + final maxSize = + limits == 0x01 || limits == 0x03 ? d.readUnsigned() : null; + final import = ir.ImportedMemory(module, moduleName, name, + ir.FinalizableIndex(), shared, minSize, maxSize); + import.finalizableIndex.value = importedMemories.length; + importedMemories.add(import); + imports.add(import); + break; + case 0x03: // Global + final type = ir.GlobalType.deserialize(d, types.defined); + final import = ir.ImportedGlobal( + module, moduleName, name, ir.FinalizableIndex(), type); + import.finalizableIndex.value = importedGlobals.length; + importedGlobals.add(import); + imports.add(import); + break; + case 0x04: // Tag + final exceptionByte = d.readByte(); + if (exceptionByte != 0x00) throw 'unexpected'; + d.readUnsigned(); // typeIndex + throw 'runtimeType'; + default: + throw "Invalid import kind: $kind"; + } + } + } + return ir.Imports.deserialized(imports, importedFunctions, importedTags, + importedGlobals, importedTables, importedMemories); + } } class FunctionSection extends Section { + static const int sectionId = 3; + final List functions; FunctionSection(this.functions, super.watchPoints); @override - int get id => 3; + int get id => sectionId; @override void serializeContents(Serializer s) { @@ -109,15 +231,35 @@ class FunctionSection extends Section { } } } + + static ir.Functions deserialize(Deserializer? d, ir.Module module, + ir.Types types, List imported) { + if (d == null) { + return ir.Functions.withoutDeclared(imported, []); + } + + final List defined = []; + final count = d.readUnsigned(); + for (int i = 0; i < count; i++) { + final typeIndex = d.readUnsigned(); + final type = types[typeIndex] as ir.FunctionType; + final function = ir.DefinedFunction.withoutBody( + module, ir.FinalizableIndex()..value = imported.length + i, type); + defined.add(function); + } + return ir.Functions.withoutDeclared(imported, defined); + } } class TableSection extends Section { + static const int sectionId = 4; + final List tables; TableSection(this.tables, super.watchPOints); @override - int get id => 4; + int get id => sectionId; @override void serializeContents(Serializer s) { @@ -125,15 +267,40 @@ class TableSection extends Section { s.writeList(tables); } } + + static ir.Tables deserialize(Deserializer? d, ir.Module module, + ir.Types types, List imported) { + if (d == null) return ir.Tables(imported, []); + + final defined = []; + final count = d.readUnsigned(); + for (int i = 0; i < count; i++) { + final type = ir.RefType.deserialize(d, types.defined); + final limits = d.readByte(); + final minSize = d.readUnsigned(); + final maxSize = limits == 0x01 ? d.readUnsigned() : null; + final table = ir.DefinedTable( + module, + [], + ir.FinalizableIndex()..value = imported.length + i, + type, + minSize, + maxSize); + defined.add(table); + } + return ir.Tables(imported, defined); + } } class MemorySection extends Section { + static const int sectionId = 5; + final List memories; MemorySection(this.memories, super.watchPoints); @override - int get id => 5; + int get id => sectionId; @override void serializeContents(Serializer s) { @@ -141,15 +308,40 @@ class MemorySection extends Section { s.writeList(memories); } } + + static ir.Memories deserialize( + Deserializer? d, ir.Module module, List imported) { + if (d == null) return ir.Memories(imported, []); + + final defined = []; + final count = d.readUnsigned(); + for (int i = 0; i < count; i++) { + final limits = d.readByte(); + final shared = limits == 0x03; + final minSize = d.readUnsigned(); + final maxSize = + limits == 0x01 || limits == 0x03 ? d.readUnsigned() : null; + final memory = ir.DefinedMemory( + module, + ir.FinalizableIndex()..value = imported.length + i, + shared, + minSize, + maxSize); + defined.add(memory); + } + return ir.Memories(imported, defined); + } } class TagSection extends Section { + static const int sectionId = 13; + final List tags; TagSection(this.tags, super.watchPoints); @override - int get id => 13; + int get id => sectionId; @override void serializeContents(Serializer s) { @@ -157,15 +349,36 @@ class TagSection extends Section { s.writeList(tags); } } + + static ir.Tags deserialize(Deserializer? d, ir.Module module, ir.Types types, + List imported) { + if (d == null) return ir.Tags([], imported); + + final defined = []; + final count = d.readUnsigned(); + for (int i = 0; i < count; i++) { + final attribute = d.readByte(); + if (attribute != 0) { + throw "Invalid tag attribute: $attribute"; + } + final type = types[d.readUnsigned()] as ir.FunctionType; + final tag = ir.DefinedTag( + module, ir.FinalizableIndex()..value = imported.length + i, type); + defined.add(tag); + } + return ir.Tags(defined, []); + } } class GlobalSection extends Section { + static const int sectionId = 6; + final List globals; GlobalSection(this.globals, super.watchPoints); @override - int get id => 6; + int get id => sectionId; @override void serializeContents(Serializer s) { @@ -173,15 +386,41 @@ class GlobalSection extends Section { s.writeList(globals); } } + + static ir.Globals deserialize( + Deserializer? d, + ir.Module module, + ir.Types types, + ir.Functions functions, + List imported) { + if (d == null) { + return ir.Globals(imported, []); + } + + final globals = ir.Globals(imported, []); + + final count = d.readUnsigned(); + for (int i = 0; i < count; i++) { + final type = ir.GlobalType.deserialize(d, types.defined); + final initializer = + ir.Instructions.deserializeConst(d, types, functions, globals); + final global = ir.DefinedGlobal(module, initializer, + ir.FinalizableIndex()..value = globals.length, type); + globals.defined.add(global); + } + return globals; + } } class ExportSection extends Section { + static const int sectionId = 7; + final List exports; ExportSection(this.exports, super.watchPoints); @override - int get id => 7; + int get id => sectionId; @override void serializeContents(Serializer s) { @@ -189,15 +428,57 @@ class ExportSection extends Section { s.writeList(exports); } } + + static ir.Exports deserialize( + Deserializer? d, + ir.Functions functions, + ir.Tables tables, + ir.Memories memories, + ir.Globals globals, + ir.Tags tags) { + if (d == null) { + return ir.Exports([]); + } + + final exports = []; + final count = d.readUnsigned(); + for (int i = 0; i < count; i++) { + final name = d.readName(); + final kind = d.readByte(); + final index = d.readUnsigned(); + switch (kind) { + case 0x00: + exports.add(ir.FunctionExport(name, functions[index])); + break; + case 0x01: + exports.add(ir.TableExport(name, tables[index])); + break; + case 0x02: + exports.add(ir.MemoryExport(name, memories[index])); + break; + case 0x03: + exports.add(ir.GlobalExport(name, globals[index])); + break; + case 0x04: + exports.add(ir.TagExport(name, tags[index])); + break; + default: + throw "Invalid export kind: $kind"; + } + } + return ir.Exports(exports); + } } class StartSection extends Section { + static const int sectionId = 8; + final ir.BaseFunction? startFunction; StartSection(this.startFunction, super.watchPoints); @override - int get id => 8; + int get id => sectionId; @override void serializeContents(Serializer s) { @@ -205,6 +486,13 @@ class StartSection extends Section { s.writeUnsigned(startFunction!.index); } } + + static ir.BaseFunction? deserialize(Deserializer? d, ir.Functions functions) { + if (d == null) { + return null; + } + return functions[d.readUnsigned()]; + } } sealed class _Element implements Serializable {} @@ -262,6 +550,8 @@ class _DeclaredElement implements _Element { } class ElementSection extends Section { + static const int sectionId = 9; + final List definedTables; final List importedTables; final List declaredFunctions; @@ -270,7 +560,7 @@ class ElementSection extends Section { this.declaredFunctions, super.watchPoints); @override - int get id => 9; + int get id => sectionId; @override void serializeContents(Serializer s) { @@ -309,22 +599,97 @@ class ElementSection extends Section { lastIndex = index; } } - for (final func in declaredFunctions) { - elements.add(_DeclaredElement([func])); + if (declaredFunctions.isNotEmpty) { + elements.add(_DeclaredElement(declaredFunctions)); } if (elements.isNotEmpty) { s.writeList(elements); } } + + static void deserialize( + Deserializer? d, + ir.Module module, + ir.Types types, + ir.Functions functions, + ir.Tables tables, + ir.Globals globals, + ) { + if (d == null) { + functions.declared = []; + return; + } + final declaredFunctions = []; + final count = d.readUnsigned(); + for (int i = 0; i < count; i++) { + final kind = d.readByte(); + int tableIndex; + switch (kind) { + case 0x00: + tableIndex = 0; + break; + case 0x06: + tableIndex = d.readUnsigned(); + break; + case 0x03: + final elemkind = d.readByte(); + if (elemkind != 0x00) throw "unsupported elemkind"; + final funcs = d.readList((d) => functions[d.readUnsigned()]); + declaredFunctions.addAll(funcs); + continue; + default: + throw "unsupported element segment kind $kind"; + } + + final offsetInitializer = + ir.Instructions.deserializeConst(d, types, functions, globals); + final instructions = offsetInitializer.instructions; + assert(instructions.length == 2 && + instructions[0] is ir.I32Const && + instructions[1] is ir.End); + final offset = (instructions[0] as ir.I32Const).value; + + if (kind == 0x06) { + ir.RefType.deserialize(d, types.defined); + } + + final table = tables[tableIndex]; + if (table is ir.DefinedTable) { + final count = d.readUnsigned(); + for (int j = 0; j < count; j++) { + late ir.BaseFunction func; + if (tableIndex == 0) { + final funcIndex = d.readUnsigned(); + func = functions[funcIndex]; + } else { + final funcInitializer = + ir.Instructions.deserializeConst(d, types, functions, globals); + final refFunc = funcInitializer.instructions.single as ir.RefFunc; + func = refFunc.function; + } + if (table.elements.length <= offset + j) { + table.elements.length = offset + j + 1; + } + table.elements[offset + j] = func; + } + } else { + throw "unsupported table type"; + } + } + + functions.declared = declaredFunctions; + } } class DataCountSection extends Section { + static const int sectionId = 12; + final List dataSegments; DataCountSection(this.dataSegments, super.watchPoints); @override - int get id => 12; + int get id => sectionId; @override void serializeContents(Serializer s) { @@ -332,15 +697,28 @@ class DataCountSection extends Section { s.writeUnsigned(dataSegments.length); } } + + static ir.DataSegments deserialize(Deserializer? d) { + if (d == null) { + return ir.DataSegments([]); + } + final count = d.readUnsigned(); + final uninitializedSegments = [ + for (int i = 0; i < count; ++i) ir.DataSegment.uninitialized() + ]; + return ir.DataSegments(uninitializedSegments); + } } class CodeSection extends Section { + static const int sectionId = 10; + final List functions; CodeSection(this.functions, super.watchPoints); @override - int get id => 10; + int get id => sectionId; @override void serializeContents(Serializer s) { @@ -348,15 +726,69 @@ class CodeSection extends Section { s.writeList(functions); } } + + static void deserialize( + Deserializer? d, + List definedFunctions, + ir.Module module, + ir.Types types, + ir.Functions functions, + ir.Tables tables, + ir.Memories memories, + ir.Tags tags, + ir.Globals globals, + ir.DataSegments dataSegments, + ) { + if (d == null) { + return; + } + + final count = d.readUnsigned(); + if (count != functions.defined.length) { + throw "Code count mismatch"; + } + for (int i = 0; i < count; i++) { + final function = definedFunctions[i]; + final type = function.type; + + final locals = [ + // Parameters + for (int i = 0; i < type.inputs.length; ++i) + ir.Local(i, type.inputs[i]), + ]; + final instructions = []; + + final bodySize = d.readUnsigned(); + final bodyDeserializer = Deserializer(d.readBytes(bodySize)); + + final localDeclCount = bodyDeserializer.readUnsigned(); + for (int j = 0; j < localDeclCount; j++) { + final localCount = bodyDeserializer.readUnsigned(); + final type = ir.ValueType.deserialize(bodyDeserializer, types.defined); + for (int k = 0; k < localCount; k++) { + locals.add(ir.Local(locals.length, type)); + } + } + while (!bodyDeserializer.isAtEnd) { + final instruction = ir.Instruction.deserialize(bodyDeserializer, types, + tables, tags, globals, dataSegments, memories, functions); + instructions.add(instruction); + } + + function.body = ir.Instructions(locals, {}, instructions, null, [], []); + } + } } class DataSection extends Section { + static const int sectionId = 11; + final List dataSegments; DataSection(this.dataSegments, super.watchPoints); @override - int get id => 11; + int get id => sectionId; @override void serializeContents(Serializer s) { @@ -364,17 +796,76 @@ class DataSection extends Section { s.writeList(dataSegments); } } + + static void deserialize( + Deserializer? d, ir.DataSegments dataSegments, ir.Memories memories) { + final defined = dataSegments.defined; + if (d == null) { + assert(defined.isEmpty); + return; + } + + final count = d.readUnsigned(); + if (defined.length != count) { + throw "Mismatch number of data segments"; + } + for (int i = 0; i < count; i++) { + final mode = d.readByte(); + if (mode == 0x1) { + // Passive segment. + final length = d.readUnsigned(); + final content = d.readBytes(length); + defined[i] + ..index = i + ..memory = null + ..offset = null + ..content = content; + continue; + } + + ir.Memory? memory; + int? offset; + if (mode == 0x00 || mode == 0x02) { + if (mode == 0x00) { + memory = memories[0]; + } else if (mode == 0x02) { + // Active segment + final memoryIndex = d.readUnsigned(); + memory = memories[memoryIndex]; + } + + final i32ConstByte = d.readByte(); + if (i32ConstByte != 0x41) throw 'bad encoding'; + offset = d.readSigned(); + final endByte = d.readByte(); + if (endByte != 0x0B) throw 'bad encoding'; + + // final offsetInitializer = ir.Instructions.deserialize(d, module); + // offset = (offsetInitializer.instructions.single as ir.I32Const).value; + } + final content = d.readBytes(d.readUnsigned()); + defined[i] + ..index = i + ..memory = memory + ..offset = offset + ..content = content; + } + } } abstract class CustomSection extends Section { + static const int sectionId = 0; + CustomSection(super.watchPoints); @override - int get id => 0; + int get id => sectionId; } class NameSection extends CustomSection { - final String moduleName; + static const String customSectionName = 'name'; + + final String? moduleName; final List functions; final List> types; final List globals; @@ -390,7 +881,9 @@ class NameSection extends CustomSection { @override void serializeContents(Serializer s) { final moduleNameSubsection = Serializer(); - moduleNameSubsection.writeName(moduleName); + if (moduleName != null) { + moduleNameSubsection.writeName(moduleName!); + } int functionNameCount = 0; final functionNames = Serializer(); @@ -458,11 +951,13 @@ class NameSection extends CustomSection { } } - s.writeName("name"); // Name of the custom section. + s.writeName(customSectionName); s.writeByte(0); // Module name subsection - s.writeUnsigned(moduleNameSubsection.data.length); - s.writeData(moduleNameSubsection); + if (moduleNameSubsection.offset > 0) { + s.writeUnsigned(moduleNameSubsection.data.length); + s.writeData(moduleNameSubsection); + } if (functionNameCount > 0) { s.writeByte(1); // Function names subsection @@ -504,9 +999,98 @@ class NameSection extends CustomSection { s.writeData(fieldNames); } } + + static String? deserialize(Deserializer? d, ir.Functions functions, + ir.Types types, ir.Globals globals) { + String? moduleName; + + if (d == null) { + return moduleName; + } + + while (!d.isAtEnd) { + final subsectionId = d.readByte(); + final subsectionSize = d.readUnsigned(); + final subsectionDeserializer = Deserializer(d.readBytes(subsectionSize)); + switch (subsectionId) { + case 0: // Module name + moduleName = subsectionDeserializer.readName(); + break; + case 1: // Function names + final count = subsectionDeserializer.readUnsigned(); + for (int i = 0; i < count; i++) { + final funcIndex = subsectionDeserializer.readUnsigned(); + final funcName = subsectionDeserializer.readName(); + final func = functions[funcIndex]; + func.functionName = funcName; + } + break; + case 2: // Local names + final funcCount = subsectionDeserializer.readUnsigned(); + for (int i = 0; i < funcCount; i++) { + final funcIndex = subsectionDeserializer.readUnsigned(); + final localCount = subsectionDeserializer.readUnsigned(); + final func = functions[funcIndex]; + if (func is ir.DefinedFunction) { + for (int j = 0; j < localCount; j++) { + final localIndex = subsectionDeserializer.readUnsigned(); + final localName = subsectionDeserializer.readName(); + func.body.localNames[localIndex] = localName; + } + } else { + // Skip local names for imported functions + for (int j = 0; j < localCount; j++) { + subsectionDeserializer.readUnsigned(); + subsectionDeserializer.readName(); + } + } + } + break; + case 4: // Type names + final count = subsectionDeserializer.readUnsigned(); + for (int i = 0; i < count; i++) { + final typeIndex = subsectionDeserializer.readUnsigned(); + final typeName = subsectionDeserializer.readName(); + final type = types[typeIndex]; + if (type is ir.DataType) { + type.name = typeName; + } + } + break; + case 7: // Global names + final count = subsectionDeserializer.readUnsigned(); + for (int i = 0; i < count; i++) { + final globalIndex = subsectionDeserializer.readUnsigned(); + final globalName = subsectionDeserializer.readName(); + globals[globalIndex].globalName = globalName; + } + break; + case 10: // Field names + final typeCount = subsectionDeserializer.readUnsigned(); + for (int i = 0; i < typeCount; i++) { + final typeIndex = subsectionDeserializer.readUnsigned(); + final fieldCount = subsectionDeserializer.readUnsigned(); + final type = types[typeIndex]; + if (type is ir.StructType) { + for (int j = 0; j < fieldCount; j++) { + final fieldIndex = subsectionDeserializer.readUnsigned(); + final fieldName = subsectionDeserializer.readName(); + type.fieldNames[fieldIndex] = fieldName; + } + } else { + throw 'unexpected field name of non struct'; + } + } + break; + } + } + return moduleName; + } } class SourceMapSection extends CustomSection { + static const String customSectionName = 'sourceMappingURL'; + final Uri? url; SourceMapSection(this.url) : super([]); @@ -514,8 +1098,15 @@ class SourceMapSection extends CustomSection { @override void serializeContents(Serializer s) { if (url != null) { - s.writeName("sourceMappingURL"); + s.writeName(customSectionName); s.writeName(url!.toString()); } } + + static Uri? deserialize(Deserializer? d) { + if (d == null) { + return null; + } + return Uri.parse(d.readName()); + } } diff --git a/pkg/wasm_builder/lib/src/serialize/serialize.dart b/pkg/wasm_builder/lib/src/serialize/serialize.dart index c617ff11900..f762b548f9e 100644 --- a/pkg/wasm_builder/lib/src/serialize/serialize.dart +++ b/pkg/wasm_builder/lib/src/serialize/serialize.dart @@ -3,20 +3,5 @@ // BSD-style license that can be found in the LICENSE file. export 'serializer.dart' show Serializable, Serializer; -export 'sections.dart' - show - CodeSection, - DataCountSection, - DataSection, - ElementSection, - ExportSection, - FunctionSection, - GlobalSection, - ImportSection, - MemorySection, - NameSection, - SourceMapSection, - StartSection, - TableSection, - TagSection, - TypeSection; +export 'deserializer.dart' show Deserializer; +export 'sections.dart'; diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json index 9b4aedae4e4..71d2842060b 100644 --- a/tools/bots/test_matrix.json +++ b/tools/bots/test_matrix.json @@ -151,6 +151,7 @@ "third_party/devtools/", "third_party/webdriver/", "third_party/pkg/", + "third_party/flute/", "tests/.dart_tool/package_config.json", "tests/angular/", "tests/co19/co19-analyzer.status",