Changeset 575
- Timestamp:
- Feb 27, 2004, 8:32:28 AM (21 years ago)
- File:
-
- 1 edited
-
trunk/ccl/examples/objc-clos.lisp (modified) (6 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/ccl/examples/objc-clos.lisp
r549 r575 16 16 ;;; 17 17 ;;; TO DO 18 ;;; - Both method creation and invocation should be faster and cons less 19 ;;; - Resolve messages with repeated keywords 20 ;;; (rename them to :range1:range2 or don't use &key in GFs and methods) 21 ;;; - How to integrate SEND-SUPER with CALL-NEXT-METHOD? 22 ;;; - Variable arity ObjC methods 23 ;;; - Pass-by-ref structures need to keep track of IN, OUT, IN/OUT info 24 ;;; - Need to canonicalize and retain every returned :ID 25 ;;; - Support :BEFORE, :AFTER and :AROUND for ObjC methods 26 ;;; - User-defined ObjC methods via DEFMETHOD (or DEFINE-OBJ-METHOD) 18 27 ;;; - Need to fully handle init keywords and ObjC init messages 19 ;;; - Canonicalization and retention for ObjC objects20 ;;; - Support redef of CLOS parts, but not changes in ObjC parts21 ;;; - Provide Lisp structs for NS-POINT, NS-RECT, etc.?22 28 23 29 ;;; Package and module stuff … … 44 50 45 51 (defparameter *objc-import-private-ivars* t "When true, the CLASS-DIRECT-SLOTS of imported ObjC classes will contain slot definitions for instance variables whose name starts with an underscore. Note that this may exacerbate compatibility problems.") 52 53 54 ;;; ObjC messages that cannot currently be translated into CLOS methods 55 56 (defparameter *troublesome-messages* 57 '( 58 ;; Multicolon messages that don't respect the name translation rules 59 "performv::" "translateTo::" "indexOf:::" "scaleTo::" "forward::" 60 "exchange::" 61 ;; Messages involving the nonexistent NSButtonState 62 "focusRingImageForState:" "useDisabledEffectForState:" 63 "isBorderedForState:" "imageForState:" "useHighlightEffectForState:" 64 "isOpaqueForState:" "bezelStyleForState:" 65 ;; Messages containing repeated keywords 66 "orderString:range:string:range:flags:" 67 "parseSuiteOfPairsKey:separator:value:separator:allowOmitLastSeparator:" 68 "perform:with:with:" 69 "perform:withObject:withObject:" 70 "performSelector:withObject:withObject:" 71 ;; Variable arity messages 72 "appendFormat:" "arrayWithObjects:" "encodeValuesOfObjCTypes:" 73 "decodeValuesOfObjCTypes:" "dictinaryWithObjectsAndKeys:" 74 "handleFailureInFunction:object:file:lineNumber:description:" 75 "handleFailureInMethod:object:file:lineNumber:description:" 76 "initWithFormat:" "initWithObjects:" "initWithObjectsAndKeys:" 77 "initWithFormat:locale:" "localizedStringWithFormat:" "raise:format:" 78 "setWithObjects:" "stringByAppendingFormat:" "stringWithFormat:" 79 ;; Seems to involve a (:STRUCT :?) argument 80 "percentEscapeDecodeBuffer:range:stripWhitespace:")) 81 82 (defun troublesome-message-p (msg) 83 (if (member msg *troublesome-messages* :test #'string=) t nil)) 84 46 85 47 86 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; … … 897 936 (first mcomps)) 898 937 (find-package "NS")) 899 (if (zerop ncolons) '( self) '(selfarg &key))938 (if (zerop ncolons) '(%self) '(%self %arg &key)) 900 939 (mapcar #'compute-lisp-name (rest mcomps))))) 901 940 … … 914 953 (return-from %%objc-dcode (apply (method-function m) largs)))) 915 954 (apply #'no-applicable-method (%gf-dispatch-table-gf dt) largs)))) 916 ;; If only on arg is present, argsis apparently not encoded955 ;; If only one arg is present, ARGS is apparently not encoded 917 956 (if (numberp args) 918 957 (with-list-from-lexpr (l args) (invoke-method l)) … … 923 962 924 963 (defun ensure-objc-generic-function (msg) 925 (multiple-value-bind (gf-name lambda-list) (gfify msg) 926 (let ((gf (ensure-generic-function 927 gf-name 928 :lambda-list lambda-list 929 :generic-function-class (find-class 'objc-generic-function) 930 :method-class (find-class 'objc-method)))) 931 (setf (%gf-dcode gf) #'%%objc-dcode) 932 gf))) 964 (cond 965 ((null (message-descriptors msg)) 966 (error "Unknown ObjC message: ~S" msg)) 967 ((troublesome-message-p msg) nil) 968 (t 969 (multiple-value-bind (gf-name lambda-list) (gfify msg) 970 (let ((gf (ensure-generic-function 971 gf-name 972 :lambda-list lambda-list 973 :generic-function-class (find-class 'objc-generic-function) 974 :method-class (find-class 'objc-method)))) 975 (setf (%gf-dcode gf) #'%%objc-dcode) 976 gf))))) 977 978 979 ;;; Create the method function corresponding to the given ObjC MSG 980 981 (defun make-objc-method-function (msg lambda-list keys) 982 (let ((msgdescs (message-descriptors msg))) 983 (compile 984 nil 985 (if (= (length msgdescs) 1) 986 ;; The type signature is unique 987 `(lambda ,lambda-list 988 ,(build-message-send 989 msg (msg-desc-type-signature (first msgdescs)) keys)) 990 ;; The type signature is ambiguous 991 `(lambda ,lambda-list 992 (cond 993 ,@(loop for md in msgdescs 994 collect 995 `((or 996 ,@(loop for c in (msg-desc-classes md) 997 collect 998 `(typep %self ',(class-name c)))) 999 (locally 1000 (declare (,(class-name (first (msg-desc-classes md))) 1001 %self)) 1002 ,(build-message-send 1003 msg (msg-desc-type-signature md) keys)))))))))) 1004 1005 1006 ;;; Build the message-sending code for the given message with the given 1007 ;;; type signature and keys 1008 1009 (defun build-message-send (msg tsig keys) 1010 (let* ((rvars nil) 1011 (args (if (zerop (count #\: msg)) 1012 nil 1013 (loop 1014 for a in (cons '%arg keys) 1015 for ftype in (rest tsig) 1016 for r/s-assoc = (coerceable-foreign-record-p ftype) 1017 for sname = (gensym) 1018 if r/s-assoc 1019 do (push (list sname (fudge-objc-type ftype)) rvars) 1020 and collect 1021 (generate-structure-to-foreign-record-copier-form 1022 (record-structure-association-structure-name r/s-assoc) 1023 (record-structure-association-record-name r/s-assoc) 1024 :struct-name a :record-name sname) 1025 else collect a)))) 1026 (if (requires-stret-p (first tsig)) 1027 ;; STRET message send 1028 (let ((r (gensym))) 1029 `(rlet ((,r ,(fudge-objc-type (first tsig))) ,@rvars) 1030 (send/stret ,r %self ,msg ,@args) 1031 ,(create-structure-from-record-form r (cadar tsig)))) 1032 ;; Normal message send 1033 `(rlet ,rvars 1034 (send %self ,msg ,@args))))) 933 1035 934 1036 935 1037 ;;; Ensure that the method corresponding to CLASS's method for MSG exists 936 1038 937 (defun ensure-objc-method (msg &optional 938 (class (find-class 'objc:objc-object))) 939 (flet ((keywordify (sym) 940 (intern (string sym) (find-package 'keyword)))) 941 (multiple-value-bind (gf-name lambda-list keys) (gfify msg) 942 (let* ((ncolons (count #\: msg)) 943 (class-name (class-name class)) 944 (gf (ensure-objc-generic-function msg)) 945 (lambda-list (append lambda-list keys)) 946 (m 947 (ensure-method 948 gf-name 949 (if (zerop ncolons) (list class-name) (list class-name t)) 950 :function 951 (compile nil 952 `(lambda ,lambda-list 953 ,(case ncolons 954 (0 `(send self ,msg)) 955 (1 `(send self ,msg arg)) 956 (t `(send self ,msg arg ,@keys))))) 957 :qualifiers (mapcar #'keywordify keys) 958 :lambda-list lambda-list))) 959 (setf (%gf-dcode gf) #'%%objc-dcode) 960 m)))) 961 962 963 ;;; Someday, this might even work... 1039 (defun ensure-objc-method (msg) 1040 (cond 1041 ((null (message-descriptors msg)) 1042 (error "Unknown ObjC message: ~S" msg)) 1043 ((troublesome-message-p msg) nil) 1044 (t 1045 (flet ((keywordify (sym) 1046 (intern (string sym) (find-package 'keyword)))) 1047 (multiple-value-bind (gf-name lambda-list keys) (gfify msg) 1048 (let* ((gf (ensure-objc-generic-function msg)) 1049 (lambda-list (append lambda-list keys)) 1050 (m 1051 (ensure-method 1052 gf-name 1053 nil 1054 :function (make-objc-method-function msg lambda-list keys) 1055 :qualifiers (mapcar #'keywordify keys) 1056 :lambda-list lambda-list))) 1057 (setf (%gf-dcode gf) #'%%objc-dcode) 1058 m)))))) 1059 1060 1061 ;;; Generate ObjC methods for all messages in *TYPE-SIGNATURE-TABLE* 964 1062 965 1063 (defun define-all-objc-methods () … … 970 1068 *type-signature-table*)) 971 1069 1070 1071 ;;; Lisp structures analogous to common Cocoa records 1072 1073 (defstruct (ns-range (:constructor make-ns-range (location length))) 1074 location 1075 length) 1076 1077 (defun ns-make-range (loc len) 1078 (make-ns-range loc len)) 1079 1080 (defstruct (ns-point (:constructor make-ns-point (x y))) 1081 x 1082 y) 1083 1084 (defun ns-make-point (x y) 1085 (make-ns-point (coerce x 'single-float) (coerce y 'single-float))) 1086 1087 (defstruct (ns-size (:constructor make-ns-size (width height))) 1088 width 1089 height) 1090 1091 (defun ns-make-size (w h) 1092 (make-ns-size 1093 (coerce w 'single-float) 1094 (coerce h 'single-float))) 1095 1096 ;;; Note that this is linear: four fields, rather than an ns-point 1097 ;;; and an ns-size. 1098 (defstruct (ns-rect 1099 (:constructor make-ns-rect 1100 (origin.x origin.y size.width size.height))) 1101 origin.x 1102 origin.y 1103 size.width 1104 size.height) 1105 1106 (defun ns-make-rect (ox oy sw sh) 1107 (make-ns-rect 1108 (coerce ox 'single-float) 1109 (coerce oy 'single-float) 1110 (coerce sw 'single-float) 1111 (coerce sh 'single-float))) 1112 1113 (defstruct (ns-decimal 1114 (:constructor make-ns-decimal 1115 (_exponent _length _is-negative _is-compact _reserved _mantissa))) 1116 _exponent 1117 _length 1118 _is-negative 1119 _is-compact 1120 _reserved 1121 _mantissa) 1122 1123 ;;; Also linear 1124 (defstruct (cg-rect 1125 (:constructor make-cg-rect 1126 (origin.x origin.y size.width size.height))) 1127 origin.x 1128 origin.y 1129 size.width 1130 size.height) 1131 1132 (defstruct (ns-affine-transform-struct 1133 (:constructor make-ns-affine-transform-struct 1134 (m11 m12 m21 m22 tx ty))) 1135 m11 m12 m21 m22 tx ty) 1136 1137 1138 (defun generate-foreign-record-to-structure-copier-form (record-type-name structure-class-name &key (struct-name (gensym)) (record-name (gensym))) 1139 (let* ((slot-names (mapcar #'slot-definition-name (class-slots (find-class structure-class-name)))) 1140 (record-type (%foreign-type-or-record record-type-name)) 1141 (accessor-names (foreign-record-accessor-names record-type))) 1142 (unless (eq (length slot-names) (length accessor-names)) 1143 (error "Slot names ~s don't match record accessors ~s" 1144 slot-names accessor-names)) 1145 (let* ((body (mapcar #'(lambda (slot-name accessor) 1146 `(setf (slot-value ,struct-name ',slot-name) 1147 ,(%foreign-access-form record-name 1148 record-type 1149 0 1150 accessor))) 1151 slot-names accessor-names))) 1152 `(progn ,@body ,struct-name)))) 1153 1154 (defun generate-structure-to-foreign-record-copier-form 1155 (structure-class-name record-type-name 1156 &key 1157 (struct-name (gensym)) 1158 (record-name (gensym))) 1159 (let* ((slot-names (mapcar #'slot-definition-name (class-slots (find-class structure-class-name)))) 1160 (record-type (%foreign-type-or-record record-type-name)) 1161 (accessor-names (foreign-record-accessor-names record-type))) 1162 (unless (eq (length slot-names) (length accessor-names)) 1163 (error "Slot names ~s don't match record accessors ~s" 1164 slot-names accessor-names)) 1165 (let* ((body (mapcar #'(lambda (slot-name accessor) 1166 `(setf ,(%foreign-access-form record-name 1167 record-type 1168 0 1169 accessor) 1170 (slot-value ,struct-name ',slot-name))) 1171 slot-names accessor-names))) 1172 `(progn ,@body ,record-name)))) 1173 1174 (defun generate-foreign-record-to-structure-creator-form 1175 (record-type-name constructor-name &key (record-name (gensym))) 1176 (let* ((record-type (%foreign-type-or-record record-type-name)) 1177 (accessor-names (foreign-record-accessor-names record-type)) 1178 (args (mapcar #'(lambda (accessor) 1179 (%foreign-access-form record-name 1180 record-type 1181 0 1182 accessor)) 1183 accessor-names))) 1184 `(,constructor-name ,@args))) 1185 1186 1187 (defstruct record-structure-association 1188 record-name 1189 structure-name 1190 structure-constructor-name) 1191 1192 (defparameter *record-structure-associations* ()) 1193 1194 (defun record-structure-association-from-record-name (r) 1195 (find r *record-structure-associations* :key #'record-structure-association-record-name)) 1196 1197 (defun need-record-structure-association-from-record-name (r) 1198 (or (record-structure-association-from-record-name r) 1199 (error "No lisp structure associated with foreign record named ~s" r))) 1200 1201 (defun record-structure-association-from-structure-name (r) 1202 (find r *record-structure-associations* :key #'record-structure-association-structure-name)) 1203 1204 (defun associate-record-with-structure (record-name structure-name constructor-name) 1205 (let* ((already-r (record-structure-association-from-record-name record-name)) 1206 (already-s (record-structure-association-from-structure-name structure-name)) 1207 (already (or already-r already-s)) 1208 (different (not (eq already-r already-s)))) 1209 (if already 1210 (if different 1211 (if already-r 1212 (error "~&Record named ~s is already associated with structure named ~s" 1213 (record-structure-association-record-name already-r) 1214 (record-structure-association-structure-name already-r)) 1215 (if already-s 1216 (error "~&Structure named ~s is already associated with record named ~s" 1217 (record-structure-association-structure-name already-s) 1218 (record-structure-association-record-name already-s)))) 1219 (setf (record-structure-association-structure-constructor-name already) 1220 constructor-name)) 1221 (push (make-record-structure-association 1222 :record-name record-name 1223 :structure-name structure-name 1224 :structure-constructor-name constructor-name) 1225 *record-structure-associations*)) 1226 t)) 1227 1228 (defun create-structure-from-record-form (var record-type) 1229 (let* ((a (need-record-structure-association-from-record-name 1230 record-type)) 1231 (constructor 1232 (record-structure-association-structure-constructor-name a))) 1233 (generate-foreign-record-to-structure-creator-form 1234 record-type constructor :record-name var))) 1235 1236 (defun coerceable-foreign-record-p (ftype) 1237 (and (consp ftype) 1238 (eq (first ftype) :struct) 1239 (find (second ftype) *record-structure-associations* 1240 :key #'record-structure-association-record-name))) 1241 1242 (associate-record-with-structure :_<NSR>ect 'ns-rect 'make-ns-rect) 1243 (associate-record-with-structure :_<NSP>oint 'ns-point 'make-ns-point) 1244 (associate-record-with-structure :_<NSS>ize 'ns-size 'make-ns-size) 1245 (associate-record-with-structure :_<NSR>ange 'ns-range 'make-ns-range) 1246 (associate-record-with-structure :<NSD>ecimal 'ns-decimal 'make-ns-decimal) 1247 (associate-record-with-structure :<CGR>ect 'cg-rect 'make-cg-rect) 1248 (associate-record-with-structure :_<NSA>ffine<T>ransform<S>truct 1249 'ns-affine-transform-struct 1250 'make-ns-affine-transform-struct)
Note:
See TracChangeset
for help on using the changeset viewer.
