From e3b1838b04c3f7b4507cd61a26451eee05cf5777 Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Wed, 18 Jun 2025 18:28:05 +0800 Subject: [PATCH 01/16] Update minimum os versions --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 444616ab..83206cd7 100644 --- a/Package.swift +++ b/Package.swift @@ -72,7 +72,7 @@ extension Target.Dependency { let package = Package( name: "MachOSwiftSection", - platforms: [.macOS(.v10_15)], + platforms: [.macOS(.v10_15), .iOS(.v13), .tvOS(.v13), .watchOS(.v6), .visionOS(.v1)], products: [ .library( name: "MachOSwiftSection", From a129d1644de67dc0a31a84b242fc962bdf93761b Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Fri, 20 Jun 2025 00:29:16 +0800 Subject: [PATCH 02/16] Support assciatedTypes in MachOImage --- Package.swift | 3 +- Sources/Demangle/Main/NodePrinter.swift | 2 +- .../MachOExtensions/MachORepresentable+.swift | 17 ++++ .../SegmentCommandProtocol+.swift | 3 + .../MachOSwiftSection/Extensions/Array+.swift | 13 +++ .../MachOSwiftSection/Extensions/Data+.swift | 3 - .../Extensions/MachORepresentable+.swift | 47 +++++++++++ .../Extensions/SegmentCommandProtocol+.swift | 11 --- .../MachOSwiftSection/MachOFile+Swift.swift | 39 +-------- .../MachOSwiftSection/MachOImage+Swift.swift | 48 ++++++----- Sources/Semantic/SemanticString.swift | 4 +- Sources/Semantic/SemanticType.swift | 1 + .../Dumpable+/AssociatedType+Dumpable.swift | 36 ++++++--- .../SwiftDump/Dumpable+/Class+Dumpable.swift | 21 +++-- .../SwiftDump/Dumpable+/Enum+Dumpable.swift | 4 +- .../Dumpable+/Protocol+Dumpable.swift | 9 ++- .../ProtocolConformance+Dumpable.swift | 16 ++-- .../SwiftDump/Dumpable+/Struct+Dumpable.swift | 4 +- .../ContextDescriptorWrapper+Dump.swift | 5 +- .../Extensions/ResilientSuperclass+Dump.swift | 11 +-- .../MachOSwiftSectionTests/LayoutTests.swift | 1 - Tests/SwiftDumpTests/SwiftDumpTests.swift | 79 +++++++++---------- 22 files changed, 210 insertions(+), 167 deletions(-) create mode 100644 Sources/MachOExtensions/MachORepresentable+.swift create mode 100644 Sources/MachOSwiftSection/Extensions/Array+.swift delete mode 100644 Sources/MachOSwiftSection/Extensions/Data+.swift create mode 100644 Sources/MachOSwiftSection/Extensions/MachORepresentable+.swift delete mode 100644 Sources/MachOSwiftSection/Extensions/SegmentCommandProtocol+.swift diff --git a/Package.swift b/Package.swift index 83206cd7..6e5e2ce2 100644 --- a/Package.swift +++ b/Package.swift @@ -111,6 +111,7 @@ let package = Package( name: "MachOExtensions", dependencies: [ .MachOKit, + "MachOMacro", ] ), @@ -146,10 +147,10 @@ let package = Package( .target( name: "MachOSwiftSection", dependencies: [ + .MachOKit, "Demangle", "MachOFoundation", "MachOMacro", - .MachOKit, ] ), diff --git a/Sources/Demangle/Main/NodePrinter.swift b/Sources/Demangle/Main/NodePrinter.swift index 9e46f40f..011f6163 100644 --- a/Sources/Demangle/Main/NodePrinter.swift +++ b/Sources/Demangle/Main/NodePrinter.swift @@ -105,7 +105,7 @@ struct NodePrinter: Sendable { mutating func printModule(_ name: Node) { if options.contains(.displayModuleNames) { - target.write(name.text ?? "") + target.write(name.text ?? "", type: .other) } } diff --git a/Sources/MachOExtensions/MachORepresentable+.swift b/Sources/MachOExtensions/MachORepresentable+.swift new file mode 100644 index 00000000..3f0f472a --- /dev/null +++ b/Sources/MachOExtensions/MachORepresentable+.swift @@ -0,0 +1,17 @@ +import Foundation +import MachOKit +import MachOMacro + +@MachOImageAllMembersGenerator +extension MachORepresentable { + package func _section( + for name: String, + in machOFile: MachOFile + ) -> (any SectionProtocol)? { + sections.first( + where: { + $0.sectionName == name + } + ) + } +} diff --git a/Sources/MachOExtensions/SegmentCommandProtocol+.swift b/Sources/MachOExtensions/SegmentCommandProtocol+.swift index 1f5a93a9..0561adfb 100644 --- a/Sources/MachOExtensions/SegmentCommandProtocol+.swift +++ b/Sources/MachOExtensions/SegmentCommandProtocol+.swift @@ -18,3 +18,6 @@ extension SegmentCommandProtocol { ) } } + + + diff --git a/Sources/MachOSwiftSection/Extensions/Array+.swift b/Sources/MachOSwiftSection/Extensions/Array+.swift new file mode 100644 index 00000000..301efff6 --- /dev/null +++ b/Sources/MachOSwiftSection/Extensions/Array+.swift @@ -0,0 +1,13 @@ +import Foundation + +func + (lhs: [Element]?, rhs: [Element]?) -> [Element] { + (lhs ?? []) + (rhs ?? []) +} + +func + (lhs: [Element], rhs: [Element]?) -> [Element] { + lhs + (rhs ?? []) +} + +func + (lhs: [Element]?, rhs: [Element]) -> [Element] { + (lhs ?? []) + rhs +} diff --git a/Sources/MachOSwiftSection/Extensions/Data+.swift b/Sources/MachOSwiftSection/Extensions/Data+.swift deleted file mode 100644 index bf588098..00000000 --- a/Sources/MachOSwiftSection/Extensions/Data+.swift +++ /dev/null @@ -1,3 +0,0 @@ -import Foundation - -extension Data {} diff --git a/Sources/MachOSwiftSection/Extensions/MachORepresentable+.swift b/Sources/MachOSwiftSection/Extensions/MachORepresentable+.swift new file mode 100644 index 00000000..26f2c720 --- /dev/null +++ b/Sources/MachOSwiftSection/Extensions/MachORepresentable+.swift @@ -0,0 +1,47 @@ +import Foundation +import MachOKit +import MachOMacro + +extension MachOFile { + func section(for swiftMachOSection: MachOSwiftSectionName) throws -> (any SectionProtocol) { + let loadCommands = loadCommands + let swiftSection: any SectionProtocol + if let text = loadCommands.text64, + let section = text._section(for: swiftMachOSection.rawValue, in: self) { + swiftSection = section + } else if let text = loadCommands.text, + let section = text._section(for: swiftMachOSection.rawValue, in: self) { + swiftSection = section + } else if let section = sections.first(where: { $0.sectionName == swiftMachOSection.rawValue }) { + swiftSection = section + } else { + throw MachOSwiftSectionError.sectionNotFound(section: swiftMachOSection, allSectionNames: sections.map(\.sectionName)) + } + guard swiftSection.align * 2 == 4 else { + throw MachOSwiftSectionError.invalidSectionAlignment(section: swiftMachOSection, align: swiftSection.align) + } + return swiftSection + } +} + +extension MachOImage { + func section(for swiftMachOSection: MachOSwiftSectionName) throws -> (any SectionProtocol) { + let loadCommands = loadCommands + let swiftSection: any SectionProtocol + if let text = loadCommands.text64, + let section = text._section(for: swiftMachOSection.rawValue, in: self) { + swiftSection = section + } else if let text = loadCommands.text, + let section = text._section(for: swiftMachOSection.rawValue, in: self) { + swiftSection = section + } else if let section = sections.first(where: { $0.sectionName == swiftMachOSection.rawValue }) { + swiftSection = section + } else { + throw MachOSwiftSectionError.sectionNotFound(section: swiftMachOSection, allSectionNames: sections.map(\.sectionName)) + } + guard swiftSection.align * 2 == 4 else { + throw MachOSwiftSectionError.invalidSectionAlignment(section: swiftMachOSection, align: swiftSection.align) + } + return swiftSection + } +} diff --git a/Sources/MachOSwiftSection/Extensions/SegmentCommandProtocol+.swift b/Sources/MachOSwiftSection/Extensions/SegmentCommandProtocol+.swift deleted file mode 100644 index 8721258d..00000000 --- a/Sources/MachOSwiftSection/Extensions/SegmentCommandProtocol+.swift +++ /dev/null @@ -1,11 +0,0 @@ -import MachOKit - -extension SegmentCommandProtocol { - func _section(for swiftSection: MachOSwiftSectionName, in machOFile: MachOFile) -> SectionType? { - _section(for: swiftSection.rawValue, in: machOFile) - } - - func _section(for swiftSection: MachOSwiftSectionName, in machOFile: MachOImage) -> SectionType? { - _section(for: swiftSection.rawValue, in: machOFile) - } -} diff --git a/Sources/MachOSwiftSection/MachOFile+Swift.swift b/Sources/MachOSwiftSection/MachOFile+Swift.swift index a8e0e658..24c32e44 100644 --- a/Sources/MachOSwiftSection/MachOFile+Swift.swift +++ b/Sources/MachOSwiftSection/MachOFile+Swift.swift @@ -51,7 +51,7 @@ extension MachOFile.Swift { public var associatedTypeDescriptors: [AssociatedTypeDescriptor] { get throws { - let section = try _section(for: .__swift5_assocty, in: machOFile) + let section = try machOFile.section(for: .__swift5_assocty) var associatedTypeDescriptors: [AssociatedTypeDescriptor] = [] let offset = if let cache = machOFile.cache { section.address - cache.mainCacheHeader.sharedRegionStart.cast() @@ -71,30 +71,8 @@ extension MachOFile.Swift { } extension MachOFile.Swift { - private func _section(for swiftMachOSection: MachOSwiftSectionName, in machOFile: MachOFile) throws -> (any SectionProtocol) { - let loadCommands = machOFile.loadCommands - let swiftSection: any SectionProtocol - if let text = loadCommands.text64, - let section = text._section(for: swiftMachOSection, in: machOFile) { - swiftSection = section - } else if let text = loadCommands.text, - let section = text._section(for: swiftMachOSection, in: machOFile) { - swiftSection = section - } else { - throw MachOSwiftSectionError.sectionNotFound(section: swiftMachOSection, allSectionNames: machOFile.sections.map(\.sectionName)) - } - guard swiftSection.align * 2 == 4 else { - throw MachOSwiftSectionError.invalidSectionAlignment(section: swiftMachOSection, align: swiftSection.align) - } - return swiftSection - } - - private func _readDescriptors(from swiftMachOSection: MachOSwiftSectionName, in machOFile: MachOFile) throws -> [Descriptor] { - let section = try _section(for: swiftMachOSection, in: machOFile) - return try _readDescriptors(from: section, in: machOFile) - } - - private func _readDescriptors(from section: any SectionProtocol, in machO: MachOFile) throws -> [Descriptor] { + private func _readDescriptors(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 { section.address - cache.mainCacheHeader.sharedRegionStart.cast() @@ -107,14 +85,3 @@ extension MachOFile.Swift { } } -func + (lhs: [Element]?, rhs: [Element]?) -> [Element] { - (lhs ?? []) + (rhs ?? []) -} - -func + (lhs: [Element], rhs: [Element]?) -> [Element] { - lhs + (rhs ?? []) -} - -func + (lhs: [Element]?, rhs: [Element]) -> [Element] { - (lhs ?? []) + rhs -} diff --git a/Sources/MachOSwiftSection/MachOImage+Swift.swift b/Sources/MachOSwiftSection/MachOImage+Swift.swift index 9c7980d9..73a8988a 100644 --- a/Sources/MachOSwiftSection/MachOImage+Swift.swift +++ b/Sources/MachOSwiftSection/MachOImage+Swift.swift @@ -34,39 +34,35 @@ extension MachOImage.Swift { return try _readDescriptors(from: .__swift5_types, in: machOImage) + (try? _readDescriptors(from: .__swift5_types2, in: machOImage)) } } -} -extension MachOImage.Swift { - private func _section(for swiftMachOSection: MachOSwiftSectionName, in machOImage: MachOImage) throws -> (any SectionProtocol) { - let loadCommands = machOImage.loadCommands - let swiftSection: any SectionProtocol - if let text = loadCommands.text64, - let section = text._section(for: swiftMachOSection, in: machOImage) { - swiftSection = section - } else if let text = loadCommands.text, - let section = text._section(for: swiftMachOSection, in: machOImage) { - swiftSection = section - } else { - throw MachOSwiftSectionError.sectionNotFound(section: swiftMachOSection, allSectionNames: machOImage.sections.map(\.sectionName)) - } - guard swiftSection.align * 2 == 4 else { - - throw MachOSwiftSectionError.invalidSectionAlignment(section: swiftMachOSection, align: swiftSection.align) + 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 swiftSection - } - - private func _readDescriptors(from swiftMachOSection: MachOSwiftSectionName, in machOImage: MachOImage) throws -> [Descriptor] { - let section = try _section(for: swiftMachOSection, in: machOImage) - return try _readDescriptors(from: section, in: machOImage) } +} - private func _readDescriptors(from section: any SectionProtocol, in machO: MachOImage) throws -> [Descriptor] { +extension MachOImage.Swift { + private func _readDescriptors(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) diff --git a/Sources/Semantic/SemanticString.swift b/Sources/Semantic/SemanticString.swift index 767ca8c9..eb9ca2b1 100644 --- a/Sources/Semantic/SemanticString.swift +++ b/Sources/Semantic/SemanticString.swift @@ -43,9 +43,9 @@ public struct SemanticString: Sendable, TextOutputStream, Codable { .init(components: components.map { modifier($0) }) } - public func replacing(from type: SemanticType, to newType: SemanticType) -> SemanticString { + public func replacing(from types: SemanticType..., to newType: SemanticType) -> SemanticString { map { component in - if component.type == type { + if types.contains(component.type) { return AnyComponent(string: component.string, type: newType) } else { return component diff --git a/Sources/Semantic/SemanticType.swift b/Sources/Semantic/SemanticType.swift index 74602a12..858b99f9 100644 --- a/Sources/Semantic/SemanticType.swift +++ b/Sources/Semantic/SemanticType.swift @@ -12,4 +12,5 @@ public enum SemanticType: CaseIterable, Codable, Sendable { case memberName case functionName case functionDeclaration + case other } diff --git a/Sources/SwiftDump/Dumpable+/AssociatedType+Dumpable.swift b/Sources/SwiftDump/Dumpable+/AssociatedType+Dumpable.swift index a52e6c1c..9780d490 100644 --- a/Sources/SwiftDump/Dumpable+/AssociatedType+Dumpable.swift +++ b/Sources/SwiftDump/Dumpable+/AssociatedType+Dumpable.swift @@ -5,25 +5,37 @@ import MachOMacro import Semantic extension AssociatedType: Dumpable { + @MachOImageGenerator + @SemanticStringBuilder + public func dumpTypeName(using options: DemangleOptions, in machOFile: MachOFile) throws -> SemanticString { + try MetadataReader.demangleSymbol(for: conformingTypeName, in: machOFile).printSemantic(using: options).replacing(from: .typeName, .other, to: .typeDeclaration) + } + + @MachOImageGenerator + @SemanticStringBuilder + public func dumpProtocolName(using options: DemangleOptions, in machOFile: MachOFile) throws -> SemanticString { + try MetadataReader.demangleSymbol(for: protocolTypeName, in: machOFile).printSemantic(using: options) + } + @MachOImageGenerator @SemanticStringBuilder public func dump(using options: DemangleOptions, in machOFile: MachOFile) throws -> SemanticString { Keyword(.extension) - + Space() - - try MetadataReader.demangleSymbol(for: conformingTypeName, in: machOFile).printSemantic(using: options).replacing(from: .typeName, to: .typeDeclaration) - + + try dumpTypeName(using: options, in: machOFile) + Standard(":") - + Space() - - try MetadataReader.demangleSymbol(for: protocolTypeName, in: machOFile).printSemantic(using: options) - + + try dumpProtocolName(using: options, in: machOFile) + Space() - + Standard("{") - + for (offset, record) in records.offsetEnumerated() { BreakLine() @@ -40,7 +52,7 @@ extension AssociatedType: Dumpable { Standard("=") Space() - + try MetadataReader.demangleSymbol(for: record.substitutedTypeName(in: machOFile), in: machOFile).printSemantic(using: options) if offset.isEnd { @@ -51,5 +63,3 @@ extension AssociatedType: Dumpable { Standard("}") } } - - diff --git a/Sources/SwiftDump/Dumpable+/Class+Dumpable.swift b/Sources/SwiftDump/Dumpable+/Class+Dumpable.swift index 8f9bbb5e..9f4c7cd7 100644 --- a/Sources/SwiftDump/Dumpable+/Class+Dumpable.swift +++ b/Sources/SwiftDump/Dumpable+/Class+Dumpable.swift @@ -6,12 +6,11 @@ import MachOFoundation import MachOSwiftSection extension Class: NamedDumpable { - @MachOImageGenerator public func dumpName(using options: DemangleOptions, in machOFile: MachOFile) throws -> SemanticString { - try MetadataReader.demangleContext(for: .type(.class(descriptor)), in: machOFile).printSemantic(using: options) + try MetadataReader.demangleContext(for: .type(.class(descriptor)), in: machOFile).printSemantic(using: options).replacingTypeNameOrOtherToTypeDeclaration() } - + @MachOImageGenerator @SemanticStringBuilder public func dump(using options: DemangleOptions, in machOFile: MachOFile) throws -> SemanticString { @@ -19,7 +18,7 @@ extension Class: NamedDumpable { Space() - try dumpName(using: options, in: machOFile).replacing(from: .typeName, to: .typeDeclaration) + try dumpName(using: options, in: machOFile) if let genericContext { try genericContext.dumpGenericParameters(in: machOFile) @@ -32,7 +31,7 @@ extension Class: NamedDumpable { } else if let resilientSuperclass, let kind = descriptor.resilientSuperclassReferenceKind, let superclass = try resilientSuperclass.dumpSuperclass(using: options, for: kind, in: machOFile) { Standard(":") Space() - TypeName(superclass) + superclass } if let genericContext, genericContext.requirements.count > 0 { @@ -93,7 +92,7 @@ extension Class: NamedDumpable { Indent(level: 1) dumpMethodKind(for: descriptor) - + dumpMethodKeyword(for: descriptor) try dumpMethodDeclaration(for: descriptor, using: options, in: machOFile) @@ -107,7 +106,7 @@ extension Class: NamedDumpable { BreakLine() Indent(level: 1) - + if let methodDescriptor = try descriptor.methodDescriptor(in: machOFile) { switch methodDescriptor { case .symbol(let symbol): @@ -162,15 +161,14 @@ extension Class: NamedDumpable { Standard("}") } - + @SemanticStringBuilder private func dumpMethodKind(for descriptor: MethodDescriptor) -> SemanticString { InlineComment("[\(descriptor.flags.kind)]") Space() } - - + @SemanticStringBuilder private func dumpMethodKeyword(for descriptor: MethodDescriptor) -> SemanticString { if !descriptor.flags.isInstance, descriptor.flags.kind != .`init` { @@ -188,7 +186,7 @@ extension Class: NamedDumpable { Space() } } - + @MachOImageGenerator @SemanticStringBuilder private func dumpMethodDeclaration(for descriptor: MethodDescriptor, using options: DemangleOptions, in machOFile: MachOFile) throws -> SemanticString { @@ -208,7 +206,6 @@ extension Node { } } - extension String { var insertSubFunctionPrefix: String { "sub_" + self diff --git a/Sources/SwiftDump/Dumpable+/Enum+Dumpable.swift b/Sources/SwiftDump/Dumpable+/Enum+Dumpable.swift index 25712e04..68c5f437 100644 --- a/Sources/SwiftDump/Dumpable+/Enum+Dumpable.swift +++ b/Sources/SwiftDump/Dumpable+/Enum+Dumpable.swift @@ -8,7 +8,7 @@ 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) + try MetadataReader.demangleContext(for: .type(.enum(descriptor)), in: machOFile).printSemantic(using: options).replacingTypeNameOrOtherToTypeDeclaration() } @MachOImageGenerator @@ -18,7 +18,7 @@ extension Enum: NamedDumpable { Space() - try dumpName(using: options, in: machOFile).replacing(from: .typeName, to: .typeDeclaration) + try dumpName(using: options, in: machOFile) if let genericContext { try genericContext.dumpGenericParameters(in: machOFile) diff --git a/Sources/SwiftDump/Dumpable+/Protocol+Dumpable.swift b/Sources/SwiftDump/Dumpable+/Protocol+Dumpable.swift index 17f605bf..41bd1de5 100644 --- a/Sources/SwiftDump/Dumpable+/Protocol+Dumpable.swift +++ b/Sources/SwiftDump/Dumpable+/Protocol+Dumpable.swift @@ -5,18 +5,19 @@ import MachOMacro import Semantic extension MachOSwiftSection.`Protocol`: NamedDumpable { - @MachOImageGenerator public func dumpName(using options: DemangleOptions, in machOFile: MachOFile) throws -> SemanticString { - try MetadataReader.demangleContext(for: .protocol(descriptor), in: machOFile).printSemantic(using: options) + try MetadataReader.demangleContext(for: .protocol(descriptor), in: machOFile).printSemantic(using: options).replacing(from: .typeName, .other, to: .typeDeclaration) } - + @MachOImageGenerator @SemanticStringBuilder public func dump(using options: DemangleOptions, in machOFile: MachOFile) throws -> SemanticString { Keyword(.protocol) + Space() - try dumpName(using: options, in: machOFile).replacing(from: .typeName, to: .typeDeclaration) + + try dumpName(using: options, in: machOFile) if numberOfRequirementsInSignature > 0 { Space() diff --git a/Sources/SwiftDump/Dumpable+/ProtocolConformance+Dumpable.swift b/Sources/SwiftDump/Dumpable+/ProtocolConformance+Dumpable.swift index ba70b175..782a4957 100644 --- a/Sources/SwiftDump/Dumpable+/ProtocolConformance+Dumpable.swift +++ b/Sources/SwiftDump/Dumpable+/ProtocolConformance+Dumpable.swift @@ -10,13 +10,13 @@ extension ProtocolConformance: Dumpable { public func dumpTypeName(using options: DemangleOptions, in machOFile: MachOFile) throws -> SemanticString { switch typeReference { case .directTypeDescriptor(let descriptor): - try TypeDeclaration(descriptor.flatMap { try $0.dumpName(using: options, in: machOFile) }.valueOrEmpty) + (try descriptor?.dumpName(using: options, in: machOFile) ?? SemanticString()).replacingTypeNameOrOtherToTypeDeclaration() case .indirectTypeDescriptor(let descriptor): switch descriptor { case .symbol(let unsolvedSymbol): - try MetadataReader.demangleType(for: unsolvedSymbol, in: machOFile).printSemantic(using: options).replacing(from: .typeName, to: .typeDeclaration) + try MetadataReader.demangleType(for: unsolvedSymbol, in: machOFile).printSemantic(using: options).replacingTypeNameOrOtherToTypeDeclaration() case .element(let element): - try TypeDeclaration(element.dumpName(using: options, in: machOFile)) + (try element.dumpName(using: options, in: machOFile) ?? SemanticString()).replacingTypeNameOrOtherToTypeDeclaration() case nil: Standard("") } @@ -25,9 +25,9 @@ extension ProtocolConformance: Dumpable { case .indirectObjCClass(let objcClass): switch objcClass { case .symbol(let unsolvedSymbol): - try MetadataReader.demangleType(for: unsolvedSymbol, in: machOFile).printSemantic(using: options).replacing(from: .typeName, to: .typeDeclaration) + try MetadataReader.demangleType(for: unsolvedSymbol, in: machOFile).printSemantic(using: options).replacingTypeNameOrOtherToTypeDeclaration() case .element(let element): - try MetadataReader.demangleContext(for: .type(.class(element.descriptor.resolve(in: machOFile))), in: machOFile).printSemantic(using: options).replacing(from: .typeName, to: .typeDeclaration) + try MetadataReader.demangleContext(for: .type(.class(element.descriptor.resolve(in: machOFile))), in: machOFile).printSemantic(using: options).replacingTypeNameOrOtherToTypeDeclaration() case nil: Standard("") } @@ -104,3 +104,9 @@ extension ProtocolConformance: Dumpable { } } } + +extension SemanticString { + func replacingTypeNameOrOtherToTypeDeclaration() -> SemanticString { + self.replacing(from: .typeName, .other, to: .typeDeclaration) + } +} diff --git a/Sources/SwiftDump/Dumpable+/Struct+Dumpable.swift b/Sources/SwiftDump/Dumpable+/Struct+Dumpable.swift index 135c02e0..404f75a3 100644 --- a/Sources/SwiftDump/Dumpable+/Struct+Dumpable.swift +++ b/Sources/SwiftDump/Dumpable+/Struct+Dumpable.swift @@ -8,7 +8,7 @@ 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) + try MetadataReader.demangleContext(for: .type(.struct(descriptor)), in: machOFile).printSemantic(using: options).replacingTypeNameOrOtherToTypeDeclaration() } @MachOImageGenerator @@ -18,7 +18,7 @@ extension Struct: NamedDumpable { Space() - try dumpName(using: options, in: machOFile).replacing(from: .typeName, to: .typeDeclaration) + try dumpName(using: options, in: machOFile) if let genericContext { try genericContext.dumpGenericParameters(in: machOFile) diff --git a/Sources/SwiftDump/Extensions/ContextDescriptorWrapper+Dump.swift b/Sources/SwiftDump/Extensions/ContextDescriptorWrapper+Dump.swift index 2ffb8a41..d01482f7 100644 --- a/Sources/SwiftDump/Extensions/ContextDescriptorWrapper+Dump.swift +++ b/Sources/SwiftDump/Extensions/ContextDescriptorWrapper+Dump.swift @@ -1,10 +1,11 @@ import MachOKit import MachOMacro import MachOSwiftSection +import Semantic extension ContextDescriptorWrapper { @MachOImageGenerator - package func dumpName(using options: DemangleOptions, in machOFile: MachOFile) throws -> String { - try MetadataReader.demangleContext(for: self, in: machOFile).print(using: options) + package func dumpName(using options: DemangleOptions, in machOFile: MachOFile) throws -> SemanticString { + try MetadataReader.demangleContext(for: self, in: machOFile).printSemantic(using: options) } } diff --git a/Sources/SwiftDump/Extensions/ResilientSuperclass+Dump.swift b/Sources/SwiftDump/Extensions/ResilientSuperclass+Dump.swift index 8edfb015..c0f9d869 100644 --- a/Sources/SwiftDump/Extensions/ResilientSuperclass+Dump.swift +++ b/Sources/SwiftDump/Extensions/ResilientSuperclass+Dump.swift @@ -1,10 +1,11 @@ import MachOKit import MachOMacro import MachOSwiftSection +import Semantic extension ResilientSuperclass { @MachOImageGenerator - package func dumpSuperclass(using options: DemangleOptions, for kind: TypeReferenceKind, in machOFile: MachOFile) throws -> String? { + package func dumpSuperclass(using options: DemangleOptions, for kind: TypeReferenceKind, in machOFile: MachOFile) throws -> SemanticString? { let typeReference = TypeReference.forKind(kind, at: layout.superclass.relativeOffset) let resolvedTypeReference = try typeReference.resolve(at: offset(of: \.superclass), in: machOFile) switch resolvedTypeReference { @@ -13,20 +14,20 @@ extension ResilientSuperclass { case .indirectTypeDescriptor(let resolvableElement): switch resolvableElement { case .symbol(let unsolvedSymbol): - return try MetadataReader.demangleSymbol(for: unsolvedSymbol, in: machOFile).print(using: options) + return try MetadataReader.demangleSymbol(for: unsolvedSymbol, in: machOFile).printSemantic(using: options) case .element(let element): return try element.dumpName(using: options, in: machOFile) case nil: return nil } case .directObjCClassName(let string): - return string + return string.map { SemanticString(components: TypeName($0)) } case .indirectObjCClass(let resolvableElement): switch resolvableElement { case .symbol(let unsolvedSymbol): - return try MetadataReader.demangleSymbol(for: unsolvedSymbol, in: machOFile).print(using: options) + return try MetadataReader.demangleSymbol(for: unsolvedSymbol, in: machOFile).printSemantic(using: options) case .element(let element): - return try MetadataReader.demangleContext(for: .type(.class(element.descriptor.resolve(in: machOFile))), in: machOFile).print(using: options) + return try MetadataReader.demangleContext(for: .type(.class(element.descriptor.resolve(in: machOFile))), in: machOFile).printSemantic(using: options) case nil: return nil } diff --git a/Tests/MachOSwiftSectionTests/LayoutTests.swift b/Tests/MachOSwiftSectionTests/LayoutTests.swift index 644b0893..8234e9b8 100644 --- a/Tests/MachOSwiftSectionTests/LayoutTests.swift +++ b/Tests/MachOSwiftSectionTests/LayoutTests.swift @@ -13,6 +13,5 @@ struct LayoutTests { #expect(TypeContextDescriptor.Layout.offset(of: .name) == 8) #expect(TypeContextDescriptor.Layout.offset(of: .accessFunctionPtr) == 12) #expect(TypeContextDescriptor.Layout.offset(of: .fieldDescriptor) == 16) - print(ClassMetadataObjCInterop.Layout.offset(of: .instanceAddressPoint)) } } diff --git a/Tests/SwiftDumpTests/SwiftDumpTests.swift b/Tests/SwiftDumpTests/SwiftDumpTests.swift index 3051fdd6..761c7986 100644 --- a/Tests/SwiftDumpTests/SwiftDumpTests.swift +++ b/Tests/SwiftDumpTests/SwiftDumpTests.swift @@ -8,7 +8,7 @@ import MachOFoundation import MachOTestingSupport @Suite(.serialized) -struct SwiftDumpTests { +struct DyldCacheDumpTests: DumpableTest { let mainCache: DyldCache let subCache: DyldCache @@ -19,18 +19,13 @@ struct SwiftDumpTests { let machOFileInCache: MachOFile - let machOFile: MachOFile - - let machOImage: MachOImage - let isEnabledSearchMetadata: Bool = false - init() throws { - // Cache + 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: .Foundation)) + self.machOFileInMainCache = try #require(mainCache.machOFile(named: .SwiftUI)) self.machOFileInSubCache = if #available(macOS 15.5, *) { try #require(subCache.machOFile(named: .CodableSwiftUI)) } else { @@ -38,8 +33,16 @@ struct SwiftDumpTests { } self.machOFileInCache = try #require(mainCache.machOFile(named: .AttributeGraph)) + } +} - // File +@Suite(.serialized) +struct MachOFileDumpTests: DumpableTest { + let machOFile: MachOFile + + let isEnabledSearchMetadata: Bool = false + + init() async throws { let file = try loadFromFile(named: .iOS_23A5260l_Simulator_SwiftUICore) switch file { case .fat(let fatFile): @@ -49,36 +52,21 @@ struct SwiftDumpTests { @unknown default: fatalError() } - - // Image - self.machOImage = try #require(MachOImage(named: .Foundation)) } +} - @Test func printCacheFiles() { - print("******************************[Main Cache]******************************") - for file in mainCache.machOFiles() { - print(file.imagePath) - } - print("******************************[Sub Cache]*******************************") - for file in subCache.machOFiles() { - print(file.imagePath) - } - } - - @Test func mangledName() async throws { - try MetadataReader.demangleSymbol(for: .init(offset: 0, stringValue: "_$sSo10CUICatalogC7SwiftUIE9findAsset3key10matchTypes11assetLookupxSgAC10CatalogKeyV_q_AHSSXEtSo08CUINamedJ0CRbzSlR_AC0kE9MatchTypeO7ElementRt_r0_lFSo0M5ColorC_SayANGTB503$s7b3UI5q107V05NamedC033_F70ADAD69423F89598F901BDE477D497LLV14resolveCGColor2inSo0L3RefaSgAA17EnvironmentValuesV_tFSo08M12C0CSgSSXEfU_AbC0Q0V0uQ001_wxyZ10BDE477D497LLVAC0q5CacheL0AXLLVSiTf1nncn_nTf4nnngggn_n"), in: machOFile).print(using: printOptions).print() - } - - @Test func dyldCacheLoaded() async throws { - let current = try #require(DyldCacheLoaded.current) - print(current.mainCacheHeader.sharedRegionStart) - for machOImage in current.machOImages() { - print(machOImage.ptr.uint, machOImage.path) - } +@Suite(.serialized) +struct MachOImageDumpTests: DumpableTest { + let machOImage: MachOImage + + let isEnabledSearchMetadata: Bool = false + + init() async throws { + self.machOImage = try #require(MachOImage(named: .Foundation)) } } -extension SwiftDumpTests { +extension DyldCacheDumpTests { @Test func typesInCacheFile() async throws { try await dumpTypes(for: machOFileInCache) } @@ -128,7 +116,7 @@ extension SwiftDumpTests { } } -extension SwiftDumpTests { +extension MachOFileDumpTests { @Test func typesInFile() async throws { try await dumpTypes(for: machOFile) } @@ -146,7 +134,7 @@ extension SwiftDumpTests { } } -extension SwiftDumpTests { +extension MachOImageDumpTests { @Test func typesInImage() async throws { try await dumpTypes(for: machOImage) } @@ -158,12 +146,20 @@ extension SwiftDumpTests { @Test func protocolConformancesInImage() async throws { try await dumpProtocolConformances(for: machOImage) } + + @Test func associatedTypesInImage() async throws { + try await dumpAssociatedTypes(for: machOImage) + } } -extension SwiftDumpTests { +protocol DumpableTest { + var isEnabledSearchMetadata: Bool { get } +} + +extension DumpableTest { @MachOImageGenerator @MainActor - private func dumpProtocols(for machO: MachOFile) async throws { + func dumpProtocols(for machO: MachOFile) async throws { let protocolDescriptors = try machO.swift.protocolDescriptors for protocolDescriptor in protocolDescriptors { try print(Protocol(descriptor: protocolDescriptor, in: machO).dump(using: printOptions, in: machO).string) @@ -172,7 +168,7 @@ extension SwiftDumpTests { @MachOImageGenerator @MainActor - private func dumpProtocolConformances(for machO: MachOFile) async throws { + func dumpProtocolConformances(for machO: MachOFile) async throws { let protocolConformanceDescriptors = try machO.swift.protocolConformanceDescriptors for protocolConformanceDescriptor in protocolConformanceDescriptors { @@ -182,7 +178,7 @@ extension SwiftDumpTests { @MachOImageGenerator @MainActor - private func dumpTypes(for machO: MachOFile) async throws { + func dumpTypes(for machO: MachOFile) async throws { let typeContextDescriptors = try machO.swift.typeContextDescriptors var metadataFinder: MetadataFinder? if isEnabledSearchMetadata { @@ -214,8 +210,9 @@ extension SwiftDumpTests { } } + @MachOImageGenerator @MainActor - private func dumpAssociatedTypes(for machO: MachOFile) async throws { + func dumpAssociatedTypes(for machO: MachOFile) async throws { let associatedTypeDescriptors = try machO.swift.associatedTypeDescriptors for associatedTypeDescriptor in associatedTypeDescriptors { try print(AssociatedType(descriptor: associatedTypeDescriptor, in: machO).dump(using: printOptions, in: machO).string) From f1bafcd17e9ccbd8048ae37ff327c77a3e7dad8f Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Fri, 20 Jun 2025 18:33:02 +0800 Subject: [PATCH 03/16] Bug fixes --- Sources/Demangle/Main/DemangleOptions.swift | 5 ++- Sources/Demangle/Main/NodePrinter.swift | 10 ++---- Sources/Semantic/SemanticStringBuilder.swift | 12 +++++++ .../SwiftDump/Dumpable+/Class+Dumpable.swift | 4 +-- .../ProtocolConformance+Dumpable.swift | 4 +-- .../SwiftDump/Dumpable+/Struct+Dumpable.swift | 2 +- .../swift-section/DemangleOptionGroup.swift | 5 --- Tests/SwiftDumpTests/SwiftDumpTests.swift | 34 +++++++++++++------ build-executable-product.sh | 4 +-- 9 files changed, 47 insertions(+), 33 deletions(-) diff --git a/Sources/Demangle/Main/DemangleOptions.swift b/Sources/Demangle/Main/DemangleOptions.swift index f89b4839..848ff9f7 100644 --- a/Sources/Demangle/Main/DemangleOptions.swift +++ b/Sources/Demangle/Main/DemangleOptions.swift @@ -24,8 +24,8 @@ public struct DemangleOptions: OptionSet, Codable, Sendable { public static let printForTypeName = DemangleOptions(rawValue: 1 << 19) public static let showClosureSignature = DemangleOptions(rawValue: 1 << 20) public static let showModuleInDependentMemberType = DemangleOptions(rawValue: 1 << 21) - public static let showPrefixAndSuffix = DemangleOptions(rawValue: 1 << 22) + package static let removeWeakPrefix = DemangleOptions(rawValue: 1 << 100) public init(rawValue: Int) { self.rawValue = rawValue @@ -47,8 +47,7 @@ public struct DemangleOptions: OptionSet, Codable, Sendable { .displayStdlibModule, .displayObjCModule, .showClosureSignature, - .showModuleInDependentMemberType, - .showPrefixAndSuffix + .showModuleInDependentMemberType ] public static let simplified: DemangleOptions = [ diff --git a/Sources/Demangle/Main/NodePrinter.swift b/Sources/Demangle/Main/NodePrinter.swift index 011f6163..3e7660f7 100644 --- a/Sources/Demangle/Main/NodePrinter.swift +++ b/Sources/Demangle/Main/NodePrinter.swift @@ -35,13 +35,9 @@ struct NodePrinter: Sendable { mutating func printOptional(_ optional: Node?, prefix: String? = nil, suffix: String? = nil, asPrefixContext: Bool = false) -> Node? { guard let o = optional else { return nil } - if options.contains(.showPrefixAndSuffix) { - prefix.map { target.write($0) } - } + prefix.map { target.write($0) } let r = printName(o) - if options.contains(.showPrefixAndSuffix) { - suffix.map { target.write($0) } - } + suffix.map { target.write($0) } return r } @@ -1396,7 +1392,7 @@ struct NodePrinter: Sendable { case .variadicMarker: target.write(" variadic-marker ") case .vTableAttribute: target.write("override ") case .vTableThunk: printVTableThunk(name) - case .weak: printFirstChild(name, prefix: "weak ") + case .weak: printFirstChild(name, prefix: options.contains(.removeWeakPrefix) ? "" : "weak ") case .willSet: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "willset") } diff --git a/Sources/Semantic/SemanticStringBuilder.swift b/Sources/Semantic/SemanticStringBuilder.swift index 84386563..e99a05c3 100644 --- a/Sources/Semantic/SemanticStringBuilder.swift +++ b/Sources/Semantic/SemanticStringBuilder.swift @@ -12,16 +12,28 @@ public enum SemanticStringBuilder { public static func buildPartialBlock(first: [Element]) -> [Element] { first } + public static func buildPartialBlock(first: Element?) -> [Element] { first.map { [$0] } ?? [] } + + public static func buildPartialBlock(first: [Element]?) -> [Element] { first ?? [] } + public static func buildPartialBlock(first: SemanticString) -> [Element] { first.components } + public static func buildPartialBlock(first: SemanticString?) -> [Element] { first?.components ?? [] } + public static func buildPartialBlock(first: some CustomStringConvertible) -> [Element] { [Standard(first.description)] } public static func buildPartialBlock(accumulated: [Element], next: Element) -> [Element] { accumulated + [next] } public static func buildPartialBlock(accumulated: [Element], next: [Element]) -> [Element] { accumulated + next } + public static func buildPartialBlock(accumulated: [Element], next: Element?) -> [Element] { next.map { accumulated + [$0] } ?? accumulated } + + public static func buildPartialBlock(accumulated: [Element], next: [Element]?) -> [Element] { accumulated + (next ?? []) } + public static func buildPartialBlock(accumulated: [Element], next: SemanticString) -> [Element] { accumulated + next.components } + public static func buildPartialBlock(accumulated: [Element], next: SemanticString?) -> [Element] { accumulated + (next?.components ?? []) } + public static func buildPartialBlock(accumulated: [Element], next: some CustomStringConvertible) -> [Element] { accumulated + [Standard(next.description)] } public static func buildOptional(_ components: [Element]?) -> [Element] { components ?? [] } diff --git a/Sources/SwiftDump/Dumpable+/Class+Dumpable.swift b/Sources/SwiftDump/Dumpable+/Class+Dumpable.swift index 9f4c7cd7..9f40b719 100644 --- a/Sources/SwiftDump/Dumpable+/Class+Dumpable.swift +++ b/Sources/SwiftDump/Dumpable+/Class+Dumpable.swift @@ -79,7 +79,7 @@ extension Class: NamedDumpable { Space() - demangledTypeNode.printSemantic(using: options.subtracting(.showPrefixAndSuffix)) + demangledTypeNode.printSemantic(using: options.union(.removeWeakPrefix)) if offset.isEnd { BreakLine() @@ -118,7 +118,7 @@ extension Class: NamedDumpable { Keyword(.override) Space() dumpMethodKeyword(for: element) - try dumpMethodDeclaration(for: element, using: options, in: machOFile) + try? dumpMethodDeclaration(for: element, using: options, in: machOFile) } } else { Keyword(.override) diff --git a/Sources/SwiftDump/Dumpable+/ProtocolConformance+Dumpable.swift b/Sources/SwiftDump/Dumpable+/ProtocolConformance+Dumpable.swift index 782a4957..6f50eda9 100644 --- a/Sources/SwiftDump/Dumpable+/ProtocolConformance+Dumpable.swift +++ b/Sources/SwiftDump/Dumpable+/ProtocolConformance+Dumpable.swift @@ -10,13 +10,13 @@ extension ProtocolConformance: Dumpable { public func dumpTypeName(using options: DemangleOptions, in machOFile: MachOFile) throws -> SemanticString { switch typeReference { case .directTypeDescriptor(let descriptor): - (try descriptor?.dumpName(using: options, in: machOFile) ?? SemanticString()).replacingTypeNameOrOtherToTypeDeclaration() + try descriptor?.dumpName(using: options, in: machOFile).replacingTypeNameOrOtherToTypeDeclaration() case .indirectTypeDescriptor(let descriptor): switch descriptor { case .symbol(let unsolvedSymbol): try MetadataReader.demangleType(for: unsolvedSymbol, in: machOFile).printSemantic(using: options).replacingTypeNameOrOtherToTypeDeclaration() case .element(let element): - (try element.dumpName(using: options, in: machOFile) ?? SemanticString()).replacingTypeNameOrOtherToTypeDeclaration() + try element.dumpName(using: options, in: machOFile).replacingTypeNameOrOtherToTypeDeclaration() case nil: Standard("") } diff --git a/Sources/SwiftDump/Dumpable+/Struct+Dumpable.swift b/Sources/SwiftDump/Dumpable+/Struct+Dumpable.swift index 404f75a3..8b7d73dd 100644 --- a/Sources/SwiftDump/Dumpable+/Struct+Dumpable.swift +++ b/Sources/SwiftDump/Dumpable+/Struct+Dumpable.swift @@ -67,7 +67,7 @@ extension Struct: NamedDumpable { MemberDeclaration(fieldName.stripLazyPrefix) Standard(":") Space() - demangledTypeNode.printSemantic(using: options.subtracting(.showPrefixAndSuffix)) + demangledTypeNode.printSemantic(using: options.union(.removeWeakPrefix)) if offset.isEnd { BreakLine() diff --git a/Sources/swift-section/DemangleOptionGroup.swift b/Sources/swift-section/DemangleOptionGroup.swift index 1cf980f5..ebfe6c85 100644 --- a/Sources/swift-section/DemangleOptionGroup.swift +++ b/Sources/swift-section/DemangleOptionGroup.swift @@ -66,8 +66,6 @@ struct DemangleOptionGroup: ParsableArguments { var showClosureSignature: Bool? @Flag(inversion: .prefixedEnableDisable) var showModuleInDependentMemberType: Bool? - @Flag(inversion: .prefixedEnableDisable) - var showPrefixAndSuffix: Bool? func buildSwiftDumpDemangleOptions() -> SwiftDump.DemangleOptions { @@ -138,9 +136,6 @@ struct DemangleOptionGroup: ParsableArguments { if let showModuleInDependentMemberType { options = options.update(.showModuleInDependentMemberType, enabled: showModuleInDependentMemberType) } - if let showPrefixAndSuffix { - options = options.update(.showPrefixAndSuffix, enabled: showPrefixAndSuffix) - } return options } } diff --git a/Tests/SwiftDumpTests/SwiftDumpTests.swift b/Tests/SwiftDumpTests/SwiftDumpTests.swift index 761c7986..79b46208 100644 --- a/Tests/SwiftDumpTests/SwiftDumpTests.swift +++ b/Tests/SwiftDumpTests/SwiftDumpTests.swift @@ -32,7 +32,7 @@ struct DyldCacheDumpTests: DumpableTest { try #require(subCache.machOFile(named: .UIKitCore)) } - self.machOFileInCache = try #require(mainCache.machOFile(named: .AttributeGraph)) + self.machOFileInCache = try #require(mainCache.machOFile(named: .AppKit)) } } @@ -189,19 +189,31 @@ extension DumpableTest { case .type(let typeContextDescriptorWrapper): switch typeContextDescriptorWrapper { case .enum(let enumDescriptor): - let enumType = try Enum(descriptor: enumDescriptor, in: machO) - try print(enumType.dump(using: printOptions, in: machO).string) + do { + let enumType = try Enum(descriptor: enumDescriptor, in: machO) + try print(enumType.dump(using: printOptions, in: machO).string) + } catch { + print(error) + } case .struct(let structDescriptor): - let structType = try Struct(descriptor: structDescriptor, in: machO) - try print(structType.dump(using: printOptions, in: machO).string) - if let metadata = try metadataFinder?.metadata(for: structDescriptor) as StructMetadata? { - try print(metadata.fieldOffsets(for: structDescriptor, in: machO)) + do { + let structType = try Struct(descriptor: structDescriptor, in: machO) + try print(structType.dump(using: printOptions, in: machO).string) + if let metadata = try metadataFinder?.metadata(for: structDescriptor) as StructMetadata? { + try print(metadata.fieldOffsets(for: structDescriptor, in: machO)) + } + } catch { + print(error) } case .class(let classDescriptor): - let classType = try Class(descriptor: classDescriptor, in: machO) - try print(classType.dump(using: printOptions, in: machO).string) - if let metadata = try metadataFinder?.metadata(for: classDescriptor) as ClassMetadataObjCInterop? { - try print(metadata.fieldOffsets(for: classDescriptor, in: machO)) + do { + let classType = try Class(descriptor: classDescriptor, in: machO) + try print(classType.dump(using: printOptions, in: machO).string) + if let metadata = try metadataFinder?.metadata(for: classDescriptor) as ClassMetadataObjCInterop? { + try print(metadata.fieldOffsets(for: classDescriptor, in: machO)) + } + } catch { + print(error) } } default: diff --git a/build-executable-product.sh b/build-executable-product.sh index cee3ad84..2a172b56 100755 --- a/build-executable-product.sh +++ b/build-executable-product.sh @@ -3,10 +3,10 @@ set -e # Exit on any error echo "Building x86_64 architecture..." -swift build -c release --arch x86_64 --product swift-section --enable-experimental-prebuilts +swift build -c release --arch x86_64 --product swift-section echo "Building arm64 architecture..." -swift build -c release --arch arm64 --product swift-section --enable-experimental-prebuilts +swift build -c release --arch arm64 --product swift-section # Create Products directory if [ ! -d "./Products" ]; then From c92d0ce62a0c9b160acdabd33d6c0ace600105c7 Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Sun, 22 Jun 2025 17:01:19 +0800 Subject: [PATCH 04/16] Update Testing --- .../MachOTestingSupport/DemangleOptions.swift | 16 ++ .../MachOTestingSupport/DumpableTest.swift | 89 +++++++ Sources/MachOTestingSupport/Extensions.swift | 6 + .../MachOTestingSupport/MachOFileName.swift | 4 +- Tests/SwiftDumpTests/DemangleOptions.swift | 14 - Tests/SwiftDumpTests/DyldCacheDumpTests.swift | 88 +++++++ Tests/SwiftDumpTests/MachOFileDumpTests.swift | 45 ++++ .../SwiftDumpTests/MachOImageDumpTests.swift | 37 +++ Tests/SwiftDumpTests/SwiftDumpTests.swift | 239 ------------------ 9 files changed, 284 insertions(+), 254 deletions(-) create mode 100644 Sources/MachOTestingSupport/DemangleOptions.swift create mode 100644 Sources/MachOTestingSupport/DumpableTest.swift delete mode 100644 Tests/SwiftDumpTests/DemangleOptions.swift create mode 100644 Tests/SwiftDumpTests/DyldCacheDumpTests.swift create mode 100644 Tests/SwiftDumpTests/MachOFileDumpTests.swift create mode 100644 Tests/SwiftDumpTests/MachOImageDumpTests.swift delete mode 100644 Tests/SwiftDumpTests/SwiftDumpTests.swift diff --git a/Sources/MachOTestingSupport/DemangleOptions.swift b/Sources/MachOTestingSupport/DemangleOptions.swift new file mode 100644 index 00000000..6665b992 --- /dev/null +++ b/Sources/MachOTestingSupport/DemangleOptions.swift @@ -0,0 +1,16 @@ +import Foundation +import MachOSwiftSection +import SwiftDump + +extension DemangleOptions { + static let test: DemangleOptions = { + var options = DemangleOptions.default + options.remove(.displayObjCModule) + options.insert(.synthesizeSugarOnTypes) + options.remove(.displayWhereClauses) + options.remove(.displayExtensionContexts) + options.remove(.showPrivateDiscriminators) + options.remove(.showModuleInDependentMemberType) + return options + }() +} diff --git a/Sources/MachOTestingSupport/DumpableTest.swift b/Sources/MachOTestingSupport/DumpableTest.swift new file mode 100644 index 00000000..fd95abe0 --- /dev/null +++ b/Sources/MachOTestingSupport/DumpableTest.swift @@ -0,0 +1,89 @@ +import Foundation +import MachOKit +import MachOMacro +import MachOFoundation +import MachOSwiftSection +import SwiftDump +import MachOTestingSupport + +package protocol DumpableTest { + var isEnabledSearchMetadata: Bool { get } +} + +extension DumpableTest { + @MachOImageGenerator + @MainActor + package func dumpProtocols(for machO: MachOFile) async throws { + let protocolDescriptors = try machO.swift.protocolDescriptors + for protocolDescriptor in protocolDescriptors { + try print(Protocol(descriptor: protocolDescriptor, in: machO).dump(using: .test, in: machO).string) + } + } + + @MachOImageGenerator + @MainActor + package func dumpProtocolConformances(for machO: MachOFile) async throws { + let protocolConformanceDescriptors = try machO.swift.protocolConformanceDescriptors + + for protocolConformanceDescriptor in protocolConformanceDescriptors { + try print(ProtocolConformance(descriptor: protocolConformanceDescriptor, in: machO).dump(using: .test, in: machO).string) + } + } + + @MachOImageGenerator + @MainActor + package func dumpTypes(for machO: MachOFile) async throws { + let typeContextDescriptors = try machO.swift.typeContextDescriptors + var metadataFinder: MetadataFinder? + if isEnabledSearchMetadata { + metadataFinder = MetadataFinder(machO: machO) + } + for typeContextDescriptor in typeContextDescriptors { + switch typeContextDescriptor { + case .type(let typeContextDescriptorWrapper): + switch typeContextDescriptorWrapper { + case .enum(let enumDescriptor): + do { + let enumType = try Enum(descriptor: enumDescriptor, in: machO) + try print(enumType.dump(using: .test, in: machO).string) + } catch { + print(error) + } + case .struct(let structDescriptor): + do { + let structType = try Struct(descriptor: structDescriptor, in: machO) + try print(structType.dump(using: .test, in: machO).string) + if let metadata = try metadataFinder?.metadata(for: structDescriptor) as StructMetadata? { + try print(metadata.fieldOffsets(for: structDescriptor, in: machO)) + } + } catch { + print(error) + } + case .class(let classDescriptor): + do { + let classType = try Class(descriptor: classDescriptor, in: machO) + try print(classType.dump(using: .test, in: machO).string) + if let metadata = try metadataFinder?.metadata(for: classDescriptor) as ClassMetadataObjCInterop? { + try print(metadata.fieldOffsets(for: classDescriptor, in: machO)) + } + } catch { + print(error) + } + } + default: + break + } + } + } + + @MachOImageGenerator + @MainActor + package func dumpAssociatedTypes(for machO: MachOFile) async throws { + let associatedTypeDescriptors = try machO.swift.associatedTypeDescriptors + for associatedTypeDescriptor in associatedTypeDescriptors { + try print(AssociatedType(descriptor: associatedTypeDescriptor, in: machO).dump(using: .test, in: machO).string) + } + } +} + + diff --git a/Sources/MachOTestingSupport/Extensions.swift b/Sources/MachOTestingSupport/Extensions.swift index bce55a3c..a6efe467 100644 --- a/Sources/MachOTestingSupport/Extensions.swift +++ b/Sources/MachOTestingSupport/Extensions.swift @@ -23,3 +23,9 @@ extension DyldCache { machOFile(by: .name(named.rawValue)) } } + +extension String { + package func print() { + Swift.print(self) + } +} diff --git a/Sources/MachOTestingSupport/MachOFileName.swift b/Sources/MachOTestingSupport/MachOFileName.swift index 45b3ad1a..08304812 100644 --- a/Sources/MachOTestingSupport/MachOFileName.swift +++ b/Sources/MachOTestingSupport/MachOFileName.swift @@ -2,11 +2,13 @@ package enum MachOFileName: String { case Finder = "/System/Library/CoreServices/Finder.app" case iPhoneMirroring = "/System/Applications/iPhone Mirroring.app" case ScreenContinuityUI = "/System/Applications/iPhone Mirroring.app/Contents/Frameworks/ScreenContinuityUI.framework" + case iOS_22E238_Simulator_SwiftUI = "/Library/Developer/CoreSimulator/Volumes/iOS_22E238/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 18.4.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/SwiftUI.framework" case iOS_22E238_Simulator_SwiftUICore = "/Library/Developer/CoreSimulator/Volumes/iOS_22E238/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 18.4.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/SwiftUICore.framework" case iOS_23A5260l_Simulator_SwiftUI = "/Library/Developer/CoreSimulator/Volumes/iOS_23A5260l/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 26.0.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/SwiftUI.framework" case iOS_23A5260l_Simulator_SwiftUICore = "/Library/Developer/CoreSimulator/Volumes/iOS_23A5260l/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 26.0.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/SwiftUICore.framework" case SourceEdit = "/Applications/SourceEdit.app" - case SourceEditor = "/Applications/SourceEdit.app/Contents/Frameworks/SourceEditor.framework" + case SourceEditorFromSourceEdit = "/Applications/SourceEdit.app/Contents/Frameworks/SourceEditor.framework" + case SourceEditorFromXcode = "/Applications/Xcode.app/Contents/SharedFrameworks/SourceEditor.framework" case ControlCenter = "/System/Library/CoreServices/ControlCenter.app" case Freeform = "/System/Applications/Freeform.app" } diff --git a/Tests/SwiftDumpTests/DemangleOptions.swift b/Tests/SwiftDumpTests/DemangleOptions.swift deleted file mode 100644 index 8a0699af..00000000 --- a/Tests/SwiftDumpTests/DemangleOptions.swift +++ /dev/null @@ -1,14 +0,0 @@ -import Foundation -import MachOSwiftSection -import SwiftDump - -let printOptions: DemangleOptions = { - var options = DemangleOptions.default - options.remove(.displayObjCModule) - options.insert(.synthesizeSugarOnTypes) - options.remove(.displayWhereClauses) - options.remove(.displayExtensionContexts) - options.remove(.showPrivateDiscriminators) - options.remove(.showModuleInDependentMemberType) - return options -}() diff --git a/Tests/SwiftDumpTests/DyldCacheDumpTests.swift b/Tests/SwiftDumpTests/DyldCacheDumpTests.swift new file mode 100644 index 00000000..4ecf4a36 --- /dev/null +++ b/Tests/SwiftDumpTests/DyldCacheDumpTests.swift @@ -0,0 +1,88 @@ +import Foundation +import Testing +import MachOKit +import MachOMacro +import MachOFoundation +@testable import MachOSwiftSection +@testable import SwiftDump +@testable import MachOTestingSupport + +@Suite(.serialized) +struct DyldCacheDumpTests: DumpableTest { + let mainCache: DyldCache + + let subCache: DyldCache + + let machOFileInMainCache: MachOFile + + let machOFileInSubCache: MachOFile + + let machOFileInCache: MachOFile + + let isEnabledSearchMetadata: Bool = false + + 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)) + } +} + + +extension DyldCacheDumpTests { + @Test func typesInCacheFile() async throws { + try await dumpTypes(for: machOFileInCache) + } + + @Test func typesInMainCacheFile() async throws { + try await dumpTypes(for: machOFileInMainCache) + } + + @Test func typesInSubCacheFile() async throws { + try await dumpTypes(for: machOFileInSubCache) + } + + @Test func protocolsInCacheFile() async throws { + try await dumpProtocols(for: machOFileInCache) + } + + @Test func protocolsInMainCacheFile() async throws { + try await dumpProtocols(for: machOFileInMainCache) + } + + @Test func protocolsInSubCacheFile() async throws { + try await dumpProtocols(for: machOFileInSubCache) + } + + @Test func protocolConformancesInCacheFile() async throws { + try await dumpProtocolConformances(for: machOFileInCache) + } + + @Test func protocolConformancesInMainCacheFile() async throws { + try await dumpProtocolConformances(for: machOFileInMainCache) + } + + @Test func protocolConformancesInSubCacheFile() async throws { + try await dumpProtocolConformances(for: machOFileInSubCache) + } + + @Test func associatedTypesInCacheFile() async throws { + try await dumpAssociatedTypes(for: machOFileInCache) + } + + @Test func associatedTypesInCacheMainFile() async throws { + try await dumpAssociatedTypes(for: machOFileInMainCache) + } + + @Test func associatedTypesInSubCacheFile() async throws { + try await dumpAssociatedTypes(for: machOFileInSubCache) + } +} diff --git a/Tests/SwiftDumpTests/MachOFileDumpTests.swift b/Tests/SwiftDumpTests/MachOFileDumpTests.swift new file mode 100644 index 00000000..b492406e --- /dev/null +++ b/Tests/SwiftDumpTests/MachOFileDumpTests.swift @@ -0,0 +1,45 @@ +import Foundation +import Testing +import MachOKit +import MachOMacro +import MachOFoundation +@testable import MachOSwiftSection +@testable import SwiftDump +@testable import MachOTestingSupport + +@Suite(.serialized) +struct MachOFileDumpTests: DumpableTest { + let machOFile: MachOFile + + let isEnabledSearchMetadata: Bool = false + + init() async throws { + let file = try loadFromFile(named: .iOS_22E238_Simulator_SwiftUI) + switch file { + case .fat(let fatFile): + self.machOFile = try #require(fatFile.machOFiles().first(where: { $0.header.cpu.subtype == .arm64(.arm64_all) })) + case .machO(let machO): + self.machOFile = machO + @unknown default: + fatalError() + } + } +} + +extension MachOFileDumpTests { + @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) + } +} diff --git a/Tests/SwiftDumpTests/MachOImageDumpTests.swift b/Tests/SwiftDumpTests/MachOImageDumpTests.swift new file mode 100644 index 00000000..735b9a27 --- /dev/null +++ b/Tests/SwiftDumpTests/MachOImageDumpTests.swift @@ -0,0 +1,37 @@ +import Foundation +import Testing +import MachOKit +import MachOMacro +import MachOFoundation +@testable import MachOSwiftSection +@testable import SwiftDump +@testable import MachOTestingSupport + +@Suite(.serialized) +struct MachOImageDumpTests: DumpableTest { + let machOImage: MachOImage + + let isEnabledSearchMetadata: Bool = false + + init() async throws { + self.machOImage = try #require(MachOImage(named: .Foundation)) + } +} + +extension MachOImageDumpTests { + @Test func typesInImage() async throws { + try await dumpTypes(for: machOImage) + } + + @Test func protocolsInImage() async throws { + try await dumpProtocols(for: machOImage) + } + + @Test func protocolConformancesInImage() async throws { + try await dumpProtocolConformances(for: machOImage) + } + + @Test func associatedTypesInImage() async throws { + try await dumpAssociatedTypes(for: machOImage) + } +} diff --git a/Tests/SwiftDumpTests/SwiftDumpTests.swift b/Tests/SwiftDumpTests/SwiftDumpTests.swift deleted file mode 100644 index 79b46208..00000000 --- a/Tests/SwiftDumpTests/SwiftDumpTests.swift +++ /dev/null @@ -1,239 +0,0 @@ -import Foundation -import Testing -import MachOKit -import MachOMacro -import MachOFoundation -@testable import MachOSwiftSection -@testable import SwiftDump -import MachOTestingSupport - -@Suite(.serialized) -struct DyldCacheDumpTests: DumpableTest { - let mainCache: DyldCache - - let subCache: DyldCache - - let machOFileInMainCache: MachOFile - - let machOFileInSubCache: MachOFile - - let machOFileInCache: MachOFile - - let isEnabledSearchMetadata: Bool = false - - 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)) - } -} - -@Suite(.serialized) -struct MachOFileDumpTests: DumpableTest { - let machOFile: MachOFile - - let isEnabledSearchMetadata: Bool = false - - init() async throws { - let file = try loadFromFile(named: .iOS_23A5260l_Simulator_SwiftUICore) - switch file { - case .fat(let fatFile): - self.machOFile = try #require(fatFile.machOFiles().first(where: { $0.header.cpu.subtype == .arm64(.arm64_all) })) - case .machO(let machO): - self.machOFile = machO - @unknown default: - fatalError() - } - } -} - -@Suite(.serialized) -struct MachOImageDumpTests: DumpableTest { - let machOImage: MachOImage - - let isEnabledSearchMetadata: Bool = false - - init() async throws { - self.machOImage = try #require(MachOImage(named: .Foundation)) - } -} - -extension DyldCacheDumpTests { - @Test func typesInCacheFile() async throws { - try await dumpTypes(for: machOFileInCache) - } - - @Test func typesInMainCacheFile() async throws { - try await dumpTypes(for: machOFileInMainCache) - } - - @Test func typesInSubCacheFile() async throws { - try await dumpTypes(for: machOFileInSubCache) - } - - @Test func protocolsInCacheFile() async throws { - try await dumpProtocols(for: machOFileInCache) - } - - @Test func protocolsInMainCacheFile() async throws { - try await dumpProtocols(for: machOFileInMainCache) - } - - @Test func protocolsInSubCacheFile() async throws { - try await dumpProtocols(for: machOFileInSubCache) - } - - @Test func protocolConformancesInCacheFile() async throws { - try await dumpProtocolConformances(for: machOFileInCache) - } - - @Test func protocolConformancesInMainCacheFile() async throws { - try await dumpProtocolConformances(for: machOFileInMainCache) - } - - @Test func protocolConformancesInSubCacheFile() async throws { - try await dumpProtocolConformances(for: machOFileInSubCache) - } - - @Test func associatedTypesInCacheFile() async throws { - try await dumpAssociatedTypes(for: machOFileInCache) - } - - @Test func associatedTypesInCacheMainFile() async throws { - try await dumpAssociatedTypes(for: machOFileInMainCache) - } - - @Test func associatedTypesInSubCacheFile() async throws { - try await dumpAssociatedTypes(for: machOFileInSubCache) - } -} - -extension MachOFileDumpTests { - @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) - } -} - -extension MachOImageDumpTests { - @Test func typesInImage() async throws { - try await dumpTypes(for: machOImage) - } - - @Test func protocolsInImage() async throws { - try await dumpProtocols(for: machOImage) - } - - @Test func protocolConformancesInImage() async throws { - try await dumpProtocolConformances(for: machOImage) - } - - @Test func associatedTypesInImage() async throws { - try await dumpAssociatedTypes(for: machOImage) - } -} - -protocol DumpableTest { - var isEnabledSearchMetadata: Bool { get } -} - -extension DumpableTest { - @MachOImageGenerator - @MainActor - func dumpProtocols(for machO: MachOFile) async throws { - let protocolDescriptors = try machO.swift.protocolDescriptors - for protocolDescriptor in protocolDescriptors { - try print(Protocol(descriptor: protocolDescriptor, in: machO).dump(using: printOptions, in: machO).string) - } - } - - @MachOImageGenerator - @MainActor - func dumpProtocolConformances(for machO: MachOFile) async throws { - let protocolConformanceDescriptors = try machO.swift.protocolConformanceDescriptors - - for protocolConformanceDescriptor in protocolConformanceDescriptors { - try print(ProtocolConformance(descriptor: protocolConformanceDescriptor, in: machO).dump(using: printOptions, in: machO).string) - } - } - - @MachOImageGenerator - @MainActor - func dumpTypes(for machO: MachOFile) async throws { - let typeContextDescriptors = try machO.swift.typeContextDescriptors - var metadataFinder: MetadataFinder? - if isEnabledSearchMetadata { - metadataFinder = MetadataFinder(machO: machO) - } - for typeContextDescriptor in typeContextDescriptors { - switch typeContextDescriptor { - case .type(let typeContextDescriptorWrapper): - switch typeContextDescriptorWrapper { - case .enum(let enumDescriptor): - do { - let enumType = try Enum(descriptor: enumDescriptor, in: machO) - try print(enumType.dump(using: printOptions, in: machO).string) - } catch { - print(error) - } - case .struct(let structDescriptor): - do { - let structType = try Struct(descriptor: structDescriptor, in: machO) - try print(structType.dump(using: printOptions, in: machO).string) - if let metadata = try metadataFinder?.metadata(for: structDescriptor) as StructMetadata? { - try print(metadata.fieldOffsets(for: structDescriptor, in: machO)) - } - } catch { - print(error) - } - case .class(let classDescriptor): - do { - let classType = try Class(descriptor: classDescriptor, in: machO) - try print(classType.dump(using: printOptions, in: machO).string) - if let metadata = try metadataFinder?.metadata(for: classDescriptor) as ClassMetadataObjCInterop? { - try print(metadata.fieldOffsets(for: classDescriptor, in: machO)) - } - } catch { - print(error) - } - } - default: - break - } - } - } - - @MachOImageGenerator - @MainActor - func dumpAssociatedTypes(for machO: MachOFile) async throws { - let associatedTypeDescriptors = try machO.swift.associatedTypeDescriptors - for associatedTypeDescriptor in associatedTypeDescriptors { - try print(AssociatedType(descriptor: associatedTypeDescriptor, in: machO).dump(using: printOptions, in: machO).string) - } - } -} - -extension String { - func print() { - Swift.print(self) - } -} From 10f3bcacc9bff7618a3154ce86d9f29e4f820889 Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Sun, 22 Jun 2025 17:08:37 +0800 Subject: [PATCH 05/16] Switch swift-tools-version to 6.1 --- Package.swift | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Package.swift b/Package.swift index 6e5e2ce2..cd9d9ebc 100644 --- a/Package.swift +++ b/Package.swift @@ -1,7 +1,7 @@ -// swift-tools-version: 5.10 +// swift-tools-version: 6.1 // The swift-tools-version declares the minimum version of Swift required to build this package. -import PackageDescription +@preconcurrency import PackageDescription import CompilerPluginSupport let useSPMPrebuildVersion = false @@ -89,7 +89,7 @@ let package = Package( ], dependencies: [ .MachOKit, - .package(url: "https://github.com/swiftlang/swift-syntax.git", "509.1.0"..<"602.0.0"), + .package(url: "https://github.com/swiftlang/swift-syntax.git", "509.1.0" ..< "602.0.0"), .package(url: "https://github.com/p-x9/AssociatedObject", from: "0.13.0"), .package(url: "https://github.com/p-x9/swift-fileio.git", from: "0.9.0"), .package(url: "https://github.com/apple/swift-argument-parser", from: "1.5.1"), @@ -159,6 +159,7 @@ let package = Package( dependencies: [ .MachOKit, "MachOExtensions", + "SwiftDump", ] ), @@ -220,5 +221,6 @@ let package = Package( "MachOTestingSupport", ] ), - ] + ], + swiftLanguageModes: [.v5] ) From 91ef488eb8765891614f0c4fde83ceb8eb250ddf Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Sun, 22 Jun 2025 17:09:28 +0800 Subject: [PATCH 06/16] Refactor SemanticType --- Sources/Demangle/Main/NodePrinter.swift | 41 +++++++++++++++---- .../Components/FunctionDeclaration.swift | 2 +- .../Semantic/Components/FunctionName.swift | 2 +- .../Components/MemberDeclaration.swift | 2 +- Sources/Semantic/Components/MemberName.swift | 2 +- .../Semantic/Components/TypeDeclaration.swift | 7 +++- Sources/Semantic/Components/TypeName.swift | 7 +++- Sources/Semantic/SemanticString.swift | 6 +++ Sources/Semantic/SemanticType.swift | 22 ++++++---- 9 files changed, 67 insertions(+), 24 deletions(-) diff --git a/Sources/Demangle/Main/NodePrinter.swift b/Sources/Demangle/Main/NodePrinter.swift index 3e7660f7..7e9d6034 100644 --- a/Sources/Demangle/Main/NodePrinter.swift +++ b/Sources/Demangle/Main/NodePrinter.swift @@ -5,7 +5,7 @@ struct NodePrinter: Sendable { var specializationPrefixPrinted: Bool var options: DemangleOptions var hidingCurrentModule: String = "" - + init(options: DemangleOptions = .default) { self.target = .init() self.specializationPrefixPrinted = false @@ -16,7 +16,7 @@ struct NodePrinter: Sendable { guard options.contains(.qualifyEntities) else { return false } - if !options.contains(.showModuleInDependentMemberType), let dependentMemberType = context.parent?.parent?.parent?.parent, dependentMemberType.kind == .dependentMemberType { + 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 { @@ -844,7 +844,7 @@ struct NodePrinter: Sendable { mutating func printImplDifferentiabilityKind(_ name: Node) { target.write("@differentiable") - if case let .index(value) = name.contents, let differentiability = Differentiability(value) { + if case .index(let value) = name.contents, let differentiability = Differentiability(value) { switch differentiability { case .normal: break case .linear: target.write("(_linear)") @@ -855,7 +855,7 @@ struct NodePrinter: Sendable { } mutating func printImplCoroutineKind(_ name: Node) { - guard case let .name(value) = name.contents, !value.isEmpty else { return } + guard case .name(let value) = name.contents, !value.isEmpty else { return } target.write("@\(value)") } @@ -872,7 +872,7 @@ struct NodePrinter: Sendable { } mutating func printImplParameterName(_ name: Node) { - guard case let .name(value) = name.contents, !value.isEmpty else { return } + guard case .name(let value) = name.contents, !value.isEmpty else { return } target.write("\(value) ") } @@ -1179,7 +1179,7 @@ struct NodePrinter: Sendable { case .globalVariableOnceFunction, .globalVariableOnceToken: printGlobalVariableOnceFunction(name) case .hasSymbolQuery: target.write("#_hasSymbol query for ") - case .identifier: target.write(name.text ?? "", type: (name.parent?.kind == .function || name.parent?.kind == .variable) ? .functionDeclaration : .typeName) + case .identifier: printIdentifier(name, asPrefixContext: asPrefixContext) case .implConvention: target.write(name.text ?? "") case .implCoroutineKind: printImplCoroutineKind(name) case .implDifferentiabilityKind: printImplDifferentiabilityKind(name) @@ -1399,6 +1399,29 @@ struct NodePrinter: Sendable { return nil } + mutating func printIdentifier(_ name: Node, asPrefixContext: Bool = false) { + let semanticType: SemanticType + + 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 + } + + target.write(name.text ?? "", type: semanticType) + } + mutating func printAbstractStorage(_ name: Node?, asPrefixContext: Bool, extraName: String) -> Node? { guard let n = name else { return nil } switch n.kind { @@ -1605,17 +1628,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: .functionDeclaration) + target.write(label.kind == .identifier ? (label.text ?? "") : "_", type: .function(.declaration)) 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: .functionDeclaration) + target.write(label.text ?? "", type: .function(.declaration)) target.write(":") } else { - target.write("_", type: .functionDeclaration) + target.write("_", type: .function(.declaration)) target.write(":") } } diff --git a/Sources/Semantic/Components/FunctionDeclaration.swift b/Sources/Semantic/Components/FunctionDeclaration.swift index 09e65f59..c08629e2 100644 --- a/Sources/Semantic/Components/FunctionDeclaration.swift +++ b/Sources/Semantic/Components/FunctionDeclaration.swift @@ -1,7 +1,7 @@ public struct FunctionDeclaration: SemanticStringComponent { public private(set) var string: String - public var type: SemanticType { .functionDeclaration } + public var type: SemanticType { .function(.declaration) } public init(_ string: String) { self.string = string diff --git a/Sources/Semantic/Components/FunctionName.swift b/Sources/Semantic/Components/FunctionName.swift index 01a0c4df..555f8b05 100644 --- a/Sources/Semantic/Components/FunctionName.swift +++ b/Sources/Semantic/Components/FunctionName.swift @@ -1,7 +1,7 @@ public struct FunctionName: SemanticStringComponent { public private(set) var string: String - public var type: SemanticType { .functionName } + public var type: SemanticType { .function(.name) } public init(_ string: String) { self.string = string diff --git a/Sources/Semantic/Components/MemberDeclaration.swift b/Sources/Semantic/Components/MemberDeclaration.swift index 90e64c8a..1361518e 100644 --- a/Sources/Semantic/Components/MemberDeclaration.swift +++ b/Sources/Semantic/Components/MemberDeclaration.swift @@ -1,7 +1,7 @@ public struct MemberDeclaration: SemanticStringComponent { public private(set) var string: String - public var type: SemanticType { .memberDeclaration } + public var type: SemanticType { .member(.declaration) } public init(_ string: String) { self.string = string diff --git a/Sources/Semantic/Components/MemberName.swift b/Sources/Semantic/Components/MemberName.swift index efc21c0c..5b83c80f 100644 --- a/Sources/Semantic/Components/MemberName.swift +++ b/Sources/Semantic/Components/MemberName.swift @@ -1,7 +1,7 @@ public struct MemberName: SemanticStringComponent { public private(set) var string: String - public var type: SemanticType { .memberName } + public var type: SemanticType { .member(.name) } public init(_ string: String) { self.string = string diff --git a/Sources/Semantic/Components/TypeDeclaration.swift b/Sources/Semantic/Components/TypeDeclaration.swift index fa561327..09d3571b 100644 --- a/Sources/Semantic/Components/TypeDeclaration.swift +++ b/Sources/Semantic/Components/TypeDeclaration.swift @@ -1,9 +1,12 @@ public struct TypeDeclaration: SemanticStringComponent { public private(set) var string: String - public var type: SemanticType { .typeDeclaration } + public let kind: SemanticType.TypeKind + + public var type: SemanticType { .type(kind, .declaration) } - public init(_ string: String) { + public init(kind: SemanticType.TypeKind, _ string: String) { + self.kind = kind self.string = string } } diff --git a/Sources/Semantic/Components/TypeName.swift b/Sources/Semantic/Components/TypeName.swift index 391ca864..c2ae1dd6 100644 --- a/Sources/Semantic/Components/TypeName.swift +++ b/Sources/Semantic/Components/TypeName.swift @@ -1,9 +1,12 @@ public struct TypeName: SemanticStringComponent { public private(set) var string: String - public var type: SemanticType { .typeName } + public let kind: SemanticType.TypeKind + + public var type: SemanticType { .type(kind, .name) } - public init(_ string: String) { + public init(kind: SemanticType.TypeKind, _ string: String) { + self.kind = kind self.string = string } } diff --git a/Sources/Semantic/SemanticString.swift b/Sources/Semantic/SemanticString.swift index eb9ca2b1..6e109084 100644 --- a/Sources/Semantic/SemanticString.swift +++ b/Sources/Semantic/SemanticString.swift @@ -43,6 +43,12 @@ public struct SemanticString: Sendable, TextOutputStream, Codable { .init(components: components.map { modifier($0) }) } + public func replacing(_ transform: (SemanticType) -> SemanticType) -> SemanticString { + map { component in + return AnyComponent(string: component.string, type: transform(component.type)) + } + } + public func replacing(from types: SemanticType..., to newType: SemanticType) -> SemanticString { map { component in if types.contains(component.type) { diff --git a/Sources/Semantic/SemanticType.swift b/Sources/Semantic/SemanticType.swift index 858b99f9..431234d1 100644 --- a/Sources/Semantic/SemanticType.swift +++ b/Sources/Semantic/SemanticType.swift @@ -1,16 +1,24 @@ -public enum SemanticType: CaseIterable, Codable, Sendable { +public enum SemanticType: Hashable, Codable, Sendable { + public enum TypeKind: CaseIterable, Hashable, Codable, Sendable { + case `enum` + case `struct` + case `class` + case `protocol` + case other + } + public enum Context: CaseIterable, Hashable, Codable, Sendable { + case declaration + case name + } case standard case comment case keyword case variable - case typeName case numeric case argument case error - case typeDeclaration - case memberDeclaration - case memberName - case functionName - case functionDeclaration + case type(TypeKind, Context) + case member(Context) + case function(Context) case other } From 78d5d10ca8884dbe09dbb5ebd90b0ba0be83473b Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Sun, 22 Jun 2025 17:11:47 +0800 Subject: [PATCH 07/16] Fix MachOImage issue of symbol not found --- .../MachORepresentableWithCache.swift | 22 ++++++++++++++++--- .../Symbol/MachOSymbolCache.swift | 16 +++++++++----- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/Sources/MachOExtensions/MachORepresentableWithCache.swift b/Sources/MachOExtensions/MachORepresentableWithCache.swift index 3a53e12a..5bdb45ba 100644 --- a/Sources/MachOExtensions/MachORepresentableWithCache.swift +++ b/Sources/MachOExtensions/MachORepresentableWithCache.swift @@ -1,7 +1,9 @@ import MachOKit package protocol MachORepresentableWithCache: MachORepresentable { - var cache: DyldCache? { get } + associatedtype Cache: DyldCacheRepresentable + + var cache: Cache? { get } var startOffset: Int { get } } @@ -16,8 +18,22 @@ extension MachOFile: MachORepresentableWithCache { } extension MachOImage: MachORepresentableWithCache { - package var cache: DyldCache? { nil } - package var startOffset: Int { 0 } + package var cache: DyldCacheLoaded? { + guard let currentCache = DyldCacheLoaded.current else { return nil } + + if ptr.int - currentCache.mainCacheHeader.sharedRegionStart.cast() >= 0 { + return currentCache + } + return nil + } + + package var startOffset: Int { + if let cache { + return cache.mainCacheHeader.sharedRegionStart.cast() + } else { + return 0 + } + } } package func address(of fileOffset: Int, in machO: MachO) -> UInt64 { diff --git a/Sources/MachOPointer/Symbol/MachOSymbolCache.swift b/Sources/MachOPointer/Symbol/MachOSymbolCache.swift index f3d2a461..cda94418 100644 --- a/Sources/MachOPointer/Symbol/MachOSymbolCache.swift +++ b/Sources/MachOPointer/Symbol/MachOSymbolCache.swift @@ -34,14 +34,16 @@ class MachOSymbolCache { var cacheEntry: CacheEntry = [:] for symbol in symbols64 where !symbol.name.isEmpty { var offset = symbol.offset + cacheEntry[offset] = .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] = .init(offset: offset, stringValue: symbol.name) } for exportedSymbol in machO.exportedSymbols { if var offset = exportedSymbol.offset { + cacheEntry[offset] = .init(offset: offset, stringValue: exportedSymbol.name) offset += machO.startOffset cacheEntry[offset] = .init(offset: offset, stringValue: exportedSymbol.name) } @@ -78,15 +80,19 @@ class MachOSymbolCache { func symbol(for offset: Int, in machOImage: MachOImage) -> MachOSymbol? { let identifier = CacheIdentifier.image(machOImage.ptr) - return symbol(for: offset, with: identifier) + return symbol(for: offset, with: identifier, in: machOImage) } func symbol(for offset: Int, in machOFile: MachOFile) -> MachOSymbol? { let identifier = CacheIdentifier.file(machOFile.imagePath) - return symbol(for: offset, with: identifier) + return symbol(for: offset, with: identifier, in: machOFile) } - private func symbol(for offset: Int, with identifier: CacheIdentifier) -> MachOSymbol? { - return entryByIdentifier[identifier]?[offset] + private func symbol(for offset: Int, with identifier: CacheIdentifier, in machO: MachO) -> MachOSymbol? { + if let symbol = entryByIdentifier[identifier, default: [:]][offset] { + return symbol + } else { + return nil + } } } From 302a731f6d6f09baf4c5301e671756f951ab1651 Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Sun, 22 Jun 2025 17:12:05 +0800 Subject: [PATCH 08/16] Optimize SwiftDump --- .../Models/Protocol/Protocol.swift | 2 +- .../Models/Type/Class/Class.swift | 1 + .../Dumpable+/AssociatedType+Dumpable.swift | 6 +-- .../SwiftDump/Dumpable+/Class+Dumpable.swift | 6 ++- .../Dumpable+/OpaqueType+Dumpable.swift | 39 ------------------- .../Dumpable+/Protocol+Dumpable.swift | 7 +++- .../ProtocolConformance+Dumpable.swift | 20 ++++++++-- Sources/SwiftDump/Dumpable.swift | 15 +++++-- .../Extensions/GenericContext+Dump.swift | 4 +- .../SwiftDump/Extensions/Keyword+Swift.swift | 1 + .../Extensions/ResilientSuperclass+Dump.swift | 2 +- Sources/swift-section/DumpCommand.swift | 2 +- Sources/swift-section/Extensions.swift | 25 ++++++------ 13 files changed, 60 insertions(+), 70 deletions(-) delete mode 100644 Sources/SwiftDump/Dumpable+/OpaqueType+Dumpable.swift diff --git a/Sources/MachOSwiftSection/Models/Protocol/Protocol.swift b/Sources/MachOSwiftSection/Models/Protocol/Protocol.swift index 06316c01..27cb91ea 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` { public enum Error: Swift.Error { case invalidProtocolDescriptor } diff --git a/Sources/MachOSwiftSection/Models/Type/Class/Class.swift b/Sources/MachOSwiftSection/Models/Type/Class/Class.swift index bd6d1c24..a943e116 100644 --- a/Sources/MachOSwiftSection/Models/Type/Class/Class.swift +++ b/Sources/MachOSwiftSection/Models/Type/Class/Class.swift @@ -1,6 +1,7 @@ import Foundation import MachOKit import MachOMacro +import MachOFoundation // template // class swift_ptrauth_struct_context_descriptor(ClassDescriptor) diff --git a/Sources/SwiftDump/Dumpable+/AssociatedType+Dumpable.swift b/Sources/SwiftDump/Dumpable+/AssociatedType+Dumpable.swift index 9780d490..afce6449 100644 --- a/Sources/SwiftDump/Dumpable+/AssociatedType+Dumpable.swift +++ b/Sources/SwiftDump/Dumpable+/AssociatedType+Dumpable.swift @@ -4,11 +4,11 @@ import MachOSwiftSection import MachOMacro import Semantic -extension AssociatedType: Dumpable { +extension AssociatedType: ConformedDumpable { @MachOImageGenerator @SemanticStringBuilder public func dumpTypeName(using options: DemangleOptions, in machOFile: MachOFile) throws -> SemanticString { - try MetadataReader.demangleSymbol(for: conformingTypeName, in: machOFile).printSemantic(using: options).replacing(from: .typeName, .other, to: .typeDeclaration) + try MetadataReader.demangleSymbol(for: conformingTypeName, in: machOFile).printSemantic(using: options).replacingTypeNameOrOtherToTypeDeclaration() } @MachOImageGenerator @@ -45,7 +45,7 @@ extension AssociatedType: Dumpable { Space() - try TypeDeclaration(record.name(in: machOFile)) + try TypeDeclaration(kind: .other, record.name(in: machOFile)) Space() diff --git a/Sources/SwiftDump/Dumpable+/Class+Dumpable.swift b/Sources/SwiftDump/Dumpable+/Class+Dumpable.swift index 9f40b719..f6d180bf 100644 --- a/Sources/SwiftDump/Dumpable+/Class+Dumpable.swift +++ b/Sources/SwiftDump/Dumpable+/Class+Dumpable.swift @@ -14,7 +14,11 @@ extension Class: NamedDumpable { @MachOImageGenerator @SemanticStringBuilder public func dump(using options: DemangleOptions, in machOFile: MachOFile) throws -> SemanticString { - Keyword(.class) + if descriptor.isActor { + Keyword(.actor) + } else { + Keyword(.class) + } Space() diff --git a/Sources/SwiftDump/Dumpable+/OpaqueType+Dumpable.swift b/Sources/SwiftDump/Dumpable+/OpaqueType+Dumpable.swift deleted file mode 100644 index 99583242..00000000 --- a/Sources/SwiftDump/Dumpable+/OpaqueType+Dumpable.swift +++ /dev/null @@ -1,39 +0,0 @@ -import Foundation -import MachOKit -import MachOSwiftSection -import MachOMacro - -//extension OpaqueType: Dumpable { -// @MachOImageGenerator -// @StringBuilder -// public func dump(using options: SymbolPrintOptions, in machOFile: MachOFile) throws -> String { -// try "opaquetype \(descriptor.dumpFullname(using: options, in: machOFile))" -// for underlyingTypeArgumentMangledName in self.underlyingTypeArgumentMangledNames { -// try MetadataReader.demangleType(for: underlyingTypeArgumentMangledName, in: machOFile).print(using: options) -// } -// } -// -// -// -//} -// -//@MachOImageAllMembersGenerator -//extension OpaqueTypeDescriptorProtocol { -// func dumpFullname(using options: SymbolPrintOptions, in machOFile: MachOFile) throws -> String { -// var name = "" -// var parent = try parent(in: machOFile) -// findParent: while let currnetParent = parent { -// switch currnetParent { -// case .symbol(let unsolvedSymbol): -// name = unsolvedSymbol.stringValue + "." + name -// break findParent -// case .element(let element): -// if let parentName = try element.dumpName(using: options, in: machOFile) { -// name = parentName + "." + name -// } -// parent = try element.contextDescriptor.parent(in: machOFile) -// } -// } -// return name -// } -//} diff --git a/Sources/SwiftDump/Dumpable+/Protocol+Dumpable.swift b/Sources/SwiftDump/Dumpable+/Protocol+Dumpable.swift index 41bd1de5..082cdbae 100644 --- a/Sources/SwiftDump/Dumpable+/Protocol+Dumpable.swift +++ b/Sources/SwiftDump/Dumpable+/Protocol+Dumpable.swift @@ -3,11 +3,12 @@ import MachOKit import MachOSwiftSection import MachOMacro import Semantic +import MachOFoundation extension MachOSwiftSection.`Protocol`: NamedDumpable { @MachOImageGenerator public func dumpName(using options: DemangleOptions, in machOFile: MachOFile) throws -> SemanticString { - try MetadataReader.demangleContext(for: .protocol(descriptor), in: machOFile).printSemantic(using: options).replacing(from: .typeName, .other, to: .typeDeclaration) + try MetadataReader.demangleContext(for: .protocol(descriptor), in: machOFile).printSemantic(using: options).replacingTypeNameOrOtherToTypeDeclaration() } @MachOImageGenerator @@ -43,7 +44,7 @@ extension MachOSwiftSection.`Protocol`: NamedDumpable { Indent(level: 1) Keyword(.associatedtype) Space() - TypeDeclaration(associatedType) + TypeDeclaration(kind: .other, associatedType) if offset.isEnd { BreakLine() } @@ -56,6 +57,8 @@ extension MachOSwiftSection.`Protocol`: NamedDumpable { if let symbol = try requirement.defaultImplementationSymbol(in: machOFile) { InlineComment("[Default Implementation]") try MetadataReader.demangleSymbol(for: symbol, in: machOFile).printSemantic(using: options) + } else if let symbol = try MachOSymbol.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+/ProtocolConformance+Dumpable.swift b/Sources/SwiftDump/Dumpable+/ProtocolConformance+Dumpable.swift index 6f50eda9..88f0634d 100644 --- a/Sources/SwiftDump/Dumpable+/ProtocolConformance+Dumpable.swift +++ b/Sources/SwiftDump/Dumpable+/ProtocolConformance+Dumpable.swift @@ -2,9 +2,10 @@ import Foundation import MachOKit import MachOSwiftSection import MachOMacro +import MachOFoundation import Semantic -extension ProtocolConformance: Dumpable { +extension ProtocolConformance: ConformedDumpable { @MachOImageGenerator @SemanticStringBuilder public func dumpTypeName(using options: DemangleOptions, in machOFile: MachOFile) throws -> SemanticString { @@ -21,7 +22,7 @@ extension ProtocolConformance: Dumpable { Standard("") } case .directObjCClassName(let objcClassName): - TypeDeclaration(objcClassName.valueOrEmpty) + TypeDeclaration(kind: .class, objcClassName.valueOrEmpty) case .indirectObjCClass(let objcClass): switch objcClass { case .symbol(let unsolvedSymbol): @@ -85,7 +86,9 @@ extension ProtocolConformance: Dumpable { Indent(level: 1) if let symbol = try resilientWitness.implementationSymbol(in: machOFile) { - try MetadataReader.demangleSymbol(for: symbol, in: machOFile).printSemantic(using: options) + 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 { case .symbol(let symbol): @@ -107,6 +110,15 @@ extension ProtocolConformance: Dumpable { extension SemanticString { func replacingTypeNameOrOtherToTypeDeclaration() -> SemanticString { - self.replacing(from: .typeName, .other, to: .typeDeclaration) + replacing { + switch $0 { + case .type(let type, .name): + return .type(type, .declaration) + case .other: + return .type(.other, .declaration) + default: + return $0 + } + } } } diff --git a/Sources/SwiftDump/Dumpable.swift b/Sources/SwiftDump/Dumpable.swift index ab060969..c1ecb7e4 100644 --- a/Sources/SwiftDump/Dumpable.swift +++ b/Sources/SwiftDump/Dumpable.swift @@ -5,11 +5,18 @@ import Semantic public typealias DemangleOptions = Demangle.DemangleOptions public protocol Dumpable { - func dump(using options: DemangleOptions, in machOFile: MachOFile) throws -> SemanticString - func dump(using options: DemangleOptions, in machOImage: MachOImage) throws -> SemanticString + func dump(using options: DemangleOptions, in machO: MachOFile) throws -> SemanticString + func dump(using options: DemangleOptions, in machO: MachOImage) throws -> SemanticString } public protocol NamedDumpable: Dumpable { - func dumpName(using options: DemangleOptions, in machOFile: MachOFile) throws -> SemanticString - func dumpName(using options: DemangleOptions, in machOImage: MachOImage) throws -> SemanticString + func dumpName(using options: DemangleOptions, in machO: MachOFile) throws -> SemanticString + func dumpName(using options: DemangleOptions, in machO: MachOImage) throws -> SemanticString +} + +public protocol ConformedDumpable: Dumpable { + func dumpTypeName(using options: DemangleOptions, in machO: MachOFile) throws -> SemanticString + func dumpProtocolName(using options: DemangleOptions, in machO: MachOFile) throws -> SemanticString + func dumpTypeName(using options: DemangleOptions, in machO: MachOImage) throws -> SemanticString + func dumpProtocolName(using options: DemangleOptions, in machO: MachOImage) throws -> SemanticString } diff --git a/Sources/SwiftDump/Extensions/GenericContext+Dump.swift b/Sources/SwiftDump/Extensions/GenericContext+Dump.swift index d813ebb1..672d0054 100644 --- a/Sources/SwiftDump/Extensions/GenericContext+Dump.swift +++ b/Sources/SwiftDump/Extensions/GenericContext+Dump.swift @@ -67,7 +67,7 @@ extension GenericRequirementDescriptor { case .element(let element): switch element { case .objc(let objc): - TypeName(try objc.mangledName(in: machOFile).rawStringValue()) + TypeName(kind: .protocol, try objc.mangledName(in: machOFile).rawStringValue()) case .swift(let protocolDescriptor): try MetadataReader.demangleContext(for: .protocol(protocolDescriptor), in: machOFile).printSemantic(using: options) } @@ -75,7 +75,7 @@ extension GenericRequirementDescriptor { case .layout(let genericRequirementLayoutKind): switch genericRequirementLayoutKind { case .class: - TypeName("AnyObject") + TypeName(kind: .other, "AnyObject") } case .conformance /* (let protocolConformanceDescriptor) */: Standard("") diff --git a/Sources/SwiftDump/Extensions/Keyword+Swift.swift b/Sources/SwiftDump/Extensions/Keyword+Swift.swift index d73cedee..2bade040 100644 --- a/Sources/SwiftDump/Extensions/Keyword+Swift.swift +++ b/Sources/SwiftDump/Extensions/Keyword+Swift.swift @@ -6,6 +6,7 @@ extension Keyword { case `extension` case `typealias` case `class` + case `actor` case `struct` case `enum` case `lazy` diff --git a/Sources/SwiftDump/Extensions/ResilientSuperclass+Dump.swift b/Sources/SwiftDump/Extensions/ResilientSuperclass+Dump.swift index c0f9d869..43a45bcd 100644 --- a/Sources/SwiftDump/Extensions/ResilientSuperclass+Dump.swift +++ b/Sources/SwiftDump/Extensions/ResilientSuperclass+Dump.swift @@ -21,7 +21,7 @@ extension ResilientSuperclass { return nil } case .directObjCClassName(let string): - return string.map { SemanticString(components: TypeName($0)) } + return string.map { SemanticString(components: TypeName(kind: .class, $0)) } case .indirectObjCClass(let resolvableElement): switch resolvableElement { case .symbol(let unsolvedSymbol): diff --git a/Sources/swift-section/DumpCommand.swift b/Sources/swift-section/DumpCommand.swift index 23dbd350..dcebc0d4 100644 --- a/Sources/swift-section/DumpCommand.swift +++ b/Sources/swift-section/DumpCommand.swift @@ -21,7 +21,7 @@ final actor DumpCommand: AsyncParsableCommand { @Option(name: .shortAndLong, help: "The output path for the dump. If not specified, the output will be printed to the console.", completion: .file()) var outputPath: String? - @Option(name: .shortAndLong, parsing: .remaining, help: "The sections to dump. If not specified, all sections will be dumped.") + @Option(name: .shortAndLong, parsing: .upToNextOption, help: "The sections to dump. If not specified, all sections will be dumped.") var sections: [SwiftSection] = SwiftSection.allCases // @Flag(inversion: .prefixedEnableDisable, help: "Enable searching for metadata.") diff --git a/Sources/swift-section/Extensions.swift b/Sources/swift-section/Extensions.swift index ea3fed26..d1ffbf99 100644 --- a/Sources/swift-section/Extensions.swift +++ b/Sources/swift-section/Extensions.swift @@ -52,15 +52,16 @@ extension String { return "#56606B" case .keyword: return "#C33381" - case .typeName: + case .type(_, .name): return "#2E0D6E" - case .typeDeclaration: + case .type(_, .declaration): return "#004975" - case .functionName, - .memberName: + case .function(.name), + .member(.name): return "#5C2699" - case .functionDeclaration, - .memberDeclaration: + case .function(.declaration), + .member(.declaration), + .variable: return "#0F68A0" case .numeric: return "#000BFF" @@ -73,15 +74,15 @@ extension String { return "#6C7987" case .keyword: return "#F2248C" - case .typeName: + case .type(_, .name): return "#D0A8FF" - case .typeDeclaration: + case .type(_, .declaration): return "#5DD8FF" - case .functionName, - .memberName: + case .function(.name), + .member(.name): return "#A167E6" - case .functionDeclaration, - .memberDeclaration: + case .function(.declaration), + .member(.declaration): return "#41A1C0" case .numeric: return "#D0BF69" From cd32c2fab4e5d0541bf7bf2180b02908b9ecc831 Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Sun, 22 Jun 2025 17:33:00 +0800 Subject: [PATCH 09/16] Update Testing --- Tests/SwiftDumpTests/MachOFileDumpTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/SwiftDumpTests/MachOFileDumpTests.swift b/Tests/SwiftDumpTests/MachOFileDumpTests.swift index b492406e..4b595f28 100644 --- a/Tests/SwiftDumpTests/MachOFileDumpTests.swift +++ b/Tests/SwiftDumpTests/MachOFileDumpTests.swift @@ -14,7 +14,7 @@ struct MachOFileDumpTests: DumpableTest { let isEnabledSearchMetadata: Bool = false init() async throws { - let file = try loadFromFile(named: .iOS_22E238_Simulator_SwiftUI) + 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(.arm64_all) })) From 4b917778413020817c26278a1f629106929cba5f Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Sun, 22 Jun 2025 17:54:33 +0800 Subject: [PATCH 10/16] Update Testing --- Package.swift | 29 +++++++++++++++++-- .../MachOTestingSupport/DemangleOptions.swift | 2 +- .../MachOTestingSupport/DumpableTest.swift | 23 +++++++-------- Sources/MachOTestingSupport/Extensions.swift | 8 ++++- .../MetadataFinderTests.swift | 10 +++---- Tests/SwiftDumpTests/DyldCacheDumpTests.swift | 1 - 6 files changed, 50 insertions(+), 23 deletions(-) diff --git a/Package.swift b/Package.swift index cd9d9ebc..83b805dd 100644 --- a/Package.swift +++ b/Package.swift @@ -4,7 +4,28 @@ @preconcurrency import PackageDescription import CompilerPluginSupport -let useSPMPrebuildVersion = false +func envEnable(_ key: String, default defaultValue: Bool = false) -> Bool { + guard let value = Context.environment[key] else { + return defaultValue + } + if value == "1" { + return true + } else if value == "0" { + return false + } else { + return defaultValue + } +} + +let isSilentTest = envEnable("MACHO_SWIFT_SECTION_SILENT_TEST", default: false) + +let useSPMPrebuildVersion = envEnable("MACHO_SWIFT_SECTION_USE_SPM_PREBUILD_VERSION", default: false) + +var testSettings: [SwiftSetting] = [] + +if isSilentTest { + testSettings.append(.define("SILENT_TEST")) +} extension Package.Dependency { static let MachOKit: Package.Dependency = { @@ -211,7 +232,8 @@ let package = Package( "MachOSwiftSection", "SwiftDump", "MachOTestingSupport", - ] + ], + swiftSettings: testSettings ), .testTarget( @@ -219,7 +241,8 @@ let package = Package( dependencies: [ "SwiftDump", "MachOTestingSupport", - ] + ], + swiftSettings: testSettings ), ], swiftLanguageModes: [.v5] diff --git a/Sources/MachOTestingSupport/DemangleOptions.swift b/Sources/MachOTestingSupport/DemangleOptions.swift index 6665b992..2cb25a75 100644 --- a/Sources/MachOTestingSupport/DemangleOptions.swift +++ b/Sources/MachOTestingSupport/DemangleOptions.swift @@ -3,7 +3,7 @@ import MachOSwiftSection import SwiftDump extension DemangleOptions { - static let test: DemangleOptions = { + package static let test: DemangleOptions = { var options = DemangleOptions.default options.remove(.displayObjCModule) options.insert(.synthesizeSugarOnTypes) diff --git a/Sources/MachOTestingSupport/DumpableTest.swift b/Sources/MachOTestingSupport/DumpableTest.swift index fd95abe0..aea8de6f 100644 --- a/Sources/MachOTestingSupport/DumpableTest.swift +++ b/Sources/MachOTestingSupport/DumpableTest.swift @@ -4,7 +4,6 @@ import MachOMacro import MachOFoundation import MachOSwiftSection import SwiftDump -import MachOTestingSupport package protocol DumpableTest { var isEnabledSearchMetadata: Bool { get } @@ -16,7 +15,7 @@ extension DumpableTest { package func dumpProtocols(for machO: MachOFile) async throws { let protocolDescriptors = try machO.swift.protocolDescriptors for protocolDescriptor in protocolDescriptors { - try print(Protocol(descriptor: protocolDescriptor, in: machO).dump(using: .test, in: machO).string) + try Protocol(descriptor: protocolDescriptor, in: machO).dump(using: .test, in: machO).string.print() } } @@ -26,7 +25,7 @@ extension DumpableTest { let protocolConformanceDescriptors = try machO.swift.protocolConformanceDescriptors for protocolConformanceDescriptor in protocolConformanceDescriptors { - try print(ProtocolConformance(descriptor: protocolConformanceDescriptor, in: machO).dump(using: .test, in: machO).string) + try ProtocolConformance(descriptor: protocolConformanceDescriptor, in: machO).dump(using: .test, in: machO).string.print() } } @@ -45,29 +44,29 @@ extension DumpableTest { case .enum(let enumDescriptor): do { let enumType = try Enum(descriptor: enumDescriptor, in: machO) - try print(enumType.dump(using: .test, in: machO).string) + try enumType.dump(using: .test, in: machO).string.print() } catch { - print(error) + error.print() } case .struct(let structDescriptor): do { let structType = try Struct(descriptor: structDescriptor, in: machO) - try print(structType.dump(using: .test, in: machO).string) + try structType.dump(using: .test, in: machO).string.print() if let metadata = try metadataFinder?.metadata(for: structDescriptor) as StructMetadata? { - try print(metadata.fieldOffsets(for: structDescriptor, in: machO)) + try metadata.fieldOffsets(for: structDescriptor, in: machO).print() } } catch { - print(error) + error.print() } case .class(let classDescriptor): do { let classType = try Class(descriptor: classDescriptor, in: machO) - try print(classType.dump(using: .test, in: machO).string) + try classType.dump(using: .test, in: machO).string.print() if let metadata = try metadataFinder?.metadata(for: classDescriptor) as ClassMetadataObjCInterop? { - try print(metadata.fieldOffsets(for: classDescriptor, in: machO)) + try metadata.fieldOffsets(for: classDescriptor, in: machO).print() } } catch { - print(error) + error.print() } } default: @@ -81,7 +80,7 @@ extension DumpableTest { package func dumpAssociatedTypes(for machO: MachOFile) async throws { let associatedTypeDescriptors = try machO.swift.associatedTypeDescriptors for associatedTypeDescriptor in associatedTypeDescriptors { - try print(AssociatedType(descriptor: associatedTypeDescriptor, in: machO).dump(using: .test, in: machO).string) + try AssociatedType(descriptor: associatedTypeDescriptor, in: machO).dump(using: .test, in: machO).string.print() } } } diff --git a/Sources/MachOTestingSupport/Extensions.swift b/Sources/MachOTestingSupport/Extensions.swift index a6efe467..75fc254f 100644 --- a/Sources/MachOTestingSupport/Extensions.swift +++ b/Sources/MachOTestingSupport/Extensions.swift @@ -24,7 +24,13 @@ extension DyldCache { } } -extension String { +extension CustomStringConvertible { + package func print() { + Swift.print(self) + } +} + +extension Error { package func print() { Swift.print(self) } diff --git a/Tests/MachOSwiftSectionTests/MetadataFinderTests.swift b/Tests/MachOSwiftSectionTests/MetadataFinderTests.swift index 8fae045d..559eabe5 100644 --- a/Tests/MachOSwiftSectionTests/MetadataFinderTests.swift +++ b/Tests/MachOSwiftSectionTests/MetadataFinderTests.swift @@ -4,8 +4,8 @@ import MachOKit import MachOTestingSupport import MachOMacro import MachOFoundation -@testable import MachOSwiftSection import SwiftDump +@testable import MachOSwiftSection @Suite struct MetadataFinderTests { @@ -73,14 +73,14 @@ struct MetadataFinderTests { guard let metadata = try finder.metadata(for: structDescriptor) as StructMetadata? else { continue } - try print(Struct(descriptor: structDescriptor, in: machO).dump(using: .default, in: machO)) - try print(metadata.fieldOffsets(for: structDescriptor, in: machO)) + try Struct(descriptor: structDescriptor, in: machO).dump(using: .test, in: machO).string.print() + try metadata.fieldOffsets(for: structDescriptor, in: machO).print() case .class(let classDescriptor): guard let metadata = try finder.metadata(for: classDescriptor) as ClassMetadataObjCInterop? else { continue } - try print(Class(descriptor: classDescriptor, in: machO).dump(using: .default, in: machO)) - try print(metadata.fieldOffsets(for: classDescriptor, in: machO)) + try Class(descriptor: classDescriptor, in: machO).dump(using: .test, in: machO).string.print() + try metadata.fieldOffsets(for: classDescriptor, in: machO).print() } } } diff --git a/Tests/SwiftDumpTests/DyldCacheDumpTests.swift b/Tests/SwiftDumpTests/DyldCacheDumpTests.swift index 4ecf4a36..54a39116 100644 --- a/Tests/SwiftDumpTests/DyldCacheDumpTests.swift +++ b/Tests/SwiftDumpTests/DyldCacheDumpTests.swift @@ -36,7 +36,6 @@ struct DyldCacheDumpTests: DumpableTest { } } - extension DyldCacheDumpTests { @Test func typesInCacheFile() async throws { try await dumpTypes(for: machOFileInCache) From 802e6164327657e706ed9d70e8841b7754f2642d Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Sun, 22 Jun 2025 17:59:40 +0800 Subject: [PATCH 11/16] Update Testing --- Sources/MachOTestingSupport/Extensions.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sources/MachOTestingSupport/Extensions.swift b/Sources/MachOTestingSupport/Extensions.swift index 75fc254f..c14cb02d 100644 --- a/Sources/MachOTestingSupport/Extensions.swift +++ b/Sources/MachOTestingSupport/Extensions.swift @@ -26,12 +26,16 @@ extension DyldCache { extension CustomStringConvertible { package func print() { + #if !SILENT_TEST Swift.print(self) + #endif } } extension Error { package func print() { + #if !SILENT_TEST Swift.print(self) + #endif } } From cb96f8636e3989493e29b890e60e300f2eaa3cd0 Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Sun, 22 Jun 2025 18:03:55 +0800 Subject: [PATCH 12/16] Update Testing --- Package.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 83b805dd..35e350b4 100644 --- a/Package.swift +++ b/Package.swift @@ -181,7 +181,8 @@ let package = Package( .MachOKit, "MachOExtensions", "SwiftDump", - ] + ], + swiftSettings: testSettings ), .target( From 536d7c8c951bd49282d18010b9abd69952c80106 Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Sun, 22 Jun 2025 18:07:46 +0800 Subject: [PATCH 13/16] Update Testing --- Tests/MachOSwiftSectionTests/MetadataFinderTests.swift | 4 ++-- Tests/SwiftDumpTests/MachOFileDumpTests.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/MachOSwiftSectionTests/MetadataFinderTests.swift b/Tests/MachOSwiftSectionTests/MetadataFinderTests.swift index 559eabe5..955e6d4d 100644 --- a/Tests/MachOSwiftSectionTests/MetadataFinderTests.swift +++ b/Tests/MachOSwiftSectionTests/MetadataFinderTests.swift @@ -38,10 +38,10 @@ struct MetadataFinderTests { self.machOFileInCache = try #require(mainCache.machOFile(named: .AttributeGraph)) // File - let file = try loadFromFile(named: .ControlCenter) + let file = try loadFromFile(named: .Finder) switch file { case .fat(let fatFile): - self.machOFile = try #require(fatFile.machOFiles().first(where: { $0.header.cpu.type == .x86_64 })) + self.machOFile = try #require(fatFile.machOFiles().first(where: { $0.header.cpu.type == .arm64 })) case .machO(let machO): self.machOFile = machO @unknown default: diff --git a/Tests/SwiftDumpTests/MachOFileDumpTests.swift b/Tests/SwiftDumpTests/MachOFileDumpTests.swift index 4b595f28..30fc62f9 100644 --- a/Tests/SwiftDumpTests/MachOFileDumpTests.swift +++ b/Tests/SwiftDumpTests/MachOFileDumpTests.swift @@ -17,7 +17,7 @@ struct MachOFileDumpTests: DumpableTest { 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(.arm64_all) })) + self.machOFile = try #require(fatFile.machOFiles().first(where: { $0.header.cpu.subtype == .arm64(.arm64e) })) case .machO(let machO): self.machOFile = machO @unknown default: From 2acfd831d4215cb16f29c9a96adcb106994f0bc3 Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Sun, 22 Jun 2025 18:18:24 +0800 Subject: [PATCH 14/16] Fix error --- Sources/Demangle/Main/DemangleOptions.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Demangle/Main/DemangleOptions.swift b/Sources/Demangle/Main/DemangleOptions.swift index 848ff9f7..f81e4907 100644 --- a/Sources/Demangle/Main/DemangleOptions.swift +++ b/Sources/Demangle/Main/DemangleOptions.swift @@ -25,7 +25,7 @@ public struct DemangleOptions: OptionSet, Codable, Sendable { public static let showClosureSignature = DemangleOptions(rawValue: 1 << 20) public static let showModuleInDependentMemberType = DemangleOptions(rawValue: 1 << 21) - package static let removeWeakPrefix = DemangleOptions(rawValue: 1 << 100) + package static let removeWeakPrefix = DemangleOptions(rawValue: 1 << 22) public init(rawValue: Int) { self.rawValue = rawValue From 7abc68e1e6984c468a77251f2db920cb3a797a47 Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Sun, 22 Jun 2025 19:10:23 +0800 Subject: [PATCH 15/16] Fix Protocol dump error --- .../SwiftDump/Dumpable+/Protocol+Dumpable.swift | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Sources/SwiftDump/Dumpable+/Protocol+Dumpable.swift b/Sources/SwiftDump/Dumpable+/Protocol+Dumpable.swift index 082cdbae..664d49a1 100644 --- a/Sources/SwiftDump/Dumpable+/Protocol+Dumpable.swift +++ b/Sources/SwiftDump/Dumpable+/Protocol+Dumpable.swift @@ -54,14 +54,18 @@ extension MachOSwiftSection.`Protocol`: NamedDumpable { for (offset, requirement) in requirements.offsetEnumerated() { BreakLine() Indent(level: 1) - if let symbol = try requirement.defaultImplementationSymbol(in: machOFile) { - InlineComment("[Default Implementation]") - try MetadataReader.demangleSymbol(for: symbol, in: machOFile).printSemantic(using: options) - } else if let symbol = try MachOSymbol.resolve(from: requirement.offset, in: machOFile) { - try MetadataReader.demangleSymbol(for: symbol, in: machOFile).printSemantic(using: options) + if let symbol = try MachOSymbol.resolve(from: requirement.offset, in: machOFile) { + try? MetadataReader.demangleSymbol(for: symbol, in: machOFile).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) { + BreakLine() + InlineComment("[Default Implementation]") + defaultImplementation + } + if offset.isEnd { BreakLine() } From d11a383605ed4f369e2d59f5150568dca4261d8f Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Sun, 22 Jun 2025 20:10:49 +0800 Subject: [PATCH 16/16] Add indent and space for protocol default implementation --- Sources/SwiftDump/Dumpable+/Protocol+Dumpable.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/SwiftDump/Dumpable+/Protocol+Dumpable.swift b/Sources/SwiftDump/Dumpable+/Protocol+Dumpable.swift index 664d49a1..ff99a2e6 100644 --- a/Sources/SwiftDump/Dumpable+/Protocol+Dumpable.swift +++ b/Sources/SwiftDump/Dumpable+/Protocol+Dumpable.swift @@ -62,7 +62,9 @@ extension MachOSwiftSection.`Protocol`: NamedDumpable { if let symbol = try requirement.defaultImplementationSymbol(in: machOFile), let defaultImplementation = try? MetadataReader.demangleSymbol(for: symbol, in: machOFile).printSemantic(using: options) { BreakLine() + Indent(level: 1) InlineComment("[Default Implementation]") + Space() defaultImplementation }