| 1 | (require "JVM") |
|---|
| 2 | |
|---|
| 3 | ;;The package is set to :jvm for convenience, since most of the symbols used |
|---|
| 4 | ;;here come from that package. However, the functions we're definining belong |
|---|
| 5 | ;;to the :java package. |
|---|
| 6 | (in-package :jvm) |
|---|
| 7 | |
|---|
| 8 | (defconstant +abcl-java-object+ (make-jvm-class-name "org.armedbear.lisp.JavaObject")) |
|---|
| 9 | |
|---|
| 10 | (defun java::make-memory-class-loader (&optional (parent (java:get-current-classloader))) |
|---|
| 11 | (java:jnew "org.armedbear.lisp.MemoryClassLoader" parent)) |
|---|
| 12 | |
|---|
| 13 | (defun java:jnew-runtime-class |
|---|
| 14 | (class-name &rest args &key (superclass "java.lang.Object") |
|---|
| 15 | interfaces constructors methods fields (access-flags '(:public)) annotations |
|---|
| 16 | (class-loader (java::make-memory-class-loader))) |
|---|
| 17 | "Creates and loads a Java class with methods calling Lisp closures |
|---|
| 18 | as given in METHODS. CLASS-NAME and SUPER-NAME are strings, |
|---|
| 19 | INTERFACES is a list of strings, CONSTRUCTORS, METHODS and FIELDS are |
|---|
| 20 | lists of constructor, method and field definitions. |
|---|
| 21 | |
|---|
| 22 | Constructor definitions are lists of the form |
|---|
| 23 | (argument-types function &optional super-invocation-arguments) |
|---|
| 24 | where argument-types is a list of strings and function is a lisp function of |
|---|
| 25 | (1+ (length argument-types)) arguments; the instance (`this') is passed in as |
|---|
| 26 | the last argument. The optional super-invocation-arguments is a list of numbers |
|---|
| 27 | between 1 and (length argument-types), where the number k stands for the kth argument |
|---|
| 28 | to the just defined constructor. If present, the constructor of the superclass |
|---|
| 29 | will be called with the appropriate arguments. E.g., if the constructor definition is |
|---|
| 30 | ((\"java.lang.String\" \"int\") #'(lambda (string i this) ...) (2 1)) |
|---|
| 31 | then the constructor of the superclass with argument types (int, java.lang.String) will |
|---|
| 32 | be called with the second and first arguments. |
|---|
| 33 | |
|---|
| 34 | Method definitions are lists of the form |
|---|
| 35 | |
|---|
| 36 | (METHOD-NAME RETURN-TYPE ARGUMENT-TYPES FUNCTION &key MODIFIERS ANNOTATIONS) |
|---|
| 37 | |
|---|
| 38 | where |
|---|
| 39 | METHOD-NAME is a string |
|---|
| 40 | RETURN-TYPE denotes the type of the object returned by the method |
|---|
| 41 | ARGUMENT-TYPES is a list of parameters to the method |
|---|
| 42 | |
|---|
| 43 | The types are either strings naming fully qualified java classes, Lisp |
|---|
| 44 | keywords referring to primitive types (:void, :int, etc.), or 2-element |
|---|
| 45 | lists where the first element is the keyword :array and the second |
|---|
| 46 | element is a keyword referring to a primitive type, e.g. (:array :byte). |
|---|
| 47 | |
|---|
| 48 | FUNCTION is a Lisp function of minimum arity (1+ (length |
|---|
| 49 | argument-types)). The instance (`this') is passed as the first |
|---|
| 50 | argument. |
|---|
| 51 | |
|---|
| 52 | Field definitions are lists of the form (field-name type &key modifiers annotations)." |
|---|
| 53 | (declare (ignorable superclass interfaces constructors methods fields access-flags annotations)) |
|---|
| 54 | (let ((stream (sys::%make-byte-array-output-stream))) |
|---|
| 55 | (multiple-value-bind (class-file method-implementation-fields) |
|---|
| 56 | (apply #'java::%jnew-runtime-class class-name stream :allow-other-keys T args) |
|---|
| 57 | (sys::put-memory-function class-loader |
|---|
| 58 | class-name (sys::%get-output-stream-bytes stream)) |
|---|
| 59 | (let ((jclass (java:jcall "loadClass" class-loader class-name))) |
|---|
| 60 | (dolist (method method-implementation-fields) |
|---|
| 61 | (setf (java:jfield jclass (car method)) (cdr method))) |
|---|
| 62 | jclass)))) |
|---|
| 63 | |
|---|
| 64 | (defconstant +abcl-lisp-integer-object+ (make-jvm-class-name "org.armedbear.lisp.LispInteger")) |
|---|
| 65 | |
|---|
| 66 | (defconstant +abcl-lisp-double-object+ |
|---|
| 67 | (make-jvm-class-name "org.armedbear.lisp.DoubleFloat")) |
|---|
| 68 | |
|---|
| 69 | (defun arg-size (type) |
|---|
| 70 | (if (keywordp type) |
|---|
| 71 | (representation-size type) |
|---|
| 72 | 1)) |
|---|
| 73 | |
|---|
| 74 | (defun box-arguments (argument-types offset all-arg-size) |
|---|
| 75 | "Emits bytecode to box Java method arguments to lisp types. |
|---|
| 76 | |
|---|
| 77 | The boxed arguments end up, in the same order, immediately after the actual |
|---|
| 78 | arguments in the local variable space. |
|---|
| 79 | |
|---|
| 80 | ARGUMENT-TYPES: list of argument types, each as in the JNEW-RUNTIME-CLASS |
|---|
| 81 | form after being passed to JAVA::CANONICALIZE-JAVA-TYPE. |
|---|
| 82 | OFFSET: Extra space used before the args, currently 1 for 'this' or zero for |
|---|
| 83 | static method. |
|---|
| 84 | ALL-ARG-SIZE: The number of 'local variables' (per JVMS23 2.6.1) used to |
|---|
| 85 | hold the arguments to this method. This would be the number of |
|---|
| 86 | arguments, except that long and double arguments take up two variables." |
|---|
| 87 | (loop |
|---|
| 88 | :for arg-type :in argument-types |
|---|
| 89 | :for argn :from offset |
|---|
| 90 | :for arg-offset :from offset |
|---|
| 91 | :do (progn |
|---|
| 92 | (cond |
|---|
| 93 | ((eq arg-type :int) |
|---|
| 94 | (iload arg-offset) |
|---|
| 95 | (emit-invokestatic +abcl-lisp-integer-object+ "getInstance" |
|---|
| 96 | (list :int) +abcl-lisp-integer-object+)) |
|---|
| 97 | ((eq arg-type :double) |
|---|
| 98 | (dload arg-offset) |
|---|
| 99 | (incf arg-offset); doubles take two spots |
|---|
| 100 | (emit-invokestatic +abcl-lisp-double-object+ "getInstance" |
|---|
| 101 | (list :double) +abcl-lisp-double-object+)) |
|---|
| 102 | ((keywordp arg-type) |
|---|
| 103 | (error "Unsupported arg-type: ~A" arg-type)) |
|---|
| 104 | (t (aload arg-offset) |
|---|
| 105 | (emit 'iconst_1) ;;true |
|---|
| 106 | (emit-invokestatic +abcl-java-object+ "getInstance" |
|---|
| 107 | (list +java-object+ :boolean) +lisp-object+))) |
|---|
| 108 | (astore (+ |
|---|
| 109 | all-arg-size; passed arguments size |
|---|
| 110 | argn; boxed argument offset |
|---|
| 111 | ))))) |
|---|
| 112 | |
|---|
| 113 | (defun java::%jnew-runtime-class |
|---|
| 114 | (class-name stream &key (superclass "java.lang.Object") |
|---|
| 115 | interfaces constructors methods fields (access-flags '(:public)) annotations) |
|---|
| 116 | "Actual implementation of jnew-runtime-class. Writes the class bytes to a stream. Returns two values: the finalized class-file structure and the alist of method implementation fields." |
|---|
| 117 | (let* ((jvm-class-name (make-jvm-class-name class-name)) |
|---|
| 118 | (class-file (make-class-file jvm-class-name (make-jvm-class-name superclass) access-flags)) |
|---|
| 119 | method-implementation-fields) |
|---|
| 120 | (setf (class-file-interfaces class-file) |
|---|
| 121 | (mapcar #'make-jvm-class-name interfaces)) |
|---|
| 122 | (when annotations |
|---|
| 123 | (class-add-attribute class-file (make-runtime-visible-annotations-attribute |
|---|
| 124 | :list (mapcar #'parse-annotation annotations)))) |
|---|
| 125 | (setf method-implementation-fields (java::runtime-class-add-methods class-file methods)) |
|---|
| 126 | (java::runtime-class-add-fields class-file fields) |
|---|
| 127 | (if (null constructors) |
|---|
| 128 | (let ((ctor (make-jvm-method :constructor :void nil :flags '(:public)))) |
|---|
| 129 | (class-add-method class-file ctor) |
|---|
| 130 | (with-code-to-method (class-file ctor) |
|---|
| 131 | (aload 0) |
|---|
| 132 | (emit-invokespecial-init (class-file-superclass class-file) nil) |
|---|
| 133 | (emit 'return))) |
|---|
| 134 | (dolist (constructor constructors) |
|---|
| 135 | (destructuring-bind (argument-types function &optional super-args) |
|---|
| 136 | constructor |
|---|
| 137 | (let* ((argument-types (mapcar #'java::canonicalize-java-type argument-types)) |
|---|
| 138 | (argc (length argument-types)) |
|---|
| 139 | (ctor (make-jvm-method :constructor :void argument-types)) |
|---|
| 140 | (field-name (string (gensym "CONSTRUCTOR"))) |
|---|
| 141 | (all-argc (1+ argc))) |
|---|
| 142 | (class-add-method class-file ctor) |
|---|
| 143 | (let ((field (make-field field-name +lisp-object+ :flags '(:public :static)))) |
|---|
| 144 | (class-add-field class-file field)) |
|---|
| 145 | (push (cons field-name function) method-implementation-fields) |
|---|
| 146 | (with-code-to-method (class-file ctor) |
|---|
| 147 | (dotimes (i (* 2 all-argc)) |
|---|
| 148 | (allocate-register nil)) |
|---|
| 149 | |
|---|
| 150 | (aload 0) |
|---|
| 151 | (dolist (arg super-args) |
|---|
| 152 | (aload arg)) |
|---|
| 153 | (emit-invokespecial-init |
|---|
| 154 | (class-file-superclass class-file) |
|---|
| 155 | (map 'list |
|---|
| 156 | (lambda (index) (elt argument-types (1- index))) |
|---|
| 157 | super-args)) |
|---|
| 158 | |
|---|
| 159 | |
|---|
| 160 | (aload 0) |
|---|
| 161 | (emit 'iconst_1) ;;true |
|---|
| 162 | (emit-invokestatic +abcl-java-object+ "getInstance" |
|---|
| 163 | (list +java-object+ :boolean) +lisp-object+) |
|---|
| 164 | (astore all-argc) |
|---|
| 165 | |
|---|
| 166 | (box-arguments argument-types 1 all-argc) |
|---|
| 167 | |
|---|
| 168 | ;;Load the Lisp function from its static field |
|---|
| 169 | (emit-getstatic (class-file-class class-file) field-name +lisp-object+) |
|---|
| 170 | (if (<= all-argc call-registers-limit) |
|---|
| 171 | (progn |
|---|
| 172 | ;;Load the boxed this |
|---|
| 173 | (aload all-argc) |
|---|
| 174 | ;;Load each boxed argument |
|---|
| 175 | (dotimes (i argc) |
|---|
| 176 | (aload (+ i 1 all-argc)))) |
|---|
| 177 | (error "execute(LispObject[]) is currently not supported")) |
|---|
| 178 | (emit-call-execute all-argc) |
|---|
| 179 | |
|---|
| 180 | (emit 'return)))))) |
|---|
| 181 | (finalize-class-file class-file) |
|---|
| 182 | (write-class-file class-file stream) |
|---|
| 183 | (finish-output stream) |
|---|
| 184 | #+test-record-generated-class-file |
|---|
| 185 | (let ((filename (merge-pathnames (format nil "~A.class" class-name)))) |
|---|
| 186 | (with-open-file (f filename :direction :output :element-type '(signed-byte 8)) |
|---|
| 187 | (write-sequence (java::list-from-jarray (sys::%get-output-stream-bytes stream)) f)) |
|---|
| 188 | (format *standard-output* "~&Wrote class file ~A.~%" filename)) |
|---|
| 189 | (values class-file method-implementation-fields))) |
|---|
| 190 | |
|---|
| 191 | (defun java::make-accessor-name (prefix name) |
|---|
| 192 | (let ((initial (char-upcase (aref name 0))) |
|---|
| 193 | (rest (subseq name 1))) |
|---|
| 194 | (format nil "~A~A~A" prefix initial rest))) |
|---|
| 195 | |
|---|
| 196 | ;;This is missing from compiler-pass2.lisp. Probably this and similar functions should reside |
|---|
| 197 | ;;in a dedicated file, independent from both runtime-class and compiler-pass2. |
|---|
| 198 | (defun emit-invokespecial (class-name method-name arg-types return-type) |
|---|
| 199 | (let* ((stack-effect (apply #'descriptor-stack-effect return-type arg-types)) |
|---|
| 200 | (index (pool-add-method-ref *pool* class-name |
|---|
| 201 | method-name (cons return-type arg-types))) |
|---|
| 202 | (instruction (apply #'%emit 'invokespecial (u2 index)))) |
|---|
| 203 | (declare (type (signed-byte 8) stack-effect)) |
|---|
| 204 | (setf (instruction-stack instruction) (1- stack-effect)))) |
|---|
| 205 | |
|---|
| 206 | (defun java::canonicalize-java-type (type) |
|---|
| 207 | (cond |
|---|
| 208 | ((stringp type) (make-jvm-class-name type)) |
|---|
| 209 | ((keywordp type) type) |
|---|
| 210 | ((consp type) type) |
|---|
| 211 | (t (error "Unrecognized Java type: ~A" type)))) |
|---|
| 212 | |
|---|
| 213 | (defun java::emit-unbox-and-return (return-type) |
|---|
| 214 | (cond |
|---|
| 215 | ((eq return-type :void) |
|---|
| 216 | (emit 'pop) |
|---|
| 217 | (emit 'return)) |
|---|
| 218 | ((eq return-type :int) |
|---|
| 219 | (emit-invokevirtual +lisp-object+ "intValue" nil :int) |
|---|
| 220 | (emit 'ireturn)) |
|---|
| 221 | ((eq return-type :boolean) |
|---|
| 222 | (emit-invokevirtual +lisp-object+ "getBooleanValue" nil :boolean) |
|---|
| 223 | (emit 'ireturn)) |
|---|
| 224 | ((eq return-type :double) |
|---|
| 225 | (emit-invokestatic +abcl-lisp-double-object+ "getValue" (list +lisp-object+) :double) |
|---|
| 226 | (emit 'dreturn)) |
|---|
| 227 | ((jvm-class-name-p return-type) |
|---|
| 228 | (emit 'ldc_w (pool-class return-type)) |
|---|
| 229 | (emit-invokevirtual +lisp-object+ "javaInstance" (list +java-class+) +java-object+) |
|---|
| 230 | (emit-checkcast return-type) |
|---|
| 231 | (emit 'areturn)) |
|---|
| 232 | (t |
|---|
| 233 | (error "Unsupported return type: ~A" return-type)))) |
|---|
| 234 | |
|---|
| 235 | (defun java::runtime-class-add-methods (class-file methods) |
|---|
| 236 | (mapcan (lambda (method) (java::runtime-class-add-method class-file method)) |
|---|
| 237 | methods)) |
|---|
| 238 | |
|---|
| 239 | (defun java::runtime-class-add-method (class-file method) |
|---|
| 240 | "Compute METHOD definition and add it to CLASS-FILE. |
|---|
| 241 | |
|---|
| 242 | Returns method implementation fields." |
|---|
| 243 | (let (method-implementation-fields) |
|---|
| 244 | (destructuring-bind (name return-type argument-types function |
|---|
| 245 | &key (modifiers '(:public)) annotations override) |
|---|
| 246 | method |
|---|
| 247 | (let* ((argument-types (mapcar #'java::canonicalize-java-type argument-types)) |
|---|
| 248 | (argc (length argument-types)) |
|---|
| 249 | (args-size (reduce #'+ (mapcar #'arg-size argument-types))) |
|---|
| 250 | (return-type (java::canonicalize-java-type return-type)) |
|---|
| 251 | (jmethod (make-jvm-method name return-type argument-types :flags modifiers)) |
|---|
| 252 | (field-name (string (gensym name))) |
|---|
| 253 | (staticp (member :static modifiers)) |
|---|
| 254 | (this-offset (if staticp 0 1)) |
|---|
| 255 | (all-argc (+ argc this-offset)) |
|---|
| 256 | (all-args-size (+ args-size this-offset))) |
|---|
| 257 | (class-add-method class-file jmethod) |
|---|
| 258 | (let ((field (make-field field-name +lisp-object+ :flags '(:public :static)))) |
|---|
| 259 | (class-add-field class-file field) |
|---|
| 260 | (push (cons field-name function) method-implementation-fields)) |
|---|
| 261 | (when annotations |
|---|
| 262 | (method-add-attribute jmethod (make-runtime-visible-annotations-attribute |
|---|
| 263 | :list (mapcar #'parse-annotation annotations)))) |
|---|
| 264 | (with-code-to-method (class-file jmethod) |
|---|
| 265 | ;;Allocate registers |
|---|
| 266 | (dolist (type argument-types) |
|---|
| 267 | ;; allocate register(s) to store raw argument |
|---|
| 268 | (allocate-register (if (keywordp type) type nil)) |
|---|
| 269 | ;; allocate register to store boxed argument |
|---|
| 270 | (allocate-register nil)) |
|---|
| 271 | (unless staticp |
|---|
| 272 | (allocate-register nil); raw 'this' |
|---|
| 273 | (allocate-register nil); boxed 'this' |
|---|
| 274 | ;;Box "this" (to be passed as the first argument to the Lisp function) |
|---|
| 275 | (aload 0) |
|---|
| 276 | (emit 'iconst_1) ;;true |
|---|
| 277 | (emit-invokestatic +abcl-java-object+ "getInstance" |
|---|
| 278 | (list +java-object+ :boolean) +lisp-object+) |
|---|
| 279 | (astore all-args-size)) |
|---|
| 280 | (box-arguments argument-types this-offset all-args-size) |
|---|
| 281 | ;;Load the Lisp function from its static field |
|---|
| 282 | (emit-getstatic (class-file-class class-file) field-name +lisp-object+) |
|---|
| 283 | (if (<= all-args-size call-registers-limit) |
|---|
| 284 | (progn |
|---|
| 285 | ;;Load the boxed this |
|---|
| 286 | (unless staticp |
|---|
| 287 | (aload all-args-size)) |
|---|
| 288 | ;;Load each boxed argument |
|---|
| 289 | (dotimes (i argc) |
|---|
| 290 | (aload (+ i 1 all-args-size)))) |
|---|
| 291 | (error "execute(LispObject[]) is currently not supported")) |
|---|
| 292 | (emit-call-execute all-argc) |
|---|
| 293 | (java::emit-unbox-and-return return-type)) |
|---|
| 294 | (cond |
|---|
| 295 | ((eq override t) |
|---|
| 296 | (let ((super-method |
|---|
| 297 | (make-jvm-method (format nil "super$~A" name) |
|---|
| 298 | return-type argument-types :flags modifiers))) |
|---|
| 299 | (class-add-method class-file super-method) |
|---|
| 300 | (with-code-to-method (class-file super-method) |
|---|
| 301 | (dotimes (i (1+ (length argument-types))) |
|---|
| 302 | (allocate-register nil)) |
|---|
| 303 | (aload 0) |
|---|
| 304 | (loop |
|---|
| 305 | :for arg-type :in argument-types |
|---|
| 306 | :for i :from 1 |
|---|
| 307 | :do (progn |
|---|
| 308 | (cond |
|---|
| 309 | ((keywordp arg-type) |
|---|
| 310 | (error "Unsupported arg-type: ~A" arg-type)) |
|---|
| 311 | ((eq arg-type :int) :todo) |
|---|
| 312 | (t (aload i))))) |
|---|
| 313 | (emit-invokespecial (class-file-superclass class-file) name |
|---|
| 314 | argument-types return-type) |
|---|
| 315 | ;(emit 'pop) |
|---|
| 316 | (cond |
|---|
| 317 | ((eq return-type :void) |
|---|
| 318 | (emit 'return)) |
|---|
| 319 | ((eq return-type :int) |
|---|
| 320 | (emit 'ireturn)) |
|---|
| 321 | ((eq return-type :boolean) |
|---|
| 322 | (emit 'ireturn)) |
|---|
| 323 | ((jvm-class-name-p return-type) |
|---|
| 324 | (emit 'areturn)) |
|---|
| 325 | (t |
|---|
| 326 | (error "Unsupported return type: ~A" return-type))))))))) |
|---|
| 327 | method-implementation-fields)) |
|---|
| 328 | |
|---|
| 329 | (defun java::runtime-class-add-fields (class-file fields) |
|---|
| 330 | (dolist (field-spec fields) |
|---|
| 331 | (destructuring-bind (name type &key (modifiers '(:public)) annotations |
|---|
| 332 | (getter nil getter-p) (setter nil setter-p) |
|---|
| 333 | (property (and (not getter-p) (not setter-p)))) |
|---|
| 334 | field-spec |
|---|
| 335 | (let* ((type (if (keywordp type) type (make-jvm-class-name type))) |
|---|
| 336 | (field (make-field name type :flags modifiers))) |
|---|
| 337 | (when (member :static modifiers) |
|---|
| 338 | (setf property nil getter nil setter nil)) |
|---|
| 339 | (when annotations |
|---|
| 340 | (field-add-attribute field (make-runtime-visible-annotations-attribute |
|---|
| 341 | :list (mapcar #'parse-annotation annotations)))) |
|---|
| 342 | (class-add-field class-file field) |
|---|
| 343 | (when (or getter property) |
|---|
| 344 | (unless (stringp getter) |
|---|
| 345 | (setf getter (java::make-accessor-name "get" (if (stringp property) property name)))) |
|---|
| 346 | (let ((jmethod (make-jvm-method getter type nil :flags '(:public)))) |
|---|
| 347 | (class-add-method class-file jmethod) |
|---|
| 348 | (with-code-to-method (class-file jmethod) |
|---|
| 349 | (aload 0) |
|---|
| 350 | (emit-getfield (class-file-class class-file) name type) |
|---|
| 351 | (cond |
|---|
| 352 | ((jvm-class-name-p type) (emit 'areturn)) |
|---|
| 353 | ((eq type :int) (emit 'ireturn)) |
|---|
| 354 | (t (error "Unsupported getter return type: ~A" type)))))) |
|---|
| 355 | (when (or setter property) |
|---|
| 356 | (unless (stringp setter) |
|---|
| 357 | (setf setter (java::make-accessor-name "set" (if (stringp property) property name)))) |
|---|
| 358 | (let ((jmethod (make-jvm-method setter :void (list type) :flags '(:public)))) |
|---|
| 359 | (class-add-method class-file jmethod) |
|---|
| 360 | (with-code-to-method (class-file jmethod) |
|---|
| 361 | (aload 0) |
|---|
| 362 | (cond |
|---|
| 363 | ((jvm-class-name-p type) (aload 1)) |
|---|
| 364 | ((eq type :int) (emit 'iload 1)) |
|---|
| 365 | (t (error "Unsupported setter parameter type: ~A" type))) |
|---|
| 366 | (emit-putfield (class-file-class class-file) name type) |
|---|
| 367 | (emit 'return)))))))) |
|---|
| 368 | |
|---|
| 369 | (defmacro java:define-java-class () :todo) |
|---|
| 370 | |
|---|
| 371 | (defun parse-annotation (annotation) |
|---|
| 372 | (when (annotation-p annotation) |
|---|
| 373 | (return-from parse-annotation annotation)) |
|---|
| 374 | (destructuring-bind (class &rest elements) (if (listp annotation) annotation (list annotation)) |
|---|
| 375 | (let (actual-elements) |
|---|
| 376 | (dolist (elem elements) |
|---|
| 377 | (push (parse-annotation-element elem) actual-elements)) |
|---|
| 378 | (make-annotation :type class :elements (nreverse actual-elements))))) |
|---|
| 379 | |
|---|
| 380 | (defun parse-annotation-element (elem) |
|---|
| 381 | (cond |
|---|
| 382 | ((annotation-element-p elem) elem) |
|---|
| 383 | ((atom elem) (make-primitive-or-string-annotation-element :name nil :value elem)) |
|---|
| 384 | ((keywordp (car elem)) (parse-annotation-element `("value" ,@elem))) |
|---|
| 385 | (t |
|---|
| 386 | (destructuring-bind (name &key value enum annotation) elem |
|---|
| 387 | (cond |
|---|
| 388 | (enum (make-enum-value-annotation-element :name name :type enum :value value)) |
|---|
| 389 | (annotation |
|---|
| 390 | (make-annotation-value-annotation-element :name name :value (parse-annotation annotation))) |
|---|
| 391 | ((listp value) |
|---|
| 392 | (make-array-annotation-element :name name :values (mapcar #'parse-annotation-element value))) |
|---|
| 393 | (t (make-primitive-or-string-annotation-element :name name :value value))))))) |
|---|
| 394 | |
|---|
| 395 | ;;TODO: |
|---|
| 396 | ;; - Returning nil as null is broken |
|---|
| 397 | ;; - Function calls with 8+ args |
|---|
| 398 | ;; - super method invocation. Idea: generate companion methods super_... to use with plain jcall. Add a flag per method to optionally disable this when not needed. |
|---|
| 399 | ;; - Constructors |
|---|
| 400 | ;; - optional accessors (CLOS methods) for properties? |
|---|
| 401 | |
|---|
| 402 | #+example |
|---|
| 403 | (java:jnew-runtime-class |
|---|
| 404 | "Foo" |
|---|
| 405 | :interfaces (list "java.lang.Comparable") |
|---|
| 406 | :fields (list '("someField" "java.lang.String") '("anotherField" "java.lang.Object" :getter t)) |
|---|
| 407 | :methods (list |
|---|
| 408 | (list "foo" :void '("java.lang.Object") |
|---|
| 409 | (lambda (this that) (print (list this that))) |
|---|
| 410 | :annotations (list "java.lang.Deprecated" |
|---|
| 411 | '("java.lang.annotation.Retention" |
|---|
| 412 | (:enum "java.lang.annotation.RetentionPolicy" :value "RUNTIME")) |
|---|
| 413 | '("javax.xml.bind.annotation.XmlAttribute" ("required" :value t)) |
|---|
| 414 | '("com.manydesigns.portofino.system.model.users.annotations.RequiresPermissions" |
|---|
| 415 | ("level" |
|---|
| 416 | :enum "com.manydesigns.portofino.model.pages.AccessLevel" |
|---|
| 417 | :value "EDIT") |
|---|
| 418 | ("permissions" :value ("foo" "bar"))))) |
|---|
| 419 | (list "bar" :int '("java.lang.Object") |
|---|
| 420 | (lambda (this that) (print (list this that)) 23)))) |
|---|
| 421 | |
|---|
| 422 | (provide "RUNTIME-CLASS") |
|---|