diff --git a/Package.resolved b/Package.resolved index 975bcb91..0d83889c 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "c42dcd3939ce04bacd6a118bdc5c00c183a6cf499711531f22ed1e82f5b5e3ae", + "originHash" : "11eaed82e8eb6edd730d4cf82f8bce43e2351a0cf3ad8750d9cb645751ff3817", "pins" : [ { "identity" : "associatedobject", @@ -10,6 +10,15 @@ "version" : "0.13.0" } }, + { + "identity" : "frameworktoolbox", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Mx-Iris/FrameworkToolbox", + "state" : { + "revision" : "ee688b5d481888ef0eff5052124cebe9b895d78f", + "version" : "0.3.0" + } + }, { "identity" : "machokit", "kind" : "remoteSourceControl", diff --git a/Package.swift b/Package.swift index 35e350b4..d486d6fe 100644 --- a/Package.swift +++ b/Package.swift @@ -115,6 +115,7 @@ let package = Package( .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"), .package(url: "https://github.com/onevcat/Rainbow", from: "4.0.0"), + .package(url: "https://github.com/Mx-Iris/FrameworkToolbox", from: "0.3.0"), ], targets: [ .target( @@ -125,6 +126,7 @@ let package = Package( name: "Demangle", dependencies: [ "Semantic", + .product(name: "FoundationToolbox", package: "FrameworkToolbox"), ] ), diff --git a/Sources/Demangle/Main/Demangler.swift b/Sources/Demangle/Main/Demangler.swift index 77f812d7..77a72637 100644 --- a/Sources/Demangle/Main/Demangler.swift +++ b/Sources/Demangle/Main/Demangler.swift @@ -104,7 +104,7 @@ extension Demangler { return result } - return Node(kind: .suffix, children: [], contents: .name(String(String.UnicodeScalarView(scanner.scalars)))) + return Node(kind: .suffix, contents: .name(String(String.UnicodeScalarView(scanner.scalars))), children: []) } private mutating func parseAndPushNames() throws { @@ -134,6 +134,7 @@ extension Demangler { case "A": return Node(kind: .isolatedAnyFunctionType) case "b": return Node(kind: .concurrentFunctionType) case "c": return try Node(kind: .globalActorFunctionType, child: require(popTypeAndGetChild())) + case "C": return Node(kind: .nonIsolatedCallerFunctionType) case "i": return try Node(typeWithChildKind: .isolated, childChild: require(popTypeAndGetChild())) case "j": return try demangleDifferentiableFunctionType() case "k": return try Node(typeWithChildKind: .noDerivative, childChild: require(popTypeAndGetChild())) @@ -303,7 +304,7 @@ extension Demangler { if let sendingResult = pop(kind: .sendingResultFunctionType) { name.addChild(sendingResult) } - if let isFunctionIsolation = pop(where: { $0 == .globalActorFunctionType || $0 == .isolatedAnyFunctionType }) { + if let isFunctionIsolation = pop(where: { $0 == .globalActorFunctionType || $0 == .isolatedAnyFunctionType || $0 == .nonIsolatedCallerFunctionType }) { name.addChild(isFunctionIsolation) } if let differentiable = pop(kind: .differentiableFunctionType) { @@ -334,7 +335,7 @@ extension Demangler { if kind == .argumentTuple { let params = try require(paramsType.children.first) let numParams = params.kind == .tuple ? params.children.count : 1 - return Node(kind: kind, children: [paramsType], contents: .index(UInt64(numParams))) + return Node(kind: kind, contents: .index(UInt64(numParams)), children: [paramsType]) } else { return Node(kind: kind, children: [paramsType]) } @@ -379,12 +380,18 @@ extension Demangler { if funcType.children.at(firstChildIndex)?.kind == .isolatedAnyFunctionType { firstChildIndex += 1 } + if funcType.children.at(firstChildIndex)?.kind == .nonIsolatedCallerFunctionType { + firstChildIndex += 1 + } if funcType.children.at(firstChildIndex)?.kind == .differentiableFunctionType { firstChildIndex += 1 } if funcType.children.at(firstChildIndex)?.kind == .throwsAnnotation || funcType.children.at(0)?.kind == .typedThrowsAnnotation { firstChildIndex += 1 } + if funcType.children.at(firstChildIndex)?.kind == .concurrentFunctionType { + firstChildIndex += 1 + } if funcType.children.at(firstChildIndex)?.kind == .asyncAnnotation { firstChildIndex += 1 } @@ -661,10 +668,10 @@ extension Demangler { name = "\(name)\(depth)" } - return Node(kind: .dependentGenericParamType, children: [ + return Node(kind: .dependentGenericParamType, contents: .name(name), children: [ Node(kind: .index, contents: .index(UInt64(depth))), Node(kind: .index, contents: .index(UInt64(index))), - ], contents: .name(name)) + ]) } private mutating func demangleStandardSubstitution() throws -> Node { @@ -877,7 +884,7 @@ extension Demangler { return Node(kind: .privateDeclName, children: [discriminator]) case "a" ... "j", "A" ... "J": - return try Node(kind: .relatedEntityDeclName, children: [require(pop())], contents: .name(String(c))) + return try Node(kind: .relatedEntityDeclName, contents: .name(String(c)), children: [require(pop())]) default: try scanner.backtrack() let discriminator = try demangleIndexAsName() @@ -1323,8 +1330,8 @@ extension Demangler { case "q": return try Node(kind: .uniquable, child: require(pop())) case "Q": return try Node(kind: .opaqueTypeDescriptor, child: require(pop())) case "r": return try Node(kind: .typeMetadataCompletionFunction, child: require(pop(kind: .type))) - case "s": return try Node(kind: .objCResilientClassStub, child: require(popProtocol())) - case "S": return try Node(kind: .protocolSelfConformanceDescriptor, child: require(pop(kind: .type))) + case "s": return try Node(kind: .objCResilientClassStub, child: require(pop(kind: .type))) + case "S": return try Node(kind: .protocolSelfConformanceDescriptor, child: popProtocol()) case "t": return try Node(kind: .fullObjCResilientClassStub, child: require(pop(kind: .type))) case "u": return try Node(kind: .methodLookupFunction, child: require(pop(kind: .type))) case "U": return try Node(kind: .objCMetadataUpdateFunction, child: require(pop(kind: .type))) @@ -1802,7 +1809,8 @@ extension Demangler { let param = paramIndexPair.element guard param.kind == .functionSignatureSpecializationParam else { continue } guard let kindName = param.children.first else { continue } - guard kindName.kind == .functionSignatureSpecializationParamKind, case let .index(i) = kindName.contents, let paramKind = FunctionSigSpecializationParamKind(rawValue: UInt64(i)) else { throw failure } + guard kindName.kind == .functionSignatureSpecializationParamKind, case let .index(i) = kindName.contents else { throw failure } + let paramKind = FunctionSigSpecializationParamKind(rawValue: UInt64(i)) switch paramKind { case .constantPropFunction, .constantPropGlobal, @@ -1881,9 +1889,6 @@ extension Demangler { param.addChild(Node(kind: .functionSignatureSpecializationParamKind, contents: .index(value))) case "g": var value = FunctionSigSpecializationParamKind.ownedToGuaranteed.rawValue - if scanner.conditional(scalar: "O") { - value |= FunctionSigSpecializationParamKind.guaranteedToOwned.rawValue - } if scanner.conditional(scalar: "X") { value |= FunctionSigSpecializationParamKind.sroa.rawValue } @@ -1944,7 +1949,7 @@ extension Demangler { default: throw failure } return try Node(kind: .fieldOffset, children: [Node(kind: .directness, contents: .index(directness)), require(pop(where: { $0.isEntity }))]) - case "S": return try Node(kind: .protocolSelfConformanceWitnessTable, child: popProtocolConformance()) + case "S": return try Node(kind: .protocolSelfConformanceWitnessTable, child: popProtocol()) case "P": return try Node(kind: .protocolWitnessTable, child: popProtocolConformance()) case "p": return try Node(kind: .protocolWitnessTablePattern, child: popProtocolConformance()) case "G": return try Node(kind: .genericProtocolWitnessTable, child: popProtocolConformance()) @@ -1965,13 +1970,7 @@ extension Demangler { return Node(kind: .associatedTypeMetadataAccessor, children: [conf, name]) case "T": let protoType = try require(pop(kind: .type)) - let assocTypePath = Node(kind: .assocTypePath) - var firstElem = false - repeat { - firstElem = pop(kind: .firstElementMarker) != nil - let assocType = try require(pop(where: { $0.isDeclName })) - assocTypePath.insertChild(assocType, at: 0) - } while !firstElem + let assocTypePath = try popAssocTypePath() return try Node(kind: .associatedTypeWitnessTableAccessor, children: [popProtocolConformance(), assocTypePath, protoType]) case "b": let protoTy = try require(pop(kind: .type)) @@ -1980,7 +1979,7 @@ extension Demangler { case "O": let sig = pop(kind: .dependentGenericSignature) let type = try require(pop(kind: .type)) - let children: [Node] = sig.map { [type, $0] } ?? [type] + var children: [Node] = sig.map { [type, $0] } ?? [type] switch try scanner.readScalar() { case "C": return Node(kind: .outlinedInitializeWithCopyNoValueWitness, children: children) case "D": return Node(kind: .outlinedAssignWithTakeNoValueWitness, children: children) @@ -1996,8 +1995,14 @@ extension Demangler { case "f": return Node(kind: .outlinedAssignWithCopy, children: children) case "h": return Node(kind: .outlinedDestroy, children: children) case "g": return Node(kind: .outlinedEnumGetTag, children: children) - case "i": return Node(kind: .outlinedEnumTagStore, children: children) - case "j": return Node(kind: .outlinedEnumProjectDataForLoad, children: children) + case "i": + let enumCaseIndex = try demangleIndexAsName() + children.append(enumCaseIndex) + return Node(kind: .outlinedEnumTagStore, children: children) + case "j": + let enumCaseIndex = try demangleIndexAsName() + children.append(enumCaseIndex) + return Node(kind: .outlinedEnumProjectDataForLoad, children: children) default: throw failure } case "Z", @@ -2015,6 +2020,38 @@ extension Demangler { } } + private mutating func popAssocTypePath() throws -> Node { + let assocTypePath = Node(kind: .assocTypePath) + var firstElem = false + repeat { + firstElem = pop(kind: .firstElementMarker) != nil + let assocType = try require(popAssocTypeName()) + assocTypePath.addChild(assocType) + } while !firstElem + assocTypePath.reverseChildren() + return assocTypePath + } + + private mutating func popAssocTypeName() -> Node? { + var proto = pop(kind: .type) + if let proto, !proto.isProtocol { + return nil + } + if proto == nil { + proto = pop(kind: .protocolSymbolicReference) + } + if proto == nil { + proto = pop(kind: .objectiveCProtocolSymbolicReference) + } + + guard let identifier = pop(kind: .identifier) else { return nil } + let assocType = Node(kind: .dependentAssociatedTypeRef, child: identifier) + if let proto { + assocType.addChild(proto) + } + return assocType + } + private mutating func demangleSpecialType() throws -> Node { let specialChar = try scanner.readScalar() switch specialChar { @@ -2400,7 +2437,7 @@ extension Demangler { private mutating func demangleValueWitness() throws -> Node { let code = try scanner.readScalars(count: 2) let kind = try require(ValueWitnessKind(code: code)) - return try Node(kind: .valueWitness, children: [require(pop(kind: .type))], contents: .index(kind.rawValue)) + return try Node(kind: .valueWitness, contents: .index(kind.rawValue), children: [require(pop(kind: .type))]) } } @@ -2563,7 +2600,7 @@ extension Demangler { case ("u", "p"): value = ValueWitnessKind.destructiveProjectEnumData.rawValue default: throw scanner.unexpectedError() } - return try Node(kind: .valueWitness, children: [demangleSwift3Type()], contents: .index(value)) + return try Node(kind: .valueWitness, contents: .index(value), children: [demangleSwift3Type()]) case ("W", "V"): return try Node(kind: .valueWitnessTable, children: [demangleSwift3Type()]) case ("W", "v"): return try Node(kind: .fieldOffset, children: [Node(kind: .directness, contents: .index(scanner.readScalar() == "d" ? 0 : 1)), demangleSwift3Entity()]) case ("W", "P"): return try Node(kind: .protocolWitnessTable, children: [demangleSwift3ProtocolConformance()]) @@ -2629,7 +2666,7 @@ extension Demangler { try scanner.match(scalar: "_") paramChildren.append(Node(kind: .functionSignatureSpecializationParamKind, contents: .index(value))) } - children.append(Node(kind: .functionSignatureSpecializationParam, children: paramChildren, contents: .index(count))) + children.append(Node(kind: .functionSignatureSpecializationParam, contents: .index(count), children: paramChildren)) count += 1 } return Node(kind: .functionSignatureSpecialization, children: children) @@ -2871,7 +2908,7 @@ extension Demangler { let type = try demangleSwift3Context() return Node(kind: .extension, children: [module, type, signature]) case "S": return try demangleSwift3SubstitutionIndex() - case "s": return Node(kind: .module, children: [], contents: .name(stdlibName)) + case "s": return Node(kind: .module, contents: .name(stdlibName), children: []) case "G": return try demangleSwift3BoundGenericArgs(nominalType: demangleSwift3NominalType()) case "F": fallthrough case "I": fallthrough @@ -2892,7 +2929,7 @@ extension Demangler { private mutating func demangleSwift3Module() throws -> Node { switch try scanner.readScalar() { case "S": return try demangleSwift3SubstitutionIndex() - case "s": return Node(kind: .module, children: [], contents: .name("Swift")) + case "s": return Node(kind: .module, contents: .name("Swift"), children: []) default: try scanner.backtrack() let module = try demangleSwift3Identifier(kind: .module) @@ -3053,7 +3090,7 @@ extension Demangler { try scanner.backtrack() (depth, index) = try (0, demangleSwift3Index() + 1) } - return Node(kind: .dependentGenericParamType, children: [Node(kind: .index, contents: .index(depth)), Node(kind: .index, contents: .index(index))], contents: .name(archetypeName(index, depth))) + return Node(kind: .dependentGenericParamType, contents: .name(archetypeName(index, depth)), children: [Node(kind: .index, contents: .index(depth)), Node(kind: .index, contents: .index(index))]) } private mutating func demangleSwift3DependentMemberTypeName(base: Node) throws -> Node { @@ -3139,7 +3176,7 @@ extension Demangler { case "D": type = try Node(kind: .dynamicSelf, children: [demangleSwift3Type()]) case "E": guard try scanner.readScalars(count: 2) == "RR" else { throw scanner.unexpectedError() } - type = Node(kind: .errorType, children: [], contents: .name("")) + type = Node(kind: .errorType, contents: .name(""), children: []) case "F": type = try demangleSwift3FunctionType(kind: .functionType) case "f": type = try demangleSwift3FunctionType(kind: .uncurriedFunctionType) case "G": type = try demangleSwift3BoundGenericArgs(nominalType: demangleSwift3NominalType()) @@ -3254,7 +3291,7 @@ extension Demangler { } else { type = try demangleSwift3GenericParamIndex() } - case "x": type = Node(kind: .dependentGenericParamType, children: [Node(kind: .index, contents: .index(0)), Node(kind: .index, contents: .index(0))], contents: .name(archetypeName(0, 0))) + case "x": type = Node(kind: .dependentGenericParamType, contents: .name(archetypeName(0, 0)), children: [Node(kind: .index, contents: .index(0)), Node(kind: .index, contents: .index(0))]) case "w": type = try demangleSwift3AssociatedTypeSimple() case "W": type = try demangleSwift3AssociatedTypeCompound() case "R": type = try Node(kind: .inOut, children: demangleSwift3Type().children) @@ -3407,6 +3444,6 @@ extension Demangler { } } - return Node(kind: k, children: [], contents: .name(identifier)) + return Node(kind: k, contents: .name(identifier), children: []) } } diff --git a/Sources/Demangle/Main/Node/Node+ChildrenBuilder.swift b/Sources/Demangle/Main/Node/Node+ChildrenBuilder.swift new file mode 100644 index 00000000..833564c5 --- /dev/null +++ b/Sources/Demangle/Main/Node/Node+ChildrenBuilder.swift @@ -0,0 +1,8 @@ +import Foundation +import FoundationToolbox + +extension Node { + package convenience init(kind: Kind, contents: Contents = .none, @ArrayBuilder childrenBuilder: () -> [Node]) { + self.init(kind: kind, contents: contents, children: childrenBuilder()) + } +} diff --git a/Sources/Demangle/Main/Node+Collection.swift b/Sources/Demangle/Main/Node/Node+Collection.swift similarity index 59% rename from Sources/Demangle/Main/Node+Collection.swift rename to Sources/Demangle/Main/Node/Node+Collection.swift index 81b169a4..cb58d615 100644 --- a/Sources/Demangle/Main/Node+Collection.swift +++ b/Sources/Demangle/Main/Node/Node+Collection.swift @@ -1,71 +1,72 @@ // MARK: - Collection Support + extension Node: Collection { public typealias Element = Node - + public struct Index: Comparable { fileprivate let path: [Int] fileprivate let traversalOrder: TraversalOrder - + fileprivate init(path: [Int], traversalOrder: TraversalOrder) { self.path = path self.traversalOrder = traversalOrder } - + public static func < (lhs: Index, rhs: Index) -> Bool { // Compare based on the actual traversal order return lhs.path.lexicographicallyPrecedes(rhs.path) } } - + public enum TraversalOrder { case preorder case inorder case postorder case levelorder } - + public var startIndex: Index { Index(path: [], traversalOrder: .preorder) } - + public var endIndex: Index { Index(path: [-1], traversalOrder: .preorder) } - + public subscript(position: Index) -> Node { return nodeAt(path: position.path, traversalOrder: position.traversalOrder) } - + public func index(after i: Index) -> Index { let nextPath = nextPath(from: i.path, traversalOrder: i.traversalOrder) return Index(path: nextPath, traversalOrder: i.traversalOrder) } - + // MARK: - Traversal Methods - + public func preorderTraversal() -> PreorderSequence { PreorderSequence(root: self) } - + public func inorderTraversal() -> InorderSequence { InorderSequence(root: self) } - + public func postorderTraversal() -> PostorderSequence { PostorderSequence(root: self) } - + public func levelorderTraversal() -> LevelorderSequence { LevelorderSequence(root: self) } - + // MARK: - Private Helper Methods - + private func nodeAt(path: [Int], traversalOrder: TraversalOrder) -> Node { if path.isEmpty { return self } - + var current = self for index in path { guard index >= 0 && index < current.children.count else { @@ -75,7 +76,7 @@ extension Node: Collection { } return current } - + private func nextPath(from currentPath: [Int], traversalOrder: TraversalOrder) -> [Int] { switch traversalOrder { case .preorder: @@ -88,191 +89,188 @@ extension Node: Collection { return nextLevelorderPath(from: currentPath) } } - + private func nextPreorderPath(from path: [Int]) -> [Int] { if path == [-1] { return [-1] } // End index - + let currentNode = nodeAt(path: path, traversalOrder: .preorder) - + // If current node has children, go to first child if !currentNode.children.isEmpty { return path + [0] } - + // Otherwise, find next sibling or ancestor's sibling var workingPath = path while !workingPath.isEmpty { let lastIndex = workingPath.removeLast() let parentNode = workingPath.isEmpty ? self : nodeAt(path: workingPath, traversalOrder: .preorder) - + if lastIndex + 1 < parentNode.children.count { return workingPath + [lastIndex + 1] } } - + return [-1] // End of traversal } - + private func nextInorderPath(from path: [Int]) -> [Int] { // Simplified inorder implementation // For a more complete implementation, you'd need to track state return nextPreorderPath(from: path) } - + private func nextPostorderPath(from path: [Int]) -> [Int] { // Simplified postorder implementation return nextPreorderPath(from: path) } - + private func nextLevelorderPath(from path: [Int]) -> [Int] { // Simplified level-order implementation return nextPreorderPath(from: path) } - + // MARK: - Sequence Types for Different Traversals public struct PreorderSequence: Sequence { - private let root: Node - - fileprivate init(root: Node) { - self.root = root - } - - public func makeIterator() -> PreorderIterator { - PreorderIterator(root: root) - } - } + public struct Iterator: IteratorProtocol { + private var stack: [Node] - public struct PreorderIterator: IteratorProtocol { - private var stack: [Node] - - fileprivate init(root: Node) { - self.stack = [root] - } - - public mutating func next() -> Node? { - guard !stack.isEmpty else { return nil } - - let current = stack.removeLast() - - // Add children in reverse order so we visit them left-to-right - for child in current.children.reversed() { - stack.append(child) + fileprivate init(root: Node) { + self.stack = [root] + } + + public mutating func next() -> Node? { + guard !stack.isEmpty else { return nil } + + let current = stack.removeLast() + + // Add children in reverse order so we visit them left-to-right + for child in current.children.reversed() { + stack.append(child) + } + + return current } - - return current } - } - public struct InorderSequence: Sequence { private let root: Node - + fileprivate init(root: Node) { self.root = root } - - public func makeIterator() -> InorderIterator { - InorderIterator(root: root) + + public func makeIterator() -> Iterator { + Iterator(root: root) } } - public struct InorderIterator: IteratorProtocol { - private var stack: [Node] - private var current: Node? - - fileprivate init(root: Node) { - self.stack = [] - self.current = root - } - - public mutating func next() -> Node? { - while current != nil || !stack.isEmpty { - // Go to the leftmost node - while let node = current { - stack.append(node) - current = node.children.first - } - - // Current must be nil at this point - if let node = stack.popLast() { - current = node.children.count > 1 ? node.children[1] : nil - return node + public struct InorderSequence: Sequence { + public struct Iterator: IteratorProtocol { + private var stack: [Node] + private var current: Node? + + fileprivate init(root: Node) { + self.stack = [] + self.current = root + } + + public mutating func next() -> Node? { + while current != nil || !stack.isEmpty { + // Go to the leftmost node + while let node = current { + stack.append(node) + current = node.children.first + } + + // Current must be nil at this point + if let node = stack.popLast() { + current = node.children.count > 1 ? node.children[1] : nil + return node + } } + return nil } - return nil } - } - public struct PostorderSequence: Sequence { private let root: Node - + fileprivate init(root: Node) { self.root = root } - - public func makeIterator() -> PostorderIterator { - PostorderIterator(root: root) + + public func makeIterator() -> Iterator { + Iterator(root: root) } } - public struct PostorderIterator: IteratorProtocol { - private var stack: [(node: Node, visited: Bool)] - - fileprivate init(root: Node) { - self.stack = [(root, false)] - } - - public mutating func next() -> Node? { - while !stack.isEmpty { - let (node, visited) = stack.removeLast() - - if visited { - return node - } else { - // Mark as visited and push back - stack.append((node, true)) - - // Push children in reverse order - for child in node.children.reversed() { - stack.append((child, false)) + public struct PostorderSequence: Sequence { + public struct Iterator: IteratorProtocol { + private var stack: [(node: Node, visited: Bool)] + + fileprivate init(root: Node) { + self.stack = [(root, false)] + } + + public mutating func next() -> Node? { + while !stack.isEmpty { + let (node, visited) = stack.removeLast() + + if visited { + return node + } else { + // Mark as visited and push back + stack.append((node, true)) + + // Push children in reverse order + for child in node.children.reversed() { + stack.append((child, false)) + } } } + return nil } - return nil } - } - public struct LevelorderSequence: Sequence { private let root: Node - + fileprivate init(root: Node) { self.root = root } - - public func makeIterator() -> LevelorderIterator { - LevelorderIterator(root: root) + + public func makeIterator() -> Iterator { + Iterator(root: root) } } - public struct LevelorderIterator: IteratorProtocol { - private var queue: [Node] - + public struct LevelorderSequence: Sequence { + public struct Iterator: IteratorProtocol { + private var queue: [Node] + + fileprivate init(root: Node) { + self.queue = [root] + } + + public mutating func next() -> Node? { + guard !queue.isEmpty else { return nil } + + let current = queue.removeFirst() + + // Add all children to the queue + queue.append(contentsOf: current.children) + + return current + } + } + + private let root: Node + fileprivate init(root: Node) { - self.queue = [root] + self.root = root } - - public mutating func next() -> Node? { - guard !queue.isEmpty else { return nil } - - let current = queue.removeFirst() - - // Add all children to the queue - queue.append(contentsOf: current.children) - - return current + + public func makeIterator() -> Iterator { + Iterator(root: root) } } - - } - diff --git a/Sources/Demangle/Main/Node/Node+CustomStringConvertible.swift b/Sources/Demangle/Main/Node/Node+CustomStringConvertible.swift new file mode 100644 index 00000000..b9165163 --- /dev/null +++ b/Sources/Demangle/Main/Node/Node+CustomStringConvertible.swift @@ -0,0 +1,22 @@ +import Semantic + +extension Node: CustomStringConvertible { + /// Overridden method to allow simple printing with default options + public var description: String { + print() + } + + /// Prints `SwiftSymbol`s to a String with the full set of printing options. + /// + /// - Parameter options: an option set containing the different `DemangleOptions` from the Swift project. + /// - Returns: `self` printed to a string according to the specified options. + public func print(using options: DemangleOptions = .default) -> String { + printSemantic(using: options).string + } + + public func printSemantic(using options: DemangleOptions = .default) -> SemanticString { + var printer = NodePrinter(options: options) + _ = printer.printName(self) + return printer.target + } +} diff --git a/Sources/Demangle/Main/Node+Kind.swift b/Sources/Demangle/Main/Node/Node+Kind.swift similarity index 99% rename from Sources/Demangle/Main/Node+Kind.swift rename to Sources/Demangle/Main/Node/Node+Kind.swift index 35f5f0cc..77b59d88 100644 --- a/Sources/Demangle/Main/Node+Kind.swift +++ b/Sources/Demangle/Main/Node/Node+Kind.swift @@ -171,6 +171,7 @@ extension Node { case isolated case isolatedDeallocator case isolatedAnyFunctionType + case nonIsolatedCallerFunctionType case isSerialized case iVarDestroyer case iVarInitializer diff --git a/Sources/Demangle/Main/Node.swift b/Sources/Demangle/Main/Node/Node.swift similarity index 79% rename from Sources/Demangle/Main/Node.swift rename to Sources/Demangle/Main/Node/Node.swift index 0cdd5b88..aa40f79d 100644 --- a/Sources/Demangle/Main/Node.swift +++ b/Sources/Demangle/Main/Node/Node.swift @@ -1,5 +1,3 @@ -import Semantic - public final class Node: @unchecked Sendable { public let kind: Kind public let contents: Contents @@ -27,7 +25,7 @@ public final class Node: @unchecked Sendable { } } - public init(kind: Kind, children: [Node] = [], contents: Contents = .none) { + public init(kind: Kind, contents: Contents = .none, children: [Node] = []) { self.kind = kind self.children = children self.contents = contents @@ -37,22 +35,22 @@ public final class Node: @unchecked Sendable { } package convenience init(kind: Kind, child: Node) { - self.init(kind: kind, children: [child], contents: .none) + self.init(kind: kind, contents: .none, children: [child]) } package convenience init(typeWithChildKind: Kind, childChild: Node) { - self.init(kind: .type, children: [Node(kind: typeWithChildKind, children: [childChild])], contents: .none) + self.init(kind: .type, contents: .none, children: [Node(kind: typeWithChildKind, children: [childChild])]) } package convenience init(typeWithChildKind: Kind, childChildren: [Node]) { - self.init(kind: .type, children: [Node(kind: typeWithChildKind, children: childChildren)], contents: .none) + self.init(kind: .type, contents: .none, children: [Node(kind: typeWithChildKind, children: childChildren)]) } package convenience init(swiftStdlibTypeKind: Kind, name: String) { - self.init(kind: .type, children: [Node(kind: swiftStdlibTypeKind, children: [ + self.init(kind: .type, contents: .none, children: [Node(kind: swiftStdlibTypeKind, children: [ Node(kind: .module, contents: .name(stdlibName)), Node(kind: .identifier, contents: .name(name)), - ])], contents: .none) + ])]) } package convenience init(swiftBuiltinType: Kind, name: String) { @@ -92,16 +90,16 @@ public final class Node: @unchecked Sendable { } else { modifiedChildren.remove(at: atIndex) } - return Node(kind: kind, children: modifiedChildren, contents: contents) + return Node(kind: kind, contents: contents, children: modifiedChildren) } package func changeKind(_ newKind: Kind, additionalChildren: [Node] = []) -> Node { if case .name(let text) = contents { - return Node(kind: newKind, children: children + additionalChildren, contents: .name(text)) + return Node(kind: newKind, contents: .name(text), children: children + additionalChildren) } else if case .index(let i) = contents { - return Node(kind: newKind, children: children + additionalChildren, contents: .index(i)) + return Node(kind: newKind, contents: .index(i), children: children + additionalChildren) } else { - return Node(kind: newKind, children: children + additionalChildren, contents: .none) + return Node(kind: newKind, contents: .none, children: children + additionalChildren) } } @@ -222,23 +220,4 @@ extension Node { } } -extension Node: CustomStringConvertible { - /// Overridden method to allow simple printing with default options - public var description: String { - print() - } - - /// Prints `SwiftSymbol`s to a String with the full set of printing options. - /// - /// - Parameter options: an option set containing the different `DemangleOptions` from the Swift project. - /// - Returns: `self` printed to a string according to the specified options. - public func print(using options: DemangleOptions = .default) -> String { - printSemantic(using: options).string - } - public func printSemantic(using options: DemangleOptions = .default) -> SemanticString { - var printer = NodePrinter(options: options) - _ = printer.printName(self) - return printer.target - } -} diff --git a/Sources/Demangle/Main/NodePrinter.swift b/Sources/Demangle/Main/NodePrinter.swift index 7e9d6034..318dfb63 100644 --- a/Sources/Demangle/Main/NodePrinter.swift +++ b/Sources/Demangle/Main/NodePrinter.swift @@ -1394,6 +1394,7 @@ struct NodePrinter: Sendable { case .vTableThunk: printVTableThunk(name) case .weak: printFirstChild(name, prefix: options.contains(.removeWeakPrefix) ? "" : "weak ") case .willSet: return printAbstractStorage(name.children.first, asPrefixContext: asPrefixContext, extraName: "willset") + case .nonIsolatedCallerFunctionType: target.write("nonisolated(nonsending) ") } return nil @@ -1695,6 +1696,11 @@ struct NodePrinter: Sendable { _ = printOptional(name.children.at(startIndex)) startIndex += 1 } + var nonIsolatedCallerNode: Node? + if name.children.at(startIndex)?.kind == .nonIsolatedCallerFunctionType { + nonIsolatedCallerNode = name.children.at(startIndex) + startIndex += 1 + } if name.children.at(startIndex)?.kind == .globalActorFunctionType { _ = printOptional(name.children.at(startIndex)) startIndex += 1 @@ -1725,6 +1731,10 @@ struct NodePrinter: Sendable { default: break } + if let nonIsolatedCallerNode { + _ = printName(nonIsolatedCallerNode) + } + if isSendable { target.write("@Sendable ") } diff --git a/Sources/Demangle/Main/ScalarScanner.swift b/Sources/Demangle/Utils/ScalarScanner.swift similarity index 100% rename from Sources/Demangle/Main/ScalarScanner.swift rename to Sources/Demangle/Utils/ScalarScanner.swift diff --git a/Sources/MachOSwiftSection/Utils/MetadataReader.swift b/Sources/MachOSwiftSection/Utils/MetadataReader.swift index 65fc7899..011c2eaa 100644 --- a/Sources/MachOSwiftSection/Utils/MetadataReader.swift +++ b/Sources/MachOSwiftSection/Utils/MetadataReader.swift @@ -390,7 +390,7 @@ extension Node { } if child.kind == .enum || child.kind == .structure || child.kind == .class || child.kind == .protocol { - return .init(kind: .type, children: [child], contents: .none) + return .init(kind: .type, contents: .none, children: [child]) } for child in child.children { diff --git a/Sources/MachOTestingSupport/DumpableTest.swift b/Sources/MachOTestingSupport/DumpableTest.swift index aea8de6f..d8a10ed5 100644 --- a/Sources/MachOTestingSupport/DumpableTest.swift +++ b/Sources/MachOTestingSupport/DumpableTest.swift @@ -10,6 +10,8 @@ package protocol DumpableTest { } extension DumpableTest { + package var isEnabledSearchMetadata: Bool { false } + @MachOImageGenerator @MainActor package func dumpProtocols(for machO: MachOFile) async throws { diff --git a/Sources/SwiftDump/Extensions/GenericContext+Dump.swift b/Sources/SwiftDump/Extensions/GenericContext+Dump.swift index 672d0054..7a692283 100644 --- a/Sources/SwiftDump/Extensions/GenericContext+Dump.swift +++ b/Sources/SwiftDump/Extensions/GenericContext+Dump.swift @@ -44,19 +44,26 @@ extension TargetGenericContext { } } +@MachOImageAllMembersGenerator extension GenericRequirementDescriptor { - @MachOImageGenerator - @SemanticStringBuilder - package func dump(using options: DemangleOptions, in machOFile: MachOFile) throws -> SemanticString { - try MetadataReader.demangleType(for: paramManagedName(in: machOFile), in: machOFile).printSemantic(using: options) - if layout.flags.kind == .sameType { - Space() - Standard("==") - Space() + + package func dumpInheritedProtocol(using options: DemangleOptions, in machOFile: MachOFile) throws -> SemanticString? { + if try paramManagedName(in: machOFile).rawStringValue() == "A" { + return try dumpParameterName(using: options, in: machOFile) } else { - Standard(":") - Space() + return nil } + } + + + @SemanticStringBuilder + package func dumpParameterName(using options: DemangleOptions, in machOFile: MachOFile) throws -> SemanticString { + let node = try MetadataReader.demangleType(for: paramManagedName(in: machOFile), in: machOFile) + node.printSemantic(using: options) + } + + @SemanticStringBuilder + package func dumpContent(using options: DemangleOptions, in machOFile: MachOFile) throws -> SemanticString { switch try resolvedContent(in: machOFile) { case .type(let mangledName): try MetadataReader.demangleType(for: mangledName, in: machOFile).printSemantic(using: options) @@ -82,7 +89,7 @@ extension GenericRequirementDescriptor { case .invertedProtocols/* (let invertedProtocols) */: Standard("") // if invertedProtocols.protocols.hasCopyable, invertedProtocols.protocols.hasEscapable { -// +// // "Copyable, Escapable" // } else if invertedProtocols.protocols.hasCopyable || invertedProtocols.protocols.hasEscapable { // if invertedProtocols.protocols.hasCopyable { @@ -100,6 +107,22 @@ extension GenericRequirementDescriptor { // } } } + + @SemanticStringBuilder + package func dump(using options: DemangleOptions, in machOFile: MachOFile) throws -> SemanticString { + try dumpParameterName(using: options, in: machOFile) + + if layout.flags.kind == .sameType { + Space() + Standard("==") + Space() + } else { + Standard(":") + Space() + } + + try dumpContent(using: options, in: machOFile) + } } extension OptionSet { diff --git a/Tests/DemangleTests/NodeTests.swift b/Tests/DemangleTests/NodeTests.swift new file mode 100644 index 00000000..77c4e2ee --- /dev/null +++ b/Tests/DemangleTests/NodeTests.swift @@ -0,0 +1,27 @@ +import Foundation +import Testing +@testable import Demangle + +struct NodeTests { + @Test func testNode() { + let node = Node(kind: .type, contents: .none) { +// Node(kind: .dependentMemberType) { +// Node(kind: .dependentGenericParamType, contents: .name("A")) { +// Node(kind: .index, contents: .index(0)) +// Node(kind: .index, contents: .index(0)) +// } + Node(kind: .dependentAssociatedTypeRef) { + Node(kind: .identifier, contents: .name("Tail")) + Node(kind: .dynamicSelf) +// Node(kind: .type) { +// Node(kind: .protocol) { +// Node(kind: .module, contents: .name("SwiftUI")) +// Node(kind: .identifier, contents: .name("TupleProtocol")) +// } +// } + } +// } + } + print(node.print()) + } +} diff --git a/Tests/MachOSwiftSectionTests/SymbolDemangleTests.swift b/Tests/MachOSwiftSectionTests/SymbolDemangleTests.swift new file mode 100644 index 00000000..0cb63a52 --- /dev/null +++ b/Tests/MachOSwiftSectionTests/SymbolDemangleTests.swift @@ -0,0 +1,53 @@ +import Foundation +import Testing +import Demangle +import MachOKit +import MachOMacro +import MachOFoundation +@testable import MachOSwiftSection + +@Suite(.serialized) +struct SymbolDemangleTests { + let mainCache: DyldCache + + let subCache: DyldCache + + let machOFileInMainCache: MachOFile + + let machOFileInSubCache: MachOFile + + let machOFileInCache: MachOFile + + init() async throws { + self.mainCache = try DyldCache(path: .current) + self.subCache = try required(mainCache.subCaches?.first?.subcache(for: mainCache)) + + self.machOFileInMainCache = try #require(mainCache.machOFile(named: .SwiftUI)) + self.machOFileInSubCache = if #available(macOS 15.5, *) { + try #require(subCache.machOFile(named: .CodableSwiftUI)) + } else { + try #require(subCache.machOFile(named: .UIKitCore)) + } + + self.machOFileInCache = try #require(mainCache.machOFile(named: .SwiftUICore)) + } + + @Test func symbols() async throws { + for symbol in machOFileInCache.symbols where symbol.name.starts(with: "_$s") { + do { + var demangler = Demangler(scalars: symbol.name.unicodeScalars) + _ = try demangler.demangleSymbol() +// print("Successfully: \(symbol.name)") + } catch { + print("Failed: \(symbol.name)") + print(error) + print("\n") + } + } + } + + @Test func demangle() async throws { + var demangler = Demangler(scalars: "_$sSTsE10compactMapySayqd__Gqd__Sg7ElementQzKXEKlFSaySSG_7SwiftUI14ToolbarStorageV5EntryV2IDVTg503$s7d4UI13f115BridgeC11makeStorage33_558B6B1E48F37C8B0E16B128287879E0LL2in4from8strategyAA0C0O08LocationF0VAJ03BarS0O_SayAA0cF0V5H20VGxtFAR2IDVSgSSXEfU_AG0F0O08LocationG0VTf1cn_nTf4ngX_n".unicodeScalars) + _ = try demangler.demangleSymbol() + } +} diff --git a/Tests/SwiftDumpTests/DyldCacheDumpTests.swift b/Tests/SwiftDumpTests/DyldCacheDumpTests.swift index 54a39116..a77f7182 100644 --- a/Tests/SwiftDumpTests/DyldCacheDumpTests.swift +++ b/Tests/SwiftDumpTests/DyldCacheDumpTests.swift @@ -19,8 +19,6 @@ struct DyldCacheDumpTests: DumpableTest { 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)) diff --git a/Tests/SwiftDumpTests/MachOFileDumpTests.swift b/Tests/SwiftDumpTests/MachOFileDumpTests.swift index 30fc62f9..a4c0eec1 100644 --- a/Tests/SwiftDumpTests/MachOFileDumpTests.swift +++ b/Tests/SwiftDumpTests/MachOFileDumpTests.swift @@ -11,8 +11,6 @@ import MachOFoundation struct MachOFileDumpTests: DumpableTest { let machOFile: MachOFile - let isEnabledSearchMetadata: Bool = false - init() async throws { let file = try loadFromFile(named: .Finder) switch file { diff --git a/Tests/SwiftDumpTests/MachOImageDumpTests.swift b/Tests/SwiftDumpTests/MachOImageDumpTests.swift index 735b9a27..8c94477e 100644 --- a/Tests/SwiftDumpTests/MachOImageDumpTests.swift +++ b/Tests/SwiftDumpTests/MachOImageDumpTests.swift @@ -11,8 +11,6 @@ import MachOFoundation struct MachOImageDumpTests: DumpableTest { let machOImage: MachOImage - let isEnabledSearchMetadata: Bool = false - init() async throws { self.machOImage = try #require(MachOImage(named: .Foundation)) }