1 | ;;;-*-Mode: LISP; Package: CCL -*- |
---|
2 | |
---|
3 | (in-package "CCL") |
---|
4 | |
---|
5 | (eval-when (:compile-toplevel :load-toplevel :execute) |
---|
6 | (require "BRIDGE")) |
---|
7 | |
---|
8 | (defun allocate-objc-object (class) |
---|
9 | (#/alloc class)) |
---|
10 | |
---|
11 | (defun conforms-to-protocol (thing protocol) |
---|
12 | (#/conformsToProtocol: thing (objc-protocol-address protocol))) |
---|
13 | |
---|
14 | |
---|
15 | |
---|
16 | |
---|
17 | #+(or apple-objc cocotron-objc) |
---|
18 | (defun iterate-over-objc-classes (fn) |
---|
19 | (let* ((n (#_objc_getClassList (%null-ptr) 0))) |
---|
20 | (declare (fixnum n)) |
---|
21 | (%stack-block ((buffer (the fixnum (ash n target::word-shift)))) |
---|
22 | (#_objc_getClassList buffer n) |
---|
23 | (do* ((i 0 (1+ i))) |
---|
24 | ((= i n) (values)) |
---|
25 | (declare (fixnum i)) |
---|
26 | (funcall fn (paref buffer (:* :id) i)))))) |
---|
27 | |
---|
28 | #+(or apple-objc cocotron-objc) |
---|
29 | (defun count-objc-classes () |
---|
30 | (#_objc_getClassList (%null-ptr) 0)) |
---|
31 | |
---|
32 | #+gnu-objc |
---|
33 | (defun iterate-over-objc-classes (fn) |
---|
34 | (rletZ ((enum-state :address)) |
---|
35 | (loop |
---|
36 | (let* ((class (#_objc_next_class enum-state))) |
---|
37 | (if (%null-ptr-p class) |
---|
38 | (return) |
---|
39 | (funcall fn class)))))) |
---|
40 | |
---|
41 | #+gnu-objc |
---|
42 | (defun count-objc-classes () |
---|
43 | (let* ((n 0)) |
---|
44 | (declare (fixnum n)) |
---|
45 | (rletZ ((enum-state :address)) |
---|
46 | (if (%null-ptr-p (#_objc_next_class enum-state)) |
---|
47 | (return n) |
---|
48 | (incf n))))) |
---|
49 | |
---|
50 | (defun %note-protocol (p) |
---|
51 | (with-macptrs ((cname (objc-message-send p "name" :address))) |
---|
52 | (let* ((namelen (%cstrlen cname)) |
---|
53 | (name (make-string namelen))) |
---|
54 | (declare (dynamic-extent name)) |
---|
55 | (%str-from-ptr cname namelen name) |
---|
56 | (let* ((proto (or (gethash name *objc-protocols*) |
---|
57 | (progn |
---|
58 | (setq name (subseq name 0)) |
---|
59 | (setf (gethash name *objc-protocols*) |
---|
60 | (make-objc-protocol :name name)))))) |
---|
61 | (unless (objc-protocol-address proto) |
---|
62 | (setf (objc-protocol-address proto) (%inc-ptr p 0))) |
---|
63 | proto)))) |
---|
64 | |
---|
65 | (defun note-class-protocols (class) |
---|
66 | #-(or apple-objc-2.0 cocotron-objc) |
---|
67 | (do* ((protocols (pref class :objc_class.protocols) |
---|
68 | (pref protocols :objc_protocol_list.next))) |
---|
69 | ((%null-ptr-p protocols)) |
---|
70 | (let* ((count (pref protocols :objc_protocol_list.count))) |
---|
71 | (with-macptrs ((list (pref protocols :objc_protocol_list.list))) |
---|
72 | (dotimes (i count) |
---|
73 | (with-macptrs ((p (paref list (:* (:* (:struct :<P>rotocol))) i))) |
---|
74 | (%note-protocol p)))))) |
---|
75 | #+(or apple-objc-2.0 cocotron-objc) |
---|
76 | (rlet ((p-out-count :int 0)) |
---|
77 | (with-macptrs ((protocols (#_class_copyProtocolList class p-out-count))) |
---|
78 | (let* ((n (pref p-out-count :int))) |
---|
79 | (dotimes (i n) |
---|
80 | (with-macptrs ((p (paref protocols (:* (:* (:struct :<P>rotocol))) i))) |
---|
81 | (%note-protocol p)))) |
---|
82 | (unless (%null-ptr-p protocols) (#_free protocols))))) |
---|
83 | |
---|
84 | |
---|
85 | (defun map-objc-classes (&optional (lookup-in-database-p t)) |
---|
86 | (iterate-over-objc-classes |
---|
87 | #'(lambda (class) |
---|
88 | (note-class-protocols class) |
---|
89 | (install-foreign-objc-class class lookup-in-database-p)))) |
---|
90 | |
---|
91 | (let* ((nclasses 0) |
---|
92 | (lock (make-lock))) |
---|
93 | (declare (fixnum nclasses)) |
---|
94 | (defun maybe-map-objc-classes (&optional use-db) |
---|
95 | (with-lock-grabbed (lock) |
---|
96 | (let* ((new (count-objc-classes))) |
---|
97 | (declare (fixnum new)) |
---|
98 | (unless (= nclasses new) |
---|
99 | (setq nclasses new) |
---|
100 | (map-objc-classes use-db)) |
---|
101 | t))) |
---|
102 | (defun reset-objc-class-count () |
---|
103 | (with-lock-grabbed (lock) |
---|
104 | (setq nclasses 0)))) |
---|
105 | |
---|
106 | (register-objc-class-decls) |
---|
107 | (maybe-map-objc-classes t) |
---|
108 | (register-objc-init-messages) |
---|
109 | (register-objc-set-messages) |
---|
110 | |
---|
111 | #+gnu-objc |
---|
112 | (defun iterate-over-class-methods (class method-function) |
---|
113 | (do* ((mlist (pref class :objc_class.methods) |
---|
114 | (pref mlist :objc_method_list.method_next))) |
---|
115 | ((%null-ptr-p mlist)) |
---|
116 | (do* ((n (pref mlist :objc_method_list.method_count)) |
---|
117 | (i 0 (1+ i)) |
---|
118 | (method (pref mlist :objc_method_list.method_list) |
---|
119 | (%incf-ptr method (record-length :objc_method)))) |
---|
120 | ((= i n)) |
---|
121 | (declare (fixnum i n)) |
---|
122 | (funcall method-function method class)))) |
---|
123 | |
---|
124 | #+gnu-objc |
---|
125 | (progn |
---|
126 | ;; Er, um ... this needs lots-o-work. |
---|
127 | (let* ((objc-class-count 0)) |
---|
128 | (defun reset-objc-class-count () (setq objc-class-count 0)) |
---|
129 | (defun note-all-library-methods (method-function) |
---|
130 | (do* ((i objc-class-count (1+ i)) |
---|
131 | (class (id->objc-class i) (id->objc-class i))) |
---|
132 | ((eq class 0)) |
---|
133 | (iterate-over-class-methods class method-function) |
---|
134 | (iterate-over-class-methods (id->objc-metaclass i) method-function)))) |
---|
135 | (def-ccl-pointers revive-objc-classes () |
---|
136 | (reset-objc-class-count))) |
---|
137 | |
---|
138 | (defun retain-obcj-object (x) |
---|
139 | (objc-message-send x "retain")) |
---|
140 | |
---|
141 | |
---|
142 | #+apple-objc-2.0 |
---|
143 | (progn |
---|
144 | (defun setup-objc-exception-globals () |
---|
145 | (flet ((set-global (offset name) |
---|
146 | (setf (%get-ptr (%int-to-ptr (+ (target-nil-value) (%kernel-global-offset offset)))) |
---|
147 | (foreign-symbol-address name)))) |
---|
148 | (set-global 'objc-2-personality "___objc_personality_v0") |
---|
149 | (set-global 'objc-2-begin-catch "objc_begin_catch") |
---|
150 | (set-global 'objc-2-end-catch "objc_end_catch") |
---|
151 | (set-global 'unwind-resume "__Unwind_Resume"))) |
---|
152 | |
---|
153 | |
---|
154 | (def-ccl-pointers setup-objc-exception-handling () |
---|
155 | (setup-objc-exception-globals)) |
---|
156 | |
---|
157 | (setup-objc-exception-globals) |
---|
158 | ) |
---|
159 | |
---|
160 | |
---|
161 | (defvar *condition-id-map* (make-id-map) "Map lisp conditions to small integers") |
---|
162 | |
---|
163 | ;;; Encapsulate an NSException in a lisp condition. |
---|
164 | (define-condition ns-exception (error) |
---|
165 | ((ns-exception :initarg :ns-exception :accessor ns-exception)) |
---|
166 | (:report (lambda (c s) |
---|
167 | (format s "Objective-C runtime exception: ~&~a" |
---|
168 | (nsobject-description (ns-exception c)))))) |
---|
169 | |
---|
170 | |
---|
171 | |
---|
172 | (defclass ns-lisp-exception (ns::ns-exception) |
---|
173 | ((condition :initarg :condition :initform nil :reader ns-lisp-exception-condition)) |
---|
174 | (:metaclass ns::+ns-object)) |
---|
175 | |
---|
176 | (objc:defmethod #/init ((self ns-lisp-exception)) |
---|
177 | (#/initWithName:reason:userInfo: self #@"lisp exception" #@"lisp exception" +null-ptr+)) |
---|
178 | |
---|
179 | |
---|
180 | (defun recognize-objc-exception (x) |
---|
181 | (if (typep x 'ns:ns-exception) |
---|
182 | (ns-exception->lisp-condition x))) |
---|
183 | |
---|
184 | (pushnew 'recognize-objc-exception *foreign-error-condition-recognizers*) |
---|
185 | |
---|
186 | (defun %make-nsstring (string) |
---|
187 | (with-encoded-cstrs :utf-8 ((s string)) |
---|
188 | (#/initWithUTF8String: (#/alloc ns:ns-string) s))) |
---|
189 | |
---|
190 | (defmacro with-autoreleased-nsstring ((nsstring lisp-string) &body body) |
---|
191 | `(let* ((,nsstring (%make-nsstring ,lisp-string))) |
---|
192 | (#/autorelease ,nsstring) |
---|
193 | ,@body)) |
---|
194 | |
---|
195 | (defmacro with-autoreleased-nsstrings (speclist &body body) |
---|
196 | (with-specs-aux 'with-autoreleased-nsstring speclist body)) |
---|
197 | |
---|
198 | (defun retain-objc-instance (instance) |
---|
199 | (#/retain instance)) |
---|
200 | |
---|
201 | ;;; May have to create/release autorelease pools before the bridge |
---|
202 | ;;; is fully reinitialized, so use low-level OBJC-MESSAGE-SEND |
---|
203 | ;;; and @class. |
---|
204 | (defun create-autorelease-pool () |
---|
205 | (objc-message-send |
---|
206 | (objc-message-send (@class "NSAutoreleasePool") "alloc") "init")) |
---|
207 | |
---|
208 | (defun release-autorelease-pool (p) |
---|
209 | (objc-message-send p "release" :void)) |
---|
210 | |
---|
211 | |
---|
212 | #-ascii-only |
---|
213 | (progn |
---|
214 | #-windows-target |
---|
215 | (defun lisp-string-from-nsstring (nsstring) |
---|
216 | ;; The NSData object created here is autoreleased. |
---|
217 | (let* ((data (#/dataUsingEncoding:allowLossyConversion: |
---|
218 | nsstring |
---|
219 | #+little-endian-target #x9c000100 |
---|
220 | #+big-endian-target #x98000100 |
---|
221 | nil))) |
---|
222 | (unless (%null-ptr-p data) |
---|
223 | (let* ((nbytes (#/length data)) |
---|
224 | (string (make-string (ash nbytes -2)))) |
---|
225 | ;; BLT the 4-byte code-points from the NSData object |
---|
226 | ;; to the string, return the string. |
---|
227 | (%copy-ptr-to-ivector (#/bytes data) 0 string 0 nbytes))))) |
---|
228 | |
---|
229 | #+windows-target |
---|
230 | (defun lisp-string-from-nsstring (nsstring) |
---|
231 | (let* ((n (#/length nsstring))) |
---|
232 | (%stack-block ((buf (* (1+ n) (record-length :unichar)))) |
---|
233 | (#/getCharacters: nsstring buf) |
---|
234 | (setf (%get-unsigned-word buf (+ n n)) 0) |
---|
235 | (%get-native-utf-16-cstring buf)))) |
---|
236 | |
---|
237 | ) |
---|
238 | |
---|
239 | #+ascii-only |
---|
240 | (defun lisp-string-from-nsstring (nsstring) |
---|
241 | (with-macptrs (cstring) |
---|
242 | (%setf-macptr cstring |
---|
243 | (#/cStringUsingEncoding: nsstring #$NSASCIIStringEncoding)) |
---|
244 | (unless (%null-ptr-p cstring) |
---|
245 | (%get-cstring cstring)))) |
---|
246 | |
---|
247 | |
---|
248 | (objc:defmethod #/reason ((self ns-lisp-exception)) |
---|
249 | (with-slots (condition) self |
---|
250 | (if condition |
---|
251 | (#/autorelease (%make-nsstring (format nil "~A" condition))) |
---|
252 | (call-next-method)))) |
---|
253 | |
---|
254 | (objc:defmethod #/description ((self ns-lisp-exception)) |
---|
255 | (#/stringWithFormat: ns:ns-string #@"Lisp exception: %@" (#/reason self))) |
---|
256 | |
---|
257 | |
---|
258 | |
---|
259 | (defun ns-exception->lisp-condition (nsexception) |
---|
260 | (if (typep nsexception 'ns-lisp-exception) |
---|
261 | (ns-lisp-exception-condition nsexception) |
---|
262 | (make-condition 'ns-exception :ns-exception nsexception))) |
---|
263 | |
---|
264 | |
---|
265 | (defmethod ns-exception ((c condition)) |
---|
266 | "Map a lisp condition object to an NSException. Note that instances |
---|
267 | of the NS-EXCEPTION condition class implement this by accessing an |
---|
268 | instance variable." |
---|
269 | ;;; Create an NSLispException with a lispid that encapsulates |
---|
270 | ;;; this condition. |
---|
271 | |
---|
272 | ;; (dbg (format nil "~a" c)) |
---|
273 | ;;(#_NSLog #@"Lisp exception: %@" :id (%make-nsstring (format nil "~a" c))) |
---|
274 | (make-instance 'ns-lisp-exception :condition c)) |
---|
275 | |
---|
276 | |
---|
277 | |
---|
278 | #+(or apple-objc cocotron-objc) ; not really |
---|
279 | (progn |
---|
280 | |
---|
281 | |
---|
282 | #+ppc-target |
---|
283 | (defun objc-callback-error-return (condition return-value-pointer return-address-pointer) |
---|
284 | ;; On PPC, the "address" of an external entry point is always |
---|
285 | ;; aligned on a 32-bit word boundary. On PPC32, it can always |
---|
286 | ;; be represented as a fixnum; on PPC64, it might be a pointer |
---|
287 | ;; instead. |
---|
288 | ;; Note that this clobbers the actual (foreign) return address, |
---|
289 | ;; replacing it with the address of #__NSRaiseError. Note also |
---|
290 | ;; that storing the NSException object as the return value has |
---|
291 | ;; the desired effect of causing #__NSRaiseError to be called |
---|
292 | ;; with that NSException as its argument (because r3 is used both |
---|
293 | ;; as the canonical return value register and used to pass the |
---|
294 | ;; first argument on PPC.) |
---|
295 | (process-debug-condition *current-process* condition (%get-frame-ptr)) |
---|
296 | (let* ((addr (%reference-external-entry-point (load-time-value (external "__NSRaiseError"))))) |
---|
297 | (if (typep addr 'fixnum) |
---|
298 | (%set-object return-address-pointer 0 addr) |
---|
299 | (setf (%get-ptr return-address-pointer 0) addr))) |
---|
300 | (setf (%get-ptr return-value-pointer 0) (ns-exception condition)) |
---|
301 | nil) |
---|
302 | |
---|
303 | #+x8664-target |
---|
304 | (progn |
---|
305 | (defloadvar *x8664-objc-callback-error-return-trampoline* |
---|
306 | (let* ((code-bytes '(#x48 #x89 #xc7 ; movq %rax %rdi |
---|
307 | #x66 #x48 #x0f #x7e #xc0 ; movd %xmm0,%rax |
---|
308 | #x52 ; pushq %rdx |
---|
309 | #xff #xe0)) ; jmp *rax |
---|
310 | (nbytes (length code-bytes)) |
---|
311 | (ptr (%allocate-callback-pointer 16))) |
---|
312 | (dotimes (i nbytes ptr) |
---|
313 | (setf (%get-unsigned-byte ptr i) (pop code-bytes))))) |
---|
314 | |
---|
315 | (defun objc-callback-error-return (condition return-value-pointer return-address-pointer) |
---|
316 | ;; The callback glue reserves space for %rax at return-value-pointer-8, |
---|
317 | ;; for %rdx at -16, for %xmm0 at -24. Store NS-EXCEPTION in the |
---|
318 | ;; %rax slot, the address of #_objc_exception_throw in the %rdx slot, the |
---|
319 | ;; original return address in the %xmm0 slot, and force a return to |
---|
320 | ;; the trampoline code above. |
---|
321 | (process-debug-condition *current-process* condition (%get-frame-ptr)) |
---|
322 | (setf (%get-ptr return-value-pointer -8) (ns-exception condition) |
---|
323 | (%get-ptr return-value-pointer -16) (%get-ptr return-address-pointer 0) |
---|
324 | (%get-ptr return-address-pointer 0) *x8664-objc-callback-error-return-trampoline*) |
---|
325 | ;; A foreign entry point is always an integer on x8664. |
---|
326 | (let* ((addr (%reference-external-entry-point (load-time-value (external "_objc_exception_throw"))))) |
---|
327 | (if (< addr 0) ;unlikely |
---|
328 | (setf (%%get-signed-longlong return-value-pointer -24) addr) |
---|
329 | (setf (%%get-unsigned-longlong return-value-pointer -24) addr))) |
---|
330 | nil) |
---|
331 | |
---|
332 | |
---|
333 | ) |
---|
334 | |
---|
335 | #+x8632-target |
---|
336 | (progn |
---|
337 | |
---|
338 | (defloadvar *x8632-objc-callback-error-return-trampoline* |
---|
339 | (let* ((code-bytes '(#x83 #xec #x10 ; subl $16,%esp |
---|
340 | #x89 #x04 #x24 ; movl %eax,(%esp) |
---|
341 | #x52 ; pushl %edx |
---|
342 | #xff #xe1)) ; jmp *ecx |
---|
343 | (nbytes (length code-bytes)) |
---|
344 | (ptr (%allocate-callback-pointer 16))) |
---|
345 | (dotimes (i nbytes ptr) |
---|
346 | (setf (%get-unsigned-byte ptr i) (pop code-bytes))))) |
---|
347 | |
---|
348 | (defun objc-callback-error-return (condition return-value-pointer return-address-pointer) |
---|
349 | (process-debug-condition *current-process* condition (%get-frame-ptr)) |
---|
350 | (let* ((addr (%reference-external-entry-point (load-time-value (external #+cocotron-objc "_NSRaiseException" #-cocotron-objc "__NSRaiseError"))))) |
---|
351 | (setf (%get-unsigned-long return-value-pointer -12 ) addr)) |
---|
352 | (setf (%get-ptr return-value-pointer -8) (ns-exception condition) |
---|
353 | (%get-ptr return-value-pointer -4) (%get-ptr return-address-pointer) |
---|
354 | (%get-ptr return-address-pointer) *x8632-objc-callback-error-return-trampoline*) |
---|
355 | nil) |
---|
356 | ) |
---|
357 | |
---|
358 | ) |
---|
359 | |
---|
360 | |
---|
361 | |
---|
362 | (defun open-main-bundle () |
---|
363 | (#/mainBundle ns:ns-bundle)) |
---|
364 | |
---|
365 | ;;; Create a new immutable dictionary just like src, replacing the |
---|
366 | ;;; value of each key in key-value-pairs with the corresponding value. |
---|
367 | (defun copy-dictionary (src &rest key-value-pairs) |
---|
368 | (declare (dynamic-extent key-value-pairs)) |
---|
369 | ;(#_NSLog #@"src = %@" :id src) |
---|
370 | (let* ((count (#/count src)) |
---|
371 | (enum (#/keyEnumerator src)) |
---|
372 | (keys (#/arrayWithCapacity: ns:ns-mutable-array count)) |
---|
373 | (values (#/arrayWithCapacity: ns:ns-mutable-array count))) |
---|
374 | (loop |
---|
375 | (let* ((nextkey (#/nextObject enum))) |
---|
376 | (when (%null-ptr-p nextkey) |
---|
377 | (return)) |
---|
378 | (do* ((kvps key-value-pairs (cddr kvps)) |
---|
379 | (newkey (car kvps) (car kvps)) |
---|
380 | (newval (cadr kvps) (cadr kvps))) |
---|
381 | ((null kvps) |
---|
382 | ;; Copy the key, value pair from the src dict |
---|
383 | (#/addObject: keys nextkey) |
---|
384 | (#/addObject: values (#/objectForKey: src nextkey))) |
---|
385 | (when (#/isEqualToString: nextkey newkey) |
---|
386 | (#/addObject: keys nextkey) |
---|
387 | (#/addObject: values newval) |
---|
388 | (return))))) |
---|
389 | (make-instance 'ns:ns-dictionary |
---|
390 | :with-objects values |
---|
391 | :for-keys keys))) |
---|
392 | |
---|
393 | |
---|
394 | |
---|
395 | |
---|
396 | (defparameter *objc-description-max-length* 1024 "Limit on the length of NSObject description strings if non-NIL.") |
---|
397 | |
---|
398 | (defun %cf-instance-p (instance) |
---|
399 | #-apple-objc (declare (ignore instance)) |
---|
400 | #+apple-objc |
---|
401 | (> (objc-message-send instance "_cfTypeID" #>CFTypeID) 1)) |
---|
402 | |
---|
403 | |
---|
404 | (defun initialized-nsobject-p (nsobject) |
---|
405 | (or (objc-class-p nsobject) |
---|
406 | (objc-metaclass-p nsobject) |
---|
407 | (has-lisp-slot-vector nsobject) |
---|
408 | (let* ((cf-p (%cf-instance-p nsobject)) |
---|
409 | (isize (if cf-p (external-call "malloc_size" :address nsobject :size_t) (%objc-class-instance-size (#/class nsobject)))) |
---|
410 | (skip (if cf-p (+ (record-length :id) 4 #+64-bit-target 4) (record-length :id)))) |
---|
411 | (declare (fixnum isize skip)) |
---|
412 | (or (> skip isize) |
---|
413 | (do* ((i skip (1+ i))) |
---|
414 | ((>= i isize)) |
---|
415 | (declare (fixnum i)) |
---|
416 | (unless (zerop (the (unsigned-byte 8) (%get-unsigned-byte nsobject i))) |
---|
417 | (return t))))))) |
---|
418 | |
---|
419 | (defun nsobject-description (nsobject) |
---|
420 | "Returns a lisp string that describes nsobject. Note that some |
---|
421 | NSObjects describe themselves in more detail than others." |
---|
422 | (if (initialized-nsobject-p nsobject) |
---|
423 | (with-autorelease-pool |
---|
424 | (let* ((desc (#/description nsobject))) |
---|
425 | (if (or (null *objc-description-max-length*) |
---|
426 | (< (#/length desc) *objc-description-max-length*)) |
---|
427 | (lisp-string-from-nsstring desc) |
---|
428 | (ns:with-ns-range (r 0 *objc-description-max-length*) |
---|
429 | (format nil "~a[...]"(lisp-string-from-nsstring (#/substringWithRange: desc r))))))) |
---|
430 | "[uninitialized]")) |
---|
431 | |
---|
432 | |
---|
433 | |
---|
434 | |
---|
435 | |
---|
436 | ;;; This can fail if the nsstring contains non-8-bit characters. |
---|
437 | (defun lisp-string-from-nsstring-substring (nsstring start length) |
---|
438 | (%stack-block ((cstring (1+ length))) |
---|
439 | (#/getCString:maxLength:range:remainingRange: |
---|
440 | nsstring cstring length (ns:make-ns-range start length) +null-ptr+) |
---|
441 | (%get-cstring cstring))) |
---|
442 | |
---|
443 | (def-standard-initial-binding *listener-autorelease-pool* nil) |
---|
444 | |
---|
445 | (setq *listener-autorelease-pool* (create-autorelease-pool)) |
---|
446 | |
---|
447 | (define-toplevel-command :global rap () "Release and reestablish *LISTENER-AUTORELEASE-POOL*" |
---|
448 | (when (eql *break-level* 0) |
---|
449 | (without-interrupts |
---|
450 | (when (boundp '*listener-autorelease-pool*) |
---|
451 | (let* ((old *listener-autorelease-pool*)) |
---|
452 | (if old (release-autorelease-pool old)) |
---|
453 | (setq *listener-autorelease-pool* (create-autorelease-pool))))))) |
---|
454 | |
---|
455 | #+apple-objc |
---|
456 | (defun show-autorelease-pools () |
---|
457 | (objc-message-send (@class ns-autorelease-pool) "showPools" :void)) |
---|
458 | |
---|
459 | #+gnu-objc |
---|
460 | (defun show-autorelease-pools () |
---|
461 | (do* ((current (objc-message-send (@class ns-autorelease-pool) "currentPool") |
---|
462 | (objc-message-send current "_parentAutoreleasePool")) |
---|
463 | (i 0 (1+ i))) |
---|
464 | ((%null-ptr-p current) (values)) |
---|
465 | (format t "~& ~d : ~a [~d]" |
---|
466 | i |
---|
467 | (nsobject-description current) |
---|
468 | (pref current :<NSA>utorelease<P>ool._released_count)))) |
---|
469 | |
---|
470 | #+cocotron-objc |
---|
471 | (defun show-autorelease-pools () |
---|
472 | (%string-to-stderr "No info about current thread's autorelease pools is available")) |
---|
473 | |
---|
474 | (define-toplevel-command :global sap () "Log information about current thread's autorelease-pool(s) to C's standard error stream" |
---|
475 | (show-autorelease-pools)) |
---|
476 | |
---|
477 | (define-toplevel-command :global kap () "Release (but don't reestablish) *LISTENER-AUTORELEASE-POOL*" |
---|
478 | (when (eql *break-level* 0) |
---|
479 | (without-interrupts |
---|
480 | (when (boundp '*listener-autorelease-pool*) |
---|
481 | (let* ((p *listener-autorelease-pool*)) |
---|
482 | (setq *listener-autorelease-pool* nil) |
---|
483 | (release-autorelease-pool p)))))) |
---|
484 | |
---|
485 | ;;; Use the interfaces for an add-on ObjC framework. We need to |
---|
486 | ;;; tell the bridge to reconsider what it knows about the type |
---|
487 | ;;; signatures of ObjC messages, since the new headers may define |
---|
488 | ;;; a method whose type signature differs from the message's existing |
---|
489 | ;;; methods. (This probably doesn't happen too often, but it's |
---|
490 | ;;; possible that some SENDs that have already been compiled would |
---|
491 | ;;; need to be recompiled with that augmented method type info, e.g., |
---|
492 | ;;; because ambiguity was introduced.) |
---|
493 | |
---|
494 | (defun augment-objc-interfaces (dirname) |
---|
495 | (use-interface-dir dirname) |
---|
496 | (register-objc-class-decls) |
---|
497 | (update-objc-method-info)) |
---|
498 | |
---|
499 | ;;; A list of "standard" locations which are known to contain |
---|
500 | ;;; framework bundles. We should look in ~/Library/Frameworks/" first, |
---|
501 | ;;; if it exists. |
---|
502 | (defparameter *standard-framework-directories* |
---|
503 | (list #p"/Library/Frameworks/" |
---|
504 | #p"/System/Library/Frameworks/")) |
---|
505 | |
---|
506 | |
---|
507 | |
---|
508 | ;;; This has to run during application (re-)initializtion, so it |
---|
509 | ;;; uses lower-level bridge features. |
---|
510 | (defun %reload-objc-framework (path) |
---|
511 | (when (probe-file path) |
---|
512 | (let* ((namestring (native-translated-namestring path))) |
---|
513 | (with-cstrs ((cnamestring namestring)) |
---|
514 | (with-nsstr (nsnamestring cnamestring (length namestring)) |
---|
515 | (with-autorelease-pool |
---|
516 | (let* ((bundle (objc-message-send (@class "NSBundle") |
---|
517 | "bundleWithPath:" |
---|
518 | :id nsnamestring :id))) |
---|
519 | (unless (%null-ptr-p bundle) |
---|
520 | (objc-message-send bundle "load" :<BOOL>))))))))) |
---|
521 | |
---|
522 | |
---|
523 | (defun load-objc-extension-framework (name) |
---|
524 | (let* ((dirs *standard-framework-directories*) |
---|
525 | (home-frameworks (make-pathname :defaults nil |
---|
526 | :directory |
---|
527 | (append (pathname-directory |
---|
528 | (user-homedir-pathname)) |
---|
529 | '("Library" "Frameworks")))) |
---|
530 | (fname (list (format nil "~a.framework" name)))) |
---|
531 | (when (probe-file home-frameworks) |
---|
532 | (pushnew home-frameworks dirs :test #'equalp)) |
---|
533 | (dolist (d dirs) |
---|
534 | (let* ((path (probe-file (make-pathname :defaults nil |
---|
535 | :directory (append (pathname-directory d) |
---|
536 | fname))))) |
---|
537 | (when path |
---|
538 | (let* ((namestring (native-translated-namestring path))) |
---|
539 | (with-cstrs ((cnamestring namestring)) |
---|
540 | (with-nsstr (nsnamestring cnamestring (length namestring)) |
---|
541 | (with-autorelease-pool |
---|
542 | (let* ((bundle (#/bundleWithPath: ns:ns-bundle nsnamestring)) |
---|
543 | (winning (unless (%null-ptr-p bundle) |
---|
544 | t))) |
---|
545 | (when winning |
---|
546 | (let* ((libpath (#/executablePath bundle))) |
---|
547 | (unless (%null-ptr-p libpath) |
---|
548 | (open-shared-library (lisp-string-from-nsstring |
---|
549 | libpath)))) |
---|
550 | (#/load bundle) |
---|
551 | (pushnew path *extension-framework-paths* |
---|
552 | :test #'equalp) |
---|
553 | (map-objc-classes) |
---|
554 | ;; Update info about init messages. |
---|
555 | (register-objc-init-messages) |
---|
556 | (register-objc-set-messages)) |
---|
557 | (return winning))))))))))) |
---|
558 | |
---|
559 | (defun objc:load-framework (framework-name interfaces-name) |
---|
560 | (use-interface-dir interfaces-name) |
---|
561 | (or (load-objc-extension-framework framework-name) |
---|
562 | (error "Can't load ObjC framework ~s" framework-name)) |
---|
563 | (augment-objc-interfaces interfaces-name)) |
---|
564 | |
---|
565 | |
---|
566 | (defmethod print-object ((p ns:protocol) stream) |
---|
567 | (print-unreadable-object (p stream :type t) |
---|
568 | (format stream "~a (#x~x)" |
---|
569 | (%get-cstring (#/name p)) |
---|
570 | (%ptr-to-int p)))) |
---|
571 | |
---|
572 | |
---|
573 | (defmethod terminate ((instance objc:objc-object)) |
---|
574 | (objc-message-send instance "release")) |
---|
575 | |
---|
576 | |
---|
577 | (provide "OBJC-SUPPORT") |
---|