11import Foundation
22
3- // TODO: Make originalIMP private
4-
53/// A runtime hook that interposes a single instance method on a class or object.
64public final class Hook {
75
@@ -72,7 +70,7 @@ public final class Hook {
7270 }
7371
7472 // ============================================================================ //
75- // MARK: ...
73+ // MARK: Target Info
7674 // ============================================================================ //
7775
7876 /// The class whose instance method is being interposed.
@@ -88,47 +86,76 @@ public final class Hook {
8886 public var selector : Selector {
8987 self . strategy. selector
9088 }
89+
90+ // ============================================================================ //
91+ // MARK: State
92+ // ============================================================================ //
9193
9294 /// The current state of the hook.
9395 public internal( set) var state = HookState . pending
9496
95- private var _strategy : HookStrategy !
96-
97- private var strategy : HookStrategy {
98- self . _strategy
99- }
100-
101- /// The effective original implementation of the hook. Might be looked up at runtime.
102- /// Do not cache this.
103- internal var originalIMP : IMP ? {
104- self . strategy. originalIMP
105- }
97+ // ============================================================================ //
98+ // MARK: Applying & Reverting
99+ // ============================================================================ //
106100
107101 /// Applies the hook by interposing the method implementation.
108102 public func apply( ) throws {
109- try execute ( newState: . active) {
103+ guard self . state == . pending else { return }
104+
105+ do {
110106 try self . strategy. replaceImplementation ( )
107+ self . state = . active
108+ } catch {
109+ self . state = . failed
110+ throw error
111111 }
112112 }
113113
114114 /// Reverts the hook, restoring the original method implementation.
115115 public func revert( ) throws {
116- try execute ( newState: . pending) {
116+ guard self . state == . active else { return }
117+
118+ do {
117119 try self . strategy. restoreImplementation ( )
120+ self . state = . pending
121+ } catch {
122+ self . state = . failed
123+ throw error
118124 }
119125 }
126+
127+ // ============================================================================ //
128+ // MARK: Original Implementation
129+ // ============================================================================ //
120130
121- private func execute( newState: HookState , task: ( ) throws -> Void ) throws {
122- do {
123- try task ( )
124- state = newState
125- } catch let error as InterposeError {
126- state = . failed
127- throw error
128- }
131+ // TODO: Make originalIMP private
132+
133+ /// The effective original implementation of the hook. Might be looked up at runtime.
134+ /// Do not cache this.
135+ internal var originalIMP : IMP ? {
136+ self . strategy. originalIMP
129137 }
138+
139+ // ============================================================================ //
140+ // MARK: Underlying Strategy
141+ // ============================================================================ //
142+
143+ private var _strategy : HookStrategy !
144+
145+ private var strategy : HookStrategy {
146+ self . _strategy
147+ }
148+
149+ }
150+
151+ extension Hook : CustomDebugStringConvertible {
152+ public var debugDescription : String {
153+ self . strategy. debugDescription
154+ }
155+ }
130156
131- // TODO: Rename to `cleanUp()`
157+ // TODO: Try to make clean-up automatic in deinit
158+ extension Hook {
132159 public func cleanup( ) {
133160 switch state {
134161 case . pending:
@@ -140,13 +167,6 @@ public final class Hook {
140167 Interpose . log ( " Leaking -[ \( `class`) . \( selector) ] IMP: \( self . strategy. hookIMP) " )
141168 }
142169 }
143-
144- }
145-
146- extension Hook : CustomDebugStringConvertible {
147- public var debugDescription : String {
148- self . strategy. debugDescription
149- }
150170}
151171
152172fileprivate enum HookTarget {
0 commit comments