[m-rev.] diff: mutables for java grade

Peter Wang novalazy at gmail.com
Mon Apr 27 13:24:43 AEST 2009


Branches: main

Add support for mutables in the java grade.
`thread_local' mutables are not yet supported.

compiler/make_hlds_passes.m:
        Add support for mutables in the java grade.

compiler/mlds_to_java.m:
        Make generated Java code call mutable initialisation predicates.
        (General module initialisation predicates are not yet working).

compiler/elds_to_erlang.m:
        Add an XXX I noticed while making the change.

diff --git a/compiler/elds_to_erlang.m b/compiler/elds_to_erlang.m
index 35033cc..dd7e5e4 100644
--- a/compiler/elds_to_erlang.m
+++ b/compiler/elds_to_erlang.m
@@ -153,6 +153,7 @@ output_erl_file(ModuleInfo, ELDS, SourceFileName, !IO) :-
     ;
         AddMainWrapper = no
     ),
+    % XXX there are also user_init_preds generated which aren't used.
     maybe_output_required_init_or_final(ModuleInfo, "mercury__required_init",
         InitPreds, !IO),
     maybe_output_required_init_or_final(ModuleInfo, "mercury__required_final",
diff --git a/compiler/make_hlds_passes.m b/compiler/make_hlds_passes.m
index 348bb12..69d0112 100644
--- a/compiler/make_hlds_passes.m
+++ b/compiler/make_hlds_passes.m
@@ -521,12 +521,13 @@ add_pass_1_mutable(Item, Status, !ModuleInfo, !Specs) :-
             WantPreInitDecl = yes,
             WantUnsafeAccessAndLockDecls = yes
         ;
-            CompilationTarget = target_erlang,
+            ( CompilationTarget = target_erlang
+            ; CompilationTarget = target_java
+            ),
             WantPreInitDecl = no,
             WantUnsafeAccessAndLockDecls = no
         ;
             ( CompilationTarget = target_il
-            ; CompilationTarget = target_java
             ; CompilationTarget = target_asm
             ; CompilationTarget = target_x86_64
             ),
@@ -849,6 +850,9 @@ add_pass_2_mutable(ItemMutable, Status,
!ModuleInfo, !Specs) :-
                 CompilationTarget = target_c,
                 ForeignLanguage = lang_c
             ;
+                CompilationTarget = target_java,
+                ForeignLanguage = lang_java
+            ;
                 CompilationTarget = target_erlang,
                 ForeignLanguage = lang_erlang
             ),
@@ -887,7 +891,6 @@ add_pass_2_mutable(ItemMutable, Status,
!ModuleInfo, !Specs) :-
             )
         ;
             ( CompilationTarget = target_il
-            ; CompilationTarget = target_java
             ; CompilationTarget = target_asm
             ; CompilationTarget = target_x86_64
             ),
@@ -1421,16 +1424,39 @@ add_pass_3_initialise(ItemInitialise, Status,
!ModuleInfo, !QualInfo,

     (
         Details = mutable_decl,
+        module_info_get_globals(!.ModuleInfo, Globals),
+        globals.get_target(Globals, CompilationTarget),
+        (
+            CompilationTarget = target_c,
+            MaybeExportLang = yes(lang_c)
+        ;
+            CompilationTarget = target_java,
+            MaybeExportLang = yes(lang_java)
+        ;
+            CompilationTarget = target_erlang,
+            MaybeExportLang = yes(lang_erlang)
+        ;
+            % Untested.
+            ( CompilationTarget = target_asm
+            ; CompilationTarget = target_il
+            ; CompilationTarget = target_x86_64
+            ),
+            MaybeExportLang = no
+        ),
+        (
+            MaybeExportLang = yes(ExportLang),
         module_info_new_user_init_pred(SymName, Arity, CName, !ModuleInfo),
-        ExportLang = lang_c,    % XXX Implement for other backends.
-        ExportPragma = pragma_foreign_export(ExportLang, SymName, pf_predicate,
-            [], CName),
+            ExportPragma = pragma_foreign_export(ExportLang, SymName,
+                pf_predicate, [], CName),
         ExportItemPragma = item_pragma_info(compiler(mutable_decl),
             ExportPragma, Context, -1),
         ExportItem = item_pragma(ExportItemPragma),
         add_item_pass_3(ExportItem, Status, _,
             !ModuleInfo, !QualInfo, !Specs)
     ;
+            MaybeExportLang = no
+        )
+    ;
         ( Details = initialise_decl
         ; Details = finalise_decl
         ; Details = solver_type
@@ -1600,6 +1626,22 @@ add_pass_3_mutable(ItemMutable, Status,
!ModuleInfo, !QualInfo, !Specs) :-
             add_c_mutable_preds(ItemMutable, TargetMutableName,
                 Status, _, !ModuleInfo, !QualInfo, !Specs)
         ;
+            CompilationTarget = target_java,
+
+            % Work out what name to give the global in the target language.
+            decide_mutable_target_var_name(!.ModuleInfo, MutAttrs,
+                ModuleName, MercuryMutableName, lang_java, Context,
+                TargetMutableName, !Specs),
+
+            % Add foreign_code item that defines the global variable used to
+            % implement the mutable.
+            add_java_mutable_defn(TargetMutableName, Type,
+                Context, !ModuleInfo, !QualInfo, !Specs),
+
+            % Add all the predicates related to mutables.
+            add_java_mutable_preds(ItemMutable, TargetMutableName,
+                Status, _, !ModuleInfo, !QualInfo, !Specs)
+        ;
             CompilationTarget = target_erlang,

             % Work out what name to give the global in the target language.
@@ -1612,7 +1654,6 @@ add_pass_3_mutable(ItemMutable, Status,
!ModuleInfo, !QualInfo, !Specs) :-
                 Status, _, !ModuleInfo, !QualInfo, !Specs)
         ;
             ( CompilationTarget = target_il
-            ; CompilationTarget = target_java
             ; CompilationTarget = target_asm
             ; CompilationTarget = target_x86_64
             )
@@ -1826,7 +1867,7 @@ add_c_mutable_preds(ItemMutableInfo,
TargetMutableName, !Status, !ModuleInfo,
         IsConstant = yes,
         InitSetPredName = mutable_secret_set_pred_sym_name(ModuleName,
             MercuryMutableName),
-        add_c_constant_mutable_access_preds(TargetMutableName,
+        add_c_java_constant_mutable_access_preds(TargetMutableName,
             ModuleName, MercuryMutableName, Attrs, Inst, BoxPolicy,
             Context, !Status, !ModuleInfo, !QualInfo, !Specs)
     ;
@@ -1838,8 +1879,8 @@ add_c_mutable_preds(ItemMutableInfo,
TargetMutableName, !Status, !ModuleInfo,
         add_c_mutable_primitive_preds(TargetMutableName, ModuleName,
             MercuryMutableName, MutAttrs, Attrs, Inst, BoxPolicy, TypeName,
             Context, !Status, !ModuleInfo, !QualInfo, !Specs),
-        add_c_mutable_user_access_preds(ModuleName, MercuryMutableName,
-            MutAttrs, Context, !Status, !ModuleInfo, !QualInfo, !Specs)
+        add_c_java_mutable_user_access_preds(ModuleName, MercuryMutableName,
+            MutAttrs, for_c, Context, !Status, !ModuleInfo, !QualInfo, !Specs)
     ),
     add_c_mutable_initialisation(IsConstant, IsThreadLocal,
         TargetMutableName, ModuleName, MercuryMutableName, MutVarset,
@@ -1847,14 +1888,15 @@ add_c_mutable_preds(ItemMutableInfo,
TargetMutableName, !Status, !ModuleInfo,
         Context, !Status, !ModuleInfo, !QualInfo, !Specs).

     % Add the access predicates for constant mutables.
+    % Shared between C and Java.
     %
-:- pred add_c_constant_mutable_access_preds(string::in, module_name::in,
+:- pred add_c_java_constant_mutable_access_preds(string::in, module_name::in,
     string::in, pragma_foreign_proc_attributes::in, mer_inst::in,
     box_policy::in, prog_context::in, import_status::in, import_status::out,
     module_info::in, module_info::out, qual_info::in, qual_info::out,
     list(error_spec)::in, list(error_spec)::out) is det.

-add_c_constant_mutable_access_preds(TargetMutableName,
+add_c_java_constant_mutable_access_preds(TargetMutableName,
         ModuleName, MutableName, Attrs, Inst, BoxPolicy, Context,
         !Status, !ModuleInfo, !QualInfo, !Specs) :-
     varset.new_named_var(varset.init, "X", X, ProgVarSet),
@@ -2042,35 +2084,50 @@
add_c_mutable_primitive_preds(TargetMutableName, ModuleName,
MutableName,
     UnsafeSetItem = item_pragma(UnsafeSetItemPragma),
     add_item_pass_3(UnsafeSetItem, !Status, !ModuleInfo, !QualInfo, !Specs).

+:- type for_c_or_java
+    --->    for_c
+    ;       for_java.
+
     % Add the access predicates for a non-constant mutable.
     % If the mutable has the `attach_to_io_state' attribute then add the
     % versions of the access preds that take the I/O state as well.
+    % Shared between C and Java.
     %
-:- pred add_c_mutable_user_access_preds(module_name::in, string::in,
-    mutable_var_attributes::in, prog_context::in,
+:- pred add_c_java_mutable_user_access_preds(module_name::in, string::in,
+    mutable_var_attributes::in, for_c_or_java::in, prog_context::in,
     import_status::in, import_status::out,
     module_info::in, module_info::out, qual_info::in, qual_info::out,
     list(error_spec)::in, list(error_spec)::out) is det.

-add_c_mutable_user_access_preds(ModuleName, MutableName, MutAttrs, Context,
-        !Status, !ModuleInfo, !QualInfo, !Specs) :-
+add_c_java_mutable_user_access_preds(ModuleName, MutableName, MutAttrs,
+        ForLang, Context, !Status, !ModuleInfo, !QualInfo, !Specs) :-
     varset.new_named_var(varset.init, "X", X, ProgVarSet0),
     LockPredName   = mutable_lock_pred_sym_name(ModuleName, MutableName),
     UnlockPredName = mutable_unlock_pred_sym_name(ModuleName, MutableName),
     SetPredName = mutable_set_pred_sym_name(ModuleName, MutableName),
     GetPredName = mutable_get_pred_sym_name(ModuleName, MutableName),
+
+    (
+        ForLang = for_c,
     CallLock   = call_expr(LockPredName, [], purity_impure) - Context,
     CallUnlock = call_expr(UnlockPredName, [], purity_impure) - Context,
+        GetterPredName = mutable_unsafe_get_pred_sym_name(ModuleName,
+            MutableName),
+        SetterPredName = mutable_unsafe_set_pred_sym_name(ModuleName,
+            MutableName)
+    ;
+        ForLang = for_java,
+        CallLock   = true_expr - Context,
+        CallUnlock = true_expr - Context,
+        GetterPredName = mutable_get_pred_sym_name(ModuleName, MutableName),
+        SetterPredName = mutable_set_pred_sym_name(ModuleName, MutableName)
+    ),

     % Construct the semipure get predicate.
-    UnsafeGetPredName = mutable_unsafe_get_pred_sym_name(ModuleName,
-        MutableName),
-    UnsafeGetCallArgs = [variable(X, Context)],
-    CallUnsafeGet = call_expr(UnsafeGetPredName, UnsafeGetCallArgs,
+    CallGetter = call_expr(GetterPredName, [variable(X, Context)],
         purity_semipure) - Context,

-    GetBody = goal_list_to_conj(Context,
-        [CallLock, CallUnsafeGet, CallUnlock]),
+    GetBody = goal_list_to_conj(Context, [CallLock, CallGetter, CallUnlock]),
     StdGetBody = promise_purity_expr(purity_semipure, GetBody) - Context,

     StdGetItemClause = item_clause_info(compiler(mutable_decl), ProgVarSet0,
@@ -2081,14 +2138,11 @@ add_c_mutable_user_access_preds(ModuleName,
MutableName, MutAttrs, Context,
     add_item_pass_3(StdGetItem, !Status, !ModuleInfo, !QualInfo, !Specs),

     % Construct the impure set predicate.
-    UnsafeSetPredName = mutable_unsafe_set_pred_sym_name(ModuleName,
-        MutableName),
-    UnsafeSetCallArgs = [variable(X, context_init)],
-    StdSetCallUnsafeSet = call_expr(UnsafeSetPredName, UnsafeSetCallArgs,
+    CallSetter = call_expr(SetterPredName, [variable(X, context_init)],
         purity_impure) - Context,

     StdSetBody = goal_list_to_conj(Context,
-        [CallLock, StdSetCallUnsafeSet, CallUnlock]),
+        [CallLock, CallSetter, CallUnlock]),

     StdSetItemClause = item_clause_info(compiler(mutable_decl), ProgVarSet0,
         pf_predicate, SetPredName, [variable(X, context_init)], StdSetBody,
@@ -2213,6 +2267,188 @@ add_c_mutable_initialisation(IsConstant,
IsThreadLocal, TargetMutableName,

 %-----------------------------------------------------------------------------%
 %
+% Java mutables
+%
+
+    % Add foreign_code item that defines the global variable used to hold the
+    % mutable.
+    %
+:- pred add_java_mutable_defn(string::in, mer_type::in, prog_context::in,
+    module_info::in, module_info::out, qual_info::in, qual_info::out,
+    list(error_spec)::in, list(error_spec)::out) is det.
+
+add_java_mutable_defn(TargetMutableName, Type,
+        Context, !ModuleInfo, !QualInfo, !Specs) :-
+    get_java_mutable_global_foreign_defn(!.ModuleInfo, Type,
+        TargetMutableName, Context, ForeignDefn),
+    ItemStatus0 = item_status(status_local, may_be_unqualified),
+    add_item_decl_pass_2(ForeignDefn, ItemStatus0, _, !ModuleInfo, !Specs).
+
+:- pred get_java_mutable_global_foreign_defn(module_info::in, mer_type::in,
+    string::in, prog_context::in, item::out) is det.
+
+get_java_mutable_global_foreign_defn(ModuleInfo, Type, TargetMutableName,
+        Context, DefnItem) :-
+    module_info_get_globals(ModuleInfo, Globals),
+    globals.lookup_bool_option(Globals, mutable_always_boxed, AlwaysBoxed),
+    TypeName = global_foreign_type_name(AlwaysBoxed, lang_java, ModuleInfo,
+        Type),
+    MutableMutexVarName = mutable_mutex_var_name(TargetMutableName),
+
+    DefnBody = string.append_list([
+        "static ", TypeName, " ", TargetMutableName, ";\n",
+        "static final java.lang.Object ", MutableMutexVarName,
+        " = new Object();\n"
+    ]),
+    DefnPragma = pragma_foreign_code(lang_java, DefnBody),
+    DefnItemPragma = item_pragma_info(compiler(mutable_decl), DefnPragma,
+        Context, -1),
+    DefnItem = item_pragma(DefnItemPragma).
+
+:- pred add_java_mutable_preds(item_mutable_info::in, string::in,
+    import_status::in, import_status::out,
+    module_info::in, module_info::out, qual_info::in, qual_info::out,
+    list(error_spec)::in, list(error_spec)::out) is det.
+
+add_java_mutable_preds(ItemMutable, TargetMutableName,
+        !Status, !ModuleInfo, !QualInfo, !Specs) :-
+    module_info_get_name(!.ModuleInfo, ModuleName),
+    ItemMutable = item_mutable_info(MercuryMutableName, _Type, InitTerm, Inst,
+        MutAttrs, MutVarset, Context, _SeqNum),
+    IsConstant = mutable_var_constant(MutAttrs),
+    Attrs = default_attributes(lang_java),
+    module_info_get_globals(!.ModuleInfo, Globals),
+    globals.lookup_bool_option(Globals, mutable_always_boxed, AlwaysBoxed),
+    (
+        AlwaysBoxed = yes,
+        BoxPolicy = always_boxed
+    ;
+        AlwaysBoxed = no,
+        BoxPolicy = native_if_possible
+    ),
+    (
+        IsConstant = yes,
+        InitSetPredName = mutable_secret_set_pred_sym_name(ModuleName,
+            MercuryMutableName),
+        add_c_java_constant_mutable_access_preds(TargetMutableName,
+            ModuleName, MercuryMutableName, Attrs, Inst, BoxPolicy,
+            Context, !Status, !ModuleInfo, !QualInfo, !Specs)
+    ;
+        IsConstant = no,
+        InitSetPredName = mutable_set_pred_sym_name(ModuleName,
+            MercuryMutableName),
+        add_java_mutable_primitive_preds(TargetMutableName, ModuleName,
+            MercuryMutableName, MutAttrs, Attrs, Inst, BoxPolicy,
+            Context, !Status, !ModuleInfo, !QualInfo, !Specs),
+        add_c_java_mutable_user_access_preds(ModuleName, MercuryMutableName,
+            MutAttrs, for_java,
+            Context, !Status, !ModuleInfo, !QualInfo, !Specs)
+    ),
+    add_java_mutable_initialisation(ModuleName, MercuryMutableName, MutVarset,
+        InitSetPredName, InitTerm,
+        Context, !Status, !ModuleInfo, !QualInfo, !Specs).
+
+    % Add the foreign clauses for the mutable's primitive access and
+    % locking predicates.
+    %
+:- pred add_java_mutable_primitive_preds(string::in, module_name::in,
string::in,
+    mutable_var_attributes::in, pragma_foreign_proc_attributes::in,
+    mer_inst::in, box_policy::in,
+    prog_context::in, import_status::in, import_status::out,
+    module_info::in, module_info::out, qual_info::in, qual_info::out,
+    list(error_spec)::in, list(error_spec)::out) is det.
+
+add_java_mutable_primitive_preds(TargetMutableName, ModuleName, MutableName,
+        MutAttrs, Attrs, Inst, BoxPolicy,
+        Context, !Status, !ModuleInfo, !QualInfo, !Specs) :-
+    % Construct the semipure get predicate.
+
+    set_purity(purity_semipure, Attrs, GetAttrs0),
+    set_thread_safe(proc_thread_safe, GetAttrs0, GetAttrs),
+    varset.new_named_var(varset.init, "X", X, ProgVarSet),
+    MutableMutexVarName = mutable_mutex_var_name(TargetMutableName),
+    GetCode =
+        "\tsynchronized (" ++ MutableMutexVarName ++ ") {\n" ++
+        "\t\tX = " ++ TargetMutableName ++ ";\n\t}\n",
+    GetForeignProc = pragma_foreign_proc(GetAttrs,
+        mutable_get_pred_sym_name(ModuleName, MutableName),
+        pf_predicate,
+        [pragma_var(X, "X", out_mode(Inst), BoxPolicy)],
+        ProgVarSet,
+        varset.init, % Inst varset.
+        fc_impl_ordinary(GetCode, yes(Context))
+    ),
+    GetItemPragma = item_pragma_info(compiler(mutable_decl),
+        GetForeignProc, Context, -1),
+    GetItem = item_pragma(GetItemPragma),
+    add_item_pass_3(GetItem, !Status, !ModuleInfo, !QualInfo, !Specs),
+
+    % Construct the impure set predicate.
+
+    set_thread_safe(proc_thread_safe, Attrs, SetAttrs),
+    TrailMutableUpdates = mutable_var_trailed(MutAttrs),
+    (
+        TrailMutableUpdates = mutable_untrailed,
+        TrailCode = ""
+    ;
+        TrailMutableUpdates = mutable_trailed,
+        Pieces = [words("Error: trailed mutable in Java grade."), nl],
+        Msg = simple_msg(Context, [always(Pieces)]),
+        Spec = error_spec(severity_error, phase_parse_tree_to_hlds, [Msg]),
+        !:Specs = [Spec | !.Specs],
+        % This is just a dummy value.
+        TrailCode = ""
+    ),
+    SetCode = "\tsynchronized (" ++ MutableMutexVarName ++ ") {\n" ++
+        "\t\t" ++ TargetMutableName ++ "= X;\n\t}\n",
+    SetForeignProc = pragma_foreign_proc(SetAttrs,
+        mutable_set_pred_sym_name(ModuleName, MutableName),
+        pf_predicate,
+        [pragma_var(X, "X", in_mode(Inst), BoxPolicy)],
+        ProgVarSet,
+        varset.init, % Inst varset.
+        fc_impl_ordinary(TrailCode ++ SetCode, yes(Context))
+    ),
+    SetItemPragma = item_pragma_info(compiler(mutable_decl),
+        SetForeignProc, Context, -1),
+    SetItem = item_pragma(SetItemPragma),
+    add_item_pass_3(SetItem, !Status, !ModuleInfo, !QualInfo, !Specs).
+
+    % Add the code required to initialise a mutable.
+    %
+:- pred add_java_mutable_initialisation(module_name::in, string::in,
+    prog_varset::in, sym_name::in, prog_term::in,
+    prog_context::in, import_status::in, import_status::out,
+    module_info::in, module_info::out, qual_info::in, qual_info::out,
+    list(error_spec)::in, list(error_spec)::out) is det.
+
+add_java_mutable_initialisation(ModuleName, MutableName, MutVarset,
+        InitSetPredName, InitTerm,
+        Context, !Status, !ModuleInfo, !QualInfo, !Specs) :-
+    % Add the `:- initialise' declaration for the mutable initialisation
+    % predicate.
+    InitPredName = mutable_init_pred_sym_name(ModuleName, MutableName),
+    InitPredArity = 0,
+
+    InitItemInitialise = item_initialise_info(compiler(mutable_decl),
+        InitPredName, InitPredArity, Context, -1),
+    InitItem = item_initialise(InitItemInitialise),
+    add_item_pass_3(InitItem, !Status, !ModuleInfo, !QualInfo, !Specs),
+
+    % Add the clause for the mutable initialisation predicate.
+    InitClauseExpr =
+        call_expr(InitSetPredName, [InitTerm], purity_impure)
+            - Context,
+
+    % See the comments for prog_io.parse_mutable_decl for the reason
+    % why we _must_ use MutVarset here.
+    PredItemClause = item_clause_info(compiler(mutable_decl), MutVarset,
+        pf_predicate, InitPredName, [], InitClauseExpr, Context, -1),
+    PredItem = item_clause(PredItemClause),
+    add_item_pass_3(PredItem, !Status, !ModuleInfo, !QualInfo, !Specs).
+
+%-----------------------------------------------------------------------------%
+%
 % Erlang mutables
 %

diff --git a/compiler/mlds_to_java.m b/compiler/mlds_to_java.m
index e42cca4..00b3284 100644
--- a/compiler/mlds_to_java.m
+++ b/compiler/mlds_to_java.m
@@ -456,7 +456,7 @@ output_import(Import, !IO) :-
 output_java_src_file(ModuleInfo, Indent, MLDS, !IO) :-
     % Run further transformations on the MLDS.
     MLDS = mlds(ModuleName, AllForeignCode, Imports, Defns0,
-        _InitPreds, _FinalPreds, _ExportedEnums),
+        InitPreds, _FinalPreds, _ExportedEnums),
     MLDS_ModuleName = mercury_module_name_to_mlds(ModuleName),

     % Find and build list of all methods which would have their addresses
@@ -494,6 +494,7 @@ output_java_src_file(ModuleInfo, Indent, MLDS, !IO) :-
         NonRttiDefns, !IO),
     output_exports(Indent + 1, ModuleInfo, MLDS_ModuleName, CtorData,
         ExportDefns, !IO),
+    output_inits(Indent + 1, ModuleInfo, InitPreds, !IO),
     output_src_end(Indent, ModuleName, !IO).
     % XXX Need to handle non-Java foreign code at this point.

@@ -1092,6 +1093,35 @@
pred_label_string(mlds_special_pred_label(PredName, MaybeTypeModule,
TypeName,

 %-----------------------------------------------------------------------------%
 %
+% Code to output calls to module initialisers.
+%
+
+:- pred output_inits(int::in, module_info::in, list(string)::in,
+    io::di, io::uo) is det.
+
+output_inits(Indent, _ModuleInfo, InitPreds, !IO) :-
+    (
+        InitPreds = []
+    ;
+        InitPreds = [_ | _],
+        % We call the initialisation predicates from a static initialisation
+        % block.
+        indent_line(Indent, !IO),
+        io.write_string("static {\n", !IO),
+        list.foldl(output_init_2(Indent + 1), InitPreds, !IO),
+        indent_line(Indent, !IO),
+        io.write_string("}\n", !IO)
+    ).
+
+:- pred output_init_2(int::in, string::in, io::di, io::uo) is det.
+
+output_init_2(Indent, InitPred, !IO) :-
+    indent_line(Indent, !IO),
+    io.write_string(InitPred, !IO),
+    io.write_string("();\n", !IO).
+
+%-----------------------------------------------------------------------------%
+%
 % Code to output the start and end of a source file.
 %
--------------------------------------------------------------------------
mercury-reviews mailing list
Post messages to:       mercury-reviews at csse.unimelb.edu.au
Administrative Queries: owner-mercury-reviews at csse.unimelb.edu.au
Subscriptions:          mercury-reviews-request at csse.unimelb.edu.au
--------------------------------------------------------------------------



More information about the reviews mailing list