4. Mapping Ada to Java¶
To allow an Ada package to be used from Java,
ada2java
generates one or more Java classes
(source files that will need to be compiled to bytecodes by
a Java compiler)
based on the content of the visible part of the Ada package spec.
This section explains and illustrates the mapping for each of the
various kinds of entities declared in a package that can be used
from Java.
In brief:
Although there are some exceptions to this rule, in general a type and certain of its associated subprograms declared in an Ada package are mapped to a Java class with methods corresponding to the Ada subprograms. Such entities are said to be attached to the resulting class.
Other entities declared in the Ada package map to static members defined in a ‘default class’ generated by
ada2java
. In particular, variables and constants in the Ada package map to private static fields in the default class and are accessed through ‘’getter’’ (and ‘setter’ for variables) methods. Such entities are said to be unattached.
If the default class is generated, its name is that of the
original Ada package (with the same casing as the identifier in the
package declaration) suffixed with _Package
.
In the examples, only the portions of the Java classes needed by users of the classes are shown.
4.1. Types¶
Types used in the Ada package map to Java types in the generated class(es). This section explains the correspondence. As a general rule, note that while most forms of type declarations have a correspondence in Java, subtype declarations are ignored, as there is no equivalent to subtypes in Java. However, subtype constraints imposed on Ada entities, such as variables or formal parameters, must be respected when referenced from Java, and can result in exceptions when constraints are violated.
4.1.1. Scalar Types¶
The following table shows how Ada scalar types are mapped to Java primitive types:
Ada type |
Java type |
---|---|
Integer type <= 32 bits |
|
Integer type > 32 bits |
|
|
|
|
|
Other enumeration type |
|
Fixed-point type |
|
Floating-point type |
|
Constraint checks generated in the Ada glue code detect errors that may
result from the range mismatches between Ada and Java.
For example, since a 16-bit Ada integer will be mapped to 32-bit int
in Java, the Java code might attempt to pass an out-of-range value to Ada.
This will raise a Constraint_Error
exception in Ada, which will be
propagated back to Java as an AdaException
exception.
For an enumeration type, a Java final class is created, with the same name as the enumeration type. This class defines the possible values for the enumeration.
Example:
package Pckg is
type Enum is (A, B, C);
end Pckg;
will give:
package Pckg;
public final class Enum {
public static final int A = 0;
public static final int B = 1;
public static final int C = 2;
}
Representation clauses for enumeration types are not currently supported.
A discussion of subprogram formal parameters of scalar types may be found in Subprogram parameters.
4.1.2. Arrays¶
Mapping Ada arrays to Java arrays would be very expensive, since it would imply a copy of the whole array each time a parameter has to be passed. Thus for efficiency an Ada array type is mapped to a dedicated ‘proxy’ class with methods that serve as accessors to attributes and components. For example:
package Ex1 is
type T1 is array(Integer range <>) of Float;
end Ex1;
will yield the following class:
public final class T1 extends com.adacore.ajis.internal.ada.AdaProxy {
...
public T1 (int First_1, int Last_1){...}
final public double Get_Element_At (int Index_1){...}
final public void Set_Element_At (int Index_1, double Value){...}
final public int First (){...}
final public int Last (){...}
final public int Length (){...}
}
A subprogram that takes a parameter of the Ada array type is mapped to a method taking a parameter of the corresponding Java ‘proxy’ class; note that this method is located in the default class, and not in the proxy class.
4.1.3. Strings¶
Directly passing String
data between Ada and Java would require
expensive copying, and thus an alternative approach is used.
The Ada type String
is mapped to the Java class AdaString
,
which encapsulates the accesses.
More specifically,
an Ada parameter of type String
of any mode, and an Ada
access String
parameter, are both mapped to a Java parameter of type
AdaString
.
For efficiency, an AdaString
object caches both its Ada and Java
string values after they have been computed.
As an example, if the Ada spec is:
package Pckg is
procedure P (V : String);
end Pckg;
then the generated Java will be:
public final class Pckg_Package {
public static void P (V : AdaString) {...}
}
If we now write:
AdaString str = new AdaString ("A string from Java");
Pckg_Package.P (str);
Pckg_Package.P (str);
Only the first call will require the expensive string translation from Java to Ada. The second invocation will directly use the cached value.
Please note that Java strings are UTF16-encoded, whereas the corresponding Ada strings will be UTF8-encoded. This may have significant impact when computing character offset on Java strings.
4.1.4. Simple Record Types¶
Simple (that is, not tagged) record types are mapped to Java final classes.
Components are accessed through a set of generated accessors
(‘getter’ / ‘setter’ methods). As a current limitation, ada2java
does not yet support accessing discriminant components.
Example:
package Pckg is
type R is
record
F1 : Integer;
F2 : Float;
end record;
end Pckg;
will give:
package Pckg;
public final class R {
public R () {...}
public final int F1 () {...}
public final void F1 (int Value) {...}
public final double F2 () {...}
public final void F2 (double Value) {.../}
}
A component that has an access-to-record type is treated as though it were of the record type itself. For example:
package Pckg is
type R is
record
F1 : Integer;
F2 : Float;
end record;
type S is
record
G1 : R;
G2 : access R;
end record;
end Pckg;
will result in both a class R
as above, and the
following class S
:
package Pckg;
public final class S {
public S () {...}
public R G1 () {...}
public void G1 (R Value) {...}
public R G2 () {...}
public void G2 (R Value) {...}
}
Only one level of indirection is implemented;
ada2java
does not support access to access-to-record.
A private (untagged) type is treated like a record type, except that
it does not have any component-accessing methods. (A later release of
ada2java
will generate methods for accessing discriminants
if the type has any.)
4.1.5. Tagged Types¶
A tagged type is mapped to a Java class of the same name. If the Ada type is abstract, then the Java type will be abstract as well.
4.1.5.1. General principles¶
A primitive (i.e., dispatching) subprogram of a tagged type is mapped to
a corresponding Java instance method.
A current restriction is that the first parameter of the Ada subprogram
must be a controlling parameter; otherwise the subprogram is mapped to a
method in the default class.
(Thus a function that delivers a value of the tagged type, but has no
controlling parameter, is mapped to a method in the default class, and
not to a method in the class corresponding to the Ada type.)
The first Ada parameter is mapped to the Java method’s implicit this
parameter.
A subprogram with a class-wide parameter is mapped to a method of the tagged type’s Java class whose corresponding parameter has the Java class type. However, as this is not properly a dispatching primitive of the Ada type, it is declared as a final method.
As an example:
package Ex1 is
type T is tagged null record;
procedure P1 (X : in out T; F : Float);
procedure P2 (X : T'Class);
procedure Q1 (I : Integer; X : T);
procedure Q2 (I : Integer; X : T'Class);
function F return T;
end Ex1;
is mapped to:
public final class Ex1_Package {
...
static public void Q1 (int I, Ex1.T X){...}
static public void Q2 (int I, Ex1.T X){...}
static public T F (){...}
} // Ex1_Package
public class T extends com.adacore.ajis.internal.ada.AdaProxy {
...
public void P1 (double F){...}
final public void P2 (){...}
} // T
4.1.5.2. Ada type hierarchies¶
Hierarchies of Ada types are preserved in the generated Java classes. Therefore, the following structure:
type R is tagged record;
type R_Child is new R with null record;
will result in:
public class R {...}
public class R_Child extends R {...}
Consistency of Java types is guaranteed at run time. For example, the following function:
package Pckg is
function F return R'Class;
end Pckg;
will result in:
public final class Pckg_Package {
public R F () {...}
}
However, if the actual type of the returned object is R_Child
, then
the value returned by the Java function will be of the Java type
corresponding to R_Child
.
4.1.5.3. Java class hierarchies¶
It is possible to extend a Java class that was generated by ada2java
from an Ada tagged type.
For example:
package Rec_Pckg
type Rec is tagged null record;
procedure P (R : Rec);
end Rec_Pckg;
results in a Java class Rec
with an instance method P
:
class Rec extends com.adacore.ajis.internal.ada.AdaProxy {
...
public void P(){...}
}
You can then write:
class Rec_Child extends Rec {
public void P () {
System.out.println ("Hello from Java");
}
}
...
Rec ref = new Rec_Child();
ref.P(); // Displays "Hello from Java"
4.2. Global Variables and Constants¶
A package containing global variables (that is, variables declared in the package spec’s visible part) is mapped to a default class containing ‘getter’ and ‘setter’ methods for accessing and updating the variables. Global constants are treated analogously, but they have only a ‘getter’ method. Variables of limited types also only have a ‘getter’ method. Note that Ada named numbers, which are really just values intended for use in static compile-time computations, are not mapped to Java.
For example:
package Globals is
V : Integer;
C : constant Integer := 100;
N : constant := 3.14159;
end Globals;
will result in the default class:
public class Globals_Package {
static public int V (){...}
static public void V (int Value){...}
static public int C (){...}
}
4.3. Subprograms¶
Ada procedures and functions are mapped to Java methods. Nondispatching
subprograms are marked as final
. Dispatching subprograms are
discussed in Tagged Types.
4.3.1. Method placement¶
In general, nondispatching subprograms are mapped to methods defined in the default class.
For example, if the input package spec is:
package Pkg is
function F return Integer;
end Pkg;
then ada2java
will generate the following default class:
public class Pkg_Package{
...
public static int F(){...}
}
However, there are cases where the subprogram can be attached to the class
of its first parameter. Attachment can be enabled / disabled depending on
user requirement. In this case, the explicit initial Ada parameter is mapped to
the implicit this
parameter in Java. See Managing Attachment to Java Proxies for further details.
4.3.2. Subprogram parameters¶
The following rules and restrictions apply to the types of subprogram formal parameters:
Scalar types
Access-to-scalar types are not supported.
A scalar type with mode
in
is mapped to the corresponding Java type. For example:procedure P (V : Integer);
will result in:
public void P (int V) {...}
A formal scalar with mode
out
orin out
will be mapped to a corresponding ‘wrapper’ class:BooleanRef
,CharacterRef
,DoubleRef
,IntegerRef
, andLongRef
respectively encapsulating the primitive typeboolean
,character
,double
,int
andlong
).Each of these classes defines
setValue
andgetValue
methods for accessing the encapsulated value.The Java application needs to construct an object of the relevant wrapper class and pass it to the method that corresponds to the Ada subprogram. After the return from the method, the Java application can invoke the
getValue
method to retrieve the new value of the actual parameter.
Record and private types
An Ada
in
,in out
, orout
formal parameter of a record or private type (either tagged or untagged), is mapped to a Java formal parameter of the class corresponding to the Ada type. Similarly, an Ada formal parameter of an access-to-record-type or access-to-private-type (either anonymous or named) is mapped to a Java formal parameter of the class corresponding to the Ada type.Example:
package Example is type R is null record; type Access_R is access all R; procedure P(V1 : R; V2 : out R; V3 : in out R; V4 : access R; V5 : Access_R); end Example;
The resulting Java class is:
public final class Example_Pckg { ... public void P (R V1, R V2, R V3, R V4, R V5){...} }
An Ada
out
orin out
parameter of an access-to-record (or access-to-private) type is mapped to a nested class. For example:package Example is type R is null record; type Access_R is access all R; procedure P(V : out Access_R); end Example;
will generate the default class and a class for
R
public class Example_Package { ... static public void P (R.Ref V){...} } public class R extends com.adacore.ajis.internal.ada.AdaProxy { ... public static class Ref implements com.adacore.ajis.IProxyRef { public void setValue (Object r) {...} public Object getValue () {...} } }
The Java application needs to construct an object of the class
R.Ref
and pass it toP
. On return, thegetValue
method may be called to retrieve the value in theout
parameter returned by the Ada procedure.Further indirection, such as an access-to-access type for a formal parameter, is not supported.
4.3.3. Overloaded operators¶
Java doesn’t allow operators overloading. When operators are overloaded in
Ada, the corresponding Java name is set by the binding generator to
OP_<operator_name>
. For example:
type Complex is record ...
function "+" (Left, Right : T) return T;
will generate on the Java side:
public class Complex {
public Complex OP_PLUS (Complex Right) {
...
}
}
Here’s a list of the equivalence between Ada operators and Java names:
Ada operator |
Java name |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4.4. Subprogram Access Types¶
Accesses to subprograms - sometimes referred to as callbacks - can’t be directly bound to Java. It is not possible to give a reference to a Java function in a type-safe fashion. ada2java generates an abstract class with an abstract member of the correct profile for each access type to be bound, the implementation of its abstract primitive being the implementation of the subprogram access. For example:
type P_Acc is access all procedure (V : Integer);
procedure Call_P_Acc (Proc : P_Acc);
pragma Annotate (AJIS, Assume_Escaped, False, Call_P_Acc, "Proc");
will be bound into:
abstract public class P_Acc {
abstract public P_Acc_Proc (int V);
}
void Call_P_Acc (P_Acc Proc);
and can be used in, for example, the following scenario:
Proc p = new Proc () {
public P_Acc_Proc (int V) {
System.out.println ("CALLED WITH " + V);
}
};
Pckg_Package.Call_P_Acc (p);
Note the use of the pragma Annotate
on the Call_P_Acc
method.
The Java implementations of bound subprogram access types are not actually
accesses to subprograms, but instances of Java objects. It’s not possible to
store such an object on the Ada side afterwards, since the complete information
can’t be kept in the access type. Therefore, the programmer must ensure that
no escape of the value is done, and take responsiblity for that by declaring
the parameter as being not escaped. Further details on escapement can be found
in Restrictions on Proxy-Owned Objects Passed to Subprograms.
4.5. Exceptions¶
Exceptions are bound into classes derived from
com.adacore.ajis.NativeException
It is then possible to throw or
handle them directly in Java code.
Example:
package Example is
An_Exception : exception;
procedure Raise_An_Exception;
end Example;
package body Example is
procedure Raise_An_Exception is
begin
raise An_Exception;
end Raise_An_Exception;
end Example;
The resulting Java class is:
public final class An_Exception extends com.adacore.ajis.NativeException {
...
}
And can be used in e.g.:
try {
Example_Package.Raise_An_Exception ();
} catch (An_Exception e) {
// process the exception
}
4.6. Renamings¶
Renamings of objects and subprograms are supported by ada2java
.
Object renamings are mapped in the same way as global objects, by means
of ‘setter’ and ‘getter’ methods in the default class for the
containing package. A subprogram renaming is represented by a method
with the name of the renaming that invokes the renamed subprogram,
declared in the appropriate class. In other words, the same rules
that apply to other subprograms apply to subprogram renamings.
4.7. Generics¶
Generic packages and subprogram can’t be directly bound to Java. However, packages and subprograms instances and bound like regular packages and subprograms.
4.8. Predefined Environment¶
In order to access descendants of Ada
or GNAT
from Java,
you need to manually invoke ada2java
on the Ada source files
from the GNAT installation directories, to generate the corresponding
Java binding classes. This step will be automated in a future release of
GNAT-AJIS.
4.9. Current Limitations¶
The following features are not supported:
Discriminants. Discriminants are not accessible from the Java class generated for a discriminated type.
Anonymous arrays. Objects with an anonymous array type are not supported, but array type declarations which declare a constrained first subtype are supported.
Interfaces. No mapping is currently provided from Ada interface types to Java interfaces.
Tasking features. Tasks and protected objects/types are ignored.