+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (defclass ginstance (ref-counted-object)
+ (;(class :allocation :alien :type pointer :offset 0)
+ )
+ (:metaclass proxy-class)
+ (:size #.(size-of 'pointer))))
+
+(defun ref-type-number (location &optional offset)
+ (declare (ignore location offset)))
+
+(setf (symbol-function 'ref-type-number) (reader-function 'type-number))
+
+(defun %type-number-of-ginstance (location)
+ (let ((class (ref-pointer location)))
+ (ref-type-number class)))
+
+(defmethod make-proxy-instance :around ((class ginstance-class) location
+ &rest initargs)
+ (declare (ignore class))
+ (let ((class (labels ((find-known-class (type-number)
+ (or
+ (find-class (type-from-number type-number) nil)
+ (unless (zerop type-number)
+ (find-known-class (type-parent type-number))))))
+ (find-known-class (%type-number-of-ginstance location)))))
+ ;; Note that changing the class argument must not alter "the
+ ;; ordered set of applicable methods" as specified in the
+ ;; Hyperspec
+ (if class
+ (apply #'call-next-method class location initargs)
+ (error "Object at ~A has an unkown type number: ~A"
+ location (%type-number-of-ginstance location)))))
+
+
+;;;; Registering fundamental types
+
+(register-type 'nil "void")
+(register-type 'pointer "gpointer")
+(register-type 'char "gchar")
+(register-type 'unsigned-char "guchar")
+(register-type 'boolean "gboolean")
+(register-type 'int "gint")
+(register-type-alias 'integer 'int)
+(register-type-alias 'fixnum 'int)
+(register-type 'unsigned-int "guint")
+(register-type 'long "glong")
+(register-type 'unsigned-long "gulong")
+(register-type 'single-float "gfloat")
+(register-type 'double-float "gdouble")
+(register-type 'string "gchararray")
+(register-type-alias 'pathname 'string)
+
+
+;;;; Introspection of type information
+
+(defvar *derivable-type-info* (make-hash-table))
+
+(defun register-derivable-type (type id expander &optional dependencies)
+ (register-type type id)
+ (let ((type-number (register-type type id)))
+ (setf
+ (gethash type-number *derivable-type-info*)
+ (list expander dependencies))))
+
+(defun find-type-info (type)
+ (dolist (super (cdr (type-hierarchy type)))
+ (let ((info (gethash super *derivable-type-info*)))
+ (return-if info))))
+
+(defun expand-type-definition (type forward-p options)
+ (let ((expander (first (find-type-info type))))
+ (funcall expander (find-type-number type t) forward-p options)))
+
+
+(defbinding type-parent (type) type-number
+ ((find-type-number type t) type-number))
+
+(defun supertype (type)
+ (type-from-number (type-parent type)))
+
+(defbinding %type-interfaces (type) pointer
+ ((find-type-number type t) type-number)
+ (n-interfaces unsigned-int :out))
+
+(defun type-interfaces (type)
+ (multiple-value-bind (array length) (%type-interfaces type)
+ (unwind-protect
+ (map-c-vector 'list #'identity array 'type-number length)
+ (deallocate-memory array))))
+
+(defun implements (type)
+ (mapcar #'type-from-number (type-interfaces type)))
+
+(defun type-hierarchy (type)
+ (let ((type-number (find-type-number type t)))
+ (unless (= type-number 0)
+ (cons type-number (type-hierarchy (type-parent type-number))))))
+
+(defbinding (type-is-p "g_type_is_a") (type super) boolean
+ ((find-type-number type) type-number)
+ ((find-type-number super) type-number))
+
+(defbinding %type-children () pointer
+ (type-number type-number)
+ (num-children unsigned-int :out))
+
+(defun map-subtypes (function type &optional prefix)
+ (let ((type-number (find-type-number type t)))
+ (multiple-value-bind (array length) (%type-children type-number)
+ (unwind-protect
+ (map-c-vector
+ 'nil
+ #'(lambda (type-number)
+ (when (or
+ (not prefix)
+ (string-prefix-p prefix (find-foreign-type-name type-number)))
+ (funcall function type-number))
+ (map-subtypes function type-number prefix))
+ array 'type-number length)
+ (deallocate-memory array)))))
+
+(defun find-types (prefix)
+ (let ((type-list nil))
+ (maphash
+ #'(lambda (type-number expander)
+ (declare (ignore expander))
+ (map-subtypes
+ #'(lambda (type-number)
+ (pushnew type-number type-list))
+ type-number prefix))
+ *derivable-type-info*)
+ type-list))
+
+(defun find-type-dependencies (type &optional options)
+ (let ((find-dependencies (second (find-type-info type))))
+ (when find-dependencies
+ (remove-duplicates
+ (mapcar #'find-type-number
+ (funcall find-dependencies (find-type-number type t) options))))))
+
+
+;; The argument is a list where each elements is on the form
+;; (type . dependencies). This function will not handle indirect
+;; dependencies and types depending on them selves.
+(defun sort-types-topologicaly (unsorted)
+ (flet ((depend-p (type1)
+ (find-if #'(lambda (type2)
+ (and
+ ;; If a type depends a subtype it has to be
+ ;; forward defined
+ (not (type-is-p (car type2) (car type1)))
+ (find (car type2) (cdr type1))))
+ unsorted)))
+ (let ((sorted
+ (loop
+ while unsorted
+ nconc (multiple-value-bind (sorted remaining)
+ (delete-collect-if
+ #'(lambda (type)
+ (or (not (cdr type)) (not (depend-p type))))
+ unsorted)
+ (cond
+ ((not sorted)
+ ;; We have a circular dependency which have to
+ ;; be resolved
+ (let ((selected
+ (find-if
+ #'(lambda (type)
+ (every
+ #'(lambda (dep)
+ (or
+ (not (type-is-p (car type) dep))
+ (not (find dep unsorted :key #'car))))
+ (cdr type)))
+ unsorted)))
+ (unless selected
+ (error "Couldn't resolve circular dependency"))
+ (setq unsorted (delete selected unsorted))
+ (list selected)))
+ (t
+ (setq unsorted remaining)
+ sorted))))))
+
+ ;; Mark types which have to be forward defined
+ (loop
+ for tmp on sorted
+ as (type . dependencies) = (first tmp)
+ collect (cons type (and
+ dependencies
+ (find-if #'(lambda (type)
+ (find (car type) dependencies))
+ (rest tmp))
+ t))))))
+
+
+(defun expand-type-definitions (type-list &optional args)
+ (flet ((type-options (type-number)
+ (let ((name (find-foreign-type-name type-number)))
+ (cdr (assoc name args :test #'string=)))))
+
+ (setq type-list
+ (delete-if
+ #'(lambda (type-number)
+ (let ((name (find-foreign-type-name type-number)))
+ (or
+ (getf (type-options type-number) :ignore)
+ (find-if
+ #'(lambda (options)
+ (and
+ (string-prefix-p (first options) name)
+ (getf (cdr options) :ignore-prefix)
+ (not (some
+ #'(lambda (exception)
+ (string= name exception))
+ (getf (cdr options) :except)))))
+ args))))
+ type-list))
+
+ (dolist (type-number type-list)
+ (let ((name (find-foreign-type-name type-number)))
+ (register-type
+ (getf (type-options type-number) :type (default-type-name name))
+ (register-type-as type-number))))
+
+ ;; This is needed for some unknown reason to get type numbers right
+ (mapc #'find-type-dependencies type-list)
+
+ (let ((sorted-type-list
+ #+clisp (mapcar #'list type-list)
+ #-clisp
+ (sort-types-topologicaly
+ (mapcar
+ #'(lambda (type)
+ (cons type (find-type-dependencies type (type-options type))))
+ type-list))))
+ `(progn
+ ,@(mapcar
+ #'(lambda (pair)
+ (destructuring-bind (type . forward-p) pair
+ (expand-type-definition type forward-p (type-options type))))
+ sorted-type-list)
+ ,@(mapcar
+ #'(lambda (pair)
+ (destructuring-bind (type . forward-p) pair
+ (when forward-p
+ (expand-type-definition type nil (type-options type)))))
+ sorted-type-list)))))
+
+(defun expand-types-with-prefix (prefix args)
+ (expand-type-definitions (find-types prefix) args))
+
+(defun expand-types-in-library (system library args)
+ (let* ((filename (library-filename system library))
+ (types (loop
+ for (type-init . %filename) in *type-initializers*
+ when (equal filename %filename)
+ collect (funcall type-init))))
+ (expand-type-definitions types args)))
+
+(defun list-types-in-library (system library)
+ (let ((filename (library-filename system library)))
+ (loop
+ for (type-init . %filename) in *type-initializers*
+ when (equal filename %filename)
+ collect type-init)))
+
+(defmacro define-types-by-introspection (prefix &rest args)
+ (expand-types-with-prefix prefix args))
+
+(defexport define-types-by-introspection (prefix &rest args)
+ (list-autoexported-symbols (expand-types-with-prefix prefix args)))
+
+(defmacro define-types-in-library (system library &rest args)
+ (expand-types-in-library system library args))
+
+(defexport define-types-in-library (system library &rest args)
+ (list-autoexported-symbols (expand-types-in-library system library args)))
+
+
+;;;; Initialize all non static types in GObject
+
+(init-types-in-library glib "libgobject-2.0")