[m-rev.] document Java foreign language interface

Fergus Henderson fjh at cs.mu.OZ.AU
Wed Dec 3 02:16:34 AEDT 2003


Estimated hours taken: 4
Branches: main

doc/reference_manual.texi:
	Document the Java foreign language interface.

	A few minor changes elsewhere in the foreign language interfacing
	chapter.  In particular, be consistent about how the language
	name is specified in foreign language interfacing pragmas.

tests/hard_coded/Mmakefile:
tests/hard_coded/java_test.m:
tests/hard_coded/java_test.exp:
	Add a test of the Java foreign language interface.

tests/hard_coded/Mmakefile:
tests/hard_coded/csharp_test.m:
tests/hard_coded/csharp_test.exp:
	Enable the test of the C# foreign language interface.

Workspace: /home/jupiter/fjh/ws-jupiter/mercury
Index: doc/reference_manual.texi
===================================================================
RCS file: /home/mercury1/repository/mercury/doc/reference_manual.texi,v
retrieving revision 1.282
diff -u -d -r1.282 reference_manual.texi
--- doc/reference_manual.texi	12 Nov 2003 11:09:49 -0000	1.282
+++ doc/reference_manual.texi	2 Dec 2003 15:04:26 -0000
@@ -5572,6 +5572,7 @@
 @menu
 * C data passing conventions ::
 * IL and C# data passing conventions ::
+* Java data passing conventions ::
 @end menu
 
 @node C data passing conventions
@@ -5632,7 +5633,66 @@
 then the IL function should return the Mercury function result value.
 Otherwise the function result is appended as an extra argument.
 Arguments of type @samp{io__state} or @samp{store__store(_)} are not
-passed at all;
+passed or returned at all;
+that's because these types represent mutable state, and in IL
+modifications to mutable state are done via side effects,
+rather than argument passing.
+
+ at node Java data passing conventions
+ at subsection Java data passing conventions
+
+The Mercury types @code{int}, @code{float}, @code{char},
+and @code{string} are mapped to the Java types 
+ at code{int}, @code{double}, @code{char} and
+ at code{java.lang.String} respectively.
+
+Mercury variables whose type is a type variable will be passed as
+ at code{java.lang.Object}.
+Mercury array types are mapped to Java array types.
+When compiling with @samp{--no-high-level-data}, all other Mercury variables
+are passed as @code{java.lang.Object[]}.
+When compiling with @samp{--high-level-data},
+Mercury variables whose type is a Mercury discriminated union type
+will be passed as a Java type whose type name is determined from
+the Mercury type name (ignoring any type parameters) followed by
+an underscore and then the type arity,
+expressed as a decimal integer.
+Mercury module qualifiers are converted to Java namespace qualifiers.
+For example the Mercury type @samp{foo__bar__baz/1} will be passed as the Java
+type @samp{foo.bar.baz_1}.
+Note an extra namespace qualifier, @samp{mercury}, will be prepended to the
+beginning of names residing in the Mercury standard library.
+Mercury variables whose type is a Mercury equivalence type
+will be passed as the representation of the right hand side of the
+equivalence type.
+
+This mapping is subject to change and you should try to avoid writing
+code that relies heavily upon a particular representation of Mercury
+terms.
+
+Mercury arguments declared with input modes are passed by value to the
+corresponding Java function.  If the Mercury procedure is a function
+whose result has an input mode, then the Mercury function result is
+appended to the list of input parameters, so that the Mercury function
+result becomes the last parameter to the corresponding Java function.
+
+The return values from the Java function consist of the following:
+first, any arguments with output modes; next, if the Mercury procedure
+is a function whose result has an output mode, the function result;
+finally, if the Mercury procedure can fail, the success indicator.
+The success indicator is a truth value of type
+ at samp{boolean} indicating success or failure:
+ at code{true} indicates success, and @code{false} indicates failure.
+If there are no return values, then the return type of the Java
+function will be @samp{void}.  If there is exactly one return
+value, then the Java return type will be the type of that value.
+If there is more than one return value, then the Java return type
+will be @samp{java.lang.Object[]}, and the Java function will return
+an array of type @samp{java.lang.Object[]} containing all the return
+values in the order described above.
+
+Arguments of type @samp{io__state} or @samp{store__store(_)} are not
+passed or returned at all;
 that's because these types represent mutable state, and in IL
 modifications to mutable state are done via side effects,
 rather than argument passing.
@@ -5746,8 +5806,8 @@
 before the object file for the module containing the
 @samp{pragma foreign_import_module} declaration.
 
-A cycle of @samp{pragma foreign_import_module}, where the language is either
- at samp{"MC++"} or @samp{"C#"}, is not permitted.
+A cycle of @samp{pragma foreign_import_module}, where the language is
+ at samp{"MC++"}, @samp{"C#"}, or @samp{"Java"}, is not permitted.
 
 @node Adding foreign definitions
 @section Adding foreign definitions
@@ -5775,9 +5835,12 @@
 @node Language specific bindings
 @section Language specific bindings
 
+ at c Please keep this menu in alphabetical order
+
 @menu
 * Interfacing with C 		:: How to write code to interface with C
 * Interfacing with C# 		:: How to write code to interface with C#
+* Interfacing with Java 	:: How to write code to interface with Java
 * Interfacing with IL 		:: How to write code to interface with IL
 * Interfacing with Managed C++ 	:: How to write code to interface with
   				   Managed C++
@@ -5800,6 +5863,9 @@
 
 @item @samp{C#}
 Use the string @code{"C#"} to set the foreign language to C#.
+      
+ at item @samp{Java}
+Use the string @code{"Java"} to set the foreign language to Java.
 
 @item @samp{IL}
 Use the string @code{"IL"} to set the foreign language to IL.
@@ -5867,8 +5933,8 @@
 
 Arguments whose mode is input will have their values set by the
 Mercury implementation on entry to the C code.  If the procedure
-succeeds, the C code must set the values of all output arguments
-before returning.  If the procedure fails, the C code need only
+succeeds, the C code must set the values of all output arguments.
+If the procedure fails, the C code need only
 set @code{SUCCESS_INDICATOR} to false (zero).
 
 @node Using pragma foreign_decl for C
@@ -5953,7 +6019,7 @@
 The C @samp{pragma foreign_type} declaration is of the form:
 
 @example
-:- pragma foreign_type(c, @var{MercuryTypeName}, @var{CForeignType}).
+:- pragma foreign_type("C", @var{MercuryTypeName}, @var{CForeignType}).
 @end example
 
 The @var{CForeignType} can be any C type name that obeys the following
@@ -5965,10 +6031,8 @@
 @samp{void (*)(void)}.  However, it would be OK to use a typedef name
 which was defined as a function pointer type.)
 
- at c XXX No point documenting this until `--gc accurate'
- at c     is officially supported.
- at c With @samp{--gc accurate}, foreign_types which are C pointer types
- at c must not point to the Mercury heap.
+With @samp{--gc accurate}, foreign_types which are C pointer types
+must not point to the Mercury heap.
 
 If the @var{MercuryTypeName} is the type of a parameter of a procedure
 defined using @samp{pragma foreign_proc},
@@ -6022,8 +6086,8 @@
 
 C# code in a @code{pragma foreign_proc} declaration
 for any procedure whose determinism indicates that it could fail
-must assign a value of type bool to the variable @samp{SUCCESS_INDICATOR}.
-For example:
+must assign a value of type @samp{bool} to the variable
+ at samp{SUCCESS_INDICATOR}.  For example:
 
 @example
 :- pred string__contains_char(string, character).
@@ -6041,8 +6105,8 @@
 
 Arguments whose mode is input will have their values set by the
 Mercury implementation on entry to the C# code.  If the procedure
-succeeds, the C# code must set the values of all output arguments
-before returning.  If the procedure fails, the C# code need only
+succeeds, the C# code must set the values of all output arguments.
+If the procedure fails, the C# code need only
 set @code{SUCCESS_INDICATOR} to false.
 
 @node Using pragma foreign_decl for C#
@@ -6060,7 +6124,7 @@
 	using System;
 ").
 :- pred hello(io__state::di, io__state::uo) is det.
-:- pragma foreign_decl("C#",
+:- pragma foreign_proc("C#",
 	hello(_IO0::di, _IO::uo),
 	[will_not_call_mercury],
 "
@@ -6099,6 +6163,144 @@
 
 @c ----------------------------------------------------------------------------
 
+ at node Interfacing with Java
+ at subsection Interfacing with Java
+
+ at menu
+* Using pragma foreign_type for Java :: Declaring Java types in Mercury
+* Using pragma foreign_proc for Java :: Calling Java code from Mercury
+* Using pragma foreign_decl for Java :: Including Java declarations in Mercury
+* Using pragma foreign_code for Java :: Including Java code in Mercury
+ at end menu
+
+ at node Using pragma foreign_type for Java
+ at subsubsection Using pragma foreign_type for Java
+
+The Java @samp{pragma foreign_type} declaration is of the form:
+
+ at example
+:- pragma foreign_type("Java", @var{MercuryTypeName}, @var{JavaType}).
+ at end example
+
+The @var{JavaType} can be any accessible Java type.
+
+If the @var{MercuryTypeName} is the type of a parameter of a procedure
+defined using @samp{pragma foreign_proc},
+it will be passed to the foreign_proc's foreign language code
+as @var{JavaType}.
+
+ at c XXX `pragma export' not supported yet for Java
+ at c Furthermore, any Mercury procedure exported with @samp{pragma export}
+ at c will use @var{JavaType} as the type for any
+ at c parameters whose Mercury type is @var{MercuryTypeName}.
+
+ at node Using pragma foreign_proc for Java
+ at subsubsection Using pragma foreign_proc for Java
+
+The Java code from Java pragma foreign_proc declarations will be placed in
+the bodies of static member functions of an automatically-generated Java class.
+Since such Java code will become part of a static member function,
+it must not refer to the @samp{this} keyword.
+It may however refer to static member variables or static member
+functions declared with @samp{pragma foreign_code}.
+
+The input and output variables for a Java @samp{pragma foreign_proc} will
+have Java types corresponding to their Mercury types.  The exact rules
+for mapping Mercury types to Java types are described in
+ at ref{Java data passing conventions}.
+
+Java code in a @code{pragma foreign_proc} declaration
+for any procedure whose determinism indicates that it could fail
+must assign a value of type @samp{boolean} to the variable @samp{succeeded}.
+For example:
+
+ at example
+:- pred string__contains_char(string, character).
+:- mode string__contains_char(in, in) is semidet.
+
+:- pragma foreign_proc("Java",
+	string__contains_char(Str::in, Ch::in),
+        [will_not_call_mercury, promise_pure],
+        "succeeded = (Str.IndexOf(Ch) != -1);").
+ at end example
+
+ at noindent
+Java code for procedures whose determinism indicates that they cannot fail
+should not access the @code{succeeded} variable.
+
+Arguments whose mode is input will have their values set by the
+Mercury implementation on entry to the Java code.
+With our current implementation, the Java code must set the values
+of all output variables, even if the procedure fails
+(i.e. the Java code sets the @samp{succeeded} variable to @code{false}).
+ at c If the procedure
+ at c succeeds, the Java code must set the values of all output arguments
+ at c If the procedure fails, the Java code need only
+ at c set the @code{succeeded} variable to false.
+
+ at node Using pragma foreign_decl for Java
+ at subsubsection Using pragma foreign_decl for Java
+
+ at samp{pragma foreign_decl} declarations for Java can be used to provide
+any top-level Java declarations (e.g.@: @samp{import} declarations
+or auxiliary class definitions) which are needed by Java code in
+ at samp{pragma foreign_proc} declarations in that module.
+
+For example:
+
+ at example
+:- pragma foreign_decl("Java", "
+import javax.swing.*;
+import java.awt.*;
+
+class MyApplet extends JApplet @{
+    public void init() @{
+        JLabel label = new JLabel(""Hello, world"");
+        label.setHorizontalAlignment(JLabel.CENTER);
+        getContentPane().add(label);
+    @}
+@}
+").
+:- pred hello(io__state::di, io__state::uo) is det.
+:- pragma foreign_proc("Java",
+	hello(_IO0::di, _IO::uo),
+	[will_not_call_mercury],
+"
+	MyApplet app = new MyApplet();
+	// ...
+").
+ at end example
+
+ at node Using pragma foreign_code for Java
+ at subsubsection Using pragma foreign_code for Java
+
+The Java code from @samp{pragma foreign_proc} declarations for Java will be
+placed in the bodies of static member functions of an
+automatically-generated Java class.  @samp{pragma foreign_code}
+can be used to define additional members of this automatically-generated
+class, which can then be referenced by @samp{pragma foreign_proc}
+declarations for Java from that module.
+
+For example:
+
+ at example
+:- pragma foreign_code("Java", "
+	static int counter = 0;
+").
+
+:- impure pred incr_counter is det.
+:- pragma foreign_proc("Java", incr_counter,
+	[will_not_call_mercury], "counter++;").
+
+:- semipure func get_counter = int.
+:- pragma foreign_proc("Java",
+	get_counter = (Result::out),
+	[will_not_call_mercury, promise_semipure],
+	"Result = counter;").
+ at end example
+
+ at c ----------------------------------------------------------------------------
+
 @node Interfacing with IL
 @subsection Interfacing with IL
 
@@ -6115,7 +6317,7 @@
 The IL @samp{pragma foreign_type} declaration is of the form:
 
 @example
-:- pragma foreign_type(il, @var{MercuryTypeName}, @var{DotNetForeignType}).
+:- pragma foreign_type("IL", @var{MercuryTypeName}, @var{DotNetForeignType}).
 @end example
 
 If the @var{MercuryTypeName} is the type of a parameter of a procedure
@@ -6148,9 +6350,11 @@
 
 @example
 :- type xmldoc.
-:- pragma foreign_type(il, xmldoc, "class [System.Xml]System.Xml.XmlDocument").
+:- pragma foreign_type("IL", xmldoc,
+	"class [System.Xml]System.Xml.XmlDocument").
 :- type int32.
-:- pragma foreign_type(il, int32, "valuetype [mscorlib]System.Int32").
+:- pragma foreign_type("IL", int32,
+	"valuetype [mscorlib]System.Int32").
 @end example
 
 ensures that on the .NET CLR backend the Mercury type @samp{xmldoc} is
@@ -6182,7 +6386,7 @@
 
 @example
 :- pred add(int::in, int::in, int::out) det.
-:- pragma foreign_proc("il", add(X::in, Y::in, Z::out), [max_stack_size(2)], "
+:- pragma foreign_proc("IL", add(X::in, Y::in, Z::out), [max_stack_size(2)], "
 	ldloc X
 	ldloc Y
 	add
@@ -6202,7 +6406,7 @@
 @c
 @c @example
 @c :- pred same(int::in, int::in) is semidet.
- at c :- pragma foreign_proc("il", same(X::in, Y::in), [max_stack_size(2)], "
+ at c :- pragma foreign_proc("IL", same(X::in, Y::in), [max_stack_size(2)], "
 @c 	ldloc X
 @c 	ldloc Y
 @c 	ceq
@@ -6212,8 +6416,7 @@
 
 Arguments whose mode is input will have their values set by the
 Mercury implementation on entry to the IL code.  If the procedure
-succeeds, the IL code must set the values of all output arguments
-before returning. 
+succeeds, the IL code must set the values of all output arguments.
 
 @c If the procedure fails, the IL code need only
 @c set @code{success} to false (zero).
@@ -6272,8 +6475,8 @@
 
 MC++ code in a @code{pragma foreign_proc} declaration
 for any procedure whose determinism indicates that it could fail
-must assign a value of type bool to the variable @samp{SUCCESS_INDICATOR}.
-For example:
+must assign a value of type @samp{bool} to the variable
+ at samp{SUCCESS_INDICATOR}.  For example:
 
 @example
 :- pred string__contains_char(string, character).
@@ -6290,8 +6493,8 @@
 
 Arguments whose mode is input will have their values set by the
 Mercury implementation on entry to the MC++ code.  If the procedure
-succeeds, the MC++ code must set the values of all output arguments
-before returning.  If the procedure fails, the MC++ code need only
+succeeds, the MC++ code must set the values of all output arguments.
+If the procedure fails, the MC++ code need only
 set @code{SUCCESS_INDICATOR} to false.
 
 @node Using pragma foreign_decl for MC++
@@ -6537,8 +6740,8 @@
 
 Arguments whose mode is input will have their values set by the
 Mercury implementation on entry to the C code.  If the procedure
-succeeds, the C code must set the values of all output arguments
-before returning.  If the procedure fails, the C code need only
+succeeds, the C code must set the values of all output arguments.
+If the procedure fails, the C code need only
 set @code{SUCCESS_INDICATOR} to false (zero).
 
 @node Nondet pragma c_code
@@ -7984,6 +8187,8 @@
 The University of Melbourne Mercury compiler performs user-requested type
 specializations when invoked with @samp{--user-guided-type-specialization},
 which is enabled at optimization level @samp{-O2} or higher.
+However, for the Java back-end, user-requested type specializations
+are ignored.
 
 @node Obsolescence
 @section Obsolescence
@@ -8056,35 +8261,6 @@
 to reset the source file name and line number to point back to the
 generated file for the automatically generated text, as in the above
 example.
-
- at c * Interfacing::                 Pragmas can be used to ease interfacing
- at c                                 with other languages.
-
- at c @node Interfacing
- at c @section Interfacing
- at c 
- at c A declaration of the form
- at c 
- at c @example
- at c :- pragma foreign_type(xmldoc, 'System__Xml__XmlDocument', il("System.Xml")).
- at c @end example
- at c 
- at c ensures that on the IL backend the mercury type @samp{xmldoc} is
- at c represented by the backend as a @samp{System.Xml.XmlDocument}.  This
- at c avoids the need to marshall values when interfacing with libraries
- at c written in other languages.  The following example shows how to do this
- at c interfacing.
- at c 
- at c @example
- at c :- pred loadxml(string::in, xmldoc::di, xmldoc::uo) is det.
- at c 
- at c :- pragma foreign_proc("C#", load(String::in, XML0::di, XML::uo),
- at c         [will_not_call_mercury],
- at c "
- at c     XML0.LoadXml(String);
- at c     XML = XML0;
- at c ").
- at c @end example
 
 @node Implementation-dependent extensions
 @chapter Implementation-dependent extensions
Index: tests/hard_coded/Mmakefile
===================================================================
RCS file: /home/mercury1/repository/tests/hard_coded/Mmakefile,v
retrieving revision 1.207
diff -u -d -r1.207 Mmakefile
--- tests/hard_coded/Mmakefile	26 Oct 2003 12:43:33 -0000	1.207
+++ tests/hard_coded/Mmakefile	2 Dec 2003 14:15:45 -0000
@@ -173,6 +173,24 @@
 	write_reg1 \
 	write_reg2
 
+# Tests of the C#, MC++, and IL foreign language interfaces only
+# work in IL grades
+ifeq "$(filter il%,$(GRADE))" ""
+	DOTNET_PROGS =
+else
+	DOTNET_PROGS = \
+		csharp_test
+endif
+
+# Tests of the Java foreign language interface only
+# work in IL grades
+ifeq "$(filter java%,$(GRADE))" ""
+	JAVA_PROGS =
+else
+	JAVA_PROGS = \
+		java_test
+endif
+
 # Fact tables current only work in the C grades.
 ifeq "$(filter il% java%,$(GRADE))" ""
 FACTT_PROGS= \
@@ -228,8 +246,6 @@
 #		predicate.  This test uses partially instantiated modes,
 #		which are not yet fully supported.
 #
-# XXX csharp_test doesn't work yet (not even in il* grades)
-#
 # XXX needs_init doesn't work yet in profiling grades.
 #
 # XXX compare_rep_array doesn't work because MR_COMPARE_BY_RTTI is
@@ -326,16 +342,17 @@
 	endif
 endif
 
-# We currently don't do any testing in grade java on this directory.
+# We currently don't do any testing in grade java on this directory,
+# except for java_test.m.
 ifneq "$(findstring java,$(GRADE))" ""
-	PROGS =
+	PROGS = $(JAVA_PROGS)
 else
 	PROGS = $(ORDINARY_PROGS) $(BROKEN_FOR_LCC_PROGS) \
 		$(CLOSURE_LAYOUT_PROGS) $(EXCEPTION_PROGS) \
 		$(BACKEND_PROGS) $(NONDET_C_PROGS) \
 		$(C_AND_GC_ONLY_PROGS) $(STATIC_LINK_PROGS) \
 		$(CHAR_REP_PROGS) $(BROKEN_FOR_PROFDEEP)  \
-		$(FACTT_PROGS)
+		$(FACTT_PROGS) $(DOTNET_PROGS) $(JAVA_PROGS)
 endif
 
 # --split-c-files does not work in the hl* grades (e.g. hlc.gc),
Index: tests/hard_coded/csharp_test.exp
===================================================================
RCS file: tests/hard_coded/csharp_test.exp
diff -N tests/hard_coded/csharp_test.exp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/hard_coded/csharp_test.exp	2 Dec 2003 14:21:35 -0000
@@ -0,0 +1 @@
+Hello, world
Index: tests/hard_coded/csharp_test.m
===================================================================
RCS file: /home/mercury1/repository/tests/hard_coded/csharp_test.m,v
retrieving revision 1.2
diff -u -d -r1.2 csharp_test.m
--- tests/hard_coded/csharp_test.m	15 May 2001 12:51:04 -0000	1.2
+++ tests/hard_coded/csharp_test.m	2 Dec 2003 14:21:08 -0000
@@ -23,7 +23,8 @@
 
 :- pragma(foreign_proc, "C#",
 	csharp_write_string(Message::in, _IO0::di, _IO::uo),
-	[will_not_call_mercury],
+	[will_not_call_mercury, promise_pure],
 "
 	// a C sharp procedure
+	Console.WriteLine(Message);
 ").
Index: tests/hard_coded/java_test.exp
===================================================================
RCS file: tests/hard_coded/java_test.exp
diff -N tests/hard_coded/java_test.exp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/hard_coded/java_test.exp	2 Dec 2003 14:20:15 -0000
@@ -0,0 +1 @@
+Hello, world
Index: tests/hard_coded/java_test.m
===================================================================
RCS file: tests/hard_coded/java_test.m
diff -N tests/hard_coded/java_test.m
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/hard_coded/java_test.m	2 Dec 2003 14:38:10 -0000
@@ -0,0 +1,69 @@
+% A test of the Java interface.
+
+:- module java_test.
+:- interface.
+:- import_module io.
+
+:- pred main(io__state::di, io__state::uo) is det.
+
+:- implementation.
+:- import_module int.
+
+:- func foo(int) = int.
+/*
+XXX `pragma export' not yet supported for Java
+:- pragma export(foo(in) = out, "foo").
+*/
+foo(X) = X + 1.
+
+main -->
+	java_write_string("Hello, world\n"),
+	( { java_semidet_succeed } ->
+		[]
+	;
+		java_write_string("java_semidet_succeed failed\n")
+	),
+	( { java_semidet_fail } ->
+		java_write_string("java_semidet_fail succeeded\n")
+	;
+		[]
+	).
+
+:- pragma(foreign_decl, "Java", "
+	// some Java top-level declarations
+	class Foo {}
+").
+
+:- pragma(foreign_code, "Java", "
+	// some Java in-class declarations
+	static void bar() {
+/*
+XXX `pragma export' not yet supported for Java
+		// test `pragma export' functions
+		if (foo(42) != 43) {
+			throw new java.lang.Error(""bar: foo failed"");
+		}
+*/
+	}
+").
+
+:- pred java_write_string(string::in, io__state::di, io__state::uo) is det.
+
+:- pragma(foreign_proc, "Java",
+	java_write_string(Message::in, _IO0::di, _IO::uo),
+	[will_not_call_mercury, promise_pure],
+"
+	// a Java procedure
+	System.out.print(Message);
+	// test that foreign_decl declarations are visible
+	Foo f;
+	// test that foreign_code declarations are visible
+	bar();
+").
+
+:- pred java_semidet_succeed is semidet.
+:- pred java_semidet_fail is semidet.
+:- pragma(foreign_proc, "Java", java_semidet_succeed,
+	[will_not_call_mercury, promise_pure], "succeeded = true;").
+:- pragma(foreign_proc, "Java", java_semidet_fail,
+	[will_not_call_mercury, promise_pure], "succeeded = false;").

-- 
Fergus Henderson <fjh at cs.mu.oz.au>  |  "I have always known that the pursuit
The University of Melbourne         |  of excellence is a lethal habit"
WWW: <http://www.cs.mu.oz.au/~fjh>  |     -- the last words of T. S. Garp.
--------------------------------------------------------------------------
mercury-reviews mailing list
post:  mercury-reviews at cs.mu.oz.au
administrative address: owner-mercury-reviews at cs.mu.oz.au
unsubscribe: Address: mercury-reviews-request at cs.mu.oz.au Message: unsubscribe
subscribe:   Address: mercury-reviews-request at cs.mu.oz.au Message: subscribe
--------------------------------------------------------------------------



More information about the reviews mailing list