early-types.lisp - clic - Clic is an command line interactive client for gopher written in Common LISP (HTM) git clone git://bitreich.org/clic/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/clic/ (DIR) Log (DIR) Files (DIR) Refs (DIR) Tags (DIR) README (DIR) LICENSE --- early-types.lisp (26726B) --- 1 ;;;; -*- Mode: lisp; indent-tabs-mode: nil -*- 2 ;;; 3 ;;; early-types.lisp --- Low-level foreign type operations. 4 ;;; 5 ;;; Copyright (C) 2005-2006, James Bielman <jamesjb@jamesjb.com> 6 ;;; Copyright (C) 2005-2007, Luis Oliveira <loliveira@common-lisp.net> 7 ;;; 8 ;;; Permission is hereby granted, free of charge, to any person 9 ;;; obtaining a copy of this software and associated documentation 10 ;;; files (the "Software"), to deal in the Software without 11 ;;; restriction, including without limitation the rights to use, copy, 12 ;;; modify, merge, publish, distribute, sublicense, and/or sell copies 13 ;;; of the Software, and to permit persons to whom the Software is 14 ;;; furnished to do so, subject to the following conditions: 15 ;;; 16 ;;; The above copyright notice and this permission notice shall be 17 ;;; included in all copies or substantial portions of the Software. 18 ;;; 19 ;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 ;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 ;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 ;;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 ;;; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 ;;; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 ;;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 ;;; DEALINGS IN THE SOFTWARE. 27 ;;; 28 29 ;;;# Early Type Definitions 30 ;;; 31 ;;; This module contains basic operations on foreign types. These 32 ;;; definitions are in a separate file because they may be used in 33 ;;; compiler macros defined later on. 34 35 (in-package #:cffi) 36 37 ;;;# Foreign Types 38 ;;; 39 ;;; Type specifications are of the form (type {args}*). The type 40 ;;; parser can specify how its arguments should look like through a 41 ;;; lambda list. 42 ;;; 43 ;;; "type" is a shortcut for "(type)", ie, no args were specified. 44 ;;; 45 ;;; Examples of such types: boolean, (boolean), (boolean :int) If the 46 ;;; boolean type parser specifies the lambda list: &optional 47 ;;; (base-type :int), then all of the above three type specs would be 48 ;;; parsed to an identical type. 49 ;;; 50 ;;; Type parsers, defined with DEFINE-PARSE-METHOD should return a 51 ;;; subtype of the foreign-type class. 52 53 (defvar *type-parsers* (make-hash-table :test 'equal) 54 "Hash table of defined type parsers.") 55 56 (define-condition cffi-error (error) 57 ()) 58 59 (define-condition foreign-type-error (cffi-error) 60 ((type-name :initarg :type-name 61 :initform (error "Must specify TYPE-NAME.") 62 :accessor foreign-type-error/type-name) 63 (namespace :initarg :namespace 64 :initform :default 65 :accessor foreign-type-error/namespace))) 66 67 (defun foreign-type-error/compound-name (e) 68 (let ((name (foreign-type-error/type-name e)) 69 (namespace (foreign-type-error/namespace e))) 70 (if (eq namespace :default) 71 name 72 `(,namespace ,name)))) 73 74 (define-condition simple-foreign-type-error (simple-error foreign-type-error) 75 ()) 76 77 (defun simple-foreign-type-error (type-name namespace format-control &rest format-arguments) 78 (error 'simple-foreign-type-error 79 :type-name type-name :namespace namespace 80 :format-control format-control :format-arguments format-arguments)) 81 82 (define-condition undefined-foreign-type-error (foreign-type-error) 83 () 84 (:report (lambda (e stream) 85 (format stream "Unknown CFFI type ~S" (foreign-type-error/compound-name e))))) 86 87 (defun undefined-foreign-type-error (type-name &optional (namespace :default)) 88 (error 'undefined-foreign-type-error :type-name type-name :namespace namespace)) 89 90 ;; TODO this is not according to the C namespace rules, 91 ;; see bug: https://bugs.launchpad.net/cffi/+bug/1527947 92 (deftype c-namespace-name () 93 '(member :default :struct :union)) 94 95 ;; for C namespaces read: https://stackoverflow.com/questions/12579142/type-namespace-in-c 96 ;; (section 6.2.3 Name spaces of identifiers) 97 ;; NOTE: :struct is probably an unfortunate name for the tagged (?) namespace 98 (defun find-type-parser (symbol &optional (namespace :default)) 99 "Return the type parser for SYMBOL. NAMESPACE is either :DEFAULT (for 100 variables, functions, and typedefs) or :STRUCT (for structs, unions, and enums)." 101 (check-type symbol (and symbol (not null))) 102 (check-type namespace c-namespace-name) 103 (or (gethash (cons namespace symbol) *type-parsers*) 104 (undefined-foreign-type-error symbol namespace))) 105 106 (defun (setf find-type-parser) (func symbol &optional (namespace :default)) 107 "Set the type parser for SYMBOL." 108 (check-type symbol (and symbol (not null))) 109 (check-type namespace c-namespace-name) 110 ;; TODO Shall we signal a redefinition warning here? 111 (setf (gethash (cons namespace symbol) *type-parsers*) func)) 112 113 (defun undefine-foreign-type (symbol &optional (namespace :default)) 114 (remhash (cons namespace symbol) *type-parsers*) 115 (values)) 116 117 ;;; Using a generic function would have been nicer but generates lots 118 ;;; of style warnings in SBCL. (Silly reason, yes.) 119 (defmacro define-parse-method (name lambda-list &body body) 120 "Define a type parser on NAME and lists whose CAR is NAME." 121 (discard-docstring body) 122 (warn-if-kw-or-belongs-to-cl name) 123 `(eval-when (:compile-toplevel :load-toplevel :execute) 124 (setf (find-type-parser ',name) 125 (lambda ,lambda-list ,@body)) 126 ',name)) 127 128 ;;; Utility function for the simple case where the type takes no 129 ;;; arguments. 130 (defun notice-foreign-type (name type &optional (namespace :default)) 131 (setf (find-type-parser name namespace) (lambda () type)) 132 name) 133 134 ;;;# Generic Functions on Types 135 136 (defgeneric canonicalize (foreign-type) 137 (:documentation 138 "Return the most primitive foreign type for FOREIGN-TYPE, either a built-in 139 type--a keyword--or a struct/union type--a list of the form (:STRUCT/:UNION name). 140 Signals an error if FOREIGN-TYPE is undefined.")) 141 142 (defgeneric aggregatep (foreign-type) 143 (:documentation 144 "Return true if FOREIGN-TYPE is an aggregate type.")) 145 146 (defgeneric foreign-type-alignment (foreign-type) 147 (:documentation 148 "Return the structure alignment in bytes of a foreign type.")) 149 150 (defgeneric foreign-type-size (foreign-type) 151 (:documentation 152 "Return the size in bytes of a foreign type.")) 153 154 (defgeneric unparse-type (foreign-type) 155 (:documentation 156 "Unparse FOREIGN-TYPE to a type specification (symbol or list).")) 157 158 ;;;# Foreign Types 159 160 (defclass foreign-type () 161 () 162 (:documentation "Base class for all foreign types.")) 163 164 (defmethod make-load-form ((type foreign-type) &optional env) 165 "Return the form used to dump types to a FASL file." 166 (declare (ignore env)) 167 `(parse-type ',(unparse-type type))) 168 169 (defmethod foreign-type-size (type) 170 "Return the size in bytes of a foreign type." 171 (foreign-type-size (parse-type type))) 172 173 (defclass named-foreign-type (foreign-type) 174 ((name 175 ;; Name of this foreign type, a symbol. 176 :initform (error "Must specify a NAME.") 177 :initarg :name 178 :accessor name))) 179 180 (defmethod print-object ((type named-foreign-type) stream) 181 "Print a FOREIGN-TYPEDEF instance to STREAM unreadably." 182 (print-unreadable-object (type stream :type t :identity nil) 183 (format stream "~S" (name type)))) 184 185 ;;; Return the type's name which can be passed to PARSE-TYPE. If 186 ;;; that's not the case for some subclass of NAMED-FOREIGN-TYPE then 187 ;;; it should specialize UNPARSE-TYPE. 188 (defmethod unparse-type ((type named-foreign-type)) 189 (name type)) 190 191 ;;;# Built-In Foreign Types 192 193 (defclass foreign-built-in-type (foreign-type) 194 ((type-keyword 195 ;; Keyword in CFFI-SYS representing this type. 196 :initform (error "A type keyword is required.") 197 :initarg :type-keyword 198 :accessor type-keyword)) 199 (:documentation "A built-in foreign type.")) 200 201 (defmethod canonicalize ((type foreign-built-in-type)) 202 "Return the built-in type keyword for TYPE." 203 (type-keyword type)) 204 205 (defmethod aggregatep ((type foreign-built-in-type)) 206 "Returns false, built-in types are never aggregate types." 207 nil) 208 209 (defmethod foreign-type-alignment ((type foreign-built-in-type)) 210 "Return the alignment of a built-in type." 211 (%foreign-type-alignment (type-keyword type))) 212 213 (defmethod foreign-type-size ((type foreign-built-in-type)) 214 "Return the size of a built-in type." 215 (%foreign-type-size (type-keyword type))) 216 217 (defmethod unparse-type ((type foreign-built-in-type)) 218 "Returns the symbolic representation of a built-in type." 219 (type-keyword type)) 220 221 (defmethod print-object ((type foreign-built-in-type) stream) 222 "Print a FOREIGN-TYPE instance to STREAM unreadably." 223 (print-unreadable-object (type stream :type t :identity nil) 224 (format stream "~S" (type-keyword type)))) 225 226 (defvar *built-in-foreign-types* nil) 227 228 (defmacro define-built-in-foreign-type (keyword) 229 "Defines a built-in foreign-type." 230 `(eval-when (:compile-toplevel :load-toplevel :execute) 231 (pushnew ,keyword *built-in-foreign-types*) 232 (notice-foreign-type 233 ,keyword (make-instance 'foreign-built-in-type :type-keyword ,keyword)))) 234 235 ;;;# Foreign Pointer Types 236 237 (defclass foreign-pointer-type (foreign-built-in-type) 238 ((pointer-type 239 ;; Type of object pointed at by this pointer, or nil for an 240 ;; untyped (void) pointer. 241 :initform nil 242 :initarg :pointer-type 243 :accessor pointer-type)) 244 (:default-initargs :type-keyword :pointer)) 245 246 ;;; Define the type parser for the :POINTER type. If no type argument 247 ;;; is provided, a void pointer will be created. 248 (let ((void-pointer (make-instance 'foreign-pointer-type))) 249 (define-parse-method :pointer (&optional type) 250 (if type 251 (make-instance 'foreign-pointer-type :pointer-type (parse-type type)) 252 ;; A bit of premature optimization here. 253 void-pointer))) 254 255 ;;; Unparse a foreign pointer type when dumping to a fasl. 256 (defmethod unparse-type ((type foreign-pointer-type)) 257 (if (pointer-type type) 258 `(:pointer ,(unparse-type (pointer-type type))) 259 :pointer)) 260 261 ;;; Print a foreign pointer type unreadably in unparsed form. 262 (defmethod print-object ((type foreign-pointer-type) stream) 263 (print-unreadable-object (type stream :type t :identity nil) 264 (format stream "~S" (unparse-type type)))) 265 266 ;;;# Structure Type 267 268 (defgeneric bare-struct-type-p (foreign-type) 269 (:documentation 270 "Return true if FOREIGN-TYPE is a bare struct type or an alias of a bare struct type. ")) 271 272 (defmethod bare-struct-type-p ((type foreign-type)) 273 "Return true if FOREIGN-TYPE is a bare struct type or an alias of a bare struct type. " 274 nil) 275 276 (defclass foreign-struct-type (named-foreign-type) 277 ((slots 278 ;; Hash table of slots in this structure, keyed by name. 279 :initform (make-hash-table) 280 :initarg :slots 281 :accessor slots) 282 (size 283 ;; Cached size in bytes of this structure. 284 :initarg :size 285 :accessor size) 286 (alignment 287 ;; This struct's alignment requirements 288 :initarg :alignment 289 :accessor alignment) 290 (bare 291 ;; we use this flag to support the (old, deprecated) semantics of 292 ;; bare struct types. FOO means (:POINTER (:STRUCT FOO) in 293 ;; functions declarations whereas FOO in a structure definition is 294 ;; a proper aggregate type: (:STRUCT FOO), etc. 295 :initform nil 296 :initarg :bare 297 :reader bare-struct-type-p))) 298 299 (defun slots-in-order (structure-type) 300 "A list of the structure's slots in order." 301 (sort (loop for slots being the hash-value of (structure-slots structure-type) 302 collect slots) 303 #'< 304 :key 'slot-offset)) 305 306 (defmethod canonicalize ((type foreign-struct-type)) 307 (if (bare-struct-type-p type) 308 :pointer 309 `(:struct ,(name type)))) 310 311 (defmethod unparse-type ((type foreign-struct-type)) 312 (if (bare-struct-type-p type) 313 (name type) 314 (canonicalize type))) 315 316 (defmethod aggregatep ((type foreign-struct-type)) 317 "Returns true, structure types are aggregate." 318 t) 319 320 (defmethod foreign-type-size ((type foreign-struct-type)) 321 "Return the size in bytes of a foreign structure type." 322 (size type)) 323 324 (defmethod foreign-type-alignment ((type foreign-struct-type)) 325 "Return the alignment requirements for this struct." 326 (alignment type)) 327 328 (defclass foreign-union-type (foreign-struct-type) ()) 329 330 (defmethod canonicalize ((type foreign-union-type)) 331 (if (bare-struct-type-p type) 332 :pointer 333 `(:union ,(name type)))) 334 335 ;;;# Foreign Typedefs 336 337 (defclass foreign-type-alias (foreign-type) 338 ((actual-type 339 ;; The FOREIGN-TYPE instance this type is an alias for. 340 :initarg :actual-type 341 :accessor actual-type 342 :initform (error "Must specify an ACTUAL-TYPE."))) 343 (:documentation "A type that aliases another type.")) 344 345 (defmethod canonicalize ((type foreign-type-alias)) 346 "Return the built-in type keyword for TYPE." 347 (canonicalize (actual-type type))) 348 349 (defmethod aggregatep ((type foreign-type-alias)) 350 "Return true if TYPE's actual type is aggregate." 351 (aggregatep (actual-type type))) 352 353 (defmethod foreign-type-alignment ((type foreign-type-alias)) 354 "Return the alignment of a foreign typedef." 355 (foreign-type-alignment (actual-type type))) 356 357 (defmethod foreign-type-size ((type foreign-type-alias)) 358 "Return the size in bytes of a foreign typedef." 359 (foreign-type-size (actual-type type))) 360 361 (defclass foreign-typedef (foreign-type-alias named-foreign-type) 362 ()) 363 364 (defun follow-typedefs (type) 365 (if (typep type 'foreign-typedef) 366 (follow-typedefs (actual-type type)) 367 type)) 368 369 (defmethod bare-struct-type-p ((type foreign-typedef)) 370 (bare-struct-type-p (follow-typedefs type))) 371 372 (defun structure-slots (type) 373 "The hash table of slots for the structure type." 374 (slots (follow-typedefs type))) 375 376 ;;;# Type Translators 377 ;;; 378 ;;; Type translation is done with generic functions at runtime for 379 ;;; subclasses of TRANSLATABLE-FOREIGN-TYPE. 380 ;;; 381 ;;; The main interface for defining type translations is through the 382 ;;; generic functions TRANSLATE-{TO,FROM}-FOREIGN and 383 ;;; FREE-TRANSLATED-OBJECT. 384 385 (defclass translatable-foreign-type (foreign-type) ()) 386 387 ;;; ENHANCED-FOREIGN-TYPE is used to define translations on top of 388 ;;; previously defined foreign types. 389 (defclass enhanced-foreign-type (translatable-foreign-type 390 foreign-type-alias) 391 ((unparsed-type :accessor unparsed-type))) 392 393 ;;; If actual-type isn't parsed already, let's parse it. This way we 394 ;;; don't have to export PARSE-TYPE and users don't have to worry 395 ;;; about this in DEFINE-FOREIGN-TYPE or DEFINE-PARSE-METHOD. 396 (defmethod initialize-instance :after ((type enhanced-foreign-type) &key) 397 (unless (typep (actual-type type) 'foreign-type) 398 (setf (actual-type type) (parse-type (actual-type type))))) 399 400 (defmethod unparse-type ((type enhanced-foreign-type)) 401 (unparsed-type type)) 402 403 ;;; Checks NAMEs, not object identity. 404 (defun check-for-typedef-cycles (type) 405 (let ((seen (make-hash-table :test 'eq))) 406 (labels ((%check (cur-type) 407 (when (typep cur-type 'foreign-typedef) 408 (when (gethash (name cur-type) seen) 409 (simple-foreign-type-error type :default 410 "Detected cycle in type ~S." type)) 411 (setf (gethash (name cur-type) seen) t) 412 (%check (actual-type cur-type))))) 413 (%check type)))) 414 415 ;;; Only now we define PARSE-TYPE because it needs to do some extra 416 ;;; work for ENHANCED-FOREIGN-TYPES. 417 (defun parse-type (type) 418 (let* ((spec (ensure-list type)) 419 (ptype (apply (find-type-parser (car spec)) (cdr spec)))) 420 (when (typep ptype 'foreign-typedef) 421 (check-for-typedef-cycles ptype)) 422 (when (typep ptype 'enhanced-foreign-type) 423 (setf (unparsed-type ptype) type)) 424 ptype)) 425 426 (defun ensure-parsed-base-type (type) 427 (follow-typedefs 428 (if (typep type 'foreign-type) 429 type 430 (parse-type type)))) 431 432 (defun canonicalize-foreign-type (type) 433 "Convert TYPE to a built-in type by following aliases. 434 Signals an error if the type cannot be resolved." 435 (canonicalize (parse-type type))) 436 437 ;;; Translate VALUE to a foreign object of the type represented by 438 ;;; TYPE, which will be a subclass of TRANSLATABLE-FOREIGN-TYPE. 439 ;;; Returns the foreign value and an optional second value which will 440 ;;; be passed to FREE-TRANSLATED-OBJECT as the PARAM argument. 441 (defgeneric translate-to-foreign (value type) 442 (:method (value type) 443 (declare (ignore type)) 444 value)) 445 446 (defgeneric translate-into-foreign-memory (value type pointer) 447 (:documentation 448 "Translate the Lisp value into the foreign memory location given by pointer. Return value is not used.") 449 (:argument-precedence-order type value pointer)) 450 451 ;;; Similar to TRANSLATE-TO-FOREIGN, used exclusively by 452 ;;; (SETF FOREIGN-STRUCT-SLOT-VALUE). 453 (defgeneric translate-aggregate-to-foreign (ptr value type)) 454 455 ;;; Translate the foreign object VALUE from the type repsented by 456 ;;; TYPE, which will be a subclass of TRANSLATABLE-FOREIGN-TYPE. 457 ;;; Returns the converted Lisp value. 458 (defgeneric translate-from-foreign (value type) 459 (:argument-precedence-order type value) 460 (:method (value type) 461 (declare (ignore type)) 462 value)) 463 464 ;;; Free an object allocated by TRANSLATE-TO-FOREIGN. VALUE is a 465 ;;; foreign object of the type represented by TYPE, which will be a 466 ;;; TRANSLATABLE-FOREIGN-TYPE subclass. PARAM, if present, contains 467 ;;; the second value returned by TRANSLATE-TO-FOREIGN, and is used to 468 ;;; communicate between the two functions. 469 ;;; 470 ;;; FIXME: I don't think this PARAM argument is necessary anymore 471 ;;; because the TYPE object can contain that information. [2008-12-31 LO] 472 (defgeneric free-translated-object (value type param) 473 (:method (value type param) 474 (declare (ignore value type param)))) 475 476 ;;;## Macroexpansion Time Translation 477 ;;; 478 ;;; The following EXPAND-* generic functions are similar to their 479 ;;; TRANSLATE-* counterparts but are usually called at macroexpansion 480 ;;; time. They offer a way to optimize the runtime translators. 481 482 ;;; This special variable is bound by the various :around methods 483 ;;; below to the respective form generated by the above %EXPAND-* 484 ;;; functions. This way, an expander can "bail out" by calling the 485 ;;; next method. All 6 of the below-defined GFs have a default method 486 ;;; that simply answers the rtf bound by the default :around method. 487 (defvar *runtime-translator-form*) 488 489 ;;; EXPAND-FROM-FOREIGN 490 491 (defgeneric expand-from-foreign (value type) 492 (:method (value type) 493 (declare (ignore type)) 494 value)) 495 496 (defmethod expand-from-foreign :around (value (type translatable-foreign-type)) 497 (let ((*runtime-translator-form* `(translate-from-foreign ,value ,type))) 498 (call-next-method))) 499 500 (defmethod expand-from-foreign (value (type translatable-foreign-type)) 501 (declare (ignore value)) 502 *runtime-translator-form*) 503 504 ;;; EXPAND-TO-FOREIGN 505 506 ;; The second return value is used to tell EXPAND-TO-FOREIGN-DYN that 507 ;; an unspecialized method was called. 508 (defgeneric expand-to-foreign (value type) 509 (:method (value type) 510 (declare (ignore type)) 511 (values value t))) 512 513 (defmethod expand-to-foreign :around (value (type translatable-foreign-type)) 514 (let ((*runtime-translator-form* `(translate-to-foreign ,value ,type))) 515 (call-next-method))) 516 517 (defmethod expand-to-foreign (value (type translatable-foreign-type)) 518 (declare (ignore value)) 519 (values *runtime-translator-form* t)) 520 521 ;;; EXPAND-INTO-FOREIGN-MEMORY 522 523 (defgeneric expand-into-foreign-memory (value type ptr) 524 (:method (value type ptr) 525 (declare (ignore type ptr)) 526 value)) 527 528 (defmethod expand-into-foreign-memory :around 529 (value (type translatable-foreign-type) ptr) 530 (let ((*runtime-translator-form* 531 `(translate-into-foreign-memory ,value ,type ,ptr))) 532 (call-next-method))) 533 534 (defmethod expand-into-foreign-memory (value (type translatable-foreign-type) ptr) 535 (declare (ignore value ptr)) 536 *runtime-translator-form*) 537 538 ;;; EXPAND-TO-FOREIGN-DYN 539 540 (defgeneric expand-to-foreign-dyn (value var body type) 541 (:method (value var body type) 542 (declare (ignore type)) 543 `(let ((,var ,value)) ,@body))) 544 545 (defmethod expand-to-foreign-dyn :around 546 (value var body (type enhanced-foreign-type)) 547 (let ((*runtime-translator-form* 548 (with-unique-names (param) 549 `(multiple-value-bind (,var ,param) 550 (translate-to-foreign ,value ,type) 551 (unwind-protect 552 (progn ,@body) 553 (free-translated-object ,var ,type ,param)))))) 554 (call-next-method))) 555 556 ;;; If this method is called it means the user hasn't defined a 557 ;;; to-foreign-dyn expansion, so we use the to-foreign expansion. 558 ;;; 559 ;;; However, we do so *only* if there's a specialized 560 ;;; EXPAND-TO-FOREIGN for TYPE because otherwise we want to use the 561 ;;; above *RUNTIME-TRANSLATOR-FORM* which includes a call to 562 ;;; FREE-TRANSLATED-OBJECT. (Or else there would occur no translation 563 ;;; at all.) 564 (defun foreign-expand-runtime-translator-or-binding (value var body type) 565 (multiple-value-bind (expansion default-etp-p) 566 (expand-to-foreign value type) 567 (if default-etp-p 568 *runtime-translator-form* 569 `(let ((,var ,expansion)) 570 ,@body)))) 571 572 (defmethod expand-to-foreign-dyn (value var body (type enhanced-foreign-type)) 573 (foreign-expand-runtime-translator-or-binding value var body type)) 574 575 ;;; EXPAND-TO-FOREIGN-DYN-INDIRECT 576 ;;; Like expand-to-foreign-dyn, but always give form that returns a 577 ;;; pointer to the object, even if it's directly representable in 578 ;;; CL, e.g. numbers. 579 580 (defgeneric expand-to-foreign-dyn-indirect (value var body type) 581 (:method (value var body type) 582 (declare (ignore type)) 583 `(let ((,var ,value)) ,@body))) 584 585 (defmethod expand-to-foreign-dyn-indirect :around 586 (value var body (type translatable-foreign-type)) 587 (let ((*runtime-translator-form* 588 `(with-foreign-object (,var ',(unparse-type type)) 589 (translate-into-foreign-memory ,value ,type ,var) 590 ,@body))) 591 (call-next-method))) 592 593 (defmethod expand-to-foreign-dyn-indirect 594 (value var body (type foreign-pointer-type)) 595 `(with-foreign-object (,var :pointer) 596 (translate-into-foreign-memory ,value ,type ,var) 597 ,@body)) 598 599 (defmethod expand-to-foreign-dyn-indirect 600 (value var body (type foreign-built-in-type)) 601 `(with-foreign-object (,var ,type) 602 (translate-into-foreign-memory ,value ,type ,var) 603 ,@body)) 604 605 (defmethod expand-to-foreign-dyn-indirect 606 (value var body (type translatable-foreign-type)) 607 (foreign-expand-runtime-translator-or-binding value var body type)) 608 609 (defmethod expand-to-foreign-dyn-indirect (value var body (type foreign-type-alias)) 610 (expand-to-foreign-dyn-indirect value var body (actual-type type))) 611 612 ;;; User interface for converting values from/to foreign using the 613 ;;; type translators. The compiler macros use the expanders when 614 ;;; possible. 615 616 (defun convert-to-foreign (value type) 617 (translate-to-foreign value (parse-type type))) 618 619 (define-compiler-macro convert-to-foreign (value type) 620 (if (constantp type) 621 (expand-to-foreign value (parse-type (eval type))) 622 `(translate-to-foreign ,value (parse-type ,type)))) 623 624 (defun convert-from-foreign (value type) 625 (translate-from-foreign value (parse-type type))) 626 627 (define-compiler-macro convert-from-foreign (value type) 628 (if (constantp type) 629 (expand-from-foreign value (parse-type (eval type))) 630 `(translate-from-foreign ,value (parse-type ,type)))) 631 632 (defun convert-into-foreign-memory (value type ptr) 633 (translate-into-foreign-memory value (parse-type type) ptr)) 634 635 (define-compiler-macro convert-into-foreign-memory (value type ptr) 636 (if (constantp type) 637 (expand-into-foreign-memory value (parse-type (eval type)) ptr) 638 `(translate-into-foreign-memory ,value (parse-type ,type) ,ptr))) 639 640 (defun free-converted-object (value type param) 641 (free-translated-object value (parse-type type) param)) 642 643 ;;;# Enhanced typedefs 644 645 (defclass enhanced-typedef (foreign-typedef) 646 ()) 647 648 (defmethod translate-to-foreign (value (type enhanced-typedef)) 649 (translate-to-foreign value (actual-type type))) 650 651 (defmethod translate-into-foreign-memory (value (type enhanced-typedef) pointer) 652 (translate-into-foreign-memory value (actual-type type) pointer)) 653 654 (defmethod translate-from-foreign (value (type enhanced-typedef)) 655 (translate-from-foreign value (actual-type type))) 656 657 (defmethod free-translated-object (value (type enhanced-typedef) param) 658 (free-translated-object value (actual-type type) param)) 659 660 (defmethod expand-from-foreign (value (type enhanced-typedef)) 661 (expand-from-foreign value (actual-type type))) 662 663 (defmethod expand-to-foreign (value (type enhanced-typedef)) 664 (expand-to-foreign value (actual-type type))) 665 666 (defmethod expand-to-foreign-dyn (value var body (type enhanced-typedef)) 667 (expand-to-foreign-dyn value var body (actual-type type))) 668 669 (defmethod expand-into-foreign-memory (value (type enhanced-typedef) ptr) 670 (expand-into-foreign-memory value (actual-type type) ptr)) 671 672 ;;;# User-defined Types and Translations. 673 674 (defmacro define-foreign-type (name supers slots &rest options) 675 (multiple-value-bind (new-options simple-parser actual-type initargs) 676 (let ((keywords '(:simple-parser :actual-type :default-initargs))) 677 (apply #'values 678 (remove-if (lambda (opt) (member (car opt) keywords)) options) 679 (mapcar (lambda (kw) (cdr (assoc kw options))) keywords))) 680 `(eval-when (:compile-toplevel :load-toplevel :execute) 681 (defclass ,name ,(or supers '(enhanced-foreign-type)) 682 ,slots 683 (:default-initargs ,@(when actual-type `(:actual-type ',actual-type)) 684 ,@initargs) 685 ,@new-options) 686 ,(when simple-parser 687 `(define-parse-method ,(car simple-parser) (&rest args) 688 (apply #'make-instance ',name args))) 689 ',name))) 690 691 (defmacro defctype (name base-type &optional documentation) 692 "Utility macro for simple C-like typedefs." 693 (declare (ignore documentation)) 694 (warn-if-kw-or-belongs-to-cl name) 695 (let* ((btype (parse-type base-type)) 696 (dtype (if (typep btype 'enhanced-foreign-type) 697 'enhanced-typedef 698 'foreign-typedef))) 699 `(eval-when (:compile-toplevel :load-toplevel :execute) 700 (notice-foreign-type 701 ',name (make-instance ',dtype :name ',name :actual-type ,btype))))) 702 703 ;;; For Verrazano. We memoize the type this way to help detect cycles. 704 (defmacro defctype* (name base-type) 705 "Like DEFCTYPE but defers instantiation until parse-time." 706 `(eval-when (:compile-toplevel :load-toplevel :execute) 707 (let (memoized-type) 708 (define-parse-method ,name () 709 (unless memoized-type 710 (setf memoized-type (make-instance 'foreign-typedef :name ',name 711 :actual-type nil) 712 (actual-type memoized-type) (parse-type ',base-type))) 713 memoized-type))))