Files
mercury/compiler/mlds_to_java.m
Zoltan Somogyi 295415090e Convert almost all remaining modules in the compiler to use
Estimated hours taken: 6
Branches: main

compiler/*.m:
	Convert almost all remaining modules in the compiler to use
	"$module, $pred" instead of "this_file" in error messages.

	In a few cases, the old error message was misleading, since it
	contained an incorrect, out-of-date or cut-and-pasted predicate name.

tests/invalid/unresolved_overloading.err_exp:
	Update an expected output containing an updated error message.
2011-05-23 05:08:24 +00:00

5227 lines
187 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 2000-2011 The University of Melbourne.
% 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.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.)
%
% - Support for Java in Mmake.
%
% - Generate names of classes etc. correctly (mostly same as IL backend)
%
% - 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.
:- interface.
:- import_module hlds.hlds_module.
:- import_module ml_backend.mlds.
:- import_module io.
%-----------------------------------------------------------------------------%
:- pred output_java_mlds(module_info::in, mlds::in, io::di, io::uo) is det.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
% XXX needed for c_util.output_quoted_string,
% c_util.output_quoted_multi_string, and
% c_util.make_float_literal.
:- import_module backend_libs.builtin_ops.
:- import_module backend_libs.c_util.
:- import_module backend_libs.rtti.
:- import_module hlds.hlds_pred. % for pred_proc_id.
:- import_module libs.file_util.
:- import_module libs.globals.
:- import_module libs.options.
:- import_module mdbcomp.prim_data.
:- import_module ml_backend.java_util.
:- import_module ml_backend.ml_code_util. % for ml_gen_local_var_decl_flags.
:- import_module ml_backend.ml_global_data.
:- import_module ml_backend.ml_type_gen. % for ml_gen_type_name
:- import_module ml_backend.ml_util.
:- import_module ml_backend.mlds.
:- import_module ml_backend.rtti_to_mlds.
:- import_module parse_tree.builtin_lib_types.
:- import_module parse_tree.file_names. % for mercury_std_library_name.
:- import_module parse_tree.java_names.
:- import_module parse_tree.prog_data.
:- import_module parse_tree.prog_foreign.
:- import_module parse_tree.prog_out.
:- import_module parse_tree.prog_type.
:- import_module assoc_list.
:- import_module bool.
:- import_module char.
:- import_module cord.
:- import_module digraph.
:- import_module int.
:- import_module library.
:- 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.
:- import_module varset.
%-----------------------------------------------------------------------------%
output_java_mlds(ModuleInfo, MLDS, !IO) :-
% Note that the Java file name that we use for modules in the
% Mercury standard library do not include a "mercury." prefix;
% that's why we don't call mercury_module_name_to_mlds here.
module_info_get_globals(ModuleInfo, Globals),
ModuleName = mlds_get_module_name(MLDS),
module_name_to_file_name(Globals, ModuleName, ".java", do_create_dirs,
JavaSourceFile, !IO),
Indent = 0,
output_to_file(Globals, JavaSourceFile,
output_java_src_file(ModuleInfo, Indent, MLDS), !IO).
%-----------------------------------------------------------------------------%
%
% Utility predicates for various purposes.
%
% Succeeds iff this definition is a data definition which defines RTTI.
%
:- pred defn_is_rtti_data(mlds_defn::in) is semidet.
defn_is_rtti_data(Defn) :-
Defn = mlds_defn(_Name, _Context, _Flags, Body),
Body = mlds_data(Type, _, _),
Type = mlds_rtti_type(_).
% Succeeds iff this definition is a data definition.
%
:- pred defn_is_data(mlds_defn::in) is semidet.
defn_is_data(Defn) :-
Defn = mlds_defn(_Name, _Context, _Flags, Body),
Body = mlds_data(_, _, _).
% Succeeds iff this type is a enumeration.
%
:- pred type_is_enum(mlds_type::in) is semidet.
type_is_enum(Type) :-
Type = mercury_type(_, Builtin, _),
Builtin = ctor_cat_enum(_).
% Succeeds iff this type is something that the Java backend will represent
% as an object i.e. something created using the new operator.
%
:- pred type_is_object(mlds_type::in) is semidet.
type_is_object(Type) :-
Type = mercury_type(_, CtorCat, _),
type_category_is_object(CtorCat) = yes.
:- func type_category_is_object(type_ctor_category) = bool.
type_category_is_object(CtorCat) = IsObject :-
(
( CtorCat = ctor_cat_builtin(_)
; CtorCat = ctor_cat_higher_order
; CtorCat = ctor_cat_tuple
; CtorCat = ctor_cat_void
),
IsObject = no
;
( CtorCat = ctor_cat_enum(_)
; CtorCat = ctor_cat_builtin_dummy
; CtorCat = ctor_cat_variable
; CtorCat = ctor_cat_system(_)
; CtorCat = ctor_cat_user(_)
),
IsObject = yes
).
% Given an lval, return its type.
%
:- func mlds_lval_type(mlds_lval) = mlds_type.
mlds_lval_type(ml_var(_, VarType)) = VarType.
mlds_lval_type(ml_field(_, _, _, FieldType, _)) = FieldType.
mlds_lval_type(ml_mem_ref(_, PtrType)) =
( PtrType = mlds_ptr_type(Type) ->
Type
;
unexpected($module, $pred, "mem_ref of non-pointer")
).
mlds_lval_type(ml_global_var_ref(_)) = _ :-
sorry($module, $pred, "global_var_ref NYI").
% Succeeds iff the Rval represents an enumeration object in the Java
% backend. We need to check both Rvals that are variables and Rvals
% that are casts. We need to know this in order to append the field name
% to the object so we can access the value of the enumeration object.
%
:- pred rval_is_enum_object(mlds_rval::in) is semidet.
rval_is_enum_object(Rval) :-
Rval = ml_lval(Lval),
(
Lval = ml_var(_, VarType),
type_is_enum(VarType)
;
Lval = ml_field(_, _, _, FieldType, _),
type_is_enum(FieldType)
).
% Succeeds iff a given string matches the unqualified interface name
% of a interface in Mercury's Java runtime system.
%
:- pred interface_is_special(string::in) is semidet.
interface_is_special("MercuryType").
interface_is_special("MethodPtr").
interface_is_special("MethodPtr1").
interface_is_special("MethodPtr2").
interface_is_special("MethodPtr3").
interface_is_special("MethodPtr4").
interface_is_special("MethodPtr5").
interface_is_special("MethodPtr6").
interface_is_special("MethodPtr7").
interface_is_special("MethodPtr8").
interface_is_special("MethodPtr9").
interface_is_special("MethodPtr10").
interface_is_special("MethodPtr11").
interface_is_special("MethodPtr12").
interface_is_special("MethodPtr13").
interface_is_special("MethodPtr14").
interface_is_special("MethodPtr15").
interface_is_special("MethodPtrN").
%-----------------------------------------------------------------------------%
%
% Code to mangle names, enforce Java code conventions regarding class names
% etc.
% XXX None of this stuff works as it should. The idea is that class
% names should start with an uppercase letter, while method names and
% package specifiers should start with a lowercase letter.
% The current implementation of the MLDS makes this rather harder to achieve
% than it might initially seem. The current position is that coding
% conventions are only enforced on library modules.
% This is needed as Java compilers don't take too well to compiling
% classes named `char',`int', `float' etc.
% XXX It might be nice if the name mangling code was taken out of which
% ever LLDS module it's hiding in and put in a separate one.
%
% XXX This won't work if we start using the Java coding conventions
% for all names. At the moment it only affects library modules.
%
:- pred enforce_java_names(string::in, string::out) is det.
enforce_java_names(Name, JavaName) :-
% If the Name contains one or more dots (`.'), then capitalize
% the first letter after the last dot.
reverse_string(Name, RevName),
( string.sub_string_search(RevName, ".", Pos) ->
string.split(RevName, Pos, Head0, Tail0),
reverse_string(Tail0, Tail),
reverse_string(Head0, Head1),
string.capitalize_first(Head1, Head),
string.append(Tail, Head, JavaName)
;
JavaName = Name
).
:- pred reverse_string(string::in, string::out) is det.
reverse_string(String0, String) :-
string.to_char_list(String0, String1),
string.from_rev_char_list(String1, String).
%-----------------------------------------------------------------------------%
%
% Code to output imports.
%
:- pred output_imports(mlds_imports::in, io::di, io::uo) is det.
output_imports(Imports, !IO) :-
list.foldl(output_import, Imports, !IO).
:- pred output_import(mlds_import::in, io::di, io::uo) is det.
output_import(Import, !IO) :-
(
Import = mercury_import(ImportType, ImportName),
(
ImportType = user_visible_interface,
unexpected($module, $pred,
"import_type `user_visible_interface' in Java backend")
;
ImportType = compiler_visible_interface
)
;
Import = foreign_import(_),
unexpected($module, $pred, "foreign import in Java backend")
),
SymName = mlds_module_name_to_sym_name(ImportName),
mangle_sym_name_for_java(SymName, 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.write_strings(["// import ", ClassFile, ";\n"], !IO).
%-----------------------------------------------------------------------------%
%
% Code to generate the `.java' file.
%
:- pred output_java_src_file(module_info::in, indent::in, mlds::in,
io::di, io::uo) is det.
output_java_src_file(ModuleInfo, Indent, MLDS, !IO) :-
% Run further transformations on the MLDS.
MLDS = mlds(ModuleName, AllForeignCode, Imports, GlobalData, Defns0,
InitPreds, FinalPreds, ExportedEnums),
ml_global_data_get_all_global_defns(GlobalData,
ScalarCellGroupMap, VectorCellGroupMap, _AllocIdMap, GlobalDefns),
% 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 [!CodeAddrs] (
!:CodeAddrs = [],
find_pointer_addressed_methods(GlobalDefns, !CodeAddrs),
find_pointer_addressed_methods(Defns0, !CodeAddrs),
map.values(ScalarCellGroupMap, ScalarCellGroups),
ScalarCellRows = list.map(func(G) = G ^ mscg_rows, ScalarCellGroups),
list.foldl(find_pointer_addressed_methods_in_scalars,
ScalarCellRows, !CodeAddrs),
make_code_addr_map(!.CodeAddrs, 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, WrapperClassDefns, map.init, AddrOfMap),
Defns1 = GlobalDefns ++ WrapperClassDefns ++ Defns0,
% Rename classes with excessively long names.
shorten_long_class_names(MLDS_ModuleName, Defns1, Defns),
% Get the foreign code for Java
% XXX We should not ignore _RevImports.
ForeignCode = mlds_get_java_foreign_code(AllForeignCode),
ForeignCode = mlds_foreign_code(RevForeignDecls, _RevImports,
RevBodyCode, ExportDefns),
ForeignDecls = list.reverse(RevForeignDecls),
ForeignBodyCode = list.reverse(RevBodyCode),
% 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_info_get_globals(ModuleInfo, Globals),
Info = init_java_out_info(ModuleInfo, AddrOfMap),
output_src_start(Globals, Info, Indent, ModuleName, Imports, ForeignDecls,
Defns, !IO),
io.write_list(ForeignBodyCode, "\n", output_java_body_code(Info, Indent),
!IO),
list.filter(defn_is_rtti_data, Defns, RttiDefns, NonRttiDefns),
io.write_string("\n// RttiDefns\n", !IO),
output_defns(Info, Indent + 1, alloc_only, RttiDefns, !IO),
output_rtti_assignments(Info, Indent + 1, RttiDefns, !IO),
list.filter(defn_is_data, NonRttiDefns, DataDefns, NonDataDefns),
io.write_string("\n// DataDefns\n", !IO),
output_data_decls(Info, Indent + 1, DataDefns, !IO),
output_data_assignments(Info, Indent + 1, DataDefns, !IO),
% Scalar common data must appear after the previous data definitions,
% and the vector common data after that.
io.write_string("\n// Scalar common data\n", !IO),
output_scalar_common_data(Info, Indent + 1, ScalarCellGroupMap, !IO),
io.write_string("\n// Vector common data\n", !IO),
output_vector_common_data(Info, Indent + 1, VectorCellGroupMap, !IO),
io.write_string("\n// NonDataDefns\n", !IO),
output_defns(Info, Indent + 1, none, NonDataDefns, !IO),
io.write_string("\n// ExportDefns\n", !IO),
output_exports(Info, Indent + 1, ExportDefns, !IO),
io.write_string("\n// ExportedEnums\n", !IO),
output_exported_enums(Info, Indent + 1, ExportedEnums, !IO),
io.write_string("\n// InitPreds\n", !IO),
output_inits(Indent + 1, InitPreds, !IO),
io.write_string("\n// FinalPreds\n", !IO),
output_finals(Indent + 1, FinalPreds, !IO),
io.write_string("\n// EnvVarNames\n", !IO),
output_env_vars(Indent + 1, NonRttiDefns, !IO),
output_src_end(Indent, ModuleName, !IO).
% XXX Need to handle non-Java foreign code at this point.
%-----------------------------------------------------------------------------%
%
% Code for working with Java `foreign_code'.
%
:- pred output_java_decl(java_out_info::in, indent::in, foreign_decl_code::in,
io::di, io::uo) is det.
output_java_decl(Info, Indent, DeclCode, !IO) :-
DeclCode = foreign_decl_code(Lang, _IsLocal, Code, Context),
(
Lang = lang_java,
indent_line(Info, mlds_make_context(Context), Indent, !IO),
io.write_string(Code, !IO),
io.nl(!IO)
;
( Lang = lang_c
; Lang = lang_csharp
; Lang = lang_il
; Lang = lang_erlang
),
sorry($module, $pred, "foreign decl other than Java")
).
:- pred output_java_body_code(java_out_info::in, indent::in,
user_foreign_code::in, io::di, io.state::uo) is det.
output_java_body_code(Info, Indent, UserForeignCode, !IO) :-
UserForeignCode = user_foreign_code(Lang, Code, Context),
% Only output Java code.
(
Lang = lang_java,
indent_line(Info, mlds_make_context(Context), Indent, !IO),
io.write_string(Code, !IO),
io.nl(!IO)
;
( Lang = lang_c
; Lang = lang_csharp
; Lang = lang_il
; Lang = lang_erlang
),
sorry($module, $pred, "foreign code other than Java")
).
% Get the foreign code for Java.
%
:- func mlds_get_java_foreign_code(map(foreign_language, mlds_foreign_code))
= mlds_foreign_code.
mlds_get_java_foreign_code(AllForeignCode) = ForeignCode :-
( map.search(AllForeignCode, lang_java, ForeignCode0) ->
ForeignCode = ForeignCode0
;
ForeignCode = mlds_foreign_code([], [], [], [])
).
%-----------------------------------------------------------------------------%
%
% Code for handling `pragma foreign_export' for Java.
%
% Exports are converted into forwarding methods that are given the
% specified name. These simply call the exported procedure.
%
% NOTE: the forwarding methods must be declared public as they might
% be referred to within foreign_procs that are inlined across module
% boundaries.
%
:- pred output_exports(java_out_info::in, indent::in,
list(mlds_pragma_export)::in, io::di, io::uo) is det.
output_exports(Info, Indent, Exports, !IO) :-
list.foldl(output_export(Info, Indent), Exports, !IO).
:- pred output_export(java_out_info::in, indent::in, mlds_pragma_export::in,
io::di, io::uo) is det.
output_export(Info0, Indent, Export, !IO) :-
Export = ml_pragma_export(Lang, ExportName, _, MLDS_Signature,
UnivQTVars, _),
expect(unify(Lang, lang_java), $module, $pred,
"foreign_export for language other than Java."),
indent_line(Indent, !IO),
io.write_string("public static ", !IO),
output_generic_tvars(UnivQTVars, !IO),
io.nl(!IO),
indent_line(Indent, !IO),
MLDS_Signature = mlds_func_params(Parameters, ReturnTypes),
Info = (Info0 ^ joi_output_generics := do_output_generics)
^ joi_univ_tvars := UnivQTVars,
(
ReturnTypes = [],
io.write_string("void", !IO)
;
ReturnTypes = [RetType],
output_type(Info, RetType, !IO)
;
ReturnTypes = [_, _ | _],
% For multiple outputs, we return an array of objects.
io.write_string("java.lang.Object []", !IO)
),
io.write_string(" " ++ ExportName, !IO),
(
list.member(Param, Parameters),
has_ptr_type(Param)
->
(
( ReturnTypes = []
; ReturnTypes = [_]
),
output_export_ref_out(Info, Indent, Export, !IO)
;
ReturnTypes = [_, _ | _],
unexpected($module, $pred, "multiple return values")
)
;
output_export_no_ref_out(Info, Indent, Export, !IO)
).
:- pred output_export_no_ref_out(java_out_info::in, indent::in,
mlds_pragma_export::in, io::di, io::uo) is det.
output_export_no_ref_out(Info, Indent, Export, !IO) :-
Export = ml_pragma_export(_Lang, _ExportName, MLDS_Name, MLDS_Signature,
_UnivQTVars, _MLDS_Context),
MLDS_Signature = mlds_func_params(Parameters, ReturnTypes),
output_params(Info, Indent + 1, Parameters, !IO),
io.nl(!IO),
indent_line(Indent, !IO),
io.write_string("{\n", !IO),
indent_line(Indent + 1, !IO),
(
ReturnTypes = []
;
ReturnTypes = [RetType],
% The cast is required when the exported method uses generics but the
% underlying method does not use generics (i.e. returns Object).
io.write_string("return (", !IO),
output_type(Info, RetType, !IO),
io.write_string(") ", !IO)
;
ReturnTypes = [_, _ | _],
io.write_string("return ", !IO)
),
write_export_call(MLDS_Name, Parameters, !IO),
indent_line(Indent, !IO),
io.write_string("}\n", !IO).
:- pred output_export_ref_out(java_out_info::in, indent::in,
mlds_pragma_export::in, io::di, io::uo) is det.
output_export_ref_out(Info, Indent, Export, !IO) :-
Export = ml_pragma_export(_Lang, _ExportName, MLDS_Name, MLDS_Signature,
_UnivQTVars, _MLDS_Context),
MLDS_Signature = mlds_func_params(Parameters, ReturnTypes),
list.filter(has_ptr_type, Parameters, RefParams, NonRefParams),
output_export_params_ref_out(Info, Indent, Parameters, !IO),
io.nl(!IO),
indent_line(Indent, !IO),
io.write_string("{\n", !IO),
indent_line(Indent + 1, !IO),
io.write_string("java.lang.Object[] results = ", !IO),
write_export_call(MLDS_Name, NonRefParams, !IO),
( ReturnTypes = [] ->
FirstRefArg = 0
; ReturnTypes = [mlds_native_bool_type] ->
% Semidet procedure.
FirstRefArg = 1
;
unexpected($module, $pred, "unexpected ReturnTypes")
),
list.foldl2(assign_ref_output(Info, Indent + 1), RefParams,
FirstRefArg, _, !IO),
(
FirstRefArg = 0
;
FirstRefArg = 1,
indent_line(Indent + 1, !IO),
Stmt = "return ((java.lang.Boolean) results[0]).booleanValue();\n",
io.write_string(Stmt, !IO)
),
indent_line(Indent, !IO),
io.write_string("}\n", !IO).
:- pred output_export_params_ref_out(java_out_info::in, indent::in,
list(mlds_argument)::in, io::di, io::uo) is det.
output_export_params_ref_out(Info, Indent, Parameters, !IO) :-
io.write_string("(", !IO),
(
Parameters = []
;
Parameters = [_ | _],
io.nl(!IO),
io.write_list(Parameters, ",\n",
output_export_param_ref_out(Info, Indent + 1), !IO)
),
io.write_string(")", !IO).
:- pred output_export_param_ref_out(java_out_info::in, indent::in,
mlds_argument::in, io::di, io::uo) is det.
output_export_param_ref_out(Info, Indent, Argument, !IO) :-
Argument = mlds_argument(Name, Type, _),
indent_line(Indent, !IO),
( Type = mlds_ptr_type(InnerType) ->
boxed_type_to_string(Info, InnerType, InnerTypeString),
io.format("jmercury.runtime.Ref<%s> ", [s(InnerTypeString)], !IO)
;
output_type(Info, Type, !IO),
io.write_string(" ", !IO)
),
output_name(Name, !IO).
:- pred write_export_call(mlds_qualified_entity_name::in,
list(mlds_argument)::in, io::di, io::uo) is det.
write_export_call(MLDS_Name, Parameters, !IO) :-
output_fully_qualified_thing(MLDS_Name, output_name, !IO),
io.write_char('(', !IO),
io.write_list(Parameters, ", ", write_argument_name, !IO),
io.write_string(");\n", !IO).
:- pred write_argument_name(mlds_argument::in, io::di, io::uo) is det.
write_argument_name(Arg, !IO) :-
Arg = mlds_argument(Name, _, _),
output_name(Name, !IO).
:- pred assign_ref_output(java_out_info::in, indent::in, mlds_argument::in,
int::in, int::out, io::di, io::uo) is det.
assign_ref_output(Info, Indent, Arg, N, N + 1, !IO) :-
Arg = mlds_argument(Name, Type, _),
indent_line(Indent, !IO),
output_name(Name, !IO),
( Type = mlds_ptr_type(InnerType) ->
boxed_type_to_string(Info, InnerType, TypeString)
;
boxed_type_to_string(Info, Type, TypeString)
),
io.format(".val = (%s) results[%d];\n", [s(TypeString), i(N)], !IO).
:- pred has_ptr_type(mlds_argument::in) is semidet.
has_ptr_type(mlds_argument(_, mlds_ptr_type(_), _)).
%-----------------------------------------------------------------------------%
%
% Code for handling `pragma foreign_export_enum' for Java.
%
:- pred output_exported_enums(java_out_info::in, indent::in,
list(mlds_exported_enum)::in, io::di, io::uo) is det.
output_exported_enums(Info, Indent, ExportedEnums, !IO) :-
list.foldl(output_exported_enum(Info, Indent), ExportedEnums, !IO).
:- pred output_exported_enum(java_out_info::in, indent::in,
mlds_exported_enum::in, io::di, io::uo) is det.
output_exported_enum(Info, Indent, ExportedEnum, !IO) :-
ExportedEnum = mlds_exported_enum(Lang, _, TypeCtor, ExportedConstants0),
(
Lang = lang_java,
ml_gen_type_name(TypeCtor, ClassName, ClassArity),
MLDS_Type = mlds_class_type(ClassName, ClassArity, mlds_enum),
% We reverse the list so the constants are printed out in order.
list.reverse(ExportedConstants0, ExportedConstants),
list.foldl(output_exported_enum_constant(Info, Indent, MLDS_Type),
ExportedConstants, !IO)
;
( Lang = lang_c
; Lang = lang_csharp
; Lang = lang_il
; Lang = lang_erlang
)
).
:- pred output_exported_enum_constant(java_out_info::in, indent::in,
mlds_type::in, mlds_exported_enum_constant::in, io::di, io::uo) is det.
output_exported_enum_constant(Info, Indent, MLDS_Type, ExportedConstant,
!IO) :-
ExportedConstant = mlds_exported_enum_constant(Name, Initializer),
indent_line(Indent, !IO),
io.write_string("public static final ", !IO),
output_type(Info, MLDS_Type, !IO),
io.write_string(" ", !IO),
io.write_string(Name, !IO),
io.write_string(" = ", !IO),
output_initializer_body(Info, Initializer, no, !IO),
io.write_string(";\n", !IO).
%-----------------------------------------------------------------------------%
%
% Code to search MLDS for all uses of function pointers.
%
% Returns code-address information (function label and signature)
% for each method/function which has its address taken in the MLDS.
%
:- pred find_pointer_addressed_methods(list(mlds_defn)::in,
list(mlds_code_addr)::in, list(mlds_code_addr)::out) is det.
find_pointer_addressed_methods([], !CodeAddrs).
find_pointer_addressed_methods([Defn | Defns], !CodeAddrs) :-
Defn = mlds_defn(_Name, _Context, _Flags, Body),
method_ptrs_in_entity_defn(Body, !CodeAddrs),
find_pointer_addressed_methods(Defns, !CodeAddrs).
:- pred find_pointer_addressed_methods_in_scalars(cord(mlds_initializer)::in,
list(mlds_code_addr)::in, list(mlds_code_addr)::out) is det.
find_pointer_addressed_methods_in_scalars(Cord, !CodeAddrs) :-
cord.foldl_pred(method_ptrs_in_initializer, Cord, !CodeAddrs).
:- pred method_ptrs_in_entity_defn(mlds_entity_defn::in,
list(mlds_code_addr)::in, list(mlds_code_addr)::out) is det.
method_ptrs_in_entity_defn(mlds_function(_MaybeID, _Params, Body,
_Attributes, _EnvVars), !CodeAddrs) :-
(
Body = body_defined_here(Statement),
method_ptrs_in_statement(Statement, !CodeAddrs)
;
Body = body_external
).
method_ptrs_in_entity_defn(mlds_data(_Type, Initializer, _GCStatement),
!CodeAddrs) :-
method_ptrs_in_initializer(Initializer, !CodeAddrs).
method_ptrs_in_entity_defn(mlds_class(ClassDefn), !CodeAddrs) :-
ClassDefn = mlds_class_defn(_, _, _, _, _, Ctors, Members),
method_ptrs_in_defns(Ctors, !CodeAddrs),
method_ptrs_in_defns(Members, !CodeAddrs).
:- pred method_ptrs_in_statements(list(statement)::in,
list(mlds_code_addr)::in, list(mlds_code_addr)::out) is det.
method_ptrs_in_statements([], !CodeAddrs).
method_ptrs_in_statements([Statement | Statements], !CodeAddrs) :-
method_ptrs_in_statement(Statement, !CodeAddrs),
method_ptrs_in_statements(Statements, !CodeAddrs).
:- pred method_ptrs_in_statement(statement::in,
list(mlds_code_addr)::in, list(mlds_code_addr)::out) is det.
method_ptrs_in_statement(statement(Stmt, _Context), !CodeAddrs) :-
method_ptrs_in_stmt(Stmt, !CodeAddrs).
:- pred method_ptrs_in_stmt(mlds_stmt::in,
list(mlds_code_addr)::in, list(mlds_code_addr)::out) is det.
method_ptrs_in_stmt(ml_stmt_block(Defns, Statements), !CodeAddrs) :-
method_ptrs_in_defns(Defns, !CodeAddrs),
method_ptrs_in_statements(Statements, !CodeAddrs).
method_ptrs_in_stmt(ml_stmt_while(_Kind, Rval, Statement), !CodeAddrs) :-
method_ptrs_in_rval(Rval, !CodeAddrs),
method_ptrs_in_statement(Statement, !CodeAddrs).
method_ptrs_in_stmt(ml_stmt_if_then_else(Rval, StatementThen,
MaybeStatementElse), !CodeAddrs) :-
method_ptrs_in_rval(Rval, !CodeAddrs),
method_ptrs_in_statement(StatementThen, !CodeAddrs),
(
MaybeStatementElse = yes(StatementElse),
method_ptrs_in_statement(StatementElse, !CodeAddrs)
;
MaybeStatementElse = no
).
method_ptrs_in_stmt(ml_stmt_switch(_Type, Rval, _Range, Cases, Default),
!CodeAddrs) :-
method_ptrs_in_rval(Rval, !CodeAddrs),
method_ptrs_in_switch_cases(Cases, !CodeAddrs),
method_ptrs_in_switch_default(Default, !CodeAddrs).
method_ptrs_in_stmt(ml_stmt_label(_), _, _) :-
unexpected($module, $pred, "labels not supported in Java.").
method_ptrs_in_stmt(ml_stmt_goto(goto_break), !CodeAddrs).
method_ptrs_in_stmt(ml_stmt_goto(goto_continue), !CodeAddrs).
method_ptrs_in_stmt(ml_stmt_goto(goto_label(_)), _, _) :-
unexpected($module, $pred, "goto label not supported in Java.").
method_ptrs_in_stmt(ml_stmt_computed_goto(_, _), _, _) :-
unexpected($module, $pred, "computed gotos not supported in Java.").
method_ptrs_in_stmt(ml_stmt_try_commit(_Lval, StatementGoal,
StatementHandler), !CodeAddrs) :-
% We don't check "_Lval" here as we expect it to be a local variable
% of type mlds_commit_type.
method_ptrs_in_statement(StatementGoal, !CodeAddrs),
method_ptrs_in_statement(StatementHandler, !CodeAddrs).
method_ptrs_in_stmt(ml_stmt_do_commit(_Rval), !CodeAddrs).
% We don't check "_Rval" here as we expect it to be a local variable
% of type mlds_commit_type.
method_ptrs_in_stmt(ml_stmt_return(Rvals), !CodeAddrs) :-
method_ptrs_in_rvals(Rvals, !CodeAddrs).
method_ptrs_in_stmt(CallStmt, !CodeAddrs) :-
CallStmt = ml_stmt_call(_FuncSig, _Rval, _MaybeThis, Rvals, _ReturnVars,
_IsTailCall),
% We don't check "_Rval" - it may be a code address but is a
% standard call rather than a function pointer use.
method_ptrs_in_rvals(Rvals, !CodeAddrs).
method_ptrs_in_stmt(ml_stmt_atomic(AtomicStatement), !CodeAddrs) :-
(
AtomicStatement = new_object(Lval, _MaybeTag, _Bool,
_Type, _MemRval, _MaybeCtorName, Rvals, _Types, _MayUseAtomic,
_AllocId)
->
% We don't need to check "_MemRval" since this just stores
% the amount of memory needed for the new object.
method_ptrs_in_lval(Lval, !CodeAddrs),
method_ptrs_in_rvals(Rvals, !CodeAddrs)
; AtomicStatement = assign(Lval, Rval) ->
method_ptrs_in_lval(Lval, !CodeAddrs),
method_ptrs_in_rval(Rval, !CodeAddrs)
;
true
).
:- pred method_ptrs_in_switch_default(mlds_switch_default::in,
list(mlds_code_addr)::in, list(mlds_code_addr)::out) is det.
method_ptrs_in_switch_default(default_is_unreachable, !CodeAddrs).
method_ptrs_in_switch_default(default_do_nothing, !CodeAddrs).
method_ptrs_in_switch_default(default_case(Statement), !CodeAddrs) :-
method_ptrs_in_statement(Statement, !CodeAddrs).
:- pred method_ptrs_in_switch_cases(list(mlds_switch_case)::in,
list(mlds_code_addr)::in, list(mlds_code_addr)::out) is det.
method_ptrs_in_switch_cases([], !CodeAddrs).
method_ptrs_in_switch_cases([Case | Cases], !CodeAddrs) :-
Case = mlds_switch_case(_FirstCond, _LaterConds, Statement),
method_ptrs_in_statement(Statement, !CodeAddrs),
method_ptrs_in_switch_cases(Cases, !CodeAddrs).
:- pred method_ptrs_in_defns(list(mlds_defn)::in, list(mlds_code_addr)::in,
list(mlds_code_addr)::out) is det.
method_ptrs_in_defns([], !CodeAddrs).
method_ptrs_in_defns([Defn | Defns], !CodeAddrs) :-
method_ptrs_in_defn(Defn, !CodeAddrs),
method_ptrs_in_defns(Defns, !CodeAddrs).
:- pred method_ptrs_in_defn(mlds_defn::in, list(mlds_code_addr)::in,
list(mlds_code_addr)::out) is det.
method_ptrs_in_defn(mlds_defn(_Name, _Context, _Flags, Body), !CodeAddrs) :-
method_ptrs_in_entity_defn(Body, !CodeAddrs).
:- pred method_ptrs_in_initializer(mlds_initializer::in,
list(mlds_code_addr)::in, list(mlds_code_addr)::out) is det.
method_ptrs_in_initializer(no_initializer, !CodeAddrs).
method_ptrs_in_initializer(init_struct(_Type, Initializers),
!CodeAddrs) :-
method_ptrs_in_initializers(Initializers, !CodeAddrs).
method_ptrs_in_initializer(init_array(Initializers), !CodeAddrs) :-
method_ptrs_in_initializers(Initializers, !CodeAddrs).
method_ptrs_in_initializer(init_obj(Rval), !CodeAddrs) :-
method_ptrs_in_rval(Rval, !CodeAddrs).
:- pred method_ptrs_in_initializers(list(mlds_initializer)::in,
list(mlds_code_addr)::in, list(mlds_code_addr)::out) is det.
method_ptrs_in_initializers([], !CodeAddrs).
method_ptrs_in_initializers([Initializer | Initializers], !CodeAddrs) :-
method_ptrs_in_initializer(Initializer, !CodeAddrs),
method_ptrs_in_initializers(Initializers, !CodeAddrs).
:- pred method_ptrs_in_rvals(list(mlds_rval)::in, list(mlds_code_addr)::in,
list(mlds_code_addr)::out) is det.
method_ptrs_in_rvals([], !CodeAddrs).
method_ptrs_in_rvals([Rval | Rvals], !CodeAddrs) :-
method_ptrs_in_rval(Rval, !CodeAddrs),
method_ptrs_in_rvals(Rvals, !CodeAddrs).
:- pred method_ptrs_in_rval(mlds_rval::in, list(mlds_code_addr)::in,
list(mlds_code_addr)::out) is det.
method_ptrs_in_rval(ml_lval(Lval), !CodeAddrs) :-
method_ptrs_in_lval(Lval, !CodeAddrs).
method_ptrs_in_rval(ml_mkword(_Tag, Rval), !CodeAddrs) :-
method_ptrs_in_rval(Rval, !CodeAddrs).
method_ptrs_in_rval(ml_const(RvalConst), !CodeAddrs) :-
(
RvalConst = mlconst_code_addr(CodeAddr),
!:CodeAddrs = [CodeAddr | !.CodeAddrs]
;
( RvalConst = mlconst_true
; RvalConst = mlconst_false
; RvalConst = mlconst_int(_)
; RvalConst = mlconst_char(_)
; RvalConst = mlconst_enum(_, _)
; RvalConst = mlconst_foreign(_, _, _)
; RvalConst = mlconst_float(_)
; RvalConst = mlconst_string(_)
; RvalConst = mlconst_multi_string(_)
; RvalConst = mlconst_named_const(_)
; RvalConst = mlconst_data_addr(_)
; RvalConst = mlconst_null(_)
)
).
method_ptrs_in_rval(ml_unop(_UnaryOp, Rval), !CodeAddrs) :-
method_ptrs_in_rval(Rval, !CodeAddrs).
method_ptrs_in_rval(ml_binop(_BinaryOp, RvalA, RvalB), !CodeAddrs) :-
method_ptrs_in_rval(RvalA, !CodeAddrs),
method_ptrs_in_rval(RvalB, !CodeAddrs).
method_ptrs_in_rval(ml_scalar_common(_), !CodeAddrs).
method_ptrs_in_rval(ml_vector_common_row(_, RowRval), !CodeAddrs) :-
method_ptrs_in_rval(RowRval, !CodeAddrs).
method_ptrs_in_rval(ml_mem_addr(_Address), !CodeAddrs).
method_ptrs_in_rval(ml_self(_Type), !CodeAddrs).
:- pred method_ptrs_in_lval(mlds_lval::in, list(mlds_code_addr)::in,
list(mlds_code_addr)::out) is det.
% Here, "_Rval" is the address of a variable so we don't check it.
method_ptrs_in_lval(ml_mem_ref(_Rval, _Type), !CodeAddrs).
% Here, "_Rval" is a pointer to a cell on the heap, and doesn't need
% to be considered.
method_ptrs_in_lval(ml_field(_MaybeTag, _Rval, _FieldId, _FieldType, _PtrType),
!CodeAddrs).
method_ptrs_in_lval(ml_var(_Variable, _Type), !CodeAddrs).
method_ptrs_in_lval(ml_global_var_ref(_), !CodeAddrs).
%-----------------------------------------------------------------------------%
%
% Code to output wrapper classes for the implementation of function pointers
% in Java.
%
% As there is no way to take the address of a method in Java, we must create a
% wrapper for that method which implements a common interface. We are then able
% to pass that class around as a java.lang.Object.
%
% XXX This implementation will not handle taking the address of instance
% methods. This is not currently a problem as they will never be generated
% by the MLDS back-end.
%
% XXX This implementation will not corectly handle the case which occurs where
% there are two or more overloaded MLDS functions (that we take the address of)
% with the same name and arity but different argument types, both in the same
% module. This is due to the fact that the names of the generated wrapper
% classes are based purely on the method name.
:- type call_method_inputs
---> cmi_separate(list(mlds_var_name))
; cmi_array(mlds_var_name).
:- pred make_code_addr_map(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([], !Map).
make_code_addr_map([CodeAddr | CodeAddrs], !Map) :-
(
CodeAddr = code_addr_proc(_ProcLabel, OrigFuncSignature)
;
CodeAddr = code_addr_internal(_ProcLabel, _SeqNum, OrigFuncSignature)
),
OrigFuncSignature = mlds_func_signature(OrigArgTypes, _OrigRetTypes),
list.length(OrigArgTypes, Arity),
multi_map.set(Arity, CodeAddr, !Map),
make_code_addr_map(CodeAddrs, !Map).
:- pred generate_addr_wrapper_class(mlds_module_name::in,
pair(arity, list(mlds_code_addr))::in, mlds_defn::out,
map(mlds_code_addr, code_addr_wrapper)::in,
map(mlds_code_addr, code_addr_wrapper)::out) is det.
generate_addr_wrapper_class(MLDS_ModuleName, Arity - CodeAddrs, ClassDefn,
!AddrOfMap) :-
% Create a name for this wrapper class based on the fully qualified method
% (predicate) name.
ClassName = "addrOf" ++ string.from_int(Arity),
% If the class is wrapping more than one method then add a member variable
% which says which predicate to call, and a constructor function to
% initialise that variable.
(
CodeAddrs = [_],
DataDefns = [],
CtorDefns = []
;
CodeAddrs = [_, _ | _],
Context = mlds_make_context(term.context_init),
% Create the member variable.
DataDefn = mlds_defn(
entity_data(mlds_data_var(mlds_var_name("ptr_num", no))),
Context, ml_gen_const_member_decl_flags,
mlds_data(mlds_native_int_type, no_initializer, gc_no_stmt)),
DataDefns = [DataDefn],
% Create the constructor function.
QualClassName = qual(MLDS_ModuleName, module_qual, ClassName),
ClassType = mlds_class_type(QualClassName, 0, mlds_class),
FieldName = qual(MLDS_ModuleName, type_qual, "ptr_num"),
FieldId = ml_field_named(FieldName, ClassType),
FieldLval = ml_field(no, ml_self(ClassType), FieldId,
mlds_native_int_type, ClassType),
CtorArgName = mlds_var_name("ptr_num", no),
CtorArg = entity_data(mlds_data_var(CtorArgName)),
CtorArgs = [mlds_argument(CtorArg, mlds_native_int_type, gc_no_stmt)],
CtorReturnValues = [],
CtorArgLval = ml_var(qual(MLDS_ModuleName, type_qual, CtorArgName),
mlds_native_int_type),
CtorArgRval = ml_lval(CtorArgLval),
CtorStatement = statement(
ml_stmt_atomic(assign(FieldLval, CtorArgRval)), Context),
Attributes = [],
EnvVarNames = set.init,
Ctor = mlds_function(no, mlds_func_params(CtorArgs, CtorReturnValues),
body_defined_here(CtorStatement), Attributes, EnvVarNames),
CtorFlags = init_decl_flags(acc_public, per_instance, non_virtual,
overridable, modifiable, concrete),
CtorDefn = mlds_defn(entity_export("<constructor>"), Context,
CtorFlags, Ctor),
CtorDefns = [CtorDefn]
;
CodeAddrs = [],
unexpected($module, $pred, "no addresses")
),
% Create a method that calls the original predicates.
generate_call_method(MLDS_ModuleName, Arity, CodeAddrs, MethodDefn),
( Arity =< max_specialised_method_ptr_arity ->
InterfaceName = "MethodPtr" ++ string.from_int(Arity)
;
InterfaceName = "MethodPtrN"
),
InterfaceModuleName = mercury_module_name_to_mlds(
java_mercury_runtime_package_name),
Interface = qual(InterfaceModuleName, module_qual, InterfaceName),
% Create class components.
ClassImports = [],
ClassExtends = [],
InterfaceDefn = mlds_class_type(Interface, 0, mlds_interface),
ClassImplements = [InterfaceDefn],
TypeParams = [],
% Put it all together.
ClassMembers = DataDefns ++ [MethodDefn],
ClassEntityName = entity_type(ClassName, 0),
ClassContext = mlds_make_context(term.context_init),
ClassFlags = addr_wrapper_decl_flags,
ClassBodyDefn = mlds_class_defn(mlds_class, ClassImports, ClassExtends,
ClassImplements, TypeParams, CtorDefns, ClassMembers),
ClassBody = mlds_class(ClassBodyDefn),
ClassDefn = mlds_defn(ClassEntityName, ClassContext, ClassFlags,
ClassBody),
add_to_address_map(ClassName, CodeAddrs, !AddrOfMap).
% The highest arity for which there is a specialised MethodPtr<n> interface.
%
:- func max_specialised_method_ptr_arity = int.
max_specialised_method_ptr_arity = 15.
:- pred generate_call_method(mlds_module_name::in, arity::in,
list(mlds_code_addr)::in, mlds_defn::out) is det.
generate_call_method(MLDS_ModuleName, Arity, CodeAddrs, MethodDefn) :-
% Create the arguments to the call method. For low arities the method
% takes n arguments directly. For higher arities the arguments are passed
% in as an array.
( Arity =< max_specialised_method_ptr_arity ->
list.map2(create_generic_arg, 1 .. Arity, ArgNames, MethodArgs),
InputArgs = cmi_separate(ArgNames)
;
ArgName = mlds_var_name("args", no),
ArgDataName = entity_data(mlds_data_var(ArgName)),
ArgType = mlds_array_type(mlds_generic_type),
Arg = mlds_argument(ArgDataName, ArgType, gc_no_stmt),
MethodArgs = [Arg],
InputArgs = cmi_array(ArgName)
),
% Create a statement to call each of the original methods.
list.map(generate_call_statement_for_addr(InputArgs), CodeAddrs,
CodeAddrStatements),
Context = mlds_make_context(term.context_init),
% If there is more than one original method then we need to switch on the
% ptr_num member variable.
(
CodeAddrStatements = [Statement]
;
CodeAddrStatements = [_, _ | _],
MaxCase = list.length(CodeAddrs) - 1,
MakeCase = (func(I, CaseStatement) = Case :-
MatchCond = match_value(ml_const(mlconst_int(I))),
Case = mlds_switch_case(MatchCond, [], CaseStatement)
),
Cases = list.map_corresponding(MakeCase, 0 .. MaxCase,
CodeAddrStatements),
SwitchVarName = mlds_var_name("ptr_num", no),
SwitchVar = qual(MLDS_ModuleName, module_qual, SwitchVarName),
SwitchVarRval = ml_lval(ml_var(SwitchVar, mlds_native_int_type)),
SwitchRange = mlds_switch_range(0, MaxCase),
Switch = ml_stmt_switch(mlds_native_int_type, SwitchVarRval,
SwitchRange, Cases, default_is_unreachable),
Statement = statement(Switch, Context)
;
CodeAddrStatements = [],
unexpected($module, $pred, "no statements")
),
% Create new method name.
PredID = hlds_pred.initial_pred_id,
ProcID = initial_proc_id,
Label = mlds_special_pred_label("call", no, "", 0),
MethodName = entity_function(Label, ProcID, no, PredID),
% Create return type.
MethodRetType = mlds_generic_type,
MethodRets = [MethodRetType],
% Put it all together.
MethodParams = mlds_func_params(MethodArgs, MethodRets),
MethodMaybeID = no,
MethodAttribs = [],
MethodEnvVarNames = set.init,
MethodBody = mlds_function(MethodMaybeID, MethodParams,
body_defined_here(Statement), MethodAttribs, MethodEnvVarNames),
MethodFlags = ml_gen_member_decl_flags,
MethodDefn = mlds_defn(MethodName, Context, MethodFlags, MethodBody).
:- pred create_generic_arg(int::in, mlds_var_name::out, mlds_argument::out)
is det.
create_generic_arg(I, ArgName, Arg) :-
ArgName = mlds_var_name("arg" ++ string.from_int(I), no),
Arg = mlds_argument(entity_data(mlds_data_var(ArgName)),
mlds_generic_type, gc_no_stmt).
:- pred generate_call_statement_for_addr(call_method_inputs::in,
mlds_code_addr::in, statement::out) is det.
generate_call_statement_for_addr(InputArgs, CodeAddr, Statement) :-
(
CodeAddr = code_addr_proc(ProcLabel, OrigFuncSignature)
;
CodeAddr = code_addr_internal(ProcLabel, _SeqNum, OrigFuncSignature)
),
OrigFuncSignature = mlds_func_signature(OrigArgTypes, OrigRetTypes),
ModuleName = ProcLabel ^ mod_name,
% Create the arguments to pass to the original method.
(
InputArgs = cmi_separate(ArgNames),
list.map_corresponding(generate_call_method_nth_arg(ModuleName),
OrigArgTypes, ArgNames, CallArgs)
;
InputArgs = cmi_array(ArrayVarName),
ArrayVar = qual(ModuleName, module_qual, ArrayVarName),
generate_call_method_args_from_array(OrigArgTypes, ArrayVar, 0,
[], CallArgs)
),
% Create a temporary variable to store the result of the call to the
% original method.
ReturnVarName = mlds_var_name("return_value", no),
ReturnVar = qual(ModuleName, module_qual, ReturnVarName),
% Create a declaration for this variable.
(
OrigRetTypes = [],
ReturnVarType = mlds_generic_type
;
OrigRetTypes = [CallRetType],
ReturnVarType = CallRetType
;
OrigRetTypes = [_, _ | _],
ReturnVarType = mlds_array_type(mlds_generic_type)
),
ReturnLval = ml_var(ReturnVar, ReturnVarType),
ReturnEntityName = entity_data(mlds_data_var(ReturnVarName)),
ReturnDecFlags = ml_gen_local_var_decl_flags,
GCStatement = gc_no_stmt, % The Java back-end does its own GC.
ReturnEntityDefn = mlds_data(ReturnVarType, no_initializer, GCStatement),
Context = mlds_make_context(term.context_init),
ReturnVarDefn = mlds_defn(ReturnEntityName, Context, ReturnDecFlags,
ReturnEntityDefn),
MethodDefns = [ReturnVarDefn],
% Create the call to the original method.
CallRval = ml_const(mlconst_code_addr(CodeAddr)),
% If the original method has a return type of void, then we obviously
% cannot assign its return value to "return_value". Thus, in this
% case the value returned by the call method will just be the value
% which "return_value" was initialised to.
(
OrigRetTypes = [],
CallRetLvals = []
;
OrigRetTypes = [_ | _],
CallRetLvals = [ReturnLval]
),
Call = ml_stmt_call(OrigFuncSignature, CallRval, no, CallArgs,
CallRetLvals, ordinary_call),
CallStatement = statement(Call, Context),
% Create a return statement that returns the result of the call to the
% original method, boxed as a java.lang.Object.
ReturnRval = ml_unop(box(ReturnVarType), ml_lval(ReturnLval)),
Return = ml_stmt_return([ReturnRval]),
ReturnStatement = statement(Return, Context),
Block = ml_stmt_block(MethodDefns, [CallStatement, ReturnStatement]),
Statement = statement(Block, Context).
:- pred generate_call_method_nth_arg(mlds_module_name::in, mlds_type::in,
mlds_var_name::in, mlds_rval::out) is det.
generate_call_method_nth_arg(ModuleName, Type, MethodArgVariable, CallArg) :-
CallArgLabel = qual(ModuleName, module_qual, MethodArgVariable),
Rval = ml_lval(ml_var(CallArgLabel, mlds_generic_type)),
CallArg = ml_unop(unbox(Type), Rval).
:- pred generate_call_method_args_from_array(list(mlds_type)::in,
mlds_var::in, int::in, list(mlds_rval)::in, list(mlds_rval)::out) is det.
generate_call_method_args_from_array([], _, _, Args, Args).
generate_call_method_args_from_array([Type | Types], ArrayVar, Counter,
Args0, Args) :-
ArrayRval = ml_lval(ml_var(ArrayVar, mlds_native_int_type)),
IndexRval = ml_const(mlconst_int(Counter)),
ElemType = array_elem_scalar(scalar_elem_generic),
Rval = ml_binop(array_index(ElemType), ArrayRval, IndexRval),
UnBoxedRval = ml_unop(unbox(Type), Rval),
Args1 = Args0 ++ [UnBoxedRval],
generate_call_method_args_from_array(Types, ArrayVar, Counter + 1,
Args1, Args).
:- func addr_wrapper_decl_flags = mlds_decl_flags.
addr_wrapper_decl_flags = MLDS_DeclFlags :-
Access = acc_private,
PerInstance = one_copy,
Virtuality = non_virtual,
Overridability = sealed,
Constness = const,
Abstractness = concrete,
MLDS_DeclFlags = init_decl_flags(Access, PerInstance,
Virtuality, Overridability, Constness, Abstractness).
:- pred add_to_address_map(string::in, list(mlds_code_addr)::in,
map(mlds_code_addr, code_addr_wrapper)::in,
map(mlds_code_addr, code_addr_wrapper)::out) is det.
add_to_address_map(ClassName, CodeAddrs, !AddrOfMap) :-
FlippedClassName = flip_initial_case(ClassName),
(
CodeAddrs = [CodeAddr],
Wrapper = code_addr_wrapper(FlippedClassName, no),
map.det_insert(CodeAddr, Wrapper, !AddrOfMap)
;
CodeAddrs = [_, _ | _],
add_to_address_map_2(FlippedClassName, CodeAddrs, 0, !AddrOfMap)
;
CodeAddrs = [],
unexpected($module, $pred, "no addresses")
).
:- pred add_to_address_map_2(string::in, list(mlds_code_addr)::in, int::in,
map(mlds_code_addr, code_addr_wrapper)::in,
map(mlds_code_addr, code_addr_wrapper)::out) is det.
add_to_address_map_2(_, [], _, !AddrOfMap).
add_to_address_map_2(FlippedClassName, [CodeAddr | CodeAddrs], I,
!AddrOfMap) :-
Wrapper = code_addr_wrapper(FlippedClassName, yes(I)),
map.det_insert(CodeAddr, Wrapper, !AddrOfMap),
add_to_address_map_2(FlippedClassName, CodeAddrs, I + 1, !AddrOfMap).
%-----------------------------------------------------------------------------%
%
% Code to rename long class names.
%
:- type class_name_renaming
---> class_name_renaming(
cnr_module :: mlds_module_name,
cnr_renaming :: map(mlds_class_name, mlds_class_name)
).
% Rename class names which are too long. Each class results in a separate
% `.class' file, so a long class name may exceed filesystem limits.
% The long names tend to be automatically generated by the compiler.
%
:- pred shorten_long_class_names(mlds_module_name::in,
list(mlds_defn)::in, list(mlds_defn)::out) is det.
shorten_long_class_names(ModuleName, Defns0, Defns) :-
list.map_foldl(maybe_shorten_long_class_name, Defns0, Defns1,
map.init, RenamingMap),
( map.is_empty(RenamingMap) ->
Defns = Defns1
;
Renaming = class_name_renaming(ModuleName, RenamingMap),
list.map(rename_class_names_defn(Renaming), Defns1, Defns)
).
:- pred maybe_shorten_long_class_name(mlds_defn::in, mlds_defn::out,
map(mlds_class_name, mlds_class_name)::in,
map(mlds_class_name, mlds_class_name)::out) is det.
maybe_shorten_long_class_name(!Defn, !Renaming) :-
Access = access(!.Defn ^ md_decl_flags),
(
% We only rename private classes for now.
Access = acc_private,
EntityName0 = !.Defn ^ md_entity_name,
(
EntityName0 = entity_type(ClassName0, Arity),
ClassName = shorten_class_name(ClassName0),
( ClassName \= ClassName0 ->
EntityName = entity_type(ClassName, Arity),
!Defn ^ md_entity_name := EntityName,
map.det_insert(ClassName0, ClassName, !Renaming)
;
true
)
;
( EntityName0 = entity_function(_, _, _, _)
; EntityName0 = entity_data(_)
; EntityName0 = entity_export(_)
)
)
;
( Access = acc_public
; Access = acc_protected
; Access = acc_default
; Access = acc_local
)
).
:- func shorten_class_name(string) = string.
shorten_class_name(ClassName0) = ClassName :-
MangledClassName0 = name_mangle_no_leading_digit(ClassName0),
( string.length(MangledClassName0) < 100 ->
ClassName = ClassName0
;
% The new name must not require name mangling, as then the name may
% again be too long. We replace all non-alphanumeric or underscore
% characters by underscores. The s_ prefix avoids having f_ as the
% prefix which is used to indicate a mangled name.
Left = string.left(ClassName0, 44),
Right = string.right(ClassName0, 44),
Hash = string.hash(ClassName0) /\ 0xffffffff,
GenName = string.format("s_%s_%08x_%s", [s(Left), i(Hash), s(Right)]),
GenList = string.to_char_list(GenName),
FilterList = list.map(replace_non_alphanum_underscore, GenList),
ClassName = string.from_char_list(FilterList)
).
:- func replace_non_alphanum_underscore(char) = char.
replace_non_alphanum_underscore(Char) =
( char.is_alnum_or_underscore(Char) ->
Char
;
'_'
).
:- pred rename_class_names_defn(class_name_renaming::in,
mlds_defn::in, mlds_defn::out) is det.
rename_class_names_defn(Renaming, !Defn) :-
EntityDefn0 = !.Defn ^ md_entity_defn,
(
EntityDefn0 = mlds_data(Type0, Initializer0, GCStatement),
rename_class_names_type(Renaming, Type0, Type),
rename_class_names_initializer(Renaming, Initializer0, Initializer),
EntityDefn = mlds_data(Type, Initializer, GCStatement)
;
EntityDefn0 = mlds_function(MaybePPId, FuncParams0, FuncBody0,
Attributes, EnvVarNames),
rename_class_names_func_params(Renaming, FuncParams0, FuncParams),
(
FuncBody0 = body_defined_here(Statement0),
rename_class_names_statement(Renaming, Statement0, Statement),
FuncBody = body_defined_here(Statement)
;
FuncBody0 = body_external,
FuncBody = body_external
),
EntityDefn = mlds_function(MaybePPId, FuncParams, FuncBody,
Attributes, EnvVarNames)
;
EntityDefn0 = mlds_class(mlds_class_defn(ClassKind, Imports, Inherits,
Implements, TypeParams, Ctors0, Members0)),
list.map(rename_class_names_defn(Renaming), Ctors0, Ctors),
list.map(rename_class_names_defn(Renaming), Members0, Members),
EntityDefn = mlds_class(mlds_class_defn(ClassKind, Imports, Inherits,
Implements, TypeParams, Ctors, Members))
),
!Defn ^ md_entity_defn := EntityDefn.
:- pred rename_class_names_type(class_name_renaming::in,
mlds_type::in, mlds_type::out) is det.
rename_class_names_type(Renaming, !Type) :-
(
!.Type = mlds_mercury_array_type(Type0),
rename_class_names_type(Renaming, Type0, Type),
!:Type = mlds_mercury_array_type(Type)
;
!.Type = mlds_cont_type(RetTypes0),
list.map(rename_class_names_type(Renaming), RetTypes0, RetTypes),
!:Type = mlds_cont_type(RetTypes)
;
!.Type = mlds_class_type(Name0, Arity, ClassKind),
Name0 = qual(ModuleName, QualKind, UnqualName0),
(
Renaming = class_name_renaming(ModuleName, RenamingMap),
map.search(RenamingMap, UnqualName0, UnqualName)
->
Name = qual(ModuleName, QualKind, UnqualName),
!:Type = mlds_class_type(Name, Arity, ClassKind)
;
true
)
;
!.Type = mlds_array_type(Type0),
rename_class_names_type(Renaming, Type0, Type),
!:Type = mlds_array_type(Type)
;
!.Type = mlds_ptr_type(Type0),
rename_class_names_type(Renaming, Type0, Type),
!:Type = mlds_ptr_type(Type)
;
!.Type = mlds_func_type(FuncParams0),
rename_class_names_func_params(Renaming, FuncParams0, FuncParams),
!:Type = mlds_func_type(FuncParams)
;
( !.Type = mercury_type(_, _, _)
; !.Type = mlds_commit_type
; !.Type = mlds_native_bool_type
; !.Type = mlds_native_int_type
; !.Type = mlds_native_float_type
; !.Type = mlds_native_char_type
; !.Type = mlds_foreign_type(_)
; !.Type = mlds_generic_type
; !.Type = mlds_generic_env_ptr_type
; !.Type = mlds_type_info_type
; !.Type = mlds_pseudo_type_info_type
; !.Type = mlds_rtti_type(_)
; !.Type = mlds_tabling_type(_)
; !.Type = mlds_unknown_type
)
).
:- pred rename_class_names_initializer(class_name_renaming::in,
mlds_initializer::in, mlds_initializer::out) is det.
rename_class_names_initializer(Renaming, !Initializer) :-
(
!.Initializer = init_obj(Rval0),
rename_class_names_rval(Renaming, Rval0, Rval),
!:Initializer = init_obj(Rval)
;
!.Initializer = init_struct(Type0, Initializers0),
rename_class_names_type(Renaming, Type0, Type),
list.map(rename_class_names_initializer(Renaming), Initializers0,
Initializers),
!:Initializer = init_struct(Type, Initializers)
;
!.Initializer = init_array(Initializers0),
list.map(rename_class_names_initializer(Renaming), Initializers0,
Initializers),
!:Initializer = init_array(Initializers)
;
!.Initializer = no_initializer
).
:- pred rename_class_names_func_params(class_name_renaming::in,
mlds_func_params::in, mlds_func_params::out) is det.
rename_class_names_func_params(Renaming, !FuncParams) :-
!.FuncParams = mlds_func_params(Arguments0, RetTypes0),
list.map(rename_class_names_argument(Renaming), Arguments0, Arguments),
list.map(rename_class_names_type(Renaming), RetTypes0, RetTypes),
!:FuncParams = mlds_func_params(Arguments, RetTypes).
:- pred rename_class_names_argument(class_name_renaming::in,
mlds_argument::in, mlds_argument::out) is det.
rename_class_names_argument(Renaming, !Argument) :-
!.Argument = mlds_argument(Name, Type0, GCStatement),
rename_class_names_type(Renaming, Type0, Type),
!:Argument = mlds_argument(Name, Type, GCStatement).
:- pred rename_class_names_statement(class_name_renaming::in,
statement::in, statement::out) is det.
rename_class_names_statement(Renaming, !Statement) :-
!.Statement = statement(Stmt0, Context),
rename_class_names_stmt(Renaming, Stmt0, Stmt),
!:Statement = statement(Stmt, Context).
:- pred rename_class_names_stmt(class_name_renaming::in,
mlds_stmt::in, mlds_stmt::out) is det.
rename_class_names_stmt(Renaming, !Stmt) :-
(
!.Stmt = ml_stmt_block(Defns0, Statements0),
list.map(rename_class_names_defn(Renaming), Defns0, Defns),
list.map(rename_class_names_statement(Renaming),
Statements0, Statements),
!:Stmt = ml_stmt_block(Defns, Statements)
;
!.Stmt = ml_stmt_while(Kind, Rval0, Statement0),
rename_class_names_rval(Renaming, Rval0, Rval),
rename_class_names_statement(Renaming, Statement0, Statement),
!:Stmt = ml_stmt_while(Kind, Rval, Statement)
;
!.Stmt = ml_stmt_if_then_else(Rval0, Statement0, MaybeElse0),
rename_class_names_rval(Renaming, Rval0, Rval),
rename_class_names_statement(Renaming, Statement0, Statement),
(
MaybeElse0 = yes(Else0),
rename_class_names_statement(Renaming, Else0, Else),
MaybeElse = yes(Else)
;
MaybeElse0 = no,
MaybeElse = no
),
!:Stmt = ml_stmt_if_then_else(Rval, Statement, MaybeElse)
;
!.Stmt = ml_stmt_switch(Type0, Rval0, SwitchRange, Cases0, Default0),
rename_class_names_type(Renaming, Type0, Type),
rename_class_names_rval(Renaming, Rval0, Rval),
list.map(rename_class_names_switch_case(Renaming), Cases0, Cases),
rename_class_names_switch_default(Renaming, Default0, Default),
!:Stmt = ml_stmt_switch(Type, Rval, SwitchRange, Cases, Default)
;
!.Stmt = ml_stmt_label(_)
;
!.Stmt = ml_stmt_goto(_)
;
!.Stmt = ml_stmt_computed_goto(Rval0, Labels),
rename_class_names_rval(Renaming, Rval0, Rval),
!:Stmt = ml_stmt_computed_goto(Rval, Labels)
;
!.Stmt = ml_stmt_call(Signature0, Rval0, MaybeThis, Rvals0, RetLvals0,
CallKind),
Signature0 = mlds_func_signature(ArgTypes0, RetTypes0),
list.map(rename_class_names_type(Renaming), ArgTypes0, ArgTypes),
list.map(rename_class_names_type(Renaming), RetTypes0, RetTypes),
Signature = mlds_func_signature(ArgTypes, RetTypes),
rename_class_names_rval(Renaming, Rval0, Rval),
list.map(rename_class_names_rval(Renaming), Rvals0, Rvals),
list.map(rename_class_names_lval(Renaming), RetLvals0, RetLvals),
!:Stmt = ml_stmt_call(Signature, Rval, MaybeThis, Rvals, RetLvals,
CallKind)
;
!.Stmt = ml_stmt_return(Rvals0),
list.map(rename_class_names_rval(Renaming), Rvals0, Rvals),
!:Stmt = ml_stmt_return(Rvals)
;
!.Stmt = ml_stmt_try_commit(Lval0, StatementA0, StatementB0),
rename_class_names_lval(Renaming, Lval0, Lval),
rename_class_names_statement(Renaming, StatementA0, StatementA),
rename_class_names_statement(Renaming, StatementB0, StatementB),
!:Stmt = ml_stmt_try_commit(Lval, StatementA, StatementB)
;
!.Stmt = ml_stmt_do_commit(Rval0),
rename_class_names_rval(Renaming, Rval0, Rval),
!:Stmt = ml_stmt_do_commit(Rval)
;
!.Stmt = ml_stmt_atomic(AtomicStatement0),
rename_class_names_atomic(Renaming, AtomicStatement0, AtomicStatement),
!:Stmt = ml_stmt_atomic(AtomicStatement)
).
:- pred rename_class_names_switch_case(class_name_renaming::in,
mlds_switch_case::in, mlds_switch_case::out) is det.
rename_class_names_switch_case(Renaming, !Case) :-
!.Case = mlds_switch_case(FirstMatchCond, LaterMatchConds, Statement0),
% The rvals in the match conditions shouldn't need renaming.
rename_class_names_statement(Renaming, Statement0, Statement),
!:Case = mlds_switch_case(FirstMatchCond, LaterMatchConds, Statement).
:- pred rename_class_names_switch_default(class_name_renaming::in,
mlds_switch_default::in, mlds_switch_default::out) is det.
rename_class_names_switch_default(Renaming, !Default) :-
(
!.Default = default_is_unreachable
;
!.Default = default_do_nothing
;
!.Default = default_case(Statement0),
rename_class_names_statement(Renaming, Statement0, Statement),
!:Default = default_case(Statement)
).
:- pred rename_class_names_atomic(class_name_renaming::in,
mlds_atomic_statement::in, mlds_atomic_statement::out) is det.
rename_class_names_atomic(Renaming, !Statement) :-
(
!.Statement = assign(Lval0, Rval0),
rename_class_names_lval(Renaming, Lval0, Lval),
rename_class_names_rval(Renaming, Rval0, Rval),
!:Statement = assign(Lval, Rval)
;
!.Statement = assign_if_in_heap(Lval0, Rval0),
rename_class_names_lval(Renaming, Lval0, Lval),
rename_class_names_rval(Renaming, Rval0, Rval),
!:Statement = assign_if_in_heap(Lval, Rval)
;
!.Statement = delete_object(Rval0),
rename_class_names_rval(Renaming, Rval0, Rval),
!:Statement = delete_object(Rval)
;
!.Statement = new_object(TargetLval0, MaybeTag, ExplicitSecTag, Type0,
MaybeSize, MaybeCtorName, Args0, ArgTypes0, MayUseAtomic, AllocId),
rename_class_names_lval(Renaming, TargetLval0, TargetLval),
rename_class_names_type(Renaming, Type0, Type),
list.map(rename_class_names_rval(Renaming), Args0, Args),
list.map(rename_class_names_type(Renaming), ArgTypes0, ArgTypes),
!:Statement = new_object(TargetLval, MaybeTag, ExplicitSecTag, Type,
MaybeSize, MaybeCtorName, Args, ArgTypes, MayUseAtomic, AllocId)
;
!.Statement = inline_target_code(Lang, Components0),
(
Lang = ml_target_java,
list.map(rename_class_names_target_code_component(Renaming),
Components0, Components),
!:Statement = inline_target_code(Lang, Components)
;
( Lang = ml_target_c
; Lang = ml_target_gnu_c
; Lang = ml_target_asm
; Lang = ml_target_il
; Lang = ml_target_csharp
)
)
;
( !.Statement = comment(_)
; !.Statement = gc_check
; !.Statement = mark_hp(_)
; !.Statement = restore_hp(_)
; !.Statement = trail_op(_)
; !.Statement = outline_foreign_proc(_, _, _, _)
)
).
:- pred rename_class_names_lval(class_name_renaming::in,
mlds_lval::in, mlds_lval::out) is det.
rename_class_names_lval(Renaming, !Lval) :-
(
!.Lval = ml_field(Tag, Address0, FieldId0, FieldType0, PtrType0),
rename_class_names_rval(Renaming, Address0, Address),
rename_class_names_field_id(Renaming, FieldId0, FieldId),
rename_class_names_type(Renaming, FieldType0, FieldType),
rename_class_names_type(Renaming, PtrType0, PtrType),
!:Lval = ml_field(Tag, Address, FieldId, FieldType, PtrType)
;
!.Lval = ml_mem_ref(Rval0, Type0),
rename_class_names_rval(Renaming, Rval0, Rval),
rename_class_names_type(Renaming, Type0, Type),
!:Lval = ml_mem_ref(Rval, Type)
;
!.Lval = ml_global_var_ref(_)
;
!.Lval = ml_var(Var, Type0),
rename_class_names_type(Renaming, Type0, Type),
!:Lval = ml_var(Var, Type)
).
:- pred rename_class_names_field_id(class_name_renaming::in,
mlds_field_id::in, mlds_field_id::out) is det.
rename_class_names_field_id(Renaming, !FieldId) :-
(
!.FieldId = ml_field_offset(Rval0),
rename_class_names_rval(Renaming, Rval0, Rval),
!:FieldId = ml_field_offset(Rval)
;
!.FieldId = ml_field_named(Name, Type0),
rename_class_names_type(Renaming, Type0, Type),
!:FieldId = ml_field_named(Name, Type)
).
:- pred rename_class_names_rval(class_name_renaming::in,
mlds_rval::in, mlds_rval::out) is det.
rename_class_names_rval(Renaming, !Rval) :-
(
!.Rval = ml_lval(Lval0),
rename_class_names_lval(Renaming, Lval0, Lval),
!:Rval = ml_lval(Lval)
;
!.Rval = ml_mkword(Tag, Rval0),
rename_class_names_rval(Renaming, Rval0, Rval),
!:Rval = ml_mkword(Tag, Rval)
;
!.Rval = ml_const(RvalConst0),
rename_class_names_rval_const(Renaming, RvalConst0, RvalConst),
!:Rval = ml_const(RvalConst)
;
!.Rval = ml_unop(Op0, Rval0),
rename_class_names_unary_op(Renaming, Op0, Op),
rename_class_names_rval(Renaming, Rval0, Rval),
!:Rval = ml_unop(Op, Rval)
;
!.Rval = ml_binop(Op, RvalA0, RvalB0),
rename_class_names_rval(Renaming, RvalA0, RvalA),
rename_class_names_rval(Renaming, RvalB0, RvalB),
!:Rval = ml_binop(Op, RvalA, RvalB)
;
!.Rval = ml_mem_addr(Lval0),
rename_class_names_lval(Renaming, Lval0, Lval),
!:Rval = ml_mem_addr(Lval)
;
!.Rval = ml_scalar_common(_)
;
!.Rval = ml_vector_common_row(VectorCommon, RowRval0),
rename_class_names_rval(Renaming, RowRval0, RowRval),
!:Rval = ml_vector_common_row(VectorCommon, RowRval)
;
!.Rval = ml_self(Type0),
rename_class_names_type(Renaming, Type0, Type),
!:Rval = ml_self(Type)
).
:- pred rename_class_names_rval_const(class_name_renaming::in,
mlds_rval_const::in, mlds_rval_const::out) is det.
rename_class_names_rval_const(Renaming, !Const) :-
(
!.Const = mlconst_foreign(Lang, String, Type0),
rename_class_names_type(Renaming, Type0, Type),
!:Const = mlconst_foreign(Lang, String, Type)
;
!.Const = mlconst_null(Type0),
rename_class_names_type(Renaming, Type0, Type),
!:Const = mlconst_null(Type)
;
( !.Const = mlconst_true
; !.Const = mlconst_false
; !.Const = mlconst_int(_)
; !.Const = mlconst_char(_)
; !.Const = mlconst_enum(_, _)
; !.Const = mlconst_float(_)
; !.Const = mlconst_string(_)
; !.Const = mlconst_multi_string(_)
; !.Const = mlconst_named_const(_)
; !.Const = mlconst_code_addr(_)
; !.Const = mlconst_data_addr(_)
)
).
:- pred rename_class_names_unary_op(class_name_renaming::in,
mlds_unary_op::in, mlds_unary_op::out) is det.
rename_class_names_unary_op(Renaming, !Op) :-
(
!.Op = box(Type0),
rename_class_names_type(Renaming, Type0, Type),
!:Op = box(Type)
;
!.Op = unbox(Type0),
rename_class_names_type(Renaming, Type0, Type),
!:Op = unbox(Type)
;
!.Op = cast(Type0),
rename_class_names_type(Renaming, Type0, Type),
!:Op = cast(Type)
;
!.Op = std_unop(_)
).
:- pred rename_class_names_target_code_component(class_name_renaming::in,
target_code_component::in, target_code_component::out) is det.
rename_class_names_target_code_component(Renaming, !Component) :-
(
!.Component = user_target_code(_, _, _)
;
!.Component = raw_target_code(_, _)
;
!.Component = target_code_alloc_id(_)
;
!.Component = target_code_input(Rval0),
rename_class_names_rval(Renaming, Rval0, Rval),
!:Component = target_code_input(Rval)
;
!.Component = target_code_output(Lval0),
rename_class_names_lval(Renaming, Lval0, Lval),
!:Component = target_code_output(Lval)
;
!.Component = target_code_type(Type0),
rename_class_names_type(Renaming, Type0, Type),
!:Component = target_code_type(Type)
;
!.Component = target_code_name(QualName0),
QualName0 = qual(ModuleName, Kind, EntityName0),
(
EntityName0 = entity_type(ClassName0, Arity),
(
Renaming = class_name_renaming(ModuleName, RenamingMap),
map.search(RenamingMap, ClassName0, ClassName)
->
EntityName = entity_type(ClassName, Arity),
QualName = qual(ModuleName, Kind, EntityName),
!:Component = target_code_name(QualName)
;
true
)
;
( EntityName0 = entity_function(_, _, _, _)
; EntityName0 = entity_data(_)
; EntityName0 = entity_export(_)
)
)
).
%-----------------------------------------------------------------------------%
%
% Code to output calls to module initialisers.
%
:- pred output_inits(int::in, list(string)::in, io::di, io::uo) is det.
output_inits(Indent, 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 module finalisers.
%
:- pred output_finals(indent::in, list(string)::in, io::di, io::uo) is det.
output_finals(Indent, FinalPreds, !IO) :-
(
FinalPreds = []
;
FinalPreds = [_ | _],
indent_line(Indent, !IO),
io.write_string("static {\n", !IO),
indent_line(Indent + 1, !IO),
io.write_string("jmercury.runtime.JavaInternal.register_finaliser(\n",
!IO),
indent_line(Indent + 2, !IO),
io.write_string("new java.lang.Runnable() {\n", !IO),
indent_line(Indent + 3, !IO),
io.write_string("public void run() {\n", !IO),
list.foldl(output_final_pred_call(Indent + 4), FinalPreds, !IO),
indent_line(Indent + 3, !IO),
io.write_string("}\n", !IO),
indent_line(Indent + 2, !IO),
io.write_string("}\n", !IO),
indent_line(Indent + 1, !IO),
io.write_string(");\n", !IO),
indent_line(Indent, !IO),
io.write_string("}\n", !IO)
).
:- pred output_final_pred_call(indent::in, string::in, io::di, io::uo) is det.
output_final_pred_call(Indent, FinalPred, !IO) :-
indent_line(Indent, !IO),
io.write_string(FinalPred, !IO),
io.write_string("();\n", !IO).
%-----------------------------------------------------------------------------%
%
% Code to output globals for environment variables.
%
:- pred output_env_vars(indent::in, list(mlds_defn)::in, io::di, io::uo)
is det.
output_env_vars(Indent, NonRttiDefns, !IO) :-
list.foldl(collect_env_var_names, NonRttiDefns, set.init, EnvVarNamesSet),
EnvVarNames = set.to_sorted_list(EnvVarNamesSet),
(
EnvVarNames = []
;
EnvVarNames = [_ | _],
list.foldl(output_env_var_definition(Indent), EnvVarNames, !IO)
).
:- pred collect_env_var_names(mlds_defn::in,
set(string)::in, set(string)::out) is det.
collect_env_var_names(Defn, !EnvVarNames) :-
Defn = mlds_defn(_, _, _, EntityDefn),
(
EntityDefn = mlds_data(_, _, _)
;
EntityDefn = mlds_function(_, _, _, _, EnvVarNames),
set.union(EnvVarNames, !EnvVarNames)
;
EntityDefn = mlds_class(_)
).
:- pred output_env_var_definition(indent::in, string::in, io::di, io::uo)
is det.
output_env_var_definition(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.
indent_line(Indent, !IO),
io.write_string("private static int mercury_envvar_", !IO),
io.write_string(EnvVarName, !IO),
io.write_string(" =\n", !IO),
indent_line(Indent + 1, !IO),
io.write_string("java.lang.System.getenv(\"", !IO),
io.write_string(EnvVarName, !IO),
io.write_string("\") == null ? 0 : 1;\n", !IO).
%-----------------------------------------------------------------------------%
%
% Code to output the start and end of a source file.
%
:- pred output_src_start(globals::in, java_out_info::in, indent::in,
mercury_module_name::in, mlds_imports::in, list(foreign_decl_code)::in,
list(mlds_defn)::in, io::di, io::uo) is det.
output_src_start(Globals, Info, Indent, MercuryModuleName, Imports,
ForeignDecls, Defns, !IO) :-
output_auto_gen_comment(Globals, MercuryModuleName, !IO),
indent_line(Indent, !IO),
io.write_string("/* :- module ", !IO),
prog_out.write_sym_name(MercuryModuleName, !IO),
io.write_string(". */\n\n", !IO),
indent_line(Indent, !IO),
io.write_string("package jmercury;\n", !IO),
output_imports(Imports, !IO),
io.write_list(ForeignDecls, "\n", output_java_decl(Info, Indent), !IO),
io.write_string("public class ", !IO),
mangle_sym_name_for_java(MercuryModuleName, module_qual, "__", ClassName),
io.write_string(ClassName, !IO),
io.write_string(" {\n", !IO),
output_debug_class_init(MercuryModuleName, "start", !IO),
% Check if this module contains a `main' predicate and if it does insert
% a `main' method in the resulting Java class that calls the `main'
% predicate.
( defns_contain_main(Defns) ->
write_main_driver(Indent + 1, ClassName, !IO)
;
true
).
:- pred write_main_driver(indent::in, string::in, io::di, io::uo) is det.
write_main_driver(Indent, ClassName, !IO) :-
indent_line(Indent, !IO),
io.write_string("public static void main", !IO),
io.write_string("(java.lang.String[] args)\n", !IO),
indent_line(Indent, !IO),
io.write_string("{\n", !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;",
"benchmarking.ML_initialise();",
"try {",
" " ++ ClassName ++ ".main_2_p_0();",
" jmercury.runtime.JavaInternal.run_finalisers();",
"} catch (jmercury.runtime.Exception e) {",
" exception.ML_report_uncaught_exception(",
" (univ.Univ_0) e.exception);",
" if (System.getenv(""MERCURY_SUPPRESS_STACK_TRACE"") == null) {",
" e.printStackTrace(System.err);",
" }",
" if (jmercury.runtime.JavaInternal.exit_status == 0) {",
" jmercury.runtime.JavaInternal.exit_status = 1;",
" }",
"}",
"java.lang.System.exit(jmercury.runtime.JavaInternal.exit_status);"
],
list.foldl(write_indented_line(Indent + 1), Body, !IO),
indent_line(Indent, !IO),
io.write_string("}\n", !IO).
:- pred write_indented_line(indent::in, string::in, io::di, io::uo) is det.
write_indented_line(Indent, Line, !IO) :-
indent_line(Indent, !IO),
io.write_string(Line, !IO),
io.nl(!IO).
:- pred output_src_end(indent::in, mercury_module_name::in, io::di, io::uo)
is det.
output_src_end(Indent, ModuleName, !IO) :-
output_debug_class_init(ModuleName, "end", !IO),
io.write_string("}\n", !IO),
indent_line(Indent, !IO),
io.write_string("// :- end_module ", !IO),
prog_out.write_sym_name(ModuleName, !IO),
io.write_string(".\n", !IO).
:- pred output_debug_class_init(mercury_module_name::in, string::in,
io::di, io::uo) is det.
output_debug_class_init(ModuleName, State, !IO) :-
list.foldl(io.write_string, [
" static {\n",
" if (System.getenv(""MERCURY_DEBUG_CLASS_INIT"") != null) {\n",
" System.out.println(""[", sym_name_mangle(ModuleName),
" ", State, " init]"");\n",
" }\n",
" }\n"
], !IO).
% Output a Java comment saying that the file was automatically
% generated and give details such as the compiler version.
%
:- pred output_auto_gen_comment(globals::in, mercury_module_name::in,
io::di, io::uo) is det.
output_auto_gen_comment(Globals, ModuleName, !IO) :-
library.version(Version),
module_name_to_file_name(Globals, ModuleName, ".m", do_not_create_dirs,
SourceFileName, !IO),
io.write_string("//\n//\n// Automatically generated from ", !IO),
io.write_string(SourceFileName, !IO),
io.write_string(" by the Mercury Compiler,\n", !IO),
io.write_string("// version ", !IO),
io.write_string(Version, !IO),
io.nl(!IO),
io.write_string("//\n", !IO),
io.write_string("//\n", !IO),
io.nl(!IO).
%-----------------------------------------------------------------------------%
%
% Code to output declarations and definitions.
%
% Options to adjust the behaviour of the output predicates.
%
:- type output_aux
---> none
% Nothing special.
; cname(mlds_entity_name)
% Pass down the class name if a definition is a constructor; this
% is needed since the class name is not available for a constructor
% in the MLDS.
; alloc_only
% When writing out RTTI structure definitions, initialise members
% with allocated top-level structures but don't fill in the fields
% yet.
; force_init.
% Used to force local variables to be initialised even if an
% initialiser is not provided.
:- pred output_defns(java_out_info::in, indent::in, output_aux::in,
list(mlds_defn)::in, io::di, io::uo) is det.
output_defns(Info, Indent, OutputAux, Defns, !IO) :-
list.foldl(output_defn(Info, Indent, OutputAux), Defns, !IO).
:- pred output_defn(java_out_info::in, indent::in, output_aux::in,
mlds_defn::in, io::di, io::uo) is det.
output_defn(Info, Indent, OutputAux, Defn, !IO) :-
Defn = mlds_defn(Name, Context, Flags, DefnBody),
indent_line(Info, Context, Indent, !IO),
( DefnBody = mlds_function(_, _, body_external, _, _) ->
% This is just a function declaration, with no body.
% Java doesn't support separate declarations and definitions,
% so just output the declaration as a comment.
% (Note that the actual definition of an external procedure
% must be given in `pragma java_code' in the same module.)
io.write_string("/* external:\n", !IO),
output_decl_flags(Info, Flags, !IO),
output_defn_body(Info, Indent, Name, OutputAux, Context, DefnBody,
!IO),
io.write_string("*/\n", !IO)
;
output_decl_flags(Info, Flags, !IO),
output_defn_body(Info, Indent, Name, OutputAux, Context, DefnBody,
!IO)
).
:- pred output_defn_body(java_out_info::in, indent::in, mlds_entity_name::in,
output_aux::in, mlds_context::in, mlds_entity_defn::in, io::di, io::uo)
is det.
output_defn_body(Info, Indent, UnqualName, OutputAux, Context, Entity, !IO) :-
(
Entity = mlds_data(Type, Initializer, _),
output_data_defn(Info, UnqualName, OutputAux, Type, Initializer,
!IO)
;
Entity = mlds_function(MaybePredProcId, Signature, MaybeBody,
_Attributes, _EnvVarNames),
output_maybe(MaybePredProcId, output_pred_proc_id(Info), !IO),
output_func(Info, Indent, UnqualName, OutputAux, Context,
Signature, MaybeBody, !IO)
;
Entity = mlds_class(ClassDefn),
output_class(Info, Indent, UnqualName, ClassDefn, !IO)
).
%-----------------------------------------------------------------------------%
%
% Code to output classes.
%
:- pred output_class(java_out_info::in, indent::in, mlds_entity_name::in,
mlds_class_defn::in, io::di, io::uo) is det.
output_class(!.Info, Indent, UnqualName, ClassDefn, !IO) :-
(
UnqualName = entity_type(ClassNamePrime, ArityPrime),
ClassName = ClassNamePrime,
Arity = ArityPrime
;
( UnqualName = entity_data(_)
; UnqualName = entity_function(_, _, _, _)
; UnqualName = entity_export(_)
),
unexpected($module, $pred, "name is not entity_type")
),
ClassDefn = mlds_class_defn(Kind, _Imports, BaseClasses, Implements,
TypeParams, Ctors, AllMembers),
!Info ^ joi_univ_tvars := TypeParams,
% Use generics in the output if this class represents a Mercury type.
( list.member(ml_java_mercury_type_interface, Implements) ->
!Info ^ joi_output_generics := do_output_generics
;
true
),
output_class_kind(Kind, !IO),
output_unqual_class_name(ClassName, Arity, !IO),
OutputGenerics = !.Info ^ joi_output_generics,
(
OutputGenerics = do_output_generics,
output_generic_tvars(TypeParams, !IO)
;
OutputGenerics = do_not_output_generics
),
io.nl(!IO),
output_extends_list(!.Info, Indent + 1, BaseClasses, !IO),
output_implements_list(Indent + 1, Implements, !IO),
indent_line(Indent, !IO),
io.write_string("{\n", !IO),
output_class_body(!.Info, Indent + 1, Kind, UnqualName, AllMembers, !IO),
io.nl(!IO),
output_defns(!.Info, Indent + 1, cname(UnqualName), Ctors, !IO),
indent_line(Indent, !IO),
io.write_string("}\n\n", !IO).
:- pred output_class_kind(mlds_class_kind::in, io::di, io::uo) is det.
output_class_kind(Kind, !IO) :-
(
Kind = mlds_interface,
io.write_string("interface ", !IO)
;
( Kind = mlds_class
; Kind = mlds_package
; Kind = mlds_enum
; Kind = mlds_struct
),
io.write_string("class ", !IO)
).
:- pred output_generic_tvars(list(tvar)::in, io::di, io::uo) is det.
output_generic_tvars(Vars, !IO) :-
(
Vars = []
;
Vars = [_ | _],
io.write_string("<", !IO),
io.write_list(Vars, ", ", output_generic_tvar, !IO),
io.write_string(">", !IO)
).
:- pred output_generic_tvar(tvar::in, io::di, io::uo) is det.
output_generic_tvar(Var, !IO) :-
generic_tvar_to_string(Var, VarName),
io.write_string(VarName, !IO).
:- pred generic_tvar_to_string(tvar::in, string::out) is det.
generic_tvar_to_string(Var, VarName) :-
varset.lookup_name(varset.init, Var, "MR_tvar_", VarName).
% Output superclass that this class extends. Java does not support
% multiple inheritance, so more than one superclass is an error.
%
:- pred output_extends_list(java_out_info::in, indent::in,
list(mlds_class_id)::in, io::di, io::uo) is det.
output_extends_list(_, _, [], !IO).
output_extends_list(Info, Indent, [SuperClass], !IO) :-
indent_line(Indent, !IO),
io.write_string("extends ", !IO),
output_type(Info, SuperClass, !IO),
io.nl(!IO).
output_extends_list(_, _, [_, _ | _], _, _) :-
unexpected($module, $pred, "multiple inheritance not supported in Java").
% Output list of interfaces that this class implements.
%
:- pred output_implements_list(indent::in, list(mlds_interface_id)::in,
io::di, io::uo) is det.
output_implements_list(Indent, InterfaceList, !IO) :-
(
InterfaceList = []
;
InterfaceList = [_ | _],
indent_line(Indent, !IO),
io.write_string("implements ", !IO),
io.write_list(InterfaceList, ",", output_interface, !IO),
io.nl(!IO)
).
:- pred output_interface(mlds_interface_id::in, io::di, io::uo) is det.
output_interface(Interface, !IO) :-
(
Interface = mlds_class_type(qual(ModuleQualifier, QualKind, Name),
Arity, _)
->
SymName = mlds_module_name_to_sym_name(ModuleQualifier),
mangle_sym_name_for_java(SymName, convert_qual_kind(QualKind),
".", ModuleName),
io.format("%s.%s", [s(ModuleName), s(Name)], !IO),
% Check if the interface is one of the ones in the runtime system.
% If it is, we don't need to output the arity.
( interface_is_special(Name) ->
true
;
io.format("%d", [i(Arity)], !IO)
)
;
unexpected($module, $pred, "interface was not a class")
).
:- pred output_class_body(java_out_info::in, indent::in, mlds_class_kind::in,
mlds_entity_name::in, list(mlds_defn)::in, io::di, io::uo) is det.
output_class_body(Info, Indent, Kind, UnqualName, AllMembers, !IO) :-
(
Kind = mlds_class,
output_defns(Info, Indent, none, AllMembers, !IO)
;
Kind = mlds_package,
unexpected($module, $pred, "cannot use package as a type")
;
Kind = mlds_interface,
output_defns(Info, Indent, none, AllMembers, !IO)
;
Kind = mlds_struct,
unexpected($module, $pred, "structs not supported in Java")
;
Kind = mlds_enum,
list.filter(defn_is_const, AllMembers, EnumConsts),
output_enum_constants(Info, Indent + 1, UnqualName, EnumConsts, !IO),
io.nl(!IO),
output_enum_ctor(Indent + 1, UnqualName, !IO)
).
%-----------------------------------------------------------------------------%
%
% Additional code for generating enumerations.
%
% Enumerations are a bit different from normal classes because although the
% ml code generator generates them as classes, it treats them as integers.
% Here we treat them as objects (instantiations of the classes) rather than
% just as integers.
:- pred defn_is_const(mlds_defn::in) is semidet.
defn_is_const(Defn) :-
Defn = mlds_defn(_Name, _Context, Flags, _DefnBody),
constness(Flags) = const.
% Output a (Java) constructor for the class representing the enumeration.
%
:- pred output_enum_ctor(indent::in, mlds_entity_name::in, io::di, io::uo)
is det.
output_enum_ctor(Indent, UnqualName, !IO) :-
indent_line(Indent, !IO),
io.write_string("private ", !IO),
output_name(UnqualName, !IO),
io.write_string("(int val) {\n", !IO),
indent_line(Indent + 1, !IO),
% Call the MercuryEnum constructor, which will set the MR_value field.
io.write_string("super(val);\n", !IO),
indent_line(Indent, !IO),
io.write_string("}\n", !IO).
:- pred output_enum_constants(java_out_info::in, indent::in,
mlds_entity_name::in, list(mlds_defn)::in, io::di, io::uo) is det.
output_enum_constants(Info, Indent, EnumName, EnumConsts, !IO) :-
io.write_list(EnumConsts, "\n",
output_enum_constant(Info, Indent, EnumName), !IO),
io.nl(!IO).
:- pred output_enum_constant(java_out_info::in, indent::in,
mlds_entity_name::in, mlds_defn::in, io::di, io::uo) is det.
output_enum_constant(_Info, Indent, EnumName, Defn, !IO) :-
Defn = mlds_defn(Name, _Context, _Flags, DefnBody),
( DefnBody = mlds_data(_Type, Initializer, _GCStatement) ->
% Make a static instance of the constant. The MLDS doesn't retain enum
% constructor names (that shouldn't be hard to change now) so it is
% easier to derive the name of the constant later by naming them after
% the integer values.
(
Initializer = init_obj(Rval),
( Rval = ml_const(mlconst_enum(N, _)) ->
indent_line(Indent, !IO),
io.write_string("public static final ", !IO),
output_name(EnumName, !IO),
io.format(" K%d = new ", [i(N)], !IO),
output_name(EnumName, !IO),
io.format("(%d); ", [i(N)], !IO),
io.write_string(" /* ", !IO),
output_name(Name, !IO),
io.write_string(" */", !IO)
;
unexpected($module, $pred, "not mlconst_enum")
)
;
( Initializer = no_initializer
; Initializer = init_struct(_, _)
; Initializer = init_array(_)
),
unexpected($module, $pred, "not mlconst_enum")
)
;
unexpected($module, $pred, "definition body was not data")
).
%-----------------------------------------------------------------------------%
%
% Code to output data declarations/definitions.
%
:- pred output_data_decls(java_out_info::in, indent::in, list(mlds_defn)::in,
io::di, io::uo) is det.
output_data_decls(_, _, [], !IO).
output_data_decls(Info, Indent, [Defn | Defns], !IO) :-
Defn = mlds_defn(Name, _Context, Flags, DefnBody),
( DefnBody = mlds_data(Type, _Initializer, _GCStatement) ->
indent_line(Indent, !IO),
output_decl_flags(Info, Flags, !IO),
output_data_decl(Info, Name, Type, !IO),
io.write_string(";\n", !IO)
;
unexpected($module, $pred, "not data")
),
output_data_decls(Info, Indent, Defns, !IO).
:- pred output_data_decl(java_out_info::in, mlds_entity_name::in,
mlds_type::in, io::di, io::uo) is det.
output_data_decl(Info, Name, Type, !IO) :-
output_type(Info, Type, !IO),
io.write_char(' ', !IO),
output_name(Name, !IO).
:- pred output_data_assignments(java_out_info::in, indent::in,
list(mlds_defn)::in, io::di, io::uo) is det.
output_data_assignments(Info, Indent, Defns, !IO) :-
% Divide into small methods to avoid running into the maximum method size
% limit.
list.chunk(Defns, 1000, DefnChunks),
list.foldl2(output_init_data_method(Info, Indent),
DefnChunks, 0, NumChunks, !IO),
% Call the individual methods.
indent_line(Indent, !IO),
io.write_string("static {\n", !IO),
int.fold_up(output_call_init_data_method(Indent + 1), 0, NumChunks - 1,
!IO),
indent_line(Indent, !IO),
io.write_string("}\n", !IO).
:- pred output_init_data_method(java_out_info::in, indent::in,
list(mlds_defn)::in, int::in, int::out, io::di, io::uo) is det.
output_init_data_method(Info, Indent, Defns, Chunk, Chunk + 1, !IO) :-
indent_line(Indent, !IO),
io.format("private static void MR_init_data_%d() {\n", [i(Chunk)], !IO),
output_init_data_statements(Info, Indent + 1, Defns, !IO),
indent_line(Indent, !IO),
io.write_string("}\n", !IO).
:- pred output_init_data_statements(java_out_info::in, indent::in,
list(mlds_defn)::in, io::di, io::uo) is det.
output_init_data_statements(_, _, [], !IO).
output_init_data_statements(Info, Indent, [Defn | Defns], !IO) :-
Defn = mlds_defn(Name, _Context, _Flags, DefnBody),
( DefnBody = mlds_data(Type, Initializer, _GCStatement) ->
indent_line(Indent, !IO),
output_name(Name, !IO),
output_initializer(Info, none, Type, Initializer, !IO),
io.write_string(";\n", !IO)
;
unexpected($module, $pred, "not mlds_data")
),
output_init_data_statements(Info, Indent, Defns, !IO).
:- pred output_call_init_data_method(indent::in, int::in, io::di, io::uo)
is det.
output_call_init_data_method(Indent, I, !IO) :-
indent_line(Indent, !IO),
io.format("MR_init_data_%d();\n", [i(I)], !IO).
:- pred output_data_defn(java_out_info::in, mlds_entity_name::in,
output_aux::in, mlds_type::in, mlds_initializer::in, io::di, io::uo)
is det.
output_data_defn(Info, Name, OutputAux, Type, Initializer, !IO) :-
output_data_decl(Info, Name, Type, !IO),
output_initializer(Info, OutputAux, Type, Initializer, !IO),
io.write_string(";\n", !IO).
%-----------------------------------------------------------------------------%
%
% Code to output common data.
%
:- pred output_scalar_common_data(java_out_info::in, indent::in,
ml_scalar_cell_map::in, io::di, io::uo) is det.
output_scalar_common_data(Info, Indent, ScalarCellGroupMap, !IO) :-
% Elements of scalar data arrays may reference elements in higher-numbered
% arrays, or elements of the same array, so we must initialise them
% separately in a static initialisation block, and we must ensure that
% elements which are referenced by other elements are initialised first.
map.foldl3(output_scalar_defns(Info, Indent), ScalarCellGroupMap,
digraph.init, Graph, map.init, Map, !IO),
( digraph.tsort(Graph, SortedScalars0) ->
% Divide into small methods to avoid running into the maximum method
% size limit.
list.reverse(SortedScalars0, SortedScalars),
list.chunk(SortedScalars, 1000, ScalarChunks),
list.foldl2(output_scalar_init_method(Info, Indent, Map),
ScalarChunks, 0, NumChunks, !IO),
% Call the individual methods.
indent_line(Indent, !IO),
io.write_string("static {\n", !IO),
int.fold_up(output_call_scalar_init_method(Indent + 1),
0, NumChunks - 1, !IO),
indent_line(Indent, !IO),
io.write_string("}\n", !IO)
;
unexpected($module, $pred, "digraph.tsort failed")
).
:- pred output_scalar_defns(java_out_info::in, indent::in,
ml_scalar_common_type_num::in, ml_scalar_cell_group::in,
digraph(mlds_scalar_common)::in, digraph(mlds_scalar_common)::out,
map(mlds_scalar_common, mlds_initializer)::in,
map(mlds_scalar_common, mlds_initializer)::out, io::di, io::uo) is det.
output_scalar_defns(Info, Indent, TypeNum, CellGroup, !Graph, !Map, !IO) :-
TypeNum = ml_scalar_common_type_num(TypeRawNum),
CellGroup = ml_scalar_cell_group(Type, _InitArraySize, _Counter, _Members,
RowInitsCord),
ArrayType = mlds_array_type(Type),
RowInits = cord.list(RowInitsCord),
indent_line(Indent, !IO),
io.write_string("private static final ", !IO),
output_type(Info, Type, !IO),
io.format("[] MR_scalar_common_%d = ", [i(TypeRawNum)], !IO),
output_initializer_alloc_only(Info, init_array(RowInits), yes(ArrayType),
!IO),
io.write_string(";\n", !IO),
MLDS_ModuleName = Info ^ joi_module_name,
list.foldl3(add_scalar_inits(MLDS_ModuleName, Type, TypeNum),
RowInits, 0, _, !Graph, !Map).
:- pred add_scalar_inits(mlds_module_name::in, mlds_type::in,
ml_scalar_common_type_num::in, mlds_initializer::in, int::in, int::out,
digraph(mlds_scalar_common)::in, digraph(mlds_scalar_common)::out,
map(mlds_scalar_common, mlds_initializer)::in,
map(mlds_scalar_common, mlds_initializer)::out) is det.
add_scalar_inits(MLDS_ModuleName, Type, TypeNum, Initializer,
RowNum, RowNum + 1, !Graph, !Map) :-
Scalar = ml_scalar_common(MLDS_ModuleName, Type, TypeNum, RowNum),
map.det_insert(Scalar, Initializer, !Map),
digraph.add_vertex(Scalar, _Key, !Graph),
add_scalar_deps(Scalar, Initializer, !Graph).
:- pred add_scalar_deps(mlds_scalar_common::in, mlds_initializer::in,
digraph(mlds_scalar_common)::in, digraph(mlds_scalar_common)::out) is det.
add_scalar_deps(FromScalar, Initializer, !Graph) :-
(
Initializer = init_obj(Rval),
add_scalar_deps_rval(FromScalar, Rval, !Graph)
;
Initializer = init_struct(_Type, Initializers),
list.foldl(add_scalar_deps(FromScalar), Initializers, !Graph)
;
Initializer = init_array(Initializers),
list.foldl(add_scalar_deps(FromScalar), Initializers, !Graph)
;
Initializer = no_initializer
).
:- pred add_scalar_deps_rval(mlds_scalar_common::in, mlds_rval::in,
digraph(mlds_scalar_common)::in, digraph(mlds_scalar_common)::out) is det.
add_scalar_deps_rval(FromScalar, Rval, !Graph) :-
(
( Rval = ml_mkword(_, RvalA)
; Rval = ml_unop(_, RvalA)
; Rval = ml_vector_common_row(_, RvalA)
),
add_scalar_deps_rval(FromScalar, RvalA, !Graph)
;
Rval = ml_binop(_, RvalA, RvalB),
add_scalar_deps_rval(FromScalar, RvalA, !Graph),
add_scalar_deps_rval(FromScalar, RvalB, !Graph)
;
Rval = ml_const(RvalConst),
add_scalar_deps_rval_const(FromScalar, RvalConst, !Graph)
;
Rval = ml_scalar_common(ToScalar),
digraph.add_vertices_and_edge(FromScalar, ToScalar, !Graph)
;
Rval = ml_self(_)
;
( Rval = ml_lval(_Lval)
; Rval = ml_mem_addr(_Lval)
),
unexpected($module, $pred, "lval or mem_addr")
).
:- pred add_scalar_deps_rval_const(mlds_scalar_common::in, mlds_rval_const::in,
digraph(mlds_scalar_common)::in, digraph(mlds_scalar_common)::out) is det.
add_scalar_deps_rval_const(FromScalar, RvalConst, !Graph) :-
(
RvalConst = mlconst_data_addr(data_addr(_, DataName)),
(
DataName = mlds_scalar_common_ref(ToScalar),
digraph.add_vertices_and_edge(FromScalar, ToScalar, !Graph)
;
( DataName = mlds_data_var(_)
; DataName = mlds_rtti(_)
; DataName = mlds_module_layout
; DataName = mlds_proc_layout(_)
; DataName = mlds_internal_layout(_, _)
; DataName = mlds_tabling_ref(_, _)
)
)
;
( RvalConst = mlconst_true
; RvalConst = mlconst_false
; RvalConst = mlconst_int(_)
; RvalConst = mlconst_enum(_, _)
; RvalConst = mlconst_char(_)
; RvalConst = mlconst_float(_)
; RvalConst = mlconst_string(_)
; RvalConst = mlconst_multi_string(_)
; RvalConst = mlconst_foreign(_, _, _)
; RvalConst = mlconst_named_const(_)
; RvalConst = mlconst_code_addr(_)
; RvalConst = mlconst_null(_)
)
).
:- pred output_scalar_init_method(java_out_info::in, indent::in,
map(mlds_scalar_common, mlds_initializer)::in,
list(mlds_scalar_common)::in, int::in, int::out, io::di, io::uo) is det.
output_scalar_init_method(Info, Indent, Map, Scalars,
ChunkNum, ChunkNum + 1, !IO) :-
indent_line(Indent, !IO),
io.format("private static void MR_init_scalars_%d() {\n",
[i(ChunkNum)], !IO),
list.foldl(output_scalar_init(Info, Indent + 1, Map), Scalars, !IO),
indent_line(Indent, !IO),
io.write_string("}\n", !IO).
:- pred output_scalar_init(java_out_info::in, indent::in,
map(mlds_scalar_common, mlds_initializer)::in, mlds_scalar_common::in,
io::di, io::uo) is det.
output_scalar_init(Info, Indent, Map, Scalar, !IO) :-
map.lookup(Map, Scalar, Initializer),
Scalar = ml_scalar_common(_, Type, TypeNum, RowNum),
TypeNum = ml_scalar_common_type_num(TypeRawNum),
indent_line(Indent, !IO),
io.format("MR_scalar_common_%d[%d] = ", [i(TypeRawNum), i(RowNum)], !IO),
output_initializer_body(Info, Initializer, yes(Type), !IO),
io.write_string(";\n", !IO).
:- pred output_call_scalar_init_method(int::in, int::in, io::di, io::uo)
is det.
output_call_scalar_init_method(Indent, ChunkNum, !IO) :-
indent_line(Indent, !IO),
io.format("MR_init_scalars_%d();\n", [i(ChunkNum)], !IO).
:- pred output_vector_common_data(java_out_info::in, indent::in,
ml_vector_cell_map::in, io::di, io::uo) is det.
output_vector_common_data(Info, Indent, VectorCellGroupMap, !IO) :-
map.foldl(output_vector_cell_group(Info, Indent), VectorCellGroupMap, !IO).
:- pred output_vector_cell_group(java_out_info::in, indent::in,
ml_vector_common_type_num::in, ml_vector_cell_group::in,
io::di, io::uo) is det.
output_vector_cell_group(Info, Indent, TypeNum, CellGroup, !IO) :-
TypeNum = ml_vector_common_type_num(TypeRawNum),
CellGroup = ml_vector_cell_group(Type, ClassDefn, _FieldIds, _NextRow,
RowInits),
output_defn(Info, Indent, none, ClassDefn, !IO),
indent_line(Indent, !IO),
io.write_string("private static final ", !IO),
output_type(Info, Type, !IO),
io.format(" MR_vector_common_%d[] = {\n", [i(TypeRawNum)], !IO),
indent_line(Indent + 1, !IO),
output_initializer_body_list(Info, cord.list(RowInits), !IO),
io.nl(!IO),
indent_line(Indent, !IO),
io.write_string("};\n", !IO).
%-----------------------------------------------------------------------------%
% We need to provide initializers for local variables to avoid problems
% with Java's rules for definite assignment. This mirrors the default
% Java initializers for class and instance variables.
%
:- func get_java_type_initializer(mlds_type) = string.
get_java_type_initializer(Type) = Initializer :-
(
Type = mercury_type(_, CtorCat, _),
(
( CtorCat = ctor_cat_builtin(cat_builtin_int)
; CtorCat = ctor_cat_builtin(cat_builtin_float)
),
Initializer = "0"
;
CtorCat = ctor_cat_builtin(cat_builtin_char),
Initializer = "'\\u0000'"
;
( CtorCat = ctor_cat_builtin(cat_builtin_string)
; CtorCat = ctor_cat_system(_)
; CtorCat = ctor_cat_higher_order
; CtorCat = ctor_cat_tuple
; CtorCat = ctor_cat_enum(_)
; CtorCat = ctor_cat_builtin_dummy
; CtorCat = ctor_cat_variable
; CtorCat = ctor_cat_void
; CtorCat = ctor_cat_user(_)
),
Initializer = "null"
)
;
( Type = mlds_native_int_type
; Type = mlds_native_float_type
),
Initializer = "0"
;
Type = mlds_native_char_type,
Initializer = "'\\u0000'"
;
Type = mlds_native_bool_type,
Initializer = "false"
;
( Type = mlds_mercury_array_type(_)
; Type = mlds_cont_type(_)
; Type = mlds_commit_type
; Type = mlds_foreign_type(_)
; Type = mlds_class_type(_, _, _)
; Type = mlds_array_type(_)
; Type = mlds_ptr_type(_)
; Type = mlds_func_type(_)
; Type = mlds_generic_type
; Type = mlds_generic_env_ptr_type
; Type = mlds_type_info_type
; Type = mlds_pseudo_type_info_type
; Type = mlds_rtti_type(_)
; Type = mlds_tabling_type(_)
),
Initializer = "null"
;
Type = mlds_unknown_type,
unexpected($module, $pred, "variable has unknown_type")
).
:- pred output_maybe(maybe(T)::in,
pred(T, io, io)::pred(in, di, uo) is det, io::di, io::uo) is det.
output_maybe(MaybeValue, OutputAction, !IO) :-
(
MaybeValue = yes(Value),
OutputAction(Value, !IO)
;
MaybeValue = no
).
%-----------------------------------------------------------------------------%
:- pred output_initializer(java_out_info::in, output_aux::in, mlds_type::in,
mlds_initializer::in, io::di, io::uo) is det.
output_initializer(Info, OutputAux, Type, Initializer, !IO) :-
NeedsInit = needs_initialization(Initializer),
(
NeedsInit = yes,
io.write_string(" = ", !IO),
% Due to cyclic references, we need to separate the allocation and
% initialisation steps of RTTI structures. If InitStyle is alloc_only
% then we output an initializer to allocate a structure without filling
% in the fields.
(
( OutputAux = none
; OutputAux = cname(_)
; OutputAux = force_init
),
output_initializer_body(Info, Initializer, yes(Type), !IO)
;
OutputAux = alloc_only,
output_initializer_alloc_only(Info, Initializer, yes(Type), !IO)
)
;
NeedsInit = no,
(
OutputAux = force_init,
% Local variables need to be initialised to avoid warnings.
io.write_string(" = ", !IO),
io.write_string(get_java_type_initializer(Type), !IO)
;
( OutputAux = none
; OutputAux = cname(_)
; OutputAux = alloc_only
)
)
).
:- func needs_initialization(mlds_initializer) = bool.
needs_initialization(no_initializer) = no.
needs_initialization(init_obj(_)) = yes.
needs_initialization(init_struct(_, _)) = yes.
needs_initialization(init_array(_)) = yes.
:- pred output_initializer_alloc_only(java_out_info::in, mlds_initializer::in,
maybe(mlds_type)::in, io::di, io::uo) is det.
output_initializer_alloc_only(Info, Initializer, MaybeType, !IO) :-
(
Initializer = no_initializer,
unexpected($module, $pred, "no_initializer")
;
Initializer = init_obj(_),
unexpected($module, $pred, "init_obj")
;
Initializer = init_struct(StructType, FieldInits),
io.write_string("new ", !IO),
(
StructType = mercury_type(_Type, CtorCat, _),
type_category_is_array(CtorCat) = is_array
->
Size = list.length(FieldInits),
io.format("java.lang.Object[%d]", [i(Size)], !IO)
;
output_type(Info, StructType, !IO),
io.write_string("()", !IO)
)
;
Initializer = init_array(ElementInits),
Size = list.length(ElementInits),
io.write_string("new ", !IO),
(
MaybeType = yes(Type),
type_to_string(Info, Type, String, ArrayDims),
io.write_string(String, !IO),
% Replace the innermost array dimension by the known size.
( list.split_last(ArrayDims, Heads, 0) ->
output_array_dimensions(Heads ++ [Size], !IO)
;
unexpected($module, $pred, "missing array dimension")
)
;
MaybeType = no,
% XXX we need to know the type here
io.format("/* XXX init_array */ Object[%d]", [i(Size)], !IO)
)
).
:- pred output_initializer_body(java_out_info::in, mlds_initializer::in,
maybe(mlds_type)::in, io::di, io::uo) is det.
output_initializer_body(Info, Initializer, MaybeType, !IO) :-
(
Initializer = no_initializer,
unexpected($module, $pred, "no_initializer")
;
Initializer = init_obj(Rval),
output_rval(Info, Rval, !IO)
;
Initializer = init_struct(StructType, FieldInits),
io.write_string("new ", !IO),
output_type(Info, StructType, !IO),
IsArray = type_is_array(StructType),
io.write_string(if IsArray = is_array then " {" else "(", !IO),
output_initializer_body_list(Info, FieldInits, !IO),
io.write_char(if IsArray = is_array then '}' else ')', !IO)
;
Initializer = init_array(ElementInits),
io.write_string("new ", !IO),
(
MaybeType = yes(Type),
output_type(Info, Type, !IO)
;
MaybeType = no,
% XXX we need to know the type here
io.write_string("/* XXX init_array */ Object[]", !IO)
),
io.write_string(" {\n\t\t", !IO),
output_initializer_body_list(Info, ElementInits, !IO),
io.write_string("}", !IO)
).
:- pred output_initializer_body_list(java_out_info::in,
list(mlds_initializer)::in, io::di, io::uo) is det.
output_initializer_body_list(Info, Inits, !IO) :-
io.write_list(Inits, ",\n\t\t",
(pred(Init::in, !.IO::di, !:IO::uo) is det :-
output_initializer_body(Info, Init, no, !IO)),
!IO).
%-----------------------------------------------------------------------------%
%
% Code to output RTTI data assignments.
%
:- pred output_rtti_assignments(java_out_info::in, indent::in,
list(mlds_defn)::in, io::di, io::uo) is det.
output_rtti_assignments(Info, Indent, Defns, !IO) :-
(
Defns = []
;
Defns = [_ | _],
OrderedDefns = order_mlds_rtti_defns(Defns),
indent_line(Indent, !IO),
io.write_string("static {\n", !IO),
list.foldl(output_rtti_defns_assignments(Info, Indent + 1),
OrderedDefns, !IO),
indent_line(Indent, !IO),
io.write_string("}\n", !IO)
).
:- pred output_rtti_defns_assignments(java_out_info::in, indent::in,
list(mlds_defn)::in, io::di, io::uo) is det.
output_rtti_defns_assignments(Info, Indent, Defns, !IO) :-
% Separate cliques.
indent_line(Indent, !IO),
io.write_string("//\n", !IO),
list.foldl(output_rtti_defn_assignments(Info, Indent),
Defns, !IO).
:- pred output_rtti_defn_assignments(java_out_info::in, indent::in,
mlds_defn::in, io::di, io::uo) is det.
output_rtti_defn_assignments(Info, Indent, Defn, !IO) :-
Defn = mlds_defn(Name, _Context, _Flags, DefnBody),
(
DefnBody = mlds_data(_Type, Initializer, _),
output_rtti_defn_assignments_2(Info, Indent, Name, Initializer, !IO)
;
( DefnBody = mlds_function(_, _, _, _, _)
; DefnBody = mlds_class(_)
),
unexpected($module, $pred, "expected mlds_data")
).
:- pred output_rtti_defn_assignments_2(java_out_info::in, indent::in,
mlds_entity_name::in, mlds_initializer::in, io::di, io::uo) is det.
output_rtti_defn_assignments_2(Info, Indent, Name, Initializer, !IO) :-
(
Initializer = no_initializer
;
Initializer = init_obj(_),
% Not encountered in practice.
unexpected($module, $pred, "init_obj")
;
Initializer = init_struct(StructType, FieldInits),
IsArray = type_is_array(StructType),
(
IsArray = not_array,
indent_line(Indent, !IO),
output_name(Name, !IO),
io.write_string(".init(", !IO),
output_initializer_body_list(Info, FieldInits, !IO),
io.write_string(");\n", !IO)
;
IsArray = is_array,
% Not encountered in practice.
unexpected($module, $pred, "is_array")
)
;
Initializer = init_array(ElementInits),
list.foldl2(output_rtti_array_assignments(Info, Indent, Name),
ElementInits, 0, _Index, !IO)
).
:- pred output_rtti_array_assignments(java_out_info::in, indent::in,
mlds_entity_name::in, mlds_initializer::in, int::in, int::out,
io::di, io::uo) is det.
output_rtti_array_assignments(Info, Indent, Name, ElementInit,
Index, Index + 1, !IO) :-
indent_line(Indent, !IO),
output_name(Name, !IO),
io.write_string("[", !IO),
io.write_int(Index, !IO),
io.write_string("] = ", !IO),
output_initializer_body(Info, ElementInit, no, !IO),
io.write_string(";\n", !IO).
%-----------------------------------------------------------------------------%
%
% Code to output function declarations/definitions.
%
:- pred output_pred_proc_id(java_out_info::in, pred_proc_id::in,
io::di, io::uo) is det.
output_pred_proc_id(Info, proc(PredId, ProcId), !IO) :-
AutoComments = Info ^ joi_auto_comments,
(
AutoComments = yes,
io.write_string("// pred_id: ", !IO),
pred_id_to_int(PredId, PredIdNum),
io.write_int(PredIdNum, !IO),
io.write_string(", proc_id: ", !IO),
proc_id_to_int(ProcId, ProcIdNum),
io.write_int(ProcIdNum, !IO),
io.nl(!IO)
;
AutoComments = no
).
:- pred output_func(java_out_info::in, indent::in, mlds_entity_name::in,
output_aux::in, mlds_context::in,
mlds_func_params::in, mlds_function_body::in, io::di, io::uo) is det.
output_func(Info, Indent, Name, OutputAux, Context, Signature, MaybeBody,
!IO) :-
(
MaybeBody = body_defined_here(Body),
output_func_decl(Info, Indent, Name, OutputAux, Signature, !IO),
io.write_string("\n", !IO),
indent_line(Info, Context, Indent, !IO),
io.write_string("{\n", !IO),
FuncInfo = func_info(Signature),
output_statement(Info, Indent + 1, FuncInfo, Body, _ExitMethods, !IO),
indent_line(Info, Context, Indent, !IO),
io.write_string("}\n", !IO) % end the function
;
MaybeBody = body_external
).
:- pred output_func_decl(java_out_info::in, indent::in, mlds_entity_name::in,
output_aux::in, mlds_func_params::in, io::di, io::uo) is det.
output_func_decl(Info, Indent, Name, OutputAux, Signature, !IO) :-
Signature = mlds_func_params(Parameters, RetTypes),
(
OutputAux = cname(CtorName),
Name = entity_export("<constructor>")
->
output_name(CtorName, !IO)
;
output_return_types(Info, RetTypes, !IO),
io.write_char(' ', !IO),
output_name(Name, !IO)
),
output_params(Info, Indent, Parameters, !IO).
:- pred output_return_types(java_out_info::in, mlds_return_types::in,
io::di, io::uo) is det.
output_return_types(Info, RetTypes, !IO) :-
(
RetTypes = [],
io.write_string("void", !IO)
;
RetTypes = [RetType],
output_type(Info, RetType, !IO)
;
RetTypes = [_, _ | _],
% For multiple outputs, we return an array of objects.
io.write_string("java.lang.Object []", !IO)
).
:- pred output_params(java_out_info::in, indent::in, mlds_arguments::in,
io::di, io::uo) is det.
output_params(Info, Indent, Parameters, !IO) :-
io.write_char('(', !IO),
(
Parameters = []
;
Parameters = [_ | _],
io.nl(!IO),
io.write_list(Parameters, ",\n", output_param(Info, Indent + 1), !IO)
),
io.write_char(')', !IO).
:- pred output_param(java_out_info::in, indent::in, mlds_argument::in,
io::di, io::uo) is det.
output_param(Info, Indent, Arg, !IO) :-
Arg = mlds_argument(Name, Type, _GCStatement),
indent_line(Indent, !IO),
output_type(Info, Type, !IO),
io.write_char(' ', !IO),
output_name(Name, !IO).
%-----------------------------------------------------------------------------%
%
% Code to output names of various entities.
%
% XXX Much of the code in this section will not work when we start enforcing
% names properly.
%
:- pred output_maybe_qualified_name(java_out_info::in,
mlds_qualified_entity_name::in, io::di, io::uo) is det.
output_maybe_qualified_name(Info, QualifiedName, !IO) :-
% Don't module qualify names which are defined in the current module.
% This avoids unnecessary verbosity, and is also necessary in the case
% of local variables and function parameters, which must not be qualified.
QualifiedName = qual(ModuleName, _QualKind, Name),
CurrentModuleName = Info ^ joi_module_name,
( ModuleName = CurrentModuleName ->
output_name(Name, !IO)
;
output_fully_qualified_thing(QualifiedName, output_name, !IO)
).
:- pred output_fully_qualified_thing(mlds_fully_qualified_name(T)::in,
pred(T, io, io)::pred(in, di, uo) is det, io::di, io::uo) is det.
output_fully_qualified_thing(QualName, OutputFunc, !IO) :-
QualName = qual(MLDS_ModuleName, QualKind, UnqualName),
qualifier_to_string(MLDS_ModuleName, QualKind, QualifierString),
io.write_string(QualifierString, !IO),
io.write_string(".", !IO),
OutputFunc(UnqualName, !IO).
:- pred qualifier_to_string(mlds_module_name::in, mlds_qual_kind::in,
string::out) is det.
qualifier_to_string(MLDS_ModuleName, QualKind, String) :-
mlds_module_name_to_package_name(MLDS_ModuleName) = OuterName,
mlds_module_name_to_sym_name(MLDS_ModuleName) = InnerName,
% The part of the qualifier that corresponds to a top-level Java class.
mangle_sym_name_for_java(OuterName, module_qual, "__", MangledOuterName),
% The later parts of the qualifier correspond to nested Java classes.
( OuterName = InnerName ->
MangledSuffix = ""
;
remove_sym_name_prefixes(InnerName, OuterName, Suffix),
mangle_sym_name_for_java(Suffix, convert_qual_kind(QualKind), ".",
MangledSuffix0),
MangledSuffix = "." ++ MangledSuffix0
),
String = MangledOuterName ++ MangledSuffix.
:- pred remove_sym_name_prefixes(sym_name::in, sym_name::in, sym_name::out)
is det.
remove_sym_name_prefixes(SymName0, Prefix, SymName) :-
(
SymName0 = qualified(Qual, Name),
( Qual = Prefix ->
SymName = unqualified(Name)
;
remove_sym_name_prefixes(Qual, Prefix, SymName1),
SymName = qualified(SymName1, Name)
)
;
SymName0 = unqualified(_),
unexpected($module, $pred, "prefix not found")
).
:- func convert_qual_kind(mlds_qual_kind) = csj_qual_kind.
convert_qual_kind(module_qual) = module_qual.
convert_qual_kind(type_qual) = type_qual.
:- pred output_module_name(mercury_module_name::in, io::di, io::uo) is det.
output_module_name(ModuleName, !IO) :-
io.write_string(sym_name_mangle(ModuleName), !IO).
:- pred output_unqual_class_name(mlds_class_name::in, arity::in,
io::di, io::uo) is det.
output_unqual_class_name(Name, Arity, !IO) :-
unqual_class_name_to_string(Name, Arity, String),
io.write_string(String, !IO).
:- pred unqual_class_name_to_string(mlds_class_name::in, arity::in,
string::out) is det.
unqual_class_name_to_string(Name, Arity, String) :-
MangledName = name_mangle_no_leading_digit(Name),
% By convention, class names should start with a capital letter.
UppercaseMangledName = flip_initial_case(MangledName),
String = UppercaseMangledName ++ "_" ++ string.from_int(Arity).
:- pred qual_class_name_to_string(mlds_class::in, arity::in, string::out)
is det.
qual_class_name_to_string(QualName, Arity, String) :-
QualName = qual(MLDS_ModuleName, QualKind, ClassName),
(
SymName = mlds_module_name_to_sym_name(MLDS_ModuleName),
SymName = java_mercury_runtime_package_name
->
% Don't mangle runtime class names.
String = "jmercury.runtime." ++ ClassName
;
qualifier_to_string(MLDS_ModuleName, QualKind, QualString),
unqual_class_name_to_string(ClassName, Arity, UnqualString),
String = QualString ++ "." ++ UnqualString
).
:- pred output_name(mlds_entity_name::in, io::di, io::uo) is det.
output_name(entity_type(Name, Arity), !IO) :-
output_unqual_class_name(Name, Arity, !IO).
output_name(entity_data(DataName), !IO) :-
output_data_name(DataName, !IO).
output_name(entity_function(PredLabel, ProcId, MaybeSeqNum, _PredId), !IO) :-
output_pred_label(PredLabel, !IO),
proc_id_to_int(ProcId, ModeNum),
io.format("_%d", [i(ModeNum)], !IO),
(
MaybeSeqNum = yes(SeqNum),
io.format("_%d", [i(SeqNum)], !IO)
;
MaybeSeqNum = no
).
output_name(entity_export(Name), !IO) :-
io.write_string(Name, !IO).
:- pred output_pred_label(mlds_pred_label::in, io::di, io::uo) is det.
output_pred_label(mlds_user_pred_label(PredOrFunc, MaybeDefiningModule, Name,
PredArity, _, _), !IO) :-
(
PredOrFunc = pf_predicate,
Suffix = "p",
OrigArity = PredArity
;
PredOrFunc = pf_function,
Suffix = "f",
OrigArity = PredArity - 1
),
MangledName = name_mangle_no_leading_digit(Name),
io.format("%s_%d_%s", [s(MangledName), i(OrigArity), s(Suffix)], !IO),
(
MaybeDefiningModule = yes(DefiningModule),
io.write_string("_in__", !IO),
output_module_name(DefiningModule, !IO)
;
MaybeDefiningModule = no
).
output_pred_label(mlds_special_pred_label(PredName, MaybeTypeModule, TypeName,
TypeArity), !IO) :-
MangledPredName = name_mangle_no_leading_digit(PredName),
MangledTypeName = name_mangle(TypeName),
io.write_string(MangledPredName, !IO),
io.write_string("__", !IO),
(
MaybeTypeModule = yes(TypeModule),
output_module_name(TypeModule, !IO),
io.write_string("__", !IO)
;
MaybeTypeModule = no
),
io.format("%s_%d", [s(MangledTypeName), i(TypeArity)], !IO).
:- pred output_data_name(mlds_data_name::in, io::di, io::uo) is det.
output_data_name(mlds_data_var(VarName), !IO) :-
output_mlds_var_name(VarName, !IO).
output_data_name(mlds_scalar_common_ref(Common), !IO) :-
Common = ml_scalar_common(_ModuleName, _Type,
ml_scalar_common_type_num(TypeNum), RowNum),
io.format("MR_scalar_common_%d[%d]", [i(TypeNum), i(RowNum)], !IO).
output_data_name(mlds_rtti(RttiId), !IO) :-
rtti.id_to_c_identifier(RttiId, RttiAddrName),
io.write_string(RttiAddrName, !IO).
output_data_name(mlds_module_layout, !IO) :-
unexpected($module, $pred, "NYI: mlds_module_layout").
output_data_name(mlds_proc_layout(_ProcLabel), !IO) :-
unexpected($module, $pred, "NYI: mlds_proc_layout").
output_data_name(mlds_internal_layout(_ProcLabel, _FuncSeqNum), !IO) :-
unexpected($module, $pred, "NYI: mlds_internal_layout").
output_data_name(mlds_tabling_ref(ProcLabel, Id), !IO) :-
Prefix = tabling_info_id_str(Id) ++ "_",
io.write_string(Prefix, !IO),
mlds_output_proc_label(mlds_std_tabling_proc_label(ProcLabel), !IO).
:- pred output_mlds_var_name(mlds_var_name::in, io::di, io::uo) is det.
output_mlds_var_name(mlds_var_name(Name, no), !IO) :-
output_valid_mangled_name(Name, !IO).
output_mlds_var_name(mlds_var_name(Name, yes(Num)), !IO) :-
output_mangled_name(string.format("%s_%d", [s(Name), i(Num)]), !IO).
%-----------------------------------------------------------------------------%
%
% Code to output types.
%
:- pred output_type(java_out_info::in, mlds_type::in, io::di, io::uo) is det.
output_type(Info, MLDS_Type, !IO) :-
output_type(Info, MLDS_Type, [], !IO).
:- pred output_type(java_out_info::in, mlds_type::in, list(int)::in,
io::di, io::uo) is det.
output_type(Info, MLDS_Type, ArrayDims0, !IO) :-
type_to_string(Info, MLDS_Type, String, ArrayDims),
io.write_string(String, !IO),
output_array_dimensions(ArrayDims ++ ArrayDims0, !IO).
:- pred output_array_dimensions(list(int)::in, io::di, io::uo) is det.
output_array_dimensions(ArrayDims, !IO) :-
list.map(array_dimension_to_string, ArrayDims, Strings),
list.foldr(io.write_string, Strings, !IO).
% type_to_string(Info, MLDS_Type, String, ArrayDims)
%
% Generate the Java name for a type. ArrayDims are the array dimensions to
% be written after the type name, if any, in reverse order to that of Java
% syntax where a non-zero integer represents a known array size and zero
% represents an unknown array size.
%
% e.g. ArrayDims = [0, 3] represents the Java array `Object[3][]',
% which should be read as `(Object[])[3]'.
%
:- pred type_to_string(java_out_info::in, mlds_type::in,
string::out, list(int)::out) is det.
type_to_string(Info, MLDS_Type, String, ArrayDims) :-
(
MLDS_Type = mercury_type(Type, CtorCat, _),
(
% We need to handle type_info (etc.) types specially --
% they get mapped to types in the runtime rather than
% in private_builtin.
hand_defined_type(Type, CtorCat, SubstituteName, ArrayDims0)
->
String = SubstituteName,
ArrayDims = ArrayDims0
;
% io.state and store.store
CtorCat = ctor_cat_builtin_dummy
->
String = "/* builtin_dummy */ java.lang.Object",
ArrayDims = []
;
Type = c_pointer_type
->
% The c_pointer type is used in the c back-end as a generic way
% to pass foreign types to automatically generated Compare and
% Unify code. When compiling to Java we must instead use
% java.lang.Object.
String = "/* c_pointer */ java.lang.Object",
ArrayDims = []
;
mercury_type_to_string(Info, Type, CtorCat, String, ArrayDims)
)
;
MLDS_Type = mlds_mercury_array_type(ElementType),
( ElementType = mercury_type(_, ctor_cat_variable, _) ->
% We can't use `java.lang.Object []', since we want a generic type
% that is capable of holding any kind of array, including e.g.
% `int []'. Java doesn't have any equivalent of .NET's System.Array
% class, so we just use the universal base `java.lang.Object'.
String = "/* Array */ java.lang.Object",
ArrayDims = []
;
% For primitive element types we use arrays of the primitive type.
% For non-primitive element types, we just use
% `java.lang.Object []'. We used to use more specific types,
% but then to create an array of the right type we need to use
% reflection to determine the class of a representative element.
% That doesn't work if the representative element is of a foreign
% type, and has the value null.
( java_builtin_type(ElementType, _, _, _) ->
type_to_string(Info, ElementType, String, ArrayDims0),
ArrayDims = [0 | ArrayDims0]
;
String = "java.lang.Object",
ArrayDims = [0]
)
)
;
MLDS_Type = mlds_native_int_type,
String = "int",
ArrayDims = []
;
MLDS_Type = mlds_native_float_type,
String = "double",
ArrayDims = []
;
MLDS_Type = mlds_native_bool_type,
String = "boolean",
ArrayDims = []
;
MLDS_Type = mlds_native_char_type,
% Java `char' not large enough for code points so we must use `int'.
String = "int",
ArrayDims = []
;
MLDS_Type = mlds_foreign_type(ForeignType),
(
ForeignType = java(java_type(Name)),
String = Name,
ArrayDims = []
;
ForeignType = c(_),
unexpected($module, $pred, "c foreign_type")
;
ForeignType = il(_),
unexpected($module, $pred, "il foreign_type")
;
ForeignType = csharp(_),
unexpected($module, $pred, "csharp foreign_type")
;
ForeignType = erlang(_),
unexpected($module, $pred, "erlang foreign_type")
)
;
MLDS_Type = mlds_class_type(Name, Arity, _ClassKind),
qual_class_name_to_string(Name, Arity, String),
ArrayDims = []
;
MLDS_Type = mlds_ptr_type(Type),
% XXX Should we report an error here, if the type pointed to
% is not a class type?
type_to_string(Info, Type, String, ArrayDims)
;
MLDS_Type = mlds_array_type(Type),
type_to_string(Info, Type, String, ArrayDims0),
ArrayDims = [0 | ArrayDims0]
;
MLDS_Type = mlds_func_type(_FuncParams),
String = "jmercury.runtime.MethodPtr",
ArrayDims = []
;
MLDS_Type = mlds_generic_type,
String = "/* generic_type */ java.lang.Object",
ArrayDims = []
;
MLDS_Type = mlds_generic_env_ptr_type,
String = "/* env_ptr */ java.lang.Object",
ArrayDims = []
;
MLDS_Type = mlds_type_info_type,
String = "jmercury.runtime.TypeInfo",
ArrayDims = []
;
MLDS_Type = mlds_pseudo_type_info_type,
String = "jmercury.runtime.PseudoTypeInfo",
ArrayDims = []
;
MLDS_Type = mlds_cont_type(_),
% XXX Should this actually be a class that extends MethodPtr?
String = "jmercury.runtime.MethodPtr",
ArrayDims = []
;
MLDS_Type = mlds_commit_type,
String = "jmercury.runtime.Commit",
ArrayDims = []
;
MLDS_Type = mlds_rtti_type(RttiIdMaybeElement),
rtti_id_maybe_element_java_type(RttiIdMaybeElement, String, IsArray),
(
IsArray = is_array,
ArrayDims = [0]
;
IsArray = not_array,
ArrayDims = []
)
;
MLDS_Type = mlds_tabling_type(TablingId),
tabling_id_java_type(TablingId, String, IsArray),
(
IsArray = is_array,
ArrayDims = [0]
;
IsArray = not_array,
ArrayDims = []
)
;
MLDS_Type = mlds_unknown_type,
unexpected($module, $pred, "unknown type")
).
:- pred mercury_type_to_string(java_out_info::in, mer_type::in,
type_ctor_category::in, string::out, list(int)::out) is det.
mercury_type_to_string(Info, Type, CtorCat, String, ArrayDims) :-
(
CtorCat = ctor_cat_builtin(cat_builtin_char),
% Java `char' not large enough for code points so we must use `int'.
String = "int",
ArrayDims = []
;
CtorCat = ctor_cat_builtin(cat_builtin_int),
String = "int",
ArrayDims = []
;
CtorCat = ctor_cat_builtin(cat_builtin_string),
String = "java.lang.String",
ArrayDims = []
;
CtorCat = ctor_cat_builtin(cat_builtin_float),
String = "double",
ArrayDims = []
;
CtorCat = ctor_cat_void,
String = "builtin.Void_0",
ArrayDims = []
;
CtorCat = ctor_cat_variable,
(
Info ^ joi_output_generics = do_output_generics,
Type = type_variable(TVar, kind_star),
list.member(TVar, Info ^ joi_univ_tvars)
->
generic_tvar_to_string(TVar, String)
;
String = "java.lang.Object"
),
ArrayDims = []
;
CtorCat = ctor_cat_tuple,
String = "/* tuple */ java.lang.Object",
ArrayDims = [0]
;
CtorCat = ctor_cat_higher_order,
String = "/* closure */ java.lang.Object",
ArrayDims = [0]
;
CtorCat = ctor_cat_system(_),
mercury_type_to_string(Info, Type, ctor_cat_user(cat_user_general),
String, ArrayDims)
;
( CtorCat = ctor_cat_enum(_)
; CtorCat = ctor_cat_user(_)
; CtorCat = ctor_cat_builtin_dummy
),
mercury_user_type_to_string(Info, Type, CtorCat, String, ArrayDims)
).
:- pred mercury_user_type_to_string(java_out_info::in, mer_type::in,
type_ctor_category::in, string::out, list(int)::out) is det.
mercury_user_type_to_string(Info, Type, CtorCat, String, ArrayDims) :-
( type_to_ctor_and_args(Type, TypeCtor, ArgsTypes) ->
ml_gen_type_name(TypeCtor, ClassName, ClassArity),
( CtorCat = ctor_cat_enum(_) ->
MLDS_Type = mlds_class_type(ClassName, ClassArity, mlds_enum)
;
MLDS_Type = mlds_class_type(ClassName, ClassArity, mlds_class)
),
type_to_string(Info, MLDS_Type, TypeString, ArrayDims),
OutputGenerics = Info ^ joi_output_generics,
(
OutputGenerics = do_output_generics,
generic_args_types_to_string(Info, ArgsTypes, GenericsString),
String = TypeString ++ GenericsString
;
OutputGenerics = do_not_output_generics,
String = TypeString
)
;
unexpected($module, $pred, "not a user type")
).
:- pred generic_args_types_to_string(java_out_info::in, list(mer_type)::in,
string::out) is det.
generic_args_types_to_string(Info, ArgsTypes, String) :-
(
ArgsTypes = [],
String = ""
;
ArgsTypes = [_ | _],
ToString = (pred(ArgType::in, ArgTypeString::out) is det :-
ModuleInfo = Info ^ joi_module_info,
MLDS_ArgType = mercury_type_to_mlds_type(ModuleInfo, ArgType),
boxed_type_to_string(Info, MLDS_ArgType, ArgTypeString)
),
list.map(ToString, ArgsTypes, ArgsTypesStrings),
ArgsTypesString = string.join_list(", ", ArgsTypesStrings),
String = "<" ++ ArgsTypesString ++ ">"
).
% Return is_array if the corresponding Java type is an array type.
%
:- func type_is_array(mlds_type) = is_array.
type_is_array(Type) = IsArray :-
( Type = mlds_array_type(_) ->
IsArray = is_array
; Type = mlds_mercury_array_type(_) ->
IsArray = is_array
; Type = mercury_type(_, CtorCat, _) ->
IsArray = type_category_is_array(CtorCat)
; Type = mlds_rtti_type(RttiIdMaybeElement) ->
rtti_id_maybe_element_java_type(RttiIdMaybeElement,
_JavaTypeName, IsArray)
;
IsArray = not_array
).
% Return is_array if the corresponding Java type is an array type.
%
:- func type_category_is_array(type_ctor_category) = is_array.
type_category_is_array(CtorCat) = IsArray :-
(
( CtorCat = ctor_cat_builtin(_)
; CtorCat = ctor_cat_enum(_)
; CtorCat = ctor_cat_builtin_dummy
; CtorCat = ctor_cat_variable
; CtorCat = ctor_cat_system(cat_system_type_info)
; CtorCat = ctor_cat_system(cat_system_type_ctor_info)
; CtorCat = ctor_cat_void
; CtorCat = ctor_cat_user(_)
),
IsArray = not_array
;
( CtorCat = ctor_cat_higher_order
; CtorCat = ctor_cat_tuple
; CtorCat = ctor_cat_system(cat_system_typeclass_info)
; CtorCat = ctor_cat_system(cat_system_base_typeclass_info)
),
IsArray = is_array
).
% hand_defined_type(Type, CtorCat, SubstituteName, ArrayDims):
%
% We need to handle type_info (etc.) types specially -- they get mapped
% to types in the runtime rather than in private_builtin.
%
:- pred hand_defined_type(mer_type::in, type_ctor_category::in, string::out,
list(int)::out) is semidet.
hand_defined_type(Type, CtorCat, SubstituteName, ArrayDims) :-
(
CtorCat = ctor_cat_system(cat_system_type_info),
SubstituteName = "jmercury.runtime.TypeInfo_Struct",
ArrayDims = []
;
CtorCat = ctor_cat_system(cat_system_type_ctor_info),
SubstituteName = "jmercury.runtime.TypeCtorInfo_Struct",
ArrayDims = []
;
CtorCat = ctor_cat_system(cat_system_typeclass_info),
SubstituteName = "/* typeclass_info */ java.lang.Object",
ArrayDims = [0]
;
CtorCat = ctor_cat_system(cat_system_base_typeclass_info),
SubstituteName = "/* base_typeclass_info */ java.lang.Object",
ArrayDims = [0]
;
CtorCat = ctor_cat_user(cat_user_general),
( Type = type_desc_type ->
SubstituteName = "jmercury.runtime.TypeInfo_Struct"
; Type = pseudo_type_desc_type ->
SubstituteName = "jmercury.runtime.PseudoTypeInfo"
; Type = type_ctor_desc_type ->
SubstituteName = "jmercury.runtime.TypeCtorInfo_Struct"
;
fail
),
ArrayDims = []
).
:- pred boxed_type_to_string(java_out_info::in, mlds_type::in, string::out)
is det.
boxed_type_to_string(Info, Type, String) :-
( java_builtin_type(Type, _, JavaBoxedName, _) ->
String = JavaBoxedName
;
type_to_string(Info, Type, String0, ArrayDims),
list.map(array_dimension_to_string, ArrayDims, RevBrackets),
list.reverse(RevBrackets, Brackets),
string.append_list([String0 | Brackets], String)
).
:- pred array_dimension_to_string(int::in, string::out) is det.
array_dimension_to_string(N, String) :-
( N = 0 ->
String = "[]"
;
String = string.format("[%d]", [i(N)])
).
%-----------------------------------------------------------------------------%
%
% Code to output declaration specifiers.
%
:- pred output_decl_flags(java_out_info::in, mlds_decl_flags::in,
io::di, io::uo) is det.
output_decl_flags(Info, Flags, !IO) :-
output_access(Info, access(Flags), !IO),
output_per_instance(per_instance(Flags), !IO),
output_virtuality(Info, virtuality(Flags), !IO),
output_overridability_constness(overridability(Flags), constness(Flags),
!IO),
output_abstractness(abstractness(Flags), !IO).
:- pred output_access(java_out_info::in, access::in, io::di, io::uo) is det.
output_access(Info, Access, !IO) :-
(
Access = acc_public,
io.write_string("public ", !IO)
;
Access = acc_private,
io.write_string("private ", !IO)
;
Access = acc_protected,
io.write_string("protected ", !IO)
;
Access = acc_default,
maybe_output_comment(Info, "default", !IO)
;
Access = acc_local
).
:- pred output_per_instance(per_instance::in, io::di, io::uo) is det.
output_per_instance(PerInstance, !IO) :-
(
PerInstance = per_instance
;
PerInstance = one_copy,
io.write_string("static ", !IO)
).
:- pred output_virtuality(java_out_info::in, virtuality::in,
io::di, io::uo) is det.
output_virtuality(Info, Virtual, !IO) :-
(
Virtual = virtual,
maybe_output_comment(Info, "virtual", !IO)
;
Virtual = non_virtual
).
:- pred output_overridability_constness(overridability::in, constness::in,
io::di, io::uo) is det.
output_overridability_constness(Overridability, Constness, !IO) :-
(
( Overridability = sealed
; Constness = const
)
->
io.write_string("final ", !IO)
;
true
).
:- pred output_abstractness(abstractness::in, io::di, io::uo) is det.
output_abstractness(Abstractness, !IO) :-
(
Abstractness = abstract,
io.write_string("abstract ", !IO)
;
Abstractness = concrete
).
:- pred maybe_output_comment(java_out_info::in, string::in,
io::di, io::uo) is det.
maybe_output_comment(Info, Comment, !IO) :-
AutoComments = Info ^ joi_auto_comments,
(
AutoComments = yes,
io.write_string("/* ", !IO),
io.write_string(Comment, !IO),
io.write_string(" */", !IO)
;
AutoComments = no
).
%-----------------------------------------------------------------------------%
%
% Code to output statements.
%
% These types are used by many of the output_stmt style predicates to
% return information about the statement's control flow,
% i.e. about the different ways in which the statement can exit.
% In general we only output the current statement if the previous
% statement could complete normally (fall through).
% We keep a set of exit methods since some statements (like an
% if-then-else) could potentially break, and also fall through.
:- type exit_methods == set.set(exit_method).
:- type exit_method
---> can_break
; can_continue
; can_return
; can_throw
; can_fall_through. % Where the instruction can complete
% normally and execution can continue
% with the following statement.
:- type code_addr_wrapper
---> code_addr_wrapper(
caw_class :: string,
caw_ptr_num :: maybe(int)
).
:- type func_info
---> func_info(
func_info_params :: mlds_func_params
).
:- func mod_name(mlds_fully_qualified_name(T)) = mlds_module_name.
mod_name(qual(ModuleName, _, _)) = ModuleName.
:- pred output_statements(java_out_info::in, indent::in, func_info::in,
list(statement)::in, exit_methods::out, io::di, io::uo) is det.
output_statements(_, _, _, [], ExitMethods, !IO) :-
ExitMethods = set.make_singleton_set(can_fall_through).
output_statements(Info, Indent, FuncInfo, [Statement | Statements],
ExitMethods, !IO) :-
output_statement(Info, Indent, FuncInfo, Statement,
StmtExitMethods, !IO),
( set.member(can_fall_through, StmtExitMethods) ->
output_statements(Info, Indent, FuncInfo, Statements,
StmtsExitMethods, !IO),
ExitMethods0 = StmtExitMethods `set.union` StmtsExitMethods,
( set.member(can_fall_through, StmtsExitMethods) ->
ExitMethods = ExitMethods0
;
% If the last statement could not complete normally
% the current block can no longer complete normally.
ExitMethods = ExitMethods0 `set.delete` can_fall_through
)
;
% Don't output any more statements from the current list since
% the preceeding statement cannot complete.
ExitMethods = StmtExitMethods
).
:- pred output_statement(java_out_info::in, indent::in,
func_info::in, statement::in, exit_methods::out, io::di, io::uo) is det.
output_statement(Info, Indent, FuncInfo,
statement(Statement, Context), ExitMethods, !IO) :-
output_context(Info, Context, !IO),
output_stmt(Info, Indent, FuncInfo, Statement, Context,
ExitMethods, !IO).
:- pred output_stmt(java_out_info::in, indent::in, func_info::in,
mlds_stmt::in, mlds_context::in, exit_methods::out, io::di, io::uo) is det.
output_stmt(Info, Indent, FuncInfo, Statement, Context, ExitMethods, !IO) :-
(
Statement = ml_stmt_block(Defns, Statements),
indent_line(Indent, !IO),
io.write_string("{\n", !IO),
(
Defns = [_ | _],
output_defns(Info, Indent + 1, force_init, Defns, !IO),
io.write_string("\n", !IO)
;
Defns = []
),
output_statements(Info, Indent + 1, FuncInfo, Statements,
ExitMethods, !IO),
indent_line(Info, Context, Indent, !IO),
io.write_string("}\n", !IO)
;
Statement = ml_stmt_while(Kind, Cond, BodyStatement),
Kind = may_loop_zero_times,
indent_line(Indent, !IO),
io.write_string("while (", !IO),
output_rval(Info, Cond, !IO),
io.write_string(")\n", !IO),
% The contained statement is reachable iff the while statement is
% reachable and the condition expression is not a constant expression
% whose value is false.
( Cond = ml_const(mlconst_false) ->
indent_line(Indent, !IO),
io.write_string("{ /* Unreachable code */ }\n", !IO),
ExitMethods = set.make_singleton_set(can_fall_through)
;
output_statement(Info, Indent + 1, FuncInfo, BodyStatement,
StmtExitMethods, !IO),
ExitMethods = while_exit_methods(Cond, StmtExitMethods)
)
;
Statement = ml_stmt_while(Kind, Cond, BodyStatement),
Kind = loop_at_least_once,
indent_line(Indent, !IO),
io.write_string("do\n", !IO),
output_statement(Info, Indent + 1, FuncInfo, BodyStatement,
StmtExitMethods, !IO),
indent_line(Info, Context, Indent, !IO),
io.write_string("while (", !IO),
output_rval(Info, Cond, !IO),
io.write_string(");\n", !IO),
ExitMethods = while_exit_methods(Cond, StmtExitMethods)
;
Statement = ml_stmt_if_then_else(Cond, Then0, MaybeElse),
% We need to take care to avoid problems caused by the dangling else
% ambiguity.
(
% For examples of the form
%
% if (...)
% if (...)
% ...
% else
% ...
%
% we need braces around the inner `if', otherwise they wouldn't
% parse they way we want them to: Java would match the `else'
% with the inner `if' rather than the outer `if'.
MaybeElse = yes(_),
Then0 = statement(ml_stmt_if_then_else(_, _, no), ThenContext)
->
Then = statement(ml_stmt_block([], [Then0]), ThenContext)
;
Then = Then0
),
indent_line(Indent, !IO),
io.write_string("if (", !IO),
output_rval(Info, Cond, !IO),
io.write_string(")\n", !IO),
output_statement(Info, Indent + 1, FuncInfo, Then,
ThenExitMethods, !IO),
(
MaybeElse = yes(Else),
indent_line(Info, Context, Indent, !IO),
io.write_string("else\n", !IO),
output_statement(Info, Indent + 1, FuncInfo, Else,
ElseExitMethods, !IO),
% An if-then-else statement can complete normally iff the
% then-statement can complete normally or the else-statement
% can complete normally.
ExitMethods = ThenExitMethods `set.union` ElseExitMethods
;
MaybeElse = no,
% An if-then statement can complete normally iff it is reachable.
ExitMethods = ThenExitMethods `set.union`
set.make_singleton_set(can_fall_through)
)
;
Statement = ml_stmt_switch(_Type, Val, _Range, Cases, Default),
indent_line(Info, Context, Indent, !IO),
io.write_string("switch (", !IO),
output_rval_maybe_with_enum(Info, Val, !IO),
io.write_string(") {\n", !IO),
output_switch_cases(Info, Indent + 1, FuncInfo, Context, Cases,
Default, ExitMethods, !IO),
indent_line(Info, Context, Indent, !IO),
io.write_string("}\n", !IO)
;
Statement = ml_stmt_label(_),
unexpected($module, $pred, "labels not supported in Java.")
;
Statement = ml_stmt_goto(goto_label(_)),
unexpected($module, $pred, "gotos not supported in Java.")
;
Statement = ml_stmt_goto(goto_break),
indent_line(Indent, !IO),
io.write_string("break;\n", !IO),
ExitMethods = set.make_singleton_set(can_break)
;
Statement = ml_stmt_goto(goto_continue),
indent_line(Indent, !IO),
io.write_string("continue;\n", !IO),
ExitMethods = set.make_singleton_set(can_continue)
;
Statement = ml_stmt_computed_goto(_, _),
unexpected($module, $pred, "computed gotos not supported in Java.")
;
Statement = ml_stmt_call(Signature, FuncRval, MaybeObject, CallArgs,
Results, _IsTailCall),
Signature = mlds_func_signature(ArgTypes, RetTypes),
indent_line(Indent, !IO),
io.write_string("{\n", !IO),
indent_line(Info, Context, Indent + 1, !IO),
(
Results = []
;
Results = [Lval],
output_lval(Info, Lval, !IO),
io.write_string(" = ", !IO)
;
Results = [_, _ | _],
% for multiple return values,
% we generate the following code:
% { java.lang.Object [] result = <func>(<args>);
% <output1> = (<type1>) result[0];
% <output2> = (<type2>) result[1];
% ...
% }
%
io.write_string("java.lang.Object [] result = ", !IO)
),
( FuncRval = ml_const(mlconst_code_addr(_)) ->
% This is a standard method call.
(
MaybeObject = yes(Object),
output_bracketed_rval(Info, Object, !IO),
io.write_string(".", !IO)
;
MaybeObject = no
),
% This is a standard function call.
output_call_rval(Info, FuncRval, !IO),
io.write_string("(", !IO),
io.write_list(CallArgs, ", ", output_rval(Info), !IO),
io.write_string(")", !IO)
;
% This is a call using a method pointer.
%
% Here we do downcasting, as a call will always return
% something of type java.lang.Object
%
% XXX This is a hack, I can't see any way to do this downcasting
% nicely, as it needs to effectively be wrapped around the method
% call itself, so it acts before this predicate's solution to
% multiple return values, see above.
(
RetTypes = []
;
RetTypes = [RetType],
boxed_type_to_string(Info, RetType, RetTypeString),
io.format("((%s) ", [s(RetTypeString)], !IO)
;
RetTypes = [_, _ | _],
io.write_string("((java.lang.Object[]) ", !IO)
),
(
MaybeObject = yes(Object),
output_bracketed_rval(Info, Object, !IO),
io.write_string(".", !IO)
;
MaybeObject = no
),
list.length(CallArgs, Arity),
( Arity =< max_specialised_method_ptr_arity ->
io.write_string("((jmercury.runtime.MethodPtr", !IO),
io.write_int(Arity, !IO),
io.write_string(") ", !IO),
output_bracketed_rval(Info, FuncRval, !IO),
io.write_string(").call___0_0(", !IO),
output_boxed_args(Info, CallArgs, ArgTypes, !IO)
;
io.write_string("((jmercury.runtime.MethodPtrN) ", !IO),
output_bracketed_rval(Info, FuncRval, !IO),
io.write_string(").call___0_0(", !IO),
output_args_as_array(Info, CallArgs, ArgTypes, !IO)
),
% Closes brackets, and calls unbox methods for downcasting.
% XXX This is a hack, see the above comment.
io.write_string(")", !IO),
(
RetTypes = []
;
RetTypes = [RetType2],
( java_builtin_type(RetType2, _, _, UnboxMethod) ->
io.write_string(").", !IO),
io.write_string(UnboxMethod, !IO),
io.write_string("()", !IO)
;
io.write_string(")", !IO)
)
;
RetTypes = [_, _ | _],
io.write_string(")", !IO)
)
),
io.write_string(";\n", !IO),
( Results = [_, _ | _] ->
% Copy the results from the "result" array into the Result
% lvals (unboxing them as we go).
output_assign_results(Info, Results, RetTypes, 0, Indent + 1,
Context, !IO)
;
true
),
% XXX Is this needed? If present, it causes compiler errors for a
% couple of files in the benchmarks directory. -mjwybrow
%
% ( IsTailCall = tail_call, Results = [] ->
% indent_line(Context, Indent + 1, !IO),
% io.write_string("return;\n", !IO)
% ;
% true
% ),
%
indent_line(Indent, !IO),
io.write_string("}\n", !IO),
ExitMethods = set.make_singleton_set(can_fall_through)
;
Statement = ml_stmt_return(Results),
(
Results = [],
indent_line(Indent, !IO),
io.write_string("return;\n", !IO)
;
Results = [Rval],
indent_line(Indent, !IO),
io.write_string("return ", !IO),
output_rval(Info, Rval, !IO),
io.write_string(";\n", !IO)
;
Results = [_, _ | _],
FuncInfo = func_info(Params),
Params = mlds_func_params(_Args, ReturnTypes),
TypesAndResults = assoc_list.from_corresponding_lists(
ReturnTypes, Results),
io.write_string("return new java.lang.Object[] {\n", !IO),
indent_line(Indent + 1, !IO),
Separator = ",\n" ++ duplicate_char(' ', (Indent + 1) * 2),
io.write_list(TypesAndResults, Separator,
(pred((Type - Result)::in, !.IO::di, !:IO::uo) is det :-
output_boxed_rval(Info, Type, Result, !IO)),
!IO),
io.write_string("\n", !IO),
indent_line(Indent, !IO),
io.write_string("};\n", !IO)
),
ExitMethods = set.make_singleton_set(can_return)
;
Statement = ml_stmt_do_commit(Ref),
indent_line(Indent, !IO),
output_rval(Info, Ref, !IO),
io.write_string(" = new jmercury.runtime.Commit();\n", !IO),
indent_line(Indent, !IO),
io.write_string("throw ", !IO),
output_rval(Info, Ref, !IO),
io.write_string(";\n", !IO),
ExitMethods = set.make_singleton_set(can_throw)
;
Statement = ml_stmt_try_commit(_Ref, Stmt, Handler),
indent_line(Indent, !IO),
io.write_string("try\n", !IO),
indent_line(Indent, !IO),
io.write_string("{\n", !IO),
output_statement(Info, Indent + 1, FuncInfo, Stmt,
TryExitMethods0, !IO),
indent_line(Indent, !IO),
io.write_string("}\n", !IO),
indent_line(Indent, !IO),
io.write_string("catch (jmercury.runtime.Commit commit_variable)\n",
!IO),
indent_line(Indent, !IO),
io.write_string("{\n", !IO),
indent_line(Indent + 1, !IO),
output_statement(Info, Indent + 1, FuncInfo, Handler,
CatchExitMethods, !IO),
indent_line(Indent, !IO),
io.write_string("}\n", !IO),
ExitMethods = (TryExitMethods0 `set.delete` can_throw)
`set.union` CatchExitMethods
;
Statement = ml_stmt_atomic(AtomicStatement),
output_atomic_stmt(Info, Indent, AtomicStatement, Context, !IO),
ExitMethods = set.make_singleton_set(can_fall_through)
).
%-----------------------------------------------------------------------------%
%
% Extra code for handling while-loops.
%
:- func while_exit_methods(mlds_rval, exit_methods) = exit_methods.
while_exit_methods(Cond, BlockExitMethods) = ExitMethods :-
% A while statement cannot complete normally if its condition
% expression is a constant expression with value true, and it
% doesn't contain a reachable break statement that exits the
% while statement.
(
% XXX This is not a sufficient way of testing for a Java
% "constant expression", though determining these accurately
% is a little difficult to do here.
Cond = ml_const(mlconst_true),
not set.member(can_break, BlockExitMethods)
->
% Cannot complete normally
ExitMethods0 = BlockExitMethods `set.delete` can_fall_through
;
ExitMethods0 = BlockExitMethods `set.insert` can_fall_through
),
ExitMethods = (ExitMethods0 `set.delete` can_continue)
`set.delete` can_break.
%-----------------------------------------------------------------------------%
%
% Extra code for handling function calls/returns.
%
:- pred output_args_as_array(java_out_info::in, list(mlds_rval)::in,
list(mlds_type)::in, io::di, io::uo) is det.
output_args_as_array(Info, CallArgs, CallArgTypes, !IO) :-
io.write_string("new java.lang.Object[] { ", !IO),
output_boxed_args(Info, CallArgs, CallArgTypes, !IO),
io.write_string("} ", !IO).
:- pred output_boxed_args(java_out_info::in, list(mlds_rval)::in,
list(mlds_type)::in, io::di, io::uo) is det.
output_boxed_args(_, [], [], !IO).
output_boxed_args(_, [_ | _], [], !IO) :-
unexpected($module, $pred, "length mismatch").
output_boxed_args(_, [], [_ | _], !IO) :-
unexpected($module, $pred, "length mismatch").
output_boxed_args(Info, [CallArg | CallArgs], [CallArgType | CallArgTypes],
!IO) :-
output_boxed_rval(Info, CallArgType, CallArg, !IO),
(
CallArgs = []
;
CallArgs = [_ | _],
io.write_string(", ", !IO),
output_boxed_args(Info, CallArgs, CallArgTypes, !IO)
).
%-----------------------------------------------------------------------------%
%
% Code for handling multiple return values.
%
% When returning multiple values,
% we generate the following code:
% { java.lang.Object [] result = <func>(<args>);
% <output1> = (<type1>) result[0];
% <output2> = (<type2>) result[1];
% ...
% }
%
% This procedure generates the assignments to the outputs.
%
:- pred output_assign_results(java_out_info::in, list(mlds_lval)::in,
list(mlds_type)::in, int::in, indent::in, mlds_context::in,
io::di, io::uo) is det.
output_assign_results(_, [], [], _, _, _, !IO).
output_assign_results(Info, [Lval | Lvals], [Type | Types], ResultIndex,
Indent, Context, !IO) :-
indent_line(Info, Context, Indent, !IO),
output_lval(Info, Lval, !IO),
io.write_string(" = ", !IO),
output_unboxed_result(Info, Type, ResultIndex, !IO),
io.write_string(";\n", !IO),
output_assign_results(Info, Lvals, Types, ResultIndex + 1,
Indent, Context, !IO).
output_assign_results(_, [_ | _], [], _, _, _, _, _) :-
unexpected($module, $pred, "list length mismatch").
output_assign_results(_, [], [_ | _], _, _, _, _, _) :-
unexpected($module, $pred, "list length mismatch").
:- pred output_unboxed_result(java_out_info::in, mlds_type::in, int::in,
io::di, io::uo) is det.
output_unboxed_result(Info, Type, ResultIndex, !IO) :-
( java_builtin_type(Type, _, JavaBoxedName, UnboxMethod) ->
io.write_string("((", !IO),
io.write_string(JavaBoxedName, !IO),
io.write_string(") ", !IO),
io.format("result[%d]).%s()", [i(ResultIndex), s(UnboxMethod)], !IO)
;
io.write_string("(", !IO),
output_type(Info, Type, !IO),
io.write_string(") ", !IO),
io.format("result[%d]", [i(ResultIndex)], !IO)
).
%-----------------------------------------------------------------------------%
%
% Extra code for outputting switch statements.
%
:- pred output_switch_cases(java_out_info::in, indent::in, func_info::in,
mlds_context::in, list(mlds_switch_case)::in, mlds_switch_default::in,
exit_methods::out, io::di, io::uo) is det.
output_switch_cases(Info, Indent, FuncInfo, Context,
[], Default, ExitMethods, !IO) :-
output_switch_default(Info, Indent, FuncInfo, Context, Default,
ExitMethods, !IO).
output_switch_cases(Info, Indent, FuncInfo, Context,
[Case | Cases], Default, ExitMethods, !IO) :-
output_switch_case(Info, Indent, FuncInfo, Context, Case,
CaseExitMethods0, !IO),
output_switch_cases(Info, Indent, FuncInfo, Context, Cases, Default,
CasesExitMethods, !IO),
( set.member(can_break, CaseExitMethods0) ->
CaseExitMethods = (CaseExitMethods0 `set.delete` can_break)
`set.insert` can_fall_through
;
CaseExitMethods = CaseExitMethods0
),
ExitMethods = CaseExitMethods `set.union` CasesExitMethods.
:- pred output_switch_case(java_out_info::in, indent::in, func_info::in,
mlds_context::in, mlds_switch_case::in, exit_methods::out,
io::di, io::uo) is det.
output_switch_case(Info, Indent, FuncInfo, Context, Case, ExitMethods, !IO) :-
Case = mlds_switch_case(FirstCond, LaterConds, Statement),
output_case_cond(Info, Indent, Context, FirstCond, !IO),
list.foldl(output_case_cond(Info, Indent, Context), LaterConds, !IO),
output_statement(Info, Indent + 1, FuncInfo, Statement,
StmtExitMethods, !IO),
( set.member(can_fall_through, StmtExitMethods) ->
indent_line(Info, Context, Indent + 1, !IO),
io.write_string("break;\n", !IO),
ExitMethods = (StmtExitMethods `set.insert` can_break)
`set.delete` can_fall_through
;
% Don't output `break' since it would be unreachable.
ExitMethods = StmtExitMethods
).
:- pred output_case_cond(java_out_info::in, indent::in, mlds_context::in,
mlds_case_match_cond::in, io::di, io::uo) is det.
output_case_cond(Info, Indent, Context, Match, !IO) :-
(
Match = match_value(Val),
indent_line(Info, Context, Indent, !IO),
io.write_string("case ", !IO),
output_rval(Info, Val, !IO),
io.write_string(":\n", !IO)
;
Match = match_range(_, _),
unexpected($module, $pred, "cannot match ranges in Java cases")
).
:- pred output_switch_default(java_out_info::in, indent::in, func_info::in,
mlds_context::in, mlds_switch_default::in, exit_methods::out,
io::di, io::uo) is det.
output_switch_default(Info, Indent, FuncInfo, Context, Default,
ExitMethods, !IO) :-
(
Default = default_do_nothing,
ExitMethods = set.make_singleton_set(can_fall_through)
;
Default = default_case(Statement),
indent_line(Info, Context, Indent, !IO),
io.write_string("default:\n", !IO),
output_statement(Info, Indent + 1, FuncInfo, Statement, ExitMethods,
!IO)
;
Default = default_is_unreachable,
indent_line(Info, Context, Indent, !IO),
io.write_string("default: /*NOTREACHED*/\n", !IO),
indent_line(Info, Context, Indent + 1, !IO),
io.write_string("throw new jmercury.runtime.UnreachableDefault();\n",
!IO),
ExitMethods = set.make_singleton_set(can_throw)
).
%-----------------------------------------------------------------------------%
%
% Code for outputting atomic statements.
%
:- pred output_atomic_stmt(java_out_info::in, indent::in,
mlds_atomic_statement::in, mlds_context::in, io::di, io::uo) is det.
output_atomic_stmt(Info, Indent, AtomicStmt, Context, !IO) :-
(
AtomicStmt = comment(Comment),
% XXX We should escape any "*/"'s in the Comment. We should also split
% the comment into lines and indent each line appropriately.
indent_line(Indent, !IO),
io.write_string("/* ", !IO),
io.write_string(Comment, !IO),
io.write_string(" */\n", !IO)
;
AtomicStmt = assign(Lval, Rval),
indent_line(Indent, !IO),
output_lval(Info, Lval, !IO),
io.write_string(" = ", !IO),
output_rval(Info, Rval, !IO),
io.write_string(";\n", !IO)
;
AtomicStmt = assign_if_in_heap(_, _),
sorry($module, $pred, "assign_if_in_heap")
;
AtomicStmt = delete_object(_Lval),
unexpected($module, $pred, "delete_object not supported in Java.")
;
AtomicStmt = new_object(Target, _MaybeTag, ExplicitSecTag, Type,
_MaybeSize, MaybeCtorName, Args, ArgTypes, _MayUseAtomic,
_AllocId),
(
ExplicitSecTag = yes,
unexpected($module, $pred, "explicit secondary tag")
;
ExplicitSecTag = no
),
indent_line(Indent, !IO),
io.write_string("{\n", !IO),
indent_line(Info, Context, Indent + 1, !IO),
output_lval(Info, Target, !IO),
io.write_string(" = new ", !IO),
% Generate class constructor name.
(
MaybeCtorName = yes(QualifiedCtorId),
\+ (
Type = mercury_type(MerType, CtorCat, _),
hand_defined_type(MerType, CtorCat, _, _)
)
->
output_type(Info, Type, !IO),
io.write_char('.', !IO),
QualifiedCtorId = qual(_ModuleName, _QualKind, CtorDefn),
CtorDefn = ctor_id(CtorName, CtorArity),
output_unqual_class_name(CtorName, CtorArity, !IO)
;
output_type(Info, Type, !IO)
),
IsArray = type_is_array(Type),
(
IsArray = is_array,
% The new object will be an array, so we need to initialise it
% using array literals syntax.
io.write_string(" {", !IO),
output_init_args(Info, Args, ArgTypes, !IO),
io.write_string("};\n", !IO)
;
IsArray = not_array,
% Generate constructor arguments.
io.write_string("(", !IO),
output_init_args(Info, Args, ArgTypes, !IO),
io.write_string(");\n", !IO)
),
indent_line(Indent, !IO),
io.write_string("}\n", !IO)
;
AtomicStmt = gc_check,
unexpected($module, $pred, "gc_check not implemented.")
;
AtomicStmt = mark_hp(_Lval),
unexpected($module, $pred, "mark_hp not implemented.")
;
AtomicStmt = restore_hp(_Rval),
unexpected($module, $pred, "restore_hp not implemented.")
;
AtomicStmt = trail_op(_TrailOp),
unexpected($module, $pred, "trail_ops not implemented.")
;
AtomicStmt = inline_target_code(TargetLang, Components),
(
TargetLang = ml_target_java,
indent_line(Indent, !IO),
list.foldl(output_target_code_component(Info), Components, !IO)
;
( TargetLang = ml_target_c
; TargetLang = ml_target_gnu_c
; TargetLang = ml_target_asm
; TargetLang = ml_target_il
; TargetLang = ml_target_csharp
),
unexpected($module, $pred,
"inline_target_code only works for lang_java")
)
;
AtomicStmt = outline_foreign_proc(_TargetLang, _Vs, _Lvals, _Code),
unexpected($module, $pred,
"foreign language interfacing not implemented")
).
%-----------------------------------------------------------------------------%
:- pred output_target_code_component(java_out_info::in,
target_code_component::in, io::di, io::uo) is det.
output_target_code_component(Info, TargetCode, !IO) :-
(
TargetCode = user_target_code(CodeString, MaybeUserContext, _Attrs),
(
MaybeUserContext = yes(ProgContext),
output_context(Info, mlds_make_context(ProgContext), !IO)
;
MaybeUserContext = no
),
io.write_string(CodeString, !IO)
;
TargetCode = raw_target_code(CodeString, _Attrs),
io.write_string(CodeString, !IO)
;
TargetCode = target_code_input(Rval),
output_rval(Info, Rval, !IO)
;
TargetCode = target_code_output(Lval),
output_lval(Info, Lval, !IO)
;
TargetCode = target_code_type(Type),
InfoGenerics = Info ^ joi_output_generics := do_output_generics,
output_type(InfoGenerics, Type, !IO)
;
TargetCode = target_code_name(Name),
output_maybe_qualified_name(Info, Name, !IO)
;
TargetCode = target_code_alloc_id(_),
unexpected($module, $pred, "target_code_alloc_id not implemented")
).
%-----------------------------------------------------------------------------%
% Output initial values of an object's fields as arguments for the
% object's class constructor.
%
:- pred output_init_args(java_out_info::in, list(mlds_rval)::in,
list(mlds_type)::in, io::di, io::uo) is det.
output_init_args(_, [], [], !IO).
output_init_args(_, [_ | _], [], _, _) :-
unexpected($module, $pred, "length mismatch.").
output_init_args(_, [], [_ | _], _, _) :-
unexpected($module, $pred, "length mismatch.").
output_init_args(Info, [Arg | Args], [_ArgType | ArgTypes], !IO) :-
output_rval(Info, Arg, !IO),
(
Args = []
;
Args = [_ | _],
io.write_string(", ", !IO)
),
output_init_args(Info, Args, ArgTypes, !IO).
%-----------------------------------------------------------------------------%
%
% Code to output expressions.
%
:- pred output_lval(java_out_info::in, mlds_lval::in, io::di, io::uo) is det.
output_lval(Info, Lval, !IO) :-
(
Lval = ml_field(_MaybeTag, PtrRval, FieldId, FieldType, _),
(
FieldId = ml_field_offset(OffsetRval),
(
( FieldType = mlds_generic_type
; FieldType = mercury_type(type_variable(_, _), _, _)
)
->
true
;
% The field type for field(_, _, offset(_), _, _) lvals
% must be something that maps to MR_Box.
unexpected($module, $pred, "unexpected field type")
),
% XXX We shouldn't need this cast here, but there are cases where
% it is needed and the MLDS doesn't seem to generate it.
io.write_string("((java.lang.Object[]) ", !IO),
output_rval(Info, PtrRval, !IO),
io.write_string(")[", !IO),
output_rval(Info, OffsetRval, !IO),
io.write_string("]", !IO)
;
FieldId = ml_field_named(FieldName, CtorType),
(
FieldName = qual(_, _, UnqualFieldName),
UnqualFieldName = "data_tag"
->
% If the field we are trying to access is just a `data_tag'
% then it is a member of the base class.
output_bracketed_rval(Info, PtrRval, !IO),
io.write_string(".", !IO)
;
PtrRval = ml_self(_)
->
% Suppress type cast on `this' keyword. This makes a
% difference when assigning to `final' member variables in
% constructor functions.
output_rval(Info, PtrRval, !IO),
io.write_string(".", !IO)
;
% Otherwise the field we are trying to access may be
% in a derived class. Objects are manipulated as instances
% of their base class, so we need to downcast to the derived
% class to access some fields.
io.write_string("((", !IO),
output_type(Info, CtorType, !IO),
io.write_string(") ", !IO),
output_bracketed_rval(Info, PtrRval, !IO),
io.write_string(").", !IO)
),
FieldName = qual(_, _, UnqualFieldName),
output_valid_mangled_name(UnqualFieldName, !IO)
)
;
Lval = ml_mem_ref(Rval, _Type),
output_bracketed_rval(Info, Rval, !IO)
;
Lval = ml_global_var_ref(GlobalVarRef),
GlobalVarRef = env_var_ref(EnvVarName),
io.write_string("mercury_envvar_", !IO),
io.write_string(EnvVarName, !IO)
;
Lval = ml_var(qual(ModName, QualKind, Name), _),
QualName = qual(ModName, QualKind, entity_data(mlds_data_var(Name))),
output_maybe_qualified_name(Info, QualName, !IO)
).
:- pred output_mangled_name(string::in, io::di, io::uo) is det.
output_mangled_name(Name, !IO) :-
MangledName = name_mangle(Name),
io.write_string(MangledName, !IO).
:- pred output_valid_mangled_name(string::in, io::di, io::uo) is det.
output_valid_mangled_name(Name, !IO) :-
MangledName = name_mangle(Name),
JavaSafeName = valid_java_symbol_name(MangledName),
io.write_string(JavaSafeName, !IO).
:- pred output_call_rval(java_out_info::in, mlds_rval::in, io::di, io::uo)
is det.
output_call_rval(Info, Rval, !IO) :-
(
Rval = ml_const(Const),
Const = mlconst_code_addr(CodeAddr)
->
IsCall = yes,
mlds_output_code_addr(Info, CodeAddr, IsCall, !IO)
;
output_bracketed_rval(Info, Rval, !IO)
).
:- pred output_bracketed_rval(java_out_info::in, mlds_rval::in, io::di, io::uo)
is det.
output_bracketed_rval(Info, Rval, !IO) :-
(
% If it's just a variable name, then we don't need parentheses.
( Rval = ml_lval(ml_var(_,_))
; Rval = ml_const(mlconst_code_addr(_))
)
->
output_rval(Info, Rval, !IO)
;
io.write_char('(', !IO),
output_rval(Info, Rval, !IO),
io.write_char(')', !IO)
).
:- pred output_rval(java_out_info::in, mlds_rval::in, io::di, io::uo) is det.
output_rval(Info, Rval, !IO) :-
(
Rval = ml_lval(Lval),
output_lval(Info, Lval, !IO)
;
Rval = ml_mkword(_, _),
unexpected($module, $pred, "tags not supported in Java")
;
Rval = ml_const(Const),
output_rval_const(Info, Const, !IO)
;
Rval = ml_unop(Op, RvalA),
output_unop(Info, Op, RvalA, !IO)
;
Rval = ml_binop(Op, RvalA, RvalB),
output_binop(Info, Op, RvalA, RvalB, !IO)
;
Rval = ml_mem_addr(_Lval),
unexpected($module, $pred, "mem_addr(_) not supported")
;
Rval = ml_scalar_common(_),
% This reference is not the same as a mlds_data_addr const.
unexpected($module, $pred, "ml_scalar_common")
;
Rval = ml_vector_common_row(VectorCommon, RowRval),
output_vector_common_row_rval(Info, VectorCommon, RowRval, !IO)
;
Rval = ml_self(_),
io.write_string("this", !IO)
).
:- pred output_unop(java_out_info::in, mlds_unary_op::in, mlds_rval::in,
io::di, io::uo) is det.
output_unop(Info, Unop, Expr, !IO) :-
(
Unop = cast(Type),
output_cast_rval(Info, Type, Expr, !IO)
;
Unop = box(Type),
output_boxed_rval(Info, Type, Expr, !IO)
;
Unop = unbox(Type),
output_unboxed_rval(Info, Type, Expr, !IO)
;
Unop = std_unop(StdUnop),
output_std_unop(Info, StdUnop, Expr, !IO)
).
:- pred output_cast_rval(java_out_info::in, mlds_type::in, mlds_rval::in,
io::di, io::uo) is det.
output_cast_rval(Info, Type, Expr, !IO) :-
% rtti_to_mlds.m generates casts from int to
% jmercury.runtime.PseudoTypeInfo, but for Java
% we need to treat these as constructions, not casts.
% Similarly for conversions from TypeCtorInfo to TypeInfo.
(
Type = mlds_pseudo_type_info_type,
Expr = ml_const(mlconst_int(N))
->
maybe_output_comment(Info, "cast", !IO),
( have_preallocated_pseudo_type_var(N) ->
io.write_string("jmercury.runtime.PseudoTypeInfo.K", !IO),
io.write_int(N, !IO)
;
io.write_string("new jmercury.runtime.PseudoTypeInfo(", !IO),
output_rval(Info, Expr, !IO),
io.write_string(")", !IO)
)
;
( Type = mercury_type(_, ctor_cat_system(cat_system_type_info), _)
; Type = mlds_type_info_type
)
->
% XXX We really should be able to tell if we are casting a
% TypeCtorInfo or a TypeInfo. Julien says that's probably going to
% be rather difficult as the compiler doesn't keep track of where
% type_ctor_infos are acting as type_infos properly.
maybe_output_comment(Info, "cast", !IO),
io.write_string("jmercury.runtime.TypeInfo_Struct.maybe_new(",
!IO),
output_rval(Info, Expr, !IO),
io.write_string(")", !IO)
;
java_builtin_type(Type, "int", _, _)
->
io.write_string("(int) ", !IO),
output_rval_maybe_with_enum(Info, Expr, !IO)
;
io.write_string("(", !IO),
output_type(Info, Type, !IO),
io.write_string(") ", !IO),
output_rval(Info, Expr, !IO)
).
:- pred have_preallocated_pseudo_type_var(int::in) is semidet.
have_preallocated_pseudo_type_var(N) :-
% Corresponds to static members in class PseudoTypeInfo.
N >= 1,
N =< 5.
:- pred output_boxed_rval(java_out_info::in, mlds_type::in, mlds_rval::in,
io::di, io::uo) is det.
output_boxed_rval(Info, Type, Expr, !IO) :-
( java_builtin_type(Type, _JavaName, JavaBoxedName, _) ->
% valueOf may return cached instances instead of creating new objects.
io.write_string(JavaBoxedName, !IO),
io.write_string(".valueOf(", !IO),
output_rval(Info, Expr, !IO),
io.write_string(")", !IO)
;
io.write_string("((java.lang.Object) (", !IO),
output_rval(Info, Expr, !IO),
io.write_string("))", !IO)
).
:- pred output_unboxed_rval(java_out_info::in, mlds_type::in, mlds_rval::in,
io::di, io::uo) is det.
output_unboxed_rval(Info, Type, Expr, !IO) :-
( java_builtin_type(Type, _, JavaBoxedName, UnboxMethod) ->
io.write_string("((", !IO),
io.write_string(JavaBoxedName, !IO),
io.write_string(") ", !IO),
output_bracketed_rval(Info, Expr, !IO),
io.write_string(").", !IO),
io.write_string(UnboxMethod, !IO),
io.write_string("()", !IO)
;
io.write_string("((", !IO),
output_type(Info, Type, !IO),
io.write_string(") ", !IO),
output_rval(Info, Expr, !IO),
io.write_string(")", !IO)
).
% java_builtin_type(MLDS_Type, JavaUnboxedType, JavaBoxedType, UnboxMethod):
%
% For a given Mercury type, check if this corresponds to a Java type
% which has both unboxed (builtin) and boxed (class) versions, and if so,
% return their names, and the name of the method to get the unboxed value
% from the boxed type.
%
:- pred java_builtin_type(mlds_type::in, string::out, string::out, string::out)
is semidet.
java_builtin_type(Type, "int", "java.lang.Integer", "intValue") :-
Type = mlds_native_int_type.
java_builtin_type(Type, "int", "java.lang.Integer", "intValue") :-
Type = mercury_type(builtin_type(builtin_type_int), _, _).
java_builtin_type(Type, "double", "java.lang.Double", "doubleValue") :-
Type = mlds_native_float_type.
java_builtin_type(Type, "double", "java.lang.Double", "doubleValue") :-
Type = mercury_type(builtin_type(builtin_type_float), _, _).
% Java `char' not large enough for code points so we must use `int'.
java_builtin_type(Type, "int", "java.lang.Integer", "intValue") :-
Type = mlds_native_char_type.
java_builtin_type(Type, "int", "java.lang.Integer", "intValue") :-
Type = mercury_type(builtin_type(builtin_type_char), _, _).
java_builtin_type(Type, "boolean", "java.lang.Boolean", "booleanValue") :-
Type = mlds_native_bool_type.
% io.state and store.store(S) are dummy variables
% for which we pass an arbitrary integer. For this
% reason they should have the Java type `int'.
%
java_builtin_type(Type, "int", "java.lang.Integer", "intValue") :-
% The test for defined/3 is logically redundant since all dummy
% types are defined types, but enables the compiler to infer that
% this disjunction is a switch.
Type = mercury_type(defined_type(_, _, _), TypeCtorCat, _),
TypeCtorCat = ctor_cat_builtin_dummy.
:- pred output_std_unop(java_out_info::in, builtin_ops.unary_op::in,
mlds_rval::in, io::di, io::uo) is det.
% For the Java back-end, there are no tags, so all the tagging operators
% are no-ops, except for `tag', which always returns zero (a tag of zero
% means there's no tag).
%
output_std_unop(Info, UnaryOp, Expr, !IO) :-
( UnaryOp = tag ->
io.write_string("/* tag */ 0", !IO)
;
java_unary_prefix_op(UnaryOp, UnaryOpString),
io.write_string(UnaryOpString, !IO),
io.write_string("(", !IO),
output_rval(Info, Expr, !IO),
io.write_string(")", !IO)
).
:- pred output_binop(java_out_info::in, binary_op::in, mlds_rval::in,
mlds_rval::in, io::di, io::uo) is det.
output_binop(Info, Op, X, Y, !IO) :-
( Op = array_index(_Type) ->
output_bracketed_rval(Info, X, !IO),
io.write_string("[", !IO),
output_rval(Info, Y, !IO),
io.write_string("]", !IO)
; java_string_compare_op(Op, OpStr) ->
( OpStr = "==" ->
output_rval(Info, X, !IO),
io.write_string(".equals(", !IO),
output_rval(Info, Y, !IO),
io.write_string(")", !IO)
;
io.write_string("(", !IO),
output_rval(Info, X, !IO),
io.write_string(".compareTo(", !IO),
output_rval(Info, Y, !IO),
io.write_string(") ", !IO),
io.write_string(OpStr, !IO),
io.write_string(" 0)", !IO)
)
; rval_is_enum_object(X) ->
io.write_string("(", !IO),
output_rval(Info, X, !IO),
io.write_string(".MR_value ", !IO),
output_binary_op(Op, !IO),
io.write_string(" ", !IO),
output_rval(Info, Y, !IO),
io.write_string(".MR_value)", !IO)
;
io.write_string("(", !IO),
output_rval(Info, X, !IO),
io.write_string(" ", !IO),
output_binary_op(Op, !IO),
io.write_string(" ", !IO),
output_rval(Info, Y, !IO),
io.write_string(")", !IO)
).
% Output an Rval and if the Rval is an enumeration object append the string
% ".MR_value", so we can access its value field.
%
% XXX Note that this is necessary in some places, but not in others.
% For example, it is important to do so for switch statements, as the
% argument of a switch _must_ be an integer in Java. However, adding
% the .MR_value to assignments breaks some casting... At some point, we
% need to go through all the places where output_rval and
% output_rval_maybe_with_enum are called and make sure the correct one
% is being used.
%
:- pred output_rval_maybe_with_enum(java_out_info::in, mlds_rval::in,
io::di, io::uo) is det.
output_rval_maybe_with_enum(Info, Rval, !IO) :-
output_rval(Info, Rval, !IO),
( rval_is_enum_object(Rval) ->
io.write_string(".MR_value", !IO)
;
true
).
:- pred output_binary_op(binary_op::in, io::di, io::uo) is det.
output_binary_op(Op, !IO) :-
% XXX why are these separated into three predicates?
( java_binary_infix_op(Op, OpStr) ->
io.write_string(OpStr, !IO)
; java_float_compare_op(Op, OpStr) ->
io.write_string(OpStr, !IO)
; java_float_op(Op, OpStr) ->
io.write_string(OpStr, !IO)
;
unexpected($module, $pred, "invalid binary operator")
).
:- pred output_rval_const(java_out_info::in, mlds_rval_const::in,
io::di, io::uo) is det.
output_rval_const(Info, Const, !IO) :-
(
Const = mlconst_true,
io.write_string("true", !IO)
;
Const = mlconst_false,
io.write_string("false", !IO)
;
Const = mlconst_int(N),
output_int_const(N, !IO)
;
Const = mlconst_char(N),
io.write_string("(", !IO),
output_int_const(N, !IO),
io.write_string(")", !IO)
;
Const = mlconst_enum(N, EnumType),
output_type(Info, EnumType, !IO),
io.write_string(".K", !IO),
output_int_const(N, !IO)
;
Const = mlconst_foreign(Lang, Value, _Type),
expect(unify(Lang, lang_java), $module, $pred,
"language other than Java."),
% XXX Should we parenthesize this?
io.write_string(Value, !IO)
;
Const = mlconst_float(FloatVal),
c_util.output_float_literal(FloatVal, !IO)
;
Const = mlconst_string(String),
io.write_string("""", !IO),
c_util.output_quoted_string_lang(literal_java, String, !IO),
io.write_string("""", !IO)
;
Const = mlconst_multi_string(String),
io.write_string("""", !IO),
c_util.output_quoted_multi_string_lang(literal_java, String, !IO),
io.write_string("""", !IO)
;
Const = mlconst_named_const(NamedConst),
io.write_string(NamedConst, !IO)
;
Const = mlconst_code_addr(CodeAddr),
IsCall = no,
mlds_output_code_addr(Info, CodeAddr, IsCall, !IO)
;
Const = mlconst_data_addr(DataAddr),
mlds_output_data_addr(DataAddr, !IO)
;
Const = mlconst_null(Type),
Initializer = get_java_type_initializer(Type),
io.write_string(Initializer, !IO)
).
:- pred output_int_const(int::in, io::di, io::uo) is det.
output_int_const(N, !IO) :-
% The Mercury compiler could be using 64-bit integers but Java has 32-bit
% ints. A literal 0xffffffff in a source file would be interpreted by a
% 64-bit Mercury compiler as 4294967295. If it is written out in decimal a
% Java compiler would rightly complain because the integer is too large to
% fit in a 32-bit int. However, it won't complain if the literal is
% expressed in hexadecimal (nor as the negative decimal -1).
( N < 0 ->
io.write_int(N, !IO)
;
N >> 32 = 0,
N /\ 0x80000000 = 0x80000000
->
% The bit pattern fits in 32 bits, but is too large to write as a
% positive decimal. This branch is unreachable on a 32-bit compiler.
io.format("0x%x", [i(N /\ 0xffffffff)], !IO)
;
io.write_int(N, !IO)
).
:- pred output_vector_common_row_rval(java_out_info::in,
mlds_vector_common::in, mlds_rval::in, io::di, io::uo) is det.
output_vector_common_row_rval(Info, VectorCommon, RowRval, !IO) :-
VectorCommon = ml_vector_common(_ModuleName, _Type,
ml_vector_common_type_num(TypeNum), StartRowNum, _NumRows),
io.format("MR_vector_common_%d[%d + ", [i(TypeNum), i(StartRowNum)], !IO),
output_rval(Info, RowRval, !IO),
io.write_string("]", !IO).
%-----------------------------------------------------------------------------%
:- pred mlds_output_code_addr(java_out_info::in, mlds_code_addr::in, bool::in,
io::di, io::uo) is det.
mlds_output_code_addr(Info, CodeAddr, IsCall, !IO) :-
(
IsCall = no,
% Not a function call, so we are taking the address of the
% wrapper for that function (method).
io.write_string("new ", !IO),
AddrOfMap = Info ^ joi_addrof_map,
map.lookup(AddrOfMap, CodeAddr, CodeAddrWrapper),
CodeAddrWrapper = code_addr_wrapper(ClassName, MaybePtrNum),
io.write_string(ClassName, !IO),
io.write_string("_0(", !IO),
(
MaybePtrNum = yes(PtrNum),
io.write_int(PtrNum, !IO)
;
MaybePtrNum = no
),
io.write_string(")", !IO)
;
IsCall = yes,
(
CodeAddr = code_addr_proc(Label, _Sig),
output_fully_qualified_thing(Label, mlds_output_proc_label, !IO)
;
CodeAddr = code_addr_internal(Label, SeqNum, _Sig),
output_fully_qualified_thing(Label, mlds_output_proc_label, !IO),
io.write_string("_", !IO),
io.write_int(SeqNum, !IO)
)
).
:- pred mlds_output_proc_label(mlds_proc_label::in, io::di, io::uo) is det.
mlds_output_proc_label(mlds_proc_label(PredLabel, ProcId), !IO) :-
output_pred_label(PredLabel, !IO),
proc_id_to_int(ProcId, ModeNum),
io.format("_%d", [i(ModeNum)], !IO).
:- pred mlds_output_data_addr(mlds_data_addr::in, io::di, io::uo) is det.
mlds_output_data_addr(data_addr(ModuleQualifier, DataName), !IO) :-
SymName = mlds_module_name_to_sym_name(ModuleQualifier),
mangle_sym_name_for_java(SymName, module_qual, "__", ModuleName),
io.write_string(ModuleName, !IO),
io.write_string(".", !IO),
output_data_name(DataName, !IO).
%-----------------------------------------------------------------------------%
%
% Miscellaneous stuff to handle indentation and generation of
% source context annotations.
%
:- mutable(last_context, prog_context, context_init, ground,
[untrailed, attach_to_io_state]).
:- pred output_context(java_out_info::in, mlds_context::in,
io::di, io::uo) is det.
output_context(Info, Context, !IO) :-
LineNumbers = Info ^ joi_line_numbers,
(
LineNumbers = yes,
ProgContext = mlds_get_prog_context(Context),
get_last_context(LastContext, !IO),
term.context_file(ProgContext, File),
term.context_line(ProgContext, Line),
(
ProgContext \= LastContext,
Line > 0,
File \= ""
->
% Java doesn't have an equivalent of #line directives.
% \u is treated as a Unicode escape even with comments.
io.write_string("// ", !IO),
string.replace_all(File, "\\u", "\\\\u", SafePath),
io.write_string(SafePath, !IO),
io.write_string(":", !IO),
io.write_int(Line, !IO),
io.nl(!IO),
set_last_context(ProgContext, !IO)
;
true
)
;
LineNumbers = no
).
:- pred indent_line(java_out_info::in, mlds_context::in, indent::in,
io::di, io::uo) is det.
indent_line(Info, Context, N, !IO) :-
output_context(Info, Context, !IO),
indent_line(N, !IO).
% A value of type `indent' records the number of levels of indentation
% to indent the next piece of code. Currently we output two spaces
% for each level of indentation.
% XXX There is a small amount of code duplication with mlds_to_c.m here.
:- type indent == int.
:- pred indent_line(indent::in, io::di, io::uo) is det.
indent_line(N, !IO) :-
( N =< 0 ->
true
;
io.write_string(" ", !IO),
indent_line(N - 1, !IO)
).
%-----------------------------------------------------------------------------%
:- type java_out_info
---> java_out_info(
% These are static.
joi_module_info :: module_info,
joi_auto_comments :: bool,
joi_line_numbers :: bool,
joi_module_name :: mlds_module_name,
joi_addrof_map :: map(mlds_code_addr, code_addr_wrapper),
% These are dynamic.
joi_output_generics :: output_generics,
joi_univ_tvars :: list(tvar)
).
:- type output_generics
---> do_output_generics
; do_not_output_generics.
:- func init_java_out_info(module_info, map(mlds_code_addr, code_addr_wrapper))
= java_out_info.
init_java_out_info(ModuleInfo, AddrOfMap) = Info :-
module_info_get_globals(ModuleInfo, Globals),
globals.lookup_bool_option(Globals, auto_comments, AutoComments),
globals.lookup_bool_option(Globals, line_numbers, LineNumbers),
module_info_get_name(ModuleInfo, ModuleName),
MLDS_ModuleName = mercury_module_name_to_mlds(ModuleName),
Info = java_out_info(ModuleInfo, AutoComments, LineNumbers,
MLDS_ModuleName, AddrOfMap, do_not_output_generics, []).
%-----------------------------------------------------------------------------%
:- end_module ml_backend.mlds_to_java.
%-----------------------------------------------------------------------------%