[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This chapter discusses a number of issues that ada2java
users
should be aware of.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
If Ada subprograms from the same package spec produce the same Java profile,
the binding generator will detect the problem and generate only the first
entity. Other entities of similar name will be ignored with a warning.
To prevent this, you can use the Rename
pragma to define
the Java name corresponding to an Ada entity:
pragma Annotate (AJIS, Rename, identifier, static_string_expression); |
The identifier argument denotes the Ada entity.
The static_string_expression is the name that will be used for the
corresponding Java entity generated by ada2java
.
Example:
package Example is type I1 is new Integer; type I2 is new Integer; function F return I1; pragma Annotate (AJIS, Rename, F, "F_I1"); function F return I2; pragma Annotate (AJIS, Rename, F, "F_I2"); end Example; |
This will result in two Java functions:
int F_I1 (); int F_I2 (); |
Adding a pragma to an Ada package specification is not always practical, and indeed may be impossible if the specification is from an external library. With GNAT-AJIS, you can provide the pragma in a separate Ada file, applying it to an Ada entity that is itself a renaming declaration.
This entity must be marked by another Annotate
pragma:
pragma Annotate (AJIS, Annotation_Renaming, identifier). |
where identifier denotes an entity defined by an Ada renaming declaration.
The Annotation_Renaming
pragma applies to all AJIS pragmas that are
specified for identifier.
package Example is type I1 is new Integer; type I2 is new Integer; function F return I1; function F return I2; end Example; with Example; package Renamings is function F return Example.I1 renames Example.F; pragma Annotate (AJIS, Annotation_Renaming, F); pragma Annotate (AJIS, Rename, F, "F_I1"); function F return Example.I2 renames Example.F; pragma Annotate (AJIS, Annotation_Renaming, F); pragma Annotate (AJIS, Rename, F, "F_I2"); end Renamings; |
Entities annotated with pragma Annotate(AJIS, Annotation_Renaming)
will not be mapped to Java entities;
they are assumed to be used only to define annotations.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
An object on the Ada side -- a so-called native object -- is accessed in Java through a proxy object: an instance of a class (the proxy class) generated from the Ada object's type. The proxy object contains a reference to the native object. Invoking a member function on the proxy object results in a call of a native subprogram on the native object. The native object may be either allocated or declared.
This section explains the implications of this model on the usage of the generated binding.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
If an Ada subprogram's formal parameter is not an access parameter
(i.e., it has in
, in out
or out
mode), then invoking
the corresponding Java method requires a non-null reference
to a proxy object. For example:
package P is type T is null record; procedure Proc (V : out T); end P; |
results in the following Java classes:
public class T { ... } public final class P_Package { public static Proc (T V) { ... } ... } |
You need to ensure that the V
parameter passed to Proc
is
not null
. Hence, the following will throw an exception, since
v
is implicitly initialized to null
:
T v; P_Package.Proc (v); |
A simple way to provide a non-null reference is to initialize it to an allocated object:
T v = new T (); P_Package.Proc (v); |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
If A
is an Ada type that is mapped by ada2java
to a
Java class J
, then the execution of a Java constructor J()
will create two objects:
J
,
allocated on the Java heap and subject to Garbage Collection by the JVM, and
A
, allocated on the Ada heap, referenced from the
proxy object.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Under certain circumstances, the generated Java code may construct extra native objects on the Ada heap. To help explain this, here is an example where such allocation is not needed, namely, a function returning an access value:
package P is type T is record ... end record; type T_Acc is access all T; function Create_T_Acc return T_Acc; end P; |
The generated code will look like:
public class T { ...} public final class P_Package { final public T Create_T_Acc () { ...} ... } |
On the Java side, the method Create_T_Acc
returns a
proxy object that contains the value of the pointer returned by
the call of the Ada function. So the user can write:
T v = P_Package.Create_T_Acc (); |
and then access the data of v
. Note that all the standard precautions
that apply in using an Ada pointer have to be taken in
this case as well. In particular, after the object has been freed on the
Ada side, there
should be no further references to it from Java. The
Ada programmer needs to document how the returned value should or should not
be used, and the Java programmer needs to adhere to these guidelines.
However, a function returning a record rather than an access value raises additional issues:
package P is type T is record ... end record; function Create_T return T; end P; |
results in a default class similar to the one in the previous example:
public final class P_Package { final public T Create_T () { ... } ... } |
The Java programmer can write:
T v = P_Package.Create_T (); |
But in this case, the value returned by the function is not a pointer.
A new Ada native object is automatically allocated on the heap, initialized
to a copy of the value returned by the Ada function,
and is referenced through the proxy object constructed by the Java method
Create_T
.
This is similar to the Ada situation where calling Create_T_Acc
would
not involve a copy of a T
object, whereas Create_T
would.
Because of implementation constraints there may be more than one
copy involved in the call of Create_T
(but only one native object
will be created on the heap). This will be discussed further in
4.5 Clone and Copy Semantics.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
As illustrated above, native objects may be created automatically by Java methods corresponding to Ada subprograms. This raises the issue of when/how such objects are to be deallocated.
In general, the ada2java
approach is based on the following
principles:
A native object created from Java is said to be owned by its proxy object. It has been created by the Java program, and it must be freed by the Java environment.
Such an object is tightly linked to its proxy -- when the proxy object doesn't
exist (i.e. is garbage collected), the native object becomes
inaccessible.(2)
The native object deallocation
will occur automatically, when the Java proxy is garbage collected (more
specifically through the implementation of the finalize
method).
A native object not owned by a proxy -- for example, one that has been obtained from the Ada API through an Ada pointer -- will not be deleted automatically.
The ownership state of a native object may be queried through the
getOwner
method.
This function returns a value from the enumeration
com.adacore.ajis.IProxy.Owner
, either NATIVE
or PROXY
,
specifying who is responsible for managing the memory. For example after these
assignments:
T v1 = new T (); T v2 = P_Package.Create_T_Acc (); T v3 = P_Package.Create_T (); |
the following relationships hold:
v1.getOwner () | PROXY |
v2.getOwner () | NATIVE |
v3.getOwner () | PROXY |
Native objects referenced by v1
and
v2
will be deallocated when the corresponding Java proxy is
garbage-collected.
You may change the owning attribute of a referenced native object,
through the setOwner
method of proxy classes.
This should
be used very carefully, as it may generate memory leaks or corruption. Doing
the following:
T v1 = new T (); v1.setOwner (Owner.NATIVE); |
will deactivate the object deallocation on finalization. The Java programmer becomes responsible for explicitly deallocating the native object.
Note that, while moving the owning from PROXY to NATIVE is a relatively safe operation, raising the possibility of memory leaks but not object corruption, moving in the opposite direction should be done with great care. A object managed by the NATIVE side may be deallocated by the Ada application at any moment, or be declared as the field of another object or even on the native stack in situations such as callbacks. If it's not clear where the object is coming from, cloning it (resulting automatically into an object managed by the proxy) is often the best solution.
As will be described below, only objects that are known to be managed though pointers can have their ownership changed. An object obtained, for example, from a global variable or field will need to retain its known ownership.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The generated code keeps track of how native objects have been obtained, and restricts their operations accordingly. Three possible allocators are recognized:
DYNAMIC
STATIC
NATIVE
and cannot be changed.
UNKNOWN
IProxy.getAllocator ()
function.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Passing a (reference to a) native object as an actual parameter to an Ada
subprogram whose corresponding formal parameter is either an access parameter
or of an access type
could in some cases result in a dangling reference to the native object,
whereas in other situations it might be harmless.
In order to provide the desired safety while allowing the needed generality,
ada2java
's approach is similar to the way that the Ada standard
supplies both the 'Access
and 'Unchecked_Access
attributes
for composing pointers to data objects.
In summary, when the Java side is responsible for pointer memory management
(i.e. the object is owned or it is accessed through 'Access
or
'Unrestricted_Access
), then
an exception will be thrown in Java on an attempt
to pass a reference to a native object as an actual parameter when the formal
is of a named or anonymous access type.
However, this check can be suppressed either locally (for a given formal
parameter) or globally (for all calls).
Here is a summary of the troublesome scenario:
To prevent this, an exception is thrown at step 1 above.
Here is an example:
-- Original Ada package package P is type T is record A, B : Integer; end record; type A_T is access all T; G : A_T; procedure Set_G (V : A_T); end P; package body P is procedure Set_G (V : A_T) is begin G := V; end Set_G; end P; |
// Java statements T v = new T (); P_Package.Set_G (v); // Throws an exception |
If the invocation of Set_G
did not throw an exception,
G
would become a dangling pointer when the proxy object
referenced by v is garbage collected.
However, if the invocation of Set_G
did not cause an assignment of its
formal parameter V
to a global variable, then passing v
as an
actual parameter would be harmless.
ada2java
allows the Ada API to enable such uses, and thus to
deactivate the check:
pragma Annotate (AJIS, Assume_Escaped, Condition, Subprog, Formal_Param) |
where:
True
or False
String
literal
When Condition is False
, the Ada subprogram is responsible for
ensuring that the formal parameter is not copied to a global variable, or used
to set a field of a structure that will be used outside of the scope of the
subprogram. When Condition is True
, an exception is thrown when a
proxy object is passed as an actual parameter to the Formal_Param
parameter of Subprog.
Here is an example:
package P is type T is record A, B : Integer; end record; type A_T is access all T; G : A_T; procedure Safe_Set_G (V : A_T); pragma Annotate (AJIS, Assume_Escaped, False, Safe_Set_G, "V"); end P; package body P is procedure Safe_Set_G (V : A_T) is begin G := new T'(V.all); end Safe_Set_G; end P; |
// Java statements T v = new T (); P_Package.Safe_Set_G (v); // OK |
The invocation Safe_Set_G (v)
is safe since the Ada subprogram does not
let the formal parameter "escape".
The "escaped" checks can be globally activated through the ada2java
switch
`--assume-escaped', and globally deactivated through the switch
`--no-assume-escaped'.
The default is `--assume-escaped'. An explicit pragma overrides
the global configuration switch.
Note that canceling escape checks should be done with great care, as there
is no way to ensure that no escaping has occurred. In many situations,
the programmer's intent is to create an object on the Java
side and then store the object on the native side. In such a case, canceling
proxy ownership through the IProxy.setOwner
method will have the
desired effect, for example:
// Java statements T v = new T (); v.setOwner (Owner.NATIVE); P_Package.Set_G (v); // OK |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
By default, the generated Java code is thread-safe, with locking logic that prevents multiple Java threads from accessing native methods at the same time. In summary, a Java method in a proxy object acquires a lock (a binary semaphore) before invoking the corresponding native method, and releases the lock when the native method returns (either normally or abnormally). The semaphore is global versus per Ada package; e.g. different Java threads are not allowed to invoke native methods simultaneously even if the native methods correspond to subprograms from different packages.
Even if the Java application does not explicitly create any threads, there are still two threads -- the main (user) thread and the garbage collector; thus locking is needed in this case also.
The default locking behavior is not always appropriate, however. In particular, if the native code is using tasking, with mutual exclusion enforced on the Ada side, then there is no need for locking in the Java code (and indeed such locking could have undesirable consequences including deadlock).
User control over the generation of locking code is obtained through the following pragma:
pragma Annotate (AJIS, Locking, Subprogram, LockControl); |
where Subprogram is the subprogram name, and
LockControl is one of Disable
, Check
and Protect
.
Protect
lock
... unlock
region.
If a thread t1
invokes Subprogram while some other thread t2
holds the lock,
then t1
will be queued until the lock is released.
Thus simultaneous calls of native methods are permitted but will entail
queuing.
Disable
LockControl
is Protect
.
Check
lock
... unlock
region.
An exception is thrown if a thread invokes Subprogram without holding
the lock.
The following examples illustrate the multithreading behavior and the effects of the LockControl argument to the pragma.
package P is procedure P1 (I : Integer); pragma Annotate (AJIS, Locking, P, Disable); procedure P2 (I : Integer); pragma Annotate (AJIS, Locking, P, Disable); end P; |
Concurrent invocations of P1
and P2
are allowed (i.e.,
locking code is not generated automatically).
Code protected by the lock can be provided by hand by the
Java developer. The lock is created in the library class generated for the
binding, base_package.Ada2Java.Library
, under the identifier
lock
. Thus:
import base.P_Package; import base.Ada2Java.Library; public class Main { public static void main (String [] args) { Library.lock.lock (); try { P_Package.P1 (0); P_Package.P2 (0); } finally { Library.lock.unlock (); } } } |
Following standard Java coding style,
the lock
/ unlock
logic should always appear in a try
...
finally
block,
in order to release the lock even if an unexpected exception is propagated.
In certain cases, locking is required but the logic is more complex than
simply protecting each native method invocation with a semaphore.
For example, it may be necessary to invoke a sequence of native methods as
an atomic action.
This effect can be achieved through the Check
setting for
LockControl:
package P is procedure P1 (I : Integer); pragma Annotate (AJIS, Locking, P1, Check); procedure P2 (I : Integer); pragma Annotate (AJIS, Locking, P2, Check); end P; |
The Java program has to acquire the library lock before attempting any native
call. Invoking P1
or P2
outside of a section protected by the
lock will throw a Java exception.
The locking behavior can be changed globally through the ada2java
`--[no-]locking' switch. More specifically,
here are the permitted values for this switch:
--locking-protect
Protect
for all
subprograms.
--locking-check
Check
for all subprograms.
--no-locking
Disable
for all subprograms).
Note that an AJIS Locking
pragma takes precedence over the global
switch.
The finalize
method invoked during garbage collection does not
correspond to a subprogram from the Ada package that is input to
ada2java
, and it is not affected by the `--[no-]locking' switch.
Instead, the locking logic used for the finalize
call of Java
proxies during garbage collection is determined by the
`--[no-]locking-finalize' switch:
--locking-finalize-protect
Protect
for finalize
.
--locking-finalize-check
Check
for finalize
.
--no-locking-finalize
Disable
for finalize
)
A typical usage of these two switches would be to set Check
as the
LockControl for all subprograms, and Protect
as the
LockControl for finalize
methods:
ada2java --locking-check --locking-finalize-protect p.ads |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Proxy classes are generated with an equals
implementation that calls the
corresponding Ada "="
operation. For example:
package P is type T is record F : Integer; end record; end P; |
The proxy class can be used as follows:
T v1 = new T (); T v2 = new T (); v1.F (0); "setter" method for field F v2.F (0); "setter" method for field F |
and now the result of v1.equals (v2)
is true
.
This corresponds to "shallow" equality, in contrast with "==
" which
tests pointer identity.
A new proxy is created each time a native function returns a pointer. For example:
package P is type T is null record; type A_T is access all T; G : A_T := new T; function Return_G return A_T; end P; package body P is function Return_G return A_T is begin return G; end Return_G; end P; |
AT p1 = P_Package.Return_G (); AT p2 = P_Package.Return_G (); |
Now p1 == p2
is false
,
since a new proxy is created by each function return,
but p1.equals (p2)
is true
,
The association between a proxy and its native object is lost when the proxy is passed to a native method. For example:
package P is type T is null record; type A_T is access all T; function Return_This (This : A_T) return A_T; end P; package body P is function Return_This (This : A_T) return A_T is begin return This; end Return_G; end P; |
T v = new T (); |
Now the result of (v == P_Package.Return_This (v))
is false
Java reference equality has special semantics in the case of cross-language inheritance, due to the use of a shadow native object -- see 4.6.3.3 Shadow Object Equality for more details.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
A proxy class generated from a non-limited Ada type includes a clone
method. The base class for all proxies, AdaProxy
, implements the
Cloneable
interface, and defines a public method clone
.
The clone
method performs a "shallow copy" of all the fields, except
for the native object reference.
The native object reference in the cloned proxy
is a pointer to a newly allocated Ada object.
This new native object is itself a shallow copy of the original native object.
Thus cloning a proxy does not result in the sharing of the native object by the original proxy and the cloned proxy. The latter points to a new native object. This behavior is needed to avoid a dangling reference (to the native object) when the original proxy is garbage collected.
If the proxy class corresponds to a limited type, then the generated clone method will throw an exception.
Additional semantics for clone and copy, in connection with cross-language inheritance, are covered below (see 4.6.3.4 Shadow Object Cloning).
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This section discusses a number of issues related to cross-language inheritance; i.e., defining a Java class as an extension of a proxy class for an Ada tagged type.
4.6.1 Inheriting from a Java Proxy 4.6.2 Cross Language Dispatching from Ada 4.6.3 The Shadow Native Object 4.6.4 Controlled Types
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
As explained in 3.1.5 Tagged Types, ada2java
maps an Ada tagged type
to a non-final Java proxy class. You can extend this class in Java.
For example:
package P is type T is tagged null record; procedure Prim (V : T); end P; |
results in the following Java class:
public class T { public void Prim () { ... } ... } |
which may be extended, with the instance method overridden:
public class T_Child extends T { public void Prim () { ... } } |
An object of the subclass T_Child
is also a proxy; constructing such an
object allocates a native object, referred to as a "shadow native object".
Its properties will be
described below (4.6.3 The Shadow Native Object).
The Java program can then invoke the Prim
method, with
standard Java dispatching behavior. For example:
T v1 = new T (); T v2 = new T_Child (); v1.Prim (); // Will call the native Prim v2.Prim (); // Will call the overridden Java Prim |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
A method overridden in Java can be called in Ada using the usual Ada dispatching mechanism. For example:
package P is type T is tagged null record; procedure Prim (V : T); procedure Call_Prim (V : T'Class); end P; package body P is procedure Prim (V : T) is begin -- Native Prim implementation; end Prim; procedure Call_Prim (V : T'Class) is begin Prim (V); -- Dispatching call. end Call_Prim; end P; |
The invocation of Prim
in Call_Prim
is dispatching, and
the Prim
for the type of the actual parameter
will be called. In Java, this procedure can be used in conjunction with
a cross-language extension of T, e.g.:
class T_Child extends T { public Prim () { ... } } T v = new T_Child (); P_Package.Call_Prim (v); |
The Java program is invoking the native Ada Call_Prim
procedure, which
in turn dispatches to the Java method Prim
in T_Child
.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This section describes in more detail the semantics of the shadow native object.
4.6.3.1 Basic Properties 4.6.3.2 Memory Management 4.6.3.3 Shadow Object Equality 4.6.3.4 Shadow Object Cloning
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
As seen above, cross-language dispatching is supported; an Ada dispatching call may result in the invocation of a Java method on a proxy object. This is possible because of the shadow native object concept.
For any tagged type T
declared in a package spec that is input to
ada2java
, a new type is automatically generated that extends T
with a component that references a proxy object. The Ada declaration is:
type Shadow_T is new T with record Link_To_Proxy : Java_Object; end record; |
If T_Child
is a Java class that extends T
, then
constructing an instance of T_Child
will create
a native Shadow_T
object instead of a regular T
object.
This type overrides every controlling primitive of T, and delegates the
dispatching to the
Java side. If a method corresponding to an Ada primitive operation is not
overridden in Java, then the subprogram from
the parent Ada type will be automatically called.
The use of a shadow object introduces a tight relationship between a Java proxy and its Ada native object. Basically, there is a roundtrip dependency between the two, so that a Java proxy object is associated with a unique Ada shadow native object and vice versa.
This has several non-trivial implications that are described in the sections below.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The reference from the shadow native object to the Java object is called a global reference.(3) There are two kinds of global references: regular (usually simply referred to as global references) and weak. A regular global reference prevents the object from being garbage collected, whereas a weak global reference does not.
When a native object is owned by its proxy, the proxy is responsible for releasing the native memory. In this case, the reference from the native object to the proxy is a weak global reference: garbage collection will not be prevented, and both the proxy and then the native object will be released upon collection.
However, when the native object is owned by the native side, then the native object may continue to exist even if the proxy has become inaccessible from Java. In such a case, a global reference is used, so that the garbage collector is prevented from collecting the Java object. Such a global reference will be automatically released when the Ada object is actually deallocated.
Forcing the deallocation of the native object through
IProxy.deallocateNativeObject
will release the global reference as well.
Switching the owner of the native object between NATIVE and PROXY will switch the reference from a regular global reference to a weak one.
In order to avoid a potential memory leak, the link between the shadow native object and the Java proxy has to be broken manually when the shadow native object is no longer needed on the Ada side. This may be done in two ways:
unlink
method on the proxy, from Java
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Java reference equality (==
) is not consistent with native pointer
equality, but Java object equality (equals
) is.
That is, if A
and B
are two native pointers where A = B
,
then on the Java side A.equals(B)
is true
but a==b
is
false
.
However, since the association between shadow object and proxy is one-to-one, proxy equality is meaningful. For example:
package P is type T is tagged null record; type T_Access is access all T'Class procedure Identity (Item : T_Access) return T_Access; -- Returns Item as its result end P; |
On return from Identity
, the "glue code" between Java and Ada will
check if the returned value is a shadow native object and, if so, will return
the corresponding Java proxy instead of creating a new one. Hence the
following fragment
public class T_Child extends T { } T v = new T_Child (); |
will result in the expression P_Package.Return_This (v) == v
delivering true
.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Cloning from Java will result in copying a native object. In the case of a shadow native object, there will be a new shadow object as well, referencing the newly created java proxy, thus preserving the one-to-one relationship between the shadow native object and the proxy.
However, there are cases where copies are made from Ada as well:
package P is type T is tagged null record; type T_Access is access all T'Class procedure Duplicate (Item : T_Access) return T_Access; end P; package body P is procedure Duplicate (Item : T_Access) return T_Access is begin First_Copy : T'Class := Item.all; Second_Copy : T_Access := new T'Class'(Item.all); begin return Second_Copy; end Duplicate; end P; |
Calling Duplicate
from Java will lead to two shadow object copies,
one on the stack (First_Copy
) and one on the heap
(Second_Copy.all
). As explained earlier, the
link between the shadow native object and the proxy will be deleted when
the Ada object is deallocated, at the exit of Duplicate
for the
First_Copy
variable.
The Second_Copy
proxy created in the copy process will be returned by
the Java method corresponding to the Duplicate
procedure.
Note that a proxy created by such a copy does not own its Ada native object.
Proxy cloning from Ada does not involve a clone
invocation from the Java
extended object. If the Java code needs to perform a deep copy in the
Java proxy, the method void proxyCloned (IProxy initialObject)
should
be overridden instead. The default clone
implementation of the generated
Java classes will call this method as well.
Limitation: when proxyCloned
is called from Ada, the link
between proxy and native object
is not yet established. Thus proxyCloned
is not allowed to invoke
any native methods.
The Ada type may be derived from Finalization.Controlled
, and
Adjust
may be overridden, to work around this limitation.
Note that multiple copies -- and thus repeated proxy creation -- may be involved when a (non-access) shadow object is returned. For example:
package body P is function Identity (Item : T_Access) return T'Class is begin return Item.all; end Identity; end P; |
Due to internal machinery, three copies may be needed in the implementation of the return from this function when called from Java. If it is necessary to ensure that only one copy is performed, then the Ada function should be written as a wrapper for an Ada access-returning function.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
A class that is generated from an Ada controlled type may be extended
in Java, with overriding versions of the Adjust
, Finalize
,
and/or Initialize
methods.
A current limitation is that Initialize
cannot be called on a shadow
object, e.g.:
package P is type T is new Controlled with null record; procedure Initialize (This : in out T); end P; |
can be used in:
class T_Child extends T { void Initialize () { } } T v = new T_Child (); |
But the overridden Initialize
will not be called by the constructor.
This limitation will be removed in a future release.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
By default, subprograms (except controlling primitives) are not attached --
they are placed in the default class Ada_Package_Name._Package
.
However, when all of the following conditions are met, a subprogram is
attachable to the class corresponding to its first parameter:
in
) designating a private or record
type, or
A subprogram that meets these criteria can be mapped to a method
defined in the class corresponding to the first parameter's type, and
the value of this first parameter will come from the hidden parameter
this
.
This attachment is activated by the annotation pragma Attached
:
pragma Annotate (AJIS, Attached, Condition, Subprogram); |
Example:
package Example is type Rec is null record; procedure Proc (V : Rec; I : Integer); pragma Annotate (AJIS, Attached, True, Proc); end Example; |
will map to the class
public class Rec extends com.adacore.ajis.internal.ada.AdaProxy { ... public void Proc (int I){...} } |
Attachment policies may be globally turned on / off using the following switches:
--[no-]attach-parameter
ada2java
will try to perform subprogram attachment whenever
possible. Default is --no-attach-parameter
--[no-]attach-access
null
value, since this
is always the implicit
parameter.) Default is --no-attach-access
.
--[no-]attach-controlling
--attach-controlling
.
--[no-]attach-ada2005
ada2java
will attempt
to attach a subprogram (define it in the class corresponding to the initial
parameter) that would otherwise be placed in the default package,
if it can be invoked via Ada 2005 prefix notation.
Default is --no-attach-ada2005
.
In the example below, attachment is requested for everything except noncontrolling initial access parameters:
ada2java --attach-parameter --no-attach-access p.ads |
Pragma Attached
takes precedence over the global switch.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Exceptions raised from Ada are translated into instances of the relevant
descendant of class com.adacore.ajis.NativeException
(see 3.5 Exceptions) and propagated to Java.
Exceptions raised from a Java
callback are translated back to the original Ada exception - or to
Java_Exception declared in the AJIS.Java package, and propagated to Ada.
[ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |