Browse code
feature: add safe objc method calling for repl use
Ed Langley authored on 21/10/2019 04:06:19
Showing 2 changed files
Showing 2 changed files
... | ... |
@@ -117,11 +117,25 @@ |
117 | 117 |
(sel o-selector) |
118 | 118 |
&rest) |
119 | 119 |
|
120 |
+(defmacro safe-objc-msg-send (result-type thing selector &rest args) |
|
121 |
+ (alexandria:once-only (thing selector) |
|
122 |
+ `(if [,thing @(respondsToSelector:) :pointer ,selector]b |
|
123 |
+ ,(ccase result-type |
|
124 |
+ (string `[,thing ,selector ,@args]s) |
|
125 |
+ (nsstring `[,thing ,selector ,@args]@) |
|
126 |
+ (num `[,thing ,selector ,@args]#) |
|
127 |
+ (bool `[,thing ,selector ,@args]b) |
|
128 |
+ (id `[,thing ,selector ,@args])) |
|
129 |
+ (error "invalid selector")))) |
|
130 |
+ |
|
120 | 131 |
;;; This is a macro, because objc-msg-send is a macro.... which makes "apply" impossible |
121 | 132 |
;;; \o/ |
122 | 133 |
(defmacro objc-msg-send-nsstring (thing selector &rest args) |
123 | 134 |
`[[,thing ,selector ,@args] @(UTF8String)]s) |
124 | 135 |
|
136 |
+(defmacro objc-msg-send-bool (thing selector &rest args) |
|
137 |
+ `(= 1 [,thing ,selector ,@args]#)) |
|
138 |
+ |
|
125 | 139 |
(defcfun (class-copy-method-list "class_copyMethodList" :library foundation) |
126 | 140 |
:pointer |
127 | 141 |
(cls o-class) |
... | ... |
@@ -10,42 +10,50 @@ |
10 | 10 |
(lambda (s c b) |
11 | 11 |
(declare (ignore c b)) |
12 | 12 |
(let ((class-name (coerce (loop for next-char = (peek-char nil s nil nil t) |
13 |
- while next-char |
|
14 |
- until (funcall test next-char) |
|
15 |
- collect (read-char s t nil t) |
|
16 |
- finally (when (and (not (member next-char |
|
17 |
- stop-before-chars)) |
|
18 |
- (funcall test next-char)) |
|
19 |
- (read-char s t nil t))) |
|
13 |
+ while next-char |
|
14 |
+ until (funcall test next-char) |
|
15 |
+ collect (read-char s t nil t) |
|
16 |
+ finally (when (and (not (member next-char |
|
17 |
+ stop-before-chars)) |
|
18 |
+ (funcall test next-char)) |
|
19 |
+ (read-char s t nil t))) |
|
20 | 20 |
|
21 | 21 |
'string))) |
22 | 22 |
`(,symbol-prefix ,class-name)))) |
23 | 23 |
|
24 |
+(defun read-objc-form (s char) |
|
25 |
+ (declare (ignore char)) |
|
26 |
+ (flet ((get-call-form (safe-p result-type base-form) |
|
27 |
+ (if safe-p |
|
28 |
+ `(safe-objc-msg-send ,result-type) |
|
29 |
+ `(,base-form)))) |
|
30 |
+ (let* ((info (read-delimited-list #\] s t)) |
|
31 |
+ (safe-p (when (eql #\? (peek-char nil s nil #\p t)) |
|
32 |
+ (read-char s t nil t))) |
|
33 |
+ (msg-send (case (peek-char nil s nil #\p t) |
|
34 |
+ (#\# (read-char s t nil t) (get-call-form safe-p 'num 'objc-msg-send-int)) |
|
35 |
+ (#\& (read-char s t nil t) (get-call-form safe-p 'id 'objc-msg-send)) |
|
36 |
+ (#\@ (read-char s t nil t) (get-call-form safe-p 'nsstring 'objc-msg-send-nsstring)) |
|
37 |
+ (#\b (read-char s t nil t) (get-call-form safe-p 'bool 'objc-msg-send-bool)) |
|
38 |
+ (#\s (read-char s t nil t) (get-call-form safe-p 'string 'objc-msg-send-string)) |
|
39 |
+ (t (get-call-form safe-p 'id 'objc-msg-send))))) |
|
40 |
+ (when info |
|
41 |
+ (destructuring-bind (obj message . args) info |
|
42 |
+ `(,@msg-send ,obj ,message ,@args)))))) |
|
43 |
+ |
|
24 | 44 |
(named-readtables:defreadtable :objc-readtable |
25 | 45 |
(:merge :standard) |
26 | 46 |
(:syntax-from :standard #\) #\]) |
27 |
- (:macro-char #\[ (lambda (s char) |
|
28 |
- char |
|
29 |
- (let ((info (read-delimited-list #\] s t)) |
|
30 |
- (msg-send (case (peek-char nil s nil #\p t) |
|
31 |
- (#\# (read-char s t nil t) 'objc-msg-send-int) |
|
32 |
- (#\s (read-char s t nil t) 'objc-msg-send-string) |
|
33 |
- (#\@ (read-char s t nil t) 'objc-msg-send-nsstring) |
|
34 |
- (#\& (read-char s t nil t) 'objc-msg-send) |
|
35 |
- (t 'objc-msg-send)))) |
|
36 |
- (when info |
|
37 |
- (destructuring-bind (obj message . args) info |
|
38 |
- `(,msg-send ,obj ,message ,@args))))) |
|
39 |
- nil) |
|
47 |
+ (:macro-char #\[ 'read-objc-form nil) |
|
40 | 48 |
(:dispatch-macro-char #\# #\@ |
41 | 49 |
(lambda (s c b) |
42 | 50 |
c b |
43 | 51 |
(let ((class-name (coerce (loop for c = (peek-char nil s nil nil t) |
44 |
- until (or (null c) |
|
45 |
- (serapeum:whitespacep c) |
|
46 |
- (member c |
|
47 |
- '(#\) #\( #\[ #\]))) |
|
48 |
- collect (read-char s t nil t)) |
|
52 |
+ until (or (null c) |
|
53 |
+ (serapeum:whitespacep c) |
|
54 |
+ (member c |
|
55 |
+ '(#\) #\( #\[ #\]))) |
|
56 |
+ collect (read-char s t nil t)) |
|
49 | 57 |
'string))) |
50 | 58 |
`(ensure-class ,class-name)))) |
51 | 59 |
(:macro-char #\@ :dispatch t) |