@@ -9,33 +9,48 @@ import Foundation
99import Combine
1010import ClassDump
1111import MachO. dyld
12+ import OSLog
1213
1314final class RuntimeListings : ObservableObject {
1415 static let shared = RuntimeListings ( )
1516 private static var sharedIfExists : RuntimeListings ?
17+ private static let logger = Logger ( subsystem: " null.leptos.HeaderViewer " , category: " RuntimeListings " )
1618
1719 @Published private( set) var classList : [ String ]
1820 @Published private( set) var protocolList : [ String ]
1921 @Published private( set) var imageList : [ String ]
2022
23+ @Published private( set) var protocolToImage : [ String : String ]
24+ @Published private( set) var imageToProtocols : [ String : [ String ] ]
25+
2126 private let shouldReload = PassthroughSubject < Void , Never > ( )
2227
2328 private var subscriptions : Set < AnyCancellable > = [ ]
2429
2530 private init ( ) {
26- self . classList = CDUtilities . classNames ( )
27- self . protocolList = CDUtilities . protocolNames ( )
31+ let classList = CDUtilities . classNames ( )
32+ let protocolList = CDUtilities . protocolNames ( )
33+ self . classList = classList
34+ self . protocolList = protocolList
2835 self . imageList = CDUtilities . imageNames ( )
2936
37+ let ( protocolToImage, imageToProtocols) = Self . protocolImageTrackingFor (
38+ protocolList: protocolList, protocolToImage: [ : ] , imageToProtocols: [ : ]
39+ ) ?? ( [ : ] , [ : ] )
40+ self . protocolToImage = protocolToImage
41+ self . imageToProtocols = imageToProtocols
42+
3043 RuntimeListings . sharedIfExists = self
3144
3245 shouldReload
3346 . debounce ( for: . milliseconds( 15 ) , scheduler: DispatchQueue . main)
3447 . sink { [ weak self] in
3548 guard let self else { return }
49+ Self . logger. debug ( " Start reload " )
3650 self . classList = CDUtilities . classNames ( )
3751 self . protocolList = CDUtilities . protocolNames ( )
3852 self . imageList = CDUtilities . imageNames ( )
53+ Self . logger. debug ( " End reload " )
3954 }
4055 . store ( in: & subscriptions)
4156
@@ -46,13 +61,64 @@ final class RuntimeListings: ObservableObject {
4661 _dyld_register_func_for_remove_image { _, _ in
4762 RuntimeListings . sharedIfExists? . shouldReload. send ( )
4863 }
64+
65+ $protocolList
66+ . combineLatest ( $protocolToImage, $imageToProtocols)
67+ . sink { [ unowned self] in
68+ guard let ( protocolToImage, imageToProtocols) = Self . protocolImageTrackingFor (
69+ protocolList: $0, protocolToImage: $1, imageToProtocols: $2
70+ ) else { return }
71+ self . protocolToImage = protocolToImage
72+ self . imageToProtocols = imageToProtocols
73+ }
74+ . store ( in: & subscriptions)
75+
4976 }
5077
5178 func isImageLoaded( path: String ) -> Bool {
5279 imageList. contains ( CDUtilities . patchImagePathForDyld ( path) )
5380 }
5481}
5582
83+ private extension RuntimeListings {
84+ static func protocolImageTrackingFor(
85+ protocolList: [ String ] , protocolToImage: [ String : String ] , imageToProtocols: [ String : [ String ] ]
86+ ) -> ( [ String : String ] , [ String : [ String ] ] ) ? {
87+ var protocolToImageCopy = protocolToImage
88+ var imageToProtocolsCopy = imageToProtocols
89+
90+ var dlInfo = dl_info ( )
91+ var didChange : Bool = false
92+
93+ for protocolName in protocolList {
94+ guard protocolToImageCopy [ protocolName] == nil else { continue } // happy path
95+
96+ guard let prtcl = NSProtocolFromString ( protocolName) else {
97+ logger. error ( " Failed to find protocol named ' \( protocolName, privacy: . public) ' " )
98+ continue
99+ }
100+
101+ guard dladdr ( protocol_getName ( prtcl) , & dlInfo) != 0 else {
102+ logger. warning ( " Failed to get dl_info for protocol named ' \( protocolName, privacy: . public) ' " )
103+ continue
104+ }
105+
106+ guard let abc = dlInfo. dli_fname else {
107+ logger. error ( " Failed to get dli_fname for protocol named ' \( protocolName, privacy: . public) ' " )
108+ continue
109+ }
110+
111+ let imageName = String ( cString: abc)
112+ protocolToImageCopy [ protocolName] = imageName
113+ imageToProtocolsCopy [ imageName, default: [ ] ] . append ( protocolName)
114+
115+ didChange = true
116+ }
117+ guard didChange else { return nil }
118+ return ( protocolToImageCopy, imageToProtocolsCopy)
119+ }
120+ }
121+
56122extension CDUtilities {
57123 class func imageNames( ) -> [ String ] {
58124 ( 0 ... )
0 commit comments