28. Bigloo
A practical Scheme compiler (4.3g)
User manual for version 4.3g
December 2019 -- 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)

28.1 Compiling with the JVM back-end

28.1.1 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.

28.1.2 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.


28.2 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

28.3 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.

28.4 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.

28.4.1 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.

28.4.2 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

28.4.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>) 

28.4.4 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)))))

28.4.5 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))))

28.4.6 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

;; main.scm
(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

28.4.7 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.



28.5 Performance of the JVM back-end

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.


This Html page has been produced by Skribe.
Last update Mon Dec 9 13:24:30 2019.