This document describes the facility mapping types in two cases:
- E2J (Emacs to Java) - arguments from Lisp calls to Java methods
- J2E (Java to Emacs) - values returned to Lisp code from calls to Java methods
When calling Java methods from Lisp, the arguments can be Lisp values or [Lisp representations of] Java objects. The type mapping determines which versions of an overloaded method are valid for a given call and how Lisp values are transformed to Java objects.
Given an object o of type java.util.ArrayList
, we might have a
method call like so:
(/add o anotherObject)
This example is used in the following definitions.
We consider the following elements of a method call:
- A type mapping (E2J) a mapping from a Lisp type to a Java type
- It is stored as an entry in the
gg-to-java-mappings
list
- It is stored as an entry in the
- A target object upon which a method is being called
- In the example, the target object is
o
.- The target class is the Java class of the target object
- In the example, the target class is
java.util.ArrayList
.- The method name is the name of the method being called
- In the example, the method name is
add
.- The argument list is the list of arguments to the Lisp call
- In the example the argument list is
[anotherObject]
.- A set of candidate methods which are associated with the target class corresponding to the method name begin called and arity equal to the length of the argument list
- A candidate list of types corresponding to the argument types declared for each candidate method
- The target method is the Java method ID of the candidate method that is chosen to be called
The variable =gg-to-java-mappings= is a list detailing possible mappings to Java types. Each element is a 3-element list:
- A symbol representing the Java class of objects to be created by the mapping
- A predicate which tests whether a value can be mapped to the type specified by this mapping
- A function (
symbol
orsubr
) which does the mapping from Lisp value to Java object
The built-in mapping handles Lisp strings using this facility.
(add-to-list 'gg-to-java-mapping-alist
'(java.util.String stringp gg-new-string))
If we wish to map Lisp lists to Java =List=s can add a mapping:
(defun my-list-to-java-LinkedList (l)
(let ((newlist (gg-new "java.util.LinkedList")))
(-map (lambda (it) (/add newlist it)) l)
newlist))
(add-to-list 'gg-to-java-mappings
'(java.util.LinkedList ;; type the mapping creates
listp ;; predicate whether the value can map to this type
my-list-to-java-LinkedList))
N.B. This mapping declares the
(add-to-list 'gg-to-java-mappings
'(java.util.LinkedList
stringp
(lambda (s)
(let ((l (split-string s ",")))
;; this function is defined in the previous example
(my-list-to-java-LinkedList l)))))
Give the target class t, we find all methods with a name matching the method call. This is done by walking up the class hierarchy from t. The set of methods is further reduced by removing any whose arity does not match that of the argument list. The remaining set is the set of candidate methods.
We further reduce the set of candidate methods to determine the target method. Each candidate method signature is checked for compatibility against the types of arguments in the argument list. For an argument to be compatible with a type in the signature, the argument must satisfy one of the following:
- The argument’s type matches the type or descends from the type in the corresponding position in the signature.
- The argument can be mapped to a type that either matches or descends from the type in the corresponding position in the signature. We say that this argument “requires mapping” to be compatible with the method signature.
Any candidate method whose signature is not compatible with the argument list is discarded. The target method is chosen as the candidate requiring the least number of argument mappings. If there is no single method meeting this criteria, an error is signaled.
- The lowest level of calling a method is
gg--call-method-raw
which takes the following arguments:
target
The raw object on which the method will be called.methodID
The raw method ID indicating which method to call.args
Ordered list of pairs. First element is a symbol indicating the type of the value. The second element is the value. Possible types are as follow (defined byjvalue
injni.h
):z
A boolean value given as one of the following:
- an integer in which case 0 is false and any other value is true (JNI interpretation)
- a symbol
t
to indicate true nil
to indicate falseb
A byte value given as an integer.c
A char value given as an integer.s
A short value given as an integer.i
An int value given as an integer.j
A long value given as an integer.f
A float value given as a float.d
A double value given as a float.l
A raw Java object given as a userptr.
- The type mapping takes place via (>>>?)
TBD
- Emacs defines the limits of integers (c.f.
most-positive-fixnum
) more narrowly than Java. Some constants may be defined outside this range and should be able to be represented as strings in Lisp and mapped to long values in C/Java.
(defun transformit (it)
"a STRING isntead")
(setq my-alist
'((listp . transformit)))
(defun transform-value-accordingly (it)
"Transform a value according to the first applicable mapping in gg-lisp-to-java-alist"
(let* ((pred (lambda (mapping) (funcall (car mapping) it)))
(transformer (cdr (-first pred my-alist))))
(funcall transformer it)))