Java Native Interface
1 Getting Started
1.1 Implementing Native Methods
2 High-level wrappers
2.1 Object wrappers
field-id?
method-id?
2.1.1 Object
jobject%
get-class
get-ref-type
instance-of?
same-object?
get-field-id
get-field
set-field!
get-method-id
call-method
call-nonvirtual-method
monitor-enter
monitor-exit
jni-alloc-object
jni-new-object
2.1.2 Class
jclass%
get-superclass
assignable-from?
get-static-field-id
get-static-field
set-static-field!
get-static-method-id
call-static-method
register-natives!
unregister-natives!
jni-define-class
jni-find-class
2.1.3 String
jstring%
get-length
get-chars
get-region
jni-new-string
2.1.4 Arrays
jarray%
get-length
jarray/  object%
get-element
set-element!
jni-new-object-array
jarray/  primitive%
call-with-elements/  critical
copy-to-vector!
copy-from-vector!
get-region
jarray/  boolean%
jarray/  byte%
jarray/  char%
jarray/  short%
jarray/  int%
jarray/  long%
jarray/  float%
jarray/  double%
jni-new-primitive-array
2.1.5 Exceptions (Throwable)
jthrowable%
jni-throw
jni-throw/  new
jni-exception-thrown?
jni-get-exception
jni-describe-exception
jni-clear-exception!
jni-fatal-error!
current-jni-dump-exceptions?
jni-check-exception
2.2 Casts
jobject-cast
2.3 References
reference<%>
->weak-reference
->local-reference
->global-reference
delete
get-pointer
3 Errors
exn:  fail:  jni
exn:  fail:  jni:  error
exn:  fail:  jni:  throw
4 Attaching to the JVM
current-jni-env
require-jni-env
with-jni-env
let-jni-env
with-jni-scope
with-jni-frame
5 Low Level Interface
5.1 Types
_  jboolean
_  jbyte
_  jchar
_  jshort
_  jint
_  jlong
_  jfloat
_  jdouble
_  jsize
_  jobject
_  jclass
_  jstring
_  jarray
_  jthrowable
_  jobject  Array
_  jboolean  Array
_  jbyte  Array
_  jchar  Array
_  jshort  Array
_  jint  Array
_  jlong  Array
_  jfloat  Array
_  jdouble  Array
_  jweak
_  jobject  Ref  Type
_  jfield  ID
_  jmethod  ID
_  jvalue
_  string/  modified-utf-8
JNI_  VERSION_  1_  1
JNI_  VERSION_  1_  2
JNI_  VERSION_  1_  4
JNI_  VERSION_  1_  6
JNI_  VERSION_  1_  8
JNI_  VERSION_  9
JNI_  VERSION_  10
JNI_  COMMIT
JNI_  ABORT
5.2 Modified UTF-8
bytes->string/  modified-utf-8
string->bytes/  modified-utf-8
5.3 JNI Library
JNI%
Get  Default  Java  VMInit  Args
Get  Created  Java  VMs
Create  Java  VM
_  Java  VMInit  Args
_  Java  VMOption
get-jni-env
5.4 Invocation interface
Java  VM<%>
Destroy  Java  VM
Attach  Current  Thread
Detach  Current  Thread
Get  Env
Attach  Current  Thread  As  Daemon
_  Java  VM
_  Java  VMAttach  Args
5.5 Native interface
JNIEnv<%>
_  JNIEnv
_  JNINative  Method
8.12

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🔗ℹ

See example/NativeGreet.java for a simple Java class with a native method. This can be compiled to a class file like so:

$ javac --release 10 example/NativeGreet.java

Note that we work in the top-level folder of this package due to the way in which the JRE searches for classes.

example/native-greet.rkt shows how we can implement the native method in this class as a Racket function. In this example, we attach to the JVM, load the class compiled above, register the native method and call a Java method which in turn calls our Racket method.

$ racket example/native-greet.rkt

Java said: "hello, Racket"

2 High-level wrappers🔗ℹ

2.1 Object wrappers🔗ℹ

procedure

(field-id? v)  boolean?

  v : any/c
Predicate for (wrapped) JNI field IDs.

procedure

(method-id? v)  boolean?

  v : any/c
Predicate for (wrapped) JNI method IDs.

2.1.1 Object🔗ℹ

class

jobject% : class?

  superclass: object%

  extends: reference<%>
Represents an instance of Java Object. All other object wrappers inherit from this class.

method

(send a-jobject get-class)  (is-a?/c jclass%)

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 get-field f)  any/c

  f : field-id?

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%)
Allocates (but does not initialize) a Java object. You probably don’t want to use this.

procedure

(jni-new-object class ctor arg ...)  (is-a?/c jobject%)

  class : (is-a?/c jclass%)
  ctor : method-id?
  arg : any/c
Creates a new Java object of class class, calling the specified constructor ctor.

2.1.2 Class🔗ℹ

class

jclass% : class?

  superclass: jobject%

Represents a Java 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
Defines a new Java class from bytecode.

procedure

(jni-find-class name)  (is-a?/c jclass%)

  name : string?
Find an existing Java class by name or throws an exception if not found.

2.1.3 String🔗ℹ

class

jstring% : class?

  superclass: jobject%

Represents a Java string.

method

(send a-jstring get-length)  exact-nonnegative-integer?

method

(send a-jstring get-chars)  string?

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?
Creates a new Java string.

2.1.4 Arrays🔗ℹ

class

jarray% : class?

  superclass: jobject%

method

(send a-jarray get-length)  exact-nonnegative-integer?

class

jarray/object% : class?

  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
Creates a new object array filled with initial-element.

class

jarray/primitive% : class?

  superclass: jarray%

Base class for Java primitive arrays.

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?

Java primitive arrays of each type.

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)
Creates a new primitive array.

2.1.5 Exceptions (Throwable)🔗ℹ

class

jthrowable% : class?

  superclass: jobject%

Represents an instance of a Java Throwable.

procedure

(jni-throw t)  void?

  t : (is-a?/c jthrowable%)
Throws t as a Java exception. Note that this does not result in a Racket exception: you should return normally without calling any other JNI functions to allow propagation to continue on the Java side.

procedure

(jni-throw/new jclass [message])  void?

  jclass : (is-a?/c jclass%)
  message : (or/c string? #f) = #f
Throws a new instance of jclass (which must be a subclass of Throwable). See jni-throw for semantics.

procedure

(jni-exception-thrown?)  boolean?

Check whether a Java exception is pending.

procedure

(jni-get-exception)  (or/c (is-a?/c jthrowable%) #f)

Returns the pending exception, if any, or #f if all is well.

procedure

(jni-describe-exception)  void?

Causes the JVM to dump debug information about the pending exception to stderr.

procedure

(jni-clear-exception!)  void?

Clear any pending Java exception.

procedure

(jni-fatal-error! [msg])  void?

  msg : (or/c string? #f) = #f
Causes the JVM to exit immediately, taking your Racket process with it.

When this parameter is any value other than #f, Java exceptions thrown by jni-check-exception will be dumped to the console using jni-describe-exception. The default values is #f.

procedure

(jni-check-exception)  void?

Checks whether a Java exception is pending, and throws an instance of exn:fail:jni:throw if so.

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%)
Convert obj (which may be an instance of any wrapped type) to an instance of type%. Local/global references are preserved.

2.3 References🔗ℹ

This interface represents a reference to a Java object.

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.

method

(send a-reference delete)  void?

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)
Supertype for all JNI errors.

struct

(struct exn:fail:jni:error exn:fail:jni (code)
    #:extra-constructor-name make-exn:fail:jni:error)
  code : exact-integer?
Represents an error return code from a JNI function. See the JNI documentation for the interpretation of code.

struct

(struct exn:fail:jni:throw exn:fail:jni (object)
    #:extra-constructor-name make-exn:fail:jni:throw)
  object : (is-a?/c jthrowable%)
Represents a thrown Java exception. The exception will be propagated back to the Java side unless jni-clear-exception! is called.

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)
Get or set the current thread-local JNI environment interface.

procedure

(require-jni-env)  (is-a?/c JNIEnv<%>)

Same as (current-jni-env), but raises an error if the value is #f.

procedure

(with-jni-env env thunk)  any

  env : (is-a?/c JNIEnv<%>)
  thunk : (-> any)
Evaluates (thunk) with (current-jni-env) set to return env during the dynamic scope of thunk.

syntax

(let-jni-env env body ...+)

 
  env : (is-a?/c JNIEnv<%>)
Convenience wrapper for with-jni-env.

procedure

(with-jni-scope thunk)  any

  thunk : (-> any)
Wraps a JNI scope: local references associated with the scope will be marked as invalid when thunk exits.

procedure

(with-jni-frame thunk)  (or/c (is-a? reference<%>) #f)

  thunk : (-> (or/c (is-a? reference<%>) #f))
Creates a new local frame. You can return a local reference from thunk, which will be converted to a local reference in the parent frame.

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🔗ℹ

JNI primitive types.

JNI reference types.

These are defined as tagged pointer types using define-cpointer-type, so predicate functions and /null variants are also available.

JNI array types.

These all derive from _jarray.

value

_jweak : ctype?

JNI weak reference, derived from _jobject.

Enumeration of 'JNIInvalidRefType, 'JNILocalRefType, 'JNIGlobalRefType and 'JNIWeakGlobalRefType.

Field and method IDs, represented as tagged pointers.

value

_jvalue : ctype?

A union type.

A type for modified UTF-8 strings, similar to _string/utf-8.

JNI version constants.

Modes for releasing array contents.

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)
Interprets bstr as modified UTF-8 and returns the resulting string.

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)
Produces the modified UTF-8 encoding of str.

5.3 JNI Library🔗ℹ

class

JNI% : class?

  superclass: object%

A class for initializing the JNI library and instantiating a Java VM. See the JNI documentation for details.

method

(send a-JNI GetDefaultJavaVMInitArgs version)  JavaVMInitArgs?

  version : exact-integer?

method

(send a-JNI GetCreatedJavaVMs)  (listof (is-a?/c JavaVM<%>))

method

(send a-JNI CreateJavaVM args)

  
(is-a?/c JavaVM<%>) (is-a?/c JNIEnv<%>)
  args : JavaVMInitArgs?

A structure used when initializing the JNI.
(define-cstruct _JavaVMInitArgs
  ([version _jint]
   [nOptions _jint]
   [options _JavaVMOption-pointer/null]
   [ignoreUnrecognized _jboolean]))

A structure used when initializing the JNI.
(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
A utility function to get hold of a JNIEnv<%>, creating a Java VM if necessary. This is likely be removed in the future.

5.4 Invocation interface🔗ℹ

interface

JavaVM<%> : interface?

Methods for invoking and attaching to the Java VM, corresponding to the JNI C functions.

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

(send a-JavaVM GetEnv version)  (is-a?/c JNIEnv<%>)

  version : exact-nonnegative-integer?

method

(send a-JavaVM AttachCurrentThreadAsDaemon args)

  (is-a?/c JNIEnv<%>)
  args : (or/c #f JavaVMAttachArgs?)

value

_JavaVM : ctype?

A type equivalent to JavaVM * in C. Produces an instance of JavaVM<%> when converted to a Racket value.

A structure used when attaching threads to the VM. Defined as:
(define-cstruct _JavaVMAttachArgs
  ([version _jint]
   [name _string/modified-utf-8]
   [group _jobject/null]))

5.5 Native interface🔗ℹ

interface

JNIEnv<%> : interface?

Methods for interacting with the Java environment, corresponding to the JNI C functions.
There are many functions here, so please refer to the source code and JNI documentation for details.

value

_JNIEnv : ctype?

A type equivalent to JNIEnv * in C. Produces an instance of JNIEnv<%> when converted to a Racket value.

A structure used when registering native methods. Defined as: