@@ -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