define-lisp-proxy defines a Lisp proxy, which means creating a Lisp proxy definition and attaching it to name, which can then be used to create Lisp proxies, which are Java proxies where methods invocation ends up calling Lisp functions.
define-lisp-proxy parses interface-and-method-descs to a proxy definition, and attaches it to name. This operation is a "load-time" operation: it does not require running Java, and does not create any proxy. The name can then be used at runtime as argument to make-lisp-proxy or make-lisp-proxy-with-overrides, or to the Java method
com.lispworks.LispCalls.createLispProxy. The result of any these calls is a proxy that implements the interfaces listed in interface-and-method-descs, and can be used in Java whenever an object that implements any of these interfaces is required.
Each element in the list must be either a string which is the Java interface name, or a list where the
cl:car is the Java interface name. Each item specifies a Java interface to implement, except that one item (at most) may specify options relating to the whole proxy definition, by using a list starting with the keyword
:options instead of giving an interface name.
Each method specification must be a list, where the first element is a string with the name of the Java method, and the second element is the symbol specifying what Lisp function to call for this method. The symbol specifies the function to call except when it is overridden (see below about "Overriding"). In some cases, you will want to always override the function to call (typically when you want to use a closure as the function), in which case the symbol can be and should be a keyword (which is ignored by the verifying functions), but does not have to be. See below for how the calling of the Lisp function is done.
The rest of the method specification can contain keyword/value pairs. Currently, the only supported keyword is
:with-user-data, which takes a boolean value, overrides the default value of
:with-user-data of the proxy definition. The default of
:with-user-data of the definition defaults to
nil, and can be changed in the
:options. The value of
:with-user-data specifies whether to pass the user-data of a proxy to the Lisp function.
Specifies the default function to call for methods which do not have a Lisp function. This function is applied to the arguments of the method preceded by the method-name, and if
:default-function-with-user-data is non-nil also with user-data preceding the method-name.
A boolean specifying whether the user-data of a proxy should be passed when the default function is called. When it is non-nil, the user-data is passed as the first argument to the default-function (or the function that overrides it). The default value of
A boolean specifying whether the default for calling functions in the proxy definition is with user-data or not. Each method description can override it as described above. The default value of
nil. This controls the scope of jobject arguments (that is, arguments that are not of primitive type or string). With the default value
:global, jobjects are passed as global jobjects and can be used indefinitely. When
:local, jobjects are passed as a local jobject, which means that they must not be used outside the scope of the function that is invoked by the proxy. Using a local jobject out of scope can cause the system to crash (rather than call
nil, jobjects are not passed at all to the functions. Note that means that the number of arguments that the functions in the proxy receive is different when
nil, because only arguments of primitive type or strings are passed.
If you use
:jobject-scope :local, the function can convert it to global using jobject-ensure-global, and then it can be used out of scope.
The user-data is set up for each individual proxy object by make-lisp-proxy or make-lisp-proxy-with-overrides, and thus allows you to associate each individual proxy with an arbitrary Lisp object. The proxy definition determines whether to use it when calling the Lisp functions in the proxy definition. The default value of user-data is
nil, so if you want to use it you need to specify it by using
:with-user-data, either in the
:options which would give the default value for all calls in the definition, or in individual method specifications. When user-data is passed, it is always passed to the Lisp function as the first argument. Another way to individualize proxies is to use overriding, which also allows you to use closures.
When make-lisp-proxy or make-lisp-proxy-with-overrides make a proxy, they can specify overriding of some of the symbols in the proxy definition. Overriding here means mapping one symbol to to another symbol or a function object. When a symbol is supposed to be called and it is overridden, the target of the mapping is called rather than the symbol. Note that the overriding is specific to each individual proxy rather to the proxy definition, and therefore you can have different proxies using the same proxy definition (and hence implementing the same interface(s)), but calling different Lisp functions. An advantage of overriding is that it allows you to use closures created at runtime instead of symbols.
See the entry for make-lisp-proxy for how the overriding is created.
c) If as a result of (b) there is a function to call, check whether it should be called with the user-data. If
:with-user-data was used in the method specification then use its value, otherwise if
:with-user-data was used in
:options item use this value, otherwise default to
cl:simple-errorcondition and return a default value from the Java method invocation, which is 0 for primitive types or
nullfor other types.
In addition, there is a debugger wrapper (using with-debugger-wrapper) which calls the java-to-lisp-debugger-hook (see init-java-interface) with the condition and then calls
cl:abort. If this abort is not caught by your
cl:abort restart, it is handled by the "throwing blocker" from the previous paragraph, that is the Java method returns 0 or
There is a little overhead associated with using setup-lisp-proxy as opposed to define-lisp-proxy, both in the size of the delivered application (very small) and in runtime, but the difference is not large enough to prevent using setup-lisp-proxy when it is appropriate.
There is an overhead associated with initializing a proxy definition. It is therefore a bad idea to use setup-lisp-proxy many times.
:local are useful optimizations. In proxies that are invoked infrequently, say less than 10 times each second, the difference is probably insignificant, but it is useful for proxies that are called repeatedly by Java code. For example, if you implement the interface
"java.io.FilenameFilter" to pass to
"java.io.File.list" on large directories, using
:jobject-scope :local or
nil will reduce the overhead significantly.
LispWorks User Guide and Reference Manual - 13 Feb 2015