The Java interface
When the Bigloo is configured for a JVM back-end support, the compiler is
able to produce Java class file instead of C files. In order to produce
JVM class files, use the
-jvm
compiler option. Example:
$ cat > foo.scm
(module foo (main main))
(define (main argv)
(print "Hello world: " argv))
$ bigloo -jvm foo.scm
$ a.out
⇥ Hello world: (a.out)
Compiling with the JVM back-end
Compiler JVM options
All the compiler options that control the compilation (optimization options,
debugging options, etc.), can be used in conjunction with the
-jvm
option.
However, the
-jvm
option
MUST be the first compiler option
on the command line.
In order to prevent the compiler to produce a script shell file to run
the program, it is required to use simultaneously the
-jvm
and
-c
options.
Compiling multi-modules applications
In order to compile and link multi-modules applications, it is required
to specify the association between Scheme source modules and Java
qualified type names. This task is generally complex because of the
annoying mapping that exists from Java class names and the operating
file system names. In order to get rid of this problem, the Bigloo
standard distribution contains a tool,
jfile
, that automatically
produces Bigloo Module/Java classes association files. The default name
for such a table is
.jfile
. When compiling a module, Bigloo checks
if a
.jfile
exists in the current directory, if it exists, the
file is read. The compilation option
-jfile
may be used to
specify an alternative jfile name. Example:
$ cat > foo.scm
(module foo (export (foo))) (define (foo) 'foo)
$ cat > bar.scm
(module bar (export (bar))) (define (bar) 'bar)
$ cat > hux.scm
(module hux (export (hux))) (define (hux) 'hux)
$ cat > main.scm
(module main (main main) (import foo bar hux)
(define (main argv)
(print (foo))
(print (bar))
(print (fhux)))
$ afile *.scm > .afile
$ jfile *.scm > .jfile
$ bigloo -jvm -c foo.scm
$ bigloo -jvm -c bar.scm
$ bigloo -jvm -c hux.scm
$ bigloo -jvm main.scm foo.class bar.class hux.class
For an explanation about the
.afile
, see
Modules.
JVM back-end and SRFI-0
The currently running back-end may be tested by the means of the SRFI-0
cond-expand
form (see
SRFIs). That is, when the JVM is ran,
the
bigloo-jvm
clause is true. Otherwise, the
bigloo-c
is true. Example:
$ cat > foo.scm
(module foo (main main))
(define (main argv)
(cond-expand
(bigloo-jvm (print "JVM back-end"))
(bigloo-c (print "C back-end"))
(else (error "main" "unsupported back-end" #unspecified))))
$ bigloo -jvm foo.scm
$ a.out
⇥ JVM back-end
$ bigloo foo.scm
$ a.out
⇥ C back-end
Limitation of the JVM back-end
The JVM back-end supports the entire Bigloo source language but the
call/cc
function. More precisely, using the JVM back-end, the
continuation reified in a
call/cc
form can only be invoked in the
dynamic extent of that form.
The other restrictions of the C back-end apply to the JVM
back-end. Mainly,
- Bigloo is not able to compile all the tail recursive call
without stack consumption (however, most of the tail recursive calls are
optimized by Bigloo and don't use stack activation frames).
- Bigloo compiled applications do not check for arithmetic overflow.
- When compiling to Jvm, the
extern
module clauses are not used.
- Jvm runtime system does support the following function
chdir
.
- Jvm runtime system support for
chmod
is restricted.
- In order to read a shell variable from a Bigloo compiled Jvm program,
you have to use the Bigloo link option -jvm-env
when
linking that program. However, some shell variables are
automatically defined (HOME
, USER
, CLASSPATH
and
TMPDIR
.
- JVM code generation does not support
pragma
forms.
Connecting Scheme and Java code
When compiling and linking with the JVM back-end, Bigloo source code may
use the Java API. That is, Bigloo Scheme source code may use (refer or set)
Java static variables, Bigloo source code may call static or virtual
Java methods. In addition, Bigloo variables and functions may be exported
to Java, that is use, set or called in Java source code. Java module clauses
are enabled (read and parsed) only when compiling to JVM byte code.
Java definitions are declared in Bigloo modules by the mean of a Bigloo
module clause: the
java module clause. The syntax of a
Java
clause is defined by:
<java> ⇒ <declare-class-clause>
| <declare-abstract-class-clause>
| <extend-class-clause>
| <array-clause>
| <export-clause>
As for the
extern clause,
java clauses are automatically
``transmitted'' by the importation process. That is, if module
module1
imports a module
module2
,
module
treats the
java
clauses of
module2
as though they were included in
its own module declaration. Redefinition of a variable or a function
already defined in an java clause is an error. However, the definition of
a Java class or an Java abstract class may be enriched from module to
module.
Automatic Java clauses generation
Java clauses can be automatically generated using the Jigloo program
which is distributed in the same package as Bigloo. Using Jigloo may be
a good way to understand how Java classes, methods, and variables have to
be declared in Bigloo. Jigloo reads Java
class files and generate the
Bigloo java clauses for that classes.
Declaring Java classes
The <declare-class-clause> clause denotes importation of Java classes.
<declare-class-clause> ⇒ (
class <typed-ident> <slot>* <string>)
<slot> ⇒ <field> | <method> | <constructor>
<field> ⇒ (field
<modifier> <typed-ident> <string>)
<method> ⇒ (method
<modifier> <typed-ident> (
<typed-ident>*)
<string>)
<constructor> ⇒ (constructor
<ident> (
<typed-ident>*))
<modifier> ⇒ public
| private
| protected
| static
| final
| synchronized
| abstract
When the compiler encounters a Java class declaration, it automatically
creates a predicate. If the class identifier is
id
, the predicate
is named
id?
. In addition, the compiler generates functions that
fetch and set the field values. For a field named
f
, these functions
are named
id-f
and
id-f-set!
. Methods and constructors are
also always prefixed the name of the class. That is, for a method named
m
of a class
k
, the Scheme name of the method is
k-m
.
Example:
(module java-example
(java (class point
(constructor new-default ())
(field x::int "x")
(method show::void (::point) "show")
(method static statistics::int () "PointStatistics")
"Point")
(class point-3d::point
"Point3D")))
(let ((p (point-new-default)))
(print (point? p)) ⇥ #t
(point-x-set! p 3)
(print (point-x p))) ⇥ 3
Declaring abstract Java classes
A Bigloo abstract Java class declaration corresponds to a Java interface.
It cannot be instantiate but regular classes may inherit from it.
<declare-abstract-class-clause> ⇒ (
abstract-class <typed-ident> <slot>* <string>)
Extending Java classes
A class definition may be split into several pieces. One class declaration
(see <declare-class-clause>) and several extensions. The syntax for a Java
class extension is:
<extend-class-clause> ⇒ (
class <typed-ident> <slot>*)
Example:
(module java-example2
(import java-example)
(java (class point
(field y::int "y")
(field static num::int "point_num")
(constructor new (::int ::int)))))
Declaring Java arrays
Java arrays may be allocated and used inside Scheme code. The syntax of
a Java array module clause is:
<array-clause> ⇒ (array
<ident> <typed-ident>)
The <typed-ident> must refer to the name of an existing type (i.e., a
primitive Bigloo type, a Bigloo class, an already defined Java class or
an already defined Java array). For an array named
ar
, Bigloo
generates:
- a creator named
make-ar
which is a function of one integer argument.
- a predicate named
ar?
.
- a getter named
ar-ref
which is a function of two arguments: an array and one integer argument.
- a setter named
ar-set!
which is a function of three arguments: an array, an integer, and a value of the array item types.
- a length named
ar-length
.
Example:
(module foo
(java (array int* ::int)
(class bar
(method static hello::int (::int*) "hello")
"bar"))
(main main))
(define (main argv)
(let ((tab (make-int* 2)))
(int*-set! tab 0 3)
(int*-set! tab 1 6)
(print (bar-hello tab))))
Exporting Scheme variables
As for the C connection, a Scheme variable (or function) can be exported
to the Java world if and only if it is also exported using an
export
Java clause. Type information is given in the Scheme
exportation, thus, the only requirement for a variable to be Java
exported is to be given a Java name. The Java <export-clause> does
this:
<export-clause> ⇒ (export
<ident> <string>)
Here is an example of exportation:
(module example
(export (fib::long ::long))
(java (export fib "scheme_fib")))
(define (fib x) (if (< x 2) 1 ...))
Here is a concrete example that illustrates how to invoke Scheme
functions from Java code. First, here is a Java source file:
// arr.java
import java.io.*;
import bigloo.*;
public class arr {
public static int hello( int[] tab ) {
System.out.println( "tab[ 0 ]: " + tab[ 0 ] );
System.out.println( "tab[ 1 ]: " + tab[ 1 ] );
System.out.println( "<callback: " + main.callback( 10 ) + ">" );
System.out.println("tab = main.squareJavaArray(tab);");
tab = main.squareJavaArray(tab);
System.out.println( "tab[ 0 ]: " + tab[ 0 ] );
System.out.println( "tab[ 1 ]: " + tab[ 1 ] );
return tab.length;
}
}
Then the main Scheme file
(module main
(java (array int* ::int)
(class bar
(method static hello::int (::int*) "hello")
"bar"))
(export (callback::int ::int))
(export (squareJavaArray::int* ::int*))
(main main))
(define (main argv)
(let ((tab (make-int* 2)))
(print "tab length: " (int*-length tab))
(int*-set! tab 0 3)
(int*-set! tab 1 6)
(display (int*-ref tab 1))
(newline)
(display "(java-array-int->scheme-vector tab) : ")
(display (java-array-int->scheme-vector tab))
(newline)
(display "(square-vector (java-array-int->scheme-vector tab)) : ")
(display (square-vector (java-array-int->scheme-vector tab)))
(newline)
(print (bar-hello tab))))
(define (callback x)
(+ 1 x))
(define (square x)
(* x x))
(define (square-list L)
(map square L))
(define (square-vector V)
(vector-map square V))
(define (sum-vector V)
(apply + (vector->list V)))
(define (squareJavaArray A)
(scheme-vector->java-array-int
(square-vector (java-array-int->scheme-vector A))))
(define (java-array-int->scheme-vector A)
(let* ((len (int*-length A))
(res (make-vector len)))
(display len)
(newline)
(let loop ((i 0))
(vector-set! res i (int*-ref A i))
(if (= i (- len 1))
res
(loop (+ i 1))))))
(define (scheme-vector->java-array-int V)
(let* ((len (vector-length V))
(res (make-int* len)))
(let loop ((i 0))
(int*-set! res i (vector-ref V i))
(if (= i (- len 1))
res
(loop (+ i 1))))))
To compile this example:
$ bigloo -jvm main.scm -c
$ javac arr.java -classpath=bigloo.zip:.
$ bigloo -jvm main.scm arr.class
To run it:
$ ./a.out
tab length: 2
6
(java-array-int->scheme-vector tab) : 2
#(3 6)
(square-vector (java-array-int->scheme-vector tab)) : 2
#(9 36)
tab[ 0 ]: 3
tab[ 1 ]: 6
<callback: 11>
tab = main.squareJavaArray(tab);
2
tab[ 0 ]: 9
tab[ 1 ]: 36
2
Bigloo module initialization
By default Bigloo modules are initialized when the application starts. It might
be convenient to initialize the module when the Java classes implementing
the Bigloo modules are loaded. It is possible to drive the Bigloo compiler
to introduce code inside the Java class constructors for initializing the
modules. This is the role of the
-jvm-cinit-module
compiler option.
We are currently improving and investigating about the performance of
the JVM back-end. JVM performance is extremely sensitive to the host
platform (for instance, very unfortunately, Linux seems to be a poor
platform to run JVM code). Currently, it seems that the JVM back-end
produces codes that are in between 4 times and 10 times slower than
codes produced by the C back-end. The ratio between JVM and C is subject
to changes. The gap between JVM and C code is bound to bridge because
of the huge amount of efforts applied to efficiently implement Java
virtual machines.