This is the technical documentation for the JavaBind feature (version 1.1a).
JavaBind provides a type-safe interface to Java software
in Opal. For this purpose, a so-called binding compiler
(command javabind
) automatically produces Opal
structures which connect to Java via the JNI (Java Native
Interface). The Opal library-package Java
supports the
code produced by the binding compiler and adds some generic
functionality such as catching exceptions and testing for null
objects.
For some examples of using JavaBind, see `/usr/ocs/examples/Java'.
JavaBind maps Java classes to Opal in a systematic way, as described in the following sections. The intention is that it should not be necessary for a JavaBind user to actually look at the generated Opal bindings; the documentation for the Java classes together with knowledge of their representation in Opal should suffice.
For any Java class C
we have an Opal sort
of the same name C
with conversion functions mapping upwards and
downwards along the class hierarchy. The conversion functions correspond to
casting in Java, (C)Obj
, and are
provided for any pair of classes which belong to the same path in the
inheritance tree. As in Java, casting downwards along the hierarchy is
a partial operation whose definedness can be tested with the function
C?
, which corresponds to the instanceof
primitive.
Consider the following Java classes (to illustrate the principle, the
inheritance from Object
is made explicit):
class A extends Object {...} class B extends A {...}
These classes and their inheritance relations are represented in Opal by the following sorts, conversions and tests:
SORT A FUN Object : A -> Object FUN A : Object -> A FUN A? : Object -> bool SORT B FUN A : B -> A FUN Object : B -> Object FUN B : Object -> B FUN B : A -> B FUN B? : Object -> bool FUN B? : A -> bool
Public fields of objects are represented by commands which allow their value to be retrieved and defined. Consider the class definition
public class A {public B field; ... }
field
is represented in Opal as follows:
FUN field : A -> com[B] FUN :=_field : A ** B -> com[void]
Public methods of objects are represented by commands. Consider the class definition
public class A {public B method(B1 a1, ..., Bn an) { ... } }
method
is represented in Opal as follows:
FUN method : A ** B1 ** ... ** Bn -> com[B]
Note that the method overloading of Java is perfectly matched by Opal's function overloading.
Inheritance of fields and methods of superclasses is modelled by repeating their declarations in the subclass. Consider:
public class A { public void method1 () { ... } public void method2 () { ... } } public class B extends A { public void method2 () { ... } }
This is interfaced by the Opal declarations:
FUN method1 : A -> com[void] FUN method2 : A -> com[void] FUN method1 : B -> com[void] FUN method2 : B -> com[void]
Let ObjB
be an object of type B
. Then
method1(ObjB)
invokes the method as it is defined in the superclass
A
, and method2(ObjB)
the overwritten version of
the method in B
.
Dynamic dispatching is dealt with much as in Java. Having an
object ObjA
which is known in the context to be an instance
of class A
but actually has class B
and then calling
method2(ObjA)
invokes the overwritten version from class
B
. Henceforth, method2(A(ObjB))
does not
invoke the definition of method2
from class A
, but
from B
.
A corresponding concept for Java's super
primitive, which
allows an overwritten method of a superclass to be invoked, is not
provided. In Java, super
is only allowed inside the same
class definition where the overwriting happens. Such a context is
not present in Opal's view on Java, since classes are always
accessed from the outside.
Static fields and methods are treated in a similar way to normal ones. Consider:
public class A { static public B field; static public B method(B1 a1, ..., Bn an) { ... } }
This is represented in Opal as follows:
FUN field : com[B] FUN method : B1 ** ... ** Bn -> com[B]
Interfaces are treated in a similar way to classes.
Exceptions thrown by Java methods lead to a failure of the
corresponding command execution. The structure JavaCatch
( JavaCatch) provides a command combinator to check for such
failures and also to retrieve the object thrown by the exception:
(... commands ...) catch (\\Throwable. ...)
The primitive Java types, strings and arrays are automatically mapped to corresponding Opal types. Consider:
public class A { static public int[] method(int k, String[] arr[]) { ... } }
This is represented in Opal as:
FUN method : int ** array[array[denotation]] -> array[int]
The mappings are as follows:
boolean
bool'BOOL
byte
byte'Byte
( Byte), which is
just a wrapper around int'Int
(Opal currently has no
direct equivalent to Java's byte type).
short
short'Short
( Short), which is
just a wrapper around int'Int
(Opal currently has no
direct equivalent to Java's short type).
int
int'Int
. Unfortunately, Opal
integers have only 31-bit accuracy, so the highest bit is lost. We
currently prefer this pragmatic treatement instead of the inconvenience
of using a non-standard Opal type for integers.
long
long'Long
( Long), which is just a
wrapper around denotation'DENOTATION
(Opal currently has
no direct equivalent to Java's long type, and there is no type
which can simulate 64-bit integers - thus they are represented in their
textual form).
float
sreal'SmallReal
. Unfortunately, in Opal
small reals have only 31-bit accuracy, so the highest bit is lost.
double
real'Real
.
String
denotation'DENOTATION
. Null strings are
represented by empty denotations; a special test function allows one to
observe whether a denotation has been created from null
( JavaNull).
C[]
array'Array
. Null arrays are
represented by empty arrays; a special test function allows one
to observe whether an array has been created from null ( JavaNullArray).
In Java, arbitrary cyclic dependencies between classes - even across package boundaries - can appear. Opal only allows acyclic import relations. This problem is dealt with by actually generating two structures for each Java class: the first one contains only the sorts and conversions, the second the fields and methods. Consider:
public class A { public B foo () { ... } } puplic class B { public A bar () { ... } }
Schematically, the following structures are generated to interface these classes:
SIGNATURE A_SORT SORT A ... SIGNATURE B_SORT SORT B ... SIGNATURE A IMPORT A_SORT COMPLETELY IMPORT B_SORT ONLY B FUN foo : A -> com[B] SIGNATURE B IMPORT B_SORT COMPLETELY IMPORT A_SORT ONLY A FUN bar : B -> com[A]
In virtually all cases, the user does not need to know about this treatment, since the structure containing the fields and methods also re-exports the sorts and conversions.
javabind
tool
The javabind
tool works on an input file which describes the
bindings to be generated and names the subsystem in which they should
be placed. Here is an examle of a javabind
file:
// name of the subsystem to generate system "JavaLib" // bind a class bind "java.io.BufferedInputStream"
Running javabind
on this file produces the following output:
javabind version 1.1a class java.io.InputStream indirectly referred (requesting only sorts and casts) bounded in java_io_InputStream java_io_InputStream_SORT reused from /home/uebb/opal/2.3a/lib/opal_java class java.io.BufferedInputStream bounded in java_io_BufferedInputStream class java.io.FilterInputStream indirectly referred (requesting only sorts and casts) bounded in java_io_FilterInputStream class java.lang.String indirectly referred (requesting only sorts and casts) bounded in java_lang_String java_lang_String_SORT reused from /home/uebb/opal/2.3a/lib/opal_java interface java.io.Serializable indirectly referred (requesting only sorts and casts) bounded in java_io_Serializable java_io_Serializable_SORT reused from /home/uebb/opal/2.3a/lib/opal_java class java.lang.Class indirectly referred (requesting only sorts and casts) bounded in java_lang_Class java_lang_Class_SORT reused from /home/uebb/opal/2.3a/lib/opal_java java_lang_Class reused from /home/uebb/opal/2.3a/lib/opal_java class java.lang.Object indirectly referred (requesting only sorts and casts) bounded in java_lang_Object java_lang_Object_SORT reused from /home/uebb/opal/2.3a/lib/opal_java java_lang_Object reused from /home/uebb/opal/2.3a/lib/opal_java
As seen from the output, javabind
not only generates bindings
for classes which are directly requested, but also for classes which
are indirectly referenced from the requested ones. These are superclasses
and interfaces of the requested classes, as well as classes referenced
from the member signatures.
In order to minimize the amount of bindings, for indirect referred
classes only the structure containing the sorts and conversions is
generated (such as java_io_InputStream_SORT
). Moreover,
bindings are reused from existing libraries of Java
bindings, supporting the incremental construction of richer binding
sets from existing ones. A default set of bindings is available in the
basic Opal-Java library. This set is constructed by the
javabind
file:
system "JavaDefault bind "java.lang.Object" bind "java.lang.Class" bind "java.lang.Throwable"
Note that these bindings imply a lot of other indirectly referred bindings which are present in the standard library.
In order to base bindings on existing ones other then those of the
standard library, a binding file may contain a special directive
referring to the directory of an OCS subsystem generated by javabind
:
system "JavaExtendedLib" uses "./JavaLib" bind "..."
If an application uses several binding libraries, it is important that they are hierarchically built as sketched above. Otherwise bindings for the same class would be generated twice, leading to multiply defined symbol errors at link time.
A special instruction in a javabind
file allows a group of
structures to be clustered in a single structure which re-exports
them. For example:
system "JavaLib" // bind a lot of stuff from java.io ... // cluster the structures generated for the java.io classes cluster "java.io.*" in "java_io"
Subsystem JavaVM Subsystem JavaCheck
next node: Subsystem JavaVM,
prev node: Tk,
up to node: Subsystem Tools