Files
mercury/compiler/mlds_to_java_class.m
Zoltan Somogyi 58bc167ff4 Separate enum classes from other classes in MLDS.
Classes representing enums have always treated differently from the
other kinds of classes by many pieces of code that operate on classes.
Representing them using a separate construct codifies this, and allows us
to encode the unique invariants applying to enum classes in the types.

compiler/mlds.m:
    Define a new type, mlds_enum_class_defn, which is a copy of the old
    mlds_class_defn type which has been specialized for the classes
    representing enums. This is done by deleting the fields that are
    not applicable to enum classes, and restricting the types of some
    other fields.

    Delete the mlds_enum alternative from the mlds_class_kind type,
    which is still part of the mlds_class_defn type, thus indicating
    that enums can no longer be represented by mlds_class_defns.

    Separate mlds_enum_class_id from mlds_class_ids.
    Separate mlds_enum_class_types from mlds_class_types.

    Include the list of mlds_enum_class_defns in the overall MLDS structure.

compiler/ml_type_gen.m:
    Construct mlds_enum_class_defns, not mlds_class_defn, to represent
    enum types when using the high-level data representation (i.e. when
    targeting C# and Java).

compiler/mlds_to_cs_file.m:
compiler/mlds_to_java_file.m:
    Output the mlds_enum_class_defns as well as the mlds_class_defns.

compiler/mlds_to_c_file.m:
    Insist of there being no mlds_enum_class_defns to output.

compiler/mlds_to_cs_class.m:
compiler/mlds_to_java_class.m:
    Add code to output mlds_enum_class_defns, as a copy of the original
    code to output mlds_class_defns, but specialized for the invariants
    of mlds_enum_class_defns.

compiler/mlds_to_c_class.m:
    Update some existing code to output enum constant definitions.
    Since we never generate classes for enum types when targeting C,
    and the separation of mlds_enum_class_defns from mlds_class_defns
    makes this clear, this code is now unused, so mark it with a
    consider_used pragma.

compiler/ml_accurate_gc.m:
compiler/ml_elim_nested.m:
compiler/ml_lookup_switch.m:
compiler/ml_rename_classes.m:
compiler/ml_simplify_switch.m:
compiler/ml_top_gen.m:
compiler/mlds_dump.m:
compiler/mlds_to_c_data.m:
compiler/mlds_to_c_export.m:
compiler/mlds_to_c_stmt.m:
compiler/mlds_to_c_type.m:
compiler/mlds_to_cs_data.m:
compiler/mlds_to_cs_export.m:
compiler/mlds_to_cs_type.m:
compiler/mlds_to_java_data.m:
compiler/mlds_to_java_export.m:
compiler/mlds_to_java_type.m:
    Conform to the changes above.

compiler/mlds_to_target_util.m:
    Delete now no-longer-needed predicate.
2023-06-13 08:47:09 +02:00

482 lines
18 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 2000-2012 The University of Melbourne.
% Copyright (C) 2013-2018, 2020 The Mercury team.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%---------------------------------------------------------------------------%
%
% Output class declarations and definitions in Java.
%
%---------------------------------------------------------------------------%
:- module ml_backend.mlds_to_java_class.
:- interface.
:- import_module libs.
:- import_module libs.indent.
:- import_module ml_backend.mlds.
:- import_module ml_backend.mlds_to_java_util.
:- import_module io.
:- import_module map.
%---------------------------------------------------------------------------%
:- pred output_class_defn_for_java(java_out_info::in,
io.text_output_stream::in, indent::in, mlds_class_defn::in,
io::di, io::uo) is det.
:- pred output_enum_class_defn_for_java(java_out_info::in,
io.text_output_stream::in, indent::in, mlds_enum_class_defn::in,
io::di, io::uo) is det.
%---------------------------------------------------------------------------%
% 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 maybe_shorten_long_class_name(
mlds_class_defn::in, mlds_class_defn::out,
map(mlds_class_name, mlds_class_name)::in,
map(mlds_class_name, mlds_class_name)::out) is det.
%---------------------------------------------------------------------------%
% Succeeds iff a given string matches the unqualified interface name
% of a interface in Mercury's Java runtime system.
%
:- pred interface_is_special_for_java(string::in) is semidet.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- import_module backend_libs.
:- import_module backend_libs.c_util.
:- import_module hlds.
:- import_module hlds.hlds_module.
:- import_module mdbcomp.
:- import_module mdbcomp.sym_name.
:- import_module ml_backend.ml_code_util.
:- import_module ml_backend.mlds_to_java_data.
:- import_module ml_backend.mlds_to_java_func.
:- import_module ml_backend.mlds_to_java_name.
:- import_module ml_backend.mlds_to_java_type.
:- import_module ml_backend.mlds_to_target_util.
:- import_module parse_tree.
:- import_module parse_tree.java_names.
:- import_module parse_tree.prog_data.
:- import_module parse_tree.prog_foreign.
:- import_module bool.
:- import_module char.
:- import_module int.
:- import_module list.
:- import_module maybe.
:- import_module require.
:- import_module string.
:- import_module term.
%---------------------------------------------------------------------------%
%
% Code to output classes.
%
output_class_defn_for_java(Info0, Stream, Indent, ClassDefn, !IO) :-
ClassDefn = mlds_class_defn(ClassName, ClassArity, Context, Flags, Kind,
_Imports, Inherits, Implements, TypeParams,
MemberFields, MemberClasses, MemberMethods, Ctors),
indent_line_after_context(Stream, Info0 ^ joi_line_numbers,
marker_comment, Context, Indent, !IO),
output_class_decl_flags_for_java(Info0, Stream, Flags, !IO),
Info1 = Info0 ^ joi_univ_tvars := TypeParams,
% Use generics in the output of this predicate if this class represents
% a Mercury type. Note that we do NOT return Info1 or Info to our caller.
( if list.member(ml_java_mercury_type_interface, Implements) then
Info = Info1 ^ joi_output_generics := do_output_generics
else
Info = Info1
),
KindStr = class_kind_to_string_for_java(Kind),
ClassNameStr = unqual_class_name_to_string_for_java(ClassName, ClassArity),
io.format(Stream, "%s %s", [s(KindStr), s(ClassNameStr)], !IO),
OutputGenerics = Info ^ joi_output_generics,
(
OutputGenerics = do_output_generics,
output_generic_tvars(Stream, TypeParams, !IO)
;
OutputGenerics = do_not_output_generics
),
io.nl(Stream, !IO),
Indent1 = Indent + 1,
output_inherits_list(Info, Stream, Indent1, Inherits, !IO),
output_implements_list(Stream, Indent1, Implements, !IO),
IndentStr = indent2_string(Indent),
io.format(Stream, "%s{\n", [s(IndentStr)], !IO),
(
( Kind = mlds_class
; Kind = mlds_interface
),
list.foldl(
output_field_var_defn_for_java(Info, Stream, Indent1),
MemberFields, !IO),
list.foldl(
output_class_defn_for_java(Info, Stream, Indent1),
MemberClasses, !IO),
list.foldl(
output_function_defn_for_java(Info, Stream, Indent1, oa_none),
MemberMethods, !IO)
;
Kind = mlds_struct,
unexpected($pred, "structs not supported in Java")
),
io.nl(Stream, !IO),
list.foldl(
output_function_defn_for_java(Info, Stream, Indent1,
oa_cname(ClassName, ClassArity)),
Ctors, !IO),
io.format(Stream, "%s}\n\n", [s(IndentStr)], !IO).
output_enum_class_defn_for_java(Info0, Stream, Indent, EnumDefn, !IO) :-
EnumDefn = mlds_enum_class_defn(ClassName, ClassArity, Context,
Inherits, Implements, TypeParams, _ValueField, EnumConsts, Ctors),
Info1 = Info0 ^ joi_univ_tvars := TypeParams,
% Use generics in the output of this predicate if this class represents
% a Mercury type. Note that we do NOT return Info1 or Info to our caller.
( if
Implements = yes(Interface),
Interface = ml_java_mercury_type_interface
then
InterfaceStr = interface_to_string_for_java(Interface),
Info = Info1 ^ joi_output_generics := do_output_generics
else
unexpected($pred, "no ml_java_mercury_type_interface")
),
indent_line_after_context(Stream, Info0 ^ joi_line_numbers,
marker_comment, Context, Indent, !IO),
ClassNameStr = unqual_class_name_to_string_for_java(ClassName, ClassArity),
io.format(Stream, "public static class %s", [s(ClassNameStr)], !IO),
output_generic_tvars(Stream, TypeParams, !IO),
io.nl(Stream, !IO),
IndentStr = indent2_string(Indent),
Indent1 = Indent + 1,
output_enum_inherits_list(Info, Stream, Indent1, Inherits, !IO),
io.format(Stream, "%simplements %s\n",
[s(IndentStr), s(InterfaceStr)], !IO),
io.format(Stream, "%s{\n", [s(IndentStr)], !IO),
list.foldl(
output_enum_constant_for_java(Stream, Indent1, ClassName, ClassArity),
EnumConsts, !IO),
io.nl(Stream, !IO),
output_enum_ctor_for_java(Stream, Indent1, ClassName, ClassArity, !IO),
ClassAux = oa_cname(ClassName, ClassArity),
list.foldl(
output_function_defn_for_java(Info, Stream, Indent1, ClassAux),
Ctors, !IO),
io.format(Stream, "%s}\n\n", [s(IndentStr)], !IO).
%---------------------------------------------------------------------------%
:- func class_kind_to_string_for_java(mlds_class_kind) = string.
class_kind_to_string_for_java(Kind) = KindStr :-
(
Kind = mlds_interface,
KindStr = "interface"
;
( Kind = mlds_class
; Kind = mlds_struct
),
KindStr = "class"
).
% Output superclass that this class extends. Java does not support
% multiple inheritance, so more than one superclass is an error.
%
:- pred output_inherits_list(java_out_info::in, io.text_output_stream::in,
indent::in, mlds_class_inherits::in, io::di, io::uo) is det.
output_inherits_list(Info, Stream, Indent, Inherits, !IO) :-
(
Inherits = inherits_nothing
;
(
Inherits = inherits_class(BaseClassId),
BaseType = mlds_class_type(BaseClassId)
;
Inherits = inherits_generic_env_ptr_type,
BaseType = mlds_generic_env_ptr_type
),
IndentStr = indent2_string(Indent),
BaseTypeStr = type_to_string_for_java(Info, BaseType),
io.format(Stream, "%sextends %s\n",
[s(IndentStr), s(BaseTypeStr)], !IO)
).
:- pred output_enum_inherits_list(java_out_info::in, io.text_output_stream::in,
indent::in, mlds_enum_class_inherits::in, io::di, io::uo) is det.
output_enum_inherits_list(Info, Stream, Indent, Inherits, !IO) :-
(
Inherits = inherits_nothing
;
Inherits = inherits_class(BaseClassId),
BaseType = mlds_class_type(BaseClassId),
IndentStr = indent2_string(Indent),
BaseTypeStr = type_to_string_for_java(Info, BaseType),
io.format(Stream, "%sextends %s\n",
[s(IndentStr), s(BaseTypeStr)], !IO)
).
% Output list of interfaces that this class implements.
%
:- pred output_implements_list(io.text_output_stream::in, indent::in,
list(mlds_interface_id)::in, io::di, io::uo) is det.
output_implements_list(Stream, Indent, InterfaceList, !IO) :-
(
InterfaceList = []
;
InterfaceList = [_ | _],
IndentStr = indent2_string(Indent),
InterfaceStrs = list.map(interface_to_string_for_java, InterfaceList),
InterfacesStr = string.join_list(", ", InterfaceStrs),
io.format(Stream, "%simplements %s\n",
[s(IndentStr), s(InterfacesStr)], !IO)
).
:- func interface_to_string_for_java(mlds_interface_id) = string.
interface_to_string_for_java(Interface) = String :-
Interface = mlds_interface_id(QualClassName, Arity, _),
QualClassName = qual_class_name(ModuleQualifier, QualKind, ClassName),
SymName = mlds_module_name_to_sym_name(ModuleQualifier),
mangle_sym_name_for_java(SymName, convert_qual_kind(QualKind),
".", ModuleNameStr),
% Check if the interface is one of the ones in the runtime system.
% If it is, we don't need to output the arity.
( if interface_is_special_for_java(ClassName) then
string.format("%s.%s", [s(ModuleNameStr), s(ClassName)], String)
else
string.format("%s.%s%d", [s(ModuleNameStr), s(ClassName), i(Arity)],
String)
).
%---------------------------------------------------------------------------%
%
% Code for generating enumerations.
%
% Enumerations are a bit different from normal classes, because although
% the 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.
% Output a (Java) constructor for the class representing the enumeration.
%
:- pred output_enum_ctor_for_java(io.text_output_stream::in, indent::in,
mlds_class_name::in, arity::in, io::di, io::uo) is det.
output_enum_ctor_for_java(Stream, Indent, ClassName, ClassArity, !IO) :-
IndentStr = indent2_string(Indent),
Indent1Str = indent2_string(Indent + 1),
UnqualClassnameStr =
unqual_class_name_to_string_for_java(ClassName, ClassArity),
io.format(Stream, "%sprivate %s(int val) {\n",
[s(IndentStr), s(UnqualClassnameStr)], !IO),
% Call the MercuryEnum constructor, which will set the MR_value field.
io.format(Stream, "%ssuper(val);\n", [s(Indent1Str)], !IO),
io.format(Stream, "%s}\n", [s(IndentStr)], !IO).
:- pred output_enum_constant_for_java(io.text_output_stream::in,
indent::in, mlds_class_name::in, arity::in, mlds_enum_const_defn::in,
io::di, io::uo) is det.
output_enum_constant_for_java(Stream, Indent, ClassName, ClassArity,
EnumConstDefn, !IO) :-
EnumConstDefn = mlds_enum_const_defn(FieldVarName, _Context, EnumConst),
% 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.
IndentStr = indent2_string(Indent),
UnqualClassNameStr =
unqual_class_name_to_string_for_java(ClassName, ClassArity),
FieldVarNameStr = field_var_name_to_string_for_java(FieldVarName),
(
EnumConst = mlds_enum_const_uint(EnumUInt),
io.format(Stream,
"%spublic static final %s K%u = new %s(%u); /* %s */\n",
[s(IndentStr), s(UnqualClassNameStr), u(EnumUInt),
s(UnqualClassNameStr), u(EnumUInt), s(FieldVarNameStr)], !IO)
;
EnumConst = mlds_enum_const_foreign(_Lang, _EnumNameStr, _Type),
% Foreign enums are not currently supported by the Java backend.
unexpected($pred, "not mlconst_enum")
).
%---------------------------------------------------------------------------%
:- pred output_field_var_decl_for_java(java_out_info::in,
io.text_output_stream::in, mlds_field_var_name::in, mlds_type::in,
io::di, io::uo) is det.
output_field_var_decl_for_java(Info, Stream, FieldVarName, Type, !IO) :-
TypeStr = type_to_string_for_java(Info, Type),
FieldVarNameStr = field_var_name_to_string_for_java(FieldVarName),
io.format(Stream, "%s %s", [s(TypeStr), s(FieldVarNameStr)], !IO).
:- pred output_field_var_defn_for_java(java_out_info::in,
io.text_output_stream::in, indent::in, mlds_field_var_defn::in,
io::di, io::uo) is det.
output_field_var_defn_for_java(Info, Stream, Indent, FieldVarDefn, !IO) :-
FieldVarDefn = mlds_field_var_defn(FieldVarName, Context, Flags, Type,
Initializer, _),
indent_line_after_context(Stream, Info ^ joi_line_numbers, marker_comment,
Context, Indent, !IO),
output_field_var_decl_flags_for_java(Stream, Flags, !IO),
output_field_var_decl_for_java(Info, Stream, FieldVarName, Type, !IO),
output_initializer_for_java(Info, Stream, oa_none, Indent + 1,
Type, Initializer, ";", !IO).
%---------------------------------------------------------------------------%
%
% Code to output declaration specifiers.
%
:- pred output_field_var_decl_flags_for_java(io.text_output_stream::in,
mlds_field_var_decl_flags::in, io::di, io::uo) is det.
output_field_var_decl_flags_for_java(Stream, Flags, !IO) :-
Flags = mlds_field_var_decl_flags(PerInstance, Constness),
io.write_string(Stream, "public ", !IO),
(
PerInstance = per_instance
;
PerInstance = one_copy,
io.write_string(Stream, "static ", !IO)
),
output_overridability_constness_for_java(Stream, overridable,
Constness, !IO).
:- pred output_class_decl_flags_for_java(java_out_info::in,
io.text_output_stream::in, mlds_class_decl_flags::in,
io::di, io::uo) is det.
output_class_decl_flags_for_java(_Info, Stream, Flags, !IO) :-
Flags = mlds_class_decl_flags(Access, Overrability, Constness),
(
Access = class_public,
io.write_string(Stream, "public ", !IO)
;
Access = class_private,
io.write_string(Stream, "private ", !IO)
),
% PerInstance = one_copy,
io.write_string(Stream, "static ", !IO),
output_overridability_constness_for_java(Stream, Overrability,
Constness, !IO).
:- pred output_overridability_constness_for_java(io.text_output_stream::in,
overridability::in, constness::in, io::di, io::uo) is det.
output_overridability_constness_for_java(Stream, Overridability,
Constness, !IO) :-
( if
( Overridability = sealed
; Constness = const
)
then
io.write_string(Stream, "final ", !IO)
else
true
).
%---------------------------------------------------------------------------%
%
% Code to rename long class names.
%
maybe_shorten_long_class_name(!ClassDefn, !Renaming) :-
!.ClassDefn = mlds_class_defn(ClassName0, _ClassArity, _Context, Flags,
_ClassKind, _Imports, _Inherits, _Implements, _TypeParams,
_MemberFields, _MemberClasses, _MemberMethods, _Ctors),
Flags = mlds_class_decl_flags(Access, _Overridability, _Constness),
(
% We only rename private classes for now.
Access = class_private,
ClassName = shorten_class_name(ClassName0),
( if ClassName = ClassName0 then
true
else
!ClassDefn ^ mcd_class_name := ClassName,
map.det_insert(ClassName0, ClassName, !Renaming)
)
;
Access = class_public
).
:- func shorten_class_name(string) = string.
shorten_class_name(ClassName0) = ClassName :-
MangledClassName0 = name_mangle_no_leading_digit(ClassName0),
( if string.length(MangledClassName0) < 100 then
ClassName = ClassName0
else
% 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),
Middle = c_util.hex_hash32(ClassName0),
Right = string.right(ClassName0, 44),
GenName = string.format("s_%s_%s_%s", [s(Left), s(Middle), 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) =
( if char.is_alnum_or_underscore(Char) then
Char
else
'_'
).
%---------------------------------------------------------------------------%
interface_is_special_for_java("MercuryType").
interface_is_special_for_java("MethodPtr").
interface_is_special_for_java("MethodPtr1").
interface_is_special_for_java("MethodPtr2").
interface_is_special_for_java("MethodPtr3").
interface_is_special_for_java("MethodPtr4").
interface_is_special_for_java("MethodPtr5").
interface_is_special_for_java("MethodPtr6").
interface_is_special_for_java("MethodPtr7").
interface_is_special_for_java("MethodPtr8").
interface_is_special_for_java("MethodPtr9").
interface_is_special_for_java("MethodPtr10").
interface_is_special_for_java("MethodPtr11").
interface_is_special_for_java("MethodPtr12").
interface_is_special_for_java("MethodPtr13").
interface_is_special_for_java("MethodPtr14").
interface_is_special_for_java("MethodPtr15").
interface_is_special_for_java("MethodPtrN").
%---------------------------------------------------------------------------%
:- end_module ml_backend.mlds_to_java_class.
%---------------------------------------------------------------------------%