Skip to content

Commit fe0f8de

Browse files
committed
Add annotations proposal implementation
1 parent 00c8703 commit fe0f8de

18 files changed

Lines changed: 1511 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
@@ -101,7 +101,7 @@ Proposals are grouped by their [phase](https://github.com/WebAssembly/meetings/b
101101
| [Tail Call](https://github.com/WebAssembly/tail-call) | ✅ Implemented | [0.1.4] |
102102
| [Typed Function References](https://github.com/WebAssembly/function-references) | 🚧 Parser implemented | [0.2.0] |
103103
| [Branch Hinting](https://github.com/WebAssembly/branch-hinting) | ❌ Not implemented | |
104-
| [Custom Annotation Syntax in the Text Format](https://github.com/WebAssembly/annotations) | ❌ Not implemented | |
104+
| [Custom Annotation Syntax in the Text Format](https://github.com/WebAssembly/annotations) | ✅ Implemented | `main` branch |
105105
| [Exception Handling](https://github.com/WebAssembly/exception-handling) | ❌ Not implemented | |
106106
| [Extended Constant Expressions](https://github.com/WebAssembly/extended-const) | ❌ Not implemented | |
107107
| [Garbage Collection](https://github.com/WebAssembly/gc) | ❌ Not implemented | |

Sources/WAT/BinaryEncoding/Encoder.swift

Lines changed: 103 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))
@@ -590,55 +628,55 @@ func encode(module: inout Wat, options: EncodeOptions) throws(WatParserError) ->
590628
var functionLabelNames: [[(Int, String)]] = []
591629

592630
if !functions.isEmpty {
593-
try codeEncoder.section(id: 0x0A) { encoder throws(WatParserError) in
594-
try encoder.encodeVector(
595-
functions,
596-
encodeElement: { source, encoder throws(WatParserError) in
597-
let (locals, function) = source
598-
var exprEncoder = ExpressionEncoder()
599-
// Encode locals
600-
var localsEntries: [(type: ValueType, count: UInt32)] = []
601-
for local in locals {
602-
let type = try local.type.resolve(module.types)
603-
if localsEntries.last?.type == type {
604-
localsEntries[localsEntries.count - 1].count += 1
605-
} else {
606-
localsEntries.append((type: type, count: 1))
607-
}
608-
}
609-
exprEncoder.encoder.encodeVector(localsEntries) { local, encoder in
610-
encoder.writeUnsignedLEB128(local.count)
611-
local.type.encode(to: &encoder)
631+
try codeEncoder.encodeVector(
632+
functions,
633+
encodeElement: { source, encoder throws(WatParserError) in
634+
let (locals, function) = source
635+
var exprEncoder = ExpressionEncoder()
636+
// Encode locals
637+
var localsEntries: [(type: ValueType, count: UInt32)] = []
638+
for local in locals {
639+
let type = try local.type.resolve(module.types)
640+
if localsEntries.last?.type == type {
641+
localsEntries[localsEntries.count - 1].count += 1
642+
} else {
643+
localsEntries.append((type: type, count: 1))
612644
}
613-
let parseResult = try function.parse(visitor: &exprEncoder, wat: &module, features: module.features)
614-
functionSection.append(UInt32(parseResult.typeIndex))
615-
functionLabelNames.append(parseResult.labelNames)
616-
// TODO?
617-
try exprEncoder.visitEnd()
618-
encoder.writeUnsignedLEB128(UInt(exprEncoder.encoder.output.count))
619-
encoder.output.append(contentsOf: exprEncoder.encoder.output)
620-
hasDataSegmentInstruction = hasDataSegmentInstruction || exprEncoder.hasDataSegmentInstruction
621-
})
622-
}
645+
}
646+
exprEncoder.encoder.encodeVector(localsEntries) { local, encoder in
647+
encoder.writeUnsignedLEB128(local.count)
648+
local.type.encode(to: &encoder)
649+
}
650+
let parseResult = try function.parse(visitor: &exprEncoder, wat: &module, features: module.features)
651+
functionSection.append(UInt32(parseResult.typeIndex))
652+
functionLabelNames.append(parseResult.labelNames)
653+
// TODO?
654+
try exprEncoder.visitEnd()
655+
encoder.writeUnsignedLEB128(UInt(exprEncoder.encoder.output.count))
656+
encoder.output.append(contentsOf: exprEncoder.encoder.output)
657+
hasDataSegmentInstruction = hasDataSegmentInstruction || exprEncoder.hasDataSegmentInstruction
658+
})
623659
}
624660

661+
let cs = module.customSections
662+
625663
// Section 1: Type section
626-
if !module.types.isEmpty {
627-
encoder.section(id: 0x01) { encoder in
664+
encoder.section(id: 0x01, placement: .type, customSections: cs) { encoder in
665+
if !module.types.isEmpty {
628666
encoder.encodeVector(module.types, transform: \.type.signature)
629667
}
630668
}
631669

632670
// Section 2: Import section
633-
if !module.imports.isEmpty {
634-
encoder.section(id: 0x02) { encoder in
671+
encoder.section(id: 0x02, placement: .import, customSections: cs) { encoder in
672+
if !module.imports.isEmpty {
635673
encoder.encodeVector(module.imports)
636674
}
637675
}
638676

639677
// Section 3: Function section
640-
if !functionSection.isEmpty {
641-
encoder.section(id: 0x03) { encoder in
678+
encoder.section(id: 0x03, placement: .func, customSections: cs) { encoder in
679+
if !functionSection.isEmpty {
642680
encoder.encodeVector(functionSection) { typeIndex, encoder in
643681
encoder.writeUnsignedLEB128(UInt32(typeIndex))
644682
}
@@ -647,8 +685,8 @@ func encode(module: inout Wat, options: EncodeOptions) throws(WatParserError) ->
647685

648686
// Section 4: Table section
649687
let tables = module.tablesMap.definitions()
650-
if !tables.isEmpty {
651-
try encoder.section(id: 0x04) { encoder throws(WatParserError) in
688+
try encoder.section(id: 0x04, placement: .table, customSections: cs) { encoder throws(WatParserError) in
689+
if !tables.isEmpty {
652690
try encoder.encodeVector(tables) { table, encoder throws(WatParserError) in
653691
try table.type.resolve(module.types).encode(to: &encoder)
654692
}
@@ -657,41 +695,41 @@ func encode(module: inout Wat, options: EncodeOptions) throws(WatParserError) ->
657695

658696
// Section 5: Memory section
659697
let memories = module.memories.definitions()
660-
if !memories.isEmpty {
661-
encoder.section(id: 0x05) { encoder in
698+
encoder.section(id: 0x05, placement: .memory, customSections: cs) { encoder in
699+
if !memories.isEmpty {
662700
encoder.encodeVector(memories)
663701
}
664702
}
665703

666704
// Section 6: Global section
667705
let globals = module.globals.definitions()
668-
if !globals.isEmpty {
669-
try encoder.section(id: 0x06) { encoder throws(WatParserError) in
706+
try encoder.section(id: 0x06, placement: .global, customSections: cs) { encoder throws(WatParserError) in
707+
if !globals.isEmpty {
670708
try encoder.encodeVector(globals) { global, encoder throws(WatParserError) in
671709
try global.encode(to: &encoder, wat: &module)
672710
}
673711
}
674712
}
675713

676714
// Section 7: Export section
677-
if !module.exports.isEmpty {
678-
encoder.section(id: 0x07) { encoder in
715+
encoder.section(id: 0x07, placement: .export, customSections: cs) { encoder in
716+
if !module.exports.isEmpty {
679717
encoder.encodeVector(module.exports) { export, encoder in
680718
export.encode(to: &encoder)
681719
}
682720
}
683721
}
684722

685723
// Section 8: Start section
686-
if let start = module.start {
687-
encoder.section(id: 0x08) { encoder in
724+
encoder.section(id: 0x08, placement: .start, customSections: cs) { encoder in
725+
if let start = module.start {
688726
encoder.writeUnsignedLEB128(start)
689727
}
690728
}
691729

692730
// Section 9: Element section
693-
if !module.elementsMap.isEmpty {
694-
try encoder.section(id: 0x09) { encoder throws(WatParserError) in
731+
try encoder.section(id: 0x09, placement: .elem, customSections: cs) { encoder throws(WatParserError) in
732+
if !module.elementsMap.isEmpty {
695733
try encoder.encodeVector(module.elementsMap) { element, encoder throws(WatParserError) in
696734
try element.encode(to: &encoder, wat: &module)
697735
}
@@ -706,22 +744,34 @@ func encode(module: inout Wat, options: EncodeOptions) throws(WatParserError) ->
706744
}
707745

708746
// Section 10: Code section
709-
encoder.output.append(contentsOf: codeEncoder.output)
747+
encoder.section(id: 0x0A, placement: .code, customSections: cs) { encoder in
748+
encoder.output.append(contentsOf: codeEncoder.output)
749+
}
710750

711751
// Section 11: Data section
712-
if !module.data.isEmpty {
713-
try encoder.section(id: 0x0B) { encoder throws(WatParserError) in
752+
try encoder.section(id: 0x0B, placement: .data, customSections: cs) { encoder throws(WatParserError) in
753+
if !module.data.isEmpty {
714754
try encoder.encodeVector(module.data) { data, encoder throws(WatParserError) in
715755
try data.encode(to: &encoder, wat: &module)
716756
}
717757
}
718758
}
719759

760+
// Unplaced custom sections go after all standard sections
761+
for section in cs where section.placement == .unplaced {
762+
encoder.encodeCustomSection(section)
763+
}
764+
720765
// (Optional) Name Section
721766
if options.nameSection {
722767
try encodeNameSection(module: &module, options: options, encoder: &encoder, functions: functions, functionLabelNames: functionLabelNames)
723768
}
724769

770+
// "last" placement goes after everything including the name section
771+
for cs in cs where cs.placement == .before(.last) || cs.placement == .after(.last) {
772+
encoder.encodeCustomSection(cs)
773+
}
774+
725775
return encoder.output
726776
}
727777

@@ -848,9 +898,9 @@ private func encodeNameSection(
848898
encoder.section(id: 0) { encoder in
849899
encoder.encode("name")
850900

851-
if let moduleId = module.id {
901+
if let moduleName = module.id {
852902
encoder.section(id: 0) { encoder in
853-
encoder.encode(String(moduleId.dropFirst())) // Drop "$" prefix
903+
encoder.encode(moduleName.nameValue)
854904
}
855905
}
856906
if !functionNames.isEmpty {

0 commit comments

Comments
 (0)