5
5
// Created by John Holdsworth on 05/11/2017.
6
6
// Copyright © 2017 John Holdsworth. All rights reserved.
7
7
//
8
- // $Id: //depot/ResidentEval/SwiftEval/SwiftInjection.swift#4 $
8
+ // $Id: //depot/ResidentEval/SwiftEval/SwiftInjection.swift#8 $
9
9
//
10
10
// Cut-down version of code injection in Swift. Uses code
11
11
// from SwiftEval.swift to recompile and reload class.
14
14
#if arch(x86_64) // simulator/macOS only
15
15
import Foundation
16
16
17
+ @objc public protocol SwiftInjected {
18
+ @objc optional func injected( )
19
+ }
20
+
17
21
#if os(iOS)
18
22
import UIKit
19
23
@@ -33,39 +37,72 @@ extension UIViewController {
33
37
viewDidLoad ( )
34
38
}
35
39
}
40
+ #else
41
+ import Cocoa
36
42
#endif
37
43
38
44
extension NSObject {
39
45
40
46
public func inject( ) {
41
- if let oldClass: AnyClass = object_getClass ( self ) ,
42
- let newClass = SwiftEval . rebuildClass ( oldClass: oldClass, className: " \( oldClass) " , extra: nil ) {
47
+ if let oldClass: AnyClass = object_getClass ( self ) {
48
+ SwiftInjection . inject ( oldClass: oldClass, className: " \( oldClass) " )
49
+ }
50
+ }
43
51
44
- // old-school swizzle Objective-C class & instance methods
45
- injection ( swizzle: object_getClass ( newClass) , onto: object_getClass ( oldClass) )
46
- injection ( swizzle: newClass, onto: oldClass)
52
+ @objc
53
+ public class func inject( file: String ) {
54
+ let path = URL ( fileURLWithPath: file) . deletingPathExtension ( ) . path
55
+ SwiftInjection . inject ( oldClass: nil , className: String ( path. dropFirst ( ) ) )
56
+ }
57
+ }
47
58
48
- // overwrite Swift vtable of existing class with implementations from new class
49
- let existingClass = unsafeBitCast ( oldClass, to: UnsafeMutablePointer< ClassMetadataSwift> . self )
50
- let classMetadata = unsafeBitCast ( newClass, to: UnsafeMutablePointer< ClassMetadataSwift> . self )
59
+ class SwiftInjection {
51
60
52
- func byteAddr< T> ( _ location: UnsafeMutablePointer < T > ) -> UnsafeMutablePointer < UInt8 > {
53
- return location. withMemoryRebound ( to: UInt8 . self, capacity: 1 ) { $0 }
54
- }
61
+ static func inject( oldClass: AnyClass ? , className: String ) {
62
+ if let newClasses = SwiftEval . rebuildClass ( oldClass: oldClass, className: className, extra: nil ) {
63
+ let oldClasses = //oldClass != nil ? [oldClass!] :
64
+ newClasses. map { objc_getClass ( class_getName ( $0) ) as! AnyClass }
65
+ for i in 0 ..< oldClasses. count {
66
+ let oldClass : AnyClass = oldClasses [ i] , newClass : AnyClass = newClasses [ i]
67
+
68
+ // old-school swizzle Objective-C class & instance methods
69
+ injection ( swizzle: object_getClass ( newClass) , onto: object_getClass ( oldClass) )
70
+ injection ( swizzle: newClass, onto: oldClass)
71
+
72
+ // overwrite Swift vtable of existing class with implementations from new class
73
+ let existingClass = unsafeBitCast ( oldClass, to: UnsafeMutablePointer< ClassMetadataSwift> . self )
74
+ let classMetadata = unsafeBitCast ( newClass, to: UnsafeMutablePointer< ClassMetadataSwift> . self )
55
75
56
- let vtableOffset = byteAddr ( & existingClass . pointee . IVarDestroyer ) - byteAddr ( existingClass )
57
- let vtableLength = Int ( existingClass . pointee . ClassSize -
58
- existingClass . pointee . ClassAddressPoint ) - vtableOffset
76
+ func byteAddr< T > ( _ location : UnsafeMutablePointer < T > ) -> UnsafeMutablePointer < UInt8 > {
77
+ return location . withMemoryRebound ( to : UInt8 . self , capacity : 1 ) { $0 }
78
+ }
59
79
60
- NSLog ( " \( unsafeBitCast ( classMetadata, to: AnyClass . self) ) , vtable length: \( vtableLength) " )
61
- memcpy ( byteAddr ( existingClass) + vtableOffset, byteAddr ( classMetadata) + vtableOffset, vtableLength)
80
+ let vtableOffset = byteAddr ( & existingClass. pointee. IVarDestroyer) - byteAddr( existingClass)
81
+ let vtableLength = Int ( existingClass. pointee. ClassSize -
82
+ existingClass. pointee. ClassAddressPoint) - vtableOffset
83
+
84
+ NSLog ( " \( unsafeBitCast ( classMetadata, to: AnyClass . self) ) , vtable length: \( vtableLength) " )
85
+ memcpy ( byteAddr ( existingClass) + vtableOffset, byteAddr ( classMetadata) + vtableOffset, vtableLength)
86
+
87
+ // implement -injected() method using sweep of objects in application
88
+ if class_getInstanceMethod ( oldClass, #selector( SwiftInjected . injected) ) != nil {
89
+ #if os(iOS)
90
+ let app = UIApplication . shared
91
+ #else
92
+ let app = NSApplication . shared
93
+ #endif
94
+ let seeds : [ Any ] = [ app. delegate as Any ] + app. windows
95
+ sweepValue ( seeds, for: oldClass)
96
+ seen. removeAll ( )
97
+ }
98
+ }
62
99
63
100
let notification = Notification . Name ( " INJECTION_BUNDLE_NOTIFICATION " )
64
- NotificationCenter . default. post ( name: notification, object: [ oldClass ] )
101
+ NotificationCenter . default. post ( name: notification, object: oldClasses )
65
102
}
66
103
}
67
104
68
- private func injection( swizzle newClass: AnyClass ? , onto oldClass: AnyClass ? ) {
105
+ static func injection( swizzle newClass: AnyClass ? , onto oldClass: AnyClass ? ) {
69
106
var methodCount : UInt32 = 0
70
107
if let methods = class_copyMethodList ( newClass, & methodCount) {
71
108
for i in 0 ..< Int ( methodCount) {
@@ -76,6 +113,104 @@ extension NSObject {
76
113
free ( methods)
77
114
}
78
115
}
116
+
117
+ static func sweepValue( _ value: Any , for targetClass: AnyClass ) {
118
+ let mirror = Mirror ( reflecting: value)
119
+ if var style = mirror. displayStyle {
120
+ if _typeName ( mirror. subjectType) . hasPrefix ( " Swift.ImplicitlyUnwrappedOptional< " ) {
121
+ style = . optional
122
+ }
123
+ switch style {
124
+ case . set:
125
+ fallthrough
126
+ case . collection:
127
+ for (_, child) in mirror. children {
128
+ sweepValue ( child, for: targetClass)
129
+ }
130
+ return
131
+ case . dictionary:
132
+ for (_, child) in mirror. children {
133
+ for (_, element) in Mirror ( reflecting: child) . children {
134
+ sweepValue ( element, for: targetClass)
135
+ }
136
+ }
137
+ return
138
+ case . class:
139
+ sweepInstance ( value as AnyObject , for: targetClass)
140
+ return
141
+ case . optional:
142
+ if let some = mirror. children. first? . value {
143
+ sweepValue ( some, for: targetClass)
144
+ }
145
+ return
146
+ default :
147
+ break
148
+ }
149
+ }
150
+
151
+ if let style = mirror. displayStyle {
152
+ switch style {
153
+ case . enum:
154
+ if let evals = mirror. children. first? . value {
155
+ sweepValue ( evals, for: targetClass)
156
+ }
157
+ case . tuple:
158
+ sweepMembers ( value, for: targetClass)
159
+ case . struct:
160
+ sweepMembers ( value, for: targetClass)
161
+ default :
162
+ break
163
+ }
164
+ }
165
+ }
166
+
167
+ static var seen = [ UnsafeRawPointer: Bool] ( )
168
+
169
+ static func sweepInstance( _ instance: AnyObject , for targetClass: AnyClass ) {
170
+ let reference = unsafeBitCast ( instance, to: UnsafeRawPointer . self)
171
+ if seen [ reference] == nil {
172
+ seen [ reference] = true
173
+
174
+ if object_getClass ( instance) == targetClass {
175
+ let proto = unsafeBitCast ( instance, to: SwiftInjected . self)
176
+ proto. injected ? ( )
177
+ }
178
+
179
+ sweepMembers ( instance, for: targetClass)
180
+ sweepIvars ( instance, for: targetClass)
181
+ }
182
+ }
183
+
184
+ static func sweepMembers( _ instance: Any , for targetClass: AnyClass ) {
185
+ var mirror : Mirror ? = Mirror ( reflecting: instance)
186
+ while mirror != nil {
187
+ for (_, value) in mirror!. children {
188
+ sweepValue ( value, for: targetClass)
189
+ }
190
+ mirror = mirror!. superclassMirror
191
+ }
192
+ }
193
+
194
+ static func sweepIvars( _ instance: AnyObject , for targetClass: AnyClass ) {
195
+ var icnt : UInt32 = 0 , cls : AnyClass ? = object_getClass ( instance) !
196
+ let object = " @ " . utf16. first!
197
+ while cls != nil && cls != NSURL . self {
198
+ if let ivars = class_copyIvarList ( cls, & icnt) {
199
+ for i in 0 ..< Int ( icnt) {
200
+ if let type = ivar_getTypeEncoding ( ivars [ i] ) , type [ 0 ] == object {
201
+ ( unsafeBitCast ( instance, to: UnsafePointer< Int8> . self ) + ivar_getOffset( ivars [ i] ) )
202
+ . withMemoryRebound ( to: AnyObject ? . self, capacity: 1 ) {
203
+ if let obj = $0. pointee {
204
+ sweepValue ( obj, for: targetClass)
205
+ }
206
+ }
207
+ }
208
+ }
209
+ free ( ivars)
210
+ }
211
+ cls = class_getSuperclass ( cls)
212
+ }
213
+ }
79
214
}
80
215
81
216
/**
0 commit comments