Changeset 575


Ignore:
Timestamp:
Feb 27, 2004, 8:32:28 AM (21 years ago)
Author:
beer
Message:

ObjC generic functions and methods for predefined ObjC messages mostly seem to work; support for conversion between ObjC records and Lisp structs (mostly from Gary)

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/ccl/examples/objc-clos.lisp

    r549 r575  
    1616;;;
    1717;;; 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)
    1827;;;  - Need to fully handle init keywords and ObjC init messages
    19 ;;;  - Canonicalization and retention for ObjC objects
    20 ;;;  - Support redef of CLOS parts, but not changes in ObjC parts
    21 ;;;  - Provide Lisp structs for NS-POINT, NS-RECT, etc.?
    2228
    2329;;; Package and module stuff
     
    4450
    4551(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
    4685
    4786;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     
    897936               (first mcomps))
    898937             (find-package "NS"))
    899             (if (zerop ncolons) '(self) '(self arg &key))
     938            (if (zerop ncolons) '(%self) '(%self %arg &key))
    900939            (mapcar #'compute-lisp-name (rest mcomps)))))
    901940
     
    914953                 (return-from %%objc-dcode (apply (method-function m) largs))))
    915954             (apply #'no-applicable-method (%gf-dispatch-table-gf dt) largs))))
    916     ;; If only on arg is present, args is apparently not encoded
     955    ;; If only one arg is present, ARGS is apparently not encoded
    917956    (if (numberp args)
    918957        (with-list-from-lexpr (l args) (invoke-method l))
     
    923962
    924963(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)))))
    9331035
    9341036
    9351037;;; Ensure that the method corresponding to CLASS's method for MSG exists
    9361038
    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*
    9641062
    9651063(defun define-all-objc-methods ()
     
    9701068           *type-signature-table*))
    9711069
     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.