|
Author: Quentin Ochem, AdaCore
Abstract: Ada Gem #58 — Ada and Java are two languages that rely heavily on exceptions. A large part of the Ada data model is based on the fact that data is checked at run time, and will raise various kinds of exceptions such as Constraint_Error when constraints are violated. Similarly, there are many cases where Java performs checks that can raise exceptions, among the most common being checks on casts and null dereferences. It is therefore extremely important to support exceptions that are properly propagated from one language to the other and even potentially caught/handled, without having to worry about the language of origin.
Let’s consider the following API:
with AJIS.Annotations; use AJIS.Annotations;
package API is
function Compute (I : Integer) return Integer;
type An_Operation is access function (I : Integer) return Integer;
function Indirect_Compute (Data : Integer; Operation : An_Operation)
return Integer;
pragma Annotate (AJIS, Assume_Escaped, False, Indirect_Compute, “Operation”);
end API;
with the following implementation:
package body API is
function Compute (I : Integer) return Integer is
Tmp : Natural := I;
begin
return Tmp + 1;
end Compute;
procedure Indirect_Compute (Data : Integer; Operation : An_Operation) is
begin
return Operation.all (Data);
exception
when Constraint_Error =>
return 0;
end Indirect_Compute;
end API;
The Compute subprogram uses a temporary variable of subtype Natural, and will raise a Constraint_Error if a negative value is passed for I. On the other hand, the Indirect_Compute subprogram catches all Constraint_Errors, and returns 0 if one occurs.
Generating the binding code
As with the previous Ada/Java interfacing Gems, the generation of the binding is straightforward:
ada2java api.ads –b test –o ada –c java –L my_lib –P api.gprNote that you need an api.gpr project file, defined as follows:
with “ajis”; project API is end API;
Writing the Java code
Let’s now play a bit with this code. As you can see, Java classes are generated for the Ada exceptions, such as test.Standard.Constraint_Error. These exceptions will end up as regular Java exceptions, which we can catch, for example, in the following code:
import test.API.API_Package;
import test.Standard.Constraint_Error;
public class My_Main {
public static void main (String [] argv) {
try {
int x = API_Package.Compute (-1);
} catch (Constraint_Error e) {
System.out.println (e.getMessage ());
}
}
}
And just like that, we have an exception raised from Ada, transformed into a Java exception, and finally caught in a Java block. But let’s do something a little more ambitious. Let’s raise an exception from Ada, have this exception pass through the Java frames, and then end up back in Ada:
import test.API.API_Package;
import test.API.An_Operation;
import test.Standard.Constraint_Error;
public class My_Main {
public static void main (String [] argv) {
int x = API_Package.Indirect_Compute (-1,
new An_Operation () {
public int An_Operation_Body (int I) {
API_Package.Compute (I);
}
}
);
}
}
So, here we have a callback called from Ada, implemented in Java, that calls the Ada Compute procedure. Let’s see what happens in detail. First, Indirect_Compute is called by Java, goes into Ada, which then calls the subprogram passed as an access parameter, resulting in a call to Java. This Java function then calls the Ada Compute operation, which will raise an exception due to passing -1. So at the point of the exception, the call stack looks like this:
[1] Java : main [2] Ada : Indirect_Compute [3] Java : An_Operation_Body [4] Ada : Compute <- the exception is raised here
The exception first goes from [4] to [3], as in the previous example. In this case however, it is not caught, so it is propagated to the caller, [2], which happens to be Ada code again. Fortunately, the glue code generated between [2] and [3] is able to translate the exception back from Java to the original Ada exception, and continue the propagation. In this case, there’s an exception handler in [2], which handles Constraint_Error, and will catch the one initially raised at [4].
Compiling and running
Just as in the other Ada/Java interfacing Gems, the compile and run commands on Linux break down as follows:
gprbuild –p –P ada/my_lib.gpr LD_LIBRARY_PATH=`pwd`/ada/lib/:$LD_LIBRARY_PATH export LD_LIBRARY_PATH CLASSPATH=`pwd`:`pwd`/java:<your AJIS installation>/lib/ajis.jar:$CLASSPATH javac My_Main.java java My_Main
Posted
in Ada / Ada 2005, Development Log, Devt log - Gem of the Week
If you have an idea for a Gem you would like to contribute please feel free to contact us at: gems@adacore.com
Hugues Bonnin said:
just a typo : change the “-1″ actual argument in call of dereferenced “Operation” in “Indirect_Compute” body by “Data”…
Quentin Ochem said:
Fixed – thanks for the remark!