Skip to content

Latest commit

 

History

History
283 lines (205 loc) · 9.1 KB

README.Java.md

File metadata and controls

283 lines (205 loc) · 9.1 KB

Mercury Java Backend

The Mercury compiler has a backend that generates Java source code, that can then be compiled into Java bytecode suitable for running using the Java SE runtime system. The backend is mostly complete, but some parts of the Mercury standard library are not yet implemented.

The Java backend requires Java SE 8 or higher -- older versions of Java are not supported.

Contents

  • Prerequisites
  • Installing the java grade
  • Compiling programs with the java grade
  • Running programs with the java grade
  • Limitations
  • Library support
  • Interfacing with Java
  • Performance
  • Path length restrictions on Windows
  • Java compiler memory exhaustion
  • Mercury-level debugging
  • Java-level debugging
  • Building the Mercury compiler in the java grade

Prerequisites

In order to use Mercury's Java backend you will need:

Installing the java grade

The Mercury compiler uses the grade java to target Java source code that is then compiled into Java bytecode by the Java compiler.

Mercury's autoconfiguration script will cause the java grade to be installed if it finds a suitable Java compiler (e.g. javac) and Java runtime (e.g. java) in your PATH.

You can check if your Mercury installation has been configured to include the java grade by looking if java is included in the output of the Mercury compiler's --output-stdlib-grades option.

Compiling programs with the java grade

Once you have a Mercury installation that includes the java grade, you can build programs such as hello.m or calculator.m in the samples directory.

    $ cd samples
    $ mmc --grade java --make hello

When building programs with the java grade you must use mmc --make; using mmake to build programs using the java grade is not supported.

Running programs with the java grade

You can run the hello program from the previous section by doing:

    $ ./hello

Note that hello is a simple shell script generated by the Mercury compiler that invokes the program using the Java interpreter. The actual class files are packaged up into a Java archive (JAR) named hello.jar.

If you are using the Windows command-line interpreter, i.e. cmd.exe, then setting the value of the option --target-env-type to "windows" will cause the Mercury compiler to generate a batch file that invokes the program, instead of a shell script.

(See the "Using the Mercury compiler" section of the The Mercury User's Guide for further details.)

Limitations

The following features of the Mercury implementation are not (currently) supported by the Java backend:

  • Mercury-level debugging (however, see further down).
  • Mercury-level profiling.
  • Trailing.
  • Tabling.
  • Backjumping.

Library support

The Mercury standard library has not been fully ported to Java yet. The use of unimplemented procedures will result in a run-time error, with a stack trace and a message like:

    Sorry, not implemented: foreign code for this function

If you find missing functionality, you can interface to Java using Mercury's foreign language interface.

The following individual Mercury standard library procedures are either not supported or not fully implemented:

  1. io.read_binary/{3,4}
    io.write_binary/{3,4}

    The current implementation of read_binary does not work with the way Mercury file streams are implemented for the Java backend.

  2. benchmarking.report_stats/0
    benchmarking.report_full_memory_stats/0

    Memory usage statistics are not yet available, and cpu time is not the same as in the C backends, as per time.m.

  3. io.environment.set_environment_var/{4,5}

    The Java APIs do not support setting environment variables, hence this predicate throws an exception.

  4. store.arg_ref/5
    store.new_arg_ref/5

    Due to some limits in RTTI support, dynamic type checking is missing for these predicates. They should be used with care.

  5. time.clock/3
    time.clocks_per_sec/0
    time.times/7
    time.clk_tck/0

    Because the Java APIs do not provide a way of implementing these procedures exactly in pure Java, we have approximated them with what is available.

  6. math.fma/3

    This function is not available because it is not supported by Java 8. (It will be supported once the minimum version of Java required by Mercury increases.)

Interfacing with Java

You can call Java code directly from Mercury using the foreign language interface. For example:

    :- pred to_string(T::in, string::out) is det.
    :- pragma foreign_proc("Java",
        to_string(T::in, Str::out),
        [promise_pure, will_not_call_mercury],
    "
        Str = T.toString();
    ").

The implementation will include this Java code in the module's .java file. You can then call the predicate to_string/2 exactly the same as if it were implemented using pure Mercury code.

For more information about the foreign language interface, see the Mercury Language Reference Manual. Additionally, the samples/java_interface directory in the Mercury distribution contains examples of how to use the foreign language interface with Java.

Performance

Short programs may run much more slowly in the java grade than the C grades. The runtime is probably dominated by Java class loading and running in interpreted mode. A long running program should perform reasonably well with a Just-In-Time compiler. It may also be possible to use an Ahead-Of-Time Java compiler, but we have not tried that yet.

Path length restrictions on Windows

When using the java grade on Windows, it is sometimes possible for the fully qualified names of generated files to exceed the maximum path length. If this occurs the Mercury compiler will abort with a message like:

    Uncaught Mercury exception:
    Software Error: parse_tree.module_cmds: predicate \
    `parse_tree.module_cmds.list_class_files_for_jar'/6: \
    Unexpected: io.file_type failed: No such file or directory

In this case all that can (currently) be done is to reduce the length of the build path, for example by shifting the build directory closer to the root of the file system (e.g. C:\mercury).

Java compiler memory exhaustion

It is possible for the Java compiler to run out of memory when compiling Java code generated by the Mercury compiler. If that occurs, you can pass an option to the javac program to increase the limit. For example:

    $ mmc --make foo --java --java-flag -J-Xmx512m

Or pass an option to javac using a Mercury.options file:

    JAVACFLAGS += -J-Xmx512m

Mercury-level debugging

The only Mercury-level debugger available for the Java backend is the experimental source-to-source debugger; see README.ssdebug.md for details.

Java-level debugging

By default, javac already generates line number and source file debugging information. You can include local variable debugging information by specifying --target-debug when invoking the Mercury compiler. For example:

    $ mmc --grade java --target-debug --make <progname>

You can then use the jdb debugging tool, which comes as part of the Java SDK distribution, to debug your program. For more information, see the documentation for javac and jdb.

Building the Mercury compiler in the java grade

Building the Mercury compiler and other related tools in the Java grade is NOT generally supported and should be considered experimental. In particular, a Mercury compiler built in the Java grade may be slower than normal and some features may not be available.

However, if you want to give it a try, the required steps are:

  1. Ensure that you have an existing working Mercury compiler in your PATH and a clean version of the Mercury source tree.

  2. Run prepare.sh and configure as normal.

  3. Add the line:

     GRADE=java

    to a file named Mmake.params at the top-level of the source tree.

  4. Begin the build process using the following command:

     $ mmake --use-mmc-make GRADE=java

    The Java version of the compiler MUST be built using mmake's --use-mmc-make option; the build will not work otherwise. Setting the variable GRADE in the invocation of mmake is currently necessary in order to avoid variable definition ordering problems in Mmake.workspace.

  5. To install the Java version of the compiler, do:

    $ mmake --use-mmc-make install GRADE=java