Java Native Interface
(require jni) | package: jni |
This library provides interoperability with a Java Runtime Environment via the C JNI interface. Although the wrappers provide a Racket-like interface, it is possible for unexpected usage patterns to corrupt or crash the Racket process. This whole library should be considered unsafe, and not used with untrusted code.
In particular, there are likely to be bugs. Save your work before running.
1 Getting Started
This is a work-in-progress, and only suitable for experimentation at this point. Stay tuned!
If you’re familiar with JNI and the Racket FFI, try this to get started:
#lang racket (require racket/class jni jni/unsafe) (let-jni-env (get-jni-env) (define env (require-jni-env)) (println (send env GetVersion)) (with-jni-scope (λ () (define sptr (send env NewString "hello, java")) (define s (new jstring% [ref (new local-reference% [_type _jstring] [pointer sptr])])) (println (send s get-length)) (send s delete))))
1.1 Implementing Native Methods
$ javac --release 10 example/NativeGreet.java |
$ racket example/native-greet.rkt |
Java said: "hello, Racket" |
2 High-level wrappers
2.1 Object wrappers
procedure
(method-id? v) → boolean?
v : any/c
2.1.1 Object
| ||
superclass: object% | ||
|
method
(send a-jobject get-ref-type) → exact-integer?
method
(send a-jobject instance-of? clazz) → boolean?
clazz : (is-a?/c jclass%)
method
(send a-jobject same-object? obj) → boolean?
obj : (is-a?/c jobject%)
method
(send a-jobject get-field-id name sig) → field-id?
name : string? sig : string?
method
(send a-jobject set-field! f v) → void?
f : field-id? v : any/c
method
(send a-jobject get-method-id name sig) → method-id?
name : string? sig : string?
method
(send a-jobject call-method m arg ...) → any/c
m : method-id? arg : any/c
method
(send a-jobject call-nonvirtual-method m c arg ...) → any/c m : method-id? c : (is-a?/c jclass%) arg : any/c
method
(send a-jobject monitor-enter) → void?
method
(send a-jobject monitor-exit) → void?
procedure
(jni-alloc-object class) → (is-a?/c jobject%)
class : (is-a?/c jclass%)
procedure
(jni-new-object class ctor arg ...) → (is-a?/c jobject%)
class : (is-a?/c jclass%) ctor : method-id? arg : any/c
2.1.2 Class
method
(send a-jclass get-superclass) → (or/c (is-a?/c jclass%) #f)
Returns the superclass of this class, or #f if this class is Object or an interface.
method
(send a-jclass assignable-from? cls) → boolean?
cls : (is-a?/c jclass%) Returns #t if cls is a subclass of (or the same as) this class.
method
(send a-jclass get-static-field-id name sig) → field-id? name : string? sig : string?
method
(send a-jclass get-static-field f) → any/c
f : field-id?
method
(send a-jclass set-static-field! f v) → void?
f : field-id? v : any/c
method
(send a-jclass get-static-method-id name sig) → method-id? name : string? sig : string?
method
(send a-jclass call-static-method m arg ...) → any/c
m : method-id? arg : any/c
method
(send a-jclass register-natives! methods) → void?
methods : (listof (list/c string? string? procedure?)) Registers Racket procedures as native methods of this class. The methods argument is a list of the form (name signature proc), where name is the method name, signature is a JNI signature string, and proc is the procedure implementing the method. proc must take at least one argument, which is the this reference. Additional arguments must match the signature.
method
(send a-jclass unregister-natives!) → void?
procedure
(jni-define-class name buf [loader]) → (is-a?/c jclass%)
name : string? buf : bytes? loader : (or/c (is-a?/c jobject%) #f) = #f
procedure
(jni-find-class name) → (is-a?/c jclass%)
name : string?
2.1.3 String
method
(send a-jstring get-length) → exact-nonnegative-integer?
method
(send a-jstring get-region start len) → string?
start : exact-nonnegative-integer? len : exact-positive-integer?
procedure
(jni-new-string str) → (is-a?/c jstring%)
str : string?
2.1.4 Arrays
method
(send a-jarray get-length) → exact-nonnegative-integer?
|
superclass: jarray% |
method
(send a-jarray/object get-element index)
→ (or/c (is-a?/c jobject%) #f) index : exact-nonnegative-integer?
method
(send a-jarray/object set-element! index value) → void? index : exact-nonnegative-integer? value : (or/c (is-a?/c jobject%) #f)
procedure
(jni-new-object-array length element-class [ initial-element]) → (is-a?/c jarray/object%) length : exact-nonnegative-integer? element-class : (is-a?/c jclass%) initial-element : (or/c (is-a?/c jobject%) #f) = #f
|
superclass: jarray% |
method
(send a-jarray/primitive call-with-elements/critical mode proc) → any mode : exact-integer? proc : (-> cpointer? any) Calls proc with a pointer to the array contents, using GetPrimitiveArrayCritical. Be careful with this: if something goes wrong you can break the JVM.
method
(send a-jarray/primitive copy-to-vector! start end dest [ dest-start]) → void? start : exact-nonnegative-integer? end : exact-nonnegative-integer? dest : vector? dest-start : exact-nonnegative-integer? = 0
method
(send a-jarray/primitive copy-from-vector! start end src [ src-start]) → void? start : exact-nonnegative-integer? end : exact-nonnegative-integer? src : vector? src-start : exact-nonnegative-integer? = 0
method
(send a-jarray/primitive get-region start end) → vector? start : exact-nonnegative-integer? end : exact-nonnegative-integer?
procedure
(jni-new-primitive-array length type)
→ (is-a?/c jarray/primitive%) length : exact-nonnegative-integer?
type :
(or/c 'boolean 'byte 'char 'short 'int 'long 'float 'double)
2.1.5 Exceptions (Throwable)
|
superclass: jobject% |
procedure
t : (is-a?/c jthrowable%)
procedure
(jni-throw/new jclass [message]) → void?
jclass : (is-a?/c jclass%) message : (or/c string? #f) = #f
procedure
(jni-exception-thrown?) → boolean?
procedure
(jni-get-exception) → (or/c (is-a?/c jthrowable%) #f)
procedure
procedure
procedure
(jni-fatal-error! [msg]) → void?
msg : (or/c string? #f) = #f
procedure
2.2 Casts
If you have an instance of jobject%, say, but you know that on the Java side it is really a String, it is possible to cast the object to an instance of jstring%. No type checking is performed: if you make an invalid cast, things will blow up later on.
procedure
(jobject-cast obj type%) → (is-a?/c type%)
obj : (is-a?/c jobject%) type% : (subclass?/c jobject%)
2.3 References
|
method
(send a-reference ->weak-reference) → (is-a?/c reference<%>)
Returns a new weak reference for the same object. Weak references may not be dereferenced: convert to a local or global reference first.
method
(send a-reference ->local-reference)
→ (or/c (is-a?/c reference<%>) #f) Returns a new local reference for the same object. If the current reference is a weak reference which has already been collected, this method returns #f.
method
(send a-reference ->global-reference)
→ (or/c (is-a?/c reference<%>) #f) Returns a new global reference for the same object. If the current reference is a weak reference which has already been collected, this method returns #f.Releases the reference. It is an error to use the reference after calling this method.
method
(send a-reference get-pointer) → jobject?
Returns a pointer to the referenced object. Best not to use this unless you know what you’re doing.
3 Errors
Certain JNI methods signal an error in their return code. If the error is due to a thrown Java exception, the wrapper will raise an instance of exn:fail:jni:throw. In other cases, a numeric error code is returned and the wrapper raises exn:fail:jni:error.
Not all JNI functions check for exceptions. You can call jni-check-exception to explicitly check and raise a Racket-side exception if a Java exception is pending.
Java exceptions remain "active" until they are cancelled. If you catch and handle an instance of exn:fail:jni:throw, you should also call jni-clear-exception! to prevent the exception from propagating when control returns to the Java side.
struct
(struct exn:fail:jni exn:fail () #:extra-constructor-name make-exn:fail:jni)
struct
(struct exn:fail:jni:error exn:fail:jni (code) #:extra-constructor-name make-exn:fail:jni:error) code : exact-integer?
struct
(struct exn:fail:jni:throw exn:fail:jni (object) #:extra-constructor-name make-exn:fail:jni:throw) object : (is-a?/c jthrowable%)
4 Attaching to the JVM
These functions are dangerous, and may change or be removed.
procedure
(current-jni-env) → (or/c (is-a?/c JNIEnv<%>) #f)
(current-jni-env env) → void? env : (or/c (is-a?/c JNIEnv<%>) #f)
procedure
(require-jni-env) → (is-a?/c JNIEnv<%>)
syntax
(let-jni-env env body ...+)
env : (is-a?/c JNIEnv<%>)
procedure
(with-jni-scope thunk) → any
thunk : (-> any)
procedure
(with-jni-frame thunk) → (or/c (is-a? reference<%>) #f)
thunk : (-> (or/c (is-a? reference<%>) #f))
5 Low Level Interface
(require jni/unsafe) | package: jni |
These interfaces provide access to the raw C API with minimal wrapping. Needless to say, it is easy to cause memory corruption and crashes if used incorrectly.
5.1 Types
value
_jbyte : ctype? _jchar : ctype? _jshort : ctype? _jint : ctype? _jlong : ctype? _jfloat : ctype? _jdouble : ctype? _jsize : ctype?
These are defined as tagged pointer types using define-cpointer-type, so predicate functions and /null variants are also available.
value
_jbooleanArray : ctype? _jbyteArray : ctype? _jcharArray : ctype? _jshortArray : ctype? _jintArray : ctype? _jlongArray : ctype? _jfloatArray : ctype? _jdoubleArray : ctype?
These all derive from _jarray.
value
value
_jmethodID : ctype?
value
value
JNI_VERSION_1_1 : exact-nonnegative-integer? = 65537
JNI_VERSION_1_2 : exact-nonnegative-integer? = 65538 JNI_VERSION_1_4 : exact-nonnegative-integer? = 65540 JNI_VERSION_1_6 : exact-nonnegative-integer? = 65542 JNI_VERSION_1_8 : exact-nonnegative-integer? = 65544 JNI_VERSION_9 : exact-nonnegative-integer? = 589824 JNI_VERSION_10 : exact-nonnegative-integer? = 655360
value
JNI_ABORT : exact-nonnegative-integer? = 2
5.2 Modified UTF-8
procedure
(bytes->string/modified-utf-8 bstr [ err-char start end]) → string? bstr : bytes? err-char : (or/c #f char?) = #f start : exact-nonnegative-integer? = 0 end : exact-nonnegative-integer? = (bytes-length bstr)
Invalid byte sequences will be decoded as err-char if it is a character; an exception will be raised otherwise.
procedure
(string->bytes/modified-utf-8 str [start end]) → bytes?
str : string? start : exact-nonnegative-integer? = 0 end : exact-nonnegative-integer? = (string-length str)
5.3 JNI Library
method
(send a-JNI GetDefaultJavaVMInitArgs version) → JavaVMInitArgs?
version : exact-integer?
method
(send a-JNI GetCreatedJavaVMs) → (listof (is-a?/c JavaVM<%>))
value
(define-cstruct _JavaVMInitArgs ([version _jint] [nOptions _jint] [options _JavaVMOption-pointer/null] [ignoreUnrecognized _jboolean]))
value
(define-cstruct _JavaVMOption ([optionString _string/locale] [extraInfo (_or-null _pointer)]))
procedure
(get-jni-env [version]) → (is-a?/c JNIEnv<%>)
version : exact-integer? = JNI_VERSION_10
5.4 Invocation interface
|
method
(send a-JavaVM DestroyJavaVM) → void?
method
(send a-JavaVM AttachCurrentThread thr_args)
→ (is-a?/c JNIEnv<%>) thr_args : (or/c #f JavaVMAttachArgs?)
method
(send a-JavaVM DetachCurrentThread) → void?
method
version : exact-nonnegative-integer?
method
(send a-JavaVM AttachCurrentThreadAsDaemon args)
→ (is-a?/c JNIEnv<%>) args : (or/c #f JavaVMAttachArgs?)
value
(define-cstruct _JavaVMAttachArgs ([version _jint] [name _string/modified-utf-8] [group _jobject/null]))
5.5 Native interface
|
value
(define-cstruct _JNINativeMethod ([name _string/modified-utf-8] [signature _string/modified-utf-8] [fnPtr _fpointer]))