| // Copyright (c) 2023, 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 '../ir/ir.dart' as ir; |
| import 'deserializer.dart'; |
| import 'serializer.dart'; |
| |
| abstract class Section implements Serializable { |
| final List<int> watchPoints; |
| |
| Section(this.watchPoints); |
| |
| @override |
| void serialize(Serializer s) { |
| final contents = Serializer(); |
| serializeContents(contents); |
| final data = contents.data; |
| if (data.isNotEmpty) { |
| s.writeByte(id); |
| s.writeUnsigned(data.length); |
| s.sourceMapSerializer |
| .copyMappings(contents.sourceMapSerializer, s.offset); |
| s.writeData(contents, watchPoints); |
| } |
| } |
| |
| int get id; |
| |
| void serializeContents(Serializer s); |
| } |
| |
| class TypeSection extends Section { |
| static const sectionId = 1; |
| |
| final ir.Types types; |
| |
| TypeSection(this.types, super.watchPoints); |
| |
| List<List<ir.DefType>> get recursionGroups => types.recursionGroups; |
| |
| @override |
| int get id => sectionId; |
| |
| @override |
| void serializeContents(Serializer s) { |
| if (types.recursionGroups.isEmpty) return; |
| |
| s.writeUnsigned(types.recursionGroups.length); |
| int typeIndex = 0; |
| |
| // 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<ir.RefType>() |
| .map((t) => t.heapType) |
| .whereType<ir.DefType>() |
| .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<ir.DefType> definedTypes = []; |
| final List<List<ir.DefType>> recursionGroups = []; |
| |
| final count = d.readUnsigned(); |
| int typeIndex = 0; |
| 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 = <ir.DefType>[]; |
| final startOffset = d.offset; |
| for (int j = 0; j < recursionGroupMemberCount; j++) { |
| final type = ir.DefType.deserializeAllocate(d, definedTypes); |
| type.index = typeIndex++; |
| 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 { |
| static const int sectionId = 2; |
| |
| final ir.Imports imports; |
| |
| ImportSection(this.imports, super.watchPoints); |
| |
| @override |
| int get id => sectionId; |
| |
| @override |
| void serializeContents(Serializer s) { |
| if (imports.all.isNotEmpty) { |
| s.writeList(imports.all); |
| } |
| } |
| |
| static ir.Imports deserialize( |
| Deserializer? d, ir.Module module, ir.Types types) { |
| final imports = <ir.Import>[]; |
| final importedMemories = <ir.ImportedMemory>[]; |
| final importedGlobals = <ir.ImportedGlobal>[]; |
| final importedTags = <ir.ImportedTag>[]; |
| final importedTables = <ir.ImportedTable>[]; |
| final importedFunctions = <ir.ImportedFunction>[]; |
| |
| 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'; |
| final type = types[d.readUnsigned()] as ir.FunctionType; |
| final tag = ir.ImportedTag( |
| module, moduleName, name, ir.FinalizableIndex(), type); |
| tag.finalizableIndex.value = importedTags.length; |
| importedTags.add(tag); |
| 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<ir.DefinedFunction> functions; |
| |
| FunctionSection(this.functions, super.watchPoints); |
| |
| @override |
| int get id => sectionId; |
| |
| @override |
| void serializeContents(Serializer s) { |
| if (functions.isNotEmpty) { |
| s.writeUnsigned(functions.length); |
| for (final function in functions) { |
| s.writeUnsigned(function.type.index); |
| } |
| } |
| } |
| |
| static ir.Functions deserialize(Deserializer? d, ir.Module module, |
| ir.Types types, List<ir.ImportedFunction> imported) { |
| if (d == null) { |
| return ir.Functions(imported, []); |
| } |
| |
| final List<ir.DefinedFunction> 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(imported, defined); |
| } |
| } |
| |
| class TableSection extends Section { |
| static const int sectionId = 4; |
| |
| final List<ir.DefinedTable> tables; |
| |
| TableSection(this.tables, super.watchPOints); |
| |
| @override |
| int get id => sectionId; |
| |
| @override |
| void serializeContents(Serializer s) { |
| if (tables.isNotEmpty) { |
| s.writeList(tables); |
| } |
| } |
| |
| static ir.Tables deserialize(Deserializer? d, ir.Module module, |
| ir.Types types, List<ir.ImportedTable> imported) { |
| if (d == null) return ir.Tables(imported, []); |
| |
| final defined = <ir.DefinedTable>[]; |
| 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<ir.DefinedMemory> memories; |
| |
| MemorySection(this.memories, super.watchPoints); |
| |
| @override |
| int get id => sectionId; |
| |
| @override |
| void serializeContents(Serializer s) { |
| if (memories.isNotEmpty) { |
| s.writeList(memories); |
| } |
| } |
| |
| static ir.Memories deserialize( |
| Deserializer? d, ir.Module module, List<ir.ImportedMemory> imported) { |
| if (d == null) return ir.Memories(imported, []); |
| |
| final defined = <ir.DefinedMemory>[]; |
| 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<ir.DefinedTag> tags; |
| |
| TagSection(this.tags, super.watchPoints); |
| |
| @override |
| int get id => sectionId; |
| |
| @override |
| void serializeContents(Serializer s) { |
| if (tags.isNotEmpty) { |
| s.writeList(tags); |
| } |
| } |
| |
| static ir.Tags deserialize(Deserializer? d, ir.Module module, ir.Types types, |
| List<ir.ImportedTag> imported) { |
| if (d == null) return ir.Tags([], imported); |
| |
| final defined = <ir.DefinedTag>[]; |
| 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<ir.DefinedGlobal> globals; |
| |
| GlobalSection(this.globals, super.watchPoints); |
| |
| @override |
| int get id => sectionId; |
| |
| @override |
| void serializeContents(Serializer s) { |
| if (globals.isNotEmpty) { |
| s.writeList(globals); |
| } |
| } |
| |
| static ir.Globals deserialize( |
| Deserializer? d, |
| ir.Module module, |
| ir.Types types, |
| ir.Functions functions, |
| List<ir.ImportedGlobal> 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<ir.Export> exports; |
| |
| ExportSection(this.exports, super.watchPoints); |
| |
| @override |
| int get id => sectionId; |
| |
| @override |
| void serializeContents(Serializer s) { |
| if (exports.isNotEmpty) { |
| 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 = <ir.Export>[]; |
| 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 => sectionId; |
| |
| @override |
| void serializeContents(Serializer s) { |
| if (startFunction != null) { |
| s.writeUnsigned(startFunction!.index); |
| } |
| } |
| |
| static ir.BaseFunction? deserialize(Deserializer? d, ir.Functions functions) { |
| if (d == null) { |
| return null; |
| } |
| return functions[d.readUnsigned()]; |
| } |
| } |
| |
| class ElementSection extends Section { |
| static const int sectionId = 9; |
| |
| final ir.Elements elementSegments; |
| |
| ElementSection(this.elementSegments, super.watchPoints); |
| |
| @override |
| int get id => sectionId; |
| |
| @override |
| void serializeContents(Serializer s) { |
| if (elementSegments.segments.isNotEmpty) { |
| s.writeList(elementSegments.segments); |
| } |
| } |
| |
| static ir.Elements deserialize( |
| Deserializer? d, |
| ir.Module module, |
| ir.Types types, |
| ir.Functions functions, |
| ir.Tables tables, |
| ir.Globals globals, |
| ) { |
| if (d == null) { |
| return ir.Elements([]); |
| } |
| |
| final segments = <ir.ElementSegment>[]; |
| final count = d.readUnsigned(); |
| for (int i = 0; i < count; i++) { |
| final byte = d.peekByte(); |
| if (byte == 0x03) { |
| // Declarative segment, only to forward-declare functions. |
| final es = ir.DeclarativeElementSegment.deserialize(d, functions); |
| segments.add(es); |
| continue; |
| } |
| if (byte == 0x00 || byte == 0x02) { |
| // Active element, table values are function indices. |
| final es = ir.ActiveFunctionElementSegment.deserialize( |
| d, module, types, functions, tables, globals); |
| segments.add(es); |
| continue; |
| } |
| if (byte == 0x04 || byte == 0x06) { |
| // Active element, table values are expressions. |
| final es = ir.ActiveExpressionElementSegment.deserialize( |
| d, module, types, functions, tables, globals); |
| segments.add(es); |
| continue; |
| } |
| } |
| |
| return ir.Elements(segments); |
| } |
| } |
| |
| class DataCountSection extends Section { |
| static const int sectionId = 12; |
| |
| final List<ir.DataSegment> dataSegments; |
| |
| DataCountSection(this.dataSegments, super.watchPoints); |
| |
| @override |
| int get id => sectionId; |
| |
| @override |
| void serializeContents(Serializer s) { |
| if (dataSegments.isNotEmpty) { |
| 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<ir.DefinedFunction> functions; |
| |
| CodeSection(this.functions, super.watchPoints); |
| |
| @override |
| int get id => sectionId; |
| |
| @override |
| void serializeContents(Serializer s) { |
| if (functions.isNotEmpty) { |
| s.writeList(functions); |
| } |
| } |
| |
| static void deserialize( |
| Deserializer? d, |
| List<ir.DefinedFunction> 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 = <ir.Local>[ |
| // Parameters |
| for (int i = 0; i < type.inputs.length; ++i) |
| ir.Local(i, type.inputs[i]), |
| ]; |
| final instructions = <ir.Instruction>[]; |
| |
| 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<ir.DataSegment> dataSegments; |
| |
| DataSection(this.dataSegments, super.watchPoints); |
| |
| @override |
| int get id => sectionId; |
| |
| @override |
| void serializeContents(Serializer s) { |
| if (dataSegments.isNotEmpty) { |
| 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 => sectionId; |
| } |
| |
| class NameSection extends CustomSection { |
| static const String customSectionName = 'name'; |
| |
| final String? moduleName; |
| final List<ir.BaseFunction> functions; |
| final List<List<ir.DefType>> types; |
| final List<ir.Global> globals; |
| |
| NameSection( |
| this.moduleName, |
| this.functions, |
| this.types, |
| this.globals, |
| super.watchPoints, |
| ); |
| |
| @override |
| void serializeContents(Serializer s) { |
| final moduleNameSubsection = Serializer(); |
| if (moduleName != null) { |
| moduleNameSubsection.writeName(moduleName!); |
| } |
| |
| int functionNameCount = 0; |
| final functionNames = Serializer(); |
| for (int i = 0; i < functions.length; i++) { |
| final String? functionName = functions[i].functionName; |
| if (functionName != null) { |
| functionNames.writeUnsigned(i); |
| functionNames.writeName(functionName); |
| functionNameCount++; |
| } |
| } |
| |
| int typeIndex = 0; |
| int typeNameCount = 0; |
| final typeNames = Serializer(); |
| int typesWithNamedFieldsCount = 0; |
| final fieldNames = Serializer(); |
| for (final recursionGroup in types) { |
| for (final defType in recursionGroup) { |
| if (defType is ir.DataType) { |
| final name = defType.name; |
| if (name != null) { |
| typeNames.writeUnsigned(typeIndex); |
| typeNames.writeName(name); |
| typeNameCount++; |
| if (defType is ir.StructType && defType.fieldNames.isNotEmpty) { |
| fieldNames.writeUnsigned(typeIndex); |
| fieldNames.writeUnsigned(defType.fieldNames.length); |
| for (final entry in defType.fieldNames.entries) { |
| fieldNames.writeUnsigned(entry.key); |
| fieldNames.writeName(entry.value); |
| } |
| typesWithNamedFieldsCount++; |
| } |
| } |
| } |
| typeIndex++; |
| } |
| } |
| |
| int globalNameCount = 0; |
| final globalNames = Serializer(); |
| for (int i = 0; i < globals.length; i++) { |
| final globalName = globals[i].globalName; |
| if (globalName != null) { |
| globalNames.writeUnsigned(i); |
| globalNames.writeName(globalName); |
| globalNameCount++; |
| } |
| } |
| |
| int functionsWithLocalNamesCount = 0; |
| final localNames = Serializer(); |
| for (final function in functions) { |
| if (function is ir.DefinedFunction) { |
| if (function.localNames.isNotEmpty) { |
| localNames.writeUnsigned(function.finalizableIndex.value); |
| localNames.writeUnsigned(function.localNames.length); |
| for (final entry in function.localNames.entries) { |
| localNames.writeUnsigned(entry.key); |
| localNames.writeName(entry.value); |
| } |
| functionsWithLocalNamesCount++; |
| } |
| } |
| } |
| |
| s.writeName(customSectionName); |
| |
| s.writeByte(0); // Module name subsection |
| if (moduleNameSubsection.offset > 0) { |
| s.writeUnsigned(moduleNameSubsection.data.length); |
| s.writeData(moduleNameSubsection); |
| } |
| |
| if (functionNameCount > 0) { |
| s.writeByte(1); // Function names subsection |
| s.writeUnsigned(functionNames.data.length + |
| Serializer.writeUnsignedByteCount(functionNameCount)); |
| s.writeUnsigned(functionNameCount); |
| s.writeData(functionNames); |
| } |
| |
| if (functionsWithLocalNamesCount > 0) { |
| s.writeByte(2); // Local names substion |
| s.writeUnsigned(localNames.data.length + |
| Serializer.writeUnsignedByteCount(functionsWithLocalNamesCount)); |
| s.writeUnsigned(functionsWithLocalNamesCount); |
| s.writeData(localNames); |
| } |
| |
| if (typeNameCount > 0) { |
| s.writeByte(4); // Type names subsection |
| s.writeUnsigned(typeNames.data.length + |
| Serializer.writeUnsignedByteCount(typeNameCount)); |
| s.writeUnsigned(typeNameCount); |
| s.writeData(typeNames); |
| } |
| |
| if (globalNameCount > 0) { |
| s.writeByte(7); // Global names subsection |
| s.writeUnsigned(globalNames.data.length + |
| Serializer.writeUnsignedByteCount(globalNameCount)); |
| s.writeUnsigned(globalNameCount); |
| s.writeData(globalNames); |
| } |
| |
| if (typesWithNamedFieldsCount > 0) { |
| s.writeByte(10); // Field names subsection |
| s.writeUnsigned(fieldNames.data.length + |
| Serializer.writeUnsignedByteCount(typesWithNamedFieldsCount)); |
| s.writeUnsigned(typesWithNamedFieldsCount); |
| 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([]); |
| |
| @override |
| void serializeContents(Serializer s) { |
| if (url != null) { |
| s.writeName(customSectionName); |
| s.writeName(url!.toString()); |
| } |
| } |
| |
| static Uri? deserialize(Deserializer? d) { |
| if (d == null) { |
| return null; |
| } |
| return Uri.parse(d.readName()); |
| } |
| } |