Skip to content

Commit 920bca2

Browse files
committed
Add annotations proposal implementation
# Conflicts: # README.md # Sources/WAT/BinaryEncoding/Encoder.swift
1 parent f7a9934 commit 920bca2

18 files changed

Lines changed: 1510 additions & 117 deletions

Package.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,7 @@ let package = Package(
8888
.testTarget(
8989
name: "WATTests",
9090
dependencies: [
91-
.target(
92-
name: "WasmTools",
93-
condition: .when(traits: ["ComponentModel"])
94-
),
91+
"WasmTools",
9592
"WAT",
9693
]
9794
),

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ Proposals are grouped by their [phase](https://github.com/WebAssembly/meetings/b
102102
| [Tail Call](https://github.com/WebAssembly/tail-call) | ✅ Implemented | [0.1.4] |
103103
| [Typed Function References](https://github.com/WebAssembly/function-references) | 🚧 Parser implemented | [0.2.0] |
104104
| [Branch Hinting](https://github.com/WebAssembly/branch-hinting) | ❌ Not implemented | |
105-
| [Custom Annotation Syntax in the Text Format](https://github.com/WebAssembly/annotations) | ❌ Not implemented | |
105+
| [Custom Annotation Syntax in the Text Format](https://github.com/WebAssembly/annotations) | ✅ Implemented | `main` branch |
106106
| [Extended Constant Expressions](https://github.com/WebAssembly/extended-const) | ❌ Not implemented | |
107107
| [Garbage Collection](https://github.com/WebAssembly/gc) | ❌ Not implemented | |
108108
| [Multiple Memories](https://github.com/WebAssembly/multi-memory) | ❌ Not implemented | |

Sources/WAT/BinaryEncoding/Encoder.swift

Lines changed: 102 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,45 @@ package struct Encoder {
2929
output.append(contentsOf: contentEncoder.output)
3030
}
3131

32-
package mutating func encodeVector<Source: Collection, E: Error>(
32+
/// Overload accepting any RawRepresentable<UInt8> for ergonomic use with enums.
33+
mutating func section<ID: RawRepresentable<UInt8>, E: Error>(id: ID, _ sectionContent: (inout Encoder) throws(E) -> Void) throws(E) {
34+
try section(id: id.rawValue, sectionContent)
35+
}
36+
37+
mutating func section<E: Error>(
38+
id: UInt8,
39+
placement: CustomSectionDecl.SectionKind,
40+
customSections: [CustomSectionDecl],
41+
_ body: (inout Encoder) throws(E) -> Void
42+
) throws(E) {
43+
for cs in customSections where cs.placement == .before(placement) {
44+
encodeCustomSection(cs)
45+
}
46+
var contentEncoder = Encoder()
47+
try body(&contentEncoder)
48+
if !contentEncoder.output.isEmpty {
49+
output.append(id)
50+
writeUnsignedLEB128(UInt32(contentEncoder.output.count))
51+
output.append(contentsOf: contentEncoder.output)
52+
}
53+
for cs in customSections where cs.placement == .after(placement) {
54+
encodeCustomSection(cs)
55+
}
56+
}
57+
58+
mutating func encodeCustomSection(_ cs: CustomSectionDecl) {
59+
section(id: 0) { encoder in
60+
cs.name.encode(to: &encoder)
61+
encoder.output.append(contentsOf: cs.content)
62+
}
63+
}
64+
65+
/// Append a single byte from any RawRepresentable<UInt8> value.
66+
mutating func append<T: RawRepresentable<UInt8>>(_ value: T) {
67+
output.append(value.rawValue)
68+
}
69+
70+
mutating func encodeVector<Source: Collection, E: Error>(
3371
_ values: Source, encodeElement: (Source.Element, inout Encoder) throws(E) -> Void
3472
) throws(E) {
3573
writeUnsignedLEB128(UInt32(values.count))
@@ -623,38 +661,37 @@ func encode(module: inout Wat, options: EncodeOptions) throws(WatParserError) ->
623661
var functionLabelNames: [[(Int, String)]] = []
624662

625663
if !functions.isEmpty {
626-
try codeEncoder.section(id: 0x0A) { encoder throws(WatParserError) in
627-
try encoder.encodeVector(
628-
functions,
629-
encodeElement: { source, encoder throws(WatParserError) in
630-
let (locals, function) = source
631-
var exprEncoder = ExpressionEncoder()
632-
// Encode locals
633-
var localsEntries: [(type: ValueType, count: UInt32)] = []
634-
for local in locals {
635-
let type = try local.type.resolve(module.types)
636-
if localsEntries.last?.type == type {
637-
localsEntries[localsEntries.count - 1].count += 1
638-
} else {
639-
localsEntries.append((type: type, count: 1))
640-
}
641-
}
642-
exprEncoder.encoder.encodeVector(localsEntries) { local, encoder in
643-
encoder.writeUnsignedLEB128(local.count)
644-
local.type.encode(to: &encoder)
664+
try codeEncoder.encodeVector(
665+
functions,
666+
encodeElement: { source, encoder throws(WatParserError) in
667+
let (locals, function) = source
668+
var exprEncoder = ExpressionEncoder()
669+
// Encode locals
670+
var localsEntries: [(type: ValueType, count: UInt32)] = []
671+
for local in locals {
672+
let type = try local.type.resolve(module.types)
673+
if localsEntries.last?.type == type {
674+
localsEntries[localsEntries.count - 1].count += 1
675+
} else {
676+
localsEntries.append((type: type, count: 1))
645677
}
646-
let parseResult = try function.parse(visitor: &exprEncoder, wat: &module, features: module.features)
647-
functionSection.append(UInt32(parseResult.typeIndex))
648-
functionLabelNames.append(parseResult.labelNames)
649-
// TODO?
650-
try exprEncoder.visitEnd()
651-
encoder.writeUnsignedLEB128(UInt(exprEncoder.encoder.output.count))
652-
encoder.output.append(contentsOf: exprEncoder.encoder.output)
653-
hasDataSegmentInstruction = hasDataSegmentInstruction || exprEncoder.hasDataSegmentInstruction
654-
})
655-
}
678+
}
679+
exprEncoder.encoder.encodeVector(localsEntries) { local, encoder in
680+
encoder.writeUnsignedLEB128(local.count)
681+
local.type.encode(to: &encoder)
682+
}
683+
let parseResult = try function.parse(visitor: &exprEncoder, wat: &module, features: module.features)
684+
functionSection.append(UInt32(parseResult.typeIndex))
685+
functionLabelNames.append(parseResult.labelNames)
686+
// TODO?
687+
try exprEncoder.visitEnd()
688+
encoder.writeUnsignedLEB128(UInt(exprEncoder.encoder.output.count))
689+
encoder.output.append(contentsOf: exprEncoder.encoder.output)
690+
hasDataSegmentInstruction = hasDataSegmentInstruction || exprEncoder.hasDataSegmentInstruction
691+
})
656692
}
657693

694+
let cs = module.customSections
658695
// Pre-resolve tag type indices so their types are in the type section.
659696
let tagDefinitions = module.tagsMap.definitions()
660697
for tag in tagDefinitions {
@@ -663,22 +700,22 @@ func encode(module: inout Wat, options: EncodeOptions) throws(WatParserError) ->
663700
}
664701

665702
// Section 1: Type section
666-
if !module.types.isEmpty {
667-
encoder.section(id: 0x01) { encoder in
703+
encoder.section(id: 0x01, placement: .type, customSections: cs) { encoder in
704+
if !module.types.isEmpty {
668705
encoder.encodeVector(module.types, transform: \.type.signature)
669706
}
670707
}
671708

672709
// Section 2: Import section
673-
if !module.imports.isEmpty {
674-
encoder.section(id: 0x02) { encoder in
710+
encoder.section(id: 0x02, placement: .import, customSections: cs) { encoder in
711+
if !module.imports.isEmpty {
675712
encoder.encodeVector(module.imports)
676713
}
677714
}
678715

679716
// Section 3: Function section
680-
if !functionSection.isEmpty {
681-
encoder.section(id: 0x03) { encoder in
717+
encoder.section(id: 0x03, placement: .func, customSections: cs) { encoder in
718+
if !functionSection.isEmpty {
682719
encoder.encodeVector(functionSection) { typeIndex, encoder in
683720
encoder.writeUnsignedLEB128(UInt32(typeIndex))
684721
}
@@ -687,8 +724,8 @@ func encode(module: inout Wat, options: EncodeOptions) throws(WatParserError) ->
687724

688725
// Section 4: Table section
689726
let tables = module.tablesMap.definitions()
690-
if !tables.isEmpty {
691-
try encoder.section(id: 0x04) { encoder throws(WatParserError) in
727+
try encoder.section(id: 0x04, placement: .table, customSections: cs) { encoder throws(WatParserError) in
728+
if !tables.isEmpty {
692729
try encoder.encodeVector(tables) { table, encoder throws(WatParserError) in
693730
try table.type.resolve(module.types).encode(to: &encoder)
694731
}
@@ -697,8 +734,8 @@ func encode(module: inout Wat, options: EncodeOptions) throws(WatParserError) ->
697734

698735
// Section 5: Memory section
699736
let memories = module.memories.definitions()
700-
if !memories.isEmpty {
701-
encoder.section(id: 0x05) { encoder in
737+
encoder.section(id: 0x05, placement: .memory, customSections: cs) { encoder in
738+
if !memories.isEmpty {
702739
encoder.encodeVector(memories)
703740
}
704741
}
@@ -717,33 +754,33 @@ func encode(module: inout Wat, options: EncodeOptions) throws(WatParserError) ->
717754

718755
// Section 6: Global section
719756
let globals = module.globals.definitions()
720-
if !globals.isEmpty {
721-
try encoder.section(id: 0x06) { encoder throws(WatParserError) in
757+
try encoder.section(id: 0x06, placement: .global, customSections: cs) { encoder throws(WatParserError) in
758+
if !globals.isEmpty {
722759
try encoder.encodeVector(globals) { global, encoder throws(WatParserError) in
723760
try global.encode(to: &encoder, wat: &module)
724761
}
725762
}
726763
}
727764

728765
// Section 7: Export section
729-
if !module.exports.isEmpty {
730-
encoder.section(id: 0x07) { encoder in
766+
encoder.section(id: 0x07, placement: .export, customSections: cs) { encoder in
767+
if !module.exports.isEmpty {
731768
encoder.encodeVector(module.exports) { export, encoder in
732769
export.encode(to: &encoder)
733770
}
734771
}
735772
}
736773

737774
// Section 8: Start section
738-
if let start = module.start {
739-
encoder.section(id: 0x08) { encoder in
775+
encoder.section(id: 0x08, placement: .start, customSections: cs) { encoder in
776+
if let start = module.start {
740777
encoder.writeUnsignedLEB128(start)
741778
}
742779
}
743780

744781
// Section 9: Element section
745-
if !module.elementsMap.isEmpty {
746-
try encoder.section(id: 0x09) { encoder throws(WatParserError) in
782+
try encoder.section(id: 0x09, placement: .elem, customSections: cs) { encoder throws(WatParserError) in
783+
if !module.elementsMap.isEmpty {
747784
try encoder.encodeVector(module.elementsMap) { element, encoder throws(WatParserError) in
748785
try element.encode(to: &encoder, wat: &module)
749786
}
@@ -758,22 +795,34 @@ func encode(module: inout Wat, options: EncodeOptions) throws(WatParserError) ->
758795
}
759796

760797
// Section 10: Code section
761-
encoder.output.append(contentsOf: codeEncoder.output)
798+
encoder.section(id: 0x0A, placement: .code, customSections: cs) { encoder in
799+
encoder.output.append(contentsOf: codeEncoder.output)
800+
}
762801

763802
// Section 11: Data section
764-
if !module.data.isEmpty {
765-
try encoder.section(id: 0x0B) { encoder throws(WatParserError) in
803+
try encoder.section(id: 0x0B, placement: .data, customSections: cs) { encoder throws(WatParserError) in
804+
if !module.data.isEmpty {
766805
try encoder.encodeVector(module.data) { data, encoder throws(WatParserError) in
767806
try data.encode(to: &encoder, wat: &module)
768807
}
769808
}
770809
}
771810

811+
// Unplaced custom sections go after all standard sections
812+
for section in cs where section.placement == .unplaced {
813+
encoder.encodeCustomSection(section)
814+
}
815+
772816
// (Optional) Name Section
773817
if options.nameSection {
774818
try encodeNameSection(module: &module, options: options, encoder: &encoder, functions: functions, functionLabelNames: functionLabelNames)
775819
}
776820

821+
// "last" placement goes after everything including the name section
822+
for cs in cs where cs.placement == .before(.last) || cs.placement == .after(.last) {
823+
encoder.encodeCustomSection(cs)
824+
}
825+
777826
return encoder.output
778827
}
779828

@@ -900,9 +949,9 @@ private func encodeNameSection(
900949
encoder.section(id: 0) { encoder in
901950
encoder.encode("name")
902951

903-
if let moduleId = module.id {
952+
if let moduleName = module.id {
904953
encoder.section(id: 0) { encoder in
905-
encoder.encode(String(moduleId.dropFirst())) // Drop "$" prefix
954+
encoder.encode(moduleName.nameValue)
906955
}
907956
}
908957
if !functionNames.isEmpty {

0 commit comments

Comments
 (0)