|
| 1 | +//import Foundation |
| 2 | +//import MachO.dyld |
| 3 | +// |
| 4 | +//// MARK: Interpose Class Load Watcher |
| 5 | +// |
| 6 | +//extension Interpose { |
| 7 | +// // Separate definitions to have more relevant calling syntax when completion is not needed. |
| 8 | +// |
| 9 | +// /// Interpose a class once available. Class is passed via `classParts` string array. |
| 10 | +// @discardableResult public class func whenAvailable(_ classParts: [String], |
| 11 | +// builder: @escaping (Interpose) throws -> Void) throws -> Waiter { |
| 12 | +// try whenAvailable(classParts, builder: builder, completion: nil) |
| 13 | +// } |
| 14 | +// |
| 15 | +// /// Interpose a class once available. Class is passed via `classParts` string array, with completion handler. |
| 16 | +// @discardableResult public class func whenAvailable(_ classParts: [String], |
| 17 | +// builder: @escaping (Interpose) throws -> Void, |
| 18 | +// completion: (() -> Void)? = nil) throws -> Waiter { |
| 19 | +// try whenAvailable(classParts.joined(), builder: builder, completion: completion) |
| 20 | +// } |
| 21 | +// |
| 22 | +// /// Interpose a class once available. Class is passed via `className` string. |
| 23 | +// @discardableResult public class func whenAvailable(_ className: String, |
| 24 | +// builder: @escaping (Interpose) throws -> Void) throws -> Waiter { |
| 25 | +// try whenAvailable(className, builder: builder, completion: nil) |
| 26 | +// } |
| 27 | +// |
| 28 | +// /// Interpose a class once available. Class is passed via `className` string, with completion handler. |
| 29 | +// @discardableResult public class func whenAvailable(_ className: String, |
| 30 | +// builder: @escaping (Interpose) throws -> Void, |
| 31 | +// completion: (() -> Void)? = nil) throws -> Waiter { |
| 32 | +// try Waiter(className: className, builder: builder, completion: completion) |
| 33 | +// } |
| 34 | +// |
| 35 | +// /// Helper that stores hooks to a specific class and executes them once the class becomes available. |
| 36 | +// public struct Waiter { |
| 37 | +// fileprivate let className: String |
| 38 | +// private var builder: ((Interpose) throws -> Void)? |
| 39 | +// private var completion: (() -> Void)? |
| 40 | +// |
| 41 | +// /// Initialize waiter object. |
| 42 | +// @discardableResult init(className: String, |
| 43 | +// builder: @escaping (Interpose) throws -> Void, |
| 44 | +// completion: (() -> Void)? = nil) throws { |
| 45 | +// self.className = className |
| 46 | +// self.builder = builder |
| 47 | +// self.completion = completion |
| 48 | +// |
| 49 | +// // Immediately try to execute task. If not there, install waiter. |
| 50 | +// if try tryExecute() == false { |
| 51 | +// InterposeWatcher.append(waiter: self) |
| 52 | +// } |
| 53 | +// } |
| 54 | +// |
| 55 | +// func tryExecute() throws -> Bool { |
| 56 | +// guard let builder = self.builder else { return false } |
| 57 | +// let interposer = Interpose() |
| 58 | +// try builder(interposer) |
| 59 | +// if let completion = self.completion { |
| 60 | +// completion() |
| 61 | +// } |
| 62 | +// return true |
| 63 | +// } |
| 64 | +// } |
| 65 | +//} |
| 66 | +// |
| 67 | +//// dyld C function cannot capture class context so we pack it in a static struct. |
| 68 | +//private struct InterposeWatcher { |
| 69 | +// // Global list of waiters; can be multiple per class |
| 70 | +// private static var globalWatchers: [Interpose.Waiter] = { |
| 71 | +// // Register after Swift global registers to not deadlock |
| 72 | +// DispatchQueue.main.async { InterposeWatcher.installGlobalImageLoadWatcher() } |
| 73 | +// return [] |
| 74 | +// }() |
| 75 | +// |
| 76 | +// fileprivate static func append(waiter: Interpose.Waiter) { |
| 77 | +// InterposeWatcher.globalWatcherQueue.sync { |
| 78 | +// globalWatchers.append(waiter) |
| 79 | +// } |
| 80 | +// } |
| 81 | +// |
| 82 | +// // Register hook when dyld loads an image |
| 83 | +// private static let globalWatcherQueue = DispatchQueue(label: "com.steipete.global-image-watcher") |
| 84 | +// private static func installGlobalImageLoadWatcher() { |
| 85 | +// _dyld_register_func_for_add_image { _, _ in |
| 86 | +// InterposeWatcher.globalWatcherQueue.sync { |
| 87 | +// // this is called on the thread the image is loaded. |
| 88 | +// InterposeWatcher.globalWatchers = InterposeWatcher.globalWatchers.filter { waiter -> Bool in |
| 89 | +// do { |
| 90 | +// if try waiter.tryExecute() == false { |
| 91 | +// return true // only collect if this fails because class is not there yet |
| 92 | +// } else { |
| 93 | +// Interpose.log("\(waiter.className) was successful.") |
| 94 | +// } |
| 95 | +// } catch { |
| 96 | +// Interpose.log("Error while executing task: \(error).") |
| 97 | +// // We can't bubble up the throw into the C context. |
| 98 | +// #if DEBUG |
| 99 | +// // Instead of silently eating, it's better to crash in DEBUG. |
| 100 | +// Interpose.fail("Error while executing task: \(error).") |
| 101 | +// #endif |
| 102 | +// } |
| 103 | +// return false |
| 104 | +// } |
| 105 | +// } |
| 106 | +// } |
| 107 | +// } |
| 108 | +//} |
0 commit comments