Skip to content

Commit e552c8c

Browse files
authored
CM WAT normalization: remove ComponentEncoder.GroupedFields (#325)
Following up on #323, `ComponentEncoder.GroupedFields` was responsible for normalization in the encoder. As we're shifting normalization to `ComponentWatParser`, which now accumulates fully populated `NormalizedDefinition`, `GroupedFields` is no longer needed for batching. As CM sections can be unordered, discontiguous, and interleaved, the encoder in the future will only have to iterate through `NormalizedDefinition` and flush a section when definition kind changes.
1 parent a72cd37 commit e552c8c

3 files changed

Lines changed: 74 additions & 142 deletions

File tree

Sources/WAT/BinaryEncoding/ComponentEncoder.swift

Lines changed: 49 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,35 @@
2222
) throws(WatParserError) -> [UInt8] {
2323
writeHeader()
2424

25-
// Collect metadata needed for encoding
26-
let fields = try groupFields(component.fields)
25+
// Compute metadata directly from component fields
26+
let coreInstances: [(ComponentWatParser.CoreInstanceDef, Location)] = component.fields.compactMap { field in
27+
if case .coreInstance(let def) = field.kind { return (def, field.location) }
28+
return nil
29+
}
30+
let canons: [(ComponentWatParser.CanonDef, Location)] = component.fields.compactMap { field in
31+
if case .canon(let def) = field.kind { return (def, field.location) }
32+
return nil
33+
}
34+
let exports: [(ComponentWatParser.ExportDef, Location)] = component.fields.compactMap { field in
35+
if case .componentExport(let def) = field.kind { return (def, field.location) }
36+
return nil
37+
}
2738

2839
// Build core instance index mapping (parser index -> binary index)
2940
// This accounts for inline export instances that shift indices
30-
let coreInstanceIndexMapping = buildCoreInstanceIndexMapping(component: component, fields: fields)
41+
let coreInstanceIndexMapping = buildCoreInstanceIndexMapping(coreInstances: coreInstances)
3142

3243
let coreFuncAliases = try collectCoreFuncAliases(
33-
from: fields.canons,
44+
from: canons,
3445
component: component,
3546
coreInstanceIndexMapping: coreInstanceIndexMapping
3647
)
37-
let componentFuncAliases = try collectComponentFuncAliases(from: fields.canons, component: component)
38-
let exportFuncAliases = try collectExportFuncAliases(from: fields.exports, component: component)
48+
let componentFuncAliases = try collectComponentFuncAliases(from: canons, component: component)
49+
let exportFuncAliases = try collectExportFuncAliases(from: exports, component: component)
3950

4051
// Collect type indices that are exported
4152
var exportedTypeIndices = Set<Int>()
42-
for (exportDef, _) in fields.exports {
53+
for (exportDef, _) in exports {
4354
if case .type(let indexOrId) = exportDef.descriptor {
4455
if case .index(let idx, _) = indexOrId {
4556
exportedTypeIndices.insert(Int(idx))
@@ -77,7 +88,7 @@
7788
}
7889

7990
// Helper to flush accumulated core types as a batched section
80-
var pendingCoreTypes: [UInt32] = []
91+
var pendingCoreTypes: [ComponentWatParser.CoreTypeDef] = []
8192
func flushPendingCoreTypes() throws(WatParserError) {
8293
if !pendingCoreTypes.isEmpty {
8394
try encodeBatchedCoreTypes(
@@ -89,7 +100,7 @@
89100
}
90101

91102
// Helper to flush accumulated core instances as a batched section
92-
var pendingCoreInstances: [(CoreInstanceIndex, Location)] = []
103+
var pendingCoreInstances: [(ComponentWatParser.CoreInstanceDef, Location)] = []
93104
func flushPendingCoreInstances() throws(WatParserError) {
94105
if !pendingCoreInstances.isEmpty {
95106
try encodeCoreInstances(
@@ -141,25 +152,25 @@
141152
try flushPendingSectionsExcept(field.kind.sectionKind)
142153

143154
switch field.kind {
144-
case .componentType(let typeIndex):
155+
case .componentType(_, let typeIndex):
145156
// Collect all unemitted types up to and including this type index
146157
// This ensures anonymous dependency types (with lower indices) get batched
147158
// with the types that reference them
148-
let maxIndex = Int(typeIndex.rawValue)
159+
let maxIndex = typeIndex
149160
for (oldIndex, _) in typeIndexMapping.sorted(by: { $0.key < $1.key }) {
150161
if oldIndex <= maxIndex && !emittedTypes.contains(oldIndex) && !pendingComponentTypes.contains(oldIndex) {
151162
pendingComponentTypes.append(oldIndex)
152163
}
153164
}
154165

155-
case .coreModule(let index):
156-
try encodeSingleCoreModule(index, component: component, options: options)
166+
case .coreModule(let moduleDef):
167+
try encodeSingleCoreModule(moduleDef, options: options)
157168

158-
case .coreInstance(let index):
159-
pendingCoreInstances.append((index, field.location))
169+
case .coreInstance(let instanceDef):
170+
pendingCoreInstances.append((instanceDef, field.location))
160171

161-
case .coreType(let index):
162-
pendingCoreTypes.append(UInt32(index))
172+
case .coreType(let coreTypeDef):
173+
pendingCoreTypes.append(coreTypeDef)
163174

164175
case .canon(let canonDef):
165176
try emitRequiredTypesForCanon(
@@ -208,11 +219,11 @@
208219
emittedExportFuncAliases: &emittedExportFuncAliases
209220
)
210221

211-
case .component(let index):
212-
try encodeSingleComponent(index, component: component, options: options)
222+
case .component(let nestedDef):
223+
try encodeSingleComponent(nestedDef, options: options)
213224

214-
case .instance(let index):
215-
try encodeSingleComponentInstance(index, component: component, location: field.location)
225+
case .instance(let instanceDef):
226+
try encodeSingleComponentInstance(instanceDef, component: component, location: field.location)
216227

217228
case .alias(let alias):
218229
pendingAliases.append(alias)
@@ -586,15 +597,10 @@
586597

587598
// Encode a single core module as its own section
588599
private mutating func encodeSingleCoreModule(
589-
_ moduleIndex: CoreModuleIndex,
590-
component: ComponentWatParser.ComponentDef,
600+
_ moduleDef: ComponentWatParser.ModuleDef,
591601
options: EncodeOptions
592602
) throws(WatParserError) {
593-
guard Int(moduleIndex.rawValue) < component.coreModulesMap.count else {
594-
throw WatParserError("Invalid core module index \(moduleIndex.rawValue)", location: nil)
595-
}
596-
597-
var moduleDef = component.coreModulesMap[Int(moduleIndex.rawValue)]
603+
var moduleDef = moduleDef
598604
var moduleBytes = try WAT.encode(module: &moduleDef.wat, options: options)
599605

600606
if options.nameSection, let moduleId = moduleDef.id {
@@ -740,17 +746,15 @@
740746

741747
// Encode multiple core types in a single batched section
742748
private mutating func encodeBatchedCoreTypes(
743-
_ typeIndices: [UInt32],
749+
_ typeDefs: [ComponentWatParser.CoreTypeDef],
744750
component: ComponentWatParser.ComponentDef
745751
) throws(WatParserError) {
746-
guard !typeIndices.isEmpty else { return }
752+
guard !typeDefs.isEmpty else { return }
747753

748754
try underlying.section(id: 0x03) { encoder throws(WatParserError) in
749-
encoder.writeUnsignedLEB128(UInt32(typeIndices.count))
755+
encoder.writeUnsignedLEB128(UInt32(typeDefs.count))
750756

751-
for typeIndex in typeIndices {
752-
guard Int(typeIndex) < component.coreTypesMap.count else { continue }
753-
let typeDef = component.coreTypesMap[Int(typeIndex)]
757+
for typeDef in typeDefs {
754758
try Self.encodeCoreTypeContent(typeDef, component: component, encoder: &encoder)
755759
}
756760
}
@@ -1201,15 +1205,9 @@
12011205

12021206
// Encode a single nested component as its own section
12031207
private mutating func encodeSingleComponent(
1204-
_ componentIndex: ComponentIndex,
1205-
component: ComponentWatParser.ComponentDef,
1208+
_ nestedComponent: ComponentWatParser.ComponentDef,
12061209
options: EncodeOptions
12071210
) throws(WatParserError) {
1208-
guard Int(componentIndex.rawValue) < component.componentsMap.count else {
1209-
throw WatParserError("Invalid component index \(componentIndex.rawValue)", location: nil)
1210-
}
1211-
1212-
let nestedComponent = component.componentsMap[Int(componentIndex.rawValue)]
12131211
var nestedEncoder = ComponentEncoder()
12141212
_ = try nestedEncoder.encode(nestedComponent, options: options)
12151213

@@ -1220,16 +1218,10 @@
12201218

12211219
// Encode a single component instance as its own section
12221220
private mutating func encodeSingleComponentInstance(
1223-
_ instanceIndex: ComponentInstanceIndex,
1221+
_ instanceDef: ComponentWatParser.ComponentInstanceDef,
12241222
component: ComponentWatParser.ComponentDef,
12251223
location: Location
12261224
) throws(WatParserError) {
1227-
guard Int(instanceIndex.rawValue) < component.componentInstancesMap.count else {
1228-
throw WatParserError("Invalid component instance index \(instanceIndex.rawValue)", location: location)
1229-
}
1230-
1231-
let instanceDef = component.componentInstancesMap[Int(instanceIndex.rawValue)]
1232-
12331225
guard let componentRef = instanceDef.componentRef else {
12341226
throw WatParserError("Component instance has no component reference", location: location)
12351227
}
@@ -1255,57 +1247,16 @@
12551247
}
12561248
}
12571249

1258-
private func groupFields(_ fields: [ComponentWatParser.ComponentDefField]) throws(WatParserError) -> GroupedFields {
1259-
var result = GroupedFields()
1260-
1261-
for field in fields {
1262-
switch field.kind {
1263-
case .coreModule(let index):
1264-
result.coreModules.append((index, field.location))
1265-
case .coreInstance(let index):
1266-
result.coreInstances.append((index, field.location))
1267-
case .coreType(let index):
1268-
result.coreTypes.append((index, field.location))
1269-
case .component(let index):
1270-
result.components.append((index, field.location))
1271-
case .canon(let canonDef):
1272-
result.canons.append((canonDef, field.location))
1273-
case .componentExport(let exportDef):
1274-
result.exports.append((exportDef, field.location))
1275-
case .componentImport(let importDef):
1276-
result.imports.append((importDef, field.location))
1277-
case .instance(let index):
1278-
result.instances.append((index, field.location))
1279-
case .componentType:
1280-
// Component types are handled separately in the main encoding loop
1281-
// They don't need to be grouped for auxiliary calculations
1282-
break
1283-
case .alias:
1284-
// Aliases are handled in the main encoding loop with batching
1285-
// They don't need to be grouped for auxiliary calculations
1286-
break
1287-
}
1288-
}
1289-
1290-
return result
1291-
}
1292-
12931250
/// Build a mapping from parser core instance index to binary index.
12941251
/// This accounts for inline export instances that shift indices.
12951252
private func buildCoreInstanceIndexMapping(
1296-
component: ComponentWatParser.ComponentDef,
1297-
fields: GroupedFields
1253+
coreInstances: [(ComponentWatParser.CoreInstanceDef, Location)]
12981254
) -> [Int: Int] {
12991255
var mapping: [Int: Int] = [:]
13001256
var binaryIndex = 0
13011257

13021258
// Process core instances in order, counting inline export instances
1303-
for (instanceIndex, _) in fields.coreInstances {
1304-
let idx = Int(instanceIndex.rawValue)
1305-
guard idx < component.coreInstancesMap.count else { continue }
1306-
1307-
let instanceDef = component.coreInstancesMap[idx]
1308-
1259+
for (parserIndex, (instanceDef, _)) in coreInstances.enumerated() {
13091260
// Count inline export instances that will be emitted first
13101261
var inlineExportCount = 0
13111262
for arg in instanceDef.arguments {
@@ -1316,7 +1267,7 @@
13161267

13171268
// Inline exports come first, then the main instance
13181269
// So the main instance index is offset by inline export count
1319-
mapping[idx] = binaryIndex + inlineExportCount
1270+
mapping[parserIndex] = binaryIndex + inlineExportCount
13201271
binaryIndex += 1 + inlineExportCount // 1 for main instance + inline exports
13211272
}
13221273

@@ -1372,21 +1323,18 @@
13721323
}
13731324

13741325
private mutating func encodeCoreInstances(
1375-
_ instances: [(CoreInstanceIndex, Location)],
1326+
_ instances: [(ComponentWatParser.CoreInstanceDef, Location)],
13761327
component: ComponentWatParser.ComponentDef
13771328
) throws(WatParserError) {
13781329
// First pass: collect all inline export instances that need to be created
13791330
// and compute the mapping from original to actual instance indices
13801331
var inlineExportInstances: [(exports: [ComponentWatParser.CoreInstanceDef.Argument.Kind.Export], location: Location)] = []
1381-
var inlineExportInstanceMapping: [Int: Int] = [:] // Map from (instance index, arg index) hash to inline instance index
1382-
1383-
for (instanceIndex, location) in instances {
1384-
guard Int(instanceIndex.rawValue) < component.coreInstancesMap.count else { continue }
1385-
let instanceDef = component.coreInstancesMap[Int(instanceIndex.rawValue)]
1332+
var inlineExportInstanceMapping: [Int: Int] = [:] // Map from (instance position, arg index) hash to inline instance index
13861333

1334+
for (instancePosition, (instanceDef, location)) in instances.enumerated() {
13871335
for (argIndex, arg) in instanceDef.arguments.enumerated() {
13881336
if case .exports(let exports) = arg.kind {
1389-
let key = Int(instanceIndex.rawValue) * 1000 + argIndex
1337+
let key = instancePosition * 1000 + argIndex
13901338
inlineExportInstanceMapping[key] = inlineExportInstances.count
13911339
inlineExportInstances.append((exports: exports, location: location))
13921340
}
@@ -1414,13 +1362,7 @@
14141362
// Second: encode instantiate instances (form 0x00)
14151363
let inlineInstanceBaseIndex = inlineExportInstances.count
14161364

1417-
for (instanceIndex, location) in instances {
1418-
guard Int(instanceIndex.rawValue) < component.coreInstancesMap.count else {
1419-
throw WatParserError("Invalid core instance index \(instanceIndex.rawValue)", location: location)
1420-
}
1421-
1422-
let instanceDef = component.coreInstancesMap[Int(instanceIndex.rawValue)]
1423-
1365+
for (instancePosition, (instanceDef, location)) in instances.enumerated() {
14241366
encoder.output.append(0x00) // instantiate form
14251367

14261368
let moduleIndex = try component.coreModulesMap.resolveIndex(use: instanceDef.moduleId)
@@ -1439,7 +1381,7 @@
14391381
case .exports:
14401382
// Reference the inline export instance we created earlier
14411383
encoder.output.append(0x12)
1442-
let key = Int(instanceIndex.rawValue) * 1000 + argIndex
1384+
let key = instancePosition * 1000 + argIndex
14431385
guard let inlineIndex = inlineExportInstanceMapping[key] else {
14441386
throw WatParserError("Internal error: inline export instance not found", location: location)
14451387
}
@@ -1708,17 +1650,6 @@
17081650
}
17091651
}
17101652

1711-
private struct GroupedFields {
1712-
var coreModules: [(CoreModuleIndex, Location)] = []
1713-
var coreInstances: [(CoreInstanceIndex, Location)] = []
1714-
var coreTypes: [(UInt32, Location)] = []
1715-
var components: [(ComponentIndex, Location)] = []
1716-
var instances: [(ComponentInstanceIndex, Location)] = []
1717-
var canons: [(ComponentWatParser.CanonDef, Location)] = []
1718-
var exports: [(ComponentWatParser.ExportDef, Location)] = []
1719-
var imports: [(ComponentWatParser.ImportDef, Location)] = []
1720-
}
1721-
17221653
private typealias CoreFuncAlias = (instanceIndex: Int, exportName: String)
17231654
private typealias ComponentFuncAlias = (instanceIndex: Int, exportName: String)
17241655

Sources/WAT/Parser/ComponentWatParser.swift

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@
7070
componentStack.append(ComponentDef(id: nestedId))
7171
try parseComponentFields(&parser) // Recursive call for nested components
7272
let nested = componentStack.removeLast()
73-
let idx = try currentComponent.componentsMap.add(nested)
74-
currentComponent.fields.append(.init(location: location, kind: .component(.init(idx))))
73+
_ = try currentComponent.componentsMap.add(nested)
74+
currentComponent.fields.append(.init(location: location, kind: .component(nested)))
7575
case "import":
7676
try parseComponentImport(&parser, location: location)
7777
case "export":
@@ -97,23 +97,23 @@
9797
switch coreKeyword {
9898
case "module":
9999
let moduleDef = try self.parseModuleDef(&parser)
100-
let index = try self.currentComponent.coreModulesMap.add(moduleDef)
100+
_ = try self.currentComponent.coreModulesMap.add(moduleDef)
101101
self.currentComponent.fields.append(
102-
.init(location: location, kind: .coreModule(.init(index)))
102+
.init(location: location, kind: .coreModule(moduleDef))
103103
)
104104

105105
case "instance":
106106
let instanceDef = try self.parseCoreInstanceDef(&parser)
107-
let index = try self.currentComponent.coreInstancesMap.add(instanceDef)
107+
_ = try self.currentComponent.coreInstancesMap.add(instanceDef)
108108
self.currentComponent.fields.append(
109-
.init(location: location, kind: .coreInstance(.init(index)))
109+
.init(location: location, kind: .coreInstance(instanceDef))
110110
)
111111

112112
case "type":
113113
let coreTypeDef = try self.parseCoreTypeDef(&parser)
114-
let resolvedId = try self.currentComponent.coreTypesMap.add(coreTypeDef)
114+
_ = try self.currentComponent.coreTypesMap.add(coreTypeDef)
115115
self.currentComponent.fields.append(
116-
.init(location: location, kind: .coreType(TypeIndex(resolvedId)))
116+
.init(location: location, kind: .coreType(coreTypeDef))
117117
)
118118

119119
case "func":
@@ -364,9 +364,9 @@
364364
try parser.expect(.rightParen) // Close (instantiate ...)
365365

366366
let instanceDef = ComponentInstanceDef(id: instanceId, componentRef: componentRef, arguments: arguments)
367-
let index = try currentComponent.componentInstancesMap.add(instanceDef)
367+
_ = try currentComponent.componentInstancesMap.add(instanceDef)
368368
currentComponent.fields.append(
369-
.init(location: location, kind: .instance(.init(index)))
369+
.init(location: location, kind: .instance(instanceDef))
370370
)
371371
}
372372

@@ -589,7 +589,7 @@
589589
self.currentComponent.fields.append(
590590
.init(
591591
location: typeLocation,
592-
kind: .componentType(ComponentTypeIndex(rawValue: typeIndex))
592+
kind: .componentType(typeDef, typeIndex: typeIndex)
593593
)
594594
)
595595

0 commit comments

Comments
 (0)