1- import Foundation
21import ITKSuperBuilder
2+ import ObjectiveC
33
4- final class ObjectHookStrategy : HookStrategy {
4+ internal final class ObjectHookStrategy : HookStrategy {
55
6- init (
6+ // ============================================================================ //
7+ // MARK: Initialization
8+ // ============================================================================ //
9+
10+ internal init (
711 object: NSObject ,
812 selector: Selector ,
913 makeHookIMP: @escaping ( ) -> IMP
@@ -14,23 +18,34 @@ final class ObjectHookStrategy: HookStrategy {
1418 self . makeHookIMP = makeHookIMP
1519 }
1620
17- let `class` : AnyClass
18- let object : NSObject
19- var scope : HookScope { . object( self . object) }
20- let selector : Selector
21+ // ============================================================================ //
22+ // MARK: Configuration
23+ // ============================================================================ //
24+
25+ internal let `class` : AnyClass
26+ internal let object : NSObject
27+ internal var scope : HookScope { . object( self . object) }
28+ internal let selector : Selector
2129
2230 private let makeHookIMP : ( ) -> IMP
23- private( set) var appliedHookIMP : IMP ?
24- private( set) var storedOriginalIMP : IMP ?
31+
32+ // ============================================================================ //
33+ // MARK: Implementations & Handle
34+ // ============================================================================ //
35+
36+ private( set) internal var appliedHookIMP : IMP ?
37+ private( set) internal var storedOriginalIMP : IMP ?
2538
2639 private lazy var handle = ObjectHookHandle (
2740 getOriginalIMP: { [ weak self] in self ? . storedOriginalIMP } ,
2841 setOriginalIMP: { [ weak self] in self ? . storedOriginalIMP = $0 }
2942 )
43+
44+ // ============================================================================ //
45+ // MARK: Validation
46+ // ============================================================================ //
3047
31- /// Subclass that we create on the fly
32-
33- func validate( ) throws {
48+ internal func validate( ) throws {
3449 guard class_getInstanceMethod ( self . class, self . selector) != nil else {
3550 throw InterposeError . methodNotFound (
3651 class: self . class,
@@ -55,59 +70,77 @@ final class ObjectHookStrategy: HookStrategy {
5570 }
5671 }
5772
58- func replaceImplementation( ) throws {
73+ // ============================================================================ //
74+ // MARK: Installing Implementation
75+ // ============================================================================ //
76+
77+ internal func replaceImplementation( ) throws {
5978 let hookIMP = self . makeHookIMP ( )
60- self . appliedHookIMP = hookIMP
61- ObjectHookRegistry . register ( self . handle, for: hookIMP)
6279
80+ // Fetch the method, whose implementation we want to replace.
6381 guard let method = class_getInstanceMethod ( self . class, self . selector) else {
64- throw InterposeError . methodNotFound ( class: self . class, selector: self . selector)
82+ throw InterposeError . methodNotFound (
83+ class: self . class,
84+ selector: self . selector
85+ )
6586 }
6687
67- // The implementation of the call that is hooked must exist .
88+ // Ensure that the method has an associated implementation .
6889 guard self . lookUpIMP ( ) != nil else {
6990 throw InterposeError . implementationNotFound (
7091 class: self . class,
7192 selector: self . selector
7293 )
7394 }
7495
75- // Check if there's an existing subclass we can reuse.
76- // Create one at runtime if there is none .
77- let dynamicSubclass : AnyClass = try ObjectSubclassManager . ensureSubclassInstalled ( for: self . object)
96+ // Retrieve a ready-to-use dynamic subclass. It might be reused if the object already
97+ // has one installed or a newly created one .
98+ let subclass : AnyClass = try ObjectSubclassManager . ensureSubclassInstalled ( for: self . object)
7899
79- // This function searches superclasses for implementations
80- let classImplementsMethod = class_implementsInstanceMethod ( dynamicSubclass, self . selector)
81- let encoding = method_getTypeEncoding ( method)
82-
83- // If the subclass is empty, we create a super trampoline first.
84- // If a hook already exists, we must skip this.
85- if !classImplementsMethod {
100+ // If the dynamic subclass does not implement the method directly, we create a super
101+ // trampoline first. Otherwise, when a hook for that method has already been applied
102+ // (and potentially reverted), we skip this step.
103+ if !class_implementsInstanceMethod( subclass, self . selector) {
86104 do {
87- try ITKSuperBuilder . addSuperInstanceMethod ( to: dynamicSubclass, selector: self . selector)
88- let imp = class_getMethodImplementation ( dynamicSubclass, self . selector) !
89- Interpose . log ( " Added super trampoline for -[ \( dynamicSubclass) \( self . selector) ] IMP: \( imp) " )
105+ try ITKSuperBuilder . addSuperInstanceMethod (
106+ to: subclass,
107+ selector: self . selector
108+ )
109+
110+ Interpose . log ( {
111+ var message = " Added super trampoline for -[ \( subclass) \( self . selector) ] "
112+ if let imp = class_getMethodImplementation ( subclass, self . selector) {
113+ message += " IMP: \( imp) "
114+ }
115+ return message
116+ } ( ) )
90117 } catch {
91- // Interpose.log("Failed to add super implementation to -[\(dynamicClass).\(selector)]: \(error)")
92- throw InterposeError . unknownError ( String ( describing: error) )
118+ throw InterposeError . failedToAddSuperTrampoline (
119+ class: subclass,
120+ selector: self . selector,
121+ underlyingError: error as NSError
122+ )
93123 }
94124 }
95125
96- // Replace IMP (by now we guarantee that it exists)
97- self . storedOriginalIMP = class_replaceMethod ( dynamicSubclass, self . selector, hookIMP, encoding)
98- guard self . storedOriginalIMP != nil else {
126+ guard let imp = class_replaceMethod ( subclass, self . selector, hookIMP, method_getTypeEncoding ( method) ) else {
99127 // This should not happen if the class implements the method or we have installed
100128 // the super trampoline. Instead, we should make the trampoline implementation
101129 // failable.
102130 throw InterposeError . implementationNotFound (
103- class: dynamicSubclass ,
131+ class: subclass ,
104132 selector: self . selector
105133 )
106134 }
135+
136+ self . appliedHookIMP = hookIMP
137+ self . storedOriginalIMP = imp
138+ ObjectHookRegistry . register ( self . handle, for: hookIMP)
139+
107140 Interpose . log ( " Replaced implementation for -[ \( self . class) \( self . selector) ] IMP: \( self . storedOriginalIMP!) -> \( hookIMP) " )
108141 }
109142
110- func restoreImplementation( ) throws {
143+ internal func restoreImplementation( ) throws {
111144 guard let hookIMP = self . appliedHookIMP else { return }
112145 guard let originalIMP = self . storedOriginalIMP else { return }
113146
@@ -126,7 +159,11 @@ final class ObjectHookStrategy: HookStrategy {
126159 }
127160
128161 guard let currentIMP = class_getMethodImplementation ( dynamicSubclass, self . selector) else {
129- throw InterposeError . unknownError ( " No Implementation found " )
162+ // Do we need this???
163+ throw InterposeError . implementationNotFound (
164+ class: self . class,
165+ selector: self . selector
166+ )
130167 }
131168
132169 // We are the topmost hook, replace method.
@@ -156,6 +193,10 @@ final class ObjectHookStrategy: HookStrategy {
156193 // self.dynamicSubclass = nil
157194 }
158195
196+ // ============================================================================ //
197+ // MARK: Helpers
198+ // ============================================================================ //
199+
159200 /// Traverses the object hook chain to find the handle to the parent of this hook, starting
160201 /// from the topmost IMP for the hooked method.
161202 ///
0 commit comments