-
Notifications
You must be signed in to change notification settings - Fork 49
/
Copy pathClassHook.swift
47 lines (39 loc) · 1.89 KB
/
ClassHook.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import Foundation
extension Interpose {
/// A hook to an instance method and stores both the original and new implementation.
final public class ClassHook<MethodSignature, HookSignature>: TypedHook<MethodSignature, HookSignature> {
/* HookSignature?: This must be optional or swift runtime will crash.
Or swiftc may segfault. Compiler bug? */
/// Initialize a new hook to interpose an instance method.
public init(`class`: AnyClass, selector: Selector,
implementation: (ClassHook<MethodSignature, HookSignature>) -> HookSignature?) throws {
try super.init(class: `class`, selector: selector)
replacementIMP = imp_implementationWithBlock(implementation(self) as Any)
}
override func replaceImplementation() throws {
let method = try validate()
origIMP = try klass.replace(method: method, imp: replacementIMP)
Interpose.log("Swizzled -[\(`class`).\(selector)] IMP: \(origIMP!) -> \(replacementIMP!)")
}
override func resetImplementation() throws {
let method = try validate(expectedState: .interposed)
precondition(origIMP != nil)
let previousIMP = try klass.replace(method: method, imp: origIMP!)
guard previousIMP == replacementIMP else {
throw InterposeError.unexpectedImplementation(`class`, selector, previousIMP)
}
Interpose.log("Restored -[\(`class`).\(selector)] IMP: \(origIMP!)")
}
/// The original implementation is cached at hook time.
public override var original: MethodSignature {
unsafeBitCast(origIMP, to: MethodSignature.self)
}
}
}
#if DEBUG
extension Interpose.ClassHook: CustomDebugStringConvertible {
public var debugDescription: String {
return "\(selector) -> \(String(describing: origIMP))"
}
}
#endif