%---------------------------------------------------------------------------% % vim: ft=mercury ts=4 sw=4 et %---------------------------------------------------------------------------% % Copyright (C) 2000-2012 The University of Melbourne. % Copyright (C) 2013-2025 The Mercury team. % This file may only be copied under the terms of the GNU General % Public License - see the file COPYING in the Mercury distribution. %---------------------------------------------------------------------------% % % File: mlds_to_java_file.m. % Main authors: juliensf, mjwybrow, fjh, wangp. % % Convert MLDS to Java code. % % DONE: % det and semidet predicates % multiple output arguments % boxing and unboxing % conjunctions % disjunctions % if-then-else's % enumerations % discriminated unions % higher order functions % multidet and nondet predicates % test tests/benchmarks/*.m % generate optimized tailcalls % RTTI generation % handle foreign code written in Java % Support for Java in mmc --make % Support for nested modules % % TODO: % - Support nested modules % (The problem with current code generation scheme for nested modules % is that Java does not allow the name of a class to be the same % as the name of its enclosing package. That should work now, but % javac doesn't like the filenames we give for submodules.) % % - Generate names of classes etc. correctly. % % - General code cleanup % % - handle static ground terms(?) % % - support foreign_import_module for Java % % - handle foreign code written in C % % NOTES: % To avoid namespace conflicts all Java names must be fully qualified, % e.g. The classname `String' must be qualified as `java.lang.String' % to avoid conflicting with `mercury.String'. % % There is currently some code threaded through the output predicates (usually % a variable called `ExitMethods') which keeps track of, and removes % unreachable code. Ideally this would be done as an MLDS->MLDS transformation, % preferably in a separate module. Unfortunately this is not possible % due to the fact that the back-end generates `break' statements for cases % in switches as they are output, meaning that we can't remove them in % a pass over the MLDS. % %---------------------------------------------------------------------------% :- module ml_backend.mlds_to_java_file. :- interface. :- import_module hlds. :- import_module hlds.hlds_module. :- import_module libs. :- import_module libs.maybe_util. :- import_module ml_backend.mlds. :- import_module io. %---------------------------------------------------------------------------% :- pred output_java_mlds(io.text_output_stream::in, module_info::in, mlds::in, maybe_succeeded::out, io::di, io::uo) is det. %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% :- implementation. :- import_module libs.compiler_util. :- import_module libs.file_util. :- import_module libs.globals. :- import_module libs.indent. :- import_module mdbcomp. :- import_module mdbcomp.sym_name. :- import_module ml_backend.ml_global_data. :- import_module ml_backend.ml_rename_classes. :- import_module ml_backend.ml_util. :- import_module ml_backend.mlds_to_java_class. :- import_module ml_backend.mlds_to_java_export. :- import_module ml_backend.mlds_to_java_func. :- import_module ml_backend.mlds_to_java_global. :- import_module ml_backend.mlds_to_java_util. :- import_module ml_backend.mlds_to_java_wrap. :- import_module ml_backend.mlds_to_target_util. :- import_module parse_tree. :- import_module parse_tree.file_names. :- import_module parse_tree.java_names. :- import_module parse_tree.parse_tree_out_sym_name. :- import_module parse_tree.prog_data. :- import_module parse_tree.prog_data_foreign. :- import_module parse_tree.prog_foreign. :- import_module assoc_list. :- import_module bool. :- import_module cord. :- import_module list. :- import_module map. :- import_module maybe. :- import_module multi_map. :- import_module pair. :- import_module require. :- import_module set. :- import_module string. :- import_module term_context. :- import_module uint. %---------------------------------------------------------------------------% output_java_mlds(ProgressStream, ModuleInfo, MLDS, Succeeded, !IO) :- % Note that the Java file name that we use for modules in the % Mercury standard library do not include a "mercury." prefix; % that is why we don't call mercury_module_name_to_mlds here. module_info_get_globals(ModuleInfo, Globals), ModuleName = mlds_get_module_name(MLDS), % XXX LEGACY module_name_to_file_name_create_dirs(Globals, $pred, ext_cur_ngs_gs_java(ext_cur_ngs_gs_java_java), ModuleName, JavaSourceFileName, _JavaSourceFileNameProposed, !IO), output_to_file_stream(ProgressStream, Globals, JavaSourceFileName, output_java_src_file(ModuleInfo, MLDS), Succeeded, !IO). :- pred output_java_src_file(module_info::in, mlds::in, io.text_output_stream::in, list(string)::out, io::di, io::uo) is det. output_java_src_file(ModuleInfo, MLDS, Stream, Errors, !IO) :- % Run further transformations on the MLDS. MLDS = mlds(ModuleName, Imports, GlobalData, ClassDefns0, EnumDefns, EnvDefns0, TableStructDefns0, ProcDefns0, InitPreds, FinalPreds, AllForeignCode, ExportedEnums), ml_global_data_get_all_global_defns(GlobalData, ScalarCellGroupMap, VectorCellGroupMap, _AllocIdMap, RttiDefns0, CellDefns0, ClosureWrapperFuncDefns0), % Do NOT enforce the outermost "mercury" qualifier here. This module name % is compared with other module names in the MLDS, to avoid unnecessary % module qualification. MLDS_ModuleName = mercury_module_name_to_mlds(ModuleName), % Find and build list of all methods which would have their addresses % taken to be used as a function pointer. some [!CodeAddrsInConsts] ( !:CodeAddrsInConsts = init_code_addrs_in_consts, method_ptrs_in_class_defns(ClassDefns0, !CodeAddrsInConsts), % There are no method pointers in EnvDefns0. method_ptrs_in_global_var_defns(RttiDefns0, !CodeAddrsInConsts), method_ptrs_in_global_var_defns(CellDefns0, !CodeAddrsInConsts), method_ptrs_in_global_var_defns(TableStructDefns0, !CodeAddrsInConsts), method_ptrs_in_function_defns(ClosureWrapperFuncDefns0, !CodeAddrsInConsts), method_ptrs_in_function_defns(ProcDefns0, !CodeAddrsInConsts), map.values(ScalarCellGroupMap, ScalarCellGroups), ScalarCellRows = list.map(func(G) = G ^ mscg_rows, ScalarCellGroups), list.foldl(method_ptrs_in_scalars, ScalarCellRows, !CodeAddrsInConsts), !.CodeAddrsInConsts = code_addrs_in_consts(_, _, RevSeqNumsCodeAddrs) ), assoc_list.values(RevSeqNumsCodeAddrs, RevCodeAddrs), make_code_addr_map_for_java(RevCodeAddrs, multi_map.init, CodeAddrsMap), map.to_assoc_list(CodeAddrsMap, CodeAddrsAssocList), % Create wrappers in MLDS for all pointer addressed methods. list.map_foldl(generate_addr_wrapper_class(MLDS_ModuleName), CodeAddrsAssocList, WrapperClassDefns0, map.init, AddrOfMap), % Rename classes with excessively long names. list.map_foldl(maybe_shorten_long_class_name, ClassDefns0, ClassDefns1, map.init, RenamingMap1), list.map_foldl(maybe_shorten_long_class_name, WrapperClassDefns0, WrapperClassDefns1, RenamingMap1, RenamingMap2), list.map_foldl(maybe_shorten_long_env_name, EnvDefns0, EnvDefns1, RenamingMap2, RenamingMap), ( if map.is_empty(RenamingMap) then ClassDefns = ClassDefns0, EnvDefns = EnvDefns0, WrapperClassDefns = WrapperClassDefns0, RttiDefns = RttiDefns0, CellDefns = CellDefns0, TableStructDefns = TableStructDefns0, ClosureWrapperFuncDefns = ClosureWrapperFuncDefns0, ProcDefns = ProcDefns0 else Renaming = class_name_renaming(MLDS_ModuleName, RenamingMap), list.map(rename_class_names_in_class_defn(Renaming), ClassDefns1, ClassDefns), list.map(rename_class_names_in_class_defn(Renaming), WrapperClassDefns1, WrapperClassDefns), list.map(rename_class_names_in_env_defn(Renaming), EnvDefns1, EnvDefns), list.map(rename_class_names_in_global_var_defn(Renaming), RttiDefns0, RttiDefns), list.map(rename_class_names_in_global_var_defn(Renaming), CellDefns0, CellDefns), list.map(rename_class_names_in_global_var_defn(Renaming), TableStructDefns0, TableStructDefns), list.map(rename_class_names_in_function_defn(Renaming), ClosureWrapperFuncDefns0, ClosureWrapperFuncDefns), list.map(rename_class_names_in_function_defn(Renaming), ProcDefns0, ProcDefns) ), % Get the foreign code for Java. ( if map.search(AllForeignCode, lang_java, ForeignCode) then ForeignCode = mlds_foreign_code(ForeignDeclCodes, ForeignBodyCodes, _FIMSpecs, ExportDefns) else ForeignDeclCodes = [], ForeignBodyCodes = [], ExportDefns = [] ), % Output transformed MLDS as Java source. % % The order is important here, because Java requires static constants % be defined before they can be used in static initializers. % We start with the Java foreign code declarations, since for % library/private_builtin.m they contain static constants % that will get used in the RTTI definitions. module_name_to_source_file_name(ModuleName, SourceFileName, !IO), Info = init_java_out_info(ModuleInfo, SourceFileName, AddrOfMap), output_src_start_for_java(Info, Stream, ModuleName, Imports, ForeignDeclCodes, ProcDefns, ForeignDeclErrors, !IO), list.map_foldl(output_java_body_code(Info, Stream), ForeignBodyCodes, ForeignCodeResults, !IO), list.filter_map(maybe_is_error, ForeignCodeResults, ForeignCodeErrors), ( RttiDefns = [] ; RttiDefns = [_ | _], io.write_string(Stream, "\n// RttiDefns\n", !IO), list.foldl( output_global_var_defn_for_java(Info, Stream, 1u, oa_alloc_only), RttiDefns, !IO), output_rtti_assignments_for_java(Info, Stream, 1u, RttiDefns, !IO) ), ( if CellDefns = [], TableStructDefns = [] then true else io.write_string(Stream, "\n// Cell and tabling definitions\n", !IO), output_global_var_decls_for_java(Info, Stream, 1u, CellDefns, !IO), output_global_var_decls_for_java(Info, Stream, 1u, TableStructDefns, !IO), output_global_var_assignments_for_java(Info, Stream, 1u, CellDefns ++ TableStructDefns, !IO) ), % Scalar common data must appear after the previous data definitions, and % the vector common data after that. ( if map.is_empty(ScalarCellGroupMap) then true else io.write_string(Stream, "\n// Scalar common data\n", !IO), output_scalar_common_data_for_java(Info, Stream, 1u, ScalarCellGroupMap, !IO) ), ( if map.is_empty(VectorCellGroupMap) then true else io.write_string(Stream, "\n// Vector common data\n", !IO), output_vector_common_data_for_java(Info, Stream, 1u, VectorCellGroupMap, !IO) ), list.sort(ClosureWrapperFuncDefns ++ ProcDefns, SortedFuncDefns), ( SortedFuncDefns = [] ; SortedFuncDefns = [_ | _], io.write_string(Stream, "\n// Function definitions\n", !IO), list.foldl( output_function_defn_for_java(Info, Stream, 1u, oa_none), SortedFuncDefns, !IO) ), list.sort(WrapperClassDefns ++ ClassDefns, SortedClassDefns), ( SortedClassDefns = [] ; SortedClassDefns = [_ | _], io.write_string(Stream, "\n// Class definitions\n", !IO), list.foldl(output_class_defn_for_java(Info, Stream, 1u), SortedClassDefns, !IO) ), list.sort(EnumDefns, SortedEnumDefns), ( SortedEnumDefns = [] ; SortedEnumDefns = [_ | _], io.write_string(Stream, "\n// Enum class definitions\n", !IO), list.foldl(output_enum_class_defn_for_java(Info, Stream, 1u), SortedEnumDefns, !IO) ), list.sort(EnvDefns, SortedEnvDefns), ( SortedEnvDefns = [] ; SortedEnvDefns = [_ | _], io.write_string(Stream, "\n// Env definitions\n", !IO), list.foldl(output_env_defn_for_java(Info, Stream, 1u), SortedEnvDefns, !IO) ), ( ExportDefns = [] ; ExportDefns = [_ | _], io.write_string(Stream, "\n// ExportDefns\n", !IO), output_exports_for_java(Info, Stream, 1u, ExportDefns, !IO) ), ( ExportedEnums = [] ; ExportedEnums = [_ | _], io.write_string(Stream, "\n// ExportedEnums\n", !IO), output_exported_enums_for_java(Info, Stream, 1u, ExportedEnums, !IO) ), ( InitPreds = [] ; InitPreds = [_ | _], io.write_string(Stream, "\n// InitPreds\n", !IO), output_inits_for_java(Stream, 1u, InitPreds, !IO) ), ( FinalPreds = [] ; FinalPreds = [_ | _], io.write_string(Stream, "\n// FinalPreds\n", !IO), output_finals_for_java(Stream, 1u, FinalPreds, !IO) ), set.init(EnvVarNamesSet0), list.foldl(accumulate_env_var_names, ProcDefns, EnvVarNamesSet0, EnvVarNamesSet1), list.foldl(accumulate_env_var_names, ClosureWrapperFuncDefns, EnvVarNamesSet1, EnvVarNamesSet), ( if set.is_empty(EnvVarNamesSet) then true else io.write_string(Stream, "\n// EnvVarNames\n", !IO), set.foldl(output_env_var_definition_for_java(Stream, 1u), EnvVarNamesSet, !IO) ), output_src_end_for_java(Info, Stream, ModuleName, !IO), % XXX Need to handle non-Java foreign code at this point. Errors = ForeignDeclErrors ++ ForeignCodeErrors. :- pred make_code_addr_map_for_java(list(mlds_code_addr)::in, multi_map(arity, mlds_code_addr)::in, multi_map(arity, mlds_code_addr)::out) is det. make_code_addr_map_for_java([], !Map). make_code_addr_map_for_java([CodeAddr | CodeAddrs], !Map) :- CodeAddr = mlds_code_addr(_QualFuncLabel, OrigFuncSignature), OrigFuncSignature = mlds_func_signature(OrigArgTypes, _OrigRetTypes), list.length(OrigArgTypes, Arity), multi_map.set(Arity, CodeAddr, !Map), make_code_addr_map_for_java(CodeAddrs, !Map). %---------------------------------------------------------------------------% % % Code to output the start and end of a source file. % :- pred output_src_start_for_java(java_out_info::in, io.text_output_stream::in, mercury_module_name::in, list(mlds_import)::in, list(foreign_decl_code)::in, list(mlds_function_defn)::in, list(string)::out, io::di, io::uo) is det. output_src_start_for_java(Info, Stream, MercuryModuleName, Imports, ForeignDecls, FuncDefns, Errors, !IO) :- output_auto_gen_comment(Stream, Info ^ joi_source_filename, !IO), io.format(Stream, "// :- module %s.\n\n", [s(escaped_sym_name_to_string(MercuryModuleName))], !IO), io.write_string(Stream, "package jmercury;\n", !IO), maybe_output_import_comments_for_java(Info, Stream, Imports, !IO), list.map_foldl(output_java_decl_code(Info, Stream), ForeignDecls, ForeignDeclResults, !IO), list.filter_map(maybe_is_error, ForeignDeclResults, Errors), io.write_string(Stream, "public class ", !IO), mangle_sym_name_for_java(MercuryModuleName, module_qual, "__", ClassName), io.write_string(Stream, ClassName, !IO), % The close parenthesis for this open parenthesis is written out % by output_src_end_for_java. io.write_string(Stream, " {\n", !IO), output_debug_class_init(Info, Stream, MercuryModuleName, "start", !IO), % Check whether this module contains a `main' predicate, and if it does, % insert a `main' method in the resulting Java class that % calls the `main' predicate. ( if func_defns_contain_main(FuncDefns) then write_main_driver_for_java(Stream, 1u, ClassName, !IO) else true ). :- pred write_main_driver_for_java(io.text_output_stream::in, indent::in, string::in, io::di, io::uo) is det. write_main_driver_for_java(Stream, Indent, ClassName, !IO) :- IndentStr = indent2_string(Indent), io.format(Stream, "%spublic static void main(java.lang.String[] args)\n", [s(IndentStr)], !IO), io.format(Stream, "%s{\n", [s(IndentStr)], !IO), % Save the progname and command line arguments in the class variables % of `jmercury.runtime.JavaInternal', as well as setting the default % exit status. Body = [ "jmercury.runtime.JavaInternal.progname = """ ++ ClassName ++ """;", "jmercury.runtime.JavaInternal.args = args;", "jmercury.runtime.JavaInternal.exit_status = 0;", "library.ML_std_library_init();", "benchmarking.ML_initialise();", "Runnable run_main = new Runnable() {", " public void run() {", " " ++ ClassName ++ ".main_2_p_0();", " }", "};", "jmercury.runtime.JavaInternal.runMain(run_main);", "io.flush_output_3_p_0(io.stdout_stream_0_f_0());", "io.flush_output_3_p_0(io.stderr_stream_0_f_0());", "java.lang.System.exit(jmercury.runtime.JavaInternal.exit_status);" ], Indent1Str = indent2_string(Indent + 1u), list.foldl(write_indentstr_line(Stream, Indent1Str), Body, !IO), io.format(Stream, "%s}\n", [s(IndentStr)], !IO). :- pred output_src_end_for_java(java_out_info::in, io.text_output_stream::in, mercury_module_name::in, io::di, io::uo) is det. output_src_end_for_java(Info, Stream, ModuleName, !IO) :- output_debug_class_init(Info, Stream, ModuleName, "end", !IO), % The open parenthesis for this close parenthesis was written out % by output_src_start_for_java. io.write_string(Stream, "}\n", !IO), AutoComments = Info ^ joi_auto_comments, ( AutoComments = yes, io.format(Stream, "// :- end_module %s.\n", [s(escaped_sym_name_to_string(ModuleName))], !IO) ; AutoComments = no ). :- pred output_debug_class_init(java_out_info::in, io.text_output_stream::in, mercury_module_name::in, string::in, io::di, io::uo) is det. output_debug_class_init(Info, Stream, ModuleName, State, !IO) :- DebugClassInit = get_debug_class_init(Info), ( DebugClassInit = yes, list.foldl(io.write_string(Stream), [ " static {\n", " if (System.getenv(""MERCURY_DEBUG_CLASS_INIT"") != null) {\n", " System.out.println(""[", sym_name_mangle(ModuleName), " ", State, " init]"");\n", " }\n", " }\n" ], !IO) ; DebugClassInit = no ). %---------------------------------------------------------------------------% % % Code to output imports. % :- pred maybe_output_import_comments_for_java(java_out_info::in, io.text_output_stream::in, list(mlds_import)::in, io::di, io::uo) is det. maybe_output_import_comments_for_java(Info, Stream, Imports, !IO) :- AutoComments = Info ^ joi_auto_comments, ( AutoComments = yes, list.foldl(output_import_comment_for_java(Stream), Imports, !IO), ( Imports = [] ; Imports = [_ | _], io.nl(Stream, !IO) ) ; AutoComments = no ). :- pred output_import_comment_for_java(io.text_output_stream::in, mlds_import::in, io::di, io::uo) is det. output_import_comment_for_java(Stream, Import, !IO) :- Import = mlds_import(ImportType, ModuleName), ( ImportType = user_visible_interface, unexpected($pred, "import_type `user_visible_interface' in Java backend") ; ImportType = compiler_visible_interface ), mangle_sym_name_for_java(ModuleName, module_qual, "__", ClassFile), % There are issues related to using import statements and Java's naming % conventions. To avoid these problems, we output dependencies as comments % only. This is ok, since we always use fully qualified names anyway. io.format(Stream, "// import %s;\n", [s(ClassFile)], !IO). %---------------------------------------------------------------------------% % % Code for working with Java `foreign_code'. % :- pred output_java_decl_code(java_out_info::in, io.text_output_stream::in, foreign_decl_code::in, maybe_error::out, io::di, io::uo) is det. output_java_decl_code(Info, Stream, DeclCode, Res, !IO) :- DeclCode = foreign_decl_code(Lang, _IsLocal, LiteralOrInclude, Context), ( Lang = lang_java, output_java_foreign_literal_or_include(Info, Stream, LiteralOrInclude, Context, Res, !IO) ; ( Lang = lang_c ; Lang = lang_csharp ), sorry($pred, "foreign decl other than Java") ). :- pred output_java_body_code(java_out_info::in, io.text_output_stream::in, foreign_body_code::in, maybe_error::out, io::di, io::uo) is det. output_java_body_code(Info, Stream, ForeignBodyCode, Res, !IO) :- ForeignBodyCode = foreign_body_code(Lang, LiteralOrInclude, Context), % Only output Java code. ( Lang = lang_java, output_java_foreign_literal_or_include(Info, Stream, LiteralOrInclude, Context, Res, !IO) ; ( Lang = lang_c ; Lang = lang_csharp ), sorry($pred, "foreign code other than Java") ). :- pred output_java_foreign_literal_or_include(java_out_info::in, io.text_output_stream::in, foreign_literal_or_include::in, prog_context::in, maybe_error::out, io::di, io::uo) is det. output_java_foreign_literal_or_include(Info, Stream, LiteralOrInclude, Context, Res, !IO) :- ( LiteralOrInclude = floi_literal(Code), write_string_with_context_block(Info, Stream, 0u, Code, Context, !IO), Res = ok ; LiteralOrInclude = floi_include_file(IncludeFile), SourceFileName = Info ^ joi_source_filename, make_include_file_path(SourceFileName, IncludeFile, IncludePath), output_context_for_java(Stream, Info ^ joi_foreign_line_numbers, marker_begin_block, context(IncludePath, 1), !IO), ModuleInfo = Info ^ joi_module_info, module_info_get_globals(ModuleInfo, Globals), write_include_file_contents(Stream, Globals, IncludePath, Res, !IO), io.nl(Stream, !IO), % We don't have the true end context readily available. output_context_for_java(Stream, Info ^ joi_foreign_line_numbers, marker_end_block, Context, !IO) ). %---------------------------------------------------------------------------% % % Code to output calls to module initialisers. % :- pred output_inits_for_java(io.text_output_stream::in, indent::in, list(string)::in, io::di, io::uo) is det. output_inits_for_java(Stream, Indent, InitPreds, !IO) :- ( InitPreds = [] ; InitPreds = [_ | _], IndentStr = indent2_string(Indent), Indent1Str = indent2_string(Indent + 1u), % We call the initialisation predicates from a static initialisation % block. io.format(Stream, "%sstatic {\n", [s(IndentStr)], !IO), list.foldl(output_init_for_java(Stream, Indent1Str), InitPreds, !IO), io.format(Stream, "%s}\n", [s(IndentStr)], !IO) ). :- pred output_init_for_java(io.text_output_stream::in, string::in, string::in, io::di, io::uo) is det. output_init_for_java(Stream, IndentStr, InitPred, !IO) :- io.format(Stream, "%s%s();\n", [s(IndentStr), s(InitPred)], !IO). %---------------------------------------------------------------------------% % % Code to output module finalisers. % :- pred output_finals_for_java(io.text_output_stream::in, indent::in, list(string)::in, io::di, io::uo) is det. output_finals_for_java(Stream, Indent, FinalPreds, !IO) :- ( FinalPreds = [] ; FinalPreds = [_ | _], BeforeBlock = [ "static {", " jmercury.runtime.JavaInternal.register_finaliser(", " new java.lang.Runnable() {", " public void run() {" ], AfterBlock = [ " }", " }", " );", "}" ], IndentStr = indent2_string(Indent), Indent4Str = indent2_string(Indent + 4u), list.foldl(write_indentstr_line(Stream, IndentStr), BeforeBlock, !IO), list.foldl(output_final_for_java(Stream, Indent4Str), FinalPreds, !IO), list.foldl(write_indentstr_line(Stream, IndentStr), AfterBlock, !IO) ). :- pred output_final_for_java(io.text_output_stream::in, string::in, string::in, io::di, io::uo) is det. output_final_for_java(Stream, IndentStr, FinalPred, !IO) :- io.format(Stream, "%s%s();\n", [s(IndentStr), s(FinalPred)], !IO). %---------------------------------------------------------------------------% % % Code to output globals for environment variables. % :- pred output_env_var_definition_for_java(io.text_output_stream::in, indent::in, string::in, io::di, io::uo) is det. output_env_var_definition_for_java(Stream, Indent, EnvVarName, !IO) :- % We use int because the generated code compares against zero, and changing % that is more trouble than it's worth as it affects the C backends. IndentStr = indent2_string(Indent), io.format(Stream, "%sprivate static int mercury_envvar_%s =\n", [s(IndentStr), s(EnvVarName)], !IO), io.format(Stream, "%sjava.lang.System.getenv(\"%s\") == null ? 0 : 1;\n", [s(IndentStr), s(EnvVarName)], !IO). %---------------------------------------------------------------------------% :- end_module ml_backend.mlds_to_java_file. %---------------------------------------------------------------------------%