# HG changeset patch
# Parent 26f119244959f9e43c921408e9b1c8136e193880
CFFI patches against abcl-1.1.0-dev including questionable implementation of callbacks.
Needs the following form to work:
   (CL:REQUIRE :jna)
diff --git a/src/cffi-abcl.lisp b/src/cffi-abcl.lisp
--- a/src/cffi-abcl.lisp
+++ b/src/cffi-abcl.lisp
@@ -27,6 +27,24 @@
 
 ;;; This implementation requires the Java Native Access (JNA) library.
 ;;; 
+;;; 
+;;; JNA may be automatically loaded into the current JVM process from
+;;; abcl-1.1.0-dev via 
+;;;
+;;; CL-USER> (require 'abcl-contrib)(require 'jna)
+
+(require :abcl-contrib)
+(require :jss)
+(if (let* ((version (lisp-implementation-version))
+           (pattern (#"compile" 'regex.Pattern
+                                "([1-9]+)\\.([1-9]+)\\..+"))
+           (matcher (#"matcher" pattern version))
+           (found (#"find" matcher)))
+      found)
+    (require :jna)
+    (progn 
+      (require :abcl-asdf)
+      (java:add-to-classpath (abcl-asdf:resolve-dependencies "com.sun.jna" "jna" "3.0.9"))))
 
 ;;; This is a preliminary version that will have to be cleaned up,
 ;;; optimized, etc. Nevertheless, it passes all of the relevant CFFI
@@ -68,6 +86,7 @@
 
 (in-package #:cffi-sys)
 
+
 (defun private-jfield (class-name field-name instance)
   (let ((field (find field-name
                      (jcall (jmethod "java.lang.Class" "getDeclaredFields")
@@ -297,14 +316,17 @@
               when fn do (return fn))
         (find-it name (gethash library *loaded-libraries*)))))
 
+(defun convert-calling-convention (convention)
+  (ecase convention
+    (:stdcall "ALT_CONVENTION")
+    (:cdecl "C_CONVENTION")))
+
 (defun make-function-pointer (pointer cconv)
   (jnew (private-jconstructor "com.sun.jna.Function"
                               "com.sun.jna.Pointer" "int")
         pointer
         (jfield "com.sun.jna.Function"
-                (ecase cconv
-                  (:cdecl "C_CONVENTION")
-                  (:stdcall "ALT_CONVENTION")))))
+                (convert-calling-convention convention))))
 
 (defun lisp-value-to-java (value foreign-type)
   (if (eq foreign-type :pointer)
@@ -347,32 +369,135 @@
           else do (setf return-type type)
           finally (return (values types fargs return-type)))))
 
-(defmacro %foreign-funcall (name args &key library calling-convention)
+(defmacro %foreign-funcall (name args &key library convention)
+  (declare (ignore convention))
   (multiple-value-bind (types fargs rettype)
       (foreign-funcall-type-and-args args)
     `(%%foreign-funcall (find-foreign-function ',name ',library)
                         (list ,@fargs) ',types ',rettype)))
 
-(defmacro %foreign-funcall-pointer (ptr args &key calling-convention)
+(defmacro %foreign-funcall-pointer (ptr args &key convention)
   (multiple-value-bind (types fargs rettype)
       (foreign-funcall-type-and-args args)
-    `(%%foreign-funcall (make-function-pointer ,ptr ',calling-convention)
+    `(%%foreign-funcall (make-function-pointer ,ptr ',convention)
                         (list ,@fargs) ',types ',rettype)))
 
 ;;;# Callbacks
+;;;
+;;; Implementation by Mark .
 
-;;; TODO. IIUC, implementing this functionality would require being
-;;; able to create new interface definitions at runtime, which is
-;;; apparently no supported by ABCL as of June 2009.
+(defun foreign-to-callback-type (type)
+  (ecase type
+    ((:int :unsigned-int) 
+     :int)
+    ((:long :unsigned-long) 
+     (jvm::make-jvm-class-name "com.sun.jna.NativeLong"))
+    ((:long-long :unsigned-long-long)
+     (jvm::make-jvm-class-name "java.lang.Long"))
+    (:pointer
+     (jvm::make-jvm-class-name "com.sun.jna.Pointer"))
+    (:float
+     :float)
+    (:double
+     :double)
+    ((:char :unsigned-char) 
+     :byte)
+    ((:short :unsigned-short)
+     :short)
+    (:void
+     :void)))
+    
+(defvar *callbacks* (make-hash-table))
 
-(defmacro %defcallback (name rettype arg-names arg-types body
-                        &key calling-convention)
-  (warn "callback support unimplemented"))
+(defmacro %defcallback (name return-type arg-names arg-types body
+                        &key convention)
+  (declare (ignore convention))
+  `(let ((interface-name ,(define-jna-callback-interface return-type arg-types)))
+     (setf (gethash ',name *callbacks*)
+           (jinterface-implementation interface-name "callback"
+                                      (lambda (,@arg-names)
+                                        ,body)))))
 
+(jvm::define-class-name +callback-object+ "com.sun.jna.Callback")
+(defconstant +dynamic-callback-package+
+  "org/armedbear/jna/dynamic/callbacks")
+
+(defun define-jna-callback-interface (returns args)
+  (multiple-value-bind (interface interface-name)
+      (%define-jna-callback-interface 
+       (foreign-to-callback-type returns)
+       (mapcar (lambda (type) (foreign-to-callback-type type)) args))
+    (load-class interface)
+    interface-name))
+
+(defun %define-jna-callback-interface (returns args)
+  "Returns the Java byte[] array of a class representing a Java interface.
+
+The fully qualified dotted name of the generated class is returned as
+the second value."
+  (let ((name (symbol-name (gensym))))
+    (values
+     (define-java-interface name +dynamic-callback-package+
+       `(("callback" ,returns ,args))
+       `(,+callback-object+))
+     (format nil "~A.~A" 
+             (substitute #\. #\/ +dynamic-callback-package+) name))))
+
+(defun define-java-interface (name package methods 
+                              &optional (superinterfaces nil))
+"Define a class for a Java interface called NAME in PACKAGE with METHODS.
+
+METHODS is a list of (NAME RETURN-TYPE (ARG-TYPES)) entries.  NAME is
+a string.  The values of RETURN-TYPE and the list of ARG-TYPES for the
+defined method follow the are either references to Java objects as
+created by JVM::MAKE-JVM-CLASS-NAME, or keywords representing Java
+primtive types as contained in JVM::MAP-PRIMITIVE-TYPE.
+
+SUPERINTERFACES optionally contains a list of interfaces that this
+interface extends specified as fully qualifed dotted Java names."
+  (let* ((class-name-string (format nil "~A/~A" package name))
+         (class-name (jvm::make-jvm-class-name class-name-string))
+         (class (jvm::make-class-interface-file class-name)))
+    (dolist (superinterface superinterfaces)
+      (jvm::class-add-superinterface 
+       class 
+       (if (typep superinterface 'jvm::jvm-class-name)
+           superinterface
+           (jvm::make-jvm-class-name superinterface))))
+    (dolist (method methods)
+      (let ((name (first method))
+            (returns (second method))
+            (args (third method)))
+      (jvm::class-add-method
+       class
+       (jvm::make-jvm-method name returns args
+                             :flags '(:public :abstract)))))
+    (jvm::finalize-class-file class)
+    (let ((s (sys::%make-byte-array-output-stream)))
+      (jvm::write-class-file class s)
+      (sys::%get-output-stream-bytes s))))
+
+(defun load-class (class-bytes) 
+  "Load the Java byte[] array CLASS-BYTES as a Java class."
+  (let ((load-class-method 
+         (jmethod "org.armedbear.lisp.JavaClassLoader"
+                  "loadClassFromByteArray" "[B")))
+    (jcall load-class-method java::*classloader* class-bytes)))
+
+;;; Test function: unused in CFFI
+(defun write-class (class-bytes pathname)
+  "Write the Java byte[] array CLASS-BYTES to PATHNAME."
+  (with-open-file (stream pathname 
+                          :direction :output 
+                          :element-type '(signed-byte 8))
+    (dotimes (i (jarray-length class-bytes))
+      (write-byte (jarray-ref class-bytes i) stream))))
+                          
 (defun %callback (name)
-  (error "callback support unimplemented"))
+  (or (gethash name *callbacks*)
+      (error "Undefined callback: ~S" name)))
 
-;;;# Loading and Closign Foreign Libraries
+;;;# Loading and closing Foreign Libraries
 
 (defparameter *loaded-libraries* (make-hash-table))
 
diff --git a/tests/libtest.so b/tests/libtest.so
deleted file mode 100644
Binary file tests/libtest.so has changed
diff --git a/tests/libtest2.so b/tests/libtest2.so
deleted file mode 100644
Binary file tests/libtest2.so has changed