1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
|
public protocol CanonicalStoring {
associatedtype Operand
associatedtype Pointer: Strideable
func storeUInt8(at pointer: Pointer, _ value: Operand)
func storeUInt16(at pointer: Pointer, _ value: Operand)
func storeUInt32(at pointer: Pointer, _ value: Operand)
func storeUInt64(at pointer: Pointer, _ value: Operand)
func storeInt8(at pointer: Pointer, _ value: Operand)
func storeInt16(at pointer: Pointer, _ value: Operand)
func storeInt32(at pointer: Pointer, _ value: Operand)
func storeInt64(at pointer: Pointer, _ value: Operand)
func storeFloat32(at pointer: Pointer, _ value: Operand)
func storeFloat64(at pointer: Pointer, _ value: Operand)
func storeFlags(at pointer: Pointer, _ value: Operand, type: WITFlags) throws
func storeOption(
at pointer: Pointer, _ value: Operand,
storeDiscriminant: (Operand) throws -> Void,
storePayload: (Operand) throws -> Void
) throws
func storeResult(
at pointer: Pointer, _ value: Operand,
ok: WITType?, error: WITType?,
storeDiscriminant: (Operand) throws -> Void,
storePayload: (Bool, Operand) throws -> Void
) throws
func storeVariant(
at pointer: Pointer, _ value: Operand, type: WITVariant,
storeDiscriminant: (Operand) throws -> Void,
storePayload: (Int, Operand) throws -> Void
) throws
}
extension CanonicalABI {
public static func store<Storing: CanonicalStoring, Lowering: CanonicalLowering>(
type: WITType,
value: Storing.Operand,
pointer: Storing.Pointer,
storing: inout Storing,
lowering: inout Lowering
) throws where Storing.Operand == Lowering.Operand, Storing.Pointer == Lowering.Pointer {
func storeList(buffer: Storing.Operand, length: Storing.Operand) {
storing.storeUInt32(at: pointer, buffer)
storing.storeUInt32(at: pointer.advanced(by: 4), length)
}
func storeRecord(values: [Storing.Operand], types: [WITType]) throws {
for (value, field) in zip(values, fieldOffsets(fields: types)) {
let (fieldType, offset) = field
try store(
type: fieldType, value: value,
pointer: pointer.advanced(by: Storing.Pointer.Stride(exactly: offset)!),
storing: &storing, lowering: &lowering
)
}
}
switch type {
case .bool:
storing.storeUInt8(at: pointer, lowering.lowerBool(value))
case .u8:
storing.storeUInt8(at: pointer, value)
case .u16: storing.storeUInt16(at: pointer, value)
case .u32: storing.storeUInt32(at: pointer, value)
case .u64: storing.storeUInt64(at: pointer, value)
case .s8: storing.storeInt8(at: pointer, value)
case .s16: storing.storeInt16(at: pointer, value)
case .s32: storing.storeInt32(at: pointer, value)
case .s64: storing.storeInt64(at: pointer, value)
case .float32: storing.storeFloat32(at: pointer, value)
case .float64: storing.storeFloat64(at: pointer, value)
case .char: storing.storeUInt32(at: pointer, lowering.lowerChar(value))
case .enum(let enumType):
storing.storeUInt32(at: pointer, try lowering.lowerEnum(value, type: enumType))
case .flags(let flags):
try storing.storeFlags(at: pointer, value, type: flags)
case .string:
let (buffer, length) = try lowering.lowerString(value, encoding: "utf8")
storeList(buffer: buffer, length: length)
case .option(let wrapped):
try storing.storeOption(
at: pointer, value,
storeDiscriminant: { discriminant in
try store(
type: .u8, value: discriminant, pointer: pointer,
storing: &storing, lowering: &lowering
)
},
storePayload: { payload in
let offset = Storing.Pointer.Stride(exactly: payloadOffset(cases: [wrapped, nil]))!
try store(
type: wrapped, value: payload, pointer: pointer.advanced(by: offset),
storing: &storing, lowering: &lowering
)
}
)
case .result(let ok, let error):
try storing.storeResult(
at: pointer, value, ok: ok, error: error,
storeDiscriminant: { discriminant in
try store(
type: .u8, value: discriminant, pointer: pointer,
storing: &storing, lowering: &lowering
)
},
storePayload: { isError, payload in
let offset = Storing.Pointer.Stride(exactly: payloadOffset(cases: [ok, error]))!
guard let type = isError ? error : ok else { return }
try store(
type: type, value: payload, pointer: pointer.advanced(by: offset),
storing: &storing, lowering: &lowering
)
}
)
case .list(let element):
let (buffer, length) = try lowerList(value, element: element, storing: &storing, lowering: &lowering)
storeList(buffer: buffer, length: length)
case .tuple(let types):
let values = lowering.lowerTuple(value, types: types)
try storeRecord(values: values, types: types)
case .record(let record):
let types = record.fields.map(\.type)
let values = lowering.lowerRecord(value, type: record)
try storeRecord(values: values, types: types)
case .variant(let variant):
let discriminantType = CanonicalABI.discriminantType(numberOfCases: UInt32(variant.cases.count))
try storing.storeVariant(
at: pointer, value, type: variant,
storeDiscriminant: { discriminant in
try store(
type: discriminantType.asWITType, value: discriminant,
pointer: pointer, storing: &storing, lowering: &lowering
)
},
storePayload: { i, payload in
guard let payloadType = variant.cases[i].type else { return }
let offset = Storing.Pointer.Stride(exactly: payloadOffset(cases: variant.cases.map(\.type)))!
try store(
type: payloadType, value: payload, pointer: pointer.advanced(by: offset),
storing: &storing, lowering: &lowering
)
})
default:
fatalError("TODO: storing \"\(type)\" is unimplemented")
}
}
}
|