mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 01:13:30 +00:00
It is almost the same as the unsigned_lt builtin op, but it is
implemented as "0 =< Index, Index < Range" just for Java.
This is the first step of the bootstrapping process; the second step
will add a declaration for private_builtin.in_range.
compiler/builtin_ops.m:
As above.
compiler/llds_out_data.m:
compiler/mlds_to_c_data.m:
compiler/mlds_to_cs_data.m:
compiler/mlds_to_java_data.m:
Implement the new operation.
compiler/code_util.m:
compiler/llds.m:
compiler/ml_global_data.m:
compiler/mlds_dump.m:
compiler/opt_debug.m:
Conform to the change above.
compiler/options.m:
Add a way to test whether the installed compiler supports this new op.
tests/warnings/help_text.err_exp:
Expect the new option name.
1303 lines
51 KiB
Mathematica
1303 lines
51 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2000-2012 The University of Melbourne.
|
|
% Copyright (C) 2013-2018, 2020-2025 The Mercury team.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Output MLDS lvals, rvals and initializers in Java.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module ml_backend.mlds_to_java_data.
|
|
:- interface.
|
|
|
|
:- import_module libs.
|
|
:- import_module libs.indent.
|
|
:- import_module ml_backend.mlds.
|
|
:- import_module ml_backend.mlds_to_java_util.
|
|
:- import_module ml_backend.mlds_to_target_util.
|
|
|
|
:- import_module io.
|
|
:- import_module list.
|
|
:- import_module maybe.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred output_lval_for_java(java_out_info::in, mlds_lval::in,
|
|
io.text_output_stream::in, io::di, io::uo) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred output_call_rval_for_java(java_out_info::in, mlds_rval::in,
|
|
io.text_output_stream::in, io::di, io::uo) is det.
|
|
|
|
:- pred output_bracketed_rval_for_java(java_out_info::in, mlds_rval::in,
|
|
io.text_output_stream::in, io::di, io::uo) is det.
|
|
|
|
:- pred output_rval_for_java(java_out_info::in, mlds_rval::in,
|
|
io.text_output_stream::in, io::di, io::uo) is det.
|
|
|
|
:- pred output_boxed_rval_for_java(java_out_info::in, mlds_type::in,
|
|
mlds_rval::in, io.text_output_stream::in, io::di, io::uo) is det.
|
|
|
|
% 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.
|
|
%
|
|
% XXX At the moment, this predicate is called from only two places.
|
|
% If the search mentioned above is ever done (that XXX comment
|
|
% has been there since 2018) but it does not find any more callers,
|
|
% then this predicate should be probably be inlined at its call sites
|
|
% and then deleted. Alternatively, the comment on the definition of
|
|
% the ml_cast mlds_rval suggests another, possibly simpler/better
|
|
% approach to solving this problem.
|
|
%
|
|
:- pred output_rval_maybe_with_enum_for_java(java_out_info::in, mlds_rval::in,
|
|
io.text_output_stream::in, io::di, io::uo) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Output " = " followed by the given initializer, if any, followed
|
|
% by the given suffix string and a newline.
|
|
%
|
|
% The initializer is printed using output_initializer_body_for_java with
|
|
% not_at_start_of_line (see below).
|
|
%
|
|
:- pred output_initializer_for_java(java_out_info::in,
|
|
io.text_output_stream::in, output_aux::in, indent::in, mlds_type::in,
|
|
mlds_initializer::in, string::in, io::di, io::uo) is det.
|
|
|
|
% Output the allocation part of the given initializer on the rest
|
|
% of the current line.
|
|
%
|
|
:- pred output_initializer_alloc_only_for_java(java_out_info::in,
|
|
io.text_output_stream::in, mlds_initializer::in, maybe(mlds_type)::in,
|
|
string::in, io::di, io::uo) is det.
|
|
|
|
% Output the given initializer. The formatting depends on whether
|
|
% the caller tells us that it has printed something on the current line
|
|
% already (not_at_start_of_line) or not (at_start_of_line).
|
|
%
|
|
% If the initializer is for a struct or an array, we put the initializer
|
|
% on separate lines, each indented by the given indent level, regardless
|
|
% of where we start.
|
|
%
|
|
% If the initializer is for a single object, then we put its initializer
|
|
% immediately after the previous of the current line if there is one
|
|
% (not_at_start_of_line); otherwise (at_start_of_line), we indent it by
|
|
% the specified level.
|
|
%
|
|
% In either case, we end the initializer with the given suffix
|
|
% (which will usually be a semicolon or a comma) and a newline.
|
|
%
|
|
:- pred output_initializer_body_for_java(java_out_info::in,
|
|
io.text_output_stream::in, initializer_starts::in, indent::in,
|
|
mlds_initializer::in, maybe(mlds_type)::in, string::in,
|
|
io::di, io::uo) is det.
|
|
|
|
% Output the given list of initializers with commas between them,
|
|
% putting each initializer on its own line with the given indent.
|
|
% Put the given suffix after the last initializer.
|
|
%
|
|
:- pred output_nonempty_initializer_body_list_for_java(java_out_info::in,
|
|
io.text_output_stream::in, indent::in, list(mlds_initializer)::in,
|
|
string::in, io::di, io::uo) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% 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_default_initializer_for_java(mlds_type) = string.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module backend_libs.
|
|
:- import_module backend_libs.builtin_ops.
|
|
:- import_module backend_libs.c_util.
|
|
:- import_module backend_libs.rtti.
|
|
:- import_module hlds.
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module libs.globals.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.sym_name.
|
|
:- import_module ml_backend.ml_util.
|
|
:- import_module ml_backend.mlds_to_java_name.
|
|
:- import_module ml_backend.mlds_to_java_type.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.java_names.
|
|
:- import_module parse_tree.prog_data.
|
|
:- import_module parse_tree.prog_type.
|
|
|
|
:- import_module bool.
|
|
:- import_module char.
|
|
:- import_module int.
|
|
:- import_module int16.
|
|
:- import_module int32.
|
|
:- import_module int64.
|
|
:- import_module int8.
|
|
:- import_module map.
|
|
:- import_module require.
|
|
:- import_module string.
|
|
:- import_module term.
|
|
:- import_module uint.
|
|
:- import_module uint32.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
output_lval_for_java(Info, Lval, Stream, !IO) :-
|
|
(
|
|
Lval = ml_field(_MaybeTag, PtrRval, _PtrType, FieldId, FieldType),
|
|
(
|
|
FieldId = ml_field_offset(OffsetRval),
|
|
( if
|
|
( FieldType = mlds_generic_type
|
|
; FieldType = mercury_nb_type(type_variable(_, _), _)
|
|
)
|
|
then
|
|
true
|
|
else
|
|
% The field type for field(_, _, offset(_), _, _) lvals
|
|
% must be something that maps to MR_Box.
|
|
unexpected($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(Stream, "((java.lang.Object[]) ", !IO),
|
|
output_rval_for_java(Info, PtrRval, Stream, !IO),
|
|
io.write_string(Stream, ")[", !IO),
|
|
output_rval_for_java(Info, OffsetRval, Stream, !IO),
|
|
io.write_string(Stream, "]", !IO)
|
|
;
|
|
FieldId = ml_field_named(QualFieldVarName, CtorType),
|
|
QualFieldVarName = qual_field_var_name(_, _, FieldVarName),
|
|
( if FieldVarName = fvn_data_tag then
|
|
% 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_for_java(Info, PtrRval, Stream, !IO),
|
|
io.write_string(Stream, ".", !IO)
|
|
else if PtrRval = ml_self(_) then
|
|
% Suppress type cast on `this' keyword. This makes a difference
|
|
% when assigning to `final' member variables in constructor
|
|
% functions.
|
|
output_rval_for_java(Info, PtrRval, Stream, !IO),
|
|
io.write_string(Stream, ".", !IO)
|
|
else
|
|
% 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.format(Stream, "((%s) ",
|
|
[s(type_to_string_for_java(Info, CtorType))], !IO),
|
|
output_bracketed_rval_for_java(Info, PtrRval, Stream, !IO),
|
|
io.write_string(Stream, ").", !IO)
|
|
),
|
|
FieldVarNameStr = field_var_name_to_string_for_java(FieldVarName),
|
|
io.write_string(Stream, FieldVarNameStr, !IO)
|
|
)
|
|
;
|
|
Lval = ml_mem_ref(Rval, _Type),
|
|
output_bracketed_rval_for_java(Info, Rval, Stream, !IO)
|
|
;
|
|
Lval = ml_target_global_var_ref(GlobalVarRef),
|
|
GlobalVarRef = env_var_ref(EnvVarName),
|
|
io.format(Stream, "mercury_envvar_%s", [s(EnvVarName)], !IO)
|
|
;
|
|
Lval = ml_global_var(GlobalVarName, _),
|
|
GlobalVarNameStr = maybe_qualified_global_var_name_to_string_for_java(
|
|
Info, GlobalVarName),
|
|
io.write_string(Stream, GlobalVarNameStr, !IO)
|
|
;
|
|
Lval = ml_local_var(LocalVarName, _),
|
|
LocalVarNameStr = local_var_name_to_string_for_java(LocalVarName),
|
|
io.write_string(Stream, LocalVarNameStr, !IO)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
output_call_rval_for_java(Info, Rval, Stream, !IO) :-
|
|
( if
|
|
Rval = ml_const(Const),
|
|
Const = mlconst_code_addr(CodeAddr)
|
|
then
|
|
mlds_output_call_code_addr_for_java(Stream, CodeAddr, !IO)
|
|
else
|
|
output_bracketed_rval_for_java(Info, Rval, Stream, !IO)
|
|
).
|
|
|
|
output_bracketed_rval_for_java(Info, Rval, Stream, !IO) :-
|
|
( if
|
|
% If it is just a variable name, then we don't need parentheses.
|
|
( Rval = ml_lval(ml_local_var(_,_))
|
|
; Rval = ml_lval(ml_global_var(_,_))
|
|
; Rval = ml_const(mlconst_code_addr(_))
|
|
)
|
|
then
|
|
output_rval_for_java(Info, Rval, Stream, !IO)
|
|
else
|
|
io.write_char(Stream, '(', !IO),
|
|
output_rval_for_java(Info, Rval, Stream, !IO),
|
|
io.write_char(Stream, ')', !IO)
|
|
).
|
|
|
|
output_rval_for_java(Info, Rval, Stream, !IO) :-
|
|
(
|
|
Rval = ml_lval(Lval),
|
|
output_lval_for_java(Info, Lval, Stream, !IO)
|
|
;
|
|
Rval = ml_mkword(_, _),
|
|
unexpected($pred, "tags not supported in Java")
|
|
;
|
|
Rval = ml_const(Const),
|
|
output_rval_const_for_java(Info, Stream, Const, !IO)
|
|
;
|
|
Rval = ml_cast(Type, SubRval),
|
|
output_cast_rval_for_java(Info, Type, SubRval, Stream, !IO)
|
|
;
|
|
Rval = ml_box(Type, SubRval),
|
|
output_boxed_rval_for_java(Info, Type, SubRval, Stream, !IO)
|
|
;
|
|
Rval = ml_unbox(Type, SubRval),
|
|
output_unboxed_rval_for_java(Info, Type, SubRval, Stream, !IO)
|
|
;
|
|
Rval = ml_unop(Unop, SubRval),
|
|
output_unop_for_java(Info, Stream, Unop, SubRval, !IO)
|
|
;
|
|
Rval = ml_binop(BinOp, RvalA, RvalB),
|
|
output_binop_for_java(Info, Stream, BinOp, RvalA, RvalB, !IO)
|
|
;
|
|
Rval = ml_mem_addr(_Lval),
|
|
unexpected($pred, "mem_addr(_) not supported")
|
|
;
|
|
Rval = ml_scalar_common(_),
|
|
% This reference is not the same as a mlds_data_addr const.
|
|
unexpected($pred, "ml_scalar_common")
|
|
;
|
|
Rval = ml_scalar_common_addr(ScalarCommon),
|
|
ScalarCommon = mlds_scalar_common(ModuleName, _Type,
|
|
ml_scalar_common_type_num(TypeNum), RowNum),
|
|
ModuleSymName = mlds_module_name_to_sym_name(ModuleName),
|
|
mangle_sym_name_for_java(ModuleSymName, module_qual, "__",
|
|
MangledModuleName),
|
|
io.format(Stream, "%s.MR_scalar_common_%d[%d]",
|
|
[s(MangledModuleName),i(TypeNum), i(RowNum)], !IO)
|
|
;
|
|
Rval = ml_vector_common_row_addr(VectorCommon, RowRval),
|
|
VectorCommon = mlds_vector_common(_ModuleName, _Type,
|
|
ml_vector_common_type_num(TypeNum), StartRowNum, _NumRows),
|
|
% XXX Why do we print a "MangledModuleName." prefix for scalar common
|
|
% addresses but not for vector common addresses?
|
|
io.format(Stream, "MR_vector_common_%d[%d + ",
|
|
[i(TypeNum), i(StartRowNum)], !IO),
|
|
output_rval_for_java(Info, RowRval, Stream, !IO),
|
|
io.write_string(Stream, "]", !IO)
|
|
;
|
|
Rval = ml_self(_),
|
|
io.write_string(Stream, "this", !IO)
|
|
).
|
|
|
|
:- pred output_cast_rval_for_java(java_out_info::in, mlds_type::in,
|
|
mlds_rval::in, io.text_output_stream::in, io::di, io::uo) is det.
|
|
|
|
output_cast_rval_for_java(Info, Type, Rval, Stream, !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.
|
|
( if
|
|
Type = mlds_pseudo_type_info_type,
|
|
Rval = ml_const(mlconst_int(N))
|
|
then
|
|
maybe_output_inline_comment_for_java(Info, Stream, "cast", !IO),
|
|
( if have_preallocated_pseudo_type_var_for_java(N) then
|
|
io.write_string(Stream, "jmercury.runtime.PseudoTypeInfo.K", !IO),
|
|
io.write_int(Stream, N, !IO)
|
|
else
|
|
io.write_string(Stream,
|
|
"new jmercury.runtime.PseudoTypeInfo(", !IO),
|
|
output_rval_for_java(Info, Rval, Stream, !IO),
|
|
io.write_string(Stream, ")", !IO)
|
|
)
|
|
else if
|
|
( Type = mercury_nb_type(_, ctor_cat_system(cat_system_type_info))
|
|
; Type = mlds_type_info_type
|
|
)
|
|
then
|
|
% 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. (zs agrees.)
|
|
maybe_output_inline_comment_for_java(Info, Stream, "cast", !IO),
|
|
io.write_string(Stream,
|
|
"jmercury.runtime.TypeInfo_Struct.maybe_new(", !IO),
|
|
output_rval_for_java(Info, Rval, Stream, !IO),
|
|
io.write_string(Stream, ")", !IO)
|
|
else if
|
|
java_builtin_type(Type, "int", _, _)
|
|
then
|
|
io.write_string(Stream, "(int) ", !IO),
|
|
% If Rval is an enum, it is an object with its value in a field,
|
|
% which means that we need to get that field.
|
|
output_rval_maybe_with_enum_for_java(Info, Rval, Stream, !IO)
|
|
else
|
|
io.format(Stream, "(%s) ",
|
|
[s(type_to_string_for_java(Info, Type))], !IO),
|
|
% XXX We don't call output_rval_maybe_with_enum_for_java here.
|
|
% This means that we better not cast enum values to any type
|
|
% other than "int".
|
|
output_rval_for_java(Info, Rval, Stream, !IO)
|
|
).
|
|
|
|
:- pred have_preallocated_pseudo_type_var_for_java(int::in) is semidet.
|
|
|
|
have_preallocated_pseudo_type_var_for_java(N) :-
|
|
% Corresponds to static members in class PseudoTypeInfo.
|
|
N >= 1,
|
|
N =< 5.
|
|
|
|
output_boxed_rval_for_java(Info, Type, Rval, Stream, !IO) :-
|
|
( if java_builtin_type(Type, _, JavaBoxedTypeName, _) then
|
|
% valueOf may return cached instances instead of creating new objects.
|
|
io.format(Stream, "%s.valueOf(", [s(JavaBoxedTypeName)], !IO),
|
|
output_rval_for_java(Info, Rval, Stream, !IO),
|
|
io.write_string(Stream, ")", !IO)
|
|
else
|
|
io.write_string(Stream, "((java.lang.Object) (", !IO),
|
|
output_rval_for_java(Info, Rval, Stream, !IO),
|
|
io.write_string(Stream, "))", !IO)
|
|
).
|
|
|
|
:- pred output_unboxed_rval_for_java(java_out_info::in, mlds_type::in,
|
|
mlds_rval::in, io.text_output_stream::in, io::di, io::uo) is det.
|
|
|
|
output_unboxed_rval_for_java(Info, Type, Rval, Stream, !IO) :-
|
|
( if java_builtin_type(Type, _, JavaBoxedTypeName, UnboxMethod) then
|
|
io.format(Stream, "((%s) ", [s(JavaBoxedTypeName)], !IO),
|
|
output_bracketed_rval_for_java(Info, Rval, Stream, !IO),
|
|
io.format(Stream, ").%s()", [s(UnboxMethod)], !IO)
|
|
else
|
|
io.write_string(Stream, "((", !IO),
|
|
output_type_for_java(Info, Stream, Type, !IO),
|
|
io.write_string(Stream, ") ", !IO),
|
|
output_rval_for_java(Info, Rval, Stream, !IO),
|
|
io.write_string(Stream, ")", !IO)
|
|
).
|
|
|
|
:- pred output_unop_for_java(java_out_info::in, io.text_output_stream::in,
|
|
builtin_ops.unary_op::in, mlds_rval::in, io::di, io::uo) is det.
|
|
|
|
output_unop_for_java(Info, Stream, UnaryOp, Rval, !IO) :-
|
|
% 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 is no tag).
|
|
(
|
|
UnaryOp = tag,
|
|
io.write_string(Stream, "/* tag */ 0", !IO)
|
|
;
|
|
( UnaryOp = strip_tag, UnaryOpStr = "/* strip_tag */ "
|
|
; UnaryOp = mkbody, UnaryOpStr = "/* mkbody */ "
|
|
; UnaryOp = unmkbody, UnaryOpStr = "/* unmkbody */ "
|
|
; UnaryOp = logical_not, UnaryOpStr = "!"
|
|
; UnaryOp = hash_string, UnaryOpStr = "mercury.String.hash_1_f_0"
|
|
; UnaryOp = hash_string2, UnaryOpStr = "mercury.String.hash2_1_f_0"
|
|
; UnaryOp = hash_string3, UnaryOpStr = "mercury.String.hash3_1_f_0"
|
|
; UnaryOp = hash_string4, UnaryOpStr = "mercury.String.hash4_1_f_0"
|
|
; UnaryOp = hash_string5, UnaryOpStr = "mercury.String.hash5_1_f_0"
|
|
; UnaryOp = hash_string6, UnaryOpStr = "mercury.String.hash6_1_f_0"
|
|
),
|
|
io.format(Stream, "%s(", [s(UnaryOpStr)], !IO),
|
|
output_rval_for_java(Info, Rval, Stream, !IO),
|
|
io.write_string(Stream, ")", !IO)
|
|
;
|
|
UnaryOp = bitwise_complement(IntType),
|
|
(
|
|
( IntType = int_type_int
|
|
; IntType = int_type_int32
|
|
; IntType = int_type_int64
|
|
; IntType = int_type_uint
|
|
; IntType = int_type_uint32
|
|
; IntType = int_type_uint64
|
|
),
|
|
io.write_string(Stream, "~(", !IO),
|
|
output_rval_for_java(Info, Rval, Stream, !IO),
|
|
io.write_string(Stream, ")", !IO)
|
|
;
|
|
( IntType = int_type_int8
|
|
; IntType = int_type_uint8
|
|
),
|
|
io.write_string(Stream, "(byte) (~(", !IO),
|
|
output_rval_for_java(Info, Rval, Stream, !IO),
|
|
io.write_string(Stream, "))", !IO)
|
|
;
|
|
( IntType = int_type_int16
|
|
; IntType = int_type_uint16
|
|
),
|
|
io.write_string(Stream, "(short) (~(", !IO),
|
|
output_rval_for_java(Info, Rval, Stream, !IO),
|
|
io.write_string(Stream, "))", !IO)
|
|
)
|
|
;
|
|
( UnaryOp = dword_float_get_word0
|
|
; UnaryOp = dword_float_get_word1
|
|
; UnaryOp = dword_int64_get_word0
|
|
; UnaryOp = dword_int64_get_word1
|
|
; UnaryOp = dword_uint64_get_word0
|
|
; UnaryOp = dword_uint64_get_word1
|
|
),
|
|
unexpected($pred, "invalid unary operator")
|
|
).
|
|
|
|
:- pred output_binop_for_java(java_out_info::in, io.text_output_stream::in,
|
|
binary_op::in, mlds_rval::in, mlds_rval::in, io::di, io::uo) is det.
|
|
|
|
output_binop_for_java(Info, Stream, Op, X, Y, !IO) :-
|
|
(
|
|
Op = array_index(_Type),
|
|
output_bracketed_rval_for_java(Info, X, Stream, !IO),
|
|
io.write_string(Stream, "[", !IO),
|
|
output_rval_for_java(Info, Y, Stream, !IO),
|
|
io.write_string(Stream, "]", !IO)
|
|
;
|
|
Op = str_cmp(CmpOp),
|
|
(
|
|
CmpOp = eq,
|
|
output_rval_for_java(Info, X, Stream, !IO),
|
|
io.write_string(Stream, ".equals(", !IO),
|
|
output_rval_for_java(Info, Y, Stream, !IO),
|
|
io.write_string(Stream, ")", !IO)
|
|
;
|
|
( CmpOp = ne, OpStr = "!="
|
|
; CmpOp = lt, OpStr = "<"
|
|
; CmpOp = gt, OpStr = ">"
|
|
; CmpOp = le, OpStr = "<="
|
|
; CmpOp = ge, OpStr = ">="
|
|
),
|
|
io.write_string(Stream, "(", !IO),
|
|
output_rval_for_java(Info, X, Stream, !IO),
|
|
io.write_string(Stream, ".compareTo(", !IO),
|
|
output_rval_for_java(Info, Y, Stream, !IO),
|
|
io.format(Stream, ") %s 0)", [s(OpStr)], !IO)
|
|
)
|
|
;
|
|
Op = str_nzp,
|
|
io.write_string(Stream, "(", !IO),
|
|
output_rval_for_java(Info, X, Stream, !IO),
|
|
io.write_string(Stream, ".compareTo(", !IO),
|
|
output_rval_for_java(Info, Y, Stream, !IO),
|
|
io.write_string(Stream, ")) ", !IO)
|
|
;
|
|
Op = pointer_equal_conservative,
|
|
io.write_string(Stream, "(", !IO),
|
|
output_rval_for_java(Info, X, Stream, !IO),
|
|
io.write_string(Stream, " == ", !IO),
|
|
output_rval_for_java(Info, Y, Stream, !IO),
|
|
io.write_string(Stream, ") ", !IO)
|
|
;
|
|
Op = int_arith(IntType, ArithOp),
|
|
% Handle these in a separate predicate to reduce gcc memory
|
|
% requirements, particularly when building in deep profiling grades.
|
|
output_int_arith_binop_for_java(Info, Stream, IntType, ArithOp,
|
|
X, Y, !IO)
|
|
;
|
|
Op = int_cmp(IntType, CmpOp),
|
|
% Handle these in a separate predicate to reduce gcc memory
|
|
% requirements, particularly when building in deep profiling grades.
|
|
output_int_cmp_binop_for_java(Info, Stream, IntType, CmpOp, X, Y, !IO)
|
|
;
|
|
( Op = unchecked_left_shift(_, _)
|
|
; Op = unchecked_right_shift(_, _)
|
|
; Op = bitwise_and(_)
|
|
; Op = bitwise_or(_)
|
|
; Op = bitwise_xor(_)
|
|
),
|
|
% Handle these in a separate predicate to reduce gcc memory
|
|
% requirements, particularly when building in deep profiling grades.
|
|
output_int_misc_binop_for_java(Info, Stream, Op, X, Y, !IO)
|
|
;
|
|
Op = int_as_uint_cmp(CmpOp),
|
|
( CmpOp = lt, OpStr = "<"
|
|
; CmpOp = le, OpStr = "<="
|
|
),
|
|
( if rval_is_enum_object(X) then
|
|
% The bit masking won't be needed in the vast majority of cases,
|
|
% but I (zs) believe that it *could* be possible for a
|
|
% foreign_enum pragma to assign a negative value to
|
|
% a functor in an enum type.
|
|
io.write_string(Stream, "((", !IO),
|
|
output_rval_for_java(Info, X, Stream, !IO),
|
|
io.format(Stream, ".MR_value & 0xffffffffL) %s (",
|
|
[s(OpStr)], !IO),
|
|
output_rval_for_java(Info, Y, Stream, !IO),
|
|
io.write_string(Stream, ".MR_value) & 0xffffffffL)", !IO)
|
|
else
|
|
io.write_string(Stream, "((", !IO),
|
|
output_rval_for_java(Info, X, Stream, !IO),
|
|
io.format(Stream, " & 0xffffffffL) %s (", [s(OpStr)], !IO),
|
|
output_rval_for_java(Info, Y, Stream, !IO),
|
|
io.write_string(Stream, " & 0xffffffffL))", !IO)
|
|
)
|
|
;
|
|
Op = in_range,
|
|
io.write_string(Stream, "((0 <= ", !IO),
|
|
output_rval_for_java(Info, X, Stream, !IO),
|
|
io.write_string(Stream, ") && (", !IO),
|
|
output_rval_for_java(Info, X, Stream, !IO),
|
|
io.write_string(Stream, " < ", !IO),
|
|
output_rval_for_java(Info, Y, Stream, !IO),
|
|
io.write_string(Stream, "))", !IO)
|
|
;
|
|
( Op = logical_and, OpStr = "&&"
|
|
; Op = logical_or, OpStr = "||"
|
|
; Op = float_arith(ArithOp), OpStr =
|
|
arith_op_c_operator(coerce(ArithOp))
|
|
; Op = float_cmp(CmpOp), OpStr = cmp_op_c_operator(CmpOp)
|
|
),
|
|
output_basic_binop_maybe_with_enum_for_java(Info, Stream,
|
|
OpStr, X, Y, !IO)
|
|
;
|
|
( Op = body
|
|
; Op = string_unsafe_index_code_unit
|
|
; Op = offset_str_eq(_, _)
|
|
; Op = float_from_dword
|
|
; Op = int64_from_dword
|
|
; Op = uint64_from_dword
|
|
),
|
|
unexpected($pred, "invalid binary operator")
|
|
).
|
|
|
|
:- pred output_int_arith_binop_for_java(java_out_info::in,
|
|
io.text_output_stream::in, int_type::in, arith_op::in,
|
|
mlds_rval::in, mlds_rval::in, io::di, io::uo) is det.
|
|
:- pragma no_inline(pred(output_int_arith_binop_for_java/8)).
|
|
|
|
output_int_arith_binop_for_java(Info, Stream, Type, Op, X, Y, !IO) :-
|
|
(
|
|
( Op = ao_add, OpStr = "+"
|
|
; Op = ao_sub, OpStr = "-"
|
|
; Op = ao_mul, OpStr = "*"
|
|
),
|
|
(
|
|
( Type = int_type_int
|
|
; Type = int_type_int32
|
|
; Type = int_type_int64
|
|
; Type = int_type_uint
|
|
; Type = int_type_uint32
|
|
; Type = int_type_uint64
|
|
),
|
|
output_basic_binop_maybe_with_enum_for_java(Info, Stream,
|
|
OpStr, X, Y, !IO)
|
|
;
|
|
( Type = int_type_int8, Cast = "(byte) "
|
|
; Type = int_type_int16, Cast = "(short) "
|
|
; Type = int_type_uint8, Cast = "(byte) "
|
|
; Type = int_type_uint16, Cast = "(short) "
|
|
),
|
|
io.write_string(Stream, Cast, !IO),
|
|
% XXX Document why we aren't calling
|
|
% output_basic_binop_maybe_with_enum_for_java here.
|
|
output_basic_binop_for_java(Info, Stream,
|
|
OpStr, X, Y, !IO)
|
|
)
|
|
;
|
|
( Op = ao_div, OpStr = "/"
|
|
; Op = ao_rem, OpStr = "%"
|
|
),
|
|
(
|
|
( Type = int_type_int
|
|
; Type = int_type_int32
|
|
; Type = int_type_int64
|
|
),
|
|
output_basic_binop_maybe_with_enum_for_java(Info, Stream,
|
|
OpStr, X, Y, !IO)
|
|
;
|
|
( Type = int_type_int8, Cast = "(byte) "
|
|
; Type = int_type_int16, Cast = "(short) "
|
|
),
|
|
io.write_string(Stream, Cast, !IO),
|
|
output_basic_binop_for_java(Info, Stream,
|
|
OpStr, X, Y, !IO)
|
|
;
|
|
( Type = int_type_uint8, Cast = "(byte)", Mask = "0xff"
|
|
; Type = int_type_uint16, Cast = "(short)", Mask = "0xffff"
|
|
; Type = int_type_uint32, Cast = "(int)", Mask = "0xffffffffL"
|
|
; Type = int_type_uint, Cast = "(int)", Mask = "0xffffffffL"
|
|
),
|
|
io.format(Stream, "(%s ", [s(Cast)], !IO),
|
|
output_basic_binop_with_mask_for_java(Info, Stream,
|
|
OpStr, Mask, X, Y, !IO),
|
|
io.write_string(Stream, ")", !IO)
|
|
;
|
|
Type = int_type_uint64,
|
|
% We could compute FuncName along with OpStr above,
|
|
% but int64 operands are rare enough that it is better
|
|
% not to burden the non-int64 code path with recording FuncName.
|
|
( Op = ao_div, FuncName = "java.lang.Long.divideUnsigned"
|
|
; Op = ao_rem, FuncName = "java.lang.Long.remainderUnsigned"
|
|
),
|
|
output_binop_func_call_for_java(Info, Stream,
|
|
FuncName, X, Y, !IO)
|
|
)
|
|
).
|
|
|
|
:- pred output_int_cmp_binop_for_java(java_out_info::in,
|
|
io.text_output_stream::in, int_type::in, cmp_op::in,
|
|
mlds_rval::in, mlds_rval::in, io::di, io::uo) is det.
|
|
:- pragma no_inline(pred(output_int_cmp_binop_for_java/8)).
|
|
|
|
output_int_cmp_binop_for_java(Info, Stream, Type, Op, X, Y, !IO) :-
|
|
(
|
|
( Op = eq, OpStr = "=="
|
|
; Op = ne, OpStr = "!="
|
|
),
|
|
output_basic_binop_maybe_with_enum_for_java(Info, Stream,
|
|
OpStr, X, Y, !IO)
|
|
;
|
|
( Op = lt, OpStr = "<"
|
|
; Op = gt, OpStr = ">"
|
|
; Op = le, OpStr = "<="
|
|
; Op = ge, OpStr = ">="
|
|
),
|
|
(
|
|
( Type = int_type_int
|
|
; Type = int_type_int8
|
|
; Type = int_type_int16
|
|
; Type = int_type_int32
|
|
; Type = int_type_int64
|
|
),
|
|
output_basic_binop_maybe_with_enum_for_java(Info, Stream,
|
|
OpStr, X, Y, !IO)
|
|
;
|
|
( Type = int_type_uint8, Mask = "0xff"
|
|
; Type = int_type_uint16, Mask = "0xffff"
|
|
; Type = int_type_uint32, Mask = "0xffffffffL"
|
|
; Type = int_type_uint, Mask = "0xffffffffL"
|
|
),
|
|
output_basic_binop_with_mask_for_java(Info, Stream,
|
|
OpStr, Mask, X, Y, !IO)
|
|
;
|
|
Type = int_type_uint64,
|
|
io.write_string(Stream, "(", !IO),
|
|
output_binop_func_call_for_java(Info, Stream,
|
|
"java.lang.Long.compareUnsigned", X, Y, !IO),
|
|
io.format(Stream, " %s 0)", [s(OpStr)], !IO)
|
|
)
|
|
).
|
|
|
|
:- pred output_int_misc_binop_for_java(java_out_info::in,
|
|
io.text_output_stream::in, binary_op::in(int_misc_binary_op),
|
|
mlds_rval::in, mlds_rval::in, io::di, io::uo) is det.
|
|
:- pragma no_inline(pred(output_int_misc_binop_for_java/7)).
|
|
|
|
output_int_misc_binop_for_java(Info, Stream, Op, X, Y, !IO) :-
|
|
(
|
|
( Op = bitwise_and(Type), OpStr = "&"
|
|
; Op = bitwise_or(Type), OpStr = "|"
|
|
; Op = bitwise_xor(Type), OpStr = "^"
|
|
),
|
|
(
|
|
( Type = int_type_int
|
|
; Type = int_type_int32
|
|
; Type = int_type_int64
|
|
; Type = int_type_uint
|
|
; Type = int_type_uint32
|
|
; Type = int_type_uint64
|
|
),
|
|
output_basic_binop_maybe_with_enum_for_java(Info, Stream,
|
|
OpStr, X, Y, !IO)
|
|
;
|
|
( Type = int_type_int8, Cast = "(byte) "
|
|
; Type = int_type_int16, Cast = "(short) "
|
|
; Type = int_type_uint8, Cast = "(byte) "
|
|
; Type = int_type_uint16, Cast = "(short) "
|
|
),
|
|
io.write_string(Stream, Cast, !IO),
|
|
output_basic_binop_for_java(Info, Stream,
|
|
OpStr, X, Y, !IO)
|
|
)
|
|
;
|
|
(
|
|
Op = unchecked_left_shift(Type, _ShiftByType),
|
|
OpStr = "<<"
|
|
;
|
|
Op = unchecked_right_shift(Type, _ShiftByType),
|
|
(
|
|
( Type = int_type_int
|
|
; Type = int_type_int8
|
|
; Type = int_type_int16
|
|
; Type = int_type_int32
|
|
; Type = int_type_int64
|
|
),
|
|
OpStr = ">>"
|
|
;
|
|
( Type = int_type_uint
|
|
; Type = int_type_uint8
|
|
; Type = int_type_uint16
|
|
; Type = int_type_uint32
|
|
; Type = int_type_uint64
|
|
),
|
|
OpStr = ">>>"
|
|
)
|
|
),
|
|
% We ignore the distinction between shift_by_int and shift_by_uint,
|
|
% because when targeting Java, we represent Mercury uints as
|
|
% Java ints anyway.
|
|
(
|
|
( Type = int_type_int
|
|
; Type = int_type_int32
|
|
; Type = int_type_int64
|
|
; Type = int_type_uint
|
|
; Type = int_type_uint32
|
|
; Type = int_type_uint64
|
|
),
|
|
output_basic_binop_maybe_with_enum_for_java(Info, Stream,
|
|
OpStr, X, Y, !IO)
|
|
;
|
|
( Type = int_type_int8, Cast = "(byte) ", Mask = ""
|
|
; Type = int_type_int16, Cast = "(short) ", Mask = ""
|
|
; Type = int_type_uint8, Cast = "(byte) ", Mask = "0xff"
|
|
; Type = int_type_uint16, Cast = "(short) ", Mask = "0xffff"
|
|
),
|
|
io.write_string(Stream, Cast, !IO),
|
|
% This special case is needed because we represent
|
|
% Mercury unsigned integers using signed Java integers.
|
|
% When operating on a sub-word-sized integer we want to treat
|
|
% as unsigned, we need to tell Java not to sign extend it
|
|
% if the sign extension could interfere with the operation.
|
|
% For left shifts, sign extension is irrelevant, since
|
|
% the shifted-in bits come from the bottom of the word.
|
|
% For right shifts, sign extension is relevant, since
|
|
% the shifted-in bits come from the top of the word.
|
|
( if
|
|
Op = unchecked_right_shift(_, _),
|
|
Mask \= ""
|
|
then
|
|
% Unlike output_basic_binop_with_mask_for_java,
|
|
% this code applies the mask *only* to X, not to Y.
|
|
% (As the shift amount, Y should already be in the range
|
|
% 0 .. 63.)
|
|
io.write_string(Stream, "(((", !IO),
|
|
output_rval_for_java(Info, X, Stream, !IO),
|
|
io.format(Stream, ") & %s) %s ", [s(Mask), s(OpStr)], !IO),
|
|
output_rval_for_java(Info, Y, Stream, !IO),
|
|
io.write_string(Stream, ")", !IO)
|
|
else
|
|
output_basic_binop_for_java(Info, Stream,
|
|
OpStr, X, Y, !IO)
|
|
)
|
|
)
|
|
).
|
|
|
|
:- pred output_basic_binop_maybe_with_enum_for_java(java_out_info::in,
|
|
io.text_output_stream::in, string::in, mlds_rval::in, mlds_rval::in,
|
|
io::di, io::uo) is det.
|
|
:- pragma no_inline(pred(output_basic_binop_maybe_with_enum_for_java/7)).
|
|
|
|
output_basic_binop_maybe_with_enum_for_java(Info, Stream, OpStr, X, Y, !IO) :-
|
|
( if rval_is_enum_object(X) then
|
|
io.write_string(Stream, "(", !IO),
|
|
output_rval_for_java(Info, X, Stream, !IO),
|
|
io.format(Stream, ".MR_value %s ", [s(OpStr)], !IO),
|
|
output_rval_for_java(Info, Y, Stream, !IO),
|
|
io.write_string(Stream, ".MR_value)", !IO)
|
|
else
|
|
output_basic_binop_for_java(Info, Stream, OpStr, X, Y, !IO)
|
|
).
|
|
|
|
:- pred output_basic_binop_for_java(java_out_info::in,
|
|
io.text_output_stream::in, string::in, mlds_rval::in, mlds_rval::in,
|
|
io::di, io::uo) is det.
|
|
:- pragma no_inline(pred(output_basic_binop_for_java/7)).
|
|
|
|
output_basic_binop_for_java(Info, Stream, OpStr, X, Y, !IO) :-
|
|
io.write_string(Stream, "(", !IO),
|
|
output_rval_for_java(Info, X, Stream, !IO),
|
|
io.format(Stream, " %s ", [s(OpStr)], !IO),
|
|
output_rval_for_java(Info, Y, Stream, !IO),
|
|
io.write_string(Stream, ")", !IO).
|
|
|
|
:- pred output_basic_binop_with_mask_for_java(java_out_info::in,
|
|
io.text_output_stream::in, string::in, string::in,
|
|
mlds_rval::in, mlds_rval::in, io::di, io::uo) is det.
|
|
:- pragma no_inline(pred(output_basic_binop_with_mask_for_java/8)).
|
|
|
|
output_basic_binop_with_mask_for_java(Info, Stream, OpStr, Mask, X, Y, !IO) :-
|
|
io.write_string(Stream, "(((", !IO),
|
|
output_rval_for_java(Info, X, Stream, !IO),
|
|
io.format(Stream, ") & %s) %s ((", [s(Mask), s(OpStr)], !IO),
|
|
output_rval_for_java(Info, Y, Stream, !IO),
|
|
io.format(Stream, ") & %s))", [s(Mask)], !IO).
|
|
|
|
:- pred output_binop_func_call_for_java(java_out_info::in,
|
|
io.text_output_stream::in, string::in, mlds_rval::in, mlds_rval::in,
|
|
io::di, io::uo) is det.
|
|
:- pragma no_inline(pred(output_binop_func_call_for_java/7)).
|
|
|
|
output_binop_func_call_for_java(Info, Stream, FuncName, X, Y, !IO) :-
|
|
io.format(Stream, "%s(", [s(FuncName)], !IO),
|
|
output_rval_for_java(Info, X, Stream, !IO),
|
|
io.write_string(Stream, ", ", !IO),
|
|
output_rval_for_java(Info, Y, Stream, !IO),
|
|
io.write_string(Stream, ")", !IO).
|
|
|
|
output_rval_maybe_with_enum_for_java(Info, Rval, Stream, !IO) :-
|
|
output_rval_for_java(Info, Rval, Stream, !IO),
|
|
( if rval_is_enum_object(Rval) then
|
|
io.write_string(Stream, ".MR_value", !IO)
|
|
else
|
|
true
|
|
).
|
|
|
|
:- pred output_rval_const_for_java(java_out_info::in,
|
|
io.text_output_stream::in, mlds_rval_const::in, io::di, io::uo) is det.
|
|
|
|
output_rval_const_for_java(Info, Stream, Const, !IO) :-
|
|
(
|
|
Const = mlconst_true,
|
|
io.write_string(Stream, "true", !IO)
|
|
;
|
|
Const = mlconst_false,
|
|
io.write_string(Stream, "false", !IO)
|
|
;
|
|
Const = mlconst_int(N),
|
|
output_int_const_for_java(Stream, N, !IO)
|
|
;
|
|
Const = mlconst_uint(U),
|
|
% Java does not have unsigned integer literals.
|
|
% XXX perhaps we should output this in hexadecimal?
|
|
output_int_const_for_java(Stream, uint.cast_to_int(U), !IO)
|
|
;
|
|
Const = mlconst_int8(I8),
|
|
io.format(Stream, "(byte) %d", [i8(I8)], !IO)
|
|
;
|
|
Const = mlconst_uint8(U8),
|
|
io.format(Stream, "(byte) %d", [i8(int8.cast_from_uint8(U8))], !IO)
|
|
;
|
|
Const = mlconst_int16(I16),
|
|
io.format(Stream, "(short) %d", [i16(I16)], !IO)
|
|
;
|
|
Const = mlconst_uint16(U16),
|
|
io.format(Stream, "(short) %d", [i16(int16.cast_from_uint16(U16))],
|
|
!IO)
|
|
;
|
|
Const = mlconst_int32(I32),
|
|
io.write_int32(Stream, I32, !IO)
|
|
;
|
|
Const = mlconst_uint32(U32),
|
|
io.write_int32(Stream, int32.cast_from_uint32(U32), !IO)
|
|
;
|
|
Const = mlconst_int64(I64),
|
|
io.format(Stream, "%dL", [i64(I64)], !IO)
|
|
;
|
|
Const = mlconst_uint64(U64),
|
|
io.format(Stream, "%dL", [i64(int64.cast_from_uint64(U64))], !IO)
|
|
;
|
|
Const = mlconst_char(N),
|
|
io.write_string(Stream, "(", !IO),
|
|
output_int_const_for_java(Stream, N, !IO),
|
|
io.write_string(Stream, ")", !IO)
|
|
;
|
|
Const = mlconst_enum(N, EnumType),
|
|
io.format(Stream, "%s.K",
|
|
[s(type_to_string_for_java(Info, EnumType))], !IO),
|
|
output_int_const_for_java(Stream, N, !IO)
|
|
;
|
|
Const = mlconst_foreign(Lang, Value, _Type),
|
|
expect(unify(Lang, lang_java), $pred, "language other than Java."),
|
|
% XXX Should we parenthesize this?
|
|
io.write_string(Stream, Value, !IO)
|
|
;
|
|
Const = mlconst_float(FloatVal),
|
|
c_util.output_float_literal(Stream, FloatVal, !IO)
|
|
;
|
|
Const = mlconst_string(String),
|
|
output_quoted_string_java(Stream, String, !IO)
|
|
;
|
|
Const = mlconst_multi_string(String),
|
|
output_quoted_multi_string_java(Stream, String, !IO)
|
|
;
|
|
Const = mlconst_named_const(TargetPrefixes, NamedConst),
|
|
io.write_string(Stream, TargetPrefixes ^ java_prefix, !IO),
|
|
io.write_string(Stream, NamedConst, !IO)
|
|
;
|
|
Const = mlconst_code_addr(CodeAddr),
|
|
mlds_output_wrapper_code_addr_for_java(Info, Stream, CodeAddr, !IO)
|
|
;
|
|
Const = mlconst_data_addr_local_var(LocalVarName),
|
|
LocalVarNameStr = local_var_name_to_string_for_java(LocalVarName),
|
|
io.write_string(Stream, LocalVarNameStr, !IO)
|
|
;
|
|
Const = mlconst_data_addr_global_var(ModuleName, GlobalVarName),
|
|
SymName = mlds_module_name_to_sym_name(ModuleName),
|
|
mangle_sym_name_for_java(SymName, module_qual, "__", ModuleNameStr),
|
|
GlobalVarNameStr = global_var_name_to_string_for_java(GlobalVarName),
|
|
io.format(Stream, "%s.%s",
|
|
[s(ModuleNameStr), s(GlobalVarNameStr)], !IO)
|
|
;
|
|
Const = mlconst_data_addr_rtti(ModuleName, RttiId),
|
|
SymName = mlds_module_name_to_sym_name(ModuleName),
|
|
mangle_sym_name_for_java(SymName, module_qual, "__", ModuleNameStr),
|
|
rtti.id_to_c_identifier(RttiId, RttiAddrName),
|
|
io.format(Stream, "%s.%s", [s(ModuleNameStr), s(RttiAddrName)], !IO)
|
|
;
|
|
Const = mlconst_data_addr_tabling(QualProcLabel, TablingId),
|
|
QualProcLabel = qual_proc_label(ModuleName, ProcLabel),
|
|
SymName = mlds_module_name_to_sym_name(ModuleName),
|
|
mangle_sym_name_for_java(SymName, module_qual, "__", ModuleNameStr),
|
|
TablingIdStr = tabling_info_id_str(TablingId),
|
|
ProcLabelStr = proc_label_to_string_for_java(ProcLabel),
|
|
io.format(Stream, "%s.%s_%s",
|
|
[s(ModuleNameStr), s(TablingIdStr), s(ProcLabelStr)], !IO)
|
|
;
|
|
Const = mlconst_null(Type),
|
|
Initializer = get_default_initializer_for_java(Type),
|
|
io.write_string(Stream, Initializer, !IO)
|
|
).
|
|
|
|
:- pred output_int_const_for_java(io.text_output_stream::in, int::in,
|
|
io::di, io::uo) is det.
|
|
|
|
output_int_const_for_java(Stream, 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).
|
|
( if
|
|
N > 0,
|
|
not int32.from_int(N, _I32),
|
|
uint32.from_int(N, U32)
|
|
then
|
|
% 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.
|
|
N32 = uint32.cast_to_int(U32),
|
|
io.format(Stream, "0x%x", [i(N32)], !IO)
|
|
else
|
|
io.write_int(Stream, N, !IO)
|
|
).
|
|
|
|
% Take the address of the wrapper for the given function (method).
|
|
%
|
|
:- pred mlds_output_wrapper_code_addr_for_java(java_out_info::in,
|
|
io.text_output_stream::in, mlds_code_addr::in, io::di, io::uo) is det.
|
|
|
|
mlds_output_wrapper_code_addr_for_java(Info, Stream, CodeAddr, !IO) :-
|
|
AddrOfMap = Info ^ joi_addrof_map,
|
|
map.lookup(AddrOfMap, CodeAddr, CodeAddrWrapper),
|
|
CodeAddrWrapper = code_addr_wrapper(ClassName, MaybePtrNum),
|
|
(
|
|
MaybePtrNum = yes(PtrNum),
|
|
io.format(Stream, "new %s_0(%d)", [s(ClassName), i(PtrNum)], !IO)
|
|
;
|
|
MaybePtrNum = no,
|
|
io.format(Stream, "new %s_0()", [s(ClassName)], !IO)
|
|
).
|
|
|
|
:- pred mlds_output_call_code_addr_for_java(io.text_output_stream::in,
|
|
mlds_code_addr::in, io::di, io::uo) is det.
|
|
|
|
mlds_output_call_code_addr_for_java(Stream, CodeAddr, !IO) :-
|
|
CodeAddr = mlds_code_addr(QualFuncLabel, _Signature),
|
|
QualFuncLabel = qual_func_label(ModuleName, FuncLabel),
|
|
FuncLabel = mlds_func_label(ProcLabel, MaybeAux),
|
|
Qualifier = qualifier_to_string_for_java(ModuleName, module_qual),
|
|
ProcLabelStr = proc_label_to_string_for_java(ProcLabel),
|
|
MaybeAuxSuffix = mlds_maybe_aux_func_id_to_suffix(MaybeAux),
|
|
io.format(Stream, "%s.%s%s",
|
|
[s(Qualifier), s(ProcLabelStr), s(MaybeAuxSuffix)], !IO).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% 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.
|
|
% XXX The code below does NOT check for casts.
|
|
%
|
|
:- pred rval_is_enum_object(mlds_rval::in) is semidet.
|
|
|
|
rval_is_enum_object(Rval) :-
|
|
Rval = ml_lval(Lval),
|
|
(
|
|
Lval = ml_local_var(_, Type)
|
|
;
|
|
Lval = ml_global_var(_, Type)
|
|
;
|
|
Lval = ml_field(_, _, _, _, Type)
|
|
),
|
|
type_is_enum(Type).
|
|
|
|
% Succeeds iff this type is a enumeration.
|
|
%
|
|
:- pred type_is_enum(mlds_type::in) is semidet.
|
|
|
|
type_is_enum(Type) :-
|
|
Type = mercury_nb_type(_, CtorCat),
|
|
CtorCat = ctor_cat_enum(_).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
output_initializer_for_java(Info, Stream, OutputAux, Indent, Type,
|
|
Initializer, Suffix, !IO) :-
|
|
(
|
|
( Initializer = init_obj(_)
|
|
; Initializer = init_struct(_, _)
|
|
; Initializer = init_array(_)
|
|
),
|
|
io.write_string(Stream, " = ", !IO),
|
|
% Due to cyclic references, we need to separate the allocation and
|
|
% initialisation steps of RTTI structures. If OutputAux is alloc_only,
|
|
% then we output an initializer to allocate a structure *without*
|
|
% filling in the fields.
|
|
(
|
|
( OutputAux = oa_none
|
|
; OutputAux = oa_cname(_, _)
|
|
; OutputAux = oa_force_init
|
|
),
|
|
output_initializer_body_for_java(Info, Stream,
|
|
not_at_start_of_line, Indent + 1u, Initializer, yes(Type),
|
|
Suffix, !IO)
|
|
;
|
|
OutputAux = oa_alloc_only,
|
|
output_initializer_alloc_only_for_java(Info, Stream, Initializer,
|
|
yes(Type), Suffix, !IO)
|
|
)
|
|
;
|
|
Initializer = no_initializer,
|
|
(
|
|
OutputAux = oa_force_init,
|
|
% Local variables need to be initialised to avoid warnings.
|
|
InitForType = get_default_initializer_for_java(Type),
|
|
io.format(Stream, " = %s", [s(InitForType)], !IO)
|
|
;
|
|
( OutputAux = oa_none
|
|
; OutputAux = oa_cname(_, _)
|
|
; OutputAux = oa_alloc_only
|
|
)
|
|
),
|
|
io.format(Stream, "%s\n", [s(Suffix)], !IO)
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
output_initializer_alloc_only_for_java(Info, Stream, Initializer, MaybeType,
|
|
Suffix, !IO) :-
|
|
(
|
|
Initializer = no_initializer,
|
|
unexpected($pred, "no_initializer")
|
|
;
|
|
Initializer = init_obj(_),
|
|
unexpected($pred, "init_obj")
|
|
;
|
|
Initializer = init_struct(StructType, FieldInits),
|
|
io.write_string(Stream, "new ", !IO),
|
|
( if
|
|
StructType = mercury_nb_type(_, CtorCat),
|
|
type_category_is_array(coerce(CtorCat)) = is_array
|
|
then
|
|
Size = list.length(FieldInits),
|
|
io.format(Stream, "java.lang.Object[%d]%s\n",
|
|
[i(Size), s(Suffix)], !IO)
|
|
else
|
|
io.format(Stream, "%s()%s\n",
|
|
[s(type_to_string_for_java(Info, StructType)), s(Suffix)],
|
|
!IO)
|
|
)
|
|
;
|
|
Initializer = init_array(ElementInits),
|
|
Size = list.length(ElementInits),
|
|
io.write_string(Stream, "new ", !IO),
|
|
(
|
|
MaybeType = yes(Type),
|
|
type_to_string_and_dims_for_java(Info, Type,
|
|
BaseTypeName, ArrayDims0),
|
|
make_last_dimension_known_size(ArrayDims0, Size, ArrayDims),
|
|
DimsStr = array_dimensions_to_string(ArrayDims),
|
|
io.format(Stream, "%s%s%s\n",
|
|
[s(BaseTypeName), s(DimsStr), s(Suffix)], !IO)
|
|
;
|
|
MaybeType = no,
|
|
% XXX We need to know the type here.
|
|
io.format(Stream, "/* XXX init_array */ Object[%d]%s\n",
|
|
[i(Size), s(Suffix)], !IO)
|
|
)
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
output_initializer_body_for_java(Info, Stream, InitStart, Indent, Initializer,
|
|
MaybeType, Suffix, !IO) :-
|
|
(
|
|
Initializer = no_initializer,
|
|
unexpected($pred, "no_initializer")
|
|
;
|
|
Initializer = init_obj(Rval),
|
|
(
|
|
InitStart = not_at_start_of_line
|
|
;
|
|
InitStart = at_start_of_line,
|
|
write_indent2(Stream, Indent, !IO)
|
|
),
|
|
output_rval_for_java(Info, Rval, Stream, !IO),
|
|
io.format(Stream, "%s\n", [s(Suffix)], !IO)
|
|
;
|
|
Initializer = init_struct(StructType, FieldInits),
|
|
IndentStr = indent2_string(Indent),
|
|
(
|
|
InitStart = not_at_start_of_line,
|
|
io.nl(Stream, !IO)
|
|
;
|
|
InitStart = at_start_of_line
|
|
),
|
|
io.format(Stream, "%snew ", [s(IndentStr)], !IO),
|
|
output_type_for_java(Info, Stream, StructType, ArrayDims, !IO),
|
|
init_arg_wrappers_cs_java(ArrayDims, Start, End),
|
|
(
|
|
FieldInits = [],
|
|
io.format(Stream, "%s%s%s\n", [s(Start), s(End), s(Suffix)], !IO)
|
|
;
|
|
FieldInits = [HeadFieldInit | TailFieldInits],
|
|
io.format(Stream, "%s\n", [s(Start)], !IO),
|
|
output_initializer_body_list_for_java(Info, Stream, Indent + 1u,
|
|
HeadFieldInit, TailFieldInits, "", !IO),
|
|
io.format(Stream, "%s%s%s\n",
|
|
[s(IndentStr), s(End), s(Suffix)], !IO)
|
|
)
|
|
;
|
|
Initializer = init_array(ElementInits),
|
|
(
|
|
InitStart = not_at_start_of_line,
|
|
io.nl(Stream, !IO)
|
|
;
|
|
InitStart = at_start_of_line
|
|
),
|
|
IndentStr = indent2_string(Indent),
|
|
io.format(Stream, "%snew ", [s(IndentStr)], !IO),
|
|
(
|
|
MaybeType = yes(Type),
|
|
output_type_for_java(Info, Stream, Type, !IO)
|
|
;
|
|
MaybeType = no,
|
|
% XXX We need to know the type here.
|
|
io.write_string(Stream, "/* XXX init_array */ Object[]", !IO)
|
|
),
|
|
(
|
|
ElementInits = [],
|
|
io.format(Stream, " {}%s\n", [s(Suffix)], !IO)
|
|
;
|
|
ElementInits = [HeadElementInit | TailElementInits],
|
|
io.write_string(Stream, " {\n", !IO),
|
|
output_initializer_body_list_for_java(Info, Stream, Indent + 1u,
|
|
HeadElementInit, TailElementInits, "", !IO),
|
|
io.format(Stream, "%s}%s\n", [s(IndentStr), s(Suffix)], !IO)
|
|
)
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
output_nonempty_initializer_body_list_for_java(Info, Stream, Indent, Inits,
|
|
Suffix, !IO) :-
|
|
list.det_head_tail(Inits, HeadInit, TailInits),
|
|
output_initializer_body_list_for_java(Info, Stream, Indent,
|
|
HeadInit, TailInits, Suffix, !IO).
|
|
|
|
:- pred output_initializer_body_list_for_java(java_out_info::in,
|
|
io.text_output_stream::in, indent::in, mlds_initializer::in,
|
|
list(mlds_initializer)::in, string::in, io::di, io::uo) is det.
|
|
|
|
output_initializer_body_list_for_java(Info, Stream,
|
|
Indent, HeadInit, TailInits, Suffix, !IO) :-
|
|
(
|
|
TailInits = [],
|
|
output_initializer_body_for_java(Info, Stream, at_start_of_line,
|
|
Indent, HeadInit, no, Suffix, !IO)
|
|
;
|
|
TailInits = [HeadTailInit | TailTailInits],
|
|
output_initializer_body_for_java(Info, Stream, at_start_of_line,
|
|
Indent, HeadInit, no, ",", !IO),
|
|
output_initializer_body_list_for_java(Info, Stream,
|
|
Indent, HeadTailInit, TailTailInits, Suffix, !IO)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
get_default_initializer_for_java(Type) = Initializer :-
|
|
(
|
|
Type = mercury_nb_type(_, CtorCat),
|
|
( 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_builtin_type_int(_)
|
|
; Type = mlds_builtin_type_float
|
|
),
|
|
Initializer = "0"
|
|
;
|
|
Type = mlds_builtin_type_string,
|
|
Initializer = "null"
|
|
;
|
|
Type = mlds_builtin_type_char,
|
|
Initializer = "'\\u0000'"
|
|
;
|
|
Type = mlds_native_bool_type,
|
|
Initializer = "false"
|
|
;
|
|
Type = mlds_foreign_type(ForeignLangType),
|
|
( if
|
|
java_primitive_foreign_language_type(ForeignLangType, _, _,
|
|
_, Initializer0)
|
|
then
|
|
Initializer = Initializer0
|
|
else
|
|
Initializer = "null"
|
|
)
|
|
;
|
|
( Type = mlds_mercury_array_type(_)
|
|
; Type = mlds_cont_type(_)
|
|
; Type = mlds_commit_type
|
|
; Type = mlds_class_type(_)
|
|
; Type = mlds_enum_class_type(_)
|
|
; Type = mlds_env_type(_)
|
|
; Type = mlds_struct_type(_)
|
|
; Type = mlds_array_type(_)
|
|
; Type = mlds_mostly_generic_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($pred, "variable has unknown_type")
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module ml_backend.mlds_to_java_data.
|
|
%---------------------------------------------------------------------------%
|