mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
SwiftTL: add CodeGenerator.generateLayered for per-layer output
Emits one {apiPrefix}Layer{N}.swift file per layer: file-scope
dispatch table, public struct {apiPrefix}{N} with parse/parseVector/
serializeObject, nested public enums for each sum type using the
inline-args shape. Int256 dispatch entry emitted only when a layer's
constructors reference it. Reuses typeReferenceRepresentation /
generateFieldSerialization / generateFieldParsing unchanged, passing
the struct name as the apiPrefix so nested type refs render correctly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
10054efd81
commit
803bf23269
1 changed files with 319 additions and 1 deletions
|
|
@ -198,7 +198,325 @@ enum CodeGenerator {
|
|||
|
||||
return files
|
||||
}
|
||||
|
||||
|
||||
static func generateLayered(
|
||||
apiPrefix: String,
|
||||
layerNumber: Int,
|
||||
types: [Resolver.SumType]
|
||||
) throws -> (filename: String, source: String) {
|
||||
let structName = "\(apiPrefix)\(layerNumber)"
|
||||
let filename = "\(apiPrefix)Layer\(layerNumber).swift"
|
||||
|
||||
// All nested type refs (inside the struct) use `structName` as their prefix —
|
||||
// `apiPrefix` is used only to compute `structName` and `filename`. Helpers like
|
||||
// typeReferenceRepresentation and generateFieldParsing get `structName`, not
|
||||
// `apiPrefix`, so e.g. fields render as `media: SecretApi8.DecryptedMessageMedia`.
|
||||
|
||||
var typeMap: [QualifiedName: Resolver.SumType] = [:]
|
||||
for type in types {
|
||||
typeMap[type.name] = type
|
||||
}
|
||||
|
||||
// Detect whether any constructor argument uses Int256; if so, we need the int256 parser entry.
|
||||
var usesInt256 = false
|
||||
outer: for type in types {
|
||||
for (_, constructor) in type.constructors {
|
||||
for argument in constructor.arguments {
|
||||
if containsInt256(argument.type) { usesInt256 = true; break outer }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var writer = CodeWriter()
|
||||
writer.line()
|
||||
|
||||
// File-scope dispatch table
|
||||
writer.line("fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {")
|
||||
writer.indent()
|
||||
writer.line("var dict: [Int32 : (BufferReader) -> Any?] = [:]")
|
||||
writer.line("dict[-1471112230] = { return $0.readInt32() }")
|
||||
writer.line("dict[570911930] = { return $0.readInt64() }")
|
||||
writer.line("dict[571523412] = { return $0.readDouble() }")
|
||||
writer.line("dict[-1255641564] = { return parseString($0) }")
|
||||
if usesInt256 {
|
||||
writer.line("dict[0x0929C32F] = { return parseInt256($0) }")
|
||||
}
|
||||
|
||||
let sortedTypes = types.sorted(by: { $0.name < $1.name })
|
||||
for type in sortedTypes {
|
||||
let sortedConstructors = type.constructors.values.sorted(by: { $0.name < $1.name })
|
||||
for constructor in sortedConstructors {
|
||||
writer.line("dict[\(Int32(bitPattern: constructor.id))] = { return \(structName).\(type.name).parse_\(constructor.name.value)($0) }")
|
||||
}
|
||||
}
|
||||
writer.line("return dict")
|
||||
writer.dedent()
|
||||
writer.line("}()")
|
||||
writer.line()
|
||||
|
||||
// public struct {apiPrefix}{N} {
|
||||
writer.line("public struct \(structName) {")
|
||||
writer.indent()
|
||||
|
||||
// public static func parse(_ buffer: Buffer) -> Any?
|
||||
writer.line("public static func parse(_ buffer: Buffer) -> Any? {")
|
||||
writer.indent()
|
||||
writer.line("let reader = BufferReader(buffer)")
|
||||
writer.line("if let signature = reader.readInt32() {")
|
||||
writer.indent()
|
||||
writer.line("return parse(reader, signature: signature)")
|
||||
writer.dedent()
|
||||
writer.line("}")
|
||||
writer.line("return nil")
|
||||
writer.dedent()
|
||||
writer.line("}")
|
||||
writer.line()
|
||||
|
||||
// fileprivate static func parse(_ reader: BufferReader, signature: Int32) -> Any?
|
||||
writer.line("fileprivate static func parse(_ reader: BufferReader, signature: Int32) -> Any? {")
|
||||
writer.indent()
|
||||
writer.line("if let parser = parsers[signature] {")
|
||||
writer.indent()
|
||||
writer.line("return parser(reader)")
|
||||
writer.dedent()
|
||||
writer.line("}")
|
||||
writer.line("else {")
|
||||
writer.indent()
|
||||
writer.line("telegramApiLog(\"Type constructor \\(String(signature, radix: 16, uppercase: false)) not found\")")
|
||||
writer.line("return nil")
|
||||
writer.dedent()
|
||||
writer.line("}")
|
||||
writer.dedent()
|
||||
writer.line("}")
|
||||
writer.line()
|
||||
|
||||
// fileprivate static func parseVector
|
||||
writer.line("fileprivate static func parseVector<T>(_ reader: BufferReader, elementSignature: Int32, elementType: T.Type) -> [T]? {")
|
||||
writer.indent()
|
||||
writer.line("if let count = reader.readInt32() {")
|
||||
writer.indent()
|
||||
writer.line("var array = [T]()")
|
||||
writer.line("var i: Int32 = 0")
|
||||
writer.line("while i < count {")
|
||||
writer.indent()
|
||||
writer.line("var signature = elementSignature")
|
||||
writer.line("if elementSignature == 0 {")
|
||||
writer.indent()
|
||||
writer.line("if let unboxedSignature = reader.readInt32() {")
|
||||
writer.indent()
|
||||
writer.line("signature = unboxedSignature")
|
||||
writer.dedent()
|
||||
writer.line("}")
|
||||
writer.line("else {")
|
||||
writer.indent()
|
||||
writer.line("return nil")
|
||||
writer.dedent()
|
||||
writer.line("}")
|
||||
writer.dedent()
|
||||
writer.line("}")
|
||||
writer.line("if let item = \(structName).parse(reader, signature: signature) as? T {")
|
||||
writer.indent()
|
||||
writer.line("array.append(item)")
|
||||
writer.dedent()
|
||||
writer.line("}")
|
||||
writer.line("else {")
|
||||
writer.indent()
|
||||
writer.line("return nil")
|
||||
writer.dedent()
|
||||
writer.line("}")
|
||||
writer.line("i += 1")
|
||||
writer.dedent()
|
||||
writer.line("}")
|
||||
writer.line("return array")
|
||||
writer.dedent()
|
||||
writer.line("}")
|
||||
writer.line("return nil")
|
||||
writer.dedent()
|
||||
writer.line("}")
|
||||
writer.line()
|
||||
|
||||
// public static func serializeObject
|
||||
writer.line("public static func serializeObject(_ object: Any, buffer: Buffer, boxed: Swift.Bool) {")
|
||||
writer.indent()
|
||||
writer.line("switch object {")
|
||||
for type in sortedTypes {
|
||||
writer.line("case let _1 as \(structName).\(type.name):")
|
||||
writer.indent()
|
||||
writer.line("_1.serialize(buffer, boxed)")
|
||||
writer.dedent()
|
||||
}
|
||||
writer.line("default:")
|
||||
writer.indent()
|
||||
writer.line("break")
|
||||
writer.dedent()
|
||||
writer.line("}")
|
||||
writer.dedent()
|
||||
writer.line("}")
|
||||
writer.line()
|
||||
|
||||
// Nested public enum <TypeName> { ... } for each type
|
||||
for type in sortedTypes {
|
||||
try emitLayeredType(writer: &writer, structName: structName, type: type, typeMap: typeMap)
|
||||
}
|
||||
|
||||
writer.dedent()
|
||||
writer.line("}") // close public struct
|
||||
|
||||
return (filename, writer.output())
|
||||
}
|
||||
|
||||
private static func containsInt256(_ type: Resolver.TypeReference) -> Bool {
|
||||
switch type {
|
||||
case .int256:
|
||||
return true
|
||||
case .bareVector(let element), .boxedVector(let element):
|
||||
return containsInt256(element)
|
||||
case .int32, .int64, .double, .bytes, .string, .bool, .boolTrue, .bareConstructor, .boxedType:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private static func emitLayeredType(
|
||||
writer: inout CodeWriter,
|
||||
structName: String,
|
||||
type: Resolver.SumType,
|
||||
typeMap: [QualifiedName: Resolver.SumType]
|
||||
) throws {
|
||||
let sortedConstructors = type.constructors.values.sorted(by: { $0.name < $1.name })
|
||||
|
||||
let indirectPrefix = try type.hasDirectReference(to: [type], typeMap: typeMap) ? "indirect " : ""
|
||||
writer.line("\(indirectPrefix)public enum \(type.name.value) {")
|
||||
writer.indent()
|
||||
|
||||
// case <ctor>(<args>) -- inline-args shape
|
||||
for constructor in sortedConstructors {
|
||||
var argumentsString = ""
|
||||
for argument in constructor.arguments {
|
||||
if case .boolTrue = argument.type { continue }
|
||||
if !argumentsString.isEmpty { argumentsString.append(", ") }
|
||||
argumentsString.append(argument.name.camelCased)
|
||||
argumentsString.append(": ")
|
||||
argumentsString.append(typeReferenceRepresentation(structName, argument.type))
|
||||
if argument.condition != nil { argumentsString.append("?") }
|
||||
}
|
||||
writer.line("case \(constructor.name.value)\(argumentsString.isEmpty ? "" : "(\(argumentsString))")")
|
||||
}
|
||||
writer.line()
|
||||
|
||||
// public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool)
|
||||
writer.line("public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {")
|
||||
writer.indent()
|
||||
writer.line("switch self {")
|
||||
for constructor in sortedConstructors {
|
||||
var bindString = ""
|
||||
for argument in constructor.arguments {
|
||||
if case .boolTrue = argument.type { continue }
|
||||
if !bindString.isEmpty { bindString.append(", ") }
|
||||
bindString.append("let ")
|
||||
bindString.append(argument.name.camelCasedAndEscaped)
|
||||
}
|
||||
writer.line("case .\(constructor.name.value)\(bindString.isEmpty ? "" : "(\(bindString))"):")
|
||||
writer.indent()
|
||||
writer.line("if boxed {")
|
||||
writer.indent()
|
||||
writer.line("buffer.appendInt32(\(Int32(bitPattern: constructor.id)))")
|
||||
writer.dedent()
|
||||
writer.line("}")
|
||||
|
||||
for argument in constructor.arguments {
|
||||
if case .boolTrue = argument.type { continue }
|
||||
var argumentAccessor = "\(argument.name.camelCasedAndEscaped)"
|
||||
if let condition = argument.condition {
|
||||
writer.line("if Int(\(condition.fieldName)) & Int(1 << \(condition.bitIndex)) != 0 {")
|
||||
writer.indent()
|
||||
argumentAccessor.append("!")
|
||||
generateFieldSerialization(writer: &writer, argument: argument, argumentAccessor: argumentAccessor)
|
||||
writer.dedent()
|
||||
writer.line("}")
|
||||
} else {
|
||||
generateFieldSerialization(writer: &writer, argument: argument, argumentAccessor: argumentAccessor)
|
||||
}
|
||||
}
|
||||
writer.line("break")
|
||||
writer.dedent()
|
||||
}
|
||||
writer.line("}")
|
||||
writer.dedent()
|
||||
writer.line("}")
|
||||
writer.line()
|
||||
|
||||
// fileprivate static func parse_<ctor>(_ reader: BufferReader) -> <TypeName>?
|
||||
for constructor in sortedConstructors {
|
||||
writer.line("fileprivate static func parse_\(constructor.name.value)(_ reader: BufferReader) -> \(type.name.value)? {")
|
||||
writer.indent()
|
||||
if constructor.arguments.contains(where: { if case .boolTrue = $0.type { return false } else { return true } }) {
|
||||
var argumentIndex = 0
|
||||
var argumentCheckString = ""
|
||||
var argumentCollectionString = ""
|
||||
for argument in constructor.arguments {
|
||||
if case .boolTrue = argument.type { continue }
|
||||
|
||||
writer.line("var _\(argumentIndex + 1): \(typeReferenceRepresentation(structName, argument.type))?")
|
||||
|
||||
if let condition = argument.condition {
|
||||
guard let fieldIndex = constructor.arguments.filter({ if case .boolTrue = $0.type { return false } else { return true } }).firstIndex(where: { $0.name == condition.fieldName }) else {
|
||||
throw CodeGenerationError(text: "Condition field \(condition.fieldName) not found")
|
||||
}
|
||||
writer.line("if Int(_\(fieldIndex + 1)!) & Int(1 << \(condition.bitIndex)) != 0 {")
|
||||
writer.indent()
|
||||
try generateFieldParsing(apiPrefix: structName, writer: &writer, typeMap: typeMap, argument: argument, argumentAccessor: "_\(argumentIndex + 1)")
|
||||
writer.dedent()
|
||||
writer.line("}")
|
||||
} else {
|
||||
try generateFieldParsing(apiPrefix: structName, writer: &writer, typeMap: typeMap, argument: argument, argumentAccessor: "_\(argumentIndex + 1)")
|
||||
}
|
||||
|
||||
if !argumentCheckString.isEmpty { argumentCheckString.append(" && ") }
|
||||
argumentCheckString.append("_c\(argumentIndex + 1)")
|
||||
|
||||
if !argumentCollectionString.isEmpty { argumentCollectionString.append(", ") }
|
||||
argumentCollectionString.append("\(argument.name.camelCased): _\(argumentIndex + 1)")
|
||||
if argument.condition == nil { argumentCollectionString.append("!") }
|
||||
|
||||
argumentIndex += 1
|
||||
}
|
||||
|
||||
var checkIndex = 0
|
||||
for argument in constructor.arguments {
|
||||
if case .boolTrue = argument.type { continue }
|
||||
if let condition = argument.condition {
|
||||
guard let fieldIndex = constructor.arguments.filter({ if case .boolTrue = $0.type { return false } else { return true } }).firstIndex(where: { $0.name == condition.fieldName }) else {
|
||||
throw CodeGenerationError(text: "Condition field \(condition.fieldName) not found")
|
||||
}
|
||||
writer.line("let _c\(checkIndex + 1) = (Int(_\(fieldIndex + 1)!) & Int(1 << \(condition.bitIndex)) == 0) || _\(checkIndex + 1) != nil")
|
||||
} else {
|
||||
writer.line("let _c\(checkIndex + 1) = _\(checkIndex + 1) != nil")
|
||||
}
|
||||
checkIndex += 1
|
||||
}
|
||||
|
||||
writer.line("if \(argumentCheckString) {")
|
||||
writer.indent()
|
||||
writer.line("return \(structName).\(type.name).\(constructor.name.value)\(argumentCollectionString.isEmpty ? "" : "(\(argumentCollectionString))")")
|
||||
writer.dedent()
|
||||
writer.line("}")
|
||||
writer.line("else {")
|
||||
writer.indent()
|
||||
writer.line("return nil")
|
||||
writer.dedent()
|
||||
writer.line("}")
|
||||
} else {
|
||||
writer.line("return \(structName).\(type.name).\(constructor.name.value)")
|
||||
}
|
||||
writer.dedent()
|
||||
writer.line("}")
|
||||
}
|
||||
|
||||
writer.dedent()
|
||||
writer.line("}")
|
||||
writer.line()
|
||||
}
|
||||
|
||||
private static func generateMainFile(apiPrefix: String, types: [Resolver.SumType], functions: [Resolver.Function], constructorOrder: [(typeName: QualifiedName, constructorName: String)]) throws -> String {
|
||||
var writer = CodeWriter()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue