JPL 2.0.2 Low-Level Interface
Table of Contents
Introduction
[Note: if you just want to build hybrid Java+Prolog applications, you
probably don't need to know about this interface: see the High-Level
interface documentation]
The JPL Low-Level interface is implemented by the Java
package jpl.fli. This package contains a set of classes that
mirror the data types in the SWI-Prolog Foreign Language Interface
(FLI), together with a class jpl.fli.Prolog, which contains static
variables and static native methods which reflect the constants and functions
in the FLI. The package is designed to serve as a direct translation
of the Prolog FLI and is generally not intended for the average user.
Its main purpose is to support the High-Level interface (q.v.), use of
which is preferable in most applications.
This section provides a detailed description of the Low-Level interface
for the programmer who may wish to use it much as he or she would the FLI.
As such, it presumes familiarity with the Prolog FLI. This document
is not a tutorial on how to use the Prolog FLI; consult your local
Prolog documentation for how to use the FLI. Programmers who wish
to use JPL without having to know any of the nitty gritty
details of the Low-Level interface may skip this section and read the High-Level
interface section. For information about the SWI-Prolog FLI, see
your local SWI-Prolog documentation.
The JPL Low-Level interface is highly SWI-Prolog specific,
unlike the High-Level interface (which could potentially be implemented
on top of at least some other Prolog systems).
Supported Data Types
The Low-Level interface provides definitions for the following support
classes, which are essentially "holder" classes for the corresponding data
types in the FLI:
LongHolder
|
+--- term_t
|
+-- atom_t
|
+-- functor_t
|
+-- qid_t
|
+-- fid_t
PointerHolder
|
+-- predicate_t
|
+-- module_t
With the exception of predicate_t and module_t, these classes
hold Java long (signed 64-bit) values, corresponding to the C types in
the FLI by the same name (unsigned long values). The module_t
and predicate_t classes also hold long values, but their values
are understood to be C pointers (void *).
Note. The fact that we are using
signed values to represent unsigned values should not be a problem, since
we are not using these values in arithmetic expressions that could cause
errors as a result of casts. However, any SWI-Prolog implementation
that has a word size larger than 4 bytes is not guaranteed to work correctly
with this version of the Low-Level interface.
The Low-Level interface also provides the following convenience classes
used to get information back to the JavaVM from Prolog:
-
IntHolder
-
LongHolder
-
DoubleHolder
-
StringHolder
-
PointerHolder
These classes are for use where a SWI-Prolog FLI function takes modifiable
(by reference) parameters.
jpl.fli.Prolog
The class jpl.fli.Prolog contains a set of Java constant (static
final) and static native method declarations. These declarations
more or less mirror those in the header files for the FLI (in SWI-Prolog,
SWI-Prolog.h), and can all be found in the C source file jpl_fli_Prolog.c.
The general rule of thumb is as follows:
-
All constant and function names (with a few notable exceptions) are the
same as those in the FLI, with the Prolog implementation-specific prefix
(in the case of SWI-Prolog, PL_)
removed. For example, the constant PL_VARIABLE
in the FLI becomes just VARIABLE,
and the FLI function PL_new_term_ref()
becomes just new_term_ref().
A notable exception is the throw
FLI function, which is renamed to _throw in the FLI; throw
is a reserved word in Java.
-
All the constant values are the same in the Low-Level interface as they
are in the FLI.
-
The signatures of FLI functions (with a few notable exceptions) are preserved
in the Low-Level interface. The Low-Level interface provides the
above types for this purpose.
-
Read parameters of the primitive Java types (e.g., int, float,
long,
etc.) are preserved.
-
Modify parameters of the primitive Java types take Holder classes (e.g.,
IntHolder,
DoubleHolder,
LongHolder,
etc.) in which the values are written, instead of pointers to these types.
-
Parameters of other types that are read and read/modify parameters in the
FLI still take structures (e.g., jpl.fli.term_t) as arguments in
the Low-Level interface. This preserves the signature of these methods
as much as possible. A notable exception is the FLI strip_module
function, which takes a module_t *
as a parameter; in the Low-Level interface, the strip_module method
takes a module_t, not a Holder for this type.
Using the Low-Level Interface
Programmers already comfortable with the FLI should find no surprises.
For example, to create a term_t in Java, one would do the same as
one would do in C:
term_t t = Prolog.new_term_ref();
The difference is that the Java method is now a static native method of
the Prolog class, so the syntax is slightly different than the corresponding
call in C. Moreover, many of the same rules in the FLI apply to the
Low-Level interface, as well. To make a term reference which contains
an atom, for example, one must first create the term_t, then an
atom_t,
and then put the atom into the term, as in the FLI:
term_t term = Prolog.new_term_ref();
atom_t atom = Prolog.new_atom( "foo" );
Prolog.put_atom( term, atom );
Caveats
There is nothing special about the Low-Level interface; it is really just
a straight Java mapping of the FLI, and C programmers familiar with the
FLI should have little difficulty using it. On the other hand, translating
the FLI to Java raises some peculiarities that should be mentioned.
Sequential Term References
In the FLI, one can create sequential term references via the new_term_refs
function:
term_t t0 = Prolog.new_term_refs( n);
Subsequent references are obtained by t0+1, t0+2, etc.
However, Java does not support operator overloading, so we can't obtain
subsequent term references by offsetting an initial reference. We
can, however, obtain the value field of a term_t structure an compute
subsequent references off that value, as in, for example, t0.value+1,
t0.value+2,
etc.
Variable-length Argument Lists
Some of the C functions in the FLI (e.g, PL_cons_functor()) take
variable-length arguments, function definitions whose argument lengths
are not known at compile time. However, Java has no support for such
definitions; all method definitions must have determinable signatures at
compile time.
JPL 1.0.1 was designed and implemented before Java acquired anonymous
array syntax (in Java 1.1), which make it feasible to redefine a method
to take an array of arguments in place of a variable-length argument list.
Since the SWI-Prolog FLI provides alternative functions that are equivalent
to these variable-length argument functions, JPL 1.0.1 implemented
these. The High-Level interface exploits anonymous array syntax (e.g.
when constructing a Compound), but it has not been considered necessary
to revise the implementation of the Low-Level interface.
Currently unsupported
FLI functions
A number of SWI-Prolog FLI functions are currently unsupported, and not
needed by the High-Level interface, but could and might be supported in
future versions (preference is likely to be given to those which can sensibly
be made available to applications via the High-Level interface, or which
are necessary to support future extensions to the High-Level interface).
Unsupportable FLI functions
Some SWI-Prolog FLI functions seem inherently unsupportable within this
interface:
-
PL_signal()
-
Java can't feasibly register a C function as s signal handler
-
PL_action()
-
problems with the argument types and qty; some of these actions may be
useful...
-
PL_query()
-
the ARGC, ARGV, MAX_INTEGER, MIN_INTEGER options are redundant
-
QUERY_VERSION might be useful...
-
SYMBOLFILE ?
-
PL_dispatch_hook()
PL_abort_hook()
PL_abort_unhook()
PL_on_halt()
PL_agc_hook()
-
these are of little value within Java (unless we can install Java methods?!)