5
5
// Created by John Holdsworth on 02/11/2017.
6
6
// Copyright © 2017 John Holdsworth. All rights reserved.
7
7
//
8
- // $Id: //depot/ResidentEval/SwiftEval/SwiftEval.swift#5 $
8
+ // $Id: //depot/ResidentEval/SwiftEval/SwiftEval.swift#8 $
9
9
//
10
10
// Basic implementation of ra Swift "eval()" including the
11
11
// mechanics of recompiling a class and loading the new
@@ -55,7 +55,7 @@ extension NSObject {
55
55
// update evalImpl to implement expression
56
56
57
57
if NSObject . lastEvalByClass [ className] != expression,
58
- let newClass = SwiftEval . rebuildClass ( oldClass: oldClass, className: className, extra: extra) ? . first {
58
+ let newClass = SwiftEval . instance . rebuildClass ( oldClass: oldClass, className: className, extra: extra) ? . first {
59
59
60
60
// swizzle new version of evalImpl onto class
61
61
@@ -91,13 +91,19 @@ extension String {
91
91
}
92
92
}
93
93
94
- class SwiftEval {
94
+ @objc
95
+ public class SwiftEval : NSObject {
95
96
96
- static var dylibNumber = 0
97
- static var compileByClass = [ String: String] ( )
97
+ static var instance = SwiftEval ( )
98
98
99
- static func rebuildClass( oldClass: AnyClass ? , className: String , extra: String ? ) -> [ AnyClass ] ? {
100
- let sourceURL = URL ( fileURLWithPath: #file)
99
+ var derivedData : URL ?
100
+ var projectInfo : ( file: URL , info: URL ) ?
101
+
102
+ var dylibNumber = 0
103
+ var compileByClass = [ String: String] ( )
104
+
105
+ func rebuildClass( oldClass: AnyClass ? , className: String , extra: String ? ) -> [ AnyClass ] ? {
106
+ let sourceURL = URL ( fileURLWithPath: className. contains ( " / " ) ? " / " + className : #file)
101
107
guard let derivedData = findDerivedData ( url: sourceURL) else {
102
108
return evalError ( " Could not locate derived data " )
103
109
}
@@ -107,6 +113,8 @@ class SwiftEval {
107
113
108
114
// locate compile command for class
109
115
116
+ dylibNumber += 1
117
+ let tmpfile = " /tmp/eval \( dylibNumber) "
110
118
let regexp = " -primary-file ( \" ([^ \" ]*?/ \( className) \\ .swift) \" |( \\ S*?/ \( className) \\ .swift)) "
111
119
112
120
guard var compileCommand = compileByClass [ className] ?? {
@@ -118,14 +126,14 @@ class SwiftEval {
118
126
echo " Scanning $log "
119
127
# grep log for build of class source
120
128
/usr/bin/gunzip < " $log " | perl -lpe 's/ \\ r/ \\ n/g' | \
121
- time /usr/bin/grep -E ' \( regexp) ' >/tmp/eval .sh && exit 0;
129
+ /usr/bin/grep -E ' \( regexp) ' > \( tmpfile ) .sh && exit 0;
122
130
done;
123
131
exit 1
124
132
""" ) else {
125
133
return nil
126
134
}
127
135
128
- var compileCommand = try ! String ( contentsOfFile: " /tmp/eval .sh" )
136
+ var compileCommand = try ! String ( contentsOfFile: " \( tmpfile ) .sh " )
129
137
compileCommand = compileCommand. components ( separatedBy: " -o " ) [ 0 ]
130
138
compileByClass [ className] = compileCommand
131
139
return compileCommand
@@ -180,15 +188,13 @@ class SwiftEval {
180
188
let projectDir = projectFile. deletingLastPathComponent ( ) . path
181
189
182
190
guard shell ( command: """
183
- cd " \( projectDir) " && \( compileCommand) -o /tmp/eval .o >/tmp/eval .log 2>&1 || (cat /tmp/eval .log && exit 1)
191
+ cd " \( projectDir) " && \( compileCommand) -o \( tmpfile ) .o > \( tmpfile ) .log 2>&1 || (cat \( tmpfile ) .log && exit 1)
184
192
""" ) else {
185
- return evalError ( " Re-compilation failed \n \( try ! String ( contentsOfFile: " /tmp/eval .log" ) ) " )
193
+ return evalError ( " Re-compilation failed \n \( try ! String ( contentsOfFile: " \( tmpfile ) .log " ) ) " )
186
194
}
187
195
188
196
// link object to create dynamic library
189
197
190
- dylibNumber += 1
191
- let dylib = " /tmp/eval \( dylibNumber) .dylib "
192
198
let xcode = " /Applications/Xcode.app/Contents/Developer "
193
199
194
200
#if os(iOS)
@@ -200,27 +206,27 @@ class SwiftEval {
200
206
#endif
201
207
202
208
guard shell ( command: """
203
- \( xcode) /Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -arch x86_64 -bundle \( osSpecific) -dead_strip -Xlinker -objc_abi_version -Xlinker 2 -fobjc-arc /tmp/eval .o -L \( frameworkPath) -F \( frameworkPath) -rpath \( frameworkPath) -o \( dylib )
209
+ \( xcode) /Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -arch x86_64 -bundle \( osSpecific) -dead_strip -Xlinker -objc_abi_version -Xlinker 2 -fobjc-arc \( tmpfile ) .o -L \( frameworkPath) -F \( frameworkPath) -rpath \( frameworkPath) -o \( tmpfile ) .dylib
204
210
""" ) else {
205
211
return evalError ( " Link failed " )
206
212
}
207
213
208
214
#if os(iOS)
209
215
// have to delegate code signing to macOS "signer" service
210
- guard ( try ? String ( contentsOf: URL ( string: " http://localhost:8899 " + dylib) !) ) != nil else {
216
+ guard ( try ? String ( contentsOf: URL ( string: " http://localhost:8899 \( tmpfile ) . dylib" ) !) ) != nil else {
211
217
return evalError ( " Codesign failed. Is 'signer' daemon running? " )
212
218
}
213
219
#else
214
220
guard shell ( command: """
215
- export CODESIGN_ALLOCATE= \( xcode) /Toolchains/XcodeDefault.xctoolchain/usr/bin/codesign_allocate; codesign --force -s '-' " \( dylib ) "
221
+ export CODESIGN_ALLOCATE= \( xcode) /Toolchains/XcodeDefault.xctoolchain/usr/bin/codesign_allocate; codesign --force -s '-' " \( tmpfile ) .dylib "
216
222
""" ) else {
217
223
return evalError ( " Codesign failed " )
218
224
}
219
225
#endif
220
226
221
227
// load patch .dylib into process with new version of class
222
228
223
- guard let dl = dlopen ( dylib, RTLD_NOW) else {
229
+ guard let dl = dlopen ( " \( tmpfile ) . dylib" , RTLD_NOW) else {
224
230
return evalError ( " dlopen() error: \( String ( cString: dlerror ( ) ) ) " )
225
231
}
226
232
@@ -240,18 +246,18 @@ class SwiftEval {
240
246
return [ unsafeBitCast ( newSymbol, to: AnyClass . self) ]
241
247
}
242
248
else {
243
- guard shell ( command: " \( xcode) /Toolchains/XcodeDefault.xctoolchain/usr/bin/nm /tmp/eval .o | grep 'S _OBJC_CLASS_$_' | awk '{print $3}' >/tmp/eval .classes " ) else {
249
+ guard shell ( command: " \( xcode) /Toolchains/XcodeDefault.xctoolchain/usr/bin/nm \( tmpfile ) .o | grep 'S _OBJC_CLASS_$_' | awk '{print $3}' > \( tmpfile ) .classes " ) else {
244
250
return evalError ( " Could not list classes " )
245
251
}
246
- guard var symbols = ( try ? String ( contentsOfFile: " /tmp/eval .classes" ) ) ? . components ( separatedBy: " \n " ) else {
252
+ guard var symbols = ( try ? String ( contentsOfFile: " \( tmpfile ) .classes " ) ) ? . components ( separatedBy: " \n " ) else {
247
253
return evalError ( " Could not load class list " )
248
254
}
249
255
symbols. removeLast ( )
250
256
return symbols. map { unsafeBitCast ( dlsym ( dl, String ( $0. dropFirst ( ) ) ) !, to: AnyClass . self) }
251
257
}
252
258
}
253
259
254
- static func findDerivedData( url: URL ) -> URL ? {
260
+ func findDerivedData( url: URL ) -> URL ? {
255
261
let dir = url. deletingLastPathComponent ( )
256
262
if dir. path == " / " {
257
263
return nil
@@ -265,7 +271,7 @@ class SwiftEval {
265
271
return findDerivedData ( url: dir)
266
272
}
267
273
268
- static func findProject( for source: URL , derivedData: URL ) -> ( URL , URL ) ? {
274
+ func findProject( for source: URL , derivedData: URL ) -> ( URL , URL ) ? {
269
275
let dir = source. deletingLastPathComponent ( )
270
276
if dir. path == " / " {
271
277
return nil
@@ -280,11 +286,11 @@ class SwiftEval {
280
286
return findProject ( for: dir, derivedData: derivedData)
281
287
}
282
288
283
- static func file( withExt ext: String , in files: [ String ] ) -> String ? {
289
+ func file( withExt ext: String , in files: [ String ] ) -> String ? {
284
290
return files. first { URL ( fileURLWithPath: $0) . pathExtension == ext }
285
291
}
286
292
287
- static func logDir( project: URL , derivedData: URL ) -> URL ? {
293
+ func logDir( project: URL , derivedData: URL ) -> URL ? {
288
294
let filemgr = FileManager . default
289
295
let projectPrefix = project. deletingPathExtension ( )
290
296
. lastPathComponent. replacingOccurrences ( of: " " , with: " _ " )
@@ -305,7 +311,7 @@ class SwiftEval {
305
311
. first
306
312
}
307
313
308
- static func shell( command: String ) -> Bool {
314
+ func shell( command: String ) -> Bool {
309
315
debug ( command)
310
316
311
317
let pid = fork ( )
0 commit comments