From 406d21879f454363573b57947816a6109528c0fc Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Thu, 26 Jun 2025 18:31:50 +0800 Subject: [PATCH 01/10] Add SymbolIndexStore --- Package.resolved | 11 +- Package.swift | 2 + Sources/Demangle/Utils/Extensions.swift | 4 +- .../MachOPointer/SymbolOrElementPointer.swift | 2 +- .../Models/Mangling/MangledName.swift | 2 +- .../Models/Protocol/ProtocolRequirement.swift | 4 +- .../Models/Protocol/ResilientWitness.swift | 4 +- .../MethodDefaultOverrideDescriptor.swift | 4 +- .../Type/Class/Method/MethodDescriptor.swift | 4 +- .../Method/MethodOverrideDescriptor.swift | 4 +- .../Type/TypeContextDescriptorProtocol.swift | 4 +- .../Utils/MetadataReader.swift | 6 +- Sources/MachOSymbols/MachOFile+Symbol.swift | 12 +- .../{MachOSymbol.swift => Symbol.swift} | 2 +- ...chOSymbolCache.swift => SymbolCache.swift} | 74 +++---- Sources/MachOSymbols/SymbolIndexStore.swift | 183 ++++++++++++++++++ Sources/MachOSymbols/SymbolOrElement.swift | 4 +- .../SwiftDump/Dumpable+/Enum+Dumpable.swift | 54 +++++- .../Dumpable+/Protocol+Dumpable.swift | 2 +- .../SwiftDump/Dumpable+/Struct+Dumpable.swift | 46 +++++ .../SymbolDemangleTests.swift | 30 ++- 21 files changed, 385 insertions(+), 73 deletions(-) rename Sources/MachOSymbols/{MachOSymbol.swift => Symbol.swift} (95%) rename Sources/MachOSymbols/{MachOSymbolCache.swift => SymbolCache.swift} (54%) create mode 100644 Sources/MachOSymbols/SymbolIndexStore.swift diff --git a/Package.resolved b/Package.resolved index 0d83889c..a02d807d 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "11eaed82e8eb6edd730d4cf82f8bce43e2351a0cf3ad8750d9cb645751ff3817", + "originHash" : "c063655621919263a7b3358360d2a18975b69b9fb2af587221263a629ceffba2", "pins" : [ { "identity" : "associatedobject", @@ -46,6 +46,15 @@ "version" : "1.5.1" } }, + { + "identity" : "swift-collections", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-collections", + "state" : { + "revision" : "c1805596154bb3a265fd91b8ac0c4433b4348fb0", + "version" : "1.2.0" + } + }, { "identity" : "swift-fileio", "kind" : "remoteSourceControl", diff --git a/Package.swift b/Package.swift index 5829a090..0a3e25ec 100644 --- a/Package.swift +++ b/Package.swift @@ -116,6 +116,7 @@ let package = Package( .package(url: "https://github.com/apple/swift-argument-parser", from: "1.5.1"), .package(url: "https://github.com/onevcat/Rainbow", from: "4.0.0"), .package(url: "https://github.com/Mx-Iris/FrameworkToolbox", from: "0.3.0"), + .package(url: "https://github.com/apple/swift-collections", from: "1.2.0"), ], targets: [ .target( @@ -156,6 +157,7 @@ let package = Package( "MachOReading", "MachOMacro", "Demangle", + .product(name: "OrderedCollections", package: "swift-collections"), ] ), diff --git a/Sources/Demangle/Utils/Extensions.swift b/Sources/Demangle/Utils/Extensions.swift index 3ffe6832..6a263743 100644 --- a/Sources/Demangle/Utils/Extensions.swift +++ b/Sources/Demangle/Utils/Extensions.swift @@ -25,11 +25,11 @@ extension SemanticString { } extension Array { - func at(_ index: Int) -> Element? { + package func at(_ index: Int) -> Element? { return indices.contains(index) ? self[index] : nil } - func slice(_ from: Int, _ to: Int) -> ArraySlice { + package func slice(_ from: Int, _ to: Int) -> ArraySlice { if from > to || from > endIndex || to < startIndex { return ArraySlice() } else { diff --git a/Sources/MachOPointer/SymbolOrElementPointer.swift b/Sources/MachOPointer/SymbolOrElementPointer.swift index 48c79706..8bcda8b4 100644 --- a/Sources/MachOPointer/SymbolOrElementPointer.swift +++ b/Sources/MachOPointer/SymbolOrElementPointer.swift @@ -5,7 +5,7 @@ import MachOSymbols public enum SymbolOrElementPointer: RelativeIndirectType { public typealias Resolved = SymbolOrElement - case symbol(MachOSymbol) + case symbol(Symbol) case address(UInt64) public func resolveOffset(in machOFile: MachOFile) -> Int { diff --git a/Sources/MachOSwiftSection/Models/Mangling/MangledName.swift b/Sources/MachOSwiftSection/Models/Mangling/MangledName.swift index 83e37cc2..5b582464 100644 --- a/Sources/MachOSwiftSection/Models/Mangling/MangledName.swift +++ b/Sources/MachOSwiftSection/Models/Mangling/MangledName.swift @@ -60,7 +60,7 @@ public struct MangledName { self.endOffset = endOffset } - init(unsolvedSymbol: MachOSymbol) { + init(unsolvedSymbol: Symbol) { self.init(elements: [.string(unsolvedSymbol.stringValue)], startOffset: unsolvedSymbol.offset, endOffset: nil) } diff --git a/Sources/MachOSwiftSection/Models/Protocol/ProtocolRequirement.swift b/Sources/MachOSwiftSection/Models/Protocol/ProtocolRequirement.swift index 0515c9e8..65613eae 100644 --- a/Sources/MachOSwiftSection/Models/Protocol/ProtocolRequirement.swift +++ b/Sources/MachOSwiftSection/Models/Protocol/ProtocolRequirement.swift @@ -5,7 +5,7 @@ import MachOFoundation public struct ProtocolRequirement: ResolvableLocatableLayoutWrapper { public struct Layout: Sendable { public let flags: ProtocolRequirementFlags - public let defaultImplementation: RelativeDirectPointer + public let defaultImplementation: RelativeDirectPointer } public let offset: Int @@ -20,7 +20,7 @@ public struct ProtocolRequirement: ResolvableLocatableLayoutWrapper { @MachOImageAllMembersGenerator extension ProtocolRequirement { - public func defaultImplementationSymbol(in machOFile: MachOFile) throws -> MachOSymbol? { + public func defaultImplementationSymbol(in machOFile: MachOFile) throws -> Symbol? { guard layout.defaultImplementation.isValid else { return nil } return try layout.defaultImplementation.resolve(from: offset(of: \.defaultImplementation), in: machOFile) } diff --git a/Sources/MachOSwiftSection/Models/Protocol/ResilientWitness.swift b/Sources/MachOSwiftSection/Models/Protocol/ResilientWitness.swift index 6364587b..c5abfc53 100644 --- a/Sources/MachOSwiftSection/Models/Protocol/ResilientWitness.swift +++ b/Sources/MachOSwiftSection/Models/Protocol/ResilientWitness.swift @@ -6,7 +6,7 @@ import MachOFoundation public struct ResilientWitness: ResolvableLocatableLayoutWrapper { public struct Layout: Sendable { public let requirement: RelativeProtocolRequirementPointer - public let implementation: RelativeDirectPointer + public let implementation: RelativeDirectPointer } public let offset: Int @@ -26,7 +26,7 @@ extension ResilientWitness { return try layout.requirement.resolve(from: offset(of: \.requirement), in: machOFile).asOptional } - public func implementationSymbol(in machOFile: MachOFile) throws -> MachOSymbol? { + public func implementationSymbol(in machOFile: MachOFile) throws -> Symbol? { return try layout.implementation.resolve(from: offset(of: \.implementation), in: machOFile) } } diff --git a/Sources/MachOSwiftSection/Models/Type/Class/Method/MethodDefaultOverrideDescriptor.swift b/Sources/MachOSwiftSection/Models/Type/Class/Method/MethodDefaultOverrideDescriptor.swift index 54a379d8..09f9395f 100644 --- a/Sources/MachOSwiftSection/Models/Type/Class/Method/MethodDefaultOverrideDescriptor.swift +++ b/Sources/MachOSwiftSection/Models/Type/Class/Method/MethodDefaultOverrideDescriptor.swift @@ -7,7 +7,7 @@ public struct MethodDefaultOverrideDescriptor: ResolvableLocatableLayoutWrapper public struct Layout: Sendable { public let replacement: RelativeMethodDescriptorPointer public let original: RelativeMethodDescriptorPointer - public let implementation: RelativeDirectPointer + public let implementation: RelativeDirectPointer } public var layout: Layout @@ -23,7 +23,7 @@ public struct MethodDefaultOverrideDescriptor: ResolvableLocatableLayoutWrapper @MachOImageAllMembersGenerator extension MethodDefaultOverrideDescriptor { //@MachOImageGenerator - public func implementationSymbol(in machOFile: MachOFile) throws -> MachOSymbol? { + public func implementationSymbol(in machOFile: MachOFile) throws -> Symbol? { return try layout.implementation.resolve(from: offset(of: \.implementation), in: machOFile) } } diff --git a/Sources/MachOSwiftSection/Models/Type/Class/Method/MethodDescriptor.swift b/Sources/MachOSwiftSection/Models/Type/Class/Method/MethodDescriptor.swift index 026507dc..8f62c40a 100644 --- a/Sources/MachOSwiftSection/Models/Type/Class/Method/MethodDescriptor.swift +++ b/Sources/MachOSwiftSection/Models/Type/Class/Method/MethodDescriptor.swift @@ -6,7 +6,7 @@ import MachOFoundation public struct MethodDescriptor: ResolvableLocatableLayoutWrapper { public struct Layout: Sendable { public let flags: MethodDescriptorFlags - public let implementation: RelativeDirectPointer + public let implementation: RelativeDirectPointer } public var layout: Layout @@ -22,7 +22,7 @@ public struct MethodDescriptor: ResolvableLocatableLayoutWrapper { @MachOImageAllMembersGenerator extension MethodDescriptor { //@MachOImageGenerator - public func implementationSymbol(in machOFile: MachOFile) throws -> MachOSymbol? { + public func implementationSymbol(in machOFile: MachOFile) throws -> Symbol? { return try layout.implementation.resolve(from: offset(of: \.implementation), in: machOFile) } } diff --git a/Sources/MachOSwiftSection/Models/Type/Class/Method/MethodOverrideDescriptor.swift b/Sources/MachOSwiftSection/Models/Type/Class/Method/MethodOverrideDescriptor.swift index 735c4f2c..35a01e91 100644 --- a/Sources/MachOSwiftSection/Models/Type/Class/Method/MethodOverrideDescriptor.swift +++ b/Sources/MachOSwiftSection/Models/Type/Class/Method/MethodOverrideDescriptor.swift @@ -7,7 +7,7 @@ public struct MethodOverrideDescriptor: ResolvableLocatableLayoutWrapper { public struct Layout: Sendable { public let `class`: RelativeContextPointer public let method: RelativeMethodDescriptorPointer - public let implementation: RelativeDirectPointer + public let implementation: RelativeDirectPointer } public var layout: Layout @@ -27,7 +27,7 @@ extension MethodOverrideDescriptor { return try layout.method.resolve(from: offset(of: \.method), in: machOFile).asOptional } - public func implementationSymbol(in machOFile: MachOFile) throws -> MachOSymbol? { + public func implementationSymbol(in machOFile: MachOFile) throws -> Symbol? { return try layout.implementation.resolve(from: offset(of: \.implementation), in: machOFile) } } diff --git a/Sources/MachOSwiftSection/Models/Type/TypeContextDescriptorProtocol.swift b/Sources/MachOSwiftSection/Models/Type/TypeContextDescriptorProtocol.swift index 9fafaf36..e0d49768 100644 --- a/Sources/MachOSwiftSection/Models/Type/TypeContextDescriptorProtocol.swift +++ b/Sources/MachOSwiftSection/Models/Type/TypeContextDescriptorProtocol.swift @@ -7,8 +7,8 @@ public protocol TypeContextDescriptorProtocol: NamedContextDescriptorProtocol wh @MachOImageAllMembersGenerator extension TypeContextDescriptorProtocol { - public func accessFunction(in machOFile: MachOFile) throws -> MachOSymbol? { - let ptr = RelativeDirectPointer(relativeOffset: layout.accessFunctionPtr) + public func accessFunction(in machOFile: MachOFile) throws -> Symbol? { + let ptr = RelativeDirectPointer(relativeOffset: layout.accessFunctionPtr) return try ptr.resolve(from: offset + layout.offset(of: .accessFunctionPtr), in: machOFile) } diff --git a/Sources/MachOSwiftSection/Utils/MetadataReader.swift b/Sources/MachOSwiftSection/Utils/MetadataReader.swift index 011c2eaa..09d5a952 100644 --- a/Sources/MachOSwiftSection/Utils/MetadataReader.swift +++ b/Sources/MachOSwiftSection/Utils/MetadataReader.swift @@ -14,11 +14,11 @@ public struct MetadataReader { return try demangle(for: mangledName, kind: .symbol, in: machOFile) } - public static func demangleType(for unsolvedSymbol: MachOSymbol, in machOFile: MachOFile) throws -> Node { + public static func demangleType(for unsolvedSymbol: Symbol, in machOFile: MachOFile) throws -> Node { return try required(buildContextManglingForSymbol(unsolvedSymbol, in: machOFile)) } - public static func demangleSymbol(for unsolvedSymbol: MachOSymbol, in machOFile: MachOFile) throws -> Node { + public static func demangleSymbol(for unsolvedSymbol: Symbol, in machOFile: MachOFile) throws -> Node { return try demangle(for: .init(unsolvedSymbol: unsolvedSymbol), kind: .symbol, in: machOFile) } @@ -289,7 +289,7 @@ public struct MetadataReader { return demangling } - private static func buildContextManglingForSymbol(_ symbol: MachOSymbol, in machOFile: MachOFile) throws -> Node? { + private static func buildContextManglingForSymbol(_ symbol: Symbol, in machOFile: MachOFile) throws -> Node? { var demangler = Demangler(scalars: symbol.stringValue.unicodeScalars) var demangledSymbol = try demangler.demangleSymbol() if demangledSymbol.kind == .global { diff --git a/Sources/MachOSymbols/MachOFile+Symbol.swift b/Sources/MachOSymbols/MachOFile+Symbol.swift index 2e0ef336..82347b24 100644 --- a/Sources/MachOSymbols/MachOFile+Symbol.swift +++ b/Sources/MachOSymbols/MachOFile+Symbol.swift @@ -2,15 +2,15 @@ import MachOKit import MachOExtensions extension MachOFile { - package func findSymbol(offset: Int) -> MachOSymbol? { - MachOSymbolCache.shared.createCacheIfNeeded(for: self) - return MachOSymbolCache.shared.symbol(for: offset, in: self) + package func findSymbol(offset: Int) -> MachOSymbols.Symbol? { + SymbolCache.shared.createCacheIfNeeded(for: self) + return SymbolCache.shared.symbol(for: offset, in: self) } } extension MachOImage { - package func findSymbol(offset: Int) -> MachOSymbol? { - MachOSymbolCache.shared.createCacheIfNeeded(for: self) - return MachOSymbolCache.shared.symbol(for: offset, in: self) + package func findSymbol(offset: Int) -> MachOSymbols.Symbol? { + SymbolCache.shared.createCacheIfNeeded(for: self) + return SymbolCache.shared.symbol(for: offset, in: self) } } diff --git a/Sources/MachOSymbols/MachOSymbol.swift b/Sources/MachOSymbols/Symbol.swift similarity index 95% rename from Sources/MachOSymbols/MachOSymbol.swift rename to Sources/MachOSymbols/Symbol.swift index ea2da4c8..2f0c0c3c 100644 --- a/Sources/MachOSymbols/MachOSymbol.swift +++ b/Sources/MachOSymbols/Symbol.swift @@ -3,7 +3,7 @@ import MachOMacro import MachOExtensions import MachOReading -public struct MachOSymbol: Resolvable { +public struct Symbol: Resolvable, Hashable { enum Error: Swift.Error { case symbolNotFound } diff --git a/Sources/MachOSymbols/MachOSymbolCache.swift b/Sources/MachOSymbols/SymbolCache.swift similarity index 54% rename from Sources/MachOSymbols/MachOSymbolCache.swift rename to Sources/MachOSymbols/SymbolCache.swift index 632346ff..b36faf9a 100644 --- a/Sources/MachOSymbols/MachOSymbolCache.swift +++ b/Sources/MachOSymbols/SymbolCache.swift @@ -2,35 +2,47 @@ import MachOKit import MachOExtensions import Demangle -package final class MachOSymbolCache { - package static let shared = MachOSymbolCache() +enum MachOTargetIdentifier: Hashable { + case image(UnsafeRawPointer) + case file(String) +} + +package final class SymbolCache { + package static let shared = SymbolCache() private let memoryPressureMonitor = MemoryPressureMonitor() private init() { memoryPressureMonitor.memoryWarningHandler = { [weak self] in - self?.entryByIdentifier.removeAll() + self?.cacheEntryByIdentifier.removeAll() } memoryPressureMonitor.memoryCriticalHandler = { [weak self] in - self?.entryByIdentifier.removeAll() + self?.cacheEntryByIdentifier.removeAll() } memoryPressureMonitor.startMonitoring() } - private enum CacheIdentifier: Hashable { - case image(UnsafeRawPointer) - case file(String) - } + private typealias CacheEntry = [Int: Symbol] - private typealias CacheEntry = [Int: MachOSymbol] + private var cacheEntryByIdentifier: [MachOTargetIdentifier: CacheEntry] = [:] - private var entryByIdentifier: [CacheIdentifier: CacheEntry] = [:] + @discardableResult + package func createCacheIfNeeded(for machOImage: MachOImage, isForced: Bool = false) -> Bool { + let identifier = MachOTargetIdentifier.image(machOImage.ptr) + return createCacheIfNeeded(for: identifier, in: machOImage, isForced: isForced) + } + + @discardableResult + package func createCacheIfNeeded(for machOFile: MachOFile, isForced: Bool = false) -> Bool { + let identifier = MachOTargetIdentifier.file(machOFile.imagePath) + return createCacheIfNeeded(for: identifier, in: machOFile, isForced: isForced) + } @discardableResult - private func createCacheIfNeeded(for identifier: CacheIdentifier, in machO: MachO, isForced: Bool = false) -> Bool { - guard isForced || (entryByIdentifier[identifier]?.isEmpty ?? true) else { return false } + private func createCacheIfNeeded(for identifier: MachOTargetIdentifier, in machO: MachO, isForced: Bool = false) -> Bool { + guard isForced || (cacheEntryByIdentifier[identifier]?.isEmpty ?? true) else { return false } var cacheEntry: CacheEntry = [:] for symbol in machO.symbols where symbol.name.isSwiftSymbol { var offset = symbol.offset @@ -48,48 +60,36 @@ package final class MachOSymbolCache { cacheEntry[offset] = .init(offset: offset, stringValue: exportedSymbol.name) } } - entryByIdentifier[identifier] = cacheEntry + cacheEntryByIdentifier[identifier] = cacheEntry return true } - private func removeCache(for identifier: CacheIdentifier) { - entryByIdentifier.removeValue(forKey: identifier) - } - - func removeCache(for machOImage: MachOImage) { - let identifier = CacheIdentifier.image(machOImage.ptr) + package func removeCache(for machOImage: MachOImage) { + let identifier = MachOTargetIdentifier.image(machOImage.ptr) removeCache(for: identifier) } - func removeCache(for machOFile: MachOFile) { - let identifier = CacheIdentifier.file(machOFile.imagePath) + package func removeCache(for machOFile: MachOFile) { + let identifier = MachOTargetIdentifier.file(machOFile.imagePath) removeCache(for: identifier) } - @discardableResult - func createCacheIfNeeded(for machOImage: MachOImage, isForced: Bool = false) -> Bool { - let identifier = CacheIdentifier.image(machOImage.ptr) - return createCacheIfNeeded(for: identifier, in: machOImage, isForced: isForced) - } - - @discardableResult - func createCacheIfNeeded(for machOFile: MachOFile, isForced: Bool = false) -> Bool { - let identifier = CacheIdentifier.file(machOFile.imagePath) - return createCacheIfNeeded(for: identifier, in: machOFile, isForced: isForced) + private func removeCache(for identifier: MachOTargetIdentifier) { + cacheEntryByIdentifier.removeValue(forKey: identifier) } - package func symbol(for offset: Int, in machOImage: MachOImage) -> MachOSymbol? { - let identifier = CacheIdentifier.image(machOImage.ptr) + package func symbol(for offset: Int, in machOImage: MachOImage) -> Symbol? { + let identifier = MachOTargetIdentifier.image(machOImage.ptr) return symbol(for: offset, with: identifier, in: machOImage) } - package func symbol(for offset: Int, in machOFile: MachOFile) -> MachOSymbol? { - let identifier = CacheIdentifier.file(machOFile.imagePath) + package func symbol(for offset: Int, in machOFile: MachOFile) -> Symbol? { + let identifier = MachOTargetIdentifier.file(machOFile.imagePath) return symbol(for: offset, with: identifier, in: machOFile) } - private func symbol(for offset: Int, with identifier: CacheIdentifier, in machO: MachO) -> MachOSymbol? { - if let symbol = entryByIdentifier[identifier, default: [:]][offset] { + private func symbol(for offset: Int, with identifier: MachOTargetIdentifier, in machO: MachO) -> Symbol? { + if let symbol = cacheEntryByIdentifier[identifier, default: [:]][offset] { return symbol } else { return nil diff --git a/Sources/MachOSymbols/SymbolIndexStore.swift b/Sources/MachOSymbols/SymbolIndexStore.swift new file mode 100644 index 00000000..491fa438 --- /dev/null +++ b/Sources/MachOSymbols/SymbolIndexStore.swift @@ -0,0 +1,183 @@ +import Foundation +import MachOKit +import MachOExtensions +import Demangle +import OrderedCollections + +package final class SymbolIndexStore { + package enum IndexKind: Hashable { + package enum SubKind: Hashable, CaseIterable { + case function + case functionInExtension + case staticFunction + case staticFunctionInExtension + case variable + case variableInExtension + case staticVariable + case staticVariableInExtension + } + + case `enum`(SubKind) + case `struct`(SubKind) + case `class`(SubKind) + } + + package static let shared = SymbolIndexStore() + + private let memoryPressureMonitor = MemoryPressureMonitor() + + private init() { + memoryPressureMonitor.memoryWarningHandler = { [weak self] in + self?.indexEntryByIdentifier.removeAll() + } + + memoryPressureMonitor.memoryCriticalHandler = { [weak self] in + self?.indexEntryByIdentifier.removeAll() + } + + memoryPressureMonitor.startMonitoring() + } + + private struct IndexEntry { + var isIndexed: Bool = false + var symbolsByKind: [IndexKind: [String: [Symbol]]] = [:] + } + + private var indexEntryByIdentifier: [MachOTargetIdentifier: IndexEntry] = [:] + + package func removeIndexs(for machOImage: MachOImage) { + let identifier = MachOTargetIdentifier.image(machOImage.ptr) + removeIndexs(for: identifier) + } + + package func removeIndexs(for machOFile: MachOFile) { + let identifier = MachOTargetIdentifier.file(machOFile.imagePath) + removeIndexs(for: identifier) + } + + private func removeIndexs(for identifier: MachOTargetIdentifier) { + indexEntryByIdentifier.removeValue(forKey: identifier) + } + + @discardableResult + package func startIndexingIfNeeded(for machOImage: MachOImage) -> Bool { + let identifier = MachOTargetIdentifier.image(machOImage.ptr) + return startIndexingIfNeeded(for: identifier, in: machOImage) + } + + @discardableResult + package func startIndexingIfNeeded(for machOFile: MachOFile) -> Bool { + let identifier = MachOTargetIdentifier.file(machOFile.imagePath) + return startIndexingIfNeeded(for: identifier, in: machOFile) + } + + @discardableResult + private func startIndexingIfNeeded(for identifier: MachOTargetIdentifier, in machO: MachO) -> Bool { + if let existedEntry = indexEntryByIdentifier[identifier], existedEntry.isIndexed { + return true + } + var entry = IndexEntry() + + var symbols: OrderedDictionary = [:] + + for symbol in machO.symbols where symbol.name.isSwiftSymbol { + symbols[symbol.name] = .init(offset: symbol.offset, stringValue: symbol.name) + } + + for exportedSymbol in machO.exportedSymbols where exportedSymbol.name.isSwiftSymbol { + if let offset = exportedSymbol.offset, symbols[exportedSymbol.name] == nil { + symbols[exportedSymbol.name] = .init(offset: offset, stringValue: exportedSymbol.name) + } + } + + for symbol in symbols.values { + do { + var demangler = Demangler(scalars: symbol.stringValue.unicodeScalars) + let node = try demangler.demangleSymbol() + func setFunction(_ node: Node, isStatic: Bool = false) { + if let functionNode = node.children.first, functionNode.kind == .function { + if let structureNode = functionNode.children.first, structureNode.kind == .structure { + let typeNode = Node(kind: .global) { + Node(kind: .type, child: structureNode) + } + entry.symbolsByKind[isStatic ? .struct(.staticFunction) : .struct(.function), default: [:]][typeNode.print(using: .interface), default: []].append(symbol) + } else if let enumNode = functionNode.children.first, enumNode.kind == .enum { + let typeNode = Node(kind: .global) { + Node(kind: .type, child: enumNode) + } + entry.symbolsByKind[isStatic ? .enum(.staticFunction) : .enum(.function), default: [:]][typeNode.print(using: .interface), default: []].append(symbol) + } else if let extensionNode = functionNode.children.first, extensionNode.kind == .extension { + if let structureNode = extensionNode.children.at(1), structureNode.kind == .structure { + let typeNode = Node(kind: .global) { + Node(kind: .type, child: structureNode) + } + entry.symbolsByKind[isStatic ? .struct(.staticFunctionInExtension) : .struct(.functionInExtension), default: [:]][typeNode.print(using: .interface), default: []].append(symbol) + } else if let enumNode = extensionNode.children.at(1), enumNode.kind == .enum { + let typeNode = Node(kind: .global) { + Node(kind: .type, child: enumNode) + } + entry.symbolsByKind[isStatic ? .enum(.staticFunctionInExtension) : .enum(.functionInExtension), default: [:]][typeNode.print(using: .interface), default: []].append(symbol) + } + } + } else if let propertyNode = node.children.first, propertyNode.kind == .getter || propertyNode.kind == .setter || propertyNode.kind == .modifyAccessor, let variableNode = propertyNode.children.first, variableNode.kind == .variable { + if let structureNode = variableNode.children.first, structureNode.kind == .structure { + let typeNode = Node(kind: .global) { + Node(kind: .type, child: structureNode) + } + entry.symbolsByKind[isStatic ? .struct(.staticVariable) : .struct(.variable), default: [:]][typeNode.print(using: .interface), default: []].append(symbol) + } else if let enumNode = variableNode.children.first, enumNode.kind == .enum { + let typeNode = Node(kind: .global) { + Node(kind: .type, child: enumNode) + } + entry.symbolsByKind[isStatic ? .enum(.staticVariable) : .enum(.variable), default: [:]][typeNode.print(using: .interface), default: []].append(symbol) + } else if let extensionNode = variableNode.children.first, extensionNode.kind == .extension { + if let structureNode = extensionNode.children.at(1), structureNode.kind == .structure { + let typeNode = Node(kind: .global) { + Node(kind: .type, child: structureNode) + } + entry.symbolsByKind[isStatic ? .struct(.staticVariableInExtension) : .struct(.variableInExtension), default: [:]][typeNode.print(using: .interface), default: []].append(symbol) + } else if let enumNode = extensionNode.children.at(1), enumNode.kind == .enum { + let typeNode = Node(kind: .global) { + Node(kind: .type, child: enumNode) + } + entry.symbolsByKind[isStatic ? .enum(.staticVariableInExtension) : .enum(.variableInExtension), default: [:]][typeNode.print(using: .interface), default: []].append(symbol) + } + } + } + } + + if let staticNode = node.children.first, staticNode.kind == .static { + setFunction(staticNode, isStatic: true) + } else { + setFunction(node) + } + + } catch { + print(error) + } + } + entry.isIndexed = true + indexEntryByIdentifier[identifier] = entry + return true + } + + package func symbols(of kind: IndexKind, for name: String, in machOImage: MachOImage) -> [Symbol] { + let identifier = MachOTargetIdentifier.image(machOImage.ptr) + startIndexingIfNeeded(for: identifier, in: machOImage) + return symbols(of: kind, for: name, with: identifier, in: machOImage) + } + + package func symbols(of kind: IndexKind, for name: String, in machOFile: MachOFile) -> [Symbol] { + let identifier = MachOTargetIdentifier.file(machOFile.imagePath) + startIndexingIfNeeded(for: identifier, in: machOFile) + return symbols(of: kind, for: name, with: identifier, in: machOFile) + } + + private func symbols(of kind: IndexKind, for name: String, with identifier: MachOTargetIdentifier, in machO: MachO) -> [Symbol] { + if let symbol = indexEntryByIdentifier[identifier]?.symbolsByKind[kind]?[name] { + return symbol + } else { + return [] + } + } +} diff --git a/Sources/MachOSymbols/SymbolOrElement.swift b/Sources/MachOSymbols/SymbolOrElement.swift index e4f2db65..b2d29d2d 100644 --- a/Sources/MachOSymbols/SymbolOrElement.swift +++ b/Sources/MachOSymbols/SymbolOrElement.swift @@ -3,7 +3,7 @@ import MachOReading import MachOExtensions public enum SymbolOrElement: Resolvable { - case symbol(MachOSymbol) + case symbol(Symbol) case element(Element) public var isResolved: Bool { @@ -15,7 +15,7 @@ public enum SymbolOrElement: Resolvable { } } - public var symbol: MachOSymbol? { + public var symbol: Symbol? { switch self { case .symbol(let unsolvedSymbol): return unsolvedSymbol diff --git a/Sources/SwiftDump/Dumpable+/Enum+Dumpable.swift b/Sources/SwiftDump/Dumpable+/Enum+Dumpable.swift index 68c5f437..30aeea01 100644 --- a/Sources/SwiftDump/Dumpable+/Enum+Dumpable.swift +++ b/Sources/SwiftDump/Dumpable+/Enum+Dumpable.swift @@ -3,21 +3,21 @@ import MachOKit import MachOSwiftSection import MachOMacro import Semantic +import MachOSymbols extension Enum: NamedDumpable { - @MachOImageGenerator public func dumpName(using options: DemangleOptions, in machOFile: MachOFile) throws -> SemanticString { try MetadataReader.demangleContext(for: .type(.enum(descriptor)), in: machOFile).printSemantic(using: options).replacingTypeNameOrOtherToTypeDeclaration() } - + @MachOImageGenerator @SemanticStringBuilder public func dump(using options: DemangleOptions, in machOFile: MachOFile) throws -> SemanticString { Keyword(.enum) - + Space() - + try dumpName(using: options, in: machOFile) if let genericContext { @@ -67,6 +67,52 @@ extension Enum: NamedDumpable { } } + for kind in SymbolIndexStore.IndexKind.SubKind.allCases.map({ SymbolIndexStore.IndexKind.enum($0) }) { + for (offset, function) in SymbolIndexStore.shared.symbols(of: kind, for: try dumpName(using: .interface, in: machOFile).string, in: machOFile).offsetEnumerated() { + + if offset.isStart { + BreakLine() + + Indent(level: 1) + + switch kind { + case .enum(let subKind): + switch subKind { + case .function: + InlineComment("Functions") + case .functionInExtension: + InlineComment("Functions In Extension") + case .staticFunction: + InlineComment("Static Functions") + case .staticFunctionInExtension: + InlineComment("Static Functions In Extension") + case .variable: + InlineComment("Variables") + case .variableInExtension: + InlineComment("Variables In Extension") + case .staticVariable: + InlineComment("Static Variables") + case .staticVariableInExtension: + InlineComment("Static Variables In Extension") + } + default: + fatalError("unreachable") + } + } + + BreakLine() + + Indent(level: 1) + + try MetadataReader.demangleSymbol(for: function, in: machOFile).printSemantic(using: options) + + if offset.isEnd { + BreakLine() + } + } + } + + Standard("}") } } diff --git a/Sources/SwiftDump/Dumpable+/Protocol+Dumpable.swift b/Sources/SwiftDump/Dumpable+/Protocol+Dumpable.swift index ff99a2e6..5a05012b 100644 --- a/Sources/SwiftDump/Dumpable+/Protocol+Dumpable.swift +++ b/Sources/SwiftDump/Dumpable+/Protocol+Dumpable.swift @@ -54,7 +54,7 @@ extension MachOSwiftSection.`Protocol`: NamedDumpable { for (offset, requirement) in requirements.offsetEnumerated() { BreakLine() Indent(level: 1) - if let symbol = try MachOSymbol.resolve(from: requirement.offset, in: machOFile) { + if let symbol = try Symbol.resolve(from: requirement.offset, in: machOFile) { try? MetadataReader.demangleSymbol(for: symbol, in: machOFile).printSemantic(using: options) } else { InlineComment("[Stripped Symbol]") diff --git a/Sources/SwiftDump/Dumpable+/Struct+Dumpable.swift b/Sources/SwiftDump/Dumpable+/Struct+Dumpable.swift index 8b7d73dd..6626046c 100644 --- a/Sources/SwiftDump/Dumpable+/Struct+Dumpable.swift +++ b/Sources/SwiftDump/Dumpable+/Struct+Dumpable.swift @@ -3,6 +3,7 @@ import MachOKit import MachOSwiftSection import MachOMacro import Semantic +import MachOSymbols extension Struct: NamedDumpable { @@ -74,6 +75,51 @@ extension Struct: NamedDumpable { } } + for kind in SymbolIndexStore.IndexKind.SubKind.allCases.map({ SymbolIndexStore.IndexKind.struct($0) }) { + for (offset, function) in SymbolIndexStore.shared.symbols(of: kind, for: try dumpName(using: .interface, in: machOFile).string, in: machOFile).offsetEnumerated() { + + if offset.isStart { + BreakLine() + + Indent(level: 1) + + switch kind { + case .struct(let subKind): + switch subKind { + case .function: + InlineComment("Functions") + case .functionInExtension: + InlineComment("Functions In Extension") + case .staticFunction: + InlineComment("Static Functions") + case .staticFunctionInExtension: + InlineComment("Static Functions In Extension") + case .variable: + InlineComment("Variables") + case .variableInExtension: + InlineComment("Variables In Extension") + case .staticVariable: + InlineComment("Static Variables") + case .staticVariableInExtension: + InlineComment("Static Variables In Extension") + } + default: + fatalError("unreachable") + } + } + + BreakLine() + + Indent(level: 1) + + try MetadataReader.demangleSymbol(for: function, in: machOFile).printSemantic(using: options) + + if offset.isEnd { + BreakLine() + } + } + } + Standard("}") } } diff --git a/Tests/MachOSwiftSectionTests/SymbolDemangleTests.swift b/Tests/MachOSwiftSectionTests/SymbolDemangleTests.swift index 20a1e68d..88ed495c 100644 --- a/Tests/MachOSwiftSectionTests/SymbolDemangleTests.swift +++ b/Tests/MachOSwiftSectionTests/SymbolDemangleTests.swift @@ -44,12 +44,40 @@ struct SymbolDemangleTests { } } + @Test func symbolsInSwiftUI() async throws { + let symbols = try symbols(for: .SwiftUI) + for symbol in symbols { + let swiftStdlibDemangledName = stdlib_demangleName(symbol.stringValue) + guard !symbol.stringValue.hasSuffix("$delayInitStub") else { continue } + print(symbol.stringValue) + print(swiftStdlibDemangledName) + print("\n") + } + } + @Test func demangle() async throws { var demangler = Demangler(scalars: "_$s6Charts10ChartProxyV5value2at2asx_q_tSgSo7CGPointV_x_q_tmtAA9PlottableRzAaJR_r0_lF".unicodeScalars) let node = try demangler.demangleSymbol() node.print().print() } + @Test func swiftSymbols() async throws { + let symbols = try symbols(for: .SwiftUI) + for symbol in symbols { + var demangler = Demangler(scalars: symbol.stringValue.unicodeScalars) + let node = try demangler.demangleSymbol() + if let functionNode = node.children.first, functionNode.kind == .function { + if let structureNode = functionNode.children.first, structureNode.kind == .structure { + node.print(using: .interface).print() + let typeNode = Node(kind: .global) { + Node(kind: .type, child: structureNode) + } + typeNode.print(using: .interface).print() + } + } + } + } + private func symbols(for machOImageNames: MachOImageName...) throws -> [MachOSwiftSymbol] { var symbols: [MachOSwiftSymbol] = [] for machOImageName in machOImageNames { @@ -82,5 +110,3 @@ struct SymbolDemangleTests { return symbols } } - - From cc065cb518982a0db2045513b26337376ec36b00 Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Fri, 27 Jun 2025 00:17:17 +0800 Subject: [PATCH 02/10] Update MachOSymbols --- ...hOFile+Symbol.swift => MachO+Symbol.swift} | 2 -- Sources/MachOSymbols/Symbol.swift | 9 +++----- Sources/MachOSymbols/SymbolCache.swift | 10 ++++---- Sources/MachOSymbols/SymbolIndexStore.swift | 16 ++++++------- .../MachOSymbols/Symbols/PropertySymbol.swift | 23 +++++++++++++++++++ .../SwiftDump/Dumpable+/Struct+Dumpable.swift | 4 ++-- 6 files changed, 42 insertions(+), 22 deletions(-) rename Sources/MachOSymbols/{MachOFile+Symbol.swift => MachO+Symbol.swift} (75%) create mode 100644 Sources/MachOSymbols/Symbols/PropertySymbol.swift diff --git a/Sources/MachOSymbols/MachOFile+Symbol.swift b/Sources/MachOSymbols/MachO+Symbol.swift similarity index 75% rename from Sources/MachOSymbols/MachOFile+Symbol.swift rename to Sources/MachOSymbols/MachO+Symbol.swift index 82347b24..4acc2fe4 100644 --- a/Sources/MachOSymbols/MachOFile+Symbol.swift +++ b/Sources/MachOSymbols/MachO+Symbol.swift @@ -3,14 +3,12 @@ import MachOExtensions extension MachOFile { package func findSymbol(offset: Int) -> MachOSymbols.Symbol? { - SymbolCache.shared.createCacheIfNeeded(for: self) return SymbolCache.shared.symbol(for: offset, in: self) } } extension MachOImage { package func findSymbol(offset: Int) -> MachOSymbols.Symbol? { - SymbolCache.shared.createCacheIfNeeded(for: self) return SymbolCache.shared.symbol(for: offset, in: self) } } diff --git a/Sources/MachOSymbols/Symbol.swift b/Sources/MachOSymbols/Symbol.swift index 2f0c0c3c..a8a69e01 100644 --- a/Sources/MachOSymbols/Symbol.swift +++ b/Sources/MachOSymbols/Symbol.swift @@ -1,10 +1,10 @@ import MachOKit import MachOMacro -import MachOExtensions import MachOReading +import MachOExtensions public struct Symbol: Resolvable, Hashable { - enum Error: Swift.Error { + public enum ResolveError: Swift.Error { case symbolNotFound } @@ -19,7 +19,7 @@ public struct Symbol: Resolvable, Hashable { @MachOImageGenerator public static func resolve(from fileOffset: Int, in machOFile: MachOFile) throws -> Self { - guard let symbol = try resolve(from: fileOffset, in: machOFile) else { throw Error.symbolNotFound } + guard let symbol = try resolve(from: fileOffset, in: machOFile) else { throw ResolveError.symbolNotFound } return symbol } @@ -28,9 +28,6 @@ public struct Symbol: Resolvable, Hashable { if let symbol = machOFile.findSymbol(offset: fileOffset) { return symbol } -// if let symbol = machOFile.symbol(for: fileOffset) { -// return MachOSymbol(offset: symbol.offset, stringValue: symbol.name) -// } return nil } } diff --git a/Sources/MachOSymbols/SymbolCache.swift b/Sources/MachOSymbols/SymbolCache.swift index b36faf9a..fd1d4a11 100644 --- a/Sources/MachOSymbols/SymbolCache.swift +++ b/Sources/MachOSymbols/SymbolCache.swift @@ -29,13 +29,13 @@ package final class SymbolCache { private var cacheEntryByIdentifier: [MachOTargetIdentifier: CacheEntry] = [:] @discardableResult - package func createCacheIfNeeded(for machOImage: MachOImage, isForced: Bool = false) -> Bool { + private func createCacheIfNeeded(for machOImage: MachOImage, isForced: Bool = false) -> Bool { let identifier = MachOTargetIdentifier.image(machOImage.ptr) return createCacheIfNeeded(for: identifier, in: machOImage, isForced: isForced) } @discardableResult - package func createCacheIfNeeded(for machOFile: MachOFile, isForced: Bool = false) -> Bool { + private func createCacheIfNeeded(for machOFile: MachOFile, isForced: Bool = false) -> Bool { let identifier = MachOTargetIdentifier.file(machOFile.imagePath) return createCacheIfNeeded(for: identifier, in: machOFile, isForced: isForced) } @@ -64,12 +64,12 @@ package final class SymbolCache { return true } - package func removeCache(for machOImage: MachOImage) { + private func removeCache(for machOImage: MachOImage) { let identifier = MachOTargetIdentifier.image(machOImage.ptr) removeCache(for: identifier) } - package func removeCache(for machOFile: MachOFile) { + private func removeCache(for machOFile: MachOFile) { let identifier = MachOTargetIdentifier.file(machOFile.imagePath) removeCache(for: identifier) } @@ -80,11 +80,13 @@ package final class SymbolCache { package func symbol(for offset: Int, in machOImage: MachOImage) -> Symbol? { let identifier = MachOTargetIdentifier.image(machOImage.ptr) + createCacheIfNeeded(for: identifier, in: machOImage) return symbol(for: offset, with: identifier, in: machOImage) } package func symbol(for offset: Int, in machOFile: MachOFile) -> Symbol? { let identifier = MachOTargetIdentifier.file(machOFile.imagePath) + createCacheIfNeeded(for: identifier, in: machOFile) return symbol(for: offset, with: identifier, in: machOFile) } diff --git a/Sources/MachOSymbols/SymbolIndexStore.swift b/Sources/MachOSymbols/SymbolIndexStore.swift index 491fa438..dc12cfc3 100644 --- a/Sources/MachOSymbols/SymbolIndexStore.swift +++ b/Sources/MachOSymbols/SymbolIndexStore.swift @@ -45,12 +45,12 @@ package final class SymbolIndexStore { private var indexEntryByIdentifier: [MachOTargetIdentifier: IndexEntry] = [:] - package func removeIndexs(for machOImage: MachOImage) { + private func removeIndexs(for machOImage: MachOImage) { let identifier = MachOTargetIdentifier.image(machOImage.ptr) removeIndexs(for: identifier) } - package func removeIndexs(for machOFile: MachOFile) { + private func removeIndexs(for machOFile: MachOFile) { let identifier = MachOTargetIdentifier.file(machOFile.imagePath) removeIndexs(for: identifier) } @@ -60,13 +60,13 @@ package final class SymbolIndexStore { } @discardableResult - package func startIndexingIfNeeded(for machOImage: MachOImage) -> Bool { + private func startIndexingIfNeeded(for machOImage: MachOImage) -> Bool { let identifier = MachOTargetIdentifier.image(machOImage.ptr) return startIndexingIfNeeded(for: identifier, in: machOImage) } @discardableResult - package func startIndexingIfNeeded(for machOFile: MachOFile) -> Bool { + private func startIndexingIfNeeded(for machOFile: MachOFile) -> Bool { let identifier = MachOTargetIdentifier.file(machOFile.imagePath) return startIndexingIfNeeded(for: identifier, in: machOFile) } @@ -77,7 +77,7 @@ package final class SymbolIndexStore { return true } var entry = IndexEntry() - + var symbols: OrderedDictionary = [:] for symbol in machO.symbols where symbol.name.isSwiftSymbol { @@ -94,7 +94,7 @@ package final class SymbolIndexStore { do { var demangler = Demangler(scalars: symbol.stringValue.unicodeScalars) let node = try demangler.demangleSymbol() - func setFunction(_ node: Node, isStatic: Bool = false) { + func perform(_ node: Node, isStatic: Bool) { if let functionNode = node.children.first, functionNode.kind == .function { if let structureNode = functionNode.children.first, structureNode.kind == .structure { let typeNode = Node(kind: .global) { @@ -147,9 +147,9 @@ package final class SymbolIndexStore { } if let staticNode = node.children.first, staticNode.kind == .static { - setFunction(staticNode, isStatic: true) + perform(staticNode, isStatic: true) } else { - setFunction(node) + perform(node, isStatic: false) } } catch { diff --git a/Sources/MachOSymbols/Symbols/PropertySymbol.swift b/Sources/MachOSymbols/Symbols/PropertySymbol.swift new file mode 100644 index 00000000..649a2c16 --- /dev/null +++ b/Sources/MachOSymbols/Symbols/PropertySymbol.swift @@ -0,0 +1,23 @@ +import Foundation +import MachOKit +import MachOExtensions +import Demangle +import OrderedCollections + +package struct PropertySymbol { + package enum Kind { + case getter + case setter + case modify + } + + package let symbol: Symbol + + package let kind: Kind + + package let identifier: String + + package let isStatic: Bool + + package let isInExtension: Bool +} diff --git a/Sources/SwiftDump/Dumpable+/Struct+Dumpable.swift b/Sources/SwiftDump/Dumpable+/Struct+Dumpable.swift index 6626046c..c5c944e0 100644 --- a/Sources/SwiftDump/Dumpable+/Struct+Dumpable.swift +++ b/Sources/SwiftDump/Dumpable+/Struct+Dumpable.swift @@ -76,7 +76,7 @@ extension Struct: NamedDumpable { } for kind in SymbolIndexStore.IndexKind.SubKind.allCases.map({ SymbolIndexStore.IndexKind.struct($0) }) { - for (offset, function) in SymbolIndexStore.shared.symbols(of: kind, for: try dumpName(using: .interface, in: machOFile).string, in: machOFile).offsetEnumerated() { + for (offset, symbol) in SymbolIndexStore.shared.symbols(of: kind, for: try dumpName(using: .interface, in: machOFile).string, in: machOFile).offsetEnumerated() { if offset.isStart { BreakLine() @@ -112,7 +112,7 @@ extension Struct: NamedDumpable { Indent(level: 1) - try MetadataReader.demangleSymbol(for: function, in: machOFile).printSemantic(using: options) + try MetadataReader.demangleSymbol(for: symbol, in: machOFile).printSemantic(using: options) if offset.isEnd { BreakLine() From 6a218ba5697979c8484a912c97047fedee6ac9e0 Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Fri, 27 Jun 2025 17:59:55 +0800 Subject: [PATCH 03/10] Add builtin type descriptor support --- .../MachOSwiftSection/MachOFile+Swift.swift | 64 +++--- .../MachOSwiftSection/MachOImage+Swift.swift | 49 +++-- .../AssociatedType/AssociatedType.swift | 2 +- .../AssociatedTypeDescriptor.swift | 8 +- .../Models/BuiltinType/BuiltinType.swift | 20 ++ .../BuiltinType/BuiltinTypeDescriptor.swift | 19 +- .../Models/OpaqueType/OpaqueType.swift | 2 +- .../Models/Protocol/Protocol.swift | 2 +- .../ProtocolConformance.swift | 3 +- .../Models/Type/Class/Class.swift | 7 +- .../Models/Type/Enum/Enum.swift | 7 +- .../Models/Type/Struct/Struct.swift | 7 +- .../MachOSwiftSection/Protocols/FlagSet.swift | 48 ++++- .../Protocols/MutableFlagSet.swift | 45 ---- .../Protocols/RuntimeProtocol.swift | 2 - .../Protocols/TopLevelDescriptor.swift | 9 + .../Protocols/TopLevelType.swift | 12 ++ .../Utils/MachOSwiftSectionError.swift | 17 ++ .../Utils/PrimitiveTypeMapping.swift | 10 + Sources/MachOSymbols/SymbolCache.swift | 1 + Sources/MachOSymbols/SymbolIndexStore.swift | 194 ++++++++++++------ .../MachOSymbols/Symbols/PropertySymbol.swift | 17 ++ .../MachOTestingSupport/DumpableTest.swift | 10 + .../SwiftDump/Dumpable+/Enum+Dumpable.swift | 44 +--- .../SwiftDump/Dumpable+/Struct+Dumpable.swift | 63 ++---- .../SymbolDemangleTests.swift | 10 +- Tests/SwiftDumpTests/DyldCacheDumpTests.swift | 12 ++ 27 files changed, 405 insertions(+), 279 deletions(-) delete mode 100644 Sources/MachOSwiftSection/Protocols/MutableFlagSet.swift create mode 100644 Sources/MachOSwiftSection/Protocols/TopLevelDescriptor.swift create mode 100644 Sources/MachOSwiftSection/Protocols/TopLevelType.swift create mode 100644 Sources/MachOSwiftSection/Utils/MachOSwiftSectionError.swift create mode 100644 Sources/MachOSwiftSection/Utils/PrimitiveTypeMapping.swift diff --git a/Sources/MachOSwiftSection/MachOFile+Swift.swift b/Sources/MachOSwiftSection/MachOFile+Swift.swift index 24c32e44..81fb0768 100644 --- a/Sources/MachOSwiftSection/MachOFile+Swift.swift +++ b/Sources/MachOSwiftSection/MachOFile+Swift.swift @@ -16,62 +16,58 @@ extension MachOFile { } } -enum MachOSwiftSectionError: LocalizedError { - case sectionNotFound(section: MachOSwiftSectionName, allSectionNames: [String]) - case invalidSectionAlignment(section: MachOSwiftSectionName, align: Int) - - var errorDescription: String? { - switch self { - case .sectionNotFound(let section, let allSectionNames): - return "Swift section \(section.rawValue) not found in Mach-O. Available sections: \(allSectionNames.joined(separator: ", "))" - case .invalidSectionAlignment(let section, let align): - return "Invalid alignment for Swift section \(section.rawValue). Expected alignment is 4, but found \(align)." - } - } -} - extension MachOFile.Swift { public var protocolDescriptors: [ProtocolDescriptor] { get throws { - return try _readDescriptors(from: .__swift5_protos, in: machOFile) + return try _readRelativeDescriptors(from: .__swift5_protos, in: machOFile) } } public var protocolConformanceDescriptors: [ProtocolConformanceDescriptor] { get throws { - return try _readDescriptors(from: .__swift5_proto, in: machOFile) + return try _readRelativeDescriptors(from: .__swift5_proto, in: machOFile) } } public var typeContextDescriptors: [ContextDescriptorWrapper] { get throws { - return try _readDescriptors(from: .__swift5_types, in: machOFile) + (try? _readDescriptors(from: .__swift5_types2, in: machOFile)) + return try _readRelativeDescriptors(from: .__swift5_types, in: machOFile) + (try? _readRelativeDescriptors(from: .__swift5_types2, in: machOFile)) } } public var associatedTypeDescriptors: [AssociatedTypeDescriptor] { get throws { - let section = try machOFile.section(for: .__swift5_assocty) - var associatedTypeDescriptors: [AssociatedTypeDescriptor] = [] - let offset = if let cache = machOFile.cache { - section.address - cache.mainCacheHeader.sharedRegionStart.cast() - } else { - section.offset - } - var currentOffset = offset - let endOffset = offset + section.size - while currentOffset < endOffset { - let associatedTypeDescriptor: AssociatedTypeDescriptor = try machOFile.readElement(offset: currentOffset) - currentOffset += associatedTypeDescriptor.size - associatedTypeDescriptors.append(associatedTypeDescriptor) - } - return associatedTypeDescriptors + return try _readDescriptors(from: .__swift5_assocty, in: machOFile) + } + } + + public var builtinTypeDescriptors: [BuiltinTypeDescriptor] { + get throws { + return try _readDescriptors(from: .__swift5_builtin, in: machOFile) } } } extension MachOFile.Swift { - private func _readDescriptors(from swiftMachOSection: MachOSwiftSectionName, in machO: MachOFile) throws -> [Descriptor] { + private func _readDescriptors(from swiftMachOSection: MachOSwiftSectionName, in machO: MachOFile) throws -> [Descriptor] { + let section = try machOFile.section(for: swiftMachOSection) + var descriptors: [Descriptor] = [] + let offset = if let cache = machOFile.cache { + section.address - cache.mainCacheHeader.sharedRegionStart.cast() + } else { + section.offset + } + var currentOffset = offset + let endOffset = offset + section.size + while currentOffset < endOffset { + let descriptor: Descriptor = try machOFile.readElement(offset: currentOffset) + currentOffset += descriptor.actualSize + descriptors.append(descriptor) + } + return descriptors + } + + private func _readRelativeDescriptors(from swiftMachOSection: MachOSwiftSectionName, in machO: MachOFile) throws -> [Descriptor] { let section = try machOFile.section(for: swiftMachOSection) let pointerSize: Int = MemoryLayout>.size let offset = if let cache = machO.cache { @@ -80,8 +76,6 @@ extension MachOFile.Swift { section.offset } let data: [AnyLocatableLayoutWrapper>] = try machO.readElements(offset: offset, numberOfElements: section.size / pointerSize) - return try data.map { try $0.layout.resolve(from: $0.offset, in: machO) } } } - diff --git a/Sources/MachOSwiftSection/MachOImage+Swift.swift b/Sources/MachOSwiftSection/MachOImage+Swift.swift index 73a8988a..9a3890b8 100644 --- a/Sources/MachOSwiftSection/MachOImage+Swift.swift +++ b/Sources/MachOSwiftSection/MachOImage+Swift.swift @@ -19,54 +19,59 @@ extension MachOImage { extension MachOImage.Swift { public var protocolDescriptors: [ProtocolDescriptor] { get throws { - return try _readDescriptors(from: .__swift5_protos, in: machOImage) + return try _readRelativeDescriptors(from: .__swift5_protos, in: machOImage) } } public var protocolConformanceDescriptors: [ProtocolConformanceDescriptor] { get throws { - return try _readDescriptors(from: .__swift5_proto, in: machOImage) + return try _readRelativeDescriptors(from: .__swift5_proto, in: machOImage) } } public var typeContextDescriptors: [ContextDescriptorWrapper] { get throws { - return try _readDescriptors(from: .__swift5_types, in: machOImage) + (try? _readDescriptors(from: .__swift5_types2, in: machOImage)) + return try _readRelativeDescriptors(from: .__swift5_types, in: machOImage) + (try? _readRelativeDescriptors(from: .__swift5_types2, in: machOImage)) } } public var associatedTypeDescriptors: [AssociatedTypeDescriptor] { get throws { - let section = try machOImage.section(for: .__swift5_assocty) - var associatedTypeDescriptors: [AssociatedTypeDescriptor] = [] - let vmaddrSlide = try required(machOImage.vmaddrSlide) - let start = try required(UnsafeRawPointer(bitPattern: section.address + vmaddrSlide)) - let offset = start.int - machOImage.ptr.int - var currentOffset = offset - let endOffset = offset + section.size - while currentOffset < endOffset { - let associatedTypeDescriptor: AssociatedTypeDescriptor = try machOImage.readElement(offset: currentOffset) - currentOffset += associatedTypeDescriptor.size - associatedTypeDescriptors.append(associatedTypeDescriptor) - } - return associatedTypeDescriptors + return try _readDescriptors(from: .__swift5_assocty, in: machOImage) + } + } + + public var builtinTypeDescriptors: [BuiltinTypeDescriptor] { + get throws { + return try _readDescriptors(from: .__swift5_builtin, in: machOImage) } } } extension MachOImage.Swift { - private func _readDescriptors(from swiftMachOSection: MachOSwiftSectionName, in machO: MachOImage) throws -> [Descriptor] { + private func _readDescriptors(from swiftMachOSection: MachOSwiftSectionName, in machO: MachOImage) throws -> [Descriptor] { + let section = try machOImage.section(for: swiftMachOSection) + var descriptors: [Descriptor] = [] + let vmaddrSlide = try required(machOImage.vmaddrSlide) + let start = try required(UnsafeRawPointer(bitPattern: section.address + vmaddrSlide)) + let offset = start.int - machOImage.ptr.int + var currentOffset = offset + let endOffset = offset + section.size + while currentOffset < endOffset { + let descriptor: Descriptor = try machOImage.readElement(offset: currentOffset) + currentOffset += descriptor.actualSize + descriptors.append(descriptor) + } + return descriptors + } + + private func _readRelativeDescriptors(from swiftMachOSection: MachOSwiftSectionName, in machO: MachOImage) throws -> [Descriptor] { let section = try machO.section(for: swiftMachOSection) - let vmaddrSlide = try required(machO.vmaddrSlide) let start = try required(UnsafeRawPointer(bitPattern: section.address + vmaddrSlide)) - let offset = start.int - machO.ptr.int - let pointerSize: Int = MemoryLayout>.size - let data: [AnyLocatableLayoutWrapper>] = try machO.readElements(offset: offset, numberOfElements: section.size / pointerSize) - return try data.map { try $0.layout.resolve(from: $0.offset, in: machO) } } } diff --git a/Sources/MachOSwiftSection/Models/AssociatedType/AssociatedType.swift b/Sources/MachOSwiftSection/Models/AssociatedType/AssociatedType.swift index fc08bd80..4b5152ca 100644 --- a/Sources/MachOSwiftSection/Models/AssociatedType/AssociatedType.swift +++ b/Sources/MachOSwiftSection/Models/AssociatedType/AssociatedType.swift @@ -2,7 +2,7 @@ import Foundation import MachOKit import MachOMacro -public struct AssociatedType { +public struct AssociatedType: TopLevelType { public let descriptor: AssociatedTypeDescriptor public let conformingTypeName: MangledName diff --git a/Sources/MachOSwiftSection/Models/AssociatedType/AssociatedTypeDescriptor.swift b/Sources/MachOSwiftSection/Models/AssociatedType/AssociatedTypeDescriptor.swift index 2227bf66..db78c7a4 100644 --- a/Sources/MachOSwiftSection/Models/AssociatedType/AssociatedTypeDescriptor.swift +++ b/Sources/MachOSwiftSection/Models/AssociatedType/AssociatedTypeDescriptor.swift @@ -37,8 +37,8 @@ extension AssociatedTypeDescriptor { public func associatedTypeRecords(in machOFile: MachOFile) throws -> [AssociatedTypeRecord] { return try machOFile.readElements(offset: offset + layoutSize, numberOfElements: layout.numAssociatedTypes.cast()) } - - public var size: Int { - layoutSize + (layout.numAssociatedTypes * layout.associatedTypeRecordSize).cast() - } +} + +extension AssociatedTypeDescriptor: TopLevelDescriptor { + public var actualSize: Int { layoutSize + (layout.numAssociatedTypes * layout.associatedTypeRecordSize).cast() } } diff --git a/Sources/MachOSwiftSection/Models/BuiltinType/BuiltinType.swift b/Sources/MachOSwiftSection/Models/BuiltinType/BuiltinType.swift index fecc4ab4..d70b1cef 100644 --- a/Sources/MachOSwiftSection/Models/BuiltinType/BuiltinType.swift +++ b/Sources/MachOSwiftSection/Models/BuiltinType/BuiltinType.swift @@ -1 +1,21 @@ import Foundation +import MachOSymbols +import MachOKit +import MachOMacro + +public struct BuiltinType: TopLevelType { + public let descriptor: BuiltinTypeDescriptor + + public let typeName: MangledName? + + public let symbol: Symbol? + + @MachOImageGenerator + public init(descriptor: BuiltinTypeDescriptor, in machO: MachOFile) throws { + self.descriptor = descriptor + self.typeName = try descriptor.typeName(in: machO) + self.symbol = SymbolCache.shared.symbol(for: descriptor.offset, in: machO) + } +} + + diff --git a/Sources/MachOSwiftSection/Models/BuiltinType/BuiltinTypeDescriptor.swift b/Sources/MachOSwiftSection/Models/BuiltinType/BuiltinTypeDescriptor.swift index 41ad8226..43f27abe 100644 --- a/Sources/MachOSwiftSection/Models/BuiltinType/BuiltinTypeDescriptor.swift +++ b/Sources/MachOSwiftSection/Models/BuiltinType/BuiltinTypeDescriptor.swift @@ -1,7 +1,9 @@ import Foundation +import MachOKit import MachOFoundation +import MachOMacro -public struct BuiltinTypeDescriptor: ResolvableLocatableLayoutWrapper { +public struct BuiltinTypeDescriptor: ResolvableLocatableLayoutWrapper, TopLevelDescriptor { public struct Layout: Sendable { public let typeName: RelativeDirectPointer public let size: UInt32 @@ -9,11 +11,11 @@ public struct BuiltinTypeDescriptor: ResolvableLocatableLayoutWrapper { public let stride: UInt32 public let numExtraInhabitants: UInt32 } - + public var layout: Layout - + public let offset: Int - + public init(layout: Layout, offset: Int) { self.layout = layout self.offset = offset @@ -21,14 +23,19 @@ public struct BuiltinTypeDescriptor: ResolvableLocatableLayoutWrapper { } extension BuiltinTypeDescriptor { + @MachOImageGenerator + public func typeName(in machOFile: MachOFile) throws -> MangledName? { + return try layout.typeName.resolve(from: offset(of: \.typeName), in: machOFile) + } + public var isBitwiseTakable: Bool { return (layout.alignmentAndFlags >> 16) & 0x1 != 0 } - + public var alignment: Int { (layout.alignmentAndFlags & 0xFFFF).cast() } - + public var hasMangledName: Bool { layout.typeName.isNull == false } diff --git a/Sources/MachOSwiftSection/Models/OpaqueType/OpaqueType.swift b/Sources/MachOSwiftSection/Models/OpaqueType/OpaqueType.swift index b690f67e..1510fe76 100644 --- a/Sources/MachOSwiftSection/Models/OpaqueType/OpaqueType.swift +++ b/Sources/MachOSwiftSection/Models/OpaqueType/OpaqueType.swift @@ -3,7 +3,7 @@ import MachOKit import MachOMacro import MachOFoundation -public struct OpaqueType { +public struct OpaqueType: TopLevelType { public let descriptor: OpaqueTypeDescriptor public let genericContext: GenericContext? diff --git a/Sources/MachOSwiftSection/Models/Protocol/Protocol.swift b/Sources/MachOSwiftSection/Models/Protocol/Protocol.swift index 27cb91ea..39522b88 100644 --- a/Sources/MachOSwiftSection/Models/Protocol/Protocol.swift +++ b/Sources/MachOSwiftSection/Models/Protocol/Protocol.swift @@ -8,7 +8,7 @@ import MachOMacro // TargetGenericRequirementDescriptor, // TargetProtocolRequirement>; -public struct `Protocol` { +public struct `Protocol`: TopLevelType { public enum Error: Swift.Error { case invalidProtocolDescriptor } diff --git a/Sources/MachOSwiftSection/Models/ProtocolConformance/ProtocolConformance.swift b/Sources/MachOSwiftSection/Models/ProtocolConformance/ProtocolConformance.swift index e1cf32db..13ca23f0 100644 --- a/Sources/MachOSwiftSection/Models/ProtocolConformance/ProtocolConformance.swift +++ b/Sources/MachOSwiftSection/Models/ProtocolConformance/ProtocolConformance.swift @@ -17,7 +17,8 @@ import MachOFoundation /// /// This contains enough static information to recover the witness table for a /// type's conformance to a protocol. -public struct ProtocolConformance { + +public struct ProtocolConformance: TopLevelType { public let descriptor: ProtocolConformanceDescriptor public let `protocol`: SymbolOrElement? diff --git a/Sources/MachOSwiftSection/Models/Type/Class/Class.swift b/Sources/MachOSwiftSection/Models/Type/Class/Class.swift index a943e116..208396e2 100644 --- a/Sources/MachOSwiftSection/Models/Type/Class/Class.swift +++ b/Sources/MachOSwiftSection/Models/Type/Class/Class.swift @@ -27,8 +27,7 @@ import MachOFoundation // TargetMethodDefaultOverrideTableHeader, // TargetMethodDefaultOverrideDescriptor> -@dynamicMemberLookup -public struct Class { +public struct Class: TopLevelType { public let descriptor: ClassDescriptor public let genericContext: TypeGenericContext? public let resilientSuperclass: ResilientSuperclass? @@ -162,8 +161,4 @@ public struct Class { self.methodDefaultOverrideDescriptors = [] } } - - public subscript(dynamicMember member: KeyPath) -> T { - return descriptor[keyPath: member] - } } diff --git a/Sources/MachOSwiftSection/Models/Type/Enum/Enum.swift b/Sources/MachOSwiftSection/Models/Type/Enum/Enum.swift index 4adfaaf3..dd836324 100644 --- a/Sources/MachOSwiftSection/Models/Type/Enum/Enum.swift +++ b/Sources/MachOSwiftSection/Models/Type/Enum/Enum.swift @@ -18,8 +18,7 @@ import MachOFoundation // InvertibleProtocolSet, // TargetSingletonMetadataPointer> -@dynamicMemberLookup -public struct Enum { +public struct Enum: TopLevelType { public let descriptor: EnumDescriptor public let genericContext: TypeGenericContext? public let foreignMetadataInitialization: ForeignMetadataInitialization? @@ -90,10 +89,6 @@ public struct Enum { self.singletonMetadataPointer = nil } } - - public subscript(dynamicMember member: KeyPath) -> T { - return descriptor[keyPath: member] - } } diff --git a/Sources/MachOSwiftSection/Models/Type/Struct/Struct.swift b/Sources/MachOSwiftSection/Models/Type/Struct/Struct.swift index 251c8d7f..4d96a9e4 100644 --- a/Sources/MachOSwiftSection/Models/Type/Struct/Struct.swift +++ b/Sources/MachOSwiftSection/Models/Type/Struct/Struct.swift @@ -3,8 +3,7 @@ import MachOKit import MachOMacro import MachOFoundation -@dynamicMemberLookup -public struct Struct { +public struct Struct: TopLevelType { public let descriptor: StructDescriptor public let genericContext: TypeGenericContext? public let foreignMetadataInitialization: ForeignMetadataInitialization? @@ -75,10 +74,6 @@ public struct Struct { self.singletonMetadataPointer = nil } } - - public subscript(dynamicMember member: KeyPath) -> T { - return descriptor[keyPath: member] - } } diff --git a/Sources/MachOSwiftSection/Protocols/FlagSet.swift b/Sources/MachOSwiftSection/Protocols/FlagSet.swift index e091037f..c3e994c8 100644 --- a/Sources/MachOSwiftSection/Protocols/FlagSet.swift +++ b/Sources/MachOSwiftSection/Protocols/FlagSet.swift @@ -1,5 +1,3 @@ -import Foundation - public protocol FlagSet: Equatable, RawRepresentable where RawValue: FixedWidthInteger { func flag(bit: Int) -> Bool @@ -12,7 +10,7 @@ public protocol FlagSet: Equatable, RawRepresentable where RawValue: FixedWidthI extension FlagSet { @inline(__always) - static func lowMask(forBitWidth bitWidth: Int) -> RawValue { + fileprivate static func lowMask(forBitWidth bitWidth: Int) -> RawValue { precondition(bitWidth >= 0 && bitWidth <= RawValue.bitWidth, "Bit width must be between 0 and the storage type's bit width.") if bitWidth == RawValue.bitWidth { return ~RawValue(0) // All bits set @@ -25,7 +23,7 @@ extension FlagSet { } @inline(__always) - static func mask(forFirstBit firstBit: Int, bitWidth: Int = 1) -> RawValue { + fileprivate static func mask(forFirstBit firstBit: Int, bitWidth: Int = 1) -> RawValue { precondition(firstBit >= 0, "First bit index cannot be negative.") precondition(bitWidth >= 1, "Bit width must be at least 1.") precondition(firstBit + bitWidth <= RawValue.bitWidth, "Field extends beyond the storage type's bit width.") @@ -59,4 +57,46 @@ extension FlagSet { } } +public protocol MutableFlagSet: FlagSet { + var rawValue: RawValue { get set } + + mutating func setFlag(_ value: Bool, bit: Int) + + mutating func setField( + _ value: FieldType, + firstBit: Int, + bitWidth: Int + ) +} + +extension MutableFlagSet { + @inline(__always) + public mutating func setFlag(_ value: Bool, bit: Int) { + precondition(bit >= 0 && bit < RawValue.bitWidth, "Bit index out of range.") + let mask = Self.mask(forFirstBit: bit) + if value { + rawValue |= mask + } else { + rawValue &= ~mask + } + } + @inline(__always) + public mutating func setField( + _ value: FieldType, + firstBit: Int, + bitWidth: Int + ) { + precondition(bitWidth > 0, "Bit width must be positive.") + precondition(firstBit >= 0 && (firstBit + bitWidth) <= RawValue.bitWidth, "Field range is out of bounds for the storage type.") + + let valueMask = Self.lowMask(forBitWidth: bitWidth) + let rawValueEquivalent = RawValue(truncatingIfNeeded: value) + + precondition((rawValueEquivalent & ~valueMask) == 0, "Value \(value) is too large to fit in a field of width \(bitWidth).") + + let fieldMask = Self.mask(forFirstBit: firstBit, bitWidth: bitWidth) + rawValue &= ~fieldMask + rawValue |= (rawValueEquivalent << firstBit) & fieldMask + } +} diff --git a/Sources/MachOSwiftSection/Protocols/MutableFlagSet.swift b/Sources/MachOSwiftSection/Protocols/MutableFlagSet.swift deleted file mode 100644 index c7d7b618..00000000 --- a/Sources/MachOSwiftSection/Protocols/MutableFlagSet.swift +++ /dev/null @@ -1,45 +0,0 @@ -import Foundation - -public protocol MutableFlagSet: FlagSet { - var rawValue: RawValue { get set } - - mutating func setFlag(_ value: Bool, bit: Int) - - mutating func setField( - _ value: FieldType, - firstBit: Int, - bitWidth: Int - ) -} - -extension MutableFlagSet { - @inline(__always) - public mutating func setFlag(_ value: Bool, bit: Int) { - precondition(bit >= 0 && bit < RawValue.bitWidth, "Bit index out of range.") - let mask = Self.mask(forFirstBit: bit) - if value { - rawValue |= mask - } else { - rawValue &= ~mask - } - } - - @inline(__always) - public mutating func setField( - _ value: FieldType, - firstBit: Int, - bitWidth: Int - ) { - precondition(bitWidth > 0, "Bit width must be positive.") - precondition(firstBit >= 0 && (firstBit + bitWidth) <= RawValue.bitWidth, "Field range is out of bounds for the storage type.") - - let valueMask = Self.lowMask(forBitWidth: bitWidth) - let rawValueEquivalent = RawValue(truncatingIfNeeded: value) - - precondition((rawValueEquivalent & ~valueMask) == 0, "Value \(value) is too large to fit in a field of width \(bitWidth).") - - let fieldMask = Self.mask(forFirstBit: firstBit, bitWidth: bitWidth) - rawValue &= ~fieldMask - rawValue |= (rawValueEquivalent << firstBit) & fieldMask - } -} diff --git a/Sources/MachOSwiftSection/Protocols/RuntimeProtocol.swift b/Sources/MachOSwiftSection/Protocols/RuntimeProtocol.swift index 5372ec39..35fcb5d4 100644 --- a/Sources/MachOSwiftSection/Protocols/RuntimeProtocol.swift +++ b/Sources/MachOSwiftSection/Protocols/RuntimeProtocol.swift @@ -1,5 +1,3 @@ -import Foundation - public protocol RuntimeProtocol { associatedtype StoredPointer: FixedWidthInteger associatedtype StoredSignedPointer: FixedWidthInteger diff --git a/Sources/MachOSwiftSection/Protocols/TopLevelDescriptor.swift b/Sources/MachOSwiftSection/Protocols/TopLevelDescriptor.swift new file mode 100644 index 00000000..d527463f --- /dev/null +++ b/Sources/MachOSwiftSection/Protocols/TopLevelDescriptor.swift @@ -0,0 +1,9 @@ +public protocol TopLevelDescriptor: ResolvableLocatableLayoutWrapper { + var actualSize: Int { get } +} + +extension TopLevelDescriptor { + public var actualSize: Int { + return layoutSize + } +} diff --git a/Sources/MachOSwiftSection/Protocols/TopLevelType.swift b/Sources/MachOSwiftSection/Protocols/TopLevelType.swift new file mode 100644 index 00000000..135a1310 --- /dev/null +++ b/Sources/MachOSwiftSection/Protocols/TopLevelType.swift @@ -0,0 +1,12 @@ +@dynamicMemberLookup +public protocol TopLevelType { + associatedtype Descriptor + var descriptor: Descriptor { get } + subscript(dynamicMember member: KeyPath) -> T { get } +} + +extension TopLevelType { + public subscript(dynamicMember member: KeyPath) -> T { + return descriptor[keyPath: member] + } +} diff --git a/Sources/MachOSwiftSection/Utils/MachOSwiftSectionError.swift b/Sources/MachOSwiftSection/Utils/MachOSwiftSectionError.swift new file mode 100644 index 00000000..1379a042 --- /dev/null +++ b/Sources/MachOSwiftSection/Utils/MachOSwiftSectionError.swift @@ -0,0 +1,17 @@ +import Foundation +import MachOKit +import MachOFoundation + +enum MachOSwiftSectionError: LocalizedError { + case sectionNotFound(section: MachOSwiftSectionName, allSectionNames: [String]) + case invalidSectionAlignment(section: MachOSwiftSectionName, align: Int) + + var errorDescription: String? { + switch self { + case let .sectionNotFound(section, allSectionNames): + return "Swift section \(section.rawValue) not found in Mach-O. Available sections: \(allSectionNames.joined(separator: ", "))" + case let .invalidSectionAlignment(section, align): + return "Invalid alignment for Swift section \(section.rawValue). Expected alignment is 4, but found \(align)." + } + } +} diff --git a/Sources/MachOSwiftSection/Utils/PrimitiveTypeMapping.swift b/Sources/MachOSwiftSection/Utils/PrimitiveTypeMapping.swift new file mode 100644 index 00000000..4811a18c --- /dev/null +++ b/Sources/MachOSwiftSection/Utils/PrimitiveTypeMapping.swift @@ -0,0 +1,10 @@ +import Foundation +import MachOFoundation + +class PrimitiveTypeMapping { + private let machO: MachO + + init(machO: MachO) throws { + self.machO = machO + } +} diff --git a/Sources/MachOSymbols/SymbolCache.swift b/Sources/MachOSymbols/SymbolCache.swift index fd1d4a11..1df89f2e 100644 --- a/Sources/MachOSymbols/SymbolCache.swift +++ b/Sources/MachOSymbols/SymbolCache.swift @@ -44,6 +44,7 @@ package final class SymbolCache { private func createCacheIfNeeded(for identifier: MachOTargetIdentifier, in machO: MachO, isForced: Bool = false) -> Bool { guard isForced || (cacheEntryByIdentifier[identifier]?.isEmpty ?? true) else { return false } var cacheEntry: CacheEntry = [:] + for symbol in machO.symbols where symbol.name.isSwiftSymbol { var offset = symbol.offset cacheEntry[offset] = .init(offset: offset, stringValue: symbol.name) diff --git a/Sources/MachOSymbols/SymbolIndexStore.swift b/Sources/MachOSymbols/SymbolIndexStore.swift index dc12cfc3..bc132832 100644 --- a/Sources/MachOSymbols/SymbolIndexStore.swift +++ b/Sources/MachOSymbols/SymbolIndexStore.swift @@ -5,21 +5,42 @@ import Demangle import OrderedCollections package final class SymbolIndexStore { - package enum IndexKind: Hashable { - package enum SubKind: Hashable, CaseIterable { - case function - case functionInExtension - case staticFunction - case staticFunctionInExtension - case variable - case variableInExtension - case staticVariable - case staticVariableInExtension + package enum IndexKind: Hashable, CaseIterable, CustomStringConvertible { + case allocator + case allocatorInExtension + case function + case functionInExtension + case staticFunction + case staticFunctionInExtension + case variable + case variableInExtension + case staticVariable + case staticVariableInExtension + + package var description: String { + switch self { + case .allocator: + "Allocators" + case .allocatorInExtension: + "Allocators In Extensions" + case .function: + "Functions" + case .functionInExtension: + "Functions In Extensions" + case .staticFunction: + "Static Functions" + case .staticFunctionInExtension: + "Static Functions In Extensions" + case .variable: + "Variables" + case .variableInExtension: + "Variables In Extensions" + case .staticVariable: + "Static Variables" + case .staticVariableInExtension: + "Static Variables In Extensions" + } } - - case `enum`(SubKind) - case `struct`(SubKind) - case `class`(SubKind) } package static let shared = SymbolIndexStore() @@ -94,55 +115,112 @@ package final class SymbolIndexStore { do { var demangler = Demangler(scalars: symbol.stringValue.unicodeScalars) let node = try demangler.demangleSymbol() +// func perform(_ node: Node, isStatic: Bool) { +// if let functionNode = node.children.first, functionNode.kind == .function { +// if let structureNode = functionNode.children.first, structureNode.kind == .structure { +// let typeNode = Node(kind: .global) { +// Node(kind: .type, child: structureNode) +// } +// entry.symbolsByKind[isStatic ? .struct(.staticFunction) : .struct(.function), default: [:]][typeNode.print(using: .interface), default: []].append(symbol) +// } else if let enumNode = functionNode.children.first, enumNode.kind == .enum { +// let typeNode = Node(kind: .global) { +// Node(kind: .type, child: enumNode) +// } +// entry.symbolsByKind[isStatic ? .enum(.staticFunction) : .enum(.function), default: [:]][typeNode.print(using: .interface), default: []].append(symbol) +// } else if let extensionNode = functionNode.children.first, extensionNode.kind == .extension { +// if let structureNode = extensionNode.children.at(1), structureNode.kind == .structure { +// let typeNode = Node(kind: .global) { +// Node(kind: .type, child: structureNode) +// } +// entry.symbolsByKind[isStatic ? .struct(.staticFunctionInExtension) : .struct(.functionInExtension), default: [:]][typeNode.print(using: .interface), default: []].append(symbol) +// } else if let enumNode = extensionNode.children.at(1), enumNode.kind == .enum { +// let typeNode = Node(kind: .global) { +// Node(kind: .type, child: enumNode) +// } +// entry.symbolsByKind[isStatic ? .enum(.staticFunctionInExtension) : .enum(.functionInExtension), default: [:]][typeNode.print(using: .interface), default: []].append(symbol) +// } +// } +// } else if let propertyNode = node.children.first, propertyNode.kind == .getter || propertyNode.kind == .setter || propertyNode.kind == .modifyAccessor, let variableNode = propertyNode.children.first, variableNode.kind == .variable { +// if let structureNode = variableNode.children.first, structureNode.kind == .structure { +// let typeNode = Node(kind: .global) { +// Node(kind: .type, child: structureNode) +// } +// entry.symbolsByKind[isStatic ? .struct(.staticVariable) : .struct(.variable), default: [:]][typeNode.print(using: .interface), default: []].append(symbol) +// } else if let enumNode = variableNode.children.first, enumNode.kind == .enum { +// let typeNode = Node(kind: .global) { +// Node(kind: .type, child: enumNode) +// } +// entry.symbolsByKind[isStatic ? .enum(.staticVariable) : .enum(.variable), default: [:]][typeNode.print(using: .interface), default: []].append(symbol) +// } else if let extensionNode = variableNode.children.first, extensionNode.kind == .extension { +// if let structureNode = extensionNode.children.at(1), structureNode.kind == .structure { +// let typeNode = Node(kind: .global) { +// Node(kind: .type, child: structureNode) +// } +// entry.symbolsByKind[isStatic ? .struct(.staticVariableInExtension) : .struct(.variableInExtension), default: [:]][typeNode.print(using: .interface), default: []].append(symbol) +// } else if let enumNode = extensionNode.children.at(1), enumNode.kind == .enum { +// let typeNode = Node(kind: .global) { +// Node(kind: .type, child: enumNode) +// } +// entry.symbolsByKind[isStatic ? .enum(.staticVariableInExtension) : .enum(.variableInExtension), default: [:]][typeNode.print(using: .interface), default: []].append(symbol) +// } +// } +// } +// } + func perform(_ node: Node, isStatic: Bool) { - if let functionNode = node.children.first, functionNode.kind == .function { - if let structureNode = functionNode.children.first, structureNode.kind == .structure { - let typeNode = Node(kind: .global) { - Node(kind: .type, child: structureNode) - } - entry.symbolsByKind[isStatic ? .struct(.staticFunction) : .struct(.function), default: [:]][typeNode.print(using: .interface), default: []].append(symbol) - } else if let enumNode = functionNode.children.first, enumNode.kind == .enum { - let typeNode = Node(kind: .global) { - Node(kind: .type, child: enumNode) + guard let firstChild = node.children.first else { return } + + func processTypeNode(_ typeNode: Node?, inExtension: Bool) { + guard let typeNode = typeNode else { return } + + let kind: IndexKind + switch firstChild.kind { + case .allocator: + kind = inExtension ? .allocatorInExtension : .allocator + case .function: + if isStatic { + kind = inExtension ? .staticFunctionInExtension : .staticFunction + } else { + kind = inExtension ? .functionInExtension : .function } - entry.symbolsByKind[isStatic ? .enum(.staticFunction) : .enum(.function), default: [:]][typeNode.print(using: .interface), default: []].append(symbol) - } else if let extensionNode = functionNode.children.first, extensionNode.kind == .extension { - if let structureNode = extensionNode.children.at(1), structureNode.kind == .structure { - let typeNode = Node(kind: .global) { - Node(kind: .type, child: structureNode) - } - entry.symbolsByKind[isStatic ? .struct(.staticFunctionInExtension) : .struct(.functionInExtension), default: [:]][typeNode.print(using: .interface), default: []].append(symbol) - } else if let enumNode = extensionNode.children.at(1), enumNode.kind == .enum { - let typeNode = Node(kind: .global) { - Node(kind: .type, child: enumNode) - } - entry.symbolsByKind[isStatic ? .enum(.staticFunctionInExtension) : .enum(.functionInExtension), default: [:]][typeNode.print(using: .interface), default: []].append(symbol) + case .getter, + .setter, + .modifyAccessor: + if isStatic { + kind = inExtension ? .staticVariableInExtension : .staticVariable + } else { + kind = inExtension ? .variableInExtension : .variable } + default: + return } - } else if let propertyNode = node.children.first, propertyNode.kind == .getter || propertyNode.kind == .setter || propertyNode.kind == .modifyAccessor, let variableNode = propertyNode.children.first, variableNode.kind == .variable { - if let structureNode = variableNode.children.first, structureNode.kind == .structure { - let typeNode = Node(kind: .global) { - Node(kind: .type, child: structureNode) - } - entry.symbolsByKind[isStatic ? .struct(.staticVariable) : .struct(.variable), default: [:]][typeNode.print(using: .interface), default: []].append(symbol) - } else if let enumNode = variableNode.children.first, enumNode.kind == .enum { - let typeNode = Node(kind: .global) { - Node(kind: .type, child: enumNode) - } - entry.symbolsByKind[isStatic ? .enum(.staticVariable) : .enum(.variable), default: [:]][typeNode.print(using: .interface), default: []].append(symbol) - } else if let extensionNode = variableNode.children.first, extensionNode.kind == .extension { - if let structureNode = extensionNode.children.at(1), structureNode.kind == .structure { - let typeNode = Node(kind: .global) { - Node(kind: .type, child: structureNode) - } - entry.symbolsByKind[isStatic ? .struct(.staticVariableInExtension) : .struct(.variableInExtension), default: [:]][typeNode.print(using: .interface), default: []].append(symbol) - } else if let enumNode = extensionNode.children.at(1), enumNode.kind == .enum { - let typeNode = Node(kind: .global) { - Node(kind: .type, child: enumNode) - } - entry.symbolsByKind[isStatic ? .enum(.staticVariableInExtension) : .enum(.variableInExtension), default: [:]][typeNode.print(using: .interface), default: []].append(symbol) - } + + let globalTypeNode = Node(kind: .global) { + Node(kind: .type, child: typeNode) + } + + entry.symbolsByKind[kind, default: [:]][globalTypeNode.print(using: .interface), default: []].append(symbol) + } + + switch firstChild.kind { + case .function, + .allocator: + if firstChild.children.first?.kind == .extension { + processTypeNode(firstChild.children.first?.children.at(1), inExtension: true) + } else { + processTypeNode(firstChild.children.first, inExtension: false) + } + case .getter, + .setter, + .modifyAccessor: + guard let variableNode = firstChild.children.first, variableNode.kind == .variable else { return } + if variableNode.children.first?.kind == .extension { + processTypeNode(variableNode.children.first?.children.at(1), inExtension: true) + } else { + processTypeNode(variableNode.children.first, inExtension: false) } + default: + break } } diff --git a/Sources/MachOSymbols/Symbols/PropertySymbol.swift b/Sources/MachOSymbols/Symbols/PropertySymbol.swift index 649a2c16..eddd9a32 100644 --- a/Sources/MachOSymbols/Symbols/PropertySymbol.swift +++ b/Sources/MachOSymbols/Symbols/PropertySymbol.swift @@ -21,3 +21,20 @@ package struct PropertySymbol { package let isInExtension: Bool } + +package struct FunctionSymbol { + package let symbol: Symbol + + package let identifier: String + + package let isStatic: Bool + + package let isInExtension: Bool +} + + +package struct AllocatorSymbol { + package let symbol: Symbol + + package let identifier: String +} diff --git a/Sources/MachOTestingSupport/DumpableTest.swift b/Sources/MachOTestingSupport/DumpableTest.swift index d8a10ed5..00a1fbfd 100644 --- a/Sources/MachOTestingSupport/DumpableTest.swift +++ b/Sources/MachOTestingSupport/DumpableTest.swift @@ -85,6 +85,16 @@ extension DumpableTest { try AssociatedType(descriptor: associatedTypeDescriptor, in: machO).dump(using: .test, in: machO).string.print() } } + + + @MachOImageGenerator + @MainActor + package func dumpBuiltinTypes(for machO: MachOFile) async throws { + let descriptors = try machO.swift.builtinTypeDescriptors + for descriptor in descriptors { + print(try BuiltinType(descriptor: descriptor, in: machO)) + } + } } diff --git a/Sources/SwiftDump/Dumpable+/Enum+Dumpable.swift b/Sources/SwiftDump/Dumpable+/Enum+Dumpable.swift index 30aeea01..f861e1a2 100644 --- a/Sources/SwiftDump/Dumpable+/Enum+Dumpable.swift +++ b/Sources/SwiftDump/Dumpable+/Enum+Dumpable.swift @@ -67,52 +67,28 @@ extension Enum: NamedDumpable { } } - for kind in SymbolIndexStore.IndexKind.SubKind.allCases.map({ SymbolIndexStore.IndexKind.enum($0) }) { - for (offset, function) in SymbolIndexStore.shared.symbols(of: kind, for: try dumpName(using: .interface, in: machOFile).string, in: machOFile).offsetEnumerated() { - + for kind in SymbolIndexStore.IndexKind.allCases { + for (offset, function) in try SymbolIndexStore.shared.symbols(of: kind, for: dumpName(using: .interface, in: machOFile).string, in: machOFile).offsetEnumerated() { if offset.isStart { BreakLine() - + Indent(level: 1) - - switch kind { - case .enum(let subKind): - switch subKind { - case .function: - InlineComment("Functions") - case .functionInExtension: - InlineComment("Functions In Extension") - case .staticFunction: - InlineComment("Static Functions") - case .staticFunctionInExtension: - InlineComment("Static Functions In Extension") - case .variable: - InlineComment("Variables") - case .variableInExtension: - InlineComment("Variables In Extension") - case .staticVariable: - InlineComment("Static Variables") - case .staticVariableInExtension: - InlineComment("Static Variables In Extension") - } - default: - fatalError("unreachable") - } + + InlineComment(kind.description) } - + BreakLine() - + Indent(level: 1) - + try MetadataReader.demangleSymbol(for: function, in: machOFile).printSemantic(using: options) - + if offset.isEnd { BreakLine() } } } - - + Standard("}") } } diff --git a/Sources/SwiftDump/Dumpable+/Struct+Dumpable.swift b/Sources/SwiftDump/Dumpable+/Struct+Dumpable.swift index c5c944e0..a0e05a7a 100644 --- a/Sources/SwiftDump/Dumpable+/Struct+Dumpable.swift +++ b/Sources/SwiftDump/Dumpable+/Struct+Dumpable.swift @@ -6,19 +6,18 @@ import Semantic import MachOSymbols extension Struct: NamedDumpable { - @MachOImageGenerator public func dumpName(using options: DemangleOptions, in machOFile: MachOFile) throws -> SemanticString { try MetadataReader.demangleContext(for: .type(.struct(descriptor)), in: machOFile).printSemantic(using: options).replacingTypeNameOrOtherToTypeDeclaration() } - + @MachOImageGenerator @SemanticStringBuilder public func dump(using options: DemangleOptions, in machOFile: MachOFile) throws -> SemanticString { Keyword(.struct) - + Space() - + try dumpName(using: options, in: machOFile) if let genericContext { @@ -30,21 +29,20 @@ extension Struct: NamedDumpable { try genericContext.dumpGenericRequirements(using: options, in: machOFile) } } - + Space() - + Standard("{") - + for (offset, fieldRecord) in try descriptor.fieldDescriptor(in: machOFile).records(in: machOFile).offsetEnumerated() { - BreakLine() - + Indent(level: 1) let demangledTypeNode = try MetadataReader.demangleType(for: fieldRecord.mangledTypeName(in: machOFile), in: machOFile) - + let fieldName = try fieldRecord.fieldName(in: machOFile) - + if fieldRecord.flags.contains(.isVariadic) { if demangledTypeNode.hasWeakNode { Keyword(.weak) @@ -75,51 +73,28 @@ extension Struct: NamedDumpable { } } - for kind in SymbolIndexStore.IndexKind.SubKind.allCases.map({ SymbolIndexStore.IndexKind.struct($0) }) { - for (offset, symbol) in SymbolIndexStore.shared.symbols(of: kind, for: try dumpName(using: .interface, in: machOFile).string, in: machOFile).offsetEnumerated() { - + for kind in SymbolIndexStore.IndexKind.allCases { + for (offset, symbol) in try SymbolIndexStore.shared.symbols(of: kind, for: dumpName(using: .interface, in: machOFile).string, in: machOFile).offsetEnumerated() { if offset.isStart { BreakLine() - + Indent(level: 1) - - switch kind { - case .struct(let subKind): - switch subKind { - case .function: - InlineComment("Functions") - case .functionInExtension: - InlineComment("Functions In Extension") - case .staticFunction: - InlineComment("Static Functions") - case .staticFunctionInExtension: - InlineComment("Static Functions In Extension") - case .variable: - InlineComment("Variables") - case .variableInExtension: - InlineComment("Variables In Extension") - case .staticVariable: - InlineComment("Static Variables") - case .staticVariableInExtension: - InlineComment("Static Variables In Extension") - } - default: - fatalError("unreachable") - } + + InlineComment(kind.description) } - + BreakLine() - + Indent(level: 1) - + try MetadataReader.demangleSymbol(for: symbol, in: machOFile).printSemantic(using: options) - + if offset.isEnd { BreakLine() } } } - + Standard("}") } } diff --git a/Tests/MachOSwiftSectionTests/SymbolDemangleTests.swift b/Tests/MachOSwiftSectionTests/SymbolDemangleTests.swift index 88ed495c..99227b11 100644 --- a/Tests/MachOSwiftSectionTests/SymbolDemangleTests.swift +++ b/Tests/MachOSwiftSectionTests/SymbolDemangleTests.swift @@ -45,14 +45,18 @@ struct SymbolDemangleTests { } @Test func symbolsInSwiftUI() async throws { + var string = "" let symbols = try symbols(for: .SwiftUI) for symbol in symbols { let swiftStdlibDemangledName = stdlib_demangleName(symbol.stringValue) guard !symbol.stringValue.hasSuffix("$delayInitStub") else { continue } - print(symbol.stringValue) - print(swiftStdlibDemangledName) - print("\n") + string += symbol.stringValue + string += "\n" + string += swiftStdlibDemangledName + string += "\n" + string += "\n" } + try string.write(to: .desktopDirectory.appendingPathComponent("SwiftUISwiftSymbols.txt"), atomically: true, encoding: .utf8) } @Test func demangle() async throws { diff --git a/Tests/SwiftDumpTests/DyldCacheDumpTests.swift b/Tests/SwiftDumpTests/DyldCacheDumpTests.swift index a77f7182..1cf38ba9 100644 --- a/Tests/SwiftDumpTests/DyldCacheDumpTests.swift +++ b/Tests/SwiftDumpTests/DyldCacheDumpTests.swift @@ -82,4 +82,16 @@ extension DyldCacheDumpTests { @Test func associatedTypesInSubCacheFile() async throws { try await dumpAssociatedTypes(for: machOFileInSubCache) } + + @Test func builtinTypesInCacheFile() async throws { + try await dumpBuiltinTypes(for: machOFileInCache) + } + + @Test func builtinTypesInMainCacheFile() async throws { + try await dumpBuiltinTypes(for: machOFileInMainCache) + } + + @Test func builtinTypesInSubCacheFile() async throws { + try await dumpBuiltinTypes(for: machOFileInSubCache) + } } From 6e4e8e6705ee81e34ab5772dd207912f938d6264 Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Sat, 28 Jun 2025 20:36:21 +0800 Subject: [PATCH 04/10] Optimize `Demangle` target --- Sources/Demangle/Interface.swift | 6 +- Sources/Demangle/Main/Node/Node.swift | 247 ------------------ .../Node/Node+ChildrenBuilder.swift | 0 .../{Main => }/Node/Node+Collection.swift | 0 Sources/Demangle/Node/Node+Conversions.swift | 99 +++++++ .../Node/Node+CustomStringConvertible.swift | 0 Sources/Demangle/Node/Node+Hashable.swift | 11 + .../Demangle/{Main => }/Node/Node+Kind.swift | 0 Sources/Demangle/Node/Node.swift | 127 +++++++++ Sources/Demangle/Utils/Extensions.swift | 21 ++ 10 files changed, 260 insertions(+), 251 deletions(-) delete mode 100644 Sources/Demangle/Main/Node/Node.swift rename Sources/Demangle/{Main => }/Node/Node+ChildrenBuilder.swift (100%) rename Sources/Demangle/{Main => }/Node/Node+Collection.swift (100%) create mode 100644 Sources/Demangle/Node/Node+Conversions.swift rename Sources/Demangle/{Main => }/Node/Node+CustomStringConvertible.swift (100%) create mode 100644 Sources/Demangle/Node/Node+Hashable.swift rename Sources/Demangle/{Main => }/Node/Node+Kind.swift (100%) create mode 100644 Sources/Demangle/Node/Node.swift diff --git a/Sources/Demangle/Interface.swift b/Sources/Demangle/Interface.swift index 03eabecb..1315fd98 100644 --- a/Sources/Demangle/Interface.swift +++ b/Sources/Demangle/Interface.swift @@ -5,8 +5,8 @@ /// - isType: if true, no prefix is parsed and, on completion, the first item on the parse stack is returned. /// - Returns: the successfully parsed result /// - Throws: a SwiftSymbolParseError error that contains parse position when the error occurred. -package func demangleAsNode(_ mangled: String, isType: Bool = false) throws -> Node { - return try demangleAsNode(mangled.unicodeScalars, isType: isType) +package func demangleAsNode(_ mangled: String, isType: Bool = false, symbolicReferenceResolver: SymbolicReferenceResolver? = nil) throws -> Node { + return try demangleAsNode(mangled.unicodeScalars, isType: isType, symbolicReferenceResolver: symbolicReferenceResolver) } /// Pass a collection of `UnicodeScalars` containing a Swift mangled symbol or type, get a parsed SwiftSymbol structure which can then be directly examined or printed. @@ -27,5 +27,3 @@ package func demangleAsNode(_ mangled: C, isType: Bool = false, s return try demangler.demangleSwift3TopLevelSymbol() } } - - diff --git a/Sources/Demangle/Main/Node/Node.swift b/Sources/Demangle/Main/Node/Node.swift deleted file mode 100644 index 7bb72584..00000000 --- a/Sources/Demangle/Main/Node/Node.swift +++ /dev/null @@ -1,247 +0,0 @@ -public final class Node: @unchecked Sendable { - public let kind: Kind - public let contents: Contents - public private(set) weak var parent: Node? - public private(set) var children: [Node] - - public enum Contents: Hashable, Sendable { - case none - case index(UInt64) - case name(String) - - public var hasName: Bool { - name != nil - } - - public var name: String? { - switch self { - case .none: - return nil - case .index: - return nil - case .name(let string): - return string - } - } - } - - public init(kind: Kind, contents: Contents = .none, children: [Node] = []) { - self.kind = kind - self.children = children - self.contents = contents - for child in children { - child.parent = self - } - } - - package convenience init(kind: Kind, child: Node) { - self.init(kind: kind, contents: .none, children: [child]) - } - - package convenience init(typeWithChildKind: Kind, childChild: Node) { - self.init(kind: .type, contents: .none, children: [Node(kind: typeWithChildKind, children: [childChild])]) - } - - package convenience init(typeWithChildKind: Kind, childChildren: [Node]) { - self.init(kind: .type, contents: .none, children: [Node(kind: typeWithChildKind, children: childChildren)]) - } - - package convenience init(swiftStdlibTypeKind: Kind, name: String) { - self.init(kind: .type, contents: .none, children: [Node(kind: swiftStdlibTypeKind, children: [ - Node(kind: .module, contents: .name(stdlibName)), - Node(kind: .identifier, contents: .name(name)), - ])]) - } - - package convenience init(swiftBuiltinType: Kind, name: String) { - self.init(kind: .type, children: [Node(kind: swiftBuiltinType, contents: .name(name))]) - } - - public var text: String? { - switch contents { - case .name(let s): return s - default: return nil - } - } - - public var index: UInt64? { - switch contents { - case .index(let i): return i - default: return nil - } - } - - public var isProtocol: Bool { - switch kind { - case .type: return children.first?.isProtocol ?? false - case .protocol, - .protocolSymbolicReference, - .objectiveCProtocolSymbolicReference: return true - default: return false - } - } - - package func changeChild(_ newChild: Node?, atIndex: Int) -> Node { - guard children.indices.contains(atIndex) else { return self } - - var modifiedChildren = children - if let nc = newChild { - modifiedChildren[atIndex] = nc - } else { - modifiedChildren.remove(at: atIndex) - } - return Node(kind: kind, contents: contents, children: modifiedChildren) - } - - package func changeKind(_ newKind: Kind, additionalChildren: [Node] = []) -> Node { - if case .name(let text) = contents { - return Node(kind: newKind, contents: .name(text), children: children + additionalChildren) - } else if case .index(let i) = contents { - return Node(kind: newKind, contents: .index(i), children: children + additionalChildren) - } else { - return Node(kind: newKind, contents: .none, children: children + additionalChildren) - } - } - - public func addChild(_ newChild: Node) { - newChild.parent = self - children.append(newChild) - } - - public func removeChild(at index: Int) { - guard children.indices.contains(index) else { return } - children.remove(at: index) - } - - public func insertChild(_ newChild: Node, at index: Int) { - guard index >= 0, index <= children.count else { return } - newChild.parent = self - children.insert(newChild, at: index) - } - - public func addChildren(_ newChildren: [Node]) { - for child in newChildren { - child.parent = self - } - children.append(contentsOf: newChildren) - } - - public func setChildren(_ newChildren: [Node]) { - for child in newChildren { - child.parent = self - } - children = newChildren - } - - public func setChild(_ child: Node, at index: Int) { - guard children.indices.contains(index) else { return } - child.parent = self - children[index] = child - } - - public func reverseChildren() { - children.reverse() - } - - public func reverseFirst(_ count: Int) { - children.reverseFirst(count) - } -} - -extension Node { - public var isSimpleType: Bool { - switch kind { - case .associatedType: fallthrough - case .associatedTypeRef: fallthrough - case .boundGenericClass: fallthrough - case .boundGenericEnum: fallthrough - case .boundGenericFunction: fallthrough - case .boundGenericOtherNominalType: fallthrough - case .boundGenericProtocol: fallthrough - case .boundGenericStructure: fallthrough - case .boundGenericTypeAlias: fallthrough - case .builtinTypeName: fallthrough - case .builtinTupleType: fallthrough - case .builtinFixedArray: fallthrough - case .class: fallthrough - case .dependentGenericType: fallthrough - case .dependentMemberType: fallthrough - case .dependentGenericParamType: fallthrough - case .dynamicSelf: fallthrough - case .enum: fallthrough - case .errorType: fallthrough - case .existentialMetatype: fallthrough - case .integer: fallthrough - case .labelList: fallthrough - case .metatype: fallthrough - case .metatypeRepresentation: fallthrough - case .module: fallthrough - case .negativeInteger: fallthrough - case .otherNominalType: fallthrough - case .pack: fallthrough - case .protocol: fallthrough - case .protocolSymbolicReference: fallthrough - case .returnType: fallthrough - case .silBoxType: fallthrough - case .silBoxTypeWithLayout: fallthrough - case .structure: fallthrough - case .sugaredArray: fallthrough - case .sugaredDictionary: fallthrough - case .sugaredOptional: fallthrough - case .sugaredParen: return true - case .tuple: fallthrough - case .tupleElementName: fallthrough - case .typeAlias: fallthrough - case .typeList: fallthrough - case .typeSymbolicReference: return true - case .type: - return children.first.map { $0.isSimpleType } ?? false - case .protocolList: - return children.first.map { $0.children.count <= 1 } ?? false - case .protocolListWithAnyObject: - return (children.first?.children.first).map { $0.children.count == 0 } ?? false - default: return false - } - } - - public var needSpaceBeforeType: Bool { - switch kind { - case .type: return children.first?.needSpaceBeforeType ?? false - case .functionType, - .noEscapeFunctionType, - .uncurriedFunctionType, - .dependentGenericType: return false - default: return true - } - } - - public func isIdentifier(desired: String) -> Bool { - return kind == .identifier && text == desired - } - - public var isSwiftModule: Bool { - return kind == .module && text == stdlibName - } -} - - -extension Array { - /// Reverse the first n elements of the array - /// - Parameter count: Number of elements to reverse from the beginning - mutating func reverseFirst(_ count: Int) { - guard count > 0 && count <= self.count else { return } - let endIndex = count - 1 - for i in 0..<(count / 2) { - self.swapAt(i, endIndex - i) - } - } - - /// Returns a new array with the first n elements reversed - /// - Parameter count: Number of elements to reverse from the beginning - /// - Returns: New array with first n elements reversed - func reversedFirst(_ count: Int) -> Array { - var result = self - result.reverseFirst(count) - return result - } -} diff --git a/Sources/Demangle/Main/Node/Node+ChildrenBuilder.swift b/Sources/Demangle/Node/Node+ChildrenBuilder.swift similarity index 100% rename from Sources/Demangle/Main/Node/Node+ChildrenBuilder.swift rename to Sources/Demangle/Node/Node+ChildrenBuilder.swift diff --git a/Sources/Demangle/Main/Node/Node+Collection.swift b/Sources/Demangle/Node/Node+Collection.swift similarity index 100% rename from Sources/Demangle/Main/Node/Node+Collection.swift rename to Sources/Demangle/Node/Node+Collection.swift diff --git a/Sources/Demangle/Node/Node+Conversions.swift b/Sources/Demangle/Node/Node+Conversions.swift new file mode 100644 index 00000000..976ba620 --- /dev/null +++ b/Sources/Demangle/Node/Node+Conversions.swift @@ -0,0 +1,99 @@ +extension Node { + public var text: String? { + switch contents { + case .name(let s): return s + default: return nil + } + } + + public var index: UInt64? { + switch contents { + case .index(let i): return i + default: return nil + } + } + + public var isProtocol: Bool { + switch kind { + case .type: return children.first?.isProtocol ?? false + case .protocol, + .protocolSymbolicReference, + .objectiveCProtocolSymbolicReference: return true + default: return false + } + } + + public var isSimpleType: Bool { + switch kind { + case .associatedType: fallthrough + case .associatedTypeRef: fallthrough + case .boundGenericClass: fallthrough + case .boundGenericEnum: fallthrough + case .boundGenericFunction: fallthrough + case .boundGenericOtherNominalType: fallthrough + case .boundGenericProtocol: fallthrough + case .boundGenericStructure: fallthrough + case .boundGenericTypeAlias: fallthrough + case .builtinTypeName: fallthrough + case .builtinTupleType: fallthrough + case .builtinFixedArray: fallthrough + case .class: fallthrough + case .dependentGenericType: fallthrough + case .dependentMemberType: fallthrough + case .dependentGenericParamType: fallthrough + case .dynamicSelf: fallthrough + case .enum: fallthrough + case .errorType: fallthrough + case .existentialMetatype: fallthrough + case .integer: fallthrough + case .labelList: fallthrough + case .metatype: fallthrough + case .metatypeRepresentation: fallthrough + case .module: fallthrough + case .negativeInteger: fallthrough + case .otherNominalType: fallthrough + case .pack: fallthrough + case .protocol: fallthrough + case .protocolSymbolicReference: fallthrough + case .returnType: fallthrough + case .silBoxType: fallthrough + case .silBoxTypeWithLayout: fallthrough + case .structure: fallthrough + case .sugaredArray: fallthrough + case .sugaredDictionary: fallthrough + case .sugaredOptional: fallthrough + case .sugaredParen: return true + case .tuple: fallthrough + case .tupleElementName: fallthrough + case .typeAlias: fallthrough + case .typeList: fallthrough + case .typeSymbolicReference: return true + case .type: + return children.first.map { $0.isSimpleType } ?? false + case .protocolList: + return children.first.map { $0.children.count <= 1 } ?? false + case .protocolListWithAnyObject: + return (children.first?.children.first).map { $0.children.count == 0 } ?? false + default: return false + } + } + + public var needSpaceBeforeType: Bool { + switch kind { + case .type: return children.first?.needSpaceBeforeType ?? false + case .functionType, + .noEscapeFunctionType, + .uncurriedFunctionType, + .dependentGenericType: return false + default: return true + } + } + + public func isIdentifier(desired: String) -> Bool { + return kind == .identifier && text == desired + } + + public var isSwiftModule: Bool { + return kind == .module && text == stdlibName + } +} diff --git a/Sources/Demangle/Main/Node/Node+CustomStringConvertible.swift b/Sources/Demangle/Node/Node+CustomStringConvertible.swift similarity index 100% rename from Sources/Demangle/Main/Node/Node+CustomStringConvertible.swift rename to Sources/Demangle/Node/Node+CustomStringConvertible.swift diff --git a/Sources/Demangle/Node/Node+Hashable.swift b/Sources/Demangle/Node/Node+Hashable.swift new file mode 100644 index 00000000..a8e8f464 --- /dev/null +++ b/Sources/Demangle/Node/Node+Hashable.swift @@ -0,0 +1,11 @@ +extension Node: Hashable { + public func hash(into hasher: inout Hasher) { + hasher.combine(kind) + hasher.combine(contents) + hasher.combine(children) + } + + public static func == (lhs: Node, rhs: Node) -> Bool { + return lhs.kind == rhs.kind && lhs.contents == rhs.contents && lhs.children == rhs.children + } +} diff --git a/Sources/Demangle/Main/Node/Node+Kind.swift b/Sources/Demangle/Node/Node+Kind.swift similarity index 100% rename from Sources/Demangle/Main/Node/Node+Kind.swift rename to Sources/Demangle/Node/Node+Kind.swift diff --git a/Sources/Demangle/Node/Node.swift b/Sources/Demangle/Node/Node.swift new file mode 100644 index 00000000..9677287d --- /dev/null +++ b/Sources/Demangle/Node/Node.swift @@ -0,0 +1,127 @@ +public final class Node: @unchecked Sendable { + public let kind: Kind + public let contents: Contents + public private(set) weak var parent: Node? + public private(set) var children: [Node] + + public enum Contents: Hashable, Sendable { + case none + case index(UInt64) + case name(String) + + public var hasName: Bool { + name != nil + } + + public var name: String? { + switch self { + case .none: + return nil + case .index: + return nil + case .name(let string): + return string + } + } + } + + public init(kind: Kind, contents: Contents = .none, children: [Node] = []) { + self.kind = kind + self.children = children + self.contents = contents + for child in children { + child.parent = self + } + } + + package convenience init(kind: Kind, child: Node) { + self.init(kind: kind, contents: .none, children: [child]) + } + + package convenience init(typeWithChildKind: Kind, childChild: Node) { + self.init(kind: .type, contents: .none, children: [Node(kind: typeWithChildKind, children: [childChild])]) + } + + package convenience init(typeWithChildKind: Kind, childChildren: [Node]) { + self.init(kind: .type, contents: .none, children: [Node(kind: typeWithChildKind, children: childChildren)]) + } + + package convenience init(swiftStdlibTypeKind: Kind, name: String) { + self.init(kind: .type, contents: .none, children: [Node(kind: swiftStdlibTypeKind, children: [ + Node(kind: .module, contents: .name(stdlibName)), + Node(kind: .identifier, contents: .name(name)), + ])]) + } + + package convenience init(swiftBuiltinType: Kind, name: String) { + self.init(kind: .type, children: [Node(kind: swiftBuiltinType, contents: .name(name))]) + } +} + +extension Node { + package func changeChild(_ newChild: Node?, atIndex: Int) -> Node { + guard children.indices.contains(atIndex) else { return self } + + var modifiedChildren = children + if let nc = newChild { + modifiedChildren[atIndex] = nc + } else { + modifiedChildren.remove(at: atIndex) + } + return Node(kind: kind, contents: contents, children: modifiedChildren) + } + + package func changeKind(_ newKind: Kind, additionalChildren: [Node] = []) -> Node { + if case .name(let text) = contents { + return Node(kind: newKind, contents: .name(text), children: children + additionalChildren) + } else if case .index(let i) = contents { + return Node(kind: newKind, contents: .index(i), children: children + additionalChildren) + } else { + return Node(kind: newKind, contents: .none, children: children + additionalChildren) + } + } + + package func addChild(_ newChild: Node) { + newChild.parent = self + children.append(newChild) + } + + package func removeChild(at index: Int) { + guard children.indices.contains(index) else { return } + children.remove(at: index) + } + + package func insertChild(_ newChild: Node, at index: Int) { + guard index >= 0, index <= children.count else { return } + newChild.parent = self + children.insert(newChild, at: index) + } + + package func addChildren(_ newChildren: [Node]) { + for child in newChildren { + child.parent = self + } + children.append(contentsOf: newChildren) + } + + package func setChildren(_ newChildren: [Node]) { + for child in newChildren { + child.parent = self + } + children = newChildren + } + + package func setChild(_ child: Node, at index: Int) { + guard children.indices.contains(index) else { return } + child.parent = self + children[index] = child + } + + package func reverseChildren() { + children.reverse() + } + + package func reverseFirst(_ count: Int) { + children.reverseFirst(count) + } +} diff --git a/Sources/Demangle/Utils/Extensions.swift b/Sources/Demangle/Utils/Extensions.swift index 6a263743..74bed560 100644 --- a/Sources/Demangle/Utils/Extensions.swift +++ b/Sources/Demangle/Utils/Extensions.swift @@ -73,3 +73,24 @@ extension UnicodeScalar { return isLower || isUpper } } + +extension Array { + /// Reverse the first n elements of the array + /// - Parameter count: Number of elements to reverse from the beginning + mutating func reverseFirst(_ count: Int) { + guard count > 0, count <= self.count else { return } + let endIndex = count - 1 + for i in 0 ..< (count / 2) { + swapAt(i, endIndex - i) + } + } + + /// Returns a new array with the first n elements reversed + /// - Parameter count: Number of elements to reverse from the beginning + /// - Returns: New array with first n elements reversed + func reversedFirst(_ count: Int) -> Array { + var result = self + result.reverseFirst(count) + return result + } +} From 2e7e37af7ae6144ee5865d66e091b1860343e4cc Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Sat, 28 Jun 2025 20:36:37 +0800 Subject: [PATCH 05/10] Optimize Testing --- ...DumpableTest.swift => DumpableTests.swift} | 4 +- .../MachOTestingSupport/DyldCacheTests.swift | 38 +++ .../MachOTestingSupport/MachOFileName.swift | 2 + .../MachOTestingSupport/MachOFileTests.swift | 27 ++ .../MachOTestingSupport/MachOImageTests.swift | 15 ++ .../SwiftStdlibDemangle.swift | 6 +- .../XcodeMachOFileName.swift | 248 ++++++++++++++++++ .../XcodeMachOFileTests.swift | 25 ++ .../PrimitiveTypeMappingTests.swift | 18 ++ .../SymbolDemangleTests.swift | 17 +- Tests/SwiftDumpTests/DyldCacheDumpTests.swift | 26 +- Tests/SwiftDumpTests/MachOFileDumpTests.swift | 16 +- .../SwiftDumpTests/MachOImageDumpTests.swift | 8 +- .../XcodeMachOFileDumpTests.swift | 29 ++ 14 files changed, 414 insertions(+), 65 deletions(-) rename Sources/MachOTestingSupport/{DumpableTest.swift => DumpableTests.swift} (98%) create mode 100644 Sources/MachOTestingSupport/DyldCacheTests.swift create mode 100644 Sources/MachOTestingSupport/MachOFileTests.swift create mode 100644 Sources/MachOTestingSupport/MachOImageTests.swift rename {Tests/MachOSwiftSectionTests => Sources/MachOTestingSupport}/SwiftStdlibDemangle.swift (92%) create mode 100644 Sources/MachOTestingSupport/XcodeMachOFileName.swift create mode 100644 Sources/MachOTestingSupport/XcodeMachOFileTests.swift create mode 100644 Tests/MachOSwiftSectionTests/PrimitiveTypeMappingTests.swift create mode 100644 Tests/SwiftDumpTests/XcodeMachOFileDumpTests.swift diff --git a/Sources/MachOTestingSupport/DumpableTest.swift b/Sources/MachOTestingSupport/DumpableTests.swift similarity index 98% rename from Sources/MachOTestingSupport/DumpableTest.swift rename to Sources/MachOTestingSupport/DumpableTests.swift index 00a1fbfd..d908599d 100644 --- a/Sources/MachOTestingSupport/DumpableTest.swift +++ b/Sources/MachOTestingSupport/DumpableTests.swift @@ -5,11 +5,11 @@ import MachOFoundation import MachOSwiftSection import SwiftDump -package protocol DumpableTest { +package protocol DumpableTests { var isEnabledSearchMetadata: Bool { get } } -extension DumpableTest { +extension DumpableTests { package var isEnabledSearchMetadata: Bool { false } @MachOImageGenerator diff --git a/Sources/MachOTestingSupport/DyldCacheTests.swift b/Sources/MachOTestingSupport/DyldCacheTests.swift new file mode 100644 index 00000000..1a465ee5 --- /dev/null +++ b/Sources/MachOTestingSupport/DyldCacheTests.swift @@ -0,0 +1,38 @@ +import Foundation +import Testing +import MachOKit +import MachOMacro +import MachOFoundation + +package class DyldCacheTests { + package let mainCache: DyldCache + + package let subCache: DyldCache + + package let machOFileInMainCache: MachOFile + + package let machOFileInSubCache: MachOFile + + package let machOFileInCache: MachOFile + + package class var mainCacheImageName: MachOImageName { .SwiftUI } + + package class var subCacheImageName: MachOImageName { + if #available(macOS 15.5, *) { + return .CodableSwiftUI + } else { + return .UIKitCore + } + } + + package class var cacheImageName: MachOImageName { .AttributeGraph } + + package init() async throws { + self.mainCache = try DyldCache(path: .current) + self.subCache = try required(mainCache.subCaches?.first?.subcache(for: mainCache)) + + self.machOFileInMainCache = try #require(mainCache.machOFile(named: Self.mainCacheImageName)) + self.machOFileInSubCache = try #require(subCache.machOFile(named: Self.subCacheImageName)) + self.machOFileInCache = try #require(mainCache.machOFile(named: Self.cacheImageName)) + } +} diff --git a/Sources/MachOTestingSupport/MachOFileName.swift b/Sources/MachOTestingSupport/MachOFileName.swift index 08304812..12cc9f30 100644 --- a/Sources/MachOTestingSupport/MachOFileName.swift +++ b/Sources/MachOTestingSupport/MachOFileName.swift @@ -12,3 +12,5 @@ package enum MachOFileName: String { case ControlCenter = "/System/Library/CoreServices/ControlCenter.app" case Freeform = "/System/Applications/Freeform.app" } + + diff --git a/Sources/MachOTestingSupport/MachOFileTests.swift b/Sources/MachOTestingSupport/MachOFileTests.swift new file mode 100644 index 00000000..fcb037e5 --- /dev/null +++ b/Sources/MachOTestingSupport/MachOFileTests.swift @@ -0,0 +1,27 @@ +import Foundation +import Testing +import MachOKit +import MachOMacro +import MachOFoundation + +package class MachOFileTests { + package let machOFile: MachOFile + + package class var fileName: MachOFileName { .Finder } + + package class var preferredArchitecture: CPUType { .arm64 } + + package init() async throws { + let file = try loadFromFile(named: Self.fileName) + switch file { + case .fat(let fatFile): + self.machOFile = try required(fatFile.machOFiles().first(where: { $0.header.cpuType == Self.preferredArchitecture }) ?? fatFile.machOFiles().first) + case .machO(let machO): + self.machOFile = machO + @unknown default: + fatalError() + } + } +} + + diff --git a/Sources/MachOTestingSupport/MachOImageTests.swift b/Sources/MachOTestingSupport/MachOImageTests.swift new file mode 100644 index 00000000..4163367e --- /dev/null +++ b/Sources/MachOTestingSupport/MachOImageTests.swift @@ -0,0 +1,15 @@ +import Foundation +import Testing +import MachOKit +import MachOMacro +import MachOFoundation + +package class MachOImageTests { + package let machOImage: MachOImage + + package class var imageName: MachOImageName { .Foundation } + + package init() async throws { + self.machOImage = try #require(MachOImage(named: Self.imageName)) + } +} diff --git a/Tests/MachOSwiftSectionTests/SwiftStdlibDemangle.swift b/Sources/MachOTestingSupport/SwiftStdlibDemangle.swift similarity index 92% rename from Tests/MachOSwiftSectionTests/SwiftStdlibDemangle.swift rename to Sources/MachOTestingSupport/SwiftStdlibDemangle.swift index 5fb66425..9f26bdd5 100644 --- a/Tests/MachOSwiftSectionTests/SwiftStdlibDemangle.swift +++ b/Sources/MachOTestingSupport/SwiftStdlibDemangle.swift @@ -1,7 +1,7 @@ import Foundation @_silgen_name("swift_demangle") -internal func _stdlib_demangleImpl( +private func _stdlib_demangleImpl( mangledName: UnsafePointer?, mangledNameLength: UInt, outputBuffer: UnsafeMutablePointer?, @@ -9,7 +9,7 @@ internal func _stdlib_demangleImpl( flags: UInt32 ) -> UnsafeMutablePointer? -internal func stdlib_demangleName( +package func stdlib_demangleName( _ mangledName: String ) -> String { guard !mangledName.isEmpty else { return mangledName } @@ -29,7 +29,7 @@ internal func stdlib_demangleName( } } -internal func stdlib_demangleName( +package func stdlib_demangleName( _ mangledName: UnsafePointer ) -> UnsafePointer { let demangledNamePtr = _stdlib_demangleImpl( diff --git a/Sources/MachOTestingSupport/XcodeMachOFileName.swift b/Sources/MachOTestingSupport/XcodeMachOFileName.swift new file mode 100644 index 00000000..48073218 --- /dev/null +++ b/Sources/MachOTestingSupport/XcodeMachOFileName.swift @@ -0,0 +1,248 @@ +package enum XcodeMachOFileName { + package enum SharedFrameworks: String { + case AccessibilityAudit + case AccessibilitySupport + case AppResourceGeneration + case AppThinning + case AssetRuntimeSupport + case AuthenticationAPI + case CodeCompletionFoundation + case CodeCompletionKit + case CodeGenerationIntelligence + case CombineXPC + case CommonXcodeCloudAPI + case ContentDelivery + case ContentDeliveryServices + case CoreDocumentation + case CoreSymbolicationDT + case CreateMLSwiftProtobuf + case DNTDocumentationModel + case DNTDocumentationSupport + case DNTSourceKitSupport + case DNTTransformer + case DTDeviceKit + case DTDeviceKitBase + case DTDeviceServices + case DTGTGraph + case DTGTTimeline + case DTGanache + case DTGraphKit + case DTMLOpGraph + case DTXConnectionServices + case DVTAnalytics + case DVTAnalyticsClient + case DVTAnalyticsKit + case DVTAnalyticsMetrics + case DVTAnalyticsMetricsClient + case DVTAppStoreConnect + case DVTCocoaAdditionsKit + case DVTCoreDeviceCore + case DVTCoreGlyphs + case DVTCrashLogFoundation + case DVTDeviceFoundation + case DVTDeviceKit + case DVTDeviceProvisioning + case DVTDocumentation + case DVTExplorableKit + case DVTFeedbackReporting + case DVTFoundation + case DVTITunesSoftware + case DVTITunesSoftwareServiceFoundation + case DVTIconKit + case DVTInstrumentsAnalysisCore + case DVTInstrumentsFoundation + case DVTInstrumentsUtilities + case DVTKeychain + case DVTKeychainService + case DVTKeychainUtilities + case DVTKit + case DVTLibraryKit + case DVTMacroFoundation + case DVTMarkup + case DVTPlaygroundCommunication + case DVTPlaygroundStubMacServices + case DVTPortal + case DVTProducts + case DVTProductsUI + case DVTServices + case DVTSmartSearch + case DVTSourceControl + case DVTSourceEditor + case DVTStructuredLayoutKit + case DVTSystemPrerequisites + case DVTSystemPrerequisitesUI + case DVTUserInterfaceKit + case DVTViewControllerKit + case DebugHierarchyFoundation + case DebugHierarchyKit + case DebugSymbolsDT + case DeltaFoundation + case DeltaKit + case EventsAPI + case GLTools + case GLToolsAnalysisEngine + case GLToolsCore + case GLToolsExpert + case GLToolsInterface + case GLToolsServices + case GLToolsShaderProfiler + case GPUTools + case GPUToolsCore + case GPUToolsDesktopFoundation + case GPUToolsMobileFoundation + case GPUToolsPlatform + case GPUToolsRenderer + case GPUToolsServices + case GPUToolsShaderProfiler + case GameToolsFoundation + case HexFiend + case IDEAnalyticsMetrics + case IDECodeGenerationIntelligence + case IDEDistribution + case IDEDistributionKit + case IDEMLCompilerCore + case IDEMLModelCore + case IDEMLModelEditorKit + case IDEMLReferenceObjectCompilerCore + case IDEPlaygroundEditor + case IDEPlaygroundExecution + case IDEPlaygroundResultsFoundation + case IDEPlaygroundResultsKit + case IDEPlaygroundsFoundation + case IDEPlaygroundsKit + case IDEProducts + case IDEResultKit + case LLDB + case LLDBRPC + case LearnedFeatures + case LiveExecutionResultsFoundation + case LiveExecutionResultsHost + case Localization + case LoggingSupportHost + case MLCloudDeployment + case MLCloudDeploymentXPCServiceProtocol + case MLCore + case MLCoreKit + case MLDataAnnotation + case MLDataAnnotationKit + case MLDataSource + case MLDataSourceKit + case MLEvaluation + case MLEvaluationKit + case MLExperiment + case MLExplorationKit + case MLFeature + case MLModelCore + case MLModelFormatEditor + case MLModelKit + case MLPersistenceEngine + case MLPersistenceEntity + case MLRecipeCore + case MLRecipeExecutionController + case MLRecipeExecutionServiceProtocol + case MLShared + case MLSharedKit + case MLTraceReport + case MTLTools + case MTLToolsAnalysisEngine + case MTLToolsServices + case MTLToolsShaderProfiler + case MallocStackLoggingDT + case MarkupSupport + case Notarization + case ODTDevTool + case OpenAPIRuntime + case OpenAPIURLSession + case PackedPaths + case PreviewsDeveloperTools + case PreviewsFoundationHost + case PreviewsMessagingHost + case PreviewsModel + case PreviewsPipeline + case PreviewsPlatforms + case PreviewsScenes + case PreviewsSyntax + case PreviewsUI + case PreviewsXROSMessaging + case PreviewsXROSServices + case PreviewsXcodeUI + case RealityKitAdditions + case RealityKitInspection + case RealityToolKit + case RealityToolsDeviceSupport + case Recount + case SourceEditor + case SourceEditorRegExSupport + case SourceEditorSwiftSupport + case SourceKit + case SourceKitSupport + case SourceModel + case SourceModelSupport + case SpatialInspectorFoundation + case StatusAPI + case SwiftBasicFormat + case SwiftBuild + case SwiftDiagnostics + case SwiftOperators + case SwiftPM + case SwiftParser + case SwiftParserDiagnostics + case SwiftRefactor + case SwiftSyntax + case SwiftSyntaxBuilder + case SwiftSyntaxCShims + case SwiftSyntaxMacroExpansion + case SwiftSyntaxMacros + case SwiftSyntaxSupport + case SwiftUITracingSupportDT + case SymbolCache + case SymbolCacheIndexing + case SymbolCacheSupport + case SymbolicationDT + case TestResultsUI + case Testing + case USDLib_FormatLoaderProxy_Xcode + case UVTestSupport + case XCBuild + case XCResultKit + case XCServices + case XCSourceControl + case XCStringsParser + case XCTAutomationSupport + case XCTDaemonControl + case XCTDaemonControlMobileDevice + case XCTHarness + case XCTest + case XCTestCore + case XCTestSupport + case XCUIAutomation + case XCUnit + case XcodeCloudAPI + case XcodeCloudCombineAPI + case XcodeCloudDataSource + case XcodeCloudFoundation + case XcodeCloudKit + case XcodeCloudUI + case _CodeCompletionFoundation + case kperfdataDT + case ktraceDT + case libXCTestSwiftSupport + case llbuild + package var pathComponent: String { + "/SharedFrameworks/\(rawValue).framework" + } + } + + case sharedFrameworks(SharedFrameworks) + + package var contentsDirectory: String { + "/Applications/Xcode.app/Contents" + } + + package var path: String { + switch self { + case .sharedFrameworks(let framework): + return "\(contentsDirectory)\(framework.pathComponent)" + } + } +} diff --git a/Sources/MachOTestingSupport/XcodeMachOFileTests.swift b/Sources/MachOTestingSupport/XcodeMachOFileTests.swift new file mode 100644 index 00000000..67165568 --- /dev/null +++ b/Sources/MachOTestingSupport/XcodeMachOFileTests.swift @@ -0,0 +1,25 @@ +import Foundation +import Testing +import MachOKit +import MachOMacro +import MachOFoundation + +package class XcodeMachOFileTests { + package let machOFile: MachOFile + + package class var fileName: XcodeMachOFileName { .sharedFrameworks(.DNTDocumentationModel) } + + package class var preferredArchitecture: CPUType { .arm64 } + + package init() async throws { + let file = try File.loadFromFile(url: URL(fileURLWithPath: Self.fileName.path)) + switch file { + case .fat(let fatFile): + self.machOFile = try required(fatFile.machOFiles().first(where: { $0.header.cpuType == Self.preferredArchitecture }) ?? fatFile.machOFiles().first) + case .machO(let machO): + self.machOFile = machO + @unknown default: + fatalError() + } + } +} diff --git a/Tests/MachOSwiftSectionTests/PrimitiveTypeMappingTests.swift b/Tests/MachOSwiftSectionTests/PrimitiveTypeMappingTests.swift new file mode 100644 index 00000000..ce00a5bc --- /dev/null +++ b/Tests/MachOSwiftSectionTests/PrimitiveTypeMappingTests.swift @@ -0,0 +1,18 @@ +import Foundation +import Testing +import MachOKit +import Demangle +import MachOFoundation +@testable import MachOTestingSupport +@testable import MachOSwiftSection + +@Suite +final class PrimitiveTypeMappingTests: DyldCacheTests { + + override class var cacheImageName: MachOImageName { .SwiftUI } + + @Test func mappingInSwiftUI() async throws { + let mapping = try PrimitiveTypeMapping(machO: machOFileInCache) + mapping.dump() + } +} diff --git a/Tests/MachOSwiftSectionTests/SymbolDemangleTests.swift b/Tests/MachOSwiftSectionTests/SymbolDemangleTests.swift index 99227b11..addd3ca7 100644 --- a/Tests/MachOSwiftSectionTests/SymbolDemangleTests.swift +++ b/Tests/MachOSwiftSectionTests/SymbolDemangleTests.swift @@ -5,19 +5,10 @@ import MachOKit import MachOMacro import MachOFoundation @testable import MachOSwiftSection -import MachOTestingSupport +@testable import MachOTestingSupport @Suite(.serialized) -struct SymbolDemangleTests { - let mainCache: DyldCache - - let subCache: DyldCache - - init() async throws { - self.mainCache = try DyldCache(path: .current) - self.subCache = try required(mainCache.subCaches?.first?.subcache(for: mainCache)) - } - +final class SymbolDemangleTests: DyldCacheTests { struct MachOSwiftSymbol { let imagePath: String let offset: Int @@ -46,7 +37,7 @@ struct SymbolDemangleTests { @Test func symbolsInSwiftUI() async throws { var string = "" - let symbols = try symbols(for: .SwiftUI) + let symbols = try symbols(for: .AttributeGraph) for symbol in symbols { let swiftStdlibDemangledName = stdlib_demangleName(symbol.stringValue) guard !symbol.stringValue.hasSuffix("$delayInitStub") else { continue } @@ -56,7 +47,7 @@ struct SymbolDemangleTests { string += "\n" string += "\n" } - try string.write(to: .desktopDirectory.appendingPathComponent("SwiftUISwiftSymbols.txt"), atomically: true, encoding: .utf8) + try string.write(to: .desktopDirectory.appendingPathComponent("AttributeGraphSwiftSymbols.txt"), atomically: true, encoding: .utf8) } @Test func demangle() async throws { diff --git a/Tests/SwiftDumpTests/DyldCacheDumpTests.swift b/Tests/SwiftDumpTests/DyldCacheDumpTests.swift index 1cf38ba9..8c830366 100644 --- a/Tests/SwiftDumpTests/DyldCacheDumpTests.swift +++ b/Tests/SwiftDumpTests/DyldCacheDumpTests.swift @@ -8,31 +8,7 @@ import MachOFoundation @testable import MachOTestingSupport @Suite(.serialized) -struct DyldCacheDumpTests: DumpableTest { - let mainCache: DyldCache - - let subCache: DyldCache - - let machOFileInMainCache: MachOFile - - let machOFileInSubCache: MachOFile - - let machOFileInCache: MachOFile - - init() async throws { - self.mainCache = try DyldCache(path: .current) - self.subCache = try required(mainCache.subCaches?.first?.subcache(for: mainCache)) - - self.machOFileInMainCache = try #require(mainCache.machOFile(named: .SwiftUI)) - self.machOFileInSubCache = if #available(macOS 15.5, *) { - try #require(subCache.machOFile(named: .CodableSwiftUI)) - } else { - try #require(subCache.machOFile(named: .UIKitCore)) - } - - self.machOFileInCache = try #require(mainCache.machOFile(named: .AppKit)) - } -} +final class DyldCacheDumpTests: DyldCacheTests, DumpableTests {} extension DyldCacheDumpTests { @Test func typesInCacheFile() async throws { diff --git a/Tests/SwiftDumpTests/MachOFileDumpTests.swift b/Tests/SwiftDumpTests/MachOFileDumpTests.swift index a4c0eec1..e71f2fd6 100644 --- a/Tests/SwiftDumpTests/MachOFileDumpTests.swift +++ b/Tests/SwiftDumpTests/MachOFileDumpTests.swift @@ -8,21 +8,7 @@ import MachOFoundation @testable import MachOTestingSupport @Suite(.serialized) -struct MachOFileDumpTests: DumpableTest { - let machOFile: MachOFile - - init() async throws { - let file = try loadFromFile(named: .Finder) - switch file { - case .fat(let fatFile): - self.machOFile = try #require(fatFile.machOFiles().first(where: { $0.header.cpu.subtype == .arm64(.arm64e) })) - case .machO(let machO): - self.machOFile = machO - @unknown default: - fatalError() - } - } -} +final class MachOFileDumpTests: MachOFileTests, DumpableTests {} extension MachOFileDumpTests { @Test func typesInFile() async throws { diff --git a/Tests/SwiftDumpTests/MachOImageDumpTests.swift b/Tests/SwiftDumpTests/MachOImageDumpTests.swift index 8c94477e..3c52bf97 100644 --- a/Tests/SwiftDumpTests/MachOImageDumpTests.swift +++ b/Tests/SwiftDumpTests/MachOImageDumpTests.swift @@ -8,13 +8,7 @@ import MachOFoundation @testable import MachOTestingSupport @Suite(.serialized) -struct MachOImageDumpTests: DumpableTest { - let machOImage: MachOImage - - init() async throws { - self.machOImage = try #require(MachOImage(named: .Foundation)) - } -} +final class MachOImageDumpTests: MachOImageTests, DumpableTests {} extension MachOImageDumpTests { @Test func typesInImage() async throws { diff --git a/Tests/SwiftDumpTests/XcodeMachOFileDumpTests.swift b/Tests/SwiftDumpTests/XcodeMachOFileDumpTests.swift new file mode 100644 index 00000000..080cefc0 --- /dev/null +++ b/Tests/SwiftDumpTests/XcodeMachOFileDumpTests.swift @@ -0,0 +1,29 @@ +import Foundation +import Testing +import MachOKit +import MachOMacro +import MachOFoundation +@testable import MachOSwiftSection +@testable import SwiftDump +@testable import MachOTestingSupport + +@Suite(.serialized) +final class XcodeMachOFileDumpTests: XcodeMachOFileTests, DumpableTests {} + +extension XcodeMachOFileDumpTests { + @Test func typesInFile() async throws { + try await dumpTypes(for: machOFile) + } + + @Test func protocolsInFile() async throws { + try await dumpProtocols(for: machOFile) + } + + @Test func protocolConformancesInFile() async throws { + try await dumpProtocolConformances(for: machOFile) + } + + @Test func associatedTypesInFile() async throws { + try await dumpAssociatedTypes(for: machOFile) + } +} From 6cfc31ff648e40f0172e31e156dfa53b0935508b Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Sat, 28 Jun 2025 20:37:05 +0800 Subject: [PATCH 06/10] Add PrimitiveTypeMapping --- .../MachORepresentableWithCache.swift | 15 +++++ .../MachOSwiftSection/MachOFile+Swift.swift | 2 +- .../MachOSwiftSection/MachOImage+Swift.swift | 2 +- .../NamedContextDescriptorProtocol.swift | 4 +- .../Models/Mangling/MangledName.swift | 40 ++++++------- ...hOSwiftSectionRepresentableWithCache.swift | 11 ++++ .../Protocols/SwiftSectionRepresentable.swift | 11 ++++ .../Utils/PrimitiveTypeMapping.swift | 48 +++++++++++++-- Sources/MachOSymbols/SymbolCache.swift | 58 +++---------------- Sources/MachOSymbols/SymbolIndexStore.swift | 50 +++------------- 10 files changed, 121 insertions(+), 120 deletions(-) create mode 100644 Sources/MachOSwiftSection/Protocols/MachOSwiftSectionRepresentableWithCache.swift create mode 100644 Sources/MachOSwiftSection/Protocols/SwiftSectionRepresentable.swift diff --git a/Sources/MachOExtensions/MachORepresentableWithCache.swift b/Sources/MachOExtensions/MachORepresentableWithCache.swift index 5bdb45ba..79abee6b 100644 --- a/Sources/MachOExtensions/MachORepresentableWithCache.swift +++ b/Sources/MachOExtensions/MachORepresentableWithCache.swift @@ -2,12 +2,23 @@ import MachOKit package protocol MachORepresentableWithCache: MachORepresentable { associatedtype Cache: DyldCacheRepresentable + associatedtype Identifier: Hashable + var identifier: Identifier { get } var cache: Cache? { get } var startOffset: Int { get } } +package enum MachOTargetIdentifier: Hashable { + case image(UnsafeRawPointer) + case file(String) +} + extension MachOFile: MachORepresentableWithCache { + package var identifier: MachOTargetIdentifier { + .file(imagePath) + } + package var startOffset: Int { if let cache { headerStartOffsetInCache + cache.fileStartOffset.cast() @@ -18,6 +29,10 @@ extension MachOFile: MachORepresentableWithCache { } extension MachOImage: MachORepresentableWithCache { + package var identifier: MachOTargetIdentifier { + .image(ptr) + } + package var cache: DyldCacheLoaded? { guard let currentCache = DyldCacheLoaded.current else { return nil } diff --git a/Sources/MachOSwiftSection/MachOFile+Swift.swift b/Sources/MachOSwiftSection/MachOFile+Swift.swift index 81fb0768..aacdab8c 100644 --- a/Sources/MachOSwiftSection/MachOFile+Swift.swift +++ b/Sources/MachOSwiftSection/MachOFile+Swift.swift @@ -16,7 +16,7 @@ extension MachOFile { } } -extension MachOFile.Swift { +extension MachOFile.Swift: SwiftSectionRepresentable { public var protocolDescriptors: [ProtocolDescriptor] { get throws { return try _readRelativeDescriptors(from: .__swift5_protos, in: machOFile) diff --git a/Sources/MachOSwiftSection/MachOImage+Swift.swift b/Sources/MachOSwiftSection/MachOImage+Swift.swift index 9a3890b8..8b5b2966 100644 --- a/Sources/MachOSwiftSection/MachOImage+Swift.swift +++ b/Sources/MachOSwiftSection/MachOImage+Swift.swift @@ -16,7 +16,7 @@ extension MachOImage { } } -extension MachOImage.Swift { +extension MachOImage.Swift: SwiftSectionRepresentable { public var protocolDescriptors: [ProtocolDescriptor] { get throws { return try _readRelativeDescriptors(from: .__swift5_protos, in: machOImage) diff --git a/Sources/MachOSwiftSection/Models/ContextDescriptor/NamedContextDescriptorProtocol.swift b/Sources/MachOSwiftSection/Models/ContextDescriptor/NamedContextDescriptorProtocol.swift index 8aaa50ba..d0156003 100644 --- a/Sources/MachOSwiftSection/Models/ContextDescriptor/NamedContextDescriptorProtocol.swift +++ b/Sources/MachOSwiftSection/Models/ContextDescriptor/NamedContextDescriptorProtocol.swift @@ -10,5 +10,7 @@ extension NamedContextDescriptorProtocol { try layout.name.resolve(from: offset + layout.offset(of: .name), in: machOFile) } - + public func mangledName(in machOFile: MachOFile) throws -> MangledName { + try layout.name.resolveAny(from: offset + layout.offset(of: .name), in: machOFile) + } } diff --git a/Sources/MachOSwiftSection/Models/Mangling/MangledName.swift b/Sources/MachOSwiftSection/Models/Mangling/MangledName.swift index 5b582464..5c84a98f 100644 --- a/Sources/MachOSwiftSection/Models/Mangling/MangledName.swift +++ b/Sources/MachOSwiftSection/Models/Mangling/MangledName.swift @@ -4,37 +4,37 @@ import MachOFoundation import MachOMacro public struct MangledName { - enum Element { - struct Lookup: CustomStringConvertible { - enum Reference { + package enum Element { + package struct Lookup: CustomStringConvertible { + package enum Reference { case relative(RelativeReference) case absolute(AbsoluteReference) } - struct RelativeReference: CustomStringConvertible { - let kind: UInt8 - let relativeOffset: RelativeOffset - var description: String { + package struct RelativeReference: CustomStringConvertible { + package let kind: UInt8 + package let relativeOffset: RelativeOffset + package var description: String { """ Kind: \(kind) RelativeOffset: \(relativeOffset) """ } } - struct AbsoluteReference: CustomStringConvertible { - let kind: UInt8 - let reference: UInt64 - var description: String { + package struct AbsoluteReference: CustomStringConvertible { + package let kind: UInt8 + package let reference: UInt64 + package var description: String { """ Kind: \(kind) Address: \(reference) """ } } - let offset: Int - let reference: Reference + package let offset: Int + package let reference: Reference - var description: String { + package var description: String { switch reference { case .relative(let relative): "[Relative] FileOffset: \(offset) \(relative)" @@ -48,23 +48,23 @@ public struct MangledName { case lookup(Lookup) } - let elements: [Element] + package let elements: [Element] - let startOffset: Int + package let startOffset: Int - let endOffset: Int? + package let endOffset: Int? - init(elements: [Element], startOffset: Int, endOffset: Int?) { + package init(elements: [Element], startOffset: Int, endOffset: Int?) { self.elements = elements self.startOffset = startOffset self.endOffset = endOffset } - init(unsolvedSymbol: Symbol) { + package init(unsolvedSymbol: Symbol) { self.init(elements: [.string(unsolvedSymbol.stringValue)], startOffset: unsolvedSymbol.offset, endOffset: nil) } - var lookupElements: [Element.Lookup] { + package var lookupElements: [Element.Lookup] { elements.compactMap { if case .lookup(let lookup) = $0 { lookup } else { nil } } } diff --git a/Sources/MachOSwiftSection/Protocols/MachOSwiftSectionRepresentableWithCache.swift b/Sources/MachOSwiftSection/Protocols/MachOSwiftSectionRepresentableWithCache.swift new file mode 100644 index 00000000..6a300618 --- /dev/null +++ b/Sources/MachOSwiftSection/Protocols/MachOSwiftSectionRepresentableWithCache.swift @@ -0,0 +1,11 @@ +import MachOKit +import MachOExtensions + +protocol MachOSwiftSectionRepresentableWithCache: MachORepresentableWithCache { + associatedtype SwiftSection: SwiftSectionRepresentable + + var swift: SwiftSection { get } +} + +extension MachOFile: MachOSwiftSectionRepresentableWithCache {} +extension MachOImage: MachOSwiftSectionRepresentableWithCache {} diff --git a/Sources/MachOSwiftSection/Protocols/SwiftSectionRepresentable.swift b/Sources/MachOSwiftSection/Protocols/SwiftSectionRepresentable.swift new file mode 100644 index 00000000..0c2ee50e --- /dev/null +++ b/Sources/MachOSwiftSection/Protocols/SwiftSectionRepresentable.swift @@ -0,0 +1,11 @@ +public protocol SwiftSectionRepresentable { + var protocolDescriptors: [ProtocolDescriptor] { get throws } + + var protocolConformanceDescriptors: [ProtocolConformanceDescriptor] { get throws } + + var typeContextDescriptors: [ContextDescriptorWrapper] { get throws } + + var associatedTypeDescriptors: [AssociatedTypeDescriptor] { get throws } + + var builtinTypeDescriptors: [BuiltinTypeDescriptor] { get throws } +} diff --git a/Sources/MachOSwiftSection/Utils/PrimitiveTypeMapping.swift b/Sources/MachOSwiftSection/Utils/PrimitiveTypeMapping.swift index 4811a18c..35b4a69d 100644 --- a/Sources/MachOSwiftSection/Utils/PrimitiveTypeMapping.swift +++ b/Sources/MachOSwiftSection/Utils/PrimitiveTypeMapping.swift @@ -1,10 +1,50 @@ import Foundation import MachOFoundation +import MachOKit +import Demangle +import MachOMacro -class PrimitiveTypeMapping { - private let machO: MachO +package class PrimitiveTypeMapping { + private var storage: [String: String] = [:] - init(machO: MachO) throws { - self.machO = machO + package func dump() { + for (name, type) in storage { + print("\(name) ---> \(type)") + } + } + + package func primitiveType(for name: String) -> String? { + return storage[name] + } + + @MachOImageGenerator + package init(machO: MachOFile) throws { + let builtinTypes = try machO.swift.builtinTypeDescriptors.map { try BuiltinType(descriptor: $0, in: machO) } + for builtinType in builtinTypes { + guard let typeName = builtinType.typeName else { continue } + let node = try MetadataReader.demangleType(for: typeName, in: machO) + guard node.children.first?.children.first?.text == objcModule, let descriptorLookup = typeName.lookupElements.first else { continue } + switch descriptorLookup.reference { + case .relative(let relativeReference): + guard let (kind, directness) = SymbolicReference.symbolicReference(for: relativeReference.kind) else { continue } + switch kind { + case .context: + switch directness { + case .direct: + guard let descriptor = try RelativeDirectPointer(relativeOffset: relativeReference.relativeOffset).resolve(from: descriptorLookup.offset, in: machO).namedContextDescriptor else { continue } + let name = try descriptor.name(in: machO) + let mangledName = try descriptor.mangledName(in: machO) + guard let endOffset = mangledName.endOffset else { continue } + storage[name] = try machO.readString(offset: endOffset) + case .indirect: + continue + } + default: + continue + } + case .absolute: + continue + } + } } } diff --git a/Sources/MachOSymbols/SymbolCache.swift b/Sources/MachOSymbols/SymbolCache.swift index 1df89f2e..c843497a 100644 --- a/Sources/MachOSymbols/SymbolCache.swift +++ b/Sources/MachOSymbols/SymbolCache.swift @@ -2,11 +2,6 @@ import MachOKit import MachOExtensions import Demangle -enum MachOTargetIdentifier: Hashable { - case image(UnsafeRawPointer) - case file(String) -} - package final class SymbolCache { package static let shared = SymbolCache() @@ -26,25 +21,13 @@ package final class SymbolCache { private typealias CacheEntry = [Int: Symbol] - private var cacheEntryByIdentifier: [MachOTargetIdentifier: CacheEntry] = [:] - - @discardableResult - private func createCacheIfNeeded(for machOImage: MachOImage, isForced: Bool = false) -> Bool { - let identifier = MachOTargetIdentifier.image(machOImage.ptr) - return createCacheIfNeeded(for: identifier, in: machOImage, isForced: isForced) - } - - @discardableResult - private func createCacheIfNeeded(for machOFile: MachOFile, isForced: Bool = false) -> Bool { - let identifier = MachOTargetIdentifier.file(machOFile.imagePath) - return createCacheIfNeeded(for: identifier, in: machOFile, isForced: isForced) - } + private var cacheEntryByIdentifier: [AnyHashable: CacheEntry] = [:] @discardableResult - private func createCacheIfNeeded(for identifier: MachOTargetIdentifier, in machO: MachO, isForced: Bool = false) -> Bool { - guard isForced || (cacheEntryByIdentifier[identifier]?.isEmpty ?? true) else { return false } + package func createCacheIfNeeded(in machO: MachO, isForced: Bool = false) -> Bool { + guard isForced || (cacheEntryByIdentifier[machO.identifier]?.isEmpty ?? true) else { return false } var cacheEntry: CacheEntry = [:] - + for symbol in machO.symbols where symbol.name.isSwiftSymbol { var offset = symbol.offset cacheEntry[offset] = .init(offset: offset, stringValue: symbol.name) @@ -61,38 +44,13 @@ package final class SymbolCache { cacheEntry[offset] = .init(offset: offset, stringValue: exportedSymbol.name) } } - cacheEntryByIdentifier[identifier] = cacheEntry + cacheEntryByIdentifier[machO.identifier] = cacheEntry return true } - private func removeCache(for machOImage: MachOImage) { - let identifier = MachOTargetIdentifier.image(machOImage.ptr) - removeCache(for: identifier) - } - - private func removeCache(for machOFile: MachOFile) { - let identifier = MachOTargetIdentifier.file(machOFile.imagePath) - removeCache(for: identifier) - } - - private func removeCache(for identifier: MachOTargetIdentifier) { - cacheEntryByIdentifier.removeValue(forKey: identifier) - } - - package func symbol(for offset: Int, in machOImage: MachOImage) -> Symbol? { - let identifier = MachOTargetIdentifier.image(machOImage.ptr) - createCacheIfNeeded(for: identifier, in: machOImage) - return symbol(for: offset, with: identifier, in: machOImage) - } - - package func symbol(for offset: Int, in machOFile: MachOFile) -> Symbol? { - let identifier = MachOTargetIdentifier.file(machOFile.imagePath) - createCacheIfNeeded(for: identifier, in: machOFile) - return symbol(for: offset, with: identifier, in: machOFile) - } - - private func symbol(for offset: Int, with identifier: MachOTargetIdentifier, in machO: MachO) -> Symbol? { - if let symbol = cacheEntryByIdentifier[identifier, default: [:]][offset] { + package func symbol(for offset: Int, in machO: MachO) -> Symbol? { + createCacheIfNeeded(in: machO) + if let symbol = cacheEntryByIdentifier[machO.identifier, default: [:]][offset] { return symbol } else { return nil diff --git a/Sources/MachOSymbols/SymbolIndexStore.swift b/Sources/MachOSymbols/SymbolIndexStore.swift index bc132832..3c50307d 100644 --- a/Sources/MachOSymbols/SymbolIndexStore.swift +++ b/Sources/MachOSymbols/SymbolIndexStore.swift @@ -64,37 +64,12 @@ package final class SymbolIndexStore { var symbolsByKind: [IndexKind: [String: [Symbol]]] = [:] } - private var indexEntryByIdentifier: [MachOTargetIdentifier: IndexEntry] = [:] + private var indexEntryByIdentifier: [AnyHashable: IndexEntry] = [:] - private func removeIndexs(for machOImage: MachOImage) { - let identifier = MachOTargetIdentifier.image(machOImage.ptr) - removeIndexs(for: identifier) - } - - private func removeIndexs(for machOFile: MachOFile) { - let identifier = MachOTargetIdentifier.file(machOFile.imagePath) - removeIndexs(for: identifier) - } - - private func removeIndexs(for identifier: MachOTargetIdentifier) { - indexEntryByIdentifier.removeValue(forKey: identifier) - } - - @discardableResult - private func startIndexingIfNeeded(for machOImage: MachOImage) -> Bool { - let identifier = MachOTargetIdentifier.image(machOImage.ptr) - return startIndexingIfNeeded(for: identifier, in: machOImage) - } @discardableResult - private func startIndexingIfNeeded(for machOFile: MachOFile) -> Bool { - let identifier = MachOTargetIdentifier.file(machOFile.imagePath) - return startIndexingIfNeeded(for: identifier, in: machOFile) - } - - @discardableResult - private func startIndexingIfNeeded(for identifier: MachOTargetIdentifier, in machO: MachO) -> Bool { - if let existedEntry = indexEntryByIdentifier[identifier], existedEntry.isIndexed { + package func startIndexingIfNeeded(in machO: MachO) -> Bool { + if let existedEntry = indexEntryByIdentifier[machO.identifier], existedEntry.isIndexed { return true } var entry = IndexEntry() @@ -235,24 +210,13 @@ package final class SymbolIndexStore { } } entry.isIndexed = true - indexEntryByIdentifier[identifier] = entry + indexEntryByIdentifier[machO.identifier] = entry return true } - package func symbols(of kind: IndexKind, for name: String, in machOImage: MachOImage) -> [Symbol] { - let identifier = MachOTargetIdentifier.image(machOImage.ptr) - startIndexingIfNeeded(for: identifier, in: machOImage) - return symbols(of: kind, for: name, with: identifier, in: machOImage) - } - - package func symbols(of kind: IndexKind, for name: String, in machOFile: MachOFile) -> [Symbol] { - let identifier = MachOTargetIdentifier.file(machOFile.imagePath) - startIndexingIfNeeded(for: identifier, in: machOFile) - return symbols(of: kind, for: name, with: identifier, in: machOFile) - } - - private func symbols(of kind: IndexKind, for name: String, with identifier: MachOTargetIdentifier, in machO: MachO) -> [Symbol] { - if let symbol = indexEntryByIdentifier[identifier]?.symbolsByKind[kind]?[name] { + package func symbols(of kind: IndexKind, for name: String, in machO: MachO) -> [Symbol] { + startIndexingIfNeeded(in: machO) + if let symbol = indexEntryByIdentifier[machO.identifier]?.symbolsByKind[kind]?[name] { return symbol } else { return [] From a9f019c16405a836f60faf6635b7917e0e98b6ab Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Sun, 29 Jun 2025 11:21:23 +0800 Subject: [PATCH 07/10] Handling the issue of multiple symbols with the same offset --- Package.swift | 9 ++- Sources/MachOPointer/RelativePointers.swift | 5 -- Sources/MachOReading/Resolvable.swift | 3 +- .../Models/BuiltinType/BuiltinType.swift | 3 - .../Models/Mangling/MangledName.swift | 5 +- .../Models/Mangling/MangledNameKind.swift | 4 ++ .../Models/Protocol/ProtocolRequirement.swift | 4 +- .../Models/Protocol/ResilientWitness.swift | 4 +- .../Pointer}/SymbolOrElementPointer.swift | 7 +++ Sources/MachOSymbols/MachO+Symbol.swift | 8 +-- Sources/MachOSymbols/Symbol.swift | 10 +--- Sources/MachOSymbols/SymbolCache.swift | 23 +++---- Sources/MachOSymbols/SymbolIndexStore.swift | 1 + Sources/MachOSymbols/Symbols.swift | 60 +++++++++++++++++++ .../Dumpable+/AssociatedType+Dumpable.swift | 1 + .../SwiftDump/Dumpable+/Class+Dumpable.swift | 1 + .../SwiftDump/Dumpable+/Enum+Dumpable.swift | 1 + .../Dumpable+/Protocol+Dumpable.swift | 39 ++++++++++-- .../ProtocolConformance+Dumpable.swift | 42 ++++++++++--- .../SwiftDump/Dumpable+/Struct+Dumpable.swift | 1 + .../Extensions/GenericContext+Dump.swift | 1 + .../MemoryPressureMonitor.swift | 12 ++-- .../OffsetEnumeratedSequence.swift | 0 .../Utils => Utilities}/StringBuilder.swift | 0 .../SymbolDemangleTests.swift | 13 ++-- Tests/SwiftDumpTests/DyldCacheDumpTests.swift | 10 ++-- .../XcodeMachOFileDumpTests.swift | 6 ++ 27 files changed, 205 insertions(+), 68 deletions(-) create mode 100644 Sources/MachOSwiftSection/Models/Mangling/MangledNameKind.swift rename Sources/{MachOPointer => MachOSwiftSection/Pointer}/SymbolOrElementPointer.swift (80%) create mode 100644 Sources/MachOSymbols/Symbols.swift rename Sources/{MachOSymbols => Utilities}/MemoryPressureMonitor.swift (81%) rename Sources/{SwiftDump/Utils => Utilities}/OffsetEnumeratedSequence.swift (100%) rename Sources/{SwiftDump/Utils => Utilities}/StringBuilder.swift (100%) diff --git a/Package.swift b/Package.swift index 0a3e25ec..715dcd93 100644 --- a/Package.swift +++ b/Package.swift @@ -131,6 +131,10 @@ let package = Package( ] ), + .target( + name: "Utilities" + ), + .target( name: "MachOExtensions", dependencies: [ @@ -157,6 +161,7 @@ let package = Package( "MachOReading", "MachOMacro", "Demangle", + "Utilities", .product(name: "OrderedCollections", package: "swift-collections"), ] ), @@ -167,7 +172,6 @@ let package = Package( .MachOKit, "MachOReading", "MachOMacro", - "MachOSymbols", ] ), @@ -179,6 +183,7 @@ let package = Package( "MachOExtensions", "MachOMacro", "MachOPointer", + "MachOSymbols", ] ), @@ -208,6 +213,8 @@ let package = Package( .MachOKit, "MachOSwiftSection", "Semantic", + "Utilities", + .product(name: "OrderedCollections", package: "swift-collections"), ] ), diff --git a/Sources/MachOPointer/RelativePointers.swift b/Sources/MachOPointer/RelativePointers.swift index a39bf9a7..6996a9c7 100644 --- a/Sources/MachOPointer/RelativePointers.swift +++ b/Sources/MachOPointer/RelativePointers.swift @@ -20,8 +20,3 @@ public typealias RelativeIndirectablePointerIntPair = TargetRelativeIndirectablePointerIntPair> where Integer.RawValue: FixedWidthInteger -public typealias RelativeSymbolOrElementPointer = RelativeIndirectablePointer, SymbolOrElementPointer> - -public typealias RelativeIndirectSymbolOrElementPointer = RelativeIndirectPointer, SymbolOrElementPointer> - -public typealias RelativeSymbolOrElementPointerIntPair = RelativeIndirectablePointerIntPair, Value, SymbolOrElementPointer> where Value.RawValue: FixedWidthInteger diff --git a/Sources/MachOReading/Resolvable.swift b/Sources/MachOReading/Resolvable.swift index 3120d56e..0e35db39 100644 --- a/Sources/MachOReading/Resolvable.swift +++ b/Sources/MachOReading/Resolvable.swift @@ -17,8 +17,7 @@ extension Resolvable { } public static func resolve(from fileOffset: Int, in machOFile: MachOFile) throws -> Self? { - let result: Self = try resolve(from: fileOffset, in: machOFile) - return .some(result) + return try? resolve(from: fileOffset, in: machOFile) } } diff --git a/Sources/MachOSwiftSection/Models/BuiltinType/BuiltinType.swift b/Sources/MachOSwiftSection/Models/BuiltinType/BuiltinType.swift index d70b1cef..57a18738 100644 --- a/Sources/MachOSwiftSection/Models/BuiltinType/BuiltinType.swift +++ b/Sources/MachOSwiftSection/Models/BuiltinType/BuiltinType.swift @@ -8,13 +8,10 @@ public struct BuiltinType: TopLevelType { public let typeName: MangledName? - public let symbol: Symbol? - @MachOImageGenerator public init(descriptor: BuiltinTypeDescriptor, in machO: MachOFile) throws { self.descriptor = descriptor self.typeName = try descriptor.typeName(in: machO) - self.symbol = SymbolCache.shared.symbol(for: descriptor.offset, in: machO) } } diff --git a/Sources/MachOSwiftSection/Models/Mangling/MangledName.swift b/Sources/MachOSwiftSection/Models/Mangling/MangledName.swift index 5c84a98f..4037045d 100644 --- a/Sources/MachOSwiftSection/Models/Mangling/MangledName.swift +++ b/Sources/MachOSwiftSection/Models/Mangling/MangledName.swift @@ -175,7 +175,4 @@ extension MangledName: CustomStringConvertible { } } -public enum MangledNameKind { - case type - case symbol -} + diff --git a/Sources/MachOSwiftSection/Models/Mangling/MangledNameKind.swift b/Sources/MachOSwiftSection/Models/Mangling/MangledNameKind.swift new file mode 100644 index 00000000..8f470d56 --- /dev/null +++ b/Sources/MachOSwiftSection/Models/Mangling/MangledNameKind.swift @@ -0,0 +1,4 @@ +public enum MangledNameKind { + case type + case symbol +} diff --git a/Sources/MachOSwiftSection/Models/Protocol/ProtocolRequirement.swift b/Sources/MachOSwiftSection/Models/Protocol/ProtocolRequirement.swift index 65613eae..bc0ff65a 100644 --- a/Sources/MachOSwiftSection/Models/Protocol/ProtocolRequirement.swift +++ b/Sources/MachOSwiftSection/Models/Protocol/ProtocolRequirement.swift @@ -5,7 +5,7 @@ import MachOFoundation public struct ProtocolRequirement: ResolvableLocatableLayoutWrapper { public struct Layout: Sendable { public let flags: ProtocolRequirementFlags - public let defaultImplementation: RelativeDirectPointer + public let defaultImplementation: RelativeDirectPointer } public let offset: Int @@ -20,7 +20,7 @@ public struct ProtocolRequirement: ResolvableLocatableLayoutWrapper { @MachOImageAllMembersGenerator extension ProtocolRequirement { - public func defaultImplementationSymbol(in machOFile: MachOFile) throws -> Symbol? { + public func defaultImplementationSymbols(in machOFile: MachOFile) throws -> Symbols? { guard layout.defaultImplementation.isValid else { return nil } return try layout.defaultImplementation.resolve(from: offset(of: \.defaultImplementation), in: machOFile) } diff --git a/Sources/MachOSwiftSection/Models/Protocol/ResilientWitness.swift b/Sources/MachOSwiftSection/Models/Protocol/ResilientWitness.swift index c5abfc53..ac0e5188 100644 --- a/Sources/MachOSwiftSection/Models/Protocol/ResilientWitness.swift +++ b/Sources/MachOSwiftSection/Models/Protocol/ResilientWitness.swift @@ -6,7 +6,7 @@ import MachOFoundation public struct ResilientWitness: ResolvableLocatableLayoutWrapper { public struct Layout: Sendable { public let requirement: RelativeProtocolRequirementPointer - public let implementation: RelativeDirectPointer + public let implementation: RelativeDirectPointer } public let offset: Int @@ -26,7 +26,7 @@ extension ResilientWitness { return try layout.requirement.resolve(from: offset(of: \.requirement), in: machOFile).asOptional } - public func implementationSymbol(in machOFile: MachOFile) throws -> Symbol? { + public func implementationSymbols(in machOFile: MachOFile) throws -> Symbols? { return try layout.implementation.resolve(from: offset(of: \.implementation), in: machOFile) } } diff --git a/Sources/MachOPointer/SymbolOrElementPointer.swift b/Sources/MachOSwiftSection/Pointer/SymbolOrElementPointer.swift similarity index 80% rename from Sources/MachOPointer/SymbolOrElementPointer.swift rename to Sources/MachOSwiftSection/Pointer/SymbolOrElementPointer.swift index 8bcda8b4..3fa2b041 100644 --- a/Sources/MachOPointer/SymbolOrElementPointer.swift +++ b/Sources/MachOSwiftSection/Pointer/SymbolOrElementPointer.swift @@ -1,7 +1,14 @@ import MachOKit import MachOReading +import MachOPointer import MachOSymbols +public typealias RelativeSymbolOrElementPointer = RelativeIndirectablePointer, SymbolOrElementPointer> + +public typealias RelativeIndirectSymbolOrElementPointer = RelativeIndirectPointer, SymbolOrElementPointer> + +public typealias RelativeSymbolOrElementPointerIntPair = RelativeIndirectablePointerIntPair, Value, SymbolOrElementPointer> where Value.RawValue: FixedWidthInteger + public enum SymbolOrElementPointer: RelativeIndirectType { public typealias Resolved = SymbolOrElement diff --git a/Sources/MachOSymbols/MachO+Symbol.swift b/Sources/MachOSymbols/MachO+Symbol.swift index 4acc2fe4..3f34c387 100644 --- a/Sources/MachOSymbols/MachO+Symbol.swift +++ b/Sources/MachOSymbols/MachO+Symbol.swift @@ -2,13 +2,13 @@ import MachOKit import MachOExtensions extension MachOFile { - package func findSymbol(offset: Int) -> MachOSymbols.Symbol? { - return SymbolCache.shared.symbol(for: offset, in: self) + package func symbols(offset: Int) -> MachOSymbols.Symbols? { + return SymbolCache.shared.symbols(for: offset, in: self) } } extension MachOImage { - package func findSymbol(offset: Int) -> MachOSymbols.Symbol? { - return SymbolCache.shared.symbol(for: offset, in: self) + package func symbols(offset: Int) -> MachOSymbols.Symbols? { + return SymbolCache.shared.symbols(for: offset, in: self) } } diff --git a/Sources/MachOSymbols/Symbol.swift b/Sources/MachOSymbols/Symbol.swift index a8a69e01..ef1efaf1 100644 --- a/Sources/MachOSymbols/Symbol.swift +++ b/Sources/MachOSymbols/Symbol.swift @@ -4,10 +4,6 @@ import MachOReading import MachOExtensions public struct Symbol: Resolvable, Hashable { - public enum ResolveError: Swift.Error { - case symbolNotFound - } - public let offset: Int public let stringValue: String @@ -19,15 +15,15 @@ public struct Symbol: Resolvable, Hashable { @MachOImageGenerator public static func resolve(from fileOffset: Int, in machOFile: MachOFile) throws -> Self { - guard let symbol = try resolve(from: fileOffset, in: machOFile) else { throw ResolveError.symbolNotFound } - return symbol + try required(resolve(from: fileOffset, in: machOFile)) } @MachOImageGenerator public static func resolve(from fileOffset: Int, in machOFile: MachOFile) throws -> Self? { - if let symbol = machOFile.findSymbol(offset: fileOffset) { + if let symbol = machOFile.symbols(offset: fileOffset)?.first { return symbol } return nil } } + diff --git a/Sources/MachOSymbols/SymbolCache.swift b/Sources/MachOSymbols/SymbolCache.swift index c843497a..a28ea80e 100644 --- a/Sources/MachOSymbols/SymbolCache.swift +++ b/Sources/MachOSymbols/SymbolCache.swift @@ -1,6 +1,8 @@ import MachOKit import MachOExtensions import Demangle +import OrderedCollections +import Utilities package final class SymbolCache { package static let shared = SymbolCache() @@ -19,7 +21,7 @@ package final class SymbolCache { memoryPressureMonitor.startMonitoring() } - private typealias CacheEntry = [Int: Symbol] + private typealias CacheEntry = OrderedDictionary private var cacheEntryByIdentifier: [AnyHashable: CacheEntry] = [:] @@ -27,31 +29,32 @@ package final class SymbolCache { package func createCacheIfNeeded(in machO: MachO, isForced: Bool = false) -> Bool { guard isForced || (cacheEntryByIdentifier[machO.identifier]?.isEmpty ?? true) else { return false } var cacheEntry: CacheEntry = [:] - + var cachedSymbols: Set = [] for symbol in machO.symbols where symbol.name.isSwiftSymbol { var offset = symbol.offset - cacheEntry[offset] = .init(offset: offset, stringValue: symbol.name) + cacheEntry[offset, default: []].append(.init(offset: offset, stringValue: symbol.name)) if let cache = machO.cache { offset -= cache.mainCacheHeader.sharedRegionStart.cast() - cacheEntry[offset] = .init(offset: offset, stringValue: symbol.name) + cacheEntry[offset, default: []].append(.init(offset: offset, stringValue: symbol.name)) } + cachedSymbols.insert(symbol.name) } - for exportedSymbol in machO.exportedSymbols where exportedSymbol.name.isSwiftSymbol { + for exportedSymbol in machO.exportedSymbols where exportedSymbol.name.isSwiftSymbol && !cachedSymbols.contains(exportedSymbol.name) { if var offset = exportedSymbol.offset { - cacheEntry[offset] = .init(offset: offset, stringValue: exportedSymbol.name) + cacheEntry[offset, default: []].append(.init(offset: offset, stringValue: exportedSymbol.name)) offset += machO.startOffset - cacheEntry[offset] = .init(offset: offset, stringValue: exportedSymbol.name) + cacheEntry[offset, default: []].append(.init(offset: offset, stringValue: exportedSymbol.name)) } } cacheEntryByIdentifier[machO.identifier] = cacheEntry return true } - package func symbol(for offset: Int, in machO: MachO) -> Symbol? { + package func symbols(for offset: Int, in machO: MachO) -> Symbols? { createCacheIfNeeded(in: machO) - if let symbol = cacheEntryByIdentifier[machO.identifier, default: [:]][offset] { - return symbol + if let symbols = cacheEntryByIdentifier[machO.identifier, default: [:]][offset], !symbols.isEmpty { + return .init(offset: offset, symbols: symbols) } else { return nil } diff --git a/Sources/MachOSymbols/SymbolIndexStore.swift b/Sources/MachOSymbols/SymbolIndexStore.swift index 3c50307d..ee579626 100644 --- a/Sources/MachOSymbols/SymbolIndexStore.swift +++ b/Sources/MachOSymbols/SymbolIndexStore.swift @@ -3,6 +3,7 @@ import MachOKit import MachOExtensions import Demangle import OrderedCollections +import Utilities package final class SymbolIndexStore { package enum IndexKind: Hashable, CaseIterable, CustomStringConvertible { diff --git a/Sources/MachOSymbols/Symbols.swift b/Sources/MachOSymbols/Symbols.swift new file mode 100644 index 00000000..711c0f61 --- /dev/null +++ b/Sources/MachOSymbols/Symbols.swift @@ -0,0 +1,60 @@ +import MachOKit +import MachOMacro +import MachOReading +import MachOExtensions + +public struct Symbols: Resolvable { + private var _storage: [Symbol] = [] + + public let offset: Int + + internal init(offset: Int, symbols: [Symbol]) { + self.offset = offset + self._storage = symbols + } + + @MachOImageGenerator + public static func resolve(from fileOffset: Int, in machOFile: MachOFile) throws -> Self { + try required(resolve(from: fileOffset, in: machOFile)) + } + + @MachOImageGenerator + public static func resolve(from fileOffset: Int, in machOFile: MachOFile) throws -> Self? { + return machOFile.symbols(offset: fileOffset) + } +} + +extension Symbols: RandomAccessCollection { + public typealias Element = Symbol + + public var startIndex: Int { _storage.startIndex } + + public var endIndex: Int { _storage.endIndex } + + public func index(after i: Int) -> Int { + _storage.index(after: i) + } +} + +extension Symbols: MutableCollection { + public subscript(position: Int) -> Symbol { + get { + _storage[position] + } + set { + _storage[position] = newValue + } + } + + public mutating func append(_ newElement: Symbol) { + _storage.append(newElement) + } + + public mutating func remove(at index: Int) { + _storage.remove(at: index) + } + + public mutating func removeAll() { + _storage.removeAll() + } +} diff --git a/Sources/SwiftDump/Dumpable+/AssociatedType+Dumpable.swift b/Sources/SwiftDump/Dumpable+/AssociatedType+Dumpable.swift index afce6449..a04dd1ae 100644 --- a/Sources/SwiftDump/Dumpable+/AssociatedType+Dumpable.swift +++ b/Sources/SwiftDump/Dumpable+/AssociatedType+Dumpable.swift @@ -3,6 +3,7 @@ import MachOKit import MachOSwiftSection import MachOMacro import Semantic +import Utilities extension AssociatedType: ConformedDumpable { @MachOImageGenerator diff --git a/Sources/SwiftDump/Dumpable+/Class+Dumpable.swift b/Sources/SwiftDump/Dumpable+/Class+Dumpable.swift index f6d180bf..455f1eb8 100644 --- a/Sources/SwiftDump/Dumpable+/Class+Dumpable.swift +++ b/Sources/SwiftDump/Dumpable+/Class+Dumpable.swift @@ -4,6 +4,7 @@ import MachOKit import MachOMacro import MachOFoundation import MachOSwiftSection +import Utilities extension Class: NamedDumpable { @MachOImageGenerator diff --git a/Sources/SwiftDump/Dumpable+/Enum+Dumpable.swift b/Sources/SwiftDump/Dumpable+/Enum+Dumpable.swift index f861e1a2..f4006441 100644 --- a/Sources/SwiftDump/Dumpable+/Enum+Dumpable.swift +++ b/Sources/SwiftDump/Dumpable+/Enum+Dumpable.swift @@ -4,6 +4,7 @@ import MachOSwiftSection import MachOMacro import Semantic import MachOSymbols +import Utilities extension Enum: NamedDumpable { @MachOImageGenerator diff --git a/Sources/SwiftDump/Dumpable+/Protocol+Dumpable.swift b/Sources/SwiftDump/Dumpable+/Protocol+Dumpable.swift index 5a05012b..7376bd02 100644 --- a/Sources/SwiftDump/Dumpable+/Protocol+Dumpable.swift +++ b/Sources/SwiftDump/Dumpable+/Protocol+Dumpable.swift @@ -4,6 +4,9 @@ import MachOSwiftSection import MachOMacro import Semantic import MachOFoundation +import Utilities +import Demangle +import OrderedCollections extension MachOSwiftSection.`Protocol`: NamedDumpable { @MachOImageGenerator @@ -51,28 +54,52 @@ extension MachOSwiftSection.`Protocol`: NamedDumpable { } } + var defaultImplementations: OrderedSet = [] + for (offset, requirement) in requirements.offsetEnumerated() { BreakLine() Indent(level: 1) - if let symbol = try Symbol.resolve(from: requirement.offset, in: machOFile) { - try? MetadataReader.demangleSymbol(for: symbol, in: machOFile).printSemantic(using: options) + if let symbols = try Symbols.resolve(from: requirement.offset, in: machOFile), let validNode = try validNode(for: symbols, in: machOFile) { + validNode.printSemantic(using: options) } else { InlineComment("[Stripped Symbol]") } - if let symbol = try requirement.defaultImplementationSymbol(in: machOFile), let defaultImplementation = try? MetadataReader.demangleSymbol(for: symbol, in: machOFile).printSemantic(using: options) { + if let symbols = try requirement.defaultImplementationSymbols(in: machOFile), let defaultImplementation = try validNode(for: symbols, in: machOFile, visitedNode: defaultImplementations) { + _ = defaultImplementations.append(defaultImplementation) + } + + if offset.isEnd { + BreakLine() + } + } + + for (offset, defaultImplementation) in defaultImplementations.offsetEnumerated() { + + if offset.isStart { BreakLine() Indent(level: 1) InlineComment("[Default Implementation]") - Space() - defaultImplementation } + BreakLine() + Indent(level: 1) + defaultImplementation.printSemantic(using: options) + if offset.isEnd { BreakLine() } } - Standard("}") } + + @MachOImageGenerator + private func validNode(for symbols: Symbols, in machOFile: MachOFile, visitedNode: borrowing OrderedSet = []) throws -> Node? { + for symbol in symbols { + if let node = try? MetadataReader.demangleSymbol(for: symbol, in: machOFile), let protocolNode = node.first(where: { $0.kind == .protocol }), protocolNode.print(using: .interface) == (try dumpName(using: .interface, in: machOFile)).string, !visitedNode.contains(node) { + return node + } + } + return nil + } } diff --git a/Sources/SwiftDump/Dumpable+/ProtocolConformance+Dumpable.swift b/Sources/SwiftDump/Dumpable+/ProtocolConformance+Dumpable.swift index 88f0634d..01a35a13 100644 --- a/Sources/SwiftDump/Dumpable+/ProtocolConformance+Dumpable.swift +++ b/Sources/SwiftDump/Dumpable+/ProtocolConformance+Dumpable.swift @@ -4,6 +4,9 @@ import MachOSwiftSection import MachOMacro import MachOFoundation import Semantic +import Demangle +import Utilities +import OrderedCollections extension ProtocolConformance: ConformedDumpable { @MachOImageGenerator @@ -80,22 +83,35 @@ extension ProtocolConformance: ConformedDumpable { Space() Standard("{") + var visitedNodes: OrderedSet = [] + for resilientWitness in resilientWitnesses { BreakLine() Indent(level: 1) - if let symbol = try resilientWitness.implementationSymbol(in: machOFile) { - try? MetadataReader.demangleSymbol(for: symbol, in: machOFile).printSemantic(using: options) - } else if !resilientWitness.implementation.isNull { - FunctionDeclaration(addressString(of: resilientWitness.implementation.resolveDirectOffset(from: resilientWitness.offset(of: \.implementation)), in: machOFile).insertSubFunctionPrefix) - } else if let implSymbol = try resilientWitness.requirement(in: machOFile)?.mapOptional({ try $0.defaultImplementationSymbol(in: machOFile) }) { - switch implSymbol { + if let symbols = try resilientWitness.implementationSymbols(in: machOFile), let validNode = try validNode(for: symbols, in: machOFile, visitedNode: visitedNodes) { + _ = visitedNodes.append(validNode) + validNode.printSemantic(using: options) + } else if let requirement = try resilientWitness.requirement(in: machOFile) { + switch requirement { case .symbol(let symbol): try MetadataReader.demangleSymbol(for: symbol, in: machOFile).printSemantic(using: options) - case .element(let symbol): - try MetadataReader.demangleSymbol(for: symbol, in: machOFile).printSemantic(using: options) + case .element(let element): + if let symbols = try Symbols.resolve(from: element.offset, in: machOFile), let validNode = try validNode(for: symbols, in: machOFile, visitedNode: visitedNodes) { + _ = visitedNodes.append(validNode) + validNode.printSemantic(using: options) + } else if let defaultImplementationSymbols = try element.defaultImplementationSymbols(in: machOFile), let validNode = try validNode(for: defaultImplementationSymbols, in: machOFile, visitedNode: visitedNodes) { + _ = visitedNodes.append(validNode) + validNode.printSemantic(using: options) + } else if !element.defaultImplementation.isNull { + FunctionDeclaration(addressString(of: element.defaultImplementation.resolveDirectOffset(from: element.offset(of: \.defaultImplementation)), in: machOFile).insertSubFunctionPrefix) + } else { + Error("Symbol not found") + } } + } else if !resilientWitness.implementation.isNull { + FunctionDeclaration(addressString(of: resilientWitness.implementation.resolveDirectOffset(from: resilientWitness.offset(of: \.implementation)), in: machOFile).insertSubFunctionPrefix) } else { Error("Symbol not found") } @@ -106,6 +122,16 @@ extension ProtocolConformance: ConformedDumpable { Standard("}") } } + + @MachOImageGenerator + private func validNode(for symbols: Symbols, in machOFile: MachOFile, visitedNode: borrowing OrderedSet = []) throws -> Node? { + for symbol in symbols { + if let node = try? MetadataReader.demangleSymbol(for: symbol, in: machOFile), let protocolConformanceNode = node.first(where: { $0.kind == .protocolConformance }), protocolConformanceNode.children.at(0)?.print(using: .interface) == (try dumpTypeName(using: .interface, in: machOFile)).string { + return node + } + } + return nil + } } extension SemanticString { diff --git a/Sources/SwiftDump/Dumpable+/Struct+Dumpable.swift b/Sources/SwiftDump/Dumpable+/Struct+Dumpable.swift index a0e05a7a..e829cf6b 100644 --- a/Sources/SwiftDump/Dumpable+/Struct+Dumpable.swift +++ b/Sources/SwiftDump/Dumpable+/Struct+Dumpable.swift @@ -4,6 +4,7 @@ import MachOSwiftSection import MachOMacro import Semantic import MachOSymbols +import Utilities extension Struct: NamedDumpable { @MachOImageGenerator diff --git a/Sources/SwiftDump/Extensions/GenericContext+Dump.swift b/Sources/SwiftDump/Extensions/GenericContext+Dump.swift index 7a692283..b79f234c 100644 --- a/Sources/SwiftDump/Extensions/GenericContext+Dump.swift +++ b/Sources/SwiftDump/Extensions/GenericContext+Dump.swift @@ -3,6 +3,7 @@ import MachOKit import MachOMacro import MachOFoundation import MachOSwiftSection +import Utilities extension TargetGenericContext { @MachOImageGenerator diff --git a/Sources/MachOSymbols/MemoryPressureMonitor.swift b/Sources/Utilities/MemoryPressureMonitor.swift similarity index 81% rename from Sources/MachOSymbols/MemoryPressureMonitor.swift rename to Sources/Utilities/MemoryPressureMonitor.swift index d90e4ffa..5a59a4d8 100644 --- a/Sources/MachOSymbols/MemoryPressureMonitor.swift +++ b/Sources/Utilities/MemoryPressureMonitor.swift @@ -1,16 +1,18 @@ import Dispatch -class MemoryPressureMonitor { +package class MemoryPressureMonitor { private var memoryPressureSource: DispatchSourceMemoryPressure? private let queue = DispatchQueue(label: "com.JH.MemoryPressureMonitorQueue") - var memoryWarningHandler: (() -> Void)? + package var memoryWarningHandler: (() -> Void)? - var memoryCriticalHandler: (() -> Void)? + package var memoryCriticalHandler: (() -> Void)? - func startMonitoring() { + package init() {} + + package func startMonitoring() { guard memoryPressureSource == nil else { return } let source = DispatchSource.makeMemoryPressureSource(eventMask: [.warning, .critical], queue: queue) @@ -33,7 +35,7 @@ class MemoryPressureMonitor { source.resume() } - func stopMonitoring() { + package func stopMonitoring() { memoryPressureSource?.cancel() memoryPressureSource = nil } diff --git a/Sources/SwiftDump/Utils/OffsetEnumeratedSequence.swift b/Sources/Utilities/OffsetEnumeratedSequence.swift similarity index 100% rename from Sources/SwiftDump/Utils/OffsetEnumeratedSequence.swift rename to Sources/Utilities/OffsetEnumeratedSequence.swift diff --git a/Sources/SwiftDump/Utils/StringBuilder.swift b/Sources/Utilities/StringBuilder.swift similarity index 100% rename from Sources/SwiftDump/Utils/StringBuilder.swift rename to Sources/Utilities/StringBuilder.swift diff --git a/Tests/MachOSwiftSectionTests/SymbolDemangleTests.swift b/Tests/MachOSwiftSectionTests/SymbolDemangleTests.swift index addd3ca7..3f124f70 100644 --- a/Tests/MachOSwiftSectionTests/SymbolDemangleTests.swift +++ b/Tests/MachOSwiftSectionTests/SymbolDemangleTests.swift @@ -34,10 +34,12 @@ final class SymbolDemangleTests: DyldCacheTests { } } } - - @Test func symbolsInSwiftUI() async throws { + + #if !SILENT_TEST + @Test func writeSwiftUISymbolsToDesktop() async throws { var string = "" - let symbols = try symbols(for: .AttributeGraph) + let imageName: MachOImageName = .SwiftUI + let symbols = try symbols(for: imageName) for symbol in symbols { let swiftStdlibDemangledName = stdlib_demangleName(symbol.stringValue) guard !symbol.stringValue.hasSuffix("$delayInitStub") else { continue } @@ -47,9 +49,10 @@ final class SymbolDemangleTests: DyldCacheTests { string += "\n" string += "\n" } - try string.write(to: .desktopDirectory.appendingPathComponent("AttributeGraphSwiftSymbols.txt"), atomically: true, encoding: .utf8) + try string.write(to: .desktopDirectory.appendingPathComponent("\(imageName.rawValue)-SwiftSymbols.txt"), atomically: true, encoding: .utf8) } - + #endif + @Test func demangle() async throws { var demangler = Demangler(scalars: "_$s6Charts10ChartProxyV5value2at2asx_q_tSgSo7CGPointV_x_q_tmtAA9PlottableRzAaJR_r0_lF".unicodeScalars) let node = try demangler.demangleSymbol() diff --git a/Tests/SwiftDumpTests/DyldCacheDumpTests.swift b/Tests/SwiftDumpTests/DyldCacheDumpTests.swift index 8c830366..d81e63a1 100644 --- a/Tests/SwiftDumpTests/DyldCacheDumpTests.swift +++ b/Tests/SwiftDumpTests/DyldCacheDumpTests.swift @@ -8,7 +8,9 @@ import MachOFoundation @testable import MachOTestingSupport @Suite(.serialized) -final class DyldCacheDumpTests: DyldCacheTests, DumpableTests {} +final class DyldCacheDumpTests: DyldCacheTests, DumpableTests { + override class var cacheImageName: MachOImageName { .SwiftUICore } +} extension DyldCacheDumpTests { @Test func typesInCacheFile() async throws { @@ -58,15 +60,15 @@ extension DyldCacheDumpTests { @Test func associatedTypesInSubCacheFile() async throws { try await dumpAssociatedTypes(for: machOFileInSubCache) } - + @Test func builtinTypesInCacheFile() async throws { try await dumpBuiltinTypes(for: machOFileInCache) } - + @Test func builtinTypesInMainCacheFile() async throws { try await dumpBuiltinTypes(for: machOFileInMainCache) } - + @Test func builtinTypesInSubCacheFile() async throws { try await dumpBuiltinTypes(for: machOFileInSubCache) } diff --git a/Tests/SwiftDumpTests/XcodeMachOFileDumpTests.swift b/Tests/SwiftDumpTests/XcodeMachOFileDumpTests.swift index 080cefc0..263aab4d 100644 --- a/Tests/SwiftDumpTests/XcodeMachOFileDumpTests.swift +++ b/Tests/SwiftDumpTests/XcodeMachOFileDumpTests.swift @@ -26,4 +26,10 @@ extension XcodeMachOFileDumpTests { @Test func associatedTypesInFile() async throws { try await dumpAssociatedTypes(for: machOFile) } + + @Test func symbols() async throws { + if let symbols: Symbols = try RelativeDirectPointer(relativeOffset: 2).resolve(from: 72870, in: machOFile) { + print(symbols) + } + } } From a63b6f61dfcf3b91c7dcd9308f9ea3cb42ea27b3 Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Sun, 29 Jun 2025 22:05:01 +0800 Subject: [PATCH 08/10] Node Refactoring --- Package.swift | 1 - Sources/Demangle/Main/NodePrintContext.swift | 8 + Sources/Demangle/Main/NodePrintState.swift | 5 + Sources/Demangle/Main/NodePrinter.swift | 2770 +++++++++-------- Sources/Demangle/Main/NodePrinterTarget.swift | 14 + .../Node/Node+CustomStringConvertible.swift | 31 +- Sources/Demangle/Node/Node+Kind.swift | 8 +- Sources/Demangle/Utils/Common.swift | 67 - Sources/Demangle/Utils/Extensions.swift | 34 +- Sources/Demangle/Utils/Punycode.swift | 68 + Sources/MachOPointer/RelativePointers.swift | 1 - Sources/MachOReading/Resolvable.swift | 3 +- .../Extensions/Node+SemanticString.swift | 40 + 13 files changed, 1581 insertions(+), 1469 deletions(-) create mode 100644 Sources/Demangle/Main/NodePrintContext.swift create mode 100644 Sources/Demangle/Main/NodePrintState.swift create mode 100644 Sources/Demangle/Main/NodePrinterTarget.swift create mode 100644 Sources/Demangle/Utils/Punycode.swift create mode 100644 Sources/SwiftDump/Extensions/Node+SemanticString.swift diff --git a/Package.swift b/Package.swift index 715dcd93..43ff4dc3 100644 --- a/Package.swift +++ b/Package.swift @@ -126,7 +126,6 @@ let package = Package( .target( name: "Demangle", dependencies: [ - "Semantic", .product(name: "FoundationToolbox", package: "FrameworkToolbox"), ] ), diff --git a/Sources/Demangle/Main/NodePrintContext.swift b/Sources/Demangle/Main/NodePrintContext.swift new file mode 100644 index 00000000..11ef423c --- /dev/null +++ b/Sources/Demangle/Main/NodePrintContext.swift @@ -0,0 +1,8 @@ +package struct NodePrintContext { + package let node: Node + package let state: NodePrintState + + package static func context(for node: Node, state: NodePrintState) -> NodePrintContext { + NodePrintContext(node: node, state: state) + } +} diff --git a/Sources/Demangle/Main/NodePrintState.swift b/Sources/Demangle/Main/NodePrintState.swift new file mode 100644 index 00000000..84616ce9 --- /dev/null +++ b/Sources/Demangle/Main/NodePrintState.swift @@ -0,0 +1,5 @@ +package enum NodePrintState { + case printIdentifier + case printFunctionParameters + case printModule +} diff --git a/Sources/Demangle/Main/NodePrinter.swift b/Sources/Demangle/Main/NodePrinter.swift index 82d548a2..004e92a7 100644 --- a/Sources/Demangle/Main/NodePrinter.swift +++ b/Sources/Demangle/Main/NodePrinter.swift @@ -1,1442 +1,1490 @@ -import Semantic +package struct NodePrinter: Sendable { + private var target: Target + private var specializationPrefixPrinted: Bool + private var options: DemangleOptions + private var hidingCurrentModule: String = "" -struct NodePrinter: Sendable { - var target: SemanticString - var specializationPrefixPrinted: Bool - var options: DemangleOptions - var hidingCurrentModule: String = "" - - init(options: DemangleOptions = .default) { + package init(options: DemangleOptions = .default) { self.target = .init() self.specializationPrefixPrinted = false self.options = options } - func shouldPrintContext(_ context: Node) -> Bool { - guard options.contains(.qualifyEntities) else { - return false - } - if !options.contains(.showModuleInDependentMemberType), let dependentMemberType = context.parent?.parent?.parent?.parent, dependentMemberType.kind == .dependentMemberType { - return false - } - if context.kind == .module, let text = context.text, !text.isEmpty { - switch text { - case stdlibName: return options.contains(.displayStdlibModule) - case objcModule: return options.contains(.displayObjCModule) - case hidingCurrentModule: return false - default: - if text.starts(with: lldbExpressionsModuleNamePrefix) { - return options.contains(.displayDebuggerGeneratedModule) - } - } - } - return true - } - - mutating func printOptional(_ optional: Node?, prefix: String? = nil, suffix: String? = nil, asPrefixContext: Bool = false) -> Node? { - guard let o = optional else { return nil } - prefix.map { target.write($0) } - let r = printName(o) - suffix.map { target.write($0) } - return r - } - - mutating func printFirstChild(_ ofName: Node, prefix: String? = nil, suffix: String? = nil, asPrefixContext: Bool = false) { - _ = printOptional(ofName.children.at(0), prefix: prefix, suffix: suffix) - } - - mutating func printSequence(_ names: S, prefix: String? = nil, suffix: String? = nil, separator: String? = nil) where S: Sequence, S.Element == Node { - var isFirst = true - prefix.map { target.write($0) } - for c in names { - if let s = separator, !isFirst { - target.write(s) - } else { - isFirst = false - } - _ = printName(c) - } - suffix.map { target.write($0) } - } - - mutating func printChildren(_ ofName: Node, prefix: String? = nil, suffix: String? = nil, separator: String? = nil) { - printSequence(ofName.children, prefix: prefix, suffix: suffix, separator: separator) - } - - mutating func printMacro(name: Node, asPrefixContext: Bool, label: String) -> Node? { - return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .noType, hasName: true, extraName: "\(label) macro @\(name.children.at(2)?.description ?? "") expansion #", extraIndex: (name.children.at(3)?.index ?? 0) + 1) + package mutating func printRoot(_ root: Node) -> Target { + _ = printName(root) + return target } - mutating func printAnonymousContext(_ name: Node) { - if options.contains(.qualifyEntities), options.contains(.displayExtensionContexts) { - _ = printOptional(name.children.at(1)) - target.write(".(unknown context at " + (name.children.first?.text ?? "") + ")") - if let second = name.children.at(2), !second.children.isEmpty { - target.write("<") - _ = printName(second) - target.write(">") + private mutating func printName(_ name: Node, asPrefixContext: Bool = false) -> Node? { + switch name.kind { + case .accessibleFunctionRecord: + if !options.contains(.shortenThunk) { + target.write("accessible function runtime record for ") } - } - } - - mutating func printExtension(_ name: Node) { - if options.contains(.qualifyEntities), options.contains(.displayExtensionContexts) { - printFirstChild(name, prefix: "(extension in ", suffix: "):", asPrefixContext: true) - } - _ = printOptional(name.children.at(1)) - _ = printOptional(!options.contains(.printForTypeName) ? name.children.at(2) : nil) - } - - mutating func printSuffix(_ name: Node) { - if options.contains(.displayUnmangledSuffix) { - target.write(" with unmangled suffix ") - quotedString(name.text ?? "") - } - } - - mutating func printPrivateDeclName(_ name: Node) { - _ = printOptional(name.children.at(1), prefix: options.contains(.showPrivateDiscriminators) ? "(" : nil) - target.write(options.contains(.showPrivateDiscriminators) ? "\(name.children.count > 1 ? " " : "(")in \(name.children.at(0)?.text ?? ""))" : "") - } - - mutating func printModule(_ name: Node) { - if options.contains(.displayModuleNames) { - target.write(name.text ?? "", type: .other) - } - } - - mutating func printReturnType(_ name: Node) { - if name.children.isEmpty, let t = name.text { - target.write(t) - } else { - printChildren(name) - } - } - - mutating func printRetroactiveConformance(_ name: Node) { - if name.children.count == 2 { - printChildren(name, prefix: "retroactive @ ") - } - } - - mutating func printGenericSpecializationParam(_ name: Node) { - printFirstChild(name) - _ = printOptional(name.children.at(1), prefix: " with ") - name.children.slice(2, name.children.endIndex).forEach { - target.write(" and ") - _ = printName($0) - } - } - - mutating func printFunctionSignatureSpecializationParam(_ name: Node) { - var idx = 0 - while idx < name.children.count { - guard let firstChild = name.children.at(idx), let v = firstChild.index else { return } - let k = FunctionSigSpecializationParamKind(rawValue: v) - switch k { - case .boxToValue, - .boxToStack, - .inOutToOut: - _ = printOptional(name.children.at(idx)) - idx += 1 - case .constantPropFunction, - .constantPropGlobal: - _ = printOptional(name.children.at(idx), prefix: "[", suffix: " : ") - guard let t = name.children.at(idx + 1)?.text else { return } - let demangedName = (try? demangleAsNode(t))?.description ?? "" - if demangedName.isEmpty { - target.write(t) - } else { - target.write(demangedName) - } - target.write("]") - idx += 2 - case .constantPropInteger: fallthrough - case .constantPropFloat: - _ = printOptional(name.children.at(idx), prefix: "[") - _ = printOptional(name.children.at(idx + 1), prefix: " : ", suffix: "]") - idx += 2 - case .constantPropString: - _ = printOptional(name.children.at(idx), prefix: "[") - _ = printOptional(name.children.at(idx + 1), prefix: " : ") - _ = printOptional(name.children.at(idx + 2), prefix: "'", suffix: "']") - idx += 3 - case .constantPropKeyPath: - _ = printOptional(name.children.at(idx), prefix: "[") - _ = printOptional(name.children.at(idx + 1), prefix: " : ") - _ = printOptional(name.children.at(idx + 2), prefix: "<") - _ = printOptional(name.children.at(idx + 3), prefix: ",", suffix: ">]") - idx += 4 - case .closureProp: - _ = printOptional(name.children.at(idx), prefix: "[") - _ = printOptional(name.children.at(idx + 1), prefix: " : ", suffix: ", Argument Types : [") - idx += 2 - while idx < name.children.count, let c = name.children.at(idx), c.kind == .type { - _ = printName(c) - idx += 1 - if idx < name.children.count, name.children.at(idx)?.text != nil { - target.write(", ") - } - } - target.write("]") - default: - _ = printOptional(name.children.at(idx)) - idx += 1 + case .accessorAttachedMacroExpansion: + return printMacro(name: name, asPrefixContext: asPrefixContext, label: "accessor") + case .accessorFunctionReference: + target.write("accessor function at \(name.index ?? 0)") + case .allocator: + return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .functionStyle, hasName: false, extraName: (name.children.first?.kind == .class) ? "__allocating_init" : "init") + case .anonymousContext: + printAnonymousContext(name) + case .anonymousDescriptor: + printFirstChild(name, prefix: "anonymous descriptor ") + case .anyProtocolConformanceList: + printChildren(name, prefix: "(", suffix: ")", separator: ", ") + case .argumentTuple: + printFunctionParameters(labelList: nil, parameterType: name, showTypes: options.contains(.showFunctionArgumentTypes)) + case .associatedConformanceDescriptor: + printAssociatedConformanceDescriptor(name) + case .associatedType: + return nil + case .associatedTypeDescriptor: + printFirstChild(name, prefix: "associated type descriptor for ") + case .associatedTypeGenericParamRef: + printChildren(name, prefix: "generic parameter reference for associated type ") + case .associatedTypeMetadataAccessor: + printAssociatedTypeMetadataAccessor(name) + case .associatedTypeRef: + printAssociatedTypeRef(name) + case .associatedTypeWitnessTableAccessor: + printAssociatedTypeWitnessTableAccessor(name) + case .assocTypePath: + printChildren(name, separator: ".") + case .asyncAnnotation: + target.write(" async") + case .asyncAwaitResumePartialFunction: + printAsyncAwaitResumePartialFunction(name) + case .asyncFunctionPointer: + target.write("async function pointer to ") + case .asyncRemoved: + printFirstChild(name, prefix: "async demotion of ") + case .asyncSuspendResumePartialFunction: + printAsyncSuspendResumePartialFunction(name) + case .autoDiffFunction, + .autoDiffDerivativeVTableThunk: + printAutoDiffFunctionOrSimpleThunk(name) + case .autoDiffFunctionKind: + printAutoDiffFunctionKind(name) + case .autoDiffSelfReorderingReabstractionThunk: + printAutoDiffSelfReorderingReabstractionThunk(name) + case .autoDiffSubsetParametersThunk: + printAutoDiffSubsetParametersThunk(name) + case .backDeploymentFallback: + if !options.contains(.shortenThunk) { + target.write("back deployment fallback for ") } - } - } - - mutating func printFunctionSignatureSpecializationParamKind(_ name: Node) { - let raw = name.index ?? 0 - var printedOptionSet = false - if raw & FunctionSigSpecializationParamKind.existentialToGeneric.rawValue != 0 { - printedOptionSet = true - target.write(FunctionSigSpecializationParamKind.existentialToGeneric.description) - } - if raw & FunctionSigSpecializationParamKind.dead.rawValue != 0 { - if printedOptionSet { target.write(" and ") } - printedOptionSet = true - target.write(FunctionSigSpecializationParamKind.dead.description) - } - if raw & FunctionSigSpecializationParamKind.ownedToGuaranteed.rawValue != 0 { - if printedOptionSet { target.write(" and ") } - printedOptionSet = true - target.write(FunctionSigSpecializationParamKind.ownedToGuaranteed.description) - } - if raw & FunctionSigSpecializationParamKind.guaranteedToOwned.rawValue != 0 { - if printedOptionSet { target.write(" and ") } - printedOptionSet = true - target.write(FunctionSigSpecializationParamKind.guaranteedToOwned.description) - } - if raw & FunctionSigSpecializationParamKind.sroa.rawValue != 0 { - if printedOptionSet { target.write(" and ") } - printedOptionSet = true - target.write(FunctionSigSpecializationParamKind.sroa.description) - } - - if printedOptionSet { - return - } - - if let single = FunctionSigSpecializationParamKind(rawValue: raw) { - target.write(single.description) - } - } - - mutating func printLazyProtocolWitnesstableAccessor(_ name: Node) { - _ = printOptional(name.children.at(0), prefix: "lazy protocol witness table accessor for type ") - _ = printOptional(name.children.at(1), prefix: " and conformance ") - } - - mutating func printLazyProtocolWitnesstableCacheVariable(_ name: Node) { - _ = printOptional(name.children.at(0), prefix: "lazy protocol witness table cache variable for type ") - _ = printOptional(name.children.at(1), prefix: " and conformance ") - } - - mutating func printVTableThunk(_ name: Node) { - _ = printOptional(name.children.at(1), prefix: "vtable thunk for ") - _ = printOptional(name.children.at(0), prefix: " dispatching to ") - } - - mutating func printProtocolWitness(_ name: Node) { - _ = printOptional(name.children.at(1), prefix: "protocol witness for ") - _ = printOptional(name.children.at(0), prefix: " in conformance ") - } - - mutating func printPartialApplyForwarder(_ name: Node) { - target.write("partial apply\(options.contains(.shortenPartialApply) ? "" : " forwarder")") - if !name.children.isEmpty { - printChildren(name, prefix: " for ") - } - } - - mutating func printPartialApplyObjCForwarder(_ name: Node) { - target.write("partial apply\(options.contains(.shortenPartialApply) ? "" : " ObjC forwarder")") - if !name.children.isEmpty { - printChildren(name, prefix: " for ") - } - } - - mutating func printKeyPathAccessorThunkHelper(_ name: Node) { - printFirstChild(name, prefix: "key path \(name.kind == .keyPathGetterThunkHelper ? "getter" : "setter") for ", suffix: " : ") - for child in name.children.dropFirst() { - if child.kind == .isSerialized { - target.write(", ") + case .backDeploymentThunk: + if !options.contains(.shortenThunk) { + target.write("back deployment thunk for ") } - _ = printName(child) - } - } - - mutating func printKeyPathEqualityThunkHelper(_ name: Node) { - target.write("key path index \(name.kind == .keyPathEqualsThunkHelper ? "equality" : "hash") operator for ") - var dropLast = false - if let lastChild = name.children.last, lastChild.kind == .dependentGenericSignature { - _ = printName(lastChild) - dropLast = true - } - printSequence(dropLast ? Array(name.children.dropLast()) : name.children, prefix: "(", suffix: ")", separator: ", ") - } - - mutating func printFieldOffset(_ name: Node) { - printFirstChild(name) - _ = printOptional(name.children.at(1), prefix: "field offset for ", asPrefixContext: true) - } - - mutating func printReabstractionThunk(_ name: Node) { - if options.contains(.shortenThunk) { - _ = printOptional(name.children.at(name.children.count - 2), prefix: "thunk for ") - } else { - target.write("reabstraction thunk ") - target.write(name.kind == .reabstractionThunkHelper ? "helper " : "") - _ = printOptional(name.children.at(name.children.count - 3), suffix: " ") - _ = printOptional(name.children.at(name.children.count - 1), prefix: "from ") - _ = printOptional(name.children.at(name.children.count - 2), prefix: " to ") - } - } - - mutating func printAssociatedConformanceDescriptor(_ name: Node) { - _ = printOptional(name.children.at(0), prefix: "associated conformance descriptor for ") - _ = printOptional(name.children.at(1), prefix: ".") - _ = printOptional(name.children.at(2), prefix: ": ") - } - - mutating func printDefaultAssociatedConformanceAccessor(_ name: Node) { - _ = printOptional(name.children.at(0), prefix: "default associated conformance accessor for ") - _ = printOptional(name.children.at(1), prefix: ".") - _ = printOptional(name.children.at(2), prefix: ": ") - } - - mutating func printAssociatedTypeMetadataAccessor(_ name: Node) { - _ = printOptional(name.children.at(1), prefix: "associated type metadata accessor for ") - _ = printOptional(name.children.at(0), prefix: " in ") - } - - mutating func printAssociatedTypeWitnessTableAccessor(_ name: Node) { - _ = printOptional(name.children.at(1), prefix: "associated type witness table accessor for ") - _ = printOptional(name.children.at(2), prefix: " : ") - _ = printOptional(name.children.at(0), prefix: " in ") - } - - mutating func printValueWitness(_ name: Node) { - target.write(ValueWitnessKind(rawValue: name.index ?? 0)?.description ?? "") - target.write(options.contains(.shortenValueWitness) ? " for " : " value witness for ") - printFirstChild(name) - } - - mutating func printConcreteProtocolConformance(_ name: Node) { - target.write("concrete protocol conformance ") - if let index = name.index { - target.write(" #\(index)") - } - printFirstChild(name) - target.write(" to ") - _ = printOptional(name.children.at(1)) - if let thirdChild = name.children.at(2), !thirdChild.children.isEmpty { - target.write(" with conditional requirements: ") - _ = printName(thirdChild) - } - } - - mutating func printMetatype(_ name: Node) { - if name.children.count == 2 { - printFirstChild(name, suffix: " ") - } - guard let type = name.children.at(name.children.count == 2 ? 1 : 0)?.children.first else { return } - let needParens = !type.isSimpleType - target.write(needParens ? "(" : "") - _ = printName(type) - target.write(needParens ? ")" : "") - target.write(type.kind.isExistentialType ? ".Protocol" : ".Type") - } - - mutating func printExistentialMetatype(_ name: Node) { - if name.children.count == 2 { - printFirstChild(name, suffix: " ") - } - _ = printOptional(name.children.at(name.children.count == 2 ? 1 : 0), suffix: ".Type") - } - - mutating func printAssociatedTypeRef(_ name: Node) { - printFirstChild(name) - target.write(".\(name.children.at(1)?.text ?? "")") - } - - mutating func printProtocolList(_ name: Node) { - guard let typeList = name.children.first else { return } - if typeList.children.isEmpty { - target.write("Any") - } else { - printChildren(typeList, separator: " & ") - } - } - - mutating func printProtocolListWithClass(_ name: Node) { - guard name.children.count >= 2 else { return } - _ = printOptional(name.children.at(1), suffix: " & ") - if let protocolsTypeList = name.children.first?.children.first { - printChildren(protocolsTypeList, separator: " & ") - } - } - - mutating func printProtocolListWithAnyObject(_ name: Node) { - guard let prot = name.children.first, let protocolsTypeList = prot.children.first else { return } - if protocolsTypeList.children.count > 0 { - printChildren(protocolsTypeList, suffix: " & ", separator: " & ") - } - if options.contains(.qualifyEntities) { - target.write("Swift.") - } - target.write("AnyObject") - } - - mutating func printProtocolConformance(_ name: Node) { - if name.children.count == 4 { - _ = printOptional(name.children.at(2), prefix: "property behavior storage of ") - _ = printOptional(name.children.at(0), prefix: " in ") - _ = printOptional(name.children.at(1), prefix: " : ") - } else { - printFirstChild(name) - if options.contains(.displayProtocolConformances) { - _ = printOptional(name.children.at(1), prefix: " : ") - _ = printOptional(name.children.at(2), prefix: " in ") + case .baseConformanceDescriptor: + printBaseConformanceDescriptor(name) + case .baseWitnessTableAccessor: + printBaseWitnessTableAccessor(name) + case .bodyAttachedMacroExpansion: + return printMacro(name: name, asPrefixContext: asPrefixContext, label: "body") + case .boundGenericClass, + .boundGenericStructure, + .boundGenericEnum, + .boundGenericProtocol, + .boundGenericOtherNominalType, + .boundGenericTypeAlias: + printBoundGeneric(name) + case .builtinFixedArray: + printBuildInFixedArray(name) + case .builtinTupleType: + target.write("Builtin.TheTupleType") + case .builtinTypeName: + target.write(name.text ?? "") + case .canonicalPrespecializedGenericTypeCachingOnceToken: + printFirstChild(name, prefix: "flag for loading of canonical specialized generic type metadata for ") + case .canonicalSpecializedGenericMetaclass: printFirstChild(name, prefix: "specialized generic metaclass for ") + case .canonicalSpecializedGenericTypeMetadataAccessFunction: printFirstChild(name, prefix: "canonical specialized generic type metadata accessor for ") + case .cFunctionPointer, + .objCBlock, + .noEscapeFunctionType, + .escapingAutoClosureType, + .autoClosureType, + .thinFunctionType, + .functionType, + .escapingObjCBlock, + .uncurriedFunctionType: printFunctionType(name) + case .clangType: target.write(name.text ?? "") + case .class, + .structure, + .enum, + .protocol, + .typeAlias: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .noType, hasName: true) + case .classMetadataBaseOffset: printFirstChild(name, prefix: "class metadata base offset for ") + case .compileTimeConst: printFirstChild(name, prefix: "_const ") + case .concreteProtocolConformance: printConcreteProtocolConformance(name) + case .concurrentFunctionType: target.write("@Sendable ") + case .conformanceAttachedMacroExpansion: return printMacro(name: name, asPrefixContext: asPrefixContext, label: "conformance") + case .constrainedExistential: printConstrainedExistential(name) + case .constrainedExistentialRequirementList: printChildren(name, separator: ", ") + case .constrainedExistentialSelf: target.write("Self") + case .constructor: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .functionStyle, hasName: name.children.count > 2, extraName: "init") + case .coroutineContinuationPrototype: printFirstChild(name, prefix: "coroutine continuation prototype for ") + case .curryThunk: printFirstChild(name, prefix: "curry thunk of ") + case .deallocator: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .noType, hasName: false, extraName: (name.children.first?.kind == .class) ? "__deallocating_deinit" : "deinit") + case .declContext: printFirstChild(name) + case .defaultArgumentInitializer: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .noType, hasName: false, extraName: "default argument \(name.children.at(1)?.index ?? 0)") + case .defaultAssociatedConformanceAccessor: printDefaultAssociatedConformanceAccessor(name) + case .defaultAssociatedTypeMetadataAccessor: printFirstChild(name, prefix: "default associated type metadata accessor for ") + case .dependentAssociatedConformance: printChildren(name, prefix: "dependent associated conformance ") + case .dependentAssociatedTypeRef: printDependentAssociatedTypeRef(name) + case .dependentGenericConformanceRequirement: printDependentGenericConformanceRequirement(name) + case .dependentGenericInverseConformanceRequirement: printDependentGenericInverseConformanceRequirement(name) + case .dependentGenericLayoutRequirement: printDependentGenericLayoutRequirement(name) + case .dependentGenericParamCount: return nil + case .dependentGenericParamPackMarker: break + case .dependentGenericParamType: target.write(name.text ?? "") + case .dependentGenericParamValueMarker: break + case .dependentGenericSameShapeRequirement: printDependentGenericSameShapeRequirement(name) + case .dependentGenericSameTypeRequirement: printDependentGenericSameTypeRequirement(name) + case .dependentGenericType: printDependentGenericType(name) + case .dependentMemberType: printDependentMemberType(name) + case .dependentProtocolConformanceAssociated: printDependentProtocolConformanceAssociated(name) + case .dependentProtocolConformanceInherited: printDependentProtocolConformanceInherited(name) + case .dependentProtocolConformanceRoot: printDependentProtocolConformanceRoot(name) + case .dependentPseudogenericSignature, + .dependentGenericSignature: printGenericSignature(name) + case .destructor: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .noType, hasName: false, extraName: "deinit") + case .didSet: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "didset") + case .differentiabilityWitness: printDifferentiabilityWitness(name) + case .differentiableFunctionType: printDifferentiableFunctionType(name) + case .directMethodReferenceAttribute: target.write("super ") + case .directness: name.index.flatMap { Directness(rawValue: $0)?.description }.map { target.write("\($0) ") } + case .dispatchThunk: printFirstChild(name, prefix: "dispatch thunk of ") + case .distributedAccessor: + if !options.contains(.shortenThunk) { + target.write("distributed accessor for ") } - } - } - - mutating func printImplParameter(_ name: Node) { - printFirstChild(name, suffix: " ") - if name.children.count == 3 { - _ = printOptional(name.children.at(1)) - } else if name.children.count == 4 { - _ = printOptional(name.children.at(1)) - _ = printOptional(name.children.at(2)) - } - _ = printOptional(name.children.last) - } - - mutating func printDependentProtocolConformanceAssociated(_ name: Node) { - target.write("dependent associated protocol conformance ") - if let index = name.children.at(2)?.index { - target.write("#\(index) ") - } - printFirstChild(name) - target.write(" to ") - _ = printOptional(name.children.at(1)) - } - - mutating func printDependentProtocolConformanceInherited(_ name: Node) { - target.write("dependent inherited protocol conformance ") - if let index = name.children.at(2)?.index { - target.write("#\(index) ") - } - printFirstChild(name) - target.write(" to ") - _ = printOptional(name.children.at(1)) - } - - mutating func printDependentProtocolConformanceRoot(_ name: Node) { - target.write("dependent root protocol conformance ") - if let index = name.children.at(2)?.index { - target.write("#\(index) ") - } - printFirstChild(name) - target.write(" to ") - _ = printOptional(name.children.at(1)) - } - - static func genericParameterName(depth: UInt64, index: UInt64) -> String { - var name = "" - var index = index - repeat { - if let scalar = UnicodeScalar(UnicodeScalar("A").value + UInt32(index % 26)) { - name.unicodeScalars.append(scalar) + case .distributedThunk: + if !options.contains(.shortenThunk) { + target.write("distributed thunk ") } - index /= 26 - } while index != 0 - if depth != 0 { - name.append("\(depth)") - } - return name - } - - mutating func printGenericSignature(_ name: Node) { - target.write("<") - var numGenericParams = 0 - for c in name.children { - guard c.kind == .dependentGenericParamCount else { break } - numGenericParams += 1 - } - var firstRequirement = numGenericParams - for var c in name.children.dropFirst(numGenericParams) { - if c.kind == .type { - c = c.children.first ?? c + case .droppedArgument: target.write("param\(name.index ?? 0)-removed") + case .dynamicallyReplaceableFunctionImpl: + if !options.contains(.shortenThunk) { + target.write("dynamically replaceable thunk for ") } - guard c.kind == .dependentGenericParamPackMarker || c.kind == .dependentGenericParamValueMarker else { - break + case .dynamicallyReplaceableFunctionKey: + if !options.contains(.shortenThunk) { + target.write("dynamically replaceable key for ") } - firstRequirement += 1 - } - - let isGenericParamPack = { (depth: UInt64, index: UInt64) -> Bool in - for var child in name.children.dropFirst(numGenericParams).prefix(firstRequirement) { - guard child.kind == .dependentGenericParamPackMarker else { continue } - - child = child.children.first ?? child - guard child.kind == .type else { continue } - - child = child.children.first ?? child - guard child.kind == .dependentGenericParamType else { continue } - - if index == child.children.at(0)?.index, depth == child.children.at(1)?.index { - return true - } + case .dynamicallyReplaceableFunctionVar: + if !options.contains(.shortenThunk) { + target.write("dynamically replaceable variable for ") } - - return false - } - - let isGenericParamValue = { (depth: UInt64, index: UInt64) -> Node? in - for var child in name.children.dropFirst(numGenericParams).prefix(firstRequirement) { - guard child.kind == .dependentGenericParamValueMarker else { continue } - child = child.children.first ?? child - - guard child.kind == .type else { continue } - - guard - let param = child.children.at(0), - let type = child.children.at(1), - param.kind == .dependentGenericParamType - else { - continue - } - - if index == param.children.at(0)?.index, depth == param.children.at(1)?.index { - return type - } - } - - return nil - } - - for gpDepth in 0 ..< numGenericParams { - if gpDepth != 0 { - target.write("><") - } - - guard let count = name.children.at(gpDepth)?.index else { continue } - for index in 0 ..< count { - if index != 0 { - target.write(", ") - } - - // Limit the number of printed generic parameters. In practice this - // it will never be exceeded. The limit is only important for malformed - // symbols where count can be really huge. - if index >= 128 { - target.write("...") - break - } - - if isGenericParamPack(UInt64(gpDepth), UInt64(index)) { - target.write("each ") - } - - let value = isGenericParamValue(UInt64(gpDepth), UInt64(index)) - if value != nil { - target.write("let ") - } - - target.write(Self.genericParameterName(depth: UInt64(gpDepth), index: UInt64(index))) - - if let value { - target.write(": ") - _ = printName(value) - } - } - } - - if firstRequirement != name.children.count { - if options.contains(.displayWhereClauses) { - target.write(" where ") - printSequence(name.children.dropFirst(firstRequirement), separator: ", ") - } - } - target.write(">") - } - - mutating func printDependentGenericConformanceRequirement(_ name: Node) { - printFirstChild(name) - _ = printOptional(name.children.at(1), prefix: ": ") - } - - mutating func printDependentGenericLayoutRequirement(_ name: Node) { - guard let layout = name.children.at(1), let c = layout.text?.unicodeScalars.first else { return } - printFirstChild(name, suffix: ": ") - switch c { - case "U": target.write("_UnknownLayout") - case "R": target.write("_RefCountedObject") - case "N": target.write("_NativeRefCountedObject") - case "C": target.write("AnyObject") - case "D": target.write("_NativeClass") - case "T": target.write("_Trivial") - case "E", - "e": target.write("_Trivial") - case "M", - "m": target.write("_TrivialAtMost") - default: break - } - if name.children.count > 2 { - _ = printOptional(name.children.at(2), prefix: "(") - _ = printOptional(name.children.at(3), prefix: ", ") - target.write(")") - } - } - - mutating func printDependentGenericSameTypeRequirement(_ name: Node) { - printFirstChild(name) - _ = printOptional(name.children.at(1), prefix: " == ") - } - - mutating func printDependentGenericType(_ name: Node) { - guard let depType = name.children.at(1) else { return } - printFirstChild(name) - _ = printOptional(depType, prefix: depType.needSpaceBeforeType ? " " : "") - } - - mutating func printDependentMemberType(_ name: Node) { - printFirstChild(name) - target.write(".") - _ = printOptional(name.children.at(1)) - } - - mutating func printDependentAssociatedTypeRef(_ name: Node) { - _ = printOptional(name.children.at(1), suffix: ".") - printFirstChild(name) - } - - mutating func printSilBoxTypeWithLayout(_ name: Node) { - guard let layout = name.children.first else { return } - _ = printOptional(name.children.at(1), suffix: " ") - _ = printName(layout) - if let genericArgs = name.children.at(2) { - printSequence(genericArgs.children, prefix: " <", suffix: ">", separator: ", ") - } - } - - mutating func printSugaredOptional(_ name: Node) { - if let type = name.children.first { - let needParens = !type.isSimpleType - target.write(needParens ? "(" : "") - _ = printName(type) - target.write(needParens ? ")" : "") - target.write("?") - } - } - - mutating func printSugaredDictionary(_ name: Node) { - printFirstChild(name, prefix: "[", suffix: " : ") - _ = printOptional(name.children.at(1), suffix: "]") - } - - mutating func printOpaqueType(_ name: Node) { - printFirstChild(name) - target.write(".") - _ = printOptional(name.children.at(1)) - } - - mutating func printImplInvocationsSubstitutions(_ name: Node) { - if let secondChild = name.children.at(0) { - target.write(" for <") - printChildren(secondChild, separator: ", ") - target.write(">") - } - } - - mutating func printImplPatternSubstitutions(_ name: Node) { - target.write("@substituted ") - printFirstChild(name) - if let secondChild = name.children.at(1) { - target.write(" for <") - printChildren(secondChild, separator: ", ") - target.write(">") - } - } - - mutating func printImplDifferentiability(_ name: Node) { - if let text = name.text, !text.isEmpty { - target.write("\(text) ") - } - } - - mutating func printMacroExpansionLoc(_ name: Node) { - if let module = name.children.at(0) { - target.write("module ") - _ = printName(module) - } - if let file = name.children.at(1) { - target.write(" file ") - _ = printName(file) - } - if let line = name.children.at(2) { - target.write(" line ") - _ = printName(line) - } - if let column = name.children.at(3) { - target.write(" column ") - _ = printName(column) - } - } - - mutating func printGlobalActorFunctionType(_ name: Node) { - if let firstChild = name.children.first { - target.write("@") - _ = printName(firstChild) - target.write(" ") - } - } - - mutating func printGlobalVariableOnceFunction(_ name: Node) { - target.write(name.kind == .globalVariableOnceToken ? "one-time initialization token for " : "one-time initialization function for ") - if let firstChild = name.children.first { - _ = shouldPrintContext(firstChild) - } - if let secondChild = name.children.at(1) { - _ = printName(secondChild) - } - } - - mutating func printGlobalVariableOnceDeclList(_ name: Node) { - if name.children.count == 1 { - printFirstChild(name) - } else { - printSequence(name.children, prefix: "(", suffix: ")", separator: ", ") - } - } - - mutating func printTypeThrowsAnnotation(_ name: Node) { - target.write(" throws(") - if let child = name.children.first { - _ = printName(child) + case .dynamicAttribute: target.write("dynamic ") + case .dynamicSelf: target.write("Self") + case .emptyList: target.write(" empty-list ") + case .enumCase: printFirstChild(name, prefix: "enum case for ", asPrefixContext: false) + case .errorType: target.write("") + case .existentialMetatype: printExistentialMetatype(name) + case .explicitClosure: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: options.contains(.showFunctionArgumentTypes) ? .functionStyle : .noType, hasName: false, extraName: "closure #", extraIndex: (name.children.at(1)?.index ?? 0) + 1) + case .extendedExistentialTypeShape: printExtendedExistentialTypeShape(name) + case .extension: printExtension(name) + case .extensionAttachedMacroExpansion: return printMacro(name: name, asPrefixContext: asPrefixContext, label: "extension") + case .extensionDescriptor: printFirstChild(name, prefix: "extension descriptor ") + case .fieldOffset: printFieldOffset(name) + case .firstElementMarker: target.write(" first-element-marker ") + case .freestandingMacroExpansion: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .noType, hasName: true, extraName: "freestanding macro expansion #", extraIndex: (name.children.at(2)?.index ?? 0) + 1) + case .fullObjCResilientClassStub: printFirstChild(name, prefix: "full ObjC resilient class stub for ") + case .fullTypeMetadata: printFirstChild(name, prefix: "full type metadata for ") + case .function, + .boundGenericFunction: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .functionStyle, hasName: true) + case .functionSignatureSpecialization: printSpecializationPrefix(name, description: "function signature specialization") + case .functionSignatureSpecializationParam: printFunctionSignatureSpecializationParam(name) + case .functionSignatureSpecializationParamKind: printFunctionSignatureSpecializationParamKind(name) + case .functionSignatureSpecializationParamPayload: target.write((try? demangleAsNode(name.text ?? "").description) ?? (name.text ?? "")) + case .functionSignatureSpecializationReturn: printFunctionSignatureSpecializationParam(name) + case .genericPartialSpecialization: printSpecializationPrefix(name, description: "generic partial specialization", paramPrefix: "Signature = ") + case .genericPartialSpecializationNotReAbstracted: printSpecializationPrefix(name, description: "generic not-reabstracted partial specialization", paramPrefix: "Signature = ") + case .genericProtocolWitnessTable: printFirstChild(name, prefix: "generic protocol witness table for ") + case .genericProtocolWitnessTableInstantiationFunction: printFirstChild(name, prefix: "instantiation function for generic protocol witness table for ") + case .genericSpecialization, + .genericSpecializationInResilienceDomain: printSpecializationPrefix(name, description: "generic specialization") + case .genericSpecializationNotReAbstracted: printSpecializationPrefix(name, description: "generic not re-abstracted specialization") + case .genericSpecializationParam: printGenericSpecializationParam(name) + case .genericSpecializationPrespecialized: printSpecializationPrefix(name, description: "generic pre-specialization") + case .genericTypeMetadataPattern: printFirstChild(name, prefix: "generic type metadata pattern for ") + case .genericTypeParamDecl: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .noType, hasName: true) + case .getter: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "getter") + case .global: printChildren(name) + case .globalActorFunctionType: printGlobalActorFunctionType(name) + case .globalGetter: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "getter") + case .globalVariableOnceDeclList: printGlobalVariableOnceDeclList(name) + case .globalVariableOnceFunction, + .globalVariableOnceToken: printGlobalVariableOnceFunction(name) + case .hasSymbolQuery: target.write("#_hasSymbol query for ") + case .identifier: printIdentifier(name, asPrefixContext: asPrefixContext) + case .implConvention: target.write(name.text ?? "") + case .implCoroutineKind: printImplCoroutineKind(name) + case .implDifferentiabilityKind: printImplDifferentiabilityKind(name) + case .implErasedIsolation: target.write("@isolated(any)") + case .implErrorResult: printChildren(name, prefix: "@error ", separator: " ") + case .implParameter, + .implResult: printImplParameter(name) + case .implEscaping: target.write("@escaping") + case .implFunctionAttribute: target.write(name.text ?? "") + case .implFunctionConvention: printImplFunctionConvention(name) + case .implFunctionConventionName: break + case .implFunctionType: printImplFunctionType(name) + case .implicitClosure: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: options.contains(.showFunctionArgumentTypes) ? .functionStyle : .noType, hasName: false, extraName: "implicit closure #", extraIndex: (name.children.at(1)?.index ?? 0) + 1) + case .implInvocationSubstitutions: printImplInvocationSubstitutions(name) + case .implParameterResultDifferentiability: printImplParameterName(name) + case .implParameterSending: printImplParameterName(name) + case .implPatternSubstitutions: printImplPatternSubstitutions(name) + case .implSendingResult: target.write("sending") + case .implYield: printChildren(name, prefix: "@yields ", separator: " ") + case .index: target.write("\(name.index ?? 0)") + case .indexSubset: printIndexSubset(name) + case .infixOperator: target.write("\(name.text ?? "") infix") + case .initAccessor: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "init") + case .initializer: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .noType, hasName: false, extraName: "variable initialization expression") + case .inlinedGenericFunction: printSpecializationPrefix(name, description: "inlined generic function") + case .inOut: printFirstChild(name, prefix: "inout ") + case .integer: target.write("\(name.index ?? 0)") + case .isolated: printFirstChild(name, prefix: "isolated ") + case .isolatedAnyFunctionType: target.write("@isolated(any) ") + case .isolatedDeallocator: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .noType, hasName: false, extraName: name.children.first?.kind == .class ? "__isolated_deallocating_deinit" : "deinit") + case .isSerialized: target.write("serialized") + case .iVarDestroyer: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .noType, hasName: false, extraName: "__ivar_destroyer") + case .iVarInitializer: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .noType, hasName: false, extraName: "__ivar_initializer") + case .keyPathEqualsThunkHelper, + .keyPathHashThunkHelper: printKeyPathEqualityThunkHelper(name) + case .keyPathGetterThunkHelper, + .keyPathSetterThunkHelper, + .keyPathAppliedMethodThunkHelper, + .keyPathUnappliedMethodThunkHelper: printKeyPathAccessorThunkHelper(name) + case .labelList: break + case .lazyProtocolWitnessTableAccessor: printLazyProtocolWitnesstableAccessor(name) + case .lazyProtocolWitnessTableCacheVariable: printLazyProtocolWitnesstableCacheVariable(name) + case .localDeclName: _ = printOptional(name.children.at(1), suffix: " #\((name.children.at(0)?.index ?? 0) + 1)") + case .macro: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: name.children.count == 3 ? .withColon : .functionStyle, hasName: true) + case .macroExpansionLoc: printMacroExpansionLoc(name) + case .macroExpansionUniqueName: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .noType, hasName: true, extraName: "unique name #", extraIndex: (name.children.at(2)?.index ?? 0) + 1) + case .materializeForSet: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "materializeForSet") + case .memberAttachedMacroExpansion: return printMacro(name: name, asPrefixContext: asPrefixContext, label: "member") + case .memberAttributeAttachedMacroExpansion: return printMacro(name: name, asPrefixContext: asPrefixContext, label: "memberAttribute") + case .mergedFunction: target.write(!options.contains(.shortenThunk) ? "merged " : "") + case .metaclass: printFirstChild(name, prefix: "metaclass for ") + case .metadataInstantiationCache: printFirstChild(name, prefix: "metadata instantiation cache for ") + case .metatype: printMetatype(name) + case .metatypeRepresentation: target.write(name.text ?? "") + case .methodDescriptor: printFirstChild(name, prefix: "method descriptor for ") + case .methodLookupFunction: printFirstChild(name, prefix: "method lookup function for ") + case .modify2Accessor: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "modify2") + case .modifyAccessor: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "modify") + case .module: printModule(name) + case .moduleDescriptor: printFirstChild(name, prefix: "module descriptor ") + case .nativeOwningAddressor: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "nativeOwningAddressor") + case .nativeOwningMutableAddressor: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "nativeOwningMutableAddressor") + case .nativePinningAddressor: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "nativePinningAddressor") + case .nativePinningMutableAddressor: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "nativePinningMutableAddressor") + case .negativeInteger: target.write("-\(name.index ?? 0)") + case .noDerivative: printFirstChild(name, prefix: "@noDerivative ") + case .nominalTypeDescriptor: printFirstChild(name, prefix: "nominal type descriptor for ") + case .nominalTypeDescriptorRecord: printFirstChild(name, prefix: "nominal type descriptor runtime record for ") + case .noncanonicalSpecializedGenericTypeMetadata: printFirstChild(name, prefix: "noncanonical specialized generic type metadata for ") + case .noncanonicalSpecializedGenericTypeMetadataCache: printFirstChild(name, prefix: "cache variable for noncanonical specialized generic type metadata for ") + case .nonObjCAttribute: target.write("@nonobjc ") + case .nonUniqueExtendedExistentialTypeShapeSymbolicReference: + target.write("non-unique existential shape symbolic reference 0x") + target.write((name.index ?? 0).hexadecimalString) + case .number: target.write("\(name.index ?? 0)") + case .objCAsyncCompletionHandlerImpl, + .predefinedObjCAsyncCompletionHandlerImpl: printObjCAsyncCompletionHandlerImpl(name) + case .objCAttribute: target.write("@objc ") + case .objCMetadataUpdateFunction: printFirstChild(name, prefix: "ObjC metadata update function for ") + case .objCResilientClassStub: printFirstChild(name, prefix: "ObjC resilient class stub for ") + case .objectiveCProtocolSymbolicReference: + target.write("objective-c protocol symbolic reference 0x") + target.write((name.index ?? 0).hexadecimalString) + case .opaqueReturnType: target.write("some") + case .opaqueReturnTypeIndex: break + case .opaqueReturnTypeOf: printChildren(name, prefix: "<>") + case .opaqueReturnTypeParent: break + case .opaqueType: printOpaqueType(name) + case .opaqueTypeDescriptor: printFirstChild(name, prefix: "opaque type descriptor for ") + case .opaqueTypeDescriptorAccessor: printFirstChild(name, prefix: "opaque type descriptor accessor for ") + case .opaqueTypeDescriptorAccessorImpl: printFirstChild(name, prefix: "opaque type descriptor accessor impl for ") + case .opaqueTypeDescriptorAccessorKey: printFirstChild(name, prefix: "opaque type descriptor accessor key for ") + case .opaqueTypeDescriptorAccessorVar: printFirstChild(name, prefix: "opaque type descriptor accessor var for ") + case .opaqueTypeDescriptorRecord: printFirstChild(name, prefix: "opaque type descriptor runtime record for ") + case .opaqueTypeDescriptorSymbolicReference: + target.write("opaque type symbolic reference 0x") + target.write((name.index ?? 0).hexadecimalString) + case .otherNominalType: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .noType, hasName: true) + case .outlinedAssignWithCopy, + .outlinedAssignWithCopyNoValueWitness: printFirstChild(name, prefix: "outlined assign with copy of ") + case .outlinedAssignWithTake, + .outlinedAssignWithTakeNoValueWitness: printFirstChild(name, prefix: "outlined assign with take of ") + case .outlinedBridgedMethod: target.write("outlined bridged method (\(name.text ?? "")) of ") + case .outlinedConsume: + printFirstChild(name, prefix: "outlined consume of ") + _ = printOptional(name.children.at(1)) + case .outlinedCopy: + printFirstChild(name, prefix: "outlined copy of ") + _ = printOptional(name.children.at(1)) + case .outlinedDestroy, + .outlinedDestroyNoValueWitness: printFirstChild(name, prefix: "outlined destroy of ") + case .outlinedEnumGetTag: printFirstChild(name, prefix: "outlined enum get tag of ") + case .outlinedEnumProjectDataForLoad: printFirstChild(name, prefix: "outlined enum project data for load of ") + case .outlinedEnumTagStore: printFirstChild(name, prefix: "outlined enum tag store of ") + case .outlinedInitializeWithCopy, + .outlinedInitializeWithCopyNoValueWitness: printFirstChild(name, prefix: "outlined init with copy of ") + case .outlinedInitializeWithTake: printFirstChild(name, prefix: "outlined init with take of ") + case .outlinedReadOnlyObject: target.write("outlined read-only object #\(name.index ?? 0) of ") + case .outlinedRelease: printFirstChild(name, prefix: "outlined release of ") + case .outlinedRetain: printFirstChild(name, prefix: "outlined retain of ") + case .outlinedVariable: target.write("outlined variable #\(name.index ?? 0) of ") + case .owned: printFirstChild(name, prefix: "__owned ") + case .owningAddressor: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "owningAddressor") + case .owningMutableAddressor: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "owningMutableAddressor") + case .pack: printChildren(name, prefix: "Pack{", suffix: "}", separator: ", ") + case .packElement: printFirstChild(name, prefix: "/* level: \(name.children.at(1)?.index ?? 0) */ each ") + case .packElementLevel: break + case .packExpansion: printFirstChild(name, prefix: "repeat ") + case .packProtocolConformance: printChildren(name, prefix: "pack protocol conformance ") + case .partialApplyForwarder: printPartialApplyForwarder(name) + case .partialApplyObjCForwarder: printPartialApplyObjCForwarder(name) + case .peerAttachedMacroExpansion: return printMacro(name: name, asPrefixContext: asPrefixContext, label: "peer") + case .postfixOperator: target.write("\(name.text ?? "") postfix") + case .prefixOperator: target.write("\(name.text ?? "") prefix") + case .privateDeclName: printPrivateDeclName(name) + case .propertyDescriptor: printFirstChild(name, prefix: "property descriptor for ") + case .propertyWrapperBackingInitializer: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .noType, hasName: false, extraName: "property wrapper backing initializer") + case .propertyWrapperInitFromProjectedValue: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .noType, hasName: false, extraName: "property wrapper init from projected value") + case .protocolConformance: printProtocolConformance(name) + case .protocolConformanceDescriptor: printFirstChild(name, prefix: "protocol conformance descriptor for ") + case .protocolConformanceDescriptorRecord: printFirstChild(name, prefix: "protocol conformance descriptor runtime record for ") + case .protocolConformanceRefInOtherModule: printChildren(name, prefix: "protocol conformance ref (retroactive) ") + case .protocolConformanceRefInProtocolModule: printChildren(name, prefix: "protocol conformance ref (protocol's module) ") + case .protocolConformanceRefInTypeModule: printChildren(name, prefix: "protocol conformance ref (type's module) ") + case .protocolDescriptor: printFirstChild(name, prefix: "protocol descriptor for ") + case .protocolDescriptorRecord: printFirstChild(name, prefix: "protocol descriptor runtime record for ") + case .protocolList: printProtocolList(name) + case .protocolListWithAnyObject: printProtocolListWithAnyObject(name) + case .protocolListWithClass: printProtocolListWithClass(name) + case .protocolRequirementsBaseDescriptor: printFirstChild(name, prefix: "protocol requirements base descriptor for ") + case .protocolSelfConformanceDescriptor: printFirstChild(name, prefix: "protocol self-conformance descriptor for ") + case .protocolSelfConformanceWitness: printFirstChild(name, prefix: "protocol self-conformance witness for ") + case .protocolSelfConformanceWitnessTable: printFirstChild(name, prefix: "protocol self-conformance witness table for ") + case .protocolSymbolicReference: target.write("protocol symbolic reference \("0x" + String(name.index ?? 0, radix: 16, uppercase: true))") + case .protocolWitness: printProtocolWitness(name) + case .protocolWitnessTable: printFirstChild(name, prefix: "protocol witness table for ") + case .protocolWitnessTableAccessor: printFirstChild(name, prefix: "protocol witness table accessor for ") + case .protocolWitnessTablePattern: printFirstChild(name, prefix: "protocol witness table pattern for ") + case .reabstractionThunk, + .reabstractionThunkHelper: printReabstractionThunk(name) + case .reabstractionThunkHelperWithGlobalActor: printReabstracctionThunkHelperWithGlobalActor(name) + case .reabstractionThunkHelperWithSelf: printReabstractionThunkHelperWithSelf(name) + case .read2Accessor: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "read2") + case .readAccessor: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "read") + case .reflectionMetadataAssocTypeDescriptor: printFirstChild(name, prefix: "reflection metadata associated type descriptor ") + case .reflectionMetadataBuiltinDescriptor: printFirstChild(name, prefix: "reflection metadata builtin descriptor ") + case .reflectionMetadataFieldDescriptor: printFirstChild(name, prefix: "reflection metadata field descriptor ") + case .reflectionMetadataSuperclassDescriptor: printFirstChild(name, prefix: "reflection metadata superclass descriptor ") + case .relatedEntityDeclName: printFirstChild(name, prefix: "related decl '\(name.text ?? "")' for ") + case .resilientProtocolWitnessTable: printFirstChild(name, prefix: "resilient protocol witness table for ") + case .retroactiveConformance: printRetroactiveConformance(name) + case .returnType: printReturnType(name) + case .sending: printFirstChild(name, prefix: "sending ") + case .sendingResultFunctionType: target.write("sending ") + case .setter: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "setter") + case .shared: printFirstChild(name, prefix: "__shared ") + case .silBoxImmutableField, + .silBoxMutableField: printFirstChild(name, prefix: name.kind == .silBoxImmutableField ? "let " : "var ") + case .silBoxLayout: printSequence(name.children, prefix: "{\(name.children.isEmpty ? "" : " ")", suffix: " }", separator: ", ") + case .silBoxType: printFirstChild(name, prefix: "@box ") + case .silBoxTypeWithLayout: printSilBoxTypeWithLayout(name) + case .silPackDirect: printChildren(name, prefix: "@direct Pack{", suffix: "}", separator: ", ") + case .silPackIndirect: printChildren(name, prefix: "@indirect Pack{", suffix: "}", separator: ", ") + case .silThunkHopToMainActorIfNeeded: printFirstChild(name, prefix: "hop to main actor thunk of ") + case .silThunkIdentity: printFirstChild(name, prefix: "identity thunk of ") + case .specializationPassID: target.write("\(name.index ?? 0)") + case .static: printFirstChild(name, prefix: "static ") + case .subscript: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .functionStyle, hasName: true, overwriteName: "subscript") + case .suffix: printSuffix(name) + case .sugaredArray: printFirstChild(name, prefix: "[", suffix: "]") + case .sugaredDictionary: printSugaredDictionary(name) + case .sugaredOptional: printSugaredOptional(name) + case .sugaredParen: printFirstChild(name, prefix: "(", suffix: ")") + case .symbolicExtendedExistentialType: printSymbolicExtendedExistentialType(name) + case .throwsAnnotation: target.write(" throws") + case .tuple: printChildren(name, prefix: "(", suffix: ")", separator: ", ") + case .tupleElement: printTupleElement(name) + case .tupleElementName: target.write("\(name.text ?? ""): ") + case .type: printFirstChild(name) + case .typedThrowsAnnotation: printTypeThrowsAnnotation(name) + case .typeList: printChildren(name) + case .typeMangling: printFirstChild(name) + case .typeMetadata: printFirstChild(name, prefix: "type metadata for ") + case .typeMetadataAccessFunction: printFirstChild(name, prefix: "type metadata accessor for ") + case .typeMetadataCompletionFunction: printFirstChild(name, prefix: "type metadata completion function for ") + case .typeMetadataDemanglingCache: printFirstChild(name, prefix: "demangling cache variable for type metadata for ") + case .typeMetadataInstantiationCache: printFirstChild(name, prefix: "type metadata instantiation cache for ") + case .typeMetadataInstantiationFunction: printFirstChild(name, prefix: "type metadata instantiation function for ") + case .typeMetadataLazyCache: printFirstChild(name, prefix: "lazy cache variable for type metadata for ") + case .typeMetadataSingletonInitializationCache: printFirstChild(name, prefix: "type metadata singleton initialization cache for ") + case .typeSymbolicReference: target.write("type symbolic reference \("0x" + String(name.index ?? 0, radix: 16, uppercase: true))") + case .uniquable: printFirstChild(name, prefix: "uniquable ") + case .uniqueExtendedExistentialTypeShapeSymbolicReference: + target.write("non-unique existential shape symbolic reference 0x") + target.write((name.index ?? 0).hexadecimalString) + case .unknownIndex: target.write("unknown index") + case .unmanaged: printFirstChild(name, prefix: "unowned(unsafe) ") + case .unowned: printFirstChild(name, prefix: "unowned ") + case .unsafeAddressor: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "unsafeAddressor") + case .unsafeMutableAddressor: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "unsafeMutableAddressor") + case .valueWitness: printValueWitness(name) + case .valueWitnessTable: printFirstChild(name, prefix: "value witness table for ") + case .variable: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .withColon, hasName: true) + case .variadicMarker: target.write(" variadic-marker ") + case .vTableAttribute: target.write("override ") + case .vTableThunk: printVTableThunk(name) + case .weak: printFirstChild(name, prefix: options.contains(.removeWeakPrefix) ? "" : "weak ") + case .willSet: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "willset") + case .nonIsolatedCallerFunctionType: target.write("nonisolated(nonsending) ") } - target.write(")") - } - mutating func printDifferentiableFunctionType(_ name: Node) { - target.write("@differentiable") - switch UnicodeScalar(UInt8(name.index ?? 0)) { - case "f": target.write("(_forward)") - case "r": target.write("(reverse)") - case "l": target.write("(_linear)") - default: break - } + return nil } - mutating func printDifferentiabilityWitness(_ name: Node) { - let kindNodeIndex = name.children.count - (name.children.last?.kind == .dependentGenericSignature ? 4 : 3) - let kind = (name.children.at(kindNodeIndex)?.index).flatMap { Differentiability($0) } - switch kind { - case .forward: target.write("forward-mode") - case .reverse: target.write("reverse-mode") - case .normal: target.write("normal") - case .linear: target.write("linear") - default: return + private func shouldPrintContext(_ context: Node) -> Bool { + guard options.contains(.qualifyEntities) else { + return false } - target.write(" differentiability witness for ") - var idx = 0 - while idx < name.children.count, name.children.at(idx)?.kind != .index { - _ = printOptional(name.children.at(idx)) - idx += 1 + if !options.contains(.showModuleInDependentMemberType), let dependentMemberType = context.parent?.parent?.parent?.parent, dependentMemberType.kind == .dependentMemberType { + return false } - _ = printOptional(name.children.at(idx + 1), prefix: " with respect to parameters ") - _ = printOptional(name.children.at(idx + 2), prefix: " and results ") - _ = printOptional(name.children.at(idx + 3), prefix: " with ") - } - - mutating func printAsyncAwaitResumePartialFunction(_ name: Node) { - if options.contains(.showAsyncResumePartial) { - target.write("(") - _ = printName(name.children.first!) - target.write(")") - target.write(" await resume partial function for ") + if context.kind == .module, let text = context.text, !text.isEmpty { + switch text { + case stdlibName: return options.contains(.displayStdlibModule) + case objcModule: return options.contains(.displayObjCModule) + case hidingCurrentModule: return false + default: + if text.starts(with: lldbExpressionsModuleNamePrefix) { + return options.contains(.displayDebuggerGeneratedModule) + } + } } + return true } - mutating func printAsyncSuspendResumePartialFunction(_ name: Node) { - if options.contains(.showAsyncResumePartial) { - target.write("(") - _ = printName(name.children.first!) - target.write(")") - target.write(" suspend resume partial function for ") - } + private mutating func printOptional(_ optional: Node?, prefix: String? = nil, suffix: String? = nil, asPrefixContext: Bool = false) -> Node? { + guard let o = optional else { return nil } + prefix.map { target.write($0) } + let r = printName(o) + suffix.map { target.write($0) } + return r } - mutating func printExtendedExistentialTypeShape(_ name: Node) { - let savedDisplayWhereClauses = options.contains(.displayWhereClauses) - options.insert(.displayWhereClauses) - var genSig: Node? - var type: Node? - if name.children.count == 2 { - genSig = name.children.at(1) - type = name.children.at(2) - } else { - type = name.children.at(1) - } - target.write("existential shape for ") - if let genSig { - _ = printName(genSig) - target.write(" ") - } - target.write("any ") - if let type { - _ = printName(type) - } else { - target.write("") - } - if !savedDisplayWhereClauses { - options.remove(.displayWhereClauses) - } + private mutating func printFirstChild(_ ofName: Node, prefix: String? = nil, suffix: String? = nil, asPrefixContext: Bool = false) { + _ = printOptional(ofName.children.at(0), prefix: prefix, suffix: suffix) } - mutating func printSymbolicExtendedExistentialType(_ name: Node) { - guard let shape = name.children.first else { return } - let isUnique = shape.kind == .uniqueExtendedExistentialTypeShapeSymbolicReference - target.write("symbolic existential type (\(isUnique ? "" : "non-")unique) 0x") - target.writeHex(shape.index ?? 0) - target.write(" <") - guard let second = name.children.at(1) else { return } - _ = printName(second) - if let third = name.children.at(2) { - target.write(", ") - _ = printName(third) + private mutating func printSequence(_ names: S, prefix: String? = nil, suffix: String? = nil, separator: String? = nil) where S: Sequence, S.Element == Node { + var isFirst = true + prefix.map { target.write($0) } + for c in names { + if let s = separator, !isFirst { + target.write(s) + } else { + isFirst = false + } + _ = printName(c) } - target.write(">") + suffix.map { target.write($0) } } - mutating func printTupleElement(_ name: Node) { - if let label = name.children.first(where: { $0.kind == .tupleElementName }) { - target.write("\(label.text ?? ""): ") - } - guard let type = name.children.first(where: { $0.kind == .type }) else { return } - _ = printName(type) - if let _ = name.children.first(where: { $0.kind == .variadicMarker }) { - target.write("...") - } + private mutating func printChildren(_ ofName: Node, prefix: String? = nil, suffix: String? = nil, separator: String? = nil) { + printSequence(ofName.children, prefix: prefix, suffix: suffix, separator: separator) } - mutating func printObjCAsyncCompletionHandlerImpl(_ name: Node) { - if name.kind == .predefinedObjCAsyncCompletionHandlerImpl { - target.write("predefined ") - } - target.write("@objc completion handler block implementation for ") - if name.children.count >= 4 { - _ = printOptional(name.children.at(3)) - } - printFirstChild(name, suffix: " with result type ") - _ = printOptional(name.children.at(1)) - switch name.children.at(2)?.index { - case 0: break - case 1: target.write(" nonzero on error ") - case 2: target.write(" zero on error ") - default: target.write(" ") - } + private mutating func printMacro(name: Node, asPrefixContext: Bool, label: String) -> Node? { + return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .noType, hasName: true, extraName: "\(label) macro @\(name.children.at(2)?.description ?? "") expansion #", extraIndex: (name.children.at(3)?.index ?? 0) + 1) } - mutating func printImplInvocationSubstitutions(_ name: Node) { - if let secondChild = name.children.at(0) { - target.write(" for <") - printChildren(secondChild, separator: ", ") - target.write(">") + private mutating func printAnonymousContext(_ name: Node) { + if options.contains(.qualifyEntities), options.contains(.displayExtensionContexts) { + _ = printOptional(name.children.at(1)) + target.write(".(unknown context at " + (name.children.first?.text ?? "") + ")") + if let second = name.children.at(2), !second.children.isEmpty { + target.write("<") + _ = printName(second) + target.write(">") + } } } - mutating func printImplDifferentiabilityKind(_ name: Node) { - target.write("@differentiable") - if case .index(let value) = name.contents, let differentiability = Differentiability(value) { - switch differentiability { - case .normal: break - case .linear: target.write("(_linear)") - case .forward: target.write("(_forward)") - case .reverse: target.write("(reverse)") - } + private mutating func printExtension(_ name: Node) { + if options.contains(.qualifyEntities), options.contains(.displayExtensionContexts) { + printFirstChild(name, prefix: "(extension in ", suffix: "):", asPrefixContext: true) } + _ = printOptional(name.children.at(1)) + _ = printOptional(!options.contains(.printForTypeName) ? name.children.at(2) : nil) } - mutating func printImplCoroutineKind(_ name: Node) { - guard case .name(let value) = name.contents, !value.isEmpty else { return } - target.write("@\(value)") + private mutating func printSuffix(_ name: Node) { + if options.contains(.displayUnmangledSuffix) { + target.write(" with unmangled suffix ") + quotedString(name.text ?? "") + } } - mutating func printImplFunctionConvention(_ name: Node) { - target.write("@convention(") - if let second = name.children.at(1) { - target.write("\(name.children.at(0)?.text ?? ""), mangledCType: \"") - _ = printName(second) - target.write("\"") - } else { - target.write("\(name.children.at(0)?.text ?? "")") - } - target.write(")") + private mutating func printPrivateDeclName(_ name: Node) { + _ = printOptional(name.children.at(1), prefix: options.contains(.showPrivateDiscriminators) ? "(" : nil) + target.write(options.contains(.showPrivateDiscriminators) ? "\(name.children.count > 1 ? " " : "(")in \(name.children.at(0)?.text ?? ""))" : "") } - mutating func printImplParameterName(_ name: Node) { - guard case .name(let value) = name.contents, !value.isEmpty else { return } - target.write("\(value) ") + private mutating func printModule(_ name: Node) { + if options.contains(.displayModuleNames) { + target.write(name.text ?? "", context: .context(for: name, state: .printModule)) + } } - mutating func printBaseConformanceDescriptor(_ name: Node) { - printFirstChild(name, prefix: "base conformance descriptor for ") - _ = printOptional(name.children.at(1), prefix: ": ") + private mutating func printReturnType(_ name: Node) { + if name.children.isEmpty, let t = name.text { + target.write(t) + } else { + printChildren(name) + } } - mutating func printReabstractionThunkHelperWithSelf(_ name: Node) { - target.write("reabstraction thunk ") - var idx = 0 - if name.children.count == 4 { - printFirstChild(name, suffix: " ") - idx += 1 + private mutating func printRetroactiveConformance(_ name: Node) { + if name.children.count == 2 { + printChildren(name, prefix: "retroactive @ ") } - _ = printOptional(name.children.at(idx + 2), prefix: "from ") - _ = printOptional(name.children.at(idx + 1), prefix: " to ") - _ = printOptional(name.children.at(idx), prefix: " self ") } - mutating func printReabstracctionThunkHelperWithGlobalActor(_ name: Node) { + private mutating func printGenericSpecializationParam(_ name: Node) { printFirstChild(name) - _ = printOptional(name.children.at(1), prefix: " with global actor constraint ") + _ = printOptional(name.children.at(1), prefix: " with ") + name.children.slice(2, name.children.endIndex).forEach { + target.write(" and ") + _ = printName($0) + } } - mutating func printBuildInFixedArray(_ name: Node) { - _ = printOptional(name.children.first, prefix: "Builtin.FixedArray<") - _ = printOptional(name.children.at(1), prefix: ", ", suffix: ">") + private mutating func printFunctionSignatureSpecializationParam(_ name: Node) { + var idx = 0 + while idx < name.children.count { + guard let firstChild = name.children.at(idx), let v = firstChild.index else { return } + let k = FunctionSigSpecializationParamKind(rawValue: v) + switch k { + case .boxToValue, + .boxToStack, + .inOutToOut: + _ = printOptional(name.children.at(idx)) + idx += 1 + case .constantPropFunction, + .constantPropGlobal: + _ = printOptional(name.children.at(idx), prefix: "[", suffix: " : ") + guard let t = name.children.at(idx + 1)?.text else { return } + let demangedName = (try? demangleAsNode(t))?.description ?? "" + if demangedName.isEmpty { + target.write(t) + } else { + target.write(demangedName) + } + target.write("]") + idx += 2 + case .constantPropInteger: fallthrough + case .constantPropFloat: + _ = printOptional(name.children.at(idx), prefix: "[") + _ = printOptional(name.children.at(idx + 1), prefix: " : ", suffix: "]") + idx += 2 + case .constantPropString: + _ = printOptional(name.children.at(idx), prefix: "[") + _ = printOptional(name.children.at(idx + 1), prefix: " : ") + _ = printOptional(name.children.at(idx + 2), prefix: "'", suffix: "']") + idx += 3 + case .constantPropKeyPath: + _ = printOptional(name.children.at(idx), prefix: "[") + _ = printOptional(name.children.at(idx + 1), prefix: " : ") + _ = printOptional(name.children.at(idx + 2), prefix: "<") + _ = printOptional(name.children.at(idx + 3), prefix: ",", suffix: ">]") + idx += 4 + case .closureProp: + _ = printOptional(name.children.at(idx), prefix: "[") + _ = printOptional(name.children.at(idx + 1), prefix: " : ", suffix: ", Argument Types : [") + idx += 2 + while idx < name.children.count, let c = name.children.at(idx), c.kind == .type { + _ = printName(c) + idx += 1 + if idx < name.children.count, name.children.at(idx)?.text != nil { + target.write(", ") + } + } + target.write("]") + default: + _ = printOptional(name.children.at(idx)) + idx += 1 + } + } } - mutating func printAutoDiffFunctionOrSimpleThunk(_ name: Node) { - var prefixEndIndex = 0 - while prefixEndIndex < name.children.count, name.children[prefixEndIndex].kind != .autoDiffFunctionKind { - prefixEndIndex += 1 + private mutating func printFunctionSignatureSpecializationParamKind(_ name: Node) { + let raw = name.index ?? 0 + var printedOptionSet = false + if raw & FunctionSigSpecializationParamKind.existentialToGeneric.rawValue != 0 { + printedOptionSet = true + target.write(FunctionSigSpecializationParamKind.existentialToGeneric.description) } - - let funcKind = name.children.at(prefixEndIndex) - let paramIndices = name.children.at(prefixEndIndex + 1) - let resultIndices = name.children.at(prefixEndIndex + 2) - if name.kind == .autoDiffDerivativeVTableThunk { - target.write("vtable thunk for ") + if raw & FunctionSigSpecializationParamKind.dead.rawValue != 0 { + if printedOptionSet { target.write(" and ") } + printedOptionSet = true + target.write(FunctionSigSpecializationParamKind.dead.description) } - _ = printOptional(funcKind) - target.write(" of ") - var optionalGenSig: Node? - for i in 0 ..< prefixEndIndex { - if i == prefixEndIndex - 1, name.children.at(i)?.kind == .dependentGenericSignature { - optionalGenSig = name.children.at(i) - break - } - _ = printOptional(name.children.at(i)) + if raw & FunctionSigSpecializationParamKind.ownedToGuaranteed.rawValue != 0 { + if printedOptionSet { target.write(" and ") } + printedOptionSet = true + target.write(FunctionSigSpecializationParamKind.ownedToGuaranteed.description) } - if options.contains(.shortenThunk) { + if raw & FunctionSigSpecializationParamKind.guaranteedToOwned.rawValue != 0 { + if printedOptionSet { target.write(" and ") } + printedOptionSet = true + target.write(FunctionSigSpecializationParamKind.guaranteedToOwned.description) + } + if raw & FunctionSigSpecializationParamKind.sroa.rawValue != 0 { + if printedOptionSet { target.write(" and ") } + printedOptionSet = true + target.write(FunctionSigSpecializationParamKind.sroa.description) + } + + if printedOptionSet { return } - target.write(" with respect to parameters ") - _ = printOptional(paramIndices) - target.write(" and results ") - _ = printOptional(resultIndices) - _ = printOptional(options.contains(.displayWhereClauses) ? optionalGenSig : nil, prefix: " with ") + + if let single = FunctionSigSpecializationParamKind(rawValue: raw) { + target.write(single.description) + } } - mutating func printAutoDiffFunctionKind(_ name: Node) { - guard let kind = name.index else { return } - switch AutoDiffFunctionKind(kind) { - case .forward: target.write("forward-mode derivative") - case .reverse: target.write("reverse-mode derivative") - case .differential: target.write("differential") - case .pullback: target.write("pullback") - default: break + private mutating func printLazyProtocolWitnesstableAccessor(_ name: Node) { + _ = printOptional(name.children.at(0), prefix: "lazy protocol witness table accessor for type ") + _ = printOptional(name.children.at(1), prefix: " and conformance ") + } + + private mutating func printLazyProtocolWitnesstableCacheVariable(_ name: Node) { + _ = printOptional(name.children.at(0), prefix: "lazy protocol witness table cache variable for type ") + _ = printOptional(name.children.at(1), prefix: " and conformance ") + } + + private mutating func printVTableThunk(_ name: Node) { + _ = printOptional(name.children.at(1), prefix: "vtable thunk for ") + _ = printOptional(name.children.at(0), prefix: " dispatching to ") + } + + private mutating func printProtocolWitness(_ name: Node) { + _ = printOptional(name.children.at(1), prefix: "protocol witness for ") + _ = printOptional(name.children.at(0), prefix: " in conformance ") + } + + private mutating func printPartialApplyForwarder(_ name: Node) { + target.write("partial apply\(options.contains(.shortenPartialApply) ? "" : " forwarder")") + if !name.children.isEmpty { + printChildren(name, prefix: " for ") } } - mutating func printAutoDiffSelfReorderingReabstractionThunk(_ name: Node) { - target.write("autodiff self-reordering reabstraction thunk ") - let fromType = name.children.first - _ = printOptional(options.contains(.shortenThunk) ? fromType : nil, prefix: "for ") - let toType = name.children.at(1) - var kindIndex = 2 - var optionalGenSig: Node? - if name.children.at(kindIndex)?.kind == .dependentGenericSignature { - optionalGenSig = name.children.at(kindIndex) - kindIndex += 1 + private mutating func printPartialApplyObjCForwarder(_ name: Node) { + target.write("partial apply\(options.contains(.shortenPartialApply) ? "" : " ObjC forwarder")") + if !name.children.isEmpty { + printChildren(name, prefix: " for ") } - target.write("for ") - _ = printOptional(name.children.at(kindIndex)) - _ = printOptional(optionalGenSig, suffix: " ") - _ = printOptional(fromType, prefix: " from ") - _ = printOptional(toType, prefix: " to ") } - mutating func printAutoDiffSubsetParametersThunk(_ name: Node) { - target.write("autodiff subset parameters thunk for ") - let lastIndex = name.children.count - 1 - let toParamIndices = name.children.at(lastIndex) - let resultIndices = name.children.at(lastIndex - 1) - let paramIndices = name.children.at(lastIndex - 2) - let kind = name.children.at(lastIndex - 3) - let currentIndex = lastIndex - 4 - _ = printOptional(kind, suffix: " from ") - if currentIndex == 0 { - printFirstChild(name) - } else { - printSequence(name.children.prefix(currentIndex)) + private mutating func printKeyPathAccessorThunkHelper(_ name: Node) { + printFirstChild(name, prefix: "key path \(name.kind == .keyPathGetterThunkHelper ? "getter" : "setter") for ", suffix: " : ") + for child in name.children.dropFirst() { + if child.kind == .isSerialized { + target.write(", ") + } + _ = printName(child) } - if options.contains(.shortenThunk) { - return + } + + private mutating func printKeyPathEqualityThunkHelper(_ name: Node) { + target.write("key path index \(name.kind == .keyPathEqualsThunkHelper ? "equality" : "hash") operator for ") + var dropLast = false + if let lastChild = name.children.last, lastChild.kind == .dependentGenericSignature { + _ = printName(lastChild) + dropLast = true } - target.write(" with respect to parameters ") - _ = printOptional(paramIndices) - target.write(" and results ") - _ = printOptional(resultIndices) - target.write(" to parameters ") - _ = printOptional(toParamIndices) - _ = printOptional(currentIndex > 0 ? name.children.at(currentIndex) : nil, prefix: " of type ") + printSequence(dropLast ? Array(name.children.dropLast()) : name.children, prefix: "(", suffix: ")", separator: ", ") } - mutating func printIndexSubset(_ name: Node) { - target.write("{") - var printedAnyIndex = false - for (i, c) in (name.text ?? "").enumerated() { - if c != "S" { - continue - } - if printedAnyIndex { - target.write(", ") - } - target.write("\(i)") - printedAnyIndex = true + private mutating func printFieldOffset(_ name: Node) { + printFirstChild(name) + _ = printOptional(name.children.at(1), prefix: "field offset for ", asPrefixContext: true) + } + + private mutating func printReabstractionThunk(_ name: Node) { + if options.contains(.shortenThunk) { + _ = printOptional(name.children.at(name.children.count - 2), prefix: "thunk for ") + } else { + target.write("reabstraction thunk ") + target.write(name.kind == .reabstractionThunkHelper ? "helper " : "") + _ = printOptional(name.children.at(name.children.count - 3), suffix: " ") + _ = printOptional(name.children.at(name.children.count - 1), prefix: "from ") + _ = printOptional(name.children.at(name.children.count - 2), prefix: " to ") } - target.write("}") } - mutating func printBaseWitnessTableAccessor(_ name: Node) { - _ = printOptional(name.children.at(1), prefix: "base witness table accessor for ") - _ = printOptional(name.children.at(0), prefix: " in ") + private mutating func printAssociatedConformanceDescriptor(_ name: Node) { + _ = printOptional(name.children.at(0), prefix: "associated conformance descriptor for ") + _ = printOptional(name.children.at(1), prefix: ".") + _ = printOptional(name.children.at(2), prefix: ": ") } - mutating func printDependentGenericInverseConformanceRequirement(_ name: Node) { - printFirstChild(name, suffix: ": ~") - switch name.children.at(1)?.index { - case 0: target.write("Swift.Copyable") - case 1: target.write("Swift.Escapable") - default: target.write("Swift.") - } + private mutating func printDefaultAssociatedConformanceAccessor(_ name: Node) { + _ = printOptional(name.children.at(0), prefix: "default associated conformance accessor for ") + _ = printOptional(name.children.at(1), prefix: ".") + _ = printOptional(name.children.at(2), prefix: ": ") } - mutating func printDependentGenericSameShapeRequirement(_ name: Node) { - _ = printOptional(name.children.at(0), suffix: ".shape == ") - _ = printOptional(name.children.at(1), suffix: ".shape") + private mutating func printAssociatedTypeMetadataAccessor(_ name: Node) { + _ = printOptional(name.children.at(1), prefix: "associated type metadata accessor for ") + _ = printOptional(name.children.at(0), prefix: " in ") } - mutating func printConstrainedExistential(_ name: Node) { - printFirstChild(name, prefix: "any ") - _ = printOptional(name.children.at(1), prefix: "<", suffix: ">") + private mutating func printAssociatedTypeWitnessTableAccessor(_ name: Node) { + _ = printOptional(name.children.at(1), prefix: "associated type witness table accessor for ") + _ = printOptional(name.children.at(2), prefix: " : ") + _ = printOptional(name.children.at(0), prefix: " in ") } - mutating func printName(_ name: Node, asPrefixContext: Bool = false) -> Node? { - switch name.kind { - case .accessibleFunctionRecord: target.write(conditional: !options.contains(.shortenThunk), "accessible function runtime record for ") - case .accessorAttachedMacroExpansion: return printMacro(name: name, asPrefixContext: asPrefixContext, label: "accessor") - case .accessorFunctionReference: target.write("accessor function at \(name.index ?? 0)") - case .allocator: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .functionStyle, hasName: false, extraName: (name.children.first?.kind == .class) ? "__allocating_init" : "init") - case .anonymousContext: printAnonymousContext(name) - case .anonymousDescriptor: printFirstChild(name, prefix: "anonymous descriptor ") - case .anyProtocolConformanceList: printChildren(name, prefix: "(", suffix: ")", separator: ", ") - case .argumentTuple: printFunctionParameters(labelList: nil, parameterType: name, showTypes: options.contains(.showFunctionArgumentTypes)) - case .associatedConformanceDescriptor: printAssociatedConformanceDescriptor(name) - case .associatedType: return nil - case .associatedTypeDescriptor: printFirstChild(name, prefix: "associated type descriptor for ") - case .associatedTypeGenericParamRef: printChildren(name, prefix: "generic parameter reference for associated type ") - case .associatedTypeMetadataAccessor: printAssociatedTypeMetadataAccessor(name) - case .associatedTypeRef: printAssociatedTypeRef(name) - case .associatedTypeWitnessTableAccessor: printAssociatedTypeWitnessTableAccessor(name) - case .assocTypePath: printChildren(name, separator: ".") - case .asyncAnnotation: target.write(" async") - case .asyncAwaitResumePartialFunction: printAsyncAwaitResumePartialFunction(name) - case .asyncFunctionPointer: target.write("async function pointer to ") - case .asyncRemoved: printFirstChild(name, prefix: "async demotion of ") - case .asyncSuspendResumePartialFunction: printAsyncSuspendResumePartialFunction(name) - case .autoDiffFunction, - .autoDiffDerivativeVTableThunk: printAutoDiffFunctionOrSimpleThunk(name) - case .autoDiffFunctionKind: printAutoDiffFunctionKind(name) - case .autoDiffSelfReorderingReabstractionThunk: printAutoDiffSelfReorderingReabstractionThunk(name) - case .autoDiffSubsetParametersThunk: printAutoDiffSubsetParametersThunk(name) - case .backDeploymentFallback: target.write(conditional: !options.contains(.shortenThunk), "back deployment fallback for ") - case .backDeploymentThunk: target.write(conditional: !options.contains(.shortenThunk), "back deployment thunk for ") - case .baseConformanceDescriptor: printBaseConformanceDescriptor(name) - case .baseWitnessTableAccessor: printBaseWitnessTableAccessor(name) - case .bodyAttachedMacroExpansion: return printMacro(name: name, asPrefixContext: asPrefixContext, label: "body") - case .boundGenericClass, - .boundGenericStructure, - .boundGenericEnum, - .boundGenericProtocol, - .boundGenericOtherNominalType, - .boundGenericTypeAlias: printBoundGeneric(name) - case .builtinFixedArray: printBuildInFixedArray(name) - case .builtinTupleType: target.write("Builtin.TheTupleType") - case .builtinTypeName: target.write(name.text ?? "") - case .canonicalPrespecializedGenericTypeCachingOnceToken: printFirstChild(name, prefix: "flag for loading of canonical specialized generic type metadata for ") - case .canonicalSpecializedGenericMetaclass: printFirstChild(name, prefix: "specialized generic metaclass for ") - case .canonicalSpecializedGenericTypeMetadataAccessFunction: printFirstChild(name, prefix: "canonical specialized generic type metadata accessor for ") - case .cFunctionPointer, - .objCBlock, - .noEscapeFunctionType, - .escapingAutoClosureType, - .autoClosureType, - .thinFunctionType, - .functionType, - .escapingObjCBlock, - .uncurriedFunctionType: printFunctionType(name) - case .clangType: target.write(name.text ?? "") - case .class, - .structure, - .enum, - .protocol, - .typeAlias: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .noType, hasName: true) - case .classMetadataBaseOffset: printFirstChild(name, prefix: "class metadata base offset for ") - case .compileTimeConst: printFirstChild(name, prefix: "_const ") - case .concreteProtocolConformance: printConcreteProtocolConformance(name) - case .concurrentFunctionType: target.write("@Sendable ") - case .conformanceAttachedMacroExpansion: return printMacro(name: name, asPrefixContext: asPrefixContext, label: "conformance") - case .constrainedExistential: printConstrainedExistential(name) - case .constrainedExistentialRequirementList: printChildren(name, separator: ", ") - case .constrainedExistentialSelf: target.write("Self") - case .constructor: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .functionStyle, hasName: name.children.count > 2, extraName: "init") - case .coroutineContinuationPrototype: printFirstChild(name, prefix: "coroutine continuation prototype for ") - case .curryThunk: printFirstChild(name, prefix: "curry thunk of ") - case .deallocator: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .noType, hasName: false, extraName: (name.children.first?.kind == .class) ? "__deallocating_deinit" : "deinit") - case .declContext: printFirstChild(name) - case .defaultArgumentInitializer: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .noType, hasName: false, extraName: "default argument \(name.children.at(1)?.index ?? 0)") - case .defaultAssociatedConformanceAccessor: printDefaultAssociatedConformanceAccessor(name) - case .defaultAssociatedTypeMetadataAccessor: printFirstChild(name, prefix: "default associated type metadata accessor for ") - case .dependentAssociatedConformance: printChildren(name, prefix: "dependent associated conformance ") - case .dependentAssociatedTypeRef: printDependentAssociatedTypeRef(name) - case .dependentGenericConformanceRequirement: printDependentGenericConformanceRequirement(name) - case .dependentGenericInverseConformanceRequirement: printDependentGenericInverseConformanceRequirement(name) - case .dependentGenericLayoutRequirement: printDependentGenericLayoutRequirement(name) - case .dependentGenericParamCount: return nil - case .dependentGenericParamPackMarker: break - case .dependentGenericParamType: target.write(name.text ?? "") - case .dependentGenericParamValueMarker: break - case .dependentGenericSameShapeRequirement: printDependentGenericSameShapeRequirement(name) - case .dependentGenericSameTypeRequirement: printDependentGenericSameTypeRequirement(name) - case .dependentGenericType: printDependentGenericType(name) - case .dependentMemberType: printDependentMemberType(name) - case .dependentProtocolConformanceAssociated: printDependentProtocolConformanceAssociated(name) - case .dependentProtocolConformanceInherited: printDependentProtocolConformanceInherited(name) - case .dependentProtocolConformanceRoot: printDependentProtocolConformanceRoot(name) - case .dependentPseudogenericSignature, - .dependentGenericSignature: printGenericSignature(name) - case .destructor: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .noType, hasName: false, extraName: "deinit") - case .didSet: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "didset") - case .differentiabilityWitness: printDifferentiabilityWitness(name) - case .differentiableFunctionType: printDifferentiableFunctionType(name) - case .directMethodReferenceAttribute: target.write("super ") - case .directness: name.index.flatMap { Directness(rawValue: $0)?.description }.map { target.write("\($0) ") } - case .dispatchThunk: printFirstChild(name, prefix: "dispatch thunk of ") - case .distributedAccessor: target.write(conditional: !options.contains(.shortenThunk), "distributed accessor for ") - case .distributedThunk: target.write(conditional: !options.contains(.shortenThunk), "distributed thunk ") - case .droppedArgument: target.write("param\(name.index ?? 0)-removed") - case .dynamicallyReplaceableFunctionImpl: target.write(conditional: !options.contains(.shortenThunk), "dynamically replaceable thunk for ") - case .dynamicallyReplaceableFunctionKey: target.write(conditional: !options.contains(.shortenThunk), "dynamically replaceable key for ") - case .dynamicallyReplaceableFunctionVar: target.write(conditional: !options.contains(.shortenThunk), "dynamically replaceable variable for ") - case .dynamicAttribute: target.write("dynamic ") - case .dynamicSelf: target.write("Self") - case .emptyList: target.write(" empty-list ") - case .enumCase: printFirstChild(name, prefix: "enum case for ", asPrefixContext: false) - case .errorType: target.write("") - case .existentialMetatype: printExistentialMetatype(name) - case .explicitClosure: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: options.contains(.showFunctionArgumentTypes) ? .functionStyle : .noType, hasName: false, extraName: "closure #", extraIndex: (name.children.at(1)?.index ?? 0) + 1) - case .extendedExistentialTypeShape: printExtendedExistentialTypeShape(name) - case .extension: printExtension(name) - case .extensionAttachedMacroExpansion: return printMacro(name: name, asPrefixContext: asPrefixContext, label: "extension") - case .extensionDescriptor: printFirstChild(name, prefix: "extension descriptor ") - case .fieldOffset: printFieldOffset(name) - case .firstElementMarker: target.write(" first-element-marker ") - case .freestandingMacroExpansion: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .noType, hasName: true, extraName: "freestanding macro expansion #", extraIndex: (name.children.at(2)?.index ?? 0) + 1) - case .fullObjCResilientClassStub: printFirstChild(name, prefix: "full ObjC resilient class stub for ") - case .fullTypeMetadata: printFirstChild(name, prefix: "full type metadata for ") - case .function, - .boundGenericFunction: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .functionStyle, hasName: true) - case .functionSignatureSpecialization: printSpecializationPrefix(name, description: "function signature specialization") - case .functionSignatureSpecializationParam: printFunctionSignatureSpecializationParam(name) - case .functionSignatureSpecializationParamKind: printFunctionSignatureSpecializationParamKind(name) - case .functionSignatureSpecializationParamPayload: target.write((try? demangleAsNode(name.text ?? "").description) ?? (name.text ?? "")) - case .functionSignatureSpecializationReturn: printFunctionSignatureSpecializationParam(name) - case .genericPartialSpecialization: printSpecializationPrefix(name, description: "generic partial specialization", paramPrefix: "Signature = ") - case .genericPartialSpecializationNotReAbstracted: printSpecializationPrefix(name, description: "generic not-reabstracted partial specialization", paramPrefix: "Signature = ") - case .genericProtocolWitnessTable: printFirstChild(name, prefix: "generic protocol witness table for ") - case .genericProtocolWitnessTableInstantiationFunction: printFirstChild(name, prefix: "instantiation function for generic protocol witness table for ") - case .genericSpecialization, - .genericSpecializationInResilienceDomain: printSpecializationPrefix(name, description: "generic specialization") - case .genericSpecializationNotReAbstracted: printSpecializationPrefix(name, description: "generic not re-abstracted specialization") - case .genericSpecializationParam: printGenericSpecializationParam(name) - case .genericSpecializationPrespecialized: printSpecializationPrefix(name, description: "generic pre-specialization") - case .genericTypeMetadataPattern: printFirstChild(name, prefix: "generic type metadata pattern for ") - case .genericTypeParamDecl: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .noType, hasName: true) - case .getter: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "getter") - case .global: printChildren(name) - case .globalActorFunctionType: printGlobalActorFunctionType(name) - case .globalGetter: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "getter") - case .globalVariableOnceDeclList: printGlobalVariableOnceDeclList(name) - case .globalVariableOnceFunction, - .globalVariableOnceToken: printGlobalVariableOnceFunction(name) - case .hasSymbolQuery: target.write("#_hasSymbol query for ") - case .identifier: printIdentifier(name, asPrefixContext: asPrefixContext) - case .implConvention: target.write(name.text ?? "") - case .implCoroutineKind: printImplCoroutineKind(name) - case .implDifferentiabilityKind: printImplDifferentiabilityKind(name) - case .implErasedIsolation: target.write("@isolated(any)") - case .implErrorResult: printChildren(name, prefix: "@error ", separator: " ") - case .implParameter, - .implResult: printImplParameter(name) - case .implEscaping: target.write("@escaping") - case .implFunctionAttribute: target.write(name.text ?? "") - case .implFunctionConvention: printImplFunctionConvention(name) - case .implFunctionConventionName: break - case .implFunctionType: printImplFunctionType(name) - case .implicitClosure: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: options.contains(.showFunctionArgumentTypes) ? .functionStyle : .noType, hasName: false, extraName: "implicit closure #", extraIndex: (name.children.at(1)?.index ?? 0) + 1) - case .implInvocationSubstitutions: printImplInvocationSubstitutions(name) - case .implParameterResultDifferentiability: printImplParameterName(name) - case .implParameterSending: printImplParameterName(name) - case .implPatternSubstitutions: printImplPatternSubstitutions(name) - case .implSendingResult: target.write("sending") - case .implYield: printChildren(name, prefix: "@yields ", separator: " ") - case .index: target.write("\(name.index ?? 0)") - case .indexSubset: printIndexSubset(name) - case .infixOperator: target.write("\(name.text ?? "") infix") - case .initAccessor: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "init") - case .initializer: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .noType, hasName: false, extraName: "variable initialization expression") - case .inlinedGenericFunction: printSpecializationPrefix(name, description: "inlined generic function") - case .inOut: printFirstChild(name, prefix: "inout ") - case .integer: target.write("\(name.index ?? 0)") - case .isolated: printFirstChild(name, prefix: "isolated ") - case .isolatedAnyFunctionType: target.write("@isolated(any) ") - case .isolatedDeallocator: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .noType, hasName: false, extraName: name.children.first?.kind == .class ? "__isolated_deallocating_deinit" : "deinit") - case .isSerialized: target.write("serialized") - case .iVarDestroyer: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .noType, hasName: false, extraName: "__ivar_destroyer") - case .iVarInitializer: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .noType, hasName: false, extraName: "__ivar_initializer") - case .keyPathEqualsThunkHelper, - .keyPathHashThunkHelper: printKeyPathEqualityThunkHelper(name) - case .keyPathGetterThunkHelper, - .keyPathSetterThunkHelper, - .keyPathAppliedMethodThunkHelper, - .keyPathUnappliedMethodThunkHelper: printKeyPathAccessorThunkHelper(name) - case .labelList: break - case .lazyProtocolWitnessTableAccessor: printLazyProtocolWitnesstableAccessor(name) - case .lazyProtocolWitnessTableCacheVariable: printLazyProtocolWitnesstableCacheVariable(name) - case .localDeclName: _ = printOptional(name.children.at(1), suffix: " #\((name.children.at(0)?.index ?? 0) + 1)") - case .macro: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: name.children.count == 3 ? .withColon : .functionStyle, hasName: true) - case .macroExpansionLoc: printMacroExpansionLoc(name) - case .macroExpansionUniqueName: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .noType, hasName: true, extraName: "unique name #", extraIndex: (name.children.at(2)?.index ?? 0) + 1) - case .materializeForSet: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "materializeForSet") - case .memberAttachedMacroExpansion: return printMacro(name: name, asPrefixContext: asPrefixContext, label: "member") - case .memberAttributeAttachedMacroExpansion: return printMacro(name: name, asPrefixContext: asPrefixContext, label: "memberAttribute") - case .mergedFunction: target.write(!options.contains(.shortenThunk) ? "merged " : "") - case .metaclass: printFirstChild(name, prefix: "metaclass for ") - case .metadataInstantiationCache: printFirstChild(name, prefix: "metadata instantiation cache for ") - case .metatype: printMetatype(name) - case .metatypeRepresentation: target.write(name.text ?? "") - case .methodDescriptor: printFirstChild(name, prefix: "method descriptor for ") - case .methodLookupFunction: printFirstChild(name, prefix: "method lookup function for ") - case .modify2Accessor: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "modify2") - case .modifyAccessor: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "modify") - case .module: printModule(name) - case .moduleDescriptor: printFirstChild(name, prefix: "module descriptor ") - case .nativeOwningAddressor: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "nativeOwningAddressor") - case .nativeOwningMutableAddressor: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "nativeOwningMutableAddressor") - case .nativePinningAddressor: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "nativePinningAddressor") - case .nativePinningMutableAddressor: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "nativePinningMutableAddressor") - case .negativeInteger: target.write("-\(name.index ?? 0)") - case .noDerivative: printFirstChild(name, prefix: "@noDerivative ") - case .nominalTypeDescriptor: printFirstChild(name, prefix: "nominal type descriptor for ") - case .nominalTypeDescriptorRecord: printFirstChild(name, prefix: "nominal type descriptor runtime record for ") - case .noncanonicalSpecializedGenericTypeMetadata: printFirstChild(name, prefix: "noncanonical specialized generic type metadata for ") - case .noncanonicalSpecializedGenericTypeMetadataCache: printFirstChild(name, prefix: "cache variable for noncanonical specialized generic type metadata for ") - case .nonObjCAttribute: target.write("@nonobjc ") - case .nonUniqueExtendedExistentialTypeShapeSymbolicReference: target.writeHex(prefix: "non-unique existential shape symbolic reference 0x", name.index ?? 0) - case .number: target.write("\(name.index ?? 0)") - case .objCAsyncCompletionHandlerImpl, - .predefinedObjCAsyncCompletionHandlerImpl: printObjCAsyncCompletionHandlerImpl(name) - case .objCAttribute: target.write("@objc ") - case .objCMetadataUpdateFunction: printFirstChild(name, prefix: "ObjC metadata update function for ") - case .objCResilientClassStub: printFirstChild(name, prefix: "ObjC resilient class stub for ") - case .objectiveCProtocolSymbolicReference: target.writeHex(prefix: "objective-c protocol symbolic reference 0x", name.index ?? 0) - case .opaqueReturnType: target.write("some") - case .opaqueReturnTypeIndex: break - case .opaqueReturnTypeOf: printChildren(name, prefix: "<>") - case .opaqueReturnTypeParent: break - case .opaqueType: printOpaqueType(name) - case .opaqueTypeDescriptor: printFirstChild(name, prefix: "opaque type descriptor for ") - case .opaqueTypeDescriptorAccessor: printFirstChild(name, prefix: "opaque type descriptor accessor for ") - case .opaqueTypeDescriptorAccessorImpl: printFirstChild(name, prefix: "opaque type descriptor accessor impl for ") - case .opaqueTypeDescriptorAccessorKey: printFirstChild(name, prefix: "opaque type descriptor accessor key for ") - case .opaqueTypeDescriptorAccessorVar: printFirstChild(name, prefix: "opaque type descriptor accessor var for ") - case .opaqueTypeDescriptorRecord: printFirstChild(name, prefix: "opaque type descriptor runtime record for ") - case .opaqueTypeDescriptorSymbolicReference: target.writeHex(prefix: "opaque type symbolic reference 0x", name.index ?? 0) - case .otherNominalType: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .noType, hasName: true) - case .outlinedAssignWithCopy, - .outlinedAssignWithCopyNoValueWitness: printFirstChild(name, prefix: "outlined assign with copy of ") - case .outlinedAssignWithTake, - .outlinedAssignWithTakeNoValueWitness: printFirstChild(name, prefix: "outlined assign with take of ") - case .outlinedBridgedMethod: target.write("outlined bridged method (\(name.text ?? "")) of ") - case .outlinedConsume: - printFirstChild(name, prefix: "outlined consume of ") + private mutating func printValueWitness(_ name: Node) { + target.write(ValueWitnessKind(rawValue: name.index ?? 0)?.description ?? "") + target.write(options.contains(.shortenValueWitness) ? " for " : " value witness for ") + printFirstChild(name) + } + + private mutating func printConcreteProtocolConformance(_ name: Node) { + target.write("concrete protocol conformance ") + if let index = name.index { + target.write(" #\(index)") + } + printFirstChild(name) + target.write(" to ") + _ = printOptional(name.children.at(1)) + if let thirdChild = name.children.at(2), !thirdChild.children.isEmpty { + target.write(" with conditional requirements: ") + _ = printName(thirdChild) + } + } + + private mutating func printMetatype(_ name: Node) { + if name.children.count == 2 { + printFirstChild(name, suffix: " ") + } + guard let type = name.children.at(name.children.count == 2 ? 1 : 0)?.children.first else { return } + let needParens = !type.isSimpleType + target.write(needParens ? "(" : "") + _ = printName(type) + target.write(needParens ? ")" : "") + target.write(type.kind.isExistentialType ? ".Protocol" : ".Type") + } + + private mutating func printExistentialMetatype(_ name: Node) { + if name.children.count == 2 { + printFirstChild(name, suffix: " ") + } + _ = printOptional(name.children.at(name.children.count == 2 ? 1 : 0), suffix: ".Type") + } + + private mutating func printAssociatedTypeRef(_ name: Node) { + printFirstChild(name) + target.write(".\(name.children.at(1)?.text ?? "")") + } + + private mutating func printProtocolList(_ name: Node) { + guard let typeList = name.children.first else { return } + if typeList.children.isEmpty { + target.write("Any") + } else { + printChildren(typeList, separator: " & ") + } + } + + private mutating func printProtocolListWithClass(_ name: Node) { + guard name.children.count >= 2 else { return } + _ = printOptional(name.children.at(1), suffix: " & ") + if let protocolsTypeList = name.children.first?.children.first { + printChildren(protocolsTypeList, separator: " & ") + } + } + + private mutating func printProtocolListWithAnyObject(_ name: Node) { + guard let prot = name.children.first, let protocolsTypeList = prot.children.first else { return } + if protocolsTypeList.children.count > 0 { + printChildren(protocolsTypeList, suffix: " & ", separator: " & ") + } + if options.contains(.qualifyEntities) { + target.write("Swift.") + } + target.write("AnyObject") + } + + private mutating func printProtocolConformance(_ name: Node) { + if name.children.count == 4 { + _ = printOptional(name.children.at(2), prefix: "property behavior storage of ") + _ = printOptional(name.children.at(0), prefix: " in ") + _ = printOptional(name.children.at(1), prefix: " : ") + } else { + printFirstChild(name) + if options.contains(.displayProtocolConformances) { + _ = printOptional(name.children.at(1), prefix: " : ") + _ = printOptional(name.children.at(2), prefix: " in ") + } + } + } + + private mutating func printImplParameter(_ name: Node) { + printFirstChild(name, suffix: " ") + if name.children.count == 3 { _ = printOptional(name.children.at(1)) - case .outlinedCopy: - printFirstChild(name, prefix: "outlined copy of ") + } else if name.children.count == 4 { _ = printOptional(name.children.at(1)) - case .outlinedDestroy, - .outlinedDestroyNoValueWitness: printFirstChild(name, prefix: "outlined destroy of ") - case .outlinedEnumGetTag: printFirstChild(name, prefix: "outlined enum get tag of ") - case .outlinedEnumProjectDataForLoad: printFirstChild(name, prefix: "outlined enum project data for load of ") - case .outlinedEnumTagStore: printFirstChild(name, prefix: "outlined enum tag store of ") - case .outlinedInitializeWithCopy, - .outlinedInitializeWithCopyNoValueWitness: printFirstChild(name, prefix: "outlined init with copy of ") - case .outlinedInitializeWithTake: printFirstChild(name, prefix: "outlined init with take of ") - case .outlinedReadOnlyObject: target.write("outlined read-only object #\(name.index ?? 0) of ") - case .outlinedRelease: printFirstChild(name, prefix: "outlined release of ") - case .outlinedRetain: printFirstChild(name, prefix: "outlined retain of ") - case .outlinedVariable: target.write("outlined variable #\(name.index ?? 0) of ") - case .owned: printFirstChild(name, prefix: "__owned ") - case .owningAddressor: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "owningAddressor") - case .owningMutableAddressor: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "owningMutableAddressor") - case .pack: printChildren(name, prefix: "Pack{", suffix: "}", separator: ", ") - case .packElement: printFirstChild(name, prefix: "/* level: \(name.children.at(1)?.index ?? 0) */ each ") - case .packElementLevel: break - case .packExpansion: printFirstChild(name, prefix: "repeat ") - case .packProtocolConformance: printChildren(name, prefix: "pack protocol conformance ") - case .partialApplyForwarder: printPartialApplyForwarder(name) - case .partialApplyObjCForwarder: printPartialApplyObjCForwarder(name) - case .peerAttachedMacroExpansion: return printMacro(name: name, asPrefixContext: asPrefixContext, label: "peer") - case .postfixOperator: target.write("\(name.text ?? "") postfix") - case .prefixOperator: target.write("\(name.text ?? "") prefix") - case .privateDeclName: printPrivateDeclName(name) - case .propertyDescriptor: printFirstChild(name, prefix: "property descriptor for ") - case .propertyWrapperBackingInitializer: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .noType, hasName: false, extraName: "property wrapper backing initializer") - case .propertyWrapperInitFromProjectedValue: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .noType, hasName: false, extraName: "property wrapper init from projected value") - case .protocolConformance: printProtocolConformance(name) - case .protocolConformanceDescriptor: printFirstChild(name, prefix: "protocol conformance descriptor for ") - case .protocolConformanceDescriptorRecord: printFirstChild(name, prefix: "protocol conformance descriptor runtime record for ") - case .protocolConformanceRefInOtherModule: printChildren(name, prefix: "protocol conformance ref (retroactive) ") - case .protocolConformanceRefInProtocolModule: printChildren(name, prefix: "protocol conformance ref (protocol's module) ") - case .protocolConformanceRefInTypeModule: printChildren(name, prefix: "protocol conformance ref (type's module) ") - case .protocolDescriptor: printFirstChild(name, prefix: "protocol descriptor for ") - case .protocolDescriptorRecord: printFirstChild(name, prefix: "protocol descriptor runtime record for ") - case .protocolList: printProtocolList(name) - case .protocolListWithAnyObject: printProtocolListWithAnyObject(name) - case .protocolListWithClass: printProtocolListWithClass(name) - case .protocolRequirementsBaseDescriptor: printFirstChild(name, prefix: "protocol requirements base descriptor for ") - case .protocolSelfConformanceDescriptor: printFirstChild(name, prefix: "protocol self-conformance descriptor for ") - case .protocolSelfConformanceWitness: printFirstChild(name, prefix: "protocol self-conformance witness for ") - case .protocolSelfConformanceWitnessTable: printFirstChild(name, prefix: "protocol self-conformance witness table for ") - case .protocolSymbolicReference: target.write("protocol symbolic reference \("0x" + String(name.index ?? 0, radix: 16, uppercase: true))") - case .protocolWitness: printProtocolWitness(name) - case .protocolWitnessTable: printFirstChild(name, prefix: "protocol witness table for ") - case .protocolWitnessTableAccessor: printFirstChild(name, prefix: "protocol witness table accessor for ") - case .protocolWitnessTablePattern: printFirstChild(name, prefix: "protocol witness table pattern for ") - case .reabstractionThunk, - .reabstractionThunkHelper: printReabstractionThunk(name) - case .reabstractionThunkHelperWithGlobalActor: printReabstracctionThunkHelperWithGlobalActor(name) - case .reabstractionThunkHelperWithSelf: printReabstractionThunkHelperWithSelf(name) - case .read2Accessor: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "read2") - case .readAccessor: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "read") - case .reflectionMetadataAssocTypeDescriptor: printFirstChild(name, prefix: "reflection metadata associated type descriptor ") - case .reflectionMetadataBuiltinDescriptor: printFirstChild(name, prefix: "reflection metadata builtin descriptor ") - case .reflectionMetadataFieldDescriptor: printFirstChild(name, prefix: "reflection metadata field descriptor ") - case .reflectionMetadataSuperclassDescriptor: printFirstChild(name, prefix: "reflection metadata superclass descriptor ") - case .relatedEntityDeclName: printFirstChild(name, prefix: "related decl '\(name.text ?? "")' for ") - case .resilientProtocolWitnessTable: printFirstChild(name, prefix: "resilient protocol witness table for ") - case .retroactiveConformance: printRetroactiveConformance(name) - case .returnType: printReturnType(name) - case .sending: printFirstChild(name, prefix: "sending ") - case .sendingResultFunctionType: target.write("sending ") - case .setter: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "setter") - case .shared: printFirstChild(name, prefix: "__shared ") - case .silBoxImmutableField, - .silBoxMutableField: printFirstChild(name, prefix: name.kind == .silBoxImmutableField ? "let " : "var ") - case .silBoxLayout: printSequence(name.children, prefix: "{\(name.children.isEmpty ? "" : " ")", suffix: " }", separator: ", ") - case .silBoxType: printFirstChild(name, prefix: "@box ") - case .silBoxTypeWithLayout: printSilBoxTypeWithLayout(name) - case .silPackDirect: printChildren(name, prefix: "@direct Pack{", suffix: "}", separator: ", ") - case .silPackIndirect: printChildren(name, prefix: "@indirect Pack{", suffix: "}", separator: ", ") - case .silThunkHopToMainActorIfNeeded: printFirstChild(name, prefix: "hop to main actor thunk of ") - case .silThunkIdentity: printFirstChild(name, prefix: "identity thunk of ") - case .specializationPassID: target.write("\(name.index ?? 0)") - case .static: printFirstChild(name, prefix: "static ") - case .subscript: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .functionStyle, hasName: true, overwriteName: "subscript") - case .suffix: printSuffix(name) - case .sugaredArray: printFirstChild(name, prefix: "[", suffix: "]") - case .sugaredDictionary: printSugaredDictionary(name) - case .sugaredOptional: printSugaredOptional(name) - case .sugaredParen: printFirstChild(name, prefix: "(", suffix: ")") - case .symbolicExtendedExistentialType: printSymbolicExtendedExistentialType(name) - case .throwsAnnotation: target.write(" throws") - case .tuple: printChildren(name, prefix: "(", suffix: ")", separator: ", ") - case .tupleElement: printTupleElement(name) - case .tupleElementName: target.write("\(name.text ?? ""): ") - case .type: printFirstChild(name) - case .typedThrowsAnnotation: printTypeThrowsAnnotation(name) - case .typeList: printChildren(name) - case .typeMangling: printFirstChild(name) - case .typeMetadata: printFirstChild(name, prefix: "type metadata for ") - case .typeMetadataAccessFunction: printFirstChild(name, prefix: "type metadata accessor for ") - case .typeMetadataCompletionFunction: printFirstChild(name, prefix: "type metadata completion function for ") - case .typeMetadataDemanglingCache: printFirstChild(name, prefix: "demangling cache variable for type metadata for ") - case .typeMetadataInstantiationCache: printFirstChild(name, prefix: "type metadata instantiation cache for ") - case .typeMetadataInstantiationFunction: printFirstChild(name, prefix: "type metadata instantiation function for ") - case .typeMetadataLazyCache: printFirstChild(name, prefix: "lazy cache variable for type metadata for ") - case .typeMetadataSingletonInitializationCache: printFirstChild(name, prefix: "type metadata singleton initialization cache for ") - case .typeSymbolicReference: target.write("type symbolic reference \("0x" + String(name.index ?? 0, radix: 16, uppercase: true))") - case .uniquable: printFirstChild(name, prefix: "uniquable ") - case .uniqueExtendedExistentialTypeShapeSymbolicReference: target.writeHex(prefix: "non-unique existential shape symbolic reference 0x", name.index ?? 0) - case .unknownIndex: target.write("unknown index") - case .unmanaged: printFirstChild(name, prefix: "unowned(unsafe) ") - case .unowned: printFirstChild(name, prefix: "unowned ") - case .unsafeAddressor: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "unsafeAddressor") - case .unsafeMutableAddressor: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "unsafeMutableAddressor") - case .valueWitness: printValueWitness(name) - case .valueWitnessTable: printFirstChild(name, prefix: "value witness table for ") - case .variable: return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .withColon, hasName: true) - case .variadicMarker: target.write(" variadic-marker ") - case .vTableAttribute: target.write("override ") - case .vTableThunk: printVTableThunk(name) - case .weak: printFirstChild(name, prefix: options.contains(.removeWeakPrefix) ? "" : "weak ") - case .willSet: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "willset") - case .nonIsolatedCallerFunctionType: target.write("nonisolated(nonsending) ") + _ = printOptional(name.children.at(2)) + } + _ = printOptional(name.children.last) + } + + private mutating func printDependentProtocolConformanceAssociated(_ name: Node) { + target.write("dependent associated protocol conformance ") + if let index = name.children.at(2)?.index { + target.write("#\(index) ") + } + printFirstChild(name) + target.write(" to ") + _ = printOptional(name.children.at(1)) + } + + private mutating func printDependentProtocolConformanceInherited(_ name: Node) { + target.write("dependent inherited protocol conformance ") + if let index = name.children.at(2)?.index { + target.write("#\(index) ") + } + printFirstChild(name) + target.write(" to ") + _ = printOptional(name.children.at(1)) + } + + private mutating func printDependentProtocolConformanceRoot(_ name: Node) { + target.write("dependent root protocol conformance ") + if let index = name.children.at(2)?.index { + target.write("#\(index) ") + } + printFirstChild(name) + target.write(" to ") + _ = printOptional(name.children.at(1)) + } + + private static func genericParameterName(depth: UInt64, index: UInt64) -> String { + var name = "" + var index = index + repeat { + if let scalar = UnicodeScalar(UnicodeScalar("A").value + UInt32(index % 26)) { + name.unicodeScalars.append(scalar) + } + index /= 26 + } while index != 0 + if depth != 0 { + name.append("\(depth)") + } + return name + } + + private mutating func printGenericSignature(_ name: Node) { + target.write("<") + var numGenericParams = 0 + for c in name.children { + guard c.kind == .dependentGenericParamCount else { break } + numGenericParams += 1 + } + var firstRequirement = numGenericParams + for var c in name.children.dropFirst(numGenericParams) { + if c.kind == .type { + c = c.children.first ?? c + } + guard c.kind == .dependentGenericParamPackMarker || c.kind == .dependentGenericParamValueMarker else { + break + } + firstRequirement += 1 + } + + let isGenericParamPack = { (depth: UInt64, index: UInt64) -> Bool in + for var child in name.children.dropFirst(numGenericParams).prefix(firstRequirement) { + guard child.kind == .dependentGenericParamPackMarker else { continue } + + child = child.children.first ?? child + guard child.kind == .type else { continue } + + child = child.children.first ?? child + guard child.kind == .dependentGenericParamType else { continue } + + if index == child.children.at(0)?.index, depth == child.children.at(1)?.index { + return true + } + } + + return false + } + + let isGenericParamValue = { (depth: UInt64, index: UInt64) -> Node? in + for var child in name.children.dropFirst(numGenericParams).prefix(firstRequirement) { + guard child.kind == .dependentGenericParamValueMarker else { continue } + child = child.children.first ?? child + + guard child.kind == .type else { continue } + + guard + let param = child.children.at(0), + let type = child.children.at(1), + param.kind == .dependentGenericParamType + else { + continue + } + + if index == param.children.at(0)?.index, depth == param.children.at(1)?.index { + return type + } + } + + return nil + } + + for gpDepth in 0 ..< numGenericParams { + if gpDepth != 0 { + target.write("><") + } + + guard let count = name.children.at(gpDepth)?.index else { continue } + for index in 0 ..< count { + if index != 0 { + target.write(", ") + } + + // Limit the number of printed generic parameters. In practice this + // it will never be exceeded. The limit is only important for malformed + // symbols where count can be really huge. + if index >= 128 { + target.write("...") + break + } + + if isGenericParamPack(UInt64(gpDepth), UInt64(index)) { + target.write("each ") + } + + let value = isGenericParamValue(UInt64(gpDepth), UInt64(index)) + if value != nil { + target.write("let ") + } + + target.write(Self.genericParameterName(depth: UInt64(gpDepth), index: UInt64(index))) + + if let value { + target.write(": ") + _ = printName(value) + } + } + } + + if firstRequirement != name.children.count { + if options.contains(.displayWhereClauses) { + target.write(" where ") + printSequence(name.children.dropFirst(firstRequirement), separator: ", ") + } + } + target.write(">") + } + + private mutating func printDependentGenericConformanceRequirement(_ name: Node) { + printFirstChild(name) + _ = printOptional(name.children.at(1), prefix: ": ") + } + + private mutating func printDependentGenericLayoutRequirement(_ name: Node) { + guard let layout = name.children.at(1), let c = layout.text?.unicodeScalars.first else { return } + printFirstChild(name, suffix: ": ") + switch c { + case "U": target.write("_UnknownLayout") + case "R": target.write("_RefCountedObject") + case "N": target.write("_NativeRefCountedObject") + case "C": target.write("AnyObject") + case "D": target.write("_NativeClass") + case "T": target.write("_Trivial") + case "E", + "e": target.write("_Trivial") + case "M", + "m": target.write("_TrivialAtMost") + default: break + } + if name.children.count > 2 { + _ = printOptional(name.children.at(2), prefix: "(") + _ = printOptional(name.children.at(3), prefix: ", ") + target.write(")") + } + } + + private mutating func printDependentGenericSameTypeRequirement(_ name: Node) { + printFirstChild(name) + _ = printOptional(name.children.at(1), prefix: " == ") + } + + private mutating func printDependentGenericType(_ name: Node) { + guard let depType = name.children.at(1) else { return } + printFirstChild(name) + _ = printOptional(depType, prefix: depType.needSpaceBeforeType ? " " : "") + } + + private mutating func printDependentMemberType(_ name: Node) { + printFirstChild(name) + target.write(".") + _ = printOptional(name.children.at(1)) + } + + private mutating func printDependentAssociatedTypeRef(_ name: Node) { + _ = printOptional(name.children.at(1), suffix: ".") + printFirstChild(name) + } + + private mutating func printSilBoxTypeWithLayout(_ name: Node) { + guard let layout = name.children.first else { return } + _ = printOptional(name.children.at(1), suffix: " ") + _ = printName(layout) + if let genericArgs = name.children.at(2) { + printSequence(genericArgs.children, prefix: " <", suffix: ">", separator: ", ") + } + } + + private mutating func printSugaredOptional(_ name: Node) { + if let type = name.children.first { + let needParens = !type.isSimpleType + target.write(needParens ? "(" : "") + _ = printName(type) + target.write(needParens ? ")" : "") + target.write("?") + } + } + + private mutating func printSugaredDictionary(_ name: Node) { + printFirstChild(name, prefix: "[", suffix: " : ") + _ = printOptional(name.children.at(1), suffix: "]") + } + + private mutating func printOpaqueType(_ name: Node) { + printFirstChild(name) + target.write(".") + _ = printOptional(name.children.at(1)) + } + + private mutating func printImplInvocationsSubstitutions(_ name: Node) { + if let secondChild = name.children.at(0) { + target.write(" for <") + printChildren(secondChild, separator: ", ") + target.write(">") + } + } + + private mutating func printImplPatternSubstitutions(_ name: Node) { + target.write("@substituted ") + printFirstChild(name) + if let secondChild = name.children.at(1) { + target.write(" for <") + printChildren(secondChild, separator: ", ") + target.write(">") + } + } + + private mutating func printImplDifferentiability(_ name: Node) { + if let text = name.text, !text.isEmpty { + target.write("\(text) ") + } + } + + private mutating func printMacroExpansionLoc(_ name: Node) { + if let module = name.children.at(0) { + target.write("module ") + _ = printName(module) + } + if let file = name.children.at(1) { + target.write(" file ") + _ = printName(file) + } + if let line = name.children.at(2) { + target.write(" line ") + _ = printName(line) + } + if let column = name.children.at(3) { + target.write(" column ") + _ = printName(column) + } + } + + private mutating func printGlobalActorFunctionType(_ name: Node) { + if let firstChild = name.children.first { + target.write("@") + _ = printName(firstChild) + target.write(" ") + } + } + + private mutating func printGlobalVariableOnceFunction(_ name: Node) { + target.write(name.kind == .globalVariableOnceToken ? "one-time initialization token for " : "one-time initialization function for ") + if let firstChild = name.children.first { + _ = shouldPrintContext(firstChild) + } + if let secondChild = name.children.at(1) { + _ = printName(secondChild) + } + } + + private mutating func printGlobalVariableOnceDeclList(_ name: Node) { + if name.children.count == 1 { + printFirstChild(name) + } else { + printSequence(name.children, prefix: "(", suffix: ")", separator: ", ") + } + } + + private mutating func printTypeThrowsAnnotation(_ name: Node) { + target.write(" throws(") + if let child = name.children.first { + _ = printName(child) + } + target.write(")") + } + + private mutating func printDifferentiableFunctionType(_ name: Node) { + target.write("@differentiable") + switch UnicodeScalar(UInt8(name.index ?? 0)) { + case "f": target.write("(_forward)") + case "r": target.write("(reverse)") + case "l": target.write("(_linear)") + default: break + } + } + + private mutating func printDifferentiabilityWitness(_ name: Node) { + let kindNodeIndex = name.children.count - (name.children.last?.kind == .dependentGenericSignature ? 4 : 3) + let kind = (name.children.at(kindNodeIndex)?.index).flatMap { Differentiability($0) } + switch kind { + case .forward: target.write("forward-mode") + case .reverse: target.write("reverse-mode") + case .normal: target.write("normal") + case .linear: target.write("linear") + default: return + } + target.write(" differentiability witness for ") + var idx = 0 + while idx < name.children.count, name.children.at(idx)?.kind != .index { + _ = printOptional(name.children.at(idx)) + idx += 1 + } + _ = printOptional(name.children.at(idx + 1), prefix: " with respect to parameters ") + _ = printOptional(name.children.at(idx + 2), prefix: " and results ") + _ = printOptional(name.children.at(idx + 3), prefix: " with ") + } + + private mutating func printAsyncAwaitResumePartialFunction(_ name: Node) { + if options.contains(.showAsyncResumePartial) { + target.write("(") + _ = printName(name.children.first!) + target.write(")") + target.write(" await resume partial function for ") } + } - return nil + private mutating func printAsyncSuspendResumePartialFunction(_ name: Node) { + if options.contains(.showAsyncResumePartial) { + target.write("(") + _ = printName(name.children.first!) + target.write(")") + target.write(" suspend resume partial function for ") + } + } + + private mutating func printExtendedExistentialTypeShape(_ name: Node) { + let savedDisplayWhereClauses = options.contains(.displayWhereClauses) + options.insert(.displayWhereClauses) + var genSig: Node? + var type: Node? + if name.children.count == 2 { + genSig = name.children.at(1) + type = name.children.at(2) + } else { + type = name.children.at(1) + } + target.write("existential shape for ") + if let genSig { + _ = printName(genSig) + target.write(" ") + } + target.write("any ") + if let type { + _ = printName(type) + } else { + target.write("") + } + if !savedDisplayWhereClauses { + options.remove(.displayWhereClauses) + } + } + + private mutating func printSymbolicExtendedExistentialType(_ name: Node) { + guard let shape = name.children.first else { return } + let isUnique = shape.kind == .uniqueExtendedExistentialTypeShapeSymbolicReference + target.write("symbolic existential type (\(isUnique ? "" : "non-")unique) 0x") + target.write((shape.index ?? 0).hexadecimalString) + target.write(" <") + guard let second = name.children.at(1) else { return } + _ = printName(second) + if let third = name.children.at(2) { + target.write(", ") + _ = printName(third) + } + target.write(">") + } + + private mutating func printTupleElement(_ name: Node) { + if let label = name.children.first(where: { $0.kind == .tupleElementName }) { + target.write("\(label.text ?? ""): ") + } + guard let type = name.children.first(where: { $0.kind == .type }) else { return } + _ = printName(type) + if let _ = name.children.first(where: { $0.kind == .variadicMarker }) { + target.write("...") + } + } + + private mutating func printObjCAsyncCompletionHandlerImpl(_ name: Node) { + if name.kind == .predefinedObjCAsyncCompletionHandlerImpl { + target.write("predefined ") + } + target.write("@objc completion handler block implementation for ") + if name.children.count >= 4 { + _ = printOptional(name.children.at(3)) + } + printFirstChild(name, suffix: " with result type ") + _ = printOptional(name.children.at(1)) + switch name.children.at(2)?.index { + case 0: break + case 1: target.write(" nonzero on error ") + case 2: target.write(" zero on error ") + default: target.write(" ") + } + } + + private mutating func printImplInvocationSubstitutions(_ name: Node) { + if let secondChild = name.children.at(0) { + target.write(" for <") + printChildren(secondChild, separator: ", ") + target.write(">") + } + } + + private mutating func printImplDifferentiabilityKind(_ name: Node) { + target.write("@differentiable") + if case .index(let value) = name.contents, let differentiability = Differentiability(value) { + switch differentiability { + case .normal: break + case .linear: target.write("(_linear)") + case .forward: target.write("(_forward)") + case .reverse: target.write("(reverse)") + } + } + } + + private mutating func printImplCoroutineKind(_ name: Node) { + guard case .name(let value) = name.contents, !value.isEmpty else { return } + target.write("@\(value)") + } + + private mutating func printImplFunctionConvention(_ name: Node) { + target.write("@convention(") + if let second = name.children.at(1) { + target.write("\(name.children.at(0)?.text ?? ""), mangledCType: \"") + _ = printName(second) + target.write("\"") + } else { + target.write("\(name.children.at(0)?.text ?? "")") + } + target.write(")") + } + + private mutating func printImplParameterName(_ name: Node) { + guard case .name(let value) = name.contents, !value.isEmpty else { return } + target.write("\(value) ") + } + + private mutating func printBaseConformanceDescriptor(_ name: Node) { + printFirstChild(name, prefix: "base conformance descriptor for ") + _ = printOptional(name.children.at(1), prefix: ": ") + } + + private mutating func printReabstractionThunkHelperWithSelf(_ name: Node) { + target.write("reabstraction thunk ") + var idx = 0 + if name.children.count == 4 { + printFirstChild(name, suffix: " ") + idx += 1 + } + _ = printOptional(name.children.at(idx + 2), prefix: "from ") + _ = printOptional(name.children.at(idx + 1), prefix: " to ") + _ = printOptional(name.children.at(idx), prefix: " self ") + } + + private mutating func printReabstracctionThunkHelperWithGlobalActor(_ name: Node) { + printFirstChild(name) + _ = printOptional(name.children.at(1), prefix: " with global actor constraint ") + } + + private mutating func printBuildInFixedArray(_ name: Node) { + _ = printOptional(name.children.first, prefix: "Builtin.FixedArray<") + _ = printOptional(name.children.at(1), prefix: ", ", suffix: ">") + } + + private mutating func printAutoDiffFunctionOrSimpleThunk(_ name: Node) { + var prefixEndIndex = 0 + while prefixEndIndex < name.children.count, name.children[prefixEndIndex].kind != .autoDiffFunctionKind { + prefixEndIndex += 1 + } + + let funcKind = name.children.at(prefixEndIndex) + let paramIndices = name.children.at(prefixEndIndex + 1) + let resultIndices = name.children.at(prefixEndIndex + 2) + if name.kind == .autoDiffDerivativeVTableThunk { + target.write("vtable thunk for ") + } + _ = printOptional(funcKind) + target.write(" of ") + var optionalGenSig: Node? + for i in 0 ..< prefixEndIndex { + if i == prefixEndIndex - 1, name.children.at(i)?.kind == .dependentGenericSignature { + optionalGenSig = name.children.at(i) + break + } + _ = printOptional(name.children.at(i)) + } + if options.contains(.shortenThunk) { + return + } + target.write(" with respect to parameters ") + _ = printOptional(paramIndices) + target.write(" and results ") + _ = printOptional(resultIndices) + _ = printOptional(options.contains(.displayWhereClauses) ? optionalGenSig : nil, prefix: " with ") + } + + private mutating func printAutoDiffFunctionKind(_ name: Node) { + guard let kind = name.index else { return } + switch AutoDiffFunctionKind(kind) { + case .forward: target.write("forward-mode derivative") + case .reverse: target.write("reverse-mode derivative") + case .differential: target.write("differential") + case .pullback: target.write("pullback") + default: break + } + } + + private mutating func printAutoDiffSelfReorderingReabstractionThunk(_ name: Node) { + target.write("autodiff self-reordering reabstraction thunk ") + let fromType = name.children.first + _ = printOptional(options.contains(.shortenThunk) ? fromType : nil, prefix: "for ") + let toType = name.children.at(1) + var kindIndex = 2 + var optionalGenSig: Node? + if name.children.at(kindIndex)?.kind == .dependentGenericSignature { + optionalGenSig = name.children.at(kindIndex) + kindIndex += 1 + } + target.write("for ") + _ = printOptional(name.children.at(kindIndex)) + _ = printOptional(optionalGenSig, suffix: " ") + _ = printOptional(fromType, prefix: " from ") + _ = printOptional(toType, prefix: " to ") + } + + private mutating func printAutoDiffSubsetParametersThunk(_ name: Node) { + target.write("autodiff subset parameters thunk for ") + let lastIndex = name.children.count - 1 + let toParamIndices = name.children.at(lastIndex) + let resultIndices = name.children.at(lastIndex - 1) + let paramIndices = name.children.at(lastIndex - 2) + let kind = name.children.at(lastIndex - 3) + let currentIndex = lastIndex - 4 + _ = printOptional(kind, suffix: " from ") + if currentIndex == 0 { + printFirstChild(name) + } else { + printSequence(name.children.prefix(currentIndex)) + } + if options.contains(.shortenThunk) { + return + } + target.write(" with respect to parameters ") + _ = printOptional(paramIndices) + target.write(" and results ") + _ = printOptional(resultIndices) + target.write(" to parameters ") + _ = printOptional(toParamIndices) + _ = printOptional(currentIndex > 0 ? name.children.at(currentIndex) : nil, prefix: " of type ") + } + + private mutating func printIndexSubset(_ name: Node) { + target.write("{") + var printedAnyIndex = false + for (i, c) in (name.text ?? "").enumerated() { + if c != "S" { + continue + } + if printedAnyIndex { + target.write(", ") + } + target.write("\(i)") + printedAnyIndex = true + } + target.write("}") } - mutating func printIdentifier(_ name: Node, asPrefixContext: Bool = false) { - let semanticType: SemanticType + private mutating func printBaseWitnessTableAccessor(_ name: Node) { + _ = printOptional(name.children.at(1), prefix: "base witness table accessor for ") + _ = printOptional(name.children.at(0), prefix: " in ") + } - switch name.parent?.kind { - case .function: - semanticType = .function(.declaration) - case .variable: - semanticType = .variable - case .enum: - semanticType = .type(.enum, .name) - case .structure: - semanticType = .type(.struct, .name) - case .class: - semanticType = .type(.class, .name) - case .protocol: - semanticType = .type(.protocol, .name) - default: - semanticType = .standard + private mutating func printDependentGenericInverseConformanceRequirement(_ name: Node) { + printFirstChild(name, suffix: ": ~") + switch name.children.at(1)?.index { + case 0: target.write("Swift.Copyable") + case 1: target.write("Swift.Escapable") + default: target.write("Swift.") } + } + + private mutating func printDependentGenericSameShapeRequirement(_ name: Node) { + _ = printOptional(name.children.at(0), suffix: ".shape == ") + _ = printOptional(name.children.at(1), suffix: ".shape") + } + + private mutating func printConstrainedExistential(_ name: Node) { + printFirstChild(name, prefix: "any ") + _ = printOptional(name.children.at(1), prefix: "<", suffix: ">") + } - target.write(name.text ?? "", type: semanticType) + private mutating func printIdentifier(_ name: Node, asPrefixContext: Bool = false) { + target.write(name.text ?? "", context: .context(for: name, state: .printIdentifier)) } - mutating func printAbstractStorage(_ name: Node?, asPrefixContext: Bool, extraName: String) -> Node? { + private mutating func printAbstractStorage(_ name: Node?, asPrefixContext: Bool, extraName: String) -> Node? { guard let n = name else { return nil } switch n.kind { case .variable: return printEntity(n, asPrefixContext: asPrefixContext, typePrinting: .withColon, hasName: true, extraName: extraName) @@ -1445,7 +1493,7 @@ struct NodePrinter: Sendable { } } - mutating func printEntityType(name: Node, type: Node, genericFunctionTypeList: Node?) { + private mutating func printEntityType(name: Node, type: Node, genericFunctionTypeList: Node?) { let labelList = name.children.first(where: { $0.kind == .labelList }) if labelList != nil || genericFunctionTypeList != nil { if let gftl = genericFunctionTypeList { @@ -1471,7 +1519,7 @@ struct NodePrinter: Sendable { } } - mutating func printEntity(_ name: Node, asPrefixContext: Bool, typePrinting: TypePrinting, hasName: Bool, extraName: String? = nil, extraIndex: UInt64? = nil, overwriteName: String? = nil) -> Node? { + private mutating func printEntity(_ name: Node, asPrefixContext: Bool, typePrinting: TypePrinting, hasName: Bool, extraName: String? = nil, extraIndex: UInt64? = nil, overwriteName: String? = nil) -> Node? { var genericFunctionTypeList: Node? var name = name if name.kind == .boundGenericFunction, let first = name.children.at(0), let second = name.children.at(1) { @@ -1582,7 +1630,7 @@ struct NodePrinter: Sendable { return postfixContext } - mutating func printSpecializationPrefix(_ name: Node, description: String, paramPrefix: String = "") { + private mutating func printSpecializationPrefix(_ name: Node, description: String, paramPrefix: String = "") { if !options.contains(.displayGenericSpecializations) { if !specializationPrefixPrinted { target.write("specialized ") @@ -1623,7 +1671,7 @@ struct NodePrinter: Sendable { target.write("> of ") } - mutating func printFunctionParameters(labelList: Node?, parameterType: Node, showTypes: Bool) { + private mutating func printFunctionParameters(labelList: Node?, parameterType: Node, showTypes: Bool) { guard parameterType.kind == .argumentTuple else { return } guard let t = parameterType.children.first, t.kind == .type else { return } guard let parameters = t.children.first else { return } @@ -1642,17 +1690,17 @@ struct NodePrinter: Sendable { target.write("(") for tuple in parameters.children.enumerated() { if let label = labelList?.children.at(tuple.offset) { - target.write(label.kind == .identifier ? (label.text ?? "") : "_", type: .function(.declaration)) + target.write(label.kind == .identifier ? (label.text ?? "") : "_", context: .context(for: parameterType, state: .printFunctionParameters)) target.write(":") if showTypes { target.write(" ") } } else if !showTypes { if let label = tuple.element.children.first(where: { $0.kind == .tupleElementName }) { - target.write(label.text ?? "", type: .function(.declaration)) + target.write(label.text ?? "", context: .context(for: parameterType, state: .printFunctionParameters)) target.write(":") } else { - target.write("_", type: .function(.declaration)) + target.write("_", context: .context(for: parameterType, state: .printFunctionParameters)) target.write(":") } } @@ -1667,7 +1715,7 @@ struct NodePrinter: Sendable { target.write(")") } - mutating func printConventionWithMangledCType(_ name: Node, label: String) { + private mutating func printConventionWithMangledCType(_ name: Node, label: String) { target.write("@convention(\(label)") if let firstChild = name.children.first, firstChild.kind == .clangType { target.write(", mangledCType: \"") @@ -1677,7 +1725,7 @@ struct NodePrinter: Sendable { target.write(") ") } - mutating func printFunctionType(labelList: Node? = nil, _ name: Node) { + private mutating func printFunctionType(labelList: Node? = nil, _ name: Node) { switch name.kind { case .autoClosureType, .escapingAutoClosureType: target.write("@autoclosure ") @@ -1747,7 +1795,7 @@ struct NodePrinter: Sendable { if let nonIsolatedCallerNode { _ = printName(nonIsolatedCallerNode) } - + if isSendable { target.write("@Sendable ") } @@ -1771,13 +1819,13 @@ struct NodePrinter: Sendable { _ = printOptional(name.children.at(argIndex + 1)) } - mutating func printBoundGenericNoSugar(_ name: Node) { + private mutating func printBoundGenericNoSugar(_ name: Node) { guard let typeList = name.children.at(1) else { return } printFirstChild(name) printChildren(typeList, prefix: "<", suffix: ">", separator: ", ") } - func findSugar(_ name: Node) -> SugarType { + private func findSugar(_ name: Node) -> SugarType { guard let firstChild = name.children.at(0) else { return .none } if name.children.count == 1, firstChild.kind == .type { return findSugar(firstChild) } @@ -1809,7 +1857,7 @@ struct NodePrinter: Sendable { return .none } - mutating func printBoundGeneric(_ name: Node) { + private mutating func printBoundGeneric(_ name: Node) { guard name.children.count >= 2 else { return } guard name.children.count == 2, options.contains(.synthesizeSugarOnTypes), name.kind != .boundGenericClass else { printBoundGenericNoSugar(name) @@ -1842,13 +1890,13 @@ struct NodePrinter: Sendable { } } - mutating func printImplFunctionType(_ name: Node) { - enum State: Int { case attrs, inputs, results } - var curState: State = .attrs + private enum PrintImplFunctionTypeState: Int { case attrs, inputs, results } + private mutating func printImplFunctionType(_ name: Node) { + var curState: PrintImplFunctionTypeState = .attrs var patternSubs: Node? var invocationSubs: Node? var sendingResult: Node? - let transitionTo = { (printer: inout NodePrinter, newState: State) in + let transitionTo = { (printer: inout NodePrinter, newState: PrintImplFunctionTypeState) in while curState != newState { switch curState { case .attrs: @@ -1865,7 +1913,7 @@ struct NodePrinter: Sendable { printer.target.write("(") case .results: break } - guard let nextState = State(rawValue: curState.rawValue + 1) else { break } + guard let nextState = PrintImplFunctionTypeState(rawValue: curState.rawValue + 1) else { break } curState = nextState } } @@ -1904,7 +1952,7 @@ struct NodePrinter: Sendable { } } - mutating func quotedString(_ value: String) { + private mutating func quotedString(_ value: String) { target.write("\"") for c in value.unicodeScalars { switch c { diff --git a/Sources/Demangle/Main/NodePrinterTarget.swift b/Sources/Demangle/Main/NodePrinterTarget.swift new file mode 100644 index 00000000..b90fe669 --- /dev/null +++ b/Sources/Demangle/Main/NodePrinterTarget.swift @@ -0,0 +1,14 @@ +package protocol NodePrinterTarget: Sendable { + init() + var count: Int { get } + mutating func write(_ content: String) + mutating func write(_ content: String, context: NodePrintContext) +} + +extension NodePrinterTarget { + package mutating func write(_ content: String, context: NodePrintContext) { + write(content) + } +} + +extension String: NodePrinterTarget {} diff --git a/Sources/Demangle/Node/Node+CustomStringConvertible.swift b/Sources/Demangle/Node/Node+CustomStringConvertible.swift index b9165163..97868ff0 100644 --- a/Sources/Demangle/Node/Node+CustomStringConvertible.swift +++ b/Sources/Demangle/Node/Node+CustomStringConvertible.swift @@ -1,9 +1,9 @@ -import Semantic - extension Node: CustomStringConvertible { /// Overridden method to allow simple printing with default options public var description: String { - print() + var string = "" + printNode(output: &string, node: self) + return string } /// Prints `SwiftSymbol`s to a String with the full set of printing options. @@ -11,12 +11,25 @@ extension Node: CustomStringConvertible { /// - Parameter options: an option set containing the different `DemangleOptions` from the Swift project. /// - Returns: `self` printed to a string according to the specified options. public func print(using options: DemangleOptions = .default) -> String { - printSemantic(using: options).string + var printer = NodePrinter(options: options) + return printer.printRoot(self) } - - public func printSemantic(using options: DemangleOptions = .default) -> SemanticString { - var printer = NodePrinter(options: options) - _ = printer.printName(self) - return printer.target + + + private func printNode(output: inout String, node: Node, depth: Int = 0) { + (0..<(depth * 2)).forEach { _ in output.append(" ") } + output.append("\(node.kind)") + switch node.contents { + case .none: + break + case .index(let index): + output.append(", index=\(index)") + case .name(let name): + output.append(", name=\(name)") + } + output.append("\n") + for child in node.children { + printNode(output: &output, node: child, depth: depth + 1) + } } } diff --git a/Sources/Demangle/Node/Node+Kind.swift b/Sources/Demangle/Node/Node+Kind.swift index bb707dcf..95811010 100644 --- a/Sources/Demangle/Node/Node+Kind.swift +++ b/Sources/Demangle/Node/Node+Kind.swift @@ -1,5 +1,5 @@ extension Node { - public enum Kind: Hashable, Sendable, CaseIterable { + public enum Kind: String, Hashable, Sendable, CaseIterable { case allocator case accessibleFunctionRecord case accessorFunctionReference @@ -539,3 +539,9 @@ extension Node.Kind { } } } + +extension Node.Kind: CustomStringConvertible { + public var description: String { + rawValue.capitalized + } +} diff --git a/Sources/Demangle/Utils/Common.swift b/Sources/Demangle/Utils/Common.swift index 79c9d1a9..c7f5a591 100644 --- a/Sources/Demangle/Utils/Common.swift +++ b/Sources/Demangle/Utils/Common.swift @@ -21,74 +21,7 @@ func archetypeName(_ index: UInt64, _ depth: UInt64) -> String { // MARK: Punycode.h -/// Rough adaptation of the pseudocode from 6.2 "Decoding procedure" in RFC3492 -func decodeSwiftPunycode(_ value: String) throws -> String { - let input = value.unicodeScalars - var output = [UnicodeScalar]() - var pos = input.startIndex - - // Unlike RFC3492, Swift uses underscore for delimiting - if let ipos = input.lastIndex(of: "_" as UnicodeScalar) { - output.append(contentsOf: input[input.startIndex ..< ipos].map { UnicodeScalar($0) }) - pos = input.index(ipos, offsetBy: 1) - } - - // Magic numbers from RFC3492 - var n = 128 - var i = 0 - var bias = 72 - let symbolCount = 36 - let alphaCount = 26 - while pos != input.endIndex { - let oldi = i - var w = 1 - for k in stride(from: symbolCount, to: Int.max, by: symbolCount) { - // Unlike RFC3492, Swift uses letters A-J for values 26-35 - let digit: Int - if input[pos] >= UnicodeScalar("a") { - digit = Int(input[pos].value - UnicodeScalar("a").value) - } else if input[pos] >= UnicodeScalar("A") { - digit = Int((input[pos].value - UnicodeScalar("A").value) + UInt32(alphaCount)) - } else { - throw SwiftSymbolParseError.punycodeParseError - } - - if pos != input.endIndex { - pos = input.index(pos, offsetBy: 1) - } - - i = i &+ (digit &* w) - let t = max(min(k - bias, alphaCount), 1) - if digit < t { - break - } - w = w &* (symbolCount - t) - } - - // Bias adaptation function - var delta = (i - oldi) / ((oldi == 0) ? 700 : 2) - delta = delta + delta / (output.count + 1) - var k = 0 - while delta > 455 { - delta = delta / (symbolCount - 1) - k = k + symbolCount - } - k += (symbolCount * delta) / (delta + symbolCount + 2) - - bias = k - n = n + i / (output.count + 1) - i = i % (output.count + 1) - var scalarValue = n - if scalarValue >= 0xD800, scalarValue < 0xD880 { - scalarValue -= 0xD800 - } - let validScalar = UnicodeScalar(scalarValue) ?? UnicodeScalar(".") - output.insert(validScalar, at: i) - i += 1 - } - return String(output.map { Character($0) }) -} package func getManglingPrefixLength(_ scalars: C) -> Int where C.Iterator.Element == UnicodeScalar { var scanner = ScalarScanner(scalars: scalars) diff --git a/Sources/Demangle/Utils/Extensions.swift b/Sources/Demangle/Utils/Extensions.swift index 74bed560..473cff27 100644 --- a/Sources/Demangle/Utils/Extensions.swift +++ b/Sources/Demangle/Utils/Extensions.swift @@ -1,29 +1,9 @@ -import Semantic - extension String { package var isSwiftSymbol: Bool { getManglingPrefixLength(unicodeScalars) > 0 } } -extension String { - mutating func writeHex(prefix: String? = nil, _ value: UInt64) { - if let prefix = prefix { - write(prefix) - } - write(String(value, radix: 16, uppercase: true)) - } -} - -extension SemanticString { - mutating func writeHex(prefix: String? = nil, _ value: UInt64) { - if let prefix = prefix { - write(prefix) - } - write(String(value, radix: 16, uppercase: true)) - } -} - extension Array { package func at(_ index: Int) -> Element? { return indices.contains(index) ? self[index] : nil @@ -38,14 +18,6 @@ extension Array { } } -extension TextOutputStream { - mutating func write(conditional: Bool, _ value: String) { - if conditional { - write(value) - } - } -} - /// NOTE: This extension is fileprivate to avoid clashing with CwlUtils (from which it is taken). If you want to use these functions outside this file, consider including CwlUtils. extension UnicodeScalar { /// Tests if the scalar is within a range @@ -94,3 +66,9 @@ extension Array { return result } } + +extension BinaryInteger { + var hexadecimalString: String { + String(self, radix: 16, uppercase: true) + } +} diff --git a/Sources/Demangle/Utils/Punycode.swift b/Sources/Demangle/Utils/Punycode.swift new file mode 100644 index 00000000..3f3a4971 --- /dev/null +++ b/Sources/Demangle/Utils/Punycode.swift @@ -0,0 +1,68 @@ +/// Rough adaptation of the pseudocode from 6.2 "Decoding procedure" in RFC3492 +func decodeSwiftPunycode(_ value: String) throws -> String { + let input = value.unicodeScalars + var output = [UnicodeScalar]() + + var pos = input.startIndex + + // Unlike RFC3492, Swift uses underscore for delimiting + if let ipos = input.lastIndex(of: "_" as UnicodeScalar) { + output.append(contentsOf: input[input.startIndex ..< ipos].map { UnicodeScalar($0) }) + pos = input.index(ipos, offsetBy: 1) + } + + // Magic numbers from RFC3492 + var n = 128 + var i = 0 + var bias = 72 + let symbolCount = 36 + let alphaCount = 26 + while pos != input.endIndex { + let oldi = i + var w = 1 + for k in stride(from: symbolCount, to: Int.max, by: symbolCount) { + // Unlike RFC3492, Swift uses letters A-J for values 26-35 + let digit: Int + if input[pos] >= UnicodeScalar("a") { + digit = Int(input[pos].value - UnicodeScalar("a").value) + } else if input[pos] >= UnicodeScalar("A") { + digit = Int((input[pos].value - UnicodeScalar("A").value) + UInt32(alphaCount)) + } else { + throw SwiftSymbolParseError.punycodeParseError + } + + if pos != input.endIndex { + pos = input.index(pos, offsetBy: 1) + } + + i = i &+ (digit &* w) + let t = max(min(k - bias, alphaCount), 1) + if digit < t { + break + } + w = w &* (symbolCount - t) + } + + // Bias adaptation function + var delta = (i - oldi) / ((oldi == 0) ? 700 : 2) + delta = delta + delta / (output.count + 1) + var k = 0 + while delta > 455 { + delta = delta / (symbolCount - 1) + k = k + symbolCount + } + k += (symbolCount * delta) / (delta + symbolCount + 2) + + bias = k + n = n + i / (output.count + 1) + i = i % (output.count + 1) + var scalarValue = n + if scalarValue >= 0xD800, scalarValue < 0xD880 { + scalarValue -= 0xD800 + } + let validScalar = UnicodeScalar(scalarValue) ?? UnicodeScalar(".") + output.insert(validScalar, at: i) + i += 1 + } + return String(output.map { Character($0) }) +} diff --git a/Sources/MachOPointer/RelativePointers.swift b/Sources/MachOPointer/RelativePointers.swift index 6996a9c7..276a5d67 100644 --- a/Sources/MachOPointer/RelativePointers.swift +++ b/Sources/MachOPointer/RelativePointers.swift @@ -1,6 +1,5 @@ import MachOReading import MachOExtensions -import MachOSymbols public typealias RelativeOffset = Int32 diff --git a/Sources/MachOReading/Resolvable.swift b/Sources/MachOReading/Resolvable.swift index 0e35db39..3120d56e 100644 --- a/Sources/MachOReading/Resolvable.swift +++ b/Sources/MachOReading/Resolvable.swift @@ -17,7 +17,8 @@ extension Resolvable { } public static func resolve(from fileOffset: Int, in machOFile: MachOFile) throws -> Self? { - return try? resolve(from: fileOffset, in: machOFile) + let result: Self = try resolve(from: fileOffset, in: machOFile) + return .some(result) } } diff --git a/Sources/SwiftDump/Extensions/Node+SemanticString.swift b/Sources/SwiftDump/Extensions/Node+SemanticString.swift new file mode 100644 index 00000000..27314d13 --- /dev/null +++ b/Sources/SwiftDump/Extensions/Node+SemanticString.swift @@ -0,0 +1,40 @@ +import Foundation +import Demangle +import Semantic + +extension SemanticString: NodePrinterTarget { + package mutating func write(_ content: String, context: NodePrintContext) { + switch context.state { + case .printFunctionParameters: + write(content, type: .function(.declaration)) + case .printIdentifier: + let semanticType: SemanticType + switch context.node.parent?.kind { + case .function: + semanticType = .function(.declaration) + case .variable: + semanticType = .variable + case .enum: + semanticType = .type(.enum, .name) + case .structure: + semanticType = .type(.struct, .name) + case .class: + semanticType = .type(.class, .name) + case .protocol: + semanticType = .type(.protocol, .name) + default: + semanticType = .standard + } + write(content, type: semanticType) + case .printModule: + write(content, type: .other) + } + } +} + +extension Node { + public func printSemantic(using options: DemangleOptions = .default) -> SemanticString { + var printer = NodePrinter(options: options) + return printer.printRoot(self) + } +} From b026a463bbfeae6aa8b4e1b28f7625e752dfd023 Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Sun, 29 Jun 2025 23:49:49 +0800 Subject: [PATCH 09/10] Update Testing --- Sources/Demangle/Main/NodePrinter.swift | 6 +- Sources/MachOExtensions/File+.swift | 9 +++ .../XcodeMachOFileName.swift | 9 ++- .../SymbolDemangleTests.swift | 80 +++++++++++++------ .../XcodeMachOFileDumpTests.swift | 6 -- 5 files changed, 72 insertions(+), 38 deletions(-) diff --git a/Sources/Demangle/Main/NodePrinter.swift b/Sources/Demangle/Main/NodePrinter.swift index 004e92a7..4a4d0f27 100644 --- a/Sources/Demangle/Main/NodePrinter.swift +++ b/Sources/Demangle/Main/NodePrinter.swift @@ -199,7 +199,7 @@ package struct NodePrinter: Sendable { case .functionSignatureSpecialization: printSpecializationPrefix(name, description: "function signature specialization") case .functionSignatureSpecializationParam: printFunctionSignatureSpecializationParam(name) case .functionSignatureSpecializationParamKind: printFunctionSignatureSpecializationParamKind(name) - case .functionSignatureSpecializationParamPayload: target.write((try? demangleAsNode(name.text ?? "").description) ?? (name.text ?? "")) + case .functionSignatureSpecializationParamPayload: target.write((try? demangleAsNode(name.text ?? "").print(using: options)) ?? (name.text ?? "")) case .functionSignatureSpecializationReturn: printFunctionSignatureSpecializationParam(name) case .genericPartialSpecialization: printSpecializationPrefix(name, description: "generic partial specialization", paramPrefix: "Signature = ") case .genericPartialSpecializationNotReAbstracted: printSpecializationPrefix(name, description: "generic not-reabstracted partial specialization", paramPrefix: "Signature = ") @@ -507,7 +507,7 @@ package struct NodePrinter: Sendable { } private mutating func printMacro(name: Node, asPrefixContext: Bool, label: String) -> Node? { - return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .noType, hasName: true, extraName: "\(label) macro @\(name.children.at(2)?.description ?? "") expansion #", extraIndex: (name.children.at(3)?.index ?? 0) + 1) + return printEntity(name, asPrefixContext: asPrefixContext, typePrinting: .noType, hasName: true, extraName: "\(label) macro @\(name.children.at(2)?.print(using: options) ?? "") expansion #", extraIndex: (name.children.at(3)?.index ?? 0) + 1) } private mutating func printAnonymousContext(_ name: Node) { @@ -586,7 +586,7 @@ package struct NodePrinter: Sendable { .constantPropGlobal: _ = printOptional(name.children.at(idx), prefix: "[", suffix: " : ") guard let t = name.children.at(idx + 1)?.text else { return } - let demangedName = (try? demangleAsNode(t))?.description ?? "" + let demangedName = (try? demangleAsNode(t))?.print(using: options) ?? "" if demangedName.isEmpty { target.write(t) } else { diff --git a/Sources/MachOExtensions/File+.swift b/Sources/MachOExtensions/File+.swift index 6284b501..4e73f9f3 100644 --- a/Sources/MachOExtensions/File+.swift +++ b/Sources/MachOExtensions/File+.swift @@ -9,4 +9,13 @@ extension File { } return try MachOKit.loadFromFile(url: url) } + + package var machOFiles: [MachOFile] { + switch self { + case .machO(let machOFile): + return [machOFile] + case .fat(let fatFile): + return (try? fatFile.machOFiles()) ?? [] + } + } } diff --git a/Sources/MachOTestingSupport/XcodeMachOFileName.swift b/Sources/MachOTestingSupport/XcodeMachOFileName.swift index 48073218..527e2e0c 100644 --- a/Sources/MachOTestingSupport/XcodeMachOFileName.swift +++ b/Sources/MachOTestingSupport/XcodeMachOFileName.swift @@ -1,5 +1,5 @@ -package enum XcodeMachOFileName { - package enum SharedFrameworks: String { +package enum XcodeMachOFileName: CaseIterable { + package enum SharedFrameworks: String, CaseIterable { case AccessibilityAudit case AccessibilitySupport case AppResourceGeneration @@ -226,7 +226,6 @@ package enum XcodeMachOFileName { case _CodeCompletionFoundation case kperfdataDT case ktraceDT - case libXCTestSwiftSupport case llbuild package var pathComponent: String { "/SharedFrameworks/\(rawValue).framework" @@ -245,4 +244,8 @@ package enum XcodeMachOFileName { return "\(contentsDirectory)\(framework.pathComponent)" } } + + package static var allCases: [XcodeMachOFileName] { + SharedFrameworks.allCases.map { .sharedFrameworks($0) } + } } diff --git a/Tests/MachOSwiftSectionTests/SymbolDemangleTests.swift b/Tests/MachOSwiftSectionTests/SymbolDemangleTests.swift index 3f124f70..57fc3d8d 100644 --- a/Tests/MachOSwiftSectionTests/SymbolDemangleTests.swift +++ b/Tests/MachOSwiftSectionTests/SymbolDemangleTests.swift @@ -8,7 +8,7 @@ import MachOFoundation @testable import MachOTestingSupport @Suite(.serialized) -final class SymbolDemangleTests: DyldCacheTests { +final class DyldCacheSymbolDemangleTests: DyldCacheTests { struct MachOSwiftSymbol { let imagePath: String let offset: Int @@ -18,23 +18,23 @@ final class SymbolDemangleTests: DyldCacheTests { @MainActor @Test func symbols() throws { let allSwiftSymbols = try allSymbols() - print("Total Swift Symbols: \(allSwiftSymbols.count)") + "Total Swift Symbols: \(allSwiftSymbols.count)".print() for symbol in allSwiftSymbols { let swiftStdlibDemangledName = stdlib_demangleName(symbol.stringValue) do { - guard !symbol.stringValue.hasSuffix("$delayInitStub") else { continue } - var demangler = Demangler(scalars: symbol.stringValue.unicodeScalars) - let node = try demangler.demangleSymbol() + let node = try demangleAsNode(symbol.stringValue) let swiftSectionDemanlgedName = node.print() #expect(swiftStdlibDemangledName == swiftSectionDemanlgedName, "\(symbol.stringValue)") } catch { #expect(symbol.stringValue == swiftStdlibDemangledName) + #if !SILENT_TEST print(symbol) - print(error) + #endif + error.print() } } } - + #if !SILENT_TEST @Test func writeSwiftUISymbolsToDesktop() async throws { var string = "" @@ -51,30 +51,13 @@ final class SymbolDemangleTests: DyldCacheTests { } try string.write(to: .desktopDirectory.appendingPathComponent("\(imageName.rawValue)-SwiftSymbols.txt"), atomically: true, encoding: .utf8) } - #endif - + @Test func demangle() async throws { var demangler = Demangler(scalars: "_$s6Charts10ChartProxyV5value2at2asx_q_tSgSo7CGPointV_x_q_tmtAA9PlottableRzAaJR_r0_lF".unicodeScalars) let node = try demangler.demangleSymbol() node.print().print() } - - @Test func swiftSymbols() async throws { - let symbols = try symbols(for: .SwiftUI) - for symbol in symbols { - var demangler = Demangler(scalars: symbol.stringValue.unicodeScalars) - let node = try demangler.demangleSymbol() - if let functionNode = node.children.first, functionNode.kind == .function { - if let structureNode = functionNode.children.first, structureNode.kind == .structure { - node.print(using: .interface).print() - let typeNode = Node(kind: .global) { - Node(kind: .type, child: structureNode) - } - typeNode.print(using: .interface).print() - } - } - } - } + #endif private func symbols(for machOImageNames: MachOImageName...) throws -> [MachOSwiftSymbol] { var symbols: [MachOSwiftSymbol] = [] @@ -108,3 +91,48 @@ final class SymbolDemangleTests: DyldCacheTests { return symbols } } + +@Suite(.serialized) +final class XcodeMachOFilesSymbolDemangleTests { + struct MachOSwiftSymbol { + let imagePath: String + let offset: Int + let stringValue: String + } + + @MainActor + @Test func symbols() throws { + let allSwiftSymbols = try allSymbols() + "Total Swift Symbols: \(allSwiftSymbols.count)".print() + for symbol in allSwiftSymbols { + let swiftStdlibDemangledName = stdlib_demangleName(symbol.stringValue) + do { + let node = try demangleAsNode(symbol.stringValue) + let swiftSectionDemanlgedName = node.print() + #expect(swiftStdlibDemangledName == swiftSectionDemanlgedName, "\(symbol.stringValue)") + } catch { + #expect(symbol.stringValue == swiftStdlibDemangledName) + #if !SILENT_TEST + print(symbol) + #endif + error.print() + } + } + } + + private func allSymbols() throws -> [MachOSwiftSymbol] { + guard FileManager.default.fileExists(atPath: "/Applications/Xcode.app") else { return [] } + var symbols: [MachOSwiftSymbol] = [] + for machOFile in try XcodeMachOFileName.allCases.compactMap({ try File.loadFromFile(url: .init(fileURLWithPath: $0.path)).machOFiles.first }) { + for symbol in machOFile.symbols where symbol.name.isSwiftSymbol { + symbols.append(MachOSwiftSymbol(imagePath: machOFile.imagePath, offset: symbol.offset, stringValue: symbol.name)) + } + for symbol in machOFile.exportedSymbols where symbol.name.isSwiftSymbol { + if let offset = symbol.offset { + symbols.append(MachOSwiftSymbol(imagePath: machOFile.imagePath, offset: offset, stringValue: symbol.name)) + } + } + } + return symbols + } +} diff --git a/Tests/SwiftDumpTests/XcodeMachOFileDumpTests.swift b/Tests/SwiftDumpTests/XcodeMachOFileDumpTests.swift index 263aab4d..080cefc0 100644 --- a/Tests/SwiftDumpTests/XcodeMachOFileDumpTests.swift +++ b/Tests/SwiftDumpTests/XcodeMachOFileDumpTests.swift @@ -26,10 +26,4 @@ extension XcodeMachOFileDumpTests { @Test func associatedTypesInFile() async throws { try await dumpAssociatedTypes(for: machOFile) } - - @Test func symbols() async throws { - if let symbols: Symbols = try RelativeDirectPointer(relativeOffset: 2).resolve(from: 72870, in: machOFile) { - print(symbols) - } - } } From c33274e71f3473fd6b06591d94b5579b9c3c8469 Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Tue, 1 Jul 2025 23:04:57 +0800 Subject: [PATCH 10/10] Fix generic context dumping --- Package.resolved | 11 ++- Package.swift | 2 + .../ContextDescriptorProtocol.swift | 8 +- .../ContextDescriptorWrapper.swift | 16 ++++ .../Models/Generic/GenericContext.swift | 88 ++++++++++++------- .../SwiftDump/Dumpable+/Class+Dumpable.swift | 6 +- .../SwiftDump/Dumpable+/Enum+Dumpable.swift | 6 +- .../SwiftDump/Dumpable+/Struct+Dumpable.swift | 6 +- .../Extensions/GenericContext+Dump.swift | 6 +- .../GenericContextTests.swift | 35 ++++++++ .../SymbolDemangleTests.swift | 2 + Tests/SwiftDumpTests/DyldCacheDumpTests.swift | 2 +- 12 files changed, 145 insertions(+), 43 deletions(-) create mode 100644 Tests/MachOSwiftSectionTests/GenericContextTests.swift diff --git a/Package.resolved b/Package.resolved index a02d807d..3d358966 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "c063655621919263a7b3358360d2a18975b69b9fb2af587221263a629ceffba2", + "originHash" : "af420d353ec787ca7235c44800815c9f7dceef9bf6124b29b30dca2ccaee9765", "pins" : [ { "identity" : "associatedobject", @@ -73,6 +73,15 @@ "version" : "0.4.0" } }, + { + "identity" : "swift-memberwise-init-macro", + "kind" : "remoteSourceControl", + "location" : "https://github.com/MxIris-Library-Forks/swift-memberwise-init-macro", + "state" : { + "revision" : "6121b169fb5a83d7262a69b640468606f98c6c6e", + "version" : "0.5.3-fork.1" + } + }, { "identity" : "swift-object-association", "kind" : "remoteSourceControl", diff --git a/Package.swift b/Package.swift index 43ff4dc3..553f61db 100644 --- a/Package.swift +++ b/Package.swift @@ -117,6 +117,7 @@ let package = Package( .package(url: "https://github.com/onevcat/Rainbow", from: "4.0.0"), .package(url: "https://github.com/Mx-Iris/FrameworkToolbox", from: "0.3.0"), .package(url: "https://github.com/apple/swift-collections", from: "1.2.0"), + .package(url: "https://github.com/MxIris-Library-Forks/swift-memberwise-init-macro", from: "0.5.3-fork"), ], targets: [ .target( @@ -193,6 +194,7 @@ let package = Package( "Demangle", "MachOFoundation", "MachOMacro", + .product(name: "MemberwiseInit", package: "swift-memberwise-init-macro") ] ), diff --git a/Sources/MachOSwiftSection/Models/ContextDescriptor/ContextDescriptorProtocol.swift b/Sources/MachOSwiftSection/Models/ContextDescriptor/ContextDescriptorProtocol.swift index 8d7ac1d3..5724ce6b 100644 --- a/Sources/MachOSwiftSection/Models/ContextDescriptor/ContextDescriptorProtocol.swift +++ b/Sources/MachOSwiftSection/Models/ContextDescriptor/ContextDescriptorProtocol.swift @@ -2,7 +2,13 @@ import MachOKit import MachOFoundation import MachOMacro -public protocol ContextDescriptorProtocol: ResolvableLocatableLayoutWrapper where Layout: ContextDescriptorLayout {} +public protocol ContextDescriptorProtocol: ResolvableLocatableLayoutWrapper where Layout: ContextDescriptorLayout { + func genericContext(in machO: MachOFile) throws -> GenericContext? + func parent(in machO: MachOFile) throws -> SymbolOrElement? + + func genericContext(in machO: MachOImage) throws -> GenericContext? + func parent(in machO: MachOImage) throws -> SymbolOrElement? +} @MachOImageAllMembersGenerator extension ContextDescriptorProtocol { diff --git a/Sources/MachOSwiftSection/Models/ContextDescriptor/ContextDescriptorWrapper.swift b/Sources/MachOSwiftSection/Models/ContextDescriptor/ContextDescriptorWrapper.swift index 7ff7090a..e86abb00 100644 --- a/Sources/MachOSwiftSection/Models/ContextDescriptor/ContextDescriptorWrapper.swift +++ b/Sources/MachOSwiftSection/Models/ContextDescriptor/ContextDescriptorWrapper.swift @@ -106,6 +106,22 @@ public enum ContextDescriptorWrapper { } } + public var typeContextDescriptor: (any TypeContextDescriptorProtocol)? { + if case .type(let typeContextDescriptor) = self { + switch typeContextDescriptor { + case .enum(let enumDescriptor): + return enumDescriptor + case .struct(let structDescriptor): + return structDescriptor + case .class(let classDescriptor): + return classDescriptor + } + } else { + return nil + } + } + + public subscript(dynamicMember keyPath: KeyPath) -> Property { return contextDescriptor[keyPath: keyPath] } diff --git a/Sources/MachOSwiftSection/Models/Generic/GenericContext.swift b/Sources/MachOSwiftSection/Models/Generic/GenericContext.swift index 57af97b2..d1e02f01 100644 --- a/Sources/MachOSwiftSection/Models/Generic/GenericContext.swift +++ b/Sources/MachOSwiftSection/Models/Generic/GenericContext.swift @@ -2,53 +2,53 @@ import Foundation import MachOKit import MachOFoundation import MachOMacro +import MemberwiseInit public typealias GenericContext = TargetGenericContext public typealias TypeGenericContext = TargetGenericContext +@MemberwiseInit(.private) public struct TargetGenericContext { public let offset: Int public let size: Int public let header: Header + public let parameters: [GenericParamDescriptor] public let requirements: [GenericRequirementDescriptor] public let typePackHeader: GenericPackShapeHeader? public let typePacks: [GenericPackShapeDescriptor] + public let valueHeader: GenericValueHeader? + public let values: [GenericValueDescriptor] + + public let parentParameters: [GenericParamDescriptor] + public let parentRequirements: [GenericRequirementDescriptor] + public let parentTypePacks: [GenericPackShapeDescriptor] + public let parentValues: [GenericValueDescriptor] + public let conditionalInvertibleProtocolSet: InvertibleProtocolSet? public let conditionalInvertibleProtocolsRequirementsCount: InvertibleProtocolsRequirementCount? public let conditionalInvertibleProtocolsRequirements: [GenericRequirementDescriptor] - public let valueHeader: GenericValueHeader? - public let values: [GenericValueDescriptor] - private init( - offset: Int, - size: Int, - header: Header, - parameters: [GenericParamDescriptor], - requirements: [GenericRequirementDescriptor], - typePackHeader: GenericPackShapeHeader?, - typePacks: [GenericPackShapeDescriptor], - conditionalInvertibleProtocolSet: InvertibleProtocolSet?, - conditionalInvertibleProtocolsRequirementsCount: InvertibleProtocolsRequirementCount?, - conditionalInvertibleProtocolsRequirements: [GenericRequirementDescriptor], - valueHeader: GenericValueHeader?, - values: [GenericValueDescriptor] - ) { - self.offset = offset - self.size = size - self.header = header - self.parameters = parameters - self.requirements = requirements - self.typePackHeader = typePackHeader - self.typePacks = typePacks - self.conditionalInvertibleProtocolSet = conditionalInvertibleProtocolSet - self.conditionalInvertibleProtocolsRequirementsCount = conditionalInvertibleProtocolsRequirementsCount - self.conditionalInvertibleProtocolsRequirements = conditionalInvertibleProtocolsRequirements - self.valueHeader = valueHeader - self.values = values + public let depth: Int + + public var currentParameters: [GenericParamDescriptor] { + .init(parameters.dropFirst(parentParameters.count)) } - + + public var currentRequirements: [GenericRequirementDescriptor] { + .init(requirements.dropFirst(parentRequirements.count)) + } + + public var currentTypePacks: [GenericPackShapeDescriptor] { + .init(typePacks.dropFirst(parentTypePacks.count)) + } + + public var currentValues: [GenericValueDescriptor] { + .init(values.dropFirst(parentValues.count)) + } + + public func asGenericContext() -> GenericContext { .init( offset: offset, @@ -66,11 +66,16 @@ public struct TargetGenericContext 0 { + try genericContext.dumpGenericParameters(in: machOFile) + } } if let superclassMangledName = try descriptor.superclassTypeMangledName(in: machOFile) { @@ -39,7 +41,7 @@ extension Class: NamedDumpable { superclass } - if let genericContext, genericContext.requirements.count > 0 { + if let genericContext, genericContext.currentRequirements.count > 0 { Space() Keyword(.where) Space() diff --git a/Sources/SwiftDump/Dumpable+/Enum+Dumpable.swift b/Sources/SwiftDump/Dumpable+/Enum+Dumpable.swift index f4006441..410cfd32 100644 --- a/Sources/SwiftDump/Dumpable+/Enum+Dumpable.swift +++ b/Sources/SwiftDump/Dumpable+/Enum+Dumpable.swift @@ -22,8 +22,10 @@ extension Enum: NamedDumpable { try dumpName(using: options, in: machOFile) if let genericContext { - try genericContext.dumpGenericParameters(in: machOFile) - if genericContext.requirements.count > 0 { + if genericContext.currentParameters.count > 0 { + try genericContext.dumpGenericParameters(in: machOFile) + } + if genericContext.currentRequirements.count > 0 { Space() Keyword(.where) Space() diff --git a/Sources/SwiftDump/Dumpable+/Struct+Dumpable.swift b/Sources/SwiftDump/Dumpable+/Struct+Dumpable.swift index e829cf6b..351f7d6d 100644 --- a/Sources/SwiftDump/Dumpable+/Struct+Dumpable.swift +++ b/Sources/SwiftDump/Dumpable+/Struct+Dumpable.swift @@ -22,8 +22,10 @@ extension Struct: NamedDumpable { try dumpName(using: options, in: machOFile) if let genericContext { - try genericContext.dumpGenericParameters(in: machOFile) - if genericContext.requirements.count > 0 { + if genericContext.currentParameters.count > 0 { + try genericContext.dumpGenericParameters(in: machOFile) + } + if genericContext.currentRequirements.count > 0 { Space() Keyword(.where) Space() diff --git a/Sources/SwiftDump/Extensions/GenericContext+Dump.swift b/Sources/SwiftDump/Extensions/GenericContext+Dump.swift index b79f234c..fb4df3b5 100644 --- a/Sources/SwiftDump/Extensions/GenericContext+Dump.swift +++ b/Sources/SwiftDump/Extensions/GenericContext+Dump.swift @@ -10,8 +10,8 @@ extension TargetGenericContext { @SemanticStringBuilder package func dumpGenericParameters(in machOFile: MachOFile) throws -> SemanticString { Standard("<") - for (offset, _) in parameters.offsetEnumerated() { - Standard(try genericParameterName(depth: 0, index: offset.index)) + for (offset, _) in currentParameters.offsetEnumerated() { + Standard(try genericParameterName(depth: depth, index: offset.index)) if !offset.isEnd { Standard(", ") } @@ -35,7 +35,7 @@ extension TargetGenericContext { @MachOImageGenerator @SemanticStringBuilder package func dumpGenericRequirements(using options: DemangleOptions, in machOFile: MachOFile) throws -> SemanticString { - for (offset, requirement) in requirements.offsetEnumerated() { + for (offset, requirement) in currentRequirements.offsetEnumerated() { try requirement.dump(using: options, in: machOFile) if !offset.isEnd { Standard(",") diff --git a/Tests/MachOSwiftSectionTests/GenericContextTests.swift b/Tests/MachOSwiftSectionTests/GenericContextTests.swift new file mode 100644 index 00000000..6ff5da90 --- /dev/null +++ b/Tests/MachOSwiftSectionTests/GenericContextTests.swift @@ -0,0 +1,35 @@ +import Foundation +import Testing +@testable import MachOSwiftSection +@testable import MachOTestingSupport + + +final class GenericContextTests: DyldCacheTests { + override class var cacheImageName: MachOImageName { .SwiftUI } + + + + @Test func genericContexts() async throws { + let typeDescriptors = try machOFileInCache.swift.typeContextDescriptors + + for typeDescriptor in typeDescriptors { + guard case .type(let type) = typeDescriptor else { + continue + } + switch type { + case .enum(let descriptor): + if let genericContext = try descriptor.typeGenericContext(in: machOFileInCache) { + print(genericContext.parameters) + } + case .struct(let descriptor): + if let genericContext = try descriptor.typeGenericContext(in: machOFileInCache) { + print(genericContext.parameters) + } + case .class(let descriptor): + if let genericContext = try descriptor.typeGenericContext(in: machOFileInCache) { + print(genericContext.parameters) + } + } + } + } +} diff --git a/Tests/MachOSwiftSectionTests/SymbolDemangleTests.swift b/Tests/MachOSwiftSectionTests/SymbolDemangleTests.swift index 57fc3d8d..530901ab 100644 --- a/Tests/MachOSwiftSectionTests/SymbolDemangleTests.swift +++ b/Tests/MachOSwiftSectionTests/SymbolDemangleTests.swift @@ -92,6 +92,7 @@ final class DyldCacheSymbolDemangleTests: DyldCacheTests { } } +#if !SILENT_TEST @Suite(.serialized) final class XcodeMachOFilesSymbolDemangleTests { struct MachOSwiftSymbol { @@ -136,3 +137,4 @@ final class XcodeMachOFilesSymbolDemangleTests { return symbols } } +#endif diff --git a/Tests/SwiftDumpTests/DyldCacheDumpTests.swift b/Tests/SwiftDumpTests/DyldCacheDumpTests.swift index d81e63a1..5ca03dbe 100644 --- a/Tests/SwiftDumpTests/DyldCacheDumpTests.swift +++ b/Tests/SwiftDumpTests/DyldCacheDumpTests.swift @@ -9,7 +9,7 @@ import MachOFoundation @Suite(.serialized) final class DyldCacheDumpTests: DyldCacheTests, DumpableTests { - override class var cacheImageName: MachOImageName { .SwiftUICore } + override class var cacheImageName: MachOImageName { .SwiftUI } } extension DyldCacheDumpTests {