forked from johnno1962/SwiftTrace
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSwiftAspects.swift
122 lines (108 loc) · 4.86 KB
/
SwiftAspects.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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//
// SwiftAspects.swift
// SwiftTrace
//
// Created by John Holdsworth on 20/04/2020.
// Copyright © 2020 John Holdsworth. All rights reserved.
//
// Repo: https://github.com/johnno1962/SwiftTrace
// $Id: //depot/SwiftTrace/SwiftTrace/SwiftAspects.swift#13 $
//
// Add aspects to Swift methods
// ============================
//
import Foundation
extension SwiftTrace {
public typealias EntryAspect = (_ swizzle: Swizzle, _ stack: inout EntryStack) -> Void
public typealias ExitAspect = (_ swizzle: Swizzle, _ stack: inout ExitStack) -> Void
/**
Add a closure aspect to be called before or after a "Swizzle" is called
- parameter methodName: - unmangled name of Method for aspect
- parameter onEntry: - closure to be called before "Swizzle" is called
- parameter onExit: - closure to be called after "Swizzle" returns
*/
open class func addAspect(methodName: String,
patchClass: Aspect.Type = Aspect.self,
onEntry: EntryAspect? = nil,
onExit: ExitAspect? = nil,
replaceWith: nullImplementationType? = nil) -> Bool {
return forAllClasses {
(aClass, stop) in
stop = addAspect(aClass: aClass, methodName: methodName,
onEntry: onEntry, onExit: onExit, replaceWith: replaceWith)
}
}
/**
Add a closure aspect to be called before or after a "Swizzle" is called
- parameter toClass: - specifying the class to add aspect is more efficient
- parameter methodName: - unmangled name of Method for aspect
- parameter onEntry: - closure to be called before "Swizzle" is called
- parameter onExit: - closure to be called after "Swizzle" returns
*/
open class func addAspect(aClass: AnyClass, methodName: String,
patchClass: Aspect.Type = Aspect.self,
onEntry: EntryAspect? = nil,
onExit: ExitAspect? = nil,
replaceWith: nullImplementationType? = nil) -> Bool {
return iterateMethods(ofClass: aClass) {
(name, slotIndex, vtableSlot, stop) in
if name == methodName, let method = patchClass.init(name: name,
vtableSlot: vtableSlot, onEntry: onEntry,
onExit: onExit, replaceWith: replaceWith) {
vtableSlot.pointee = method.forwardingImplementation
stop = true
}
}
}
/**
Add a closure aspect to be called before or after a "Swizzle" is called
- parameter methodName: - unmangled name of Method for aspect
*/
@discardableResult
open class func removeAspect(methodName: String) -> Bool {
return forAllClasses {
(aClass, stop) in
stop = removeAspect(aClass: aClass, methodName: methodName)
}
}
/**
Add a closure aspect to be called before or after a "Swizzle" is called
- parameter aClass: - specifying the class to remove aspect is more efficient
- parameter methodName: - unmangled name of Method for aspect
*/
@discardableResult
open class func removeAspect(aClass: AnyClass, methodName: String) -> Bool {
return iterateMethods(ofClass: aClass) {
(name, slotIndex, vtableSlot, stop) in
if name == methodName,
let swizzle = SwiftTrace.lastSwiftTrace.activeSwizzles[unsafeBitCast(vtableSlot.pointee, to: IMP.self)] {
swizzle.remove()
stop = true
}
}
}
/**
Internal class used in the implementation of aspects
*/
open class Aspect: Decorated {
let entryAspect: EntryAspect?
let exitAspect: ExitAspect?
public required init?(name: String, vtableSlot: UnsafeMutablePointer<SIMP>? = nil, original: OpaquePointer? = nil,
onEntry: EntryAspect? = nil, onExit: ExitAspect? = nil,
replaceWith: nullImplementationType? = nil) {
self.entryAspect = onEntry
self.exitAspect = onExit
super.init(name: name, vtableSlot: vtableSlot,
original: original, replaceWith: replaceWith)
}
public required init?(name: String, vtableSlot: UnsafeMutablePointer<SIMP>? = nil, objcMethod: Method? = nil, objcClass: AnyClass?, original: OpaquePointer? = nil, replaceWith: nullImplementationType? = nil) {
fatalError("Aspect.init(name:vtableSlot:objcMethod:objcClass:replaceWith:) should not be used")
}
open override func onEntry(stack: inout EntryStack) {
entryAspect?(self, &stack)
}
open override func onExit(stack: inout ExitStack) {
exitAspect?(self, &stack)
}
}
}