mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 17:33:38 +00:00
compiler/mlds_to_c_stmt.m:
compiler/mlds_to_c_class.m:
compiler/mlds_to_c_func.m:
Write out more things in calls to io.format.
Require an enum class to contain at one enum.
compiler/mlds_to_target_util.m:
Delete the output_n_indent and write_indented_line predicates,
since this series of changes has replaced all calls to them.
compiler/indent.m:
compiler/mlds_to_target_util.m:
Move the indent type (a synonym for int) from mlds_to_target_util.m
to indent.m, to prepare for the expansion of its use to other parts
of the compiler.
compiler/mlds_to_c_export.m:
compiler/mlds_to_c_file.m:
compiler/mlds_to_c_global.m:
compiler/mlds_to_cs_class.m:
compiler/mlds_to_cs_data.m:
compiler/mlds_to_cs_export.m:
compiler/mlds_to_cs_func.m:
compiler/mlds_to_cs_global.m:
compiler/mlds_to_cs_stmt.m:
compiler/mlds_to_java_class.m:
compiler/mlds_to_java_data.m:
compiler/mlds_to_java_export.m:
compiler/mlds_to_java_func.m:
compiler/mlds_to_java_global.m:
compiler/mlds_to_java_stmt.m:
compiler/mlds_to_java_util.m:
Import indent.m instead of mlds_to_target_util.m to get access
to the indent type.
941 lines
38 KiB
Mathematica
941 lines
38 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2000-2012 The University of Melbourne.
|
|
% Copyright (C) 2013-2018 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 statements in Java.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module ml_backend.mlds_to_java_stmt.
|
|
:- 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.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred output_statements_for_java(java_out_info::in,
|
|
io.text_output_stream::in, indent::in, func_info_csj::in,
|
|
list(mlds_stmt)::in, exit_methods::out, io::di, io::uo) is det.
|
|
|
|
:- pred output_statement_for_java(java_out_info::in, io.text_output_stream::in,
|
|
indent::in, func_info_csj::in, mlds_stmt::in, exit_methods::out,
|
|
io::di, io::uo) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module hlds.
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module libs.globals.
|
|
:- import_module ml_backend.ml_util.
|
|
:- import_module ml_backend.mlds_to_java_data.
|
|
:- import_module ml_backend.mlds_to_java_func. % undesirable circular dep
|
|
:- import_module ml_backend.mlds_to_java_name.
|
|
:- import_module ml_backend.mlds_to_java_type.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.parse_tree_out_info.
|
|
:- import_module parse_tree.prog_data.
|
|
:- import_module parse_tree.prog_type.
|
|
|
|
:- import_module assoc_list.
|
|
:- import_module bool.
|
|
:- import_module int.
|
|
:- import_module maybe.
|
|
:- import_module pair.
|
|
:- import_module require.
|
|
:- import_module set.
|
|
:- import_module string.
|
|
:- import_module term.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
output_statements_for_java(_, _, _, _, [], ExitMethods, !IO) :-
|
|
ExitMethods = set.make_singleton_set(can_fall_through).
|
|
output_statements_for_java(Info, Stream, Indent, FuncInfo, [Stmt | Stmts],
|
|
ExitMethods, !IO) :-
|
|
output_statement_for_java(Info, Stream, Indent, FuncInfo, Stmt,
|
|
StmtExitMethods, !IO),
|
|
( if set.member(can_fall_through, StmtExitMethods) then
|
|
output_statements_for_java(Info, Stream, Indent, FuncInfo, Stmts,
|
|
StmtsExitMethods, !IO),
|
|
ExitMethods0 = set.union(StmtExitMethods, StmtsExitMethods),
|
|
( if set.member(can_fall_through, StmtsExitMethods) then
|
|
ExitMethods = ExitMethods0
|
|
else
|
|
% If the last statement could not complete normally
|
|
% the current block can no longer complete normally.
|
|
ExitMethods = set.delete(ExitMethods0, can_fall_through)
|
|
)
|
|
else
|
|
% Don't output any more statements from the current list since
|
|
% the previous statement cannot complete.
|
|
ExitMethods = StmtExitMethods
|
|
).
|
|
|
|
output_statement_for_java(Info, Stream, Indent, FuncInfo, Stmt,
|
|
ExitMethods, !IO) :-
|
|
Context = get_mlds_stmt_context(Stmt),
|
|
output_context_for_java(Stream, Info ^ joi_line_numbers,
|
|
marker_comment, Context, !IO),
|
|
(
|
|
Stmt = ml_stmt_block(_, _, _, _),
|
|
output_stmt_block_for_java(Info, Stream, Indent, FuncInfo, Stmt,
|
|
ExitMethods, !IO)
|
|
;
|
|
Stmt = ml_stmt_while(_, _, _, _, _),
|
|
output_stmt_while_for_java(Info, Stream, Indent, FuncInfo, Stmt,
|
|
ExitMethods, !IO)
|
|
;
|
|
Stmt = ml_stmt_if_then_else(_, _, _, _),
|
|
output_stmt_if_then_else_for_java(Info, Stream, Indent, FuncInfo, Stmt,
|
|
ExitMethods, !IO)
|
|
;
|
|
Stmt = ml_stmt_switch(_, _, _, _, _, _),
|
|
output_stmt_switch_for_java(Info, Stream, Indent, FuncInfo, Stmt,
|
|
ExitMethods, !IO)
|
|
;
|
|
Stmt = ml_stmt_label(_, _),
|
|
unexpected($pred, "labels not supported in Java.")
|
|
;
|
|
Stmt = ml_stmt_goto(Target, _),
|
|
(
|
|
Target = goto_label(_),
|
|
unexpected($pred, "gotos not supported in Java.")
|
|
;
|
|
Target = goto_break_switch,
|
|
BreakContext = Info ^ joi_break_context,
|
|
(
|
|
BreakContext = bc_switch,
|
|
write_indent2(Stream, Indent, !IO),
|
|
io.write_string(Stream, "break;\n", !IO),
|
|
ExitMethods = set.make_singleton_set(can_break)
|
|
;
|
|
( BreakContext = bc_none
|
|
; BreakContext = bc_loop
|
|
),
|
|
unexpected($pred, "goto_break_switch not in switch")
|
|
)
|
|
;
|
|
Target = goto_break_loop,
|
|
BreakContext = Info ^ joi_break_context,
|
|
(
|
|
BreakContext = bc_loop,
|
|
write_indent2(Stream, Indent, !IO),
|
|
io.write_string(Stream, "break;\n", !IO),
|
|
ExitMethods = set.make_singleton_set(can_break)
|
|
;
|
|
( BreakContext = bc_none
|
|
; BreakContext = bc_switch
|
|
),
|
|
unexpected($pred, "goto_break_loop not in loop")
|
|
)
|
|
;
|
|
Target = goto_continue_loop,
|
|
write_indent2(Stream, Indent, !IO),
|
|
io.write_string(Stream, "continue;\n", !IO),
|
|
ExitMethods = set.make_singleton_set(can_continue)
|
|
)
|
|
;
|
|
Stmt = ml_stmt_computed_goto(_, _, _),
|
|
unexpected($pred, "computed gotos not supported in Java.")
|
|
;
|
|
Stmt = ml_stmt_call(_, _, _, _, _, _),
|
|
output_stmt_call_for_java(Info, Stream, Indent, FuncInfo, Stmt,
|
|
ExitMethods, !IO)
|
|
;
|
|
Stmt = ml_stmt_return(_, _),
|
|
output_stmt_return_for_java(Info, Stream, Indent, FuncInfo, Stmt,
|
|
ExitMethods, !IO)
|
|
;
|
|
Stmt = ml_stmt_do_commit(_, _),
|
|
output_stmt_do_commit_for_java(Info, Stream, Indent, FuncInfo, Stmt,
|
|
ExitMethods, !IO)
|
|
;
|
|
Stmt = ml_stmt_try_commit(_, _, _, _),
|
|
output_stmt_try_commit_for_java(Info, Stream, Indent, FuncInfo, Stmt,
|
|
ExitMethods, !IO)
|
|
;
|
|
Stmt = ml_stmt_atomic(AtomicStmt, _Context),
|
|
output_atomic_stmt_for_java(Info, Stream, Indent, AtomicStmt,
|
|
Context, !IO),
|
|
ExitMethods = set.make_singleton_set(can_fall_through)
|
|
).
|
|
|
|
:- pred output_stmt_block_for_java(java_out_info::in,
|
|
io.text_output_stream::in, indent::in, func_info_csj::in,
|
|
mlds_stmt::in(ml_stmt_is_block), exit_methods::out, io::di, io::uo) is det.
|
|
:- pragma inline(pred(output_stmt_block_for_java/8)).
|
|
|
|
output_stmt_block_for_java(Info, Stream, Indent, FuncInfo, Stmt,
|
|
ExitMethods, !IO) :-
|
|
Stmt = ml_stmt_block(LocalVarDefns, FuncDefns, SubStmts, Context),
|
|
BraceIndent = Indent,
|
|
BlockIndent = Indent + 1,
|
|
BraceIndentStr = indent2_string(BraceIndent),
|
|
io.format(Stream, "%s{\n", [s(BraceIndentStr)], !IO),
|
|
(
|
|
LocalVarDefns = [_ | _],
|
|
list.foldl(
|
|
output_local_var_defn_for_java(Info, Stream, BlockIndent),
|
|
LocalVarDefns, !IO),
|
|
io.write_string(Stream, "\n", !IO)
|
|
;
|
|
LocalVarDefns = []
|
|
),
|
|
(
|
|
FuncDefns = [_ | _],
|
|
list.foldl(
|
|
output_function_defn_for_java(Info, Stream, BlockIndent,
|
|
oa_force_init),
|
|
FuncDefns, !IO),
|
|
io.write_string(Stream, "\n", !IO)
|
|
;
|
|
FuncDefns = []
|
|
),
|
|
output_statements_for_java(Info, Stream, BlockIndent, FuncInfo, SubStmts,
|
|
ExitMethods, !IO),
|
|
indent_line_after_context(Stream, Info ^ joi_line_numbers,
|
|
marker_comment, Context, BraceIndent, !IO),
|
|
io.write_string(Stream, "}\n", !IO).
|
|
|
|
:- pred output_local_var_defn_for_java(java_out_info::in,
|
|
io.text_output_stream::in, indent::in, mlds_local_var_defn::in,
|
|
io::di, io::uo) is det.
|
|
|
|
output_local_var_defn_for_java(Info, Stream, Indent, LocalVarDefn, !IO) :-
|
|
LocalVarDefn = mlds_local_var_defn(LocalVarName, Context, Type,
|
|
Initializer, _),
|
|
indent_line_after_context(Stream, Info ^ joi_line_numbers,
|
|
marker_comment, Context, Indent, !IO),
|
|
output_local_var_decl_for_java(Info, Stream, LocalVarName, Type, !IO),
|
|
output_initializer_for_java(Info, Stream, oa_force_init, Indent + 1,
|
|
Type, Initializer, ";", !IO).
|
|
|
|
:- pred output_local_var_decl_for_java(java_out_info::in,
|
|
io.text_output_stream::in, mlds_local_var_name::in, mlds_type::in,
|
|
io::di, io::uo) is det.
|
|
|
|
output_local_var_decl_for_java(Info, Stream, LocalVarName, Type, !IO) :-
|
|
TypeStr = type_to_string_for_java(Info, Type),
|
|
LocalVarNameStr = local_var_name_to_string_for_java(LocalVarName),
|
|
io.format(Stream, "%s %s", [s(TypeStr), s(LocalVarNameStr)], !IO).
|
|
|
|
:- pred output_stmt_while_for_java(java_out_info::in,
|
|
io.text_output_stream::in, indent::in, func_info_csj::in,
|
|
mlds_stmt::in(ml_stmt_is_while), exit_methods::out, io::di, io::uo) is det.
|
|
:- pragma inline(pred(output_stmt_while_for_java/8)).
|
|
|
|
output_stmt_while_for_java(Info, Stream, Indent, FuncInfo, Stmt,
|
|
ExitMethods, !IO) :-
|
|
Stmt = ml_stmt_while(Kind, Cond, BodyStmt, _LoopLocalVars, Context),
|
|
scope_indent(BodyStmt, Indent, ScopeIndent),
|
|
IndentStr = indent2_string(Indent),
|
|
(
|
|
Kind = may_loop_zero_times,
|
|
io.format(Stream, "%swhile (", [s(IndentStr)], !IO),
|
|
output_rval_for_java(Info, Cond, Stream, !IO),
|
|
io.write_string(Stream, ")\n", !IO),
|
|
% The contained statement is reachable iff the while statement
|
|
% is reachable the condition is not a constant expression
|
|
% whose value is false.
|
|
( if Cond = ml_const(mlconst_false) then
|
|
io.format(Stream, "%s{\n", [s(IndentStr)], !IO),
|
|
io.format(Stream, "%s /* Unreachable code */\n",
|
|
[s(IndentStr)], !IO),
|
|
io.format(Stream, "%s}\n", [s(IndentStr)], !IO),
|
|
ExitMethods = set.make_singleton_set(can_fall_through)
|
|
else
|
|
BodyInfo = Info ^ joi_break_context := bc_loop,
|
|
output_statement_for_java(BodyInfo, Stream, ScopeIndent, FuncInfo,
|
|
BodyStmt, StmtExitMethods, !IO),
|
|
ExitMethods = while_exit_methods_for_java(Cond, StmtExitMethods)
|
|
)
|
|
;
|
|
Kind = loop_at_least_once,
|
|
io.format(Stream, "%sdo\n", [s(IndentStr)], !IO),
|
|
BodyInfo = Info ^ joi_break_context := bc_loop,
|
|
output_statement_for_java(BodyInfo, Stream, ScopeIndent, FuncInfo,
|
|
BodyStmt, StmtExitMethods, !IO),
|
|
indent_line_after_context(Stream, Info ^ joi_line_numbers,
|
|
marker_comment, Context, Indent, !IO),
|
|
io.write_string(Stream, "while (", !IO),
|
|
output_rval_for_java(Info, Cond, Stream, !IO),
|
|
io.write_string(Stream, ");\n", !IO),
|
|
ExitMethods = while_exit_methods_for_java(Cond, StmtExitMethods)
|
|
).
|
|
|
|
:- pred output_stmt_if_then_else_for_java(java_out_info::in,
|
|
io.text_output_stream::in, indent::in, func_info_csj::in,
|
|
mlds_stmt::in(ml_stmt_is_if_then_else), exit_methods::out,
|
|
io::di, io::uo) is det.
|
|
:- pragma inline(pred(output_stmt_if_then_else_for_java/8)).
|
|
|
|
output_stmt_if_then_else_for_java(Info, Stream, Indent, FuncInfo, Stmt,
|
|
ExitMethods, !IO) :-
|
|
Stmt = ml_stmt_if_then_else(Cond, Then0, MaybeElse, Context),
|
|
% We need to take care to avoid problems caused by the dangling else
|
|
% ambiguity.
|
|
( if
|
|
% For statements 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 = ml_stmt_if_then_else(_, _, no, ThenContext)
|
|
then
|
|
Then = ml_stmt_block([], [], [Then0], ThenContext)
|
|
else
|
|
Then = Then0
|
|
),
|
|
|
|
IndentStr = indent2_string(Indent),
|
|
io.format(Stream, "%sif (", [s(IndentStr)], !IO),
|
|
output_rval_for_java(Info, Cond, Stream, !IO),
|
|
io.write_string(Stream, ")\n", !IO),
|
|
scope_indent(Then, Indent, ThenScopeIndent),
|
|
output_statement_for_java(Info, Stream, ThenScopeIndent, FuncInfo,
|
|
Then, ThenExitMethods, !IO),
|
|
(
|
|
MaybeElse = yes(Else),
|
|
indent_line_after_context(Stream, Info ^ joi_line_numbers,
|
|
marker_comment, Context, Indent, !IO),
|
|
io.write_string(Stream, "else\n", !IO),
|
|
scope_indent(Else, Indent, ElseScopeIndent),
|
|
output_statement_for_java(Info, Stream, ElseScopeIndent, 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 = set.union(ThenExitMethods, ElseExitMethods)
|
|
;
|
|
MaybeElse = no,
|
|
% An if-then statement can complete normally iff it is reachable.
|
|
ExitMethods = set.insert(ThenExitMethods, can_fall_through)
|
|
).
|
|
|
|
:- pred output_stmt_switch_for_java(java_out_info::in,
|
|
io.text_output_stream::in, indent::in, func_info_csj::in,
|
|
mlds_stmt::in(ml_stmt_is_switch), exit_methods::out,
|
|
io::di, io::uo) is det.
|
|
:- pragma inline(pred(output_stmt_switch_for_java/8)).
|
|
|
|
output_stmt_switch_for_java(Info, Stream, Indent, FuncInfo, Stmt,
|
|
ExitMethods, !IO) :-
|
|
Stmt = ml_stmt_switch(_Type, Rval, _Range, Cases, Default,
|
|
Context),
|
|
indent_line_after_context(Stream, Info ^ joi_line_numbers,
|
|
marker_comment, Context, Indent, !IO),
|
|
io.write_string(Stream, "switch (", !IO),
|
|
output_rval_maybe_with_enum_for_java(Info, Rval, Stream, !IO),
|
|
io.write_string(Stream, ") {\n", !IO),
|
|
CaseInfo = Info ^ joi_break_context := bc_switch,
|
|
output_switch_cases_for_java(CaseInfo, Stream, Indent + 1, FuncInfo,
|
|
Context, Cases, Default, ExitMethods, !IO),
|
|
indent_line_after_context(Stream, Info ^ joi_line_numbers,
|
|
marker_comment, Context, Indent, !IO),
|
|
io.write_string(Stream, "}\n", !IO).
|
|
|
|
:- pred output_stmt_call_for_java(java_out_info::in, io.text_output_stream::in,
|
|
indent::in, func_info_csj::in, mlds_stmt::in(ml_stmt_is_call),
|
|
exit_methods::out, io::di, io::uo) is det.
|
|
:- pragma inline(pred(output_stmt_call_for_java/8)).
|
|
|
|
output_stmt_call_for_java(Info, Stream, Indent, _FuncInfo, Stmt,
|
|
ExitMethods, !IO) :-
|
|
Stmt = ml_stmt_call(Signature, FuncRval, CallArgs, Results,
|
|
_IsTailCall, Context),
|
|
Signature = mlds_func_signature(ArgTypes, RetTypes),
|
|
IndentStr = indent2_string(Indent),
|
|
io.format(Stream, "%s{\n", [s(IndentStr)], !IO),
|
|
indent_line_after_context(Stream, Info ^ joi_line_numbers,
|
|
marker_comment, Context, Indent + 1, !IO),
|
|
(
|
|
Results = []
|
|
;
|
|
Results = [Lval],
|
|
output_lval_for_java(Info, Lval, Stream, !IO),
|
|
io.write_string(Stream, " = ", !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(Stream, "java.lang.Object [] result = ", !IO)
|
|
),
|
|
( if FuncRval = ml_const(mlconst_code_addr(_)) then
|
|
% This is a standard function call.
|
|
output_call_rval_for_java(Info, FuncRval, Stream, !IO),
|
|
io.write_string(Stream, "(", !IO),
|
|
write_out_list(output_rval_for_java(Info), ", ", CallArgs,
|
|
Stream, !IO),
|
|
io.write_string(Stream, ")", !IO)
|
|
else
|
|
% 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 = [],
|
|
output_stmt_method_call_for_java(Info, Stream, FuncRval,
|
|
CallArgs, ArgTypes, !IO)
|
|
;
|
|
RetTypes = [RetType],
|
|
boxed_type_to_string_for_java(Info, RetType, RetTypeString),
|
|
io.format(Stream, "((%s) ", [s(RetTypeString)], !IO),
|
|
output_stmt_method_call_for_java(Info, Stream, FuncRval,
|
|
CallArgs, ArgTypes, !IO),
|
|
io.write_string(Stream, ")", !IO),
|
|
( if java_builtin_type(RetType, _, _, UnboxMethod) then
|
|
io.format(Stream, ".%s()", [s(UnboxMethod)], !IO)
|
|
else
|
|
true
|
|
)
|
|
;
|
|
RetTypes = [_, _ | _],
|
|
io.write_string(Stream, "((java.lang.Object[]) ", !IO),
|
|
output_stmt_method_call_for_java(Info, Stream, FuncRval,
|
|
CallArgs, ArgTypes, !IO),
|
|
io.write_string(Stream, ")", !IO)
|
|
)
|
|
),
|
|
io.write_string(Stream, ";\n", !IO),
|
|
|
|
( if Results = [_, _ | _] then
|
|
% Copy the results from the "result" array into the Result lvals
|
|
% (unboxing them as we go).
|
|
output_assign_results(Info, Stream, Results, RetTypes, 0, Indent + 1,
|
|
Context, !IO)
|
|
else
|
|
true
|
|
),
|
|
% XXX Is this needed? If present, it causes compiler errors for a
|
|
% couple of files in the benchmarks directory. -mjwybrow
|
|
%
|
|
% ( if IsTailCall = tail_call, Results = [] then
|
|
% indent_line_after_context(Context, Indent + 1, !IO),
|
|
% io.write_string(Stream, "return;\n", !IO)
|
|
% else
|
|
% true
|
|
% ),
|
|
io.format(Stream, "%s}\n", [s(IndentStr)], !IO),
|
|
ExitMethods = set.make_singleton_set(can_fall_through).
|
|
|
|
:- pred output_stmt_return_for_java(java_out_info::in,
|
|
io.text_output_stream::in, indent::in, func_info_csj::in,
|
|
mlds_stmt::in(ml_stmt_is_return), exit_methods::out,
|
|
io::di, io::uo) is det.
|
|
:- pragma inline(pred(output_stmt_return_for_java/8)).
|
|
|
|
output_stmt_return_for_java(Info, Stream, Indent, FuncInfo, Stmt,
|
|
ExitMethods, !IO) :-
|
|
Stmt = ml_stmt_return(Results, _Context),
|
|
IndentStr = indent2_string(Indent),
|
|
(
|
|
Results = [],
|
|
io.format(Stream, "%sreturn;\n", [s(IndentStr)], !IO)
|
|
;
|
|
Results = [Rval],
|
|
io.format(Stream, "%sreturn ", [s(IndentStr)], !IO),
|
|
output_rval_for_java(Info, Rval, Stream, !IO),
|
|
io.write_string(Stream, ";\n", !IO)
|
|
;
|
|
Results = [_, _ | _],
|
|
FuncInfo = func_info_csj(Params),
|
|
Params = mlds_func_params(_Args, ReturnTypes),
|
|
TypesAndResults = assoc_list.from_corresponding_lists(
|
|
ReturnTypes, Results),
|
|
io.format(Stream, "%sreturn new java.lang.Object[] {\n",
|
|
[s(IndentStr)], !IO),
|
|
Indent1Str = indent2_string(Indent + 1),
|
|
% Each call to OutputBoxedRval writes the entire contents of a line
|
|
% without the final (comma and) newline.
|
|
OutputBoxedRval =
|
|
( pred((Type - Result)::in, S::in, !.IO::di, !:IO::uo) is det :-
|
|
io.write_string(Stream, Indent1Str, !IO),
|
|
output_boxed_rval_for_java(Info, Type, Result, S, !IO)
|
|
),
|
|
write_out_list(OutputBoxedRval, ",\n", TypesAndResults, Stream, !IO),
|
|
io.format(Stream, "\n%s};\n", [s(IndentStr)], !IO)
|
|
),
|
|
ExitMethods = set.make_singleton_set(can_return).
|
|
|
|
:- pred output_stmt_do_commit_for_java(java_out_info::in,
|
|
io.text_output_stream::in, indent::in, func_info_csj::in,
|
|
mlds_stmt::in(ml_stmt_is_do_commit), exit_methods::out,
|
|
io::di, io::uo) is det.
|
|
:- pragma inline(pred(output_stmt_do_commit_for_java/8)).
|
|
|
|
output_stmt_do_commit_for_java(Info, Stream, Indent, _FuncInfo, Stmt,
|
|
ExitMethods, !IO) :-
|
|
Stmt = ml_stmt_do_commit(Ref, _Context),
|
|
IndentStr = indent2_string(Indent),
|
|
io.write_string(Stream, IndentStr, !IO),
|
|
output_rval_for_java(Info, Ref, Stream, !IO),
|
|
io.write_string(Stream, " = new jmercury.runtime.Commit();\n", !IO),
|
|
io.format(Stream, "%sthrow ", [s(IndentStr)], !IO),
|
|
output_rval_for_java(Info, Ref, Stream, !IO),
|
|
io.write_string(Stream, ";\n", !IO),
|
|
ExitMethods = set.make_singleton_set(can_throw).
|
|
|
|
:- pred output_stmt_try_commit_for_java(java_out_info::in,
|
|
io.text_output_stream::in, indent::in, func_info_csj::in,
|
|
mlds_stmt::in(ml_stmt_is_try_commit), exit_methods::out,
|
|
io::di, io::uo) is det.
|
|
:- pragma inline(pred(output_stmt_try_commit_for_java/8)).
|
|
|
|
output_stmt_try_commit_for_java(Info, Stream, Indent, FuncInfo, Stmt,
|
|
ExitMethods, !IO) :-
|
|
Stmt = ml_stmt_try_commit(_Ref, BodyStmt, HandlerStmt, _Context),
|
|
IndentStr = indent2_string(Indent),
|
|
io.format(Stream, "%stry\n", [s(IndentStr)], !IO),
|
|
io.format(Stream, "%s{\n", [s(IndentStr)], !IO),
|
|
output_statement_for_java(Info, Stream, Indent + 1, FuncInfo, BodyStmt,
|
|
TryExitMethods0, !IO),
|
|
io.format(Stream, "%s}\n", [s(IndentStr)], !IO),
|
|
io.format(Stream, "%scatch (jmercury.runtime.Commit commit_variable)\n",
|
|
[s(IndentStr)], !IO),
|
|
io.format(Stream, "%s{\n", [s(IndentStr)], !IO),
|
|
write_indent2(Stream, Indent + 1, !IO),
|
|
output_statement_for_java(Info, Stream, Indent + 1, FuncInfo, HandlerStmt,
|
|
CatchExitMethods, !IO),
|
|
io.format(Stream, "%s}\n", [s(IndentStr)], !IO),
|
|
ExitMethods = set.union(set.delete(TryExitMethods0, can_throw),
|
|
CatchExitMethods).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Extra code for handling while-loops.
|
|
%
|
|
|
|
:- func while_exit_methods_for_java(mlds_rval, exit_methods) = exit_methods.
|
|
|
|
while_exit_methods_for_java(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.
|
|
( if
|
|
% 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)
|
|
then
|
|
% Cannot complete normally.
|
|
ExitMethods0 = set.delete(BlockExitMethods, can_fall_through)
|
|
else
|
|
ExitMethods0 = set.insert(BlockExitMethods, can_fall_through)
|
|
),
|
|
ExitMethods = set.delete_list(ExitMethods0, [can_continue, can_break]).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Extra code for handling function calls/returns.
|
|
%
|
|
|
|
:- pred output_stmt_method_call_for_java(java_out_info::in,
|
|
io.text_output_stream::in, mlds_rval::in, list(mlds_rval)::in,
|
|
list(mlds_type)::in, io::di, io::uo) is det.
|
|
:- pragma inline(pred(output_stmt_method_call_for_java/7)).
|
|
|
|
output_stmt_method_call_for_java(Info, Stream, FuncRval,
|
|
CallArgs, ArgTypes, !IO) :-
|
|
list.length(CallArgs, Arity),
|
|
( if is_specialised_method_ptr_arity(Arity) then
|
|
io.format(Stream, "((jmercury.runtime.MethodPtr%d) ", [i(Arity)], !IO),
|
|
output_bracketed_rval_for_java(Info, FuncRval, Stream, !IO),
|
|
io.write_string(Stream, ").call___0_0(", !IO),
|
|
output_boxed_args(Info, Stream, CallArgs, ArgTypes, !IO),
|
|
io.write_string(Stream, ")", !IO)
|
|
else
|
|
io.write_string(Stream, "((jmercury.runtime.MethodPtrN) ", !IO),
|
|
output_bracketed_rval_for_java(Info, FuncRval, Stream, !IO),
|
|
io.write_string(Stream, ").call___0_0(", !IO),
|
|
output_args_as_array(Info, Stream, CallArgs, ArgTypes, !IO),
|
|
io.write_string(Stream, ")", !IO)
|
|
).
|
|
|
|
:- pred output_args_as_array(java_out_info::in, io.text_output_stream::in,
|
|
list(mlds_rval)::in, list(mlds_type)::in, io::di, io::uo) is det.
|
|
|
|
output_args_as_array(Info, Stream, CallArgs, CallArgTypes, !IO) :-
|
|
io.write_string(Stream, "new java.lang.Object[] { ", !IO),
|
|
output_boxed_args(Info, Stream, CallArgs, CallArgTypes, !IO),
|
|
io.write_string(Stream, "} ", !IO).
|
|
|
|
:- pred output_boxed_args(java_out_info::in, io.text_output_stream::in,
|
|
list(mlds_rval)::in, list(mlds_type)::in, io::di, io::uo) is det.
|
|
|
|
output_boxed_args(_, _, [], [], !IO).
|
|
output_boxed_args(_, _, [_ | _], [], !IO) :-
|
|
unexpected($pred, "length mismatch").
|
|
output_boxed_args(_, _, [], [_ | _], !IO) :-
|
|
unexpected($pred, "length mismatch").
|
|
output_boxed_args(Info, Stream,
|
|
[CallArg | CallArgs], [CallArgType | CallArgTypes], !IO) :-
|
|
output_boxed_rval_for_java(Info, CallArgType, CallArg, Stream, !IO),
|
|
(
|
|
CallArgs = []
|
|
;
|
|
CallArgs = [_ | _],
|
|
io.write_string(Stream, ", ", !IO),
|
|
output_boxed_args(Info, Stream, 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,
|
|
io.text_output_stream::in, list(mlds_lval)::in, list(mlds_type)::in,
|
|
int::in, indent::in, prog_context::in, io::di, io::uo) is det.
|
|
|
|
output_assign_results(_, _, [], [], _, _, _, !IO).
|
|
output_assign_results(_, _, [], [_ | _], _, _, _, _, _) :-
|
|
unexpected($pred, "list length mismatch").
|
|
output_assign_results(_, _, [_ | _], [], _, _, _, _, _) :-
|
|
unexpected($pred, "list length mismatch").
|
|
output_assign_results(Info, Stream, [Lval | Lvals], [Type | Types],
|
|
ResultIndex, Indent, Context, !IO) :-
|
|
indent_line_after_context(Stream, Info ^ joi_line_numbers,
|
|
marker_comment, Context, Indent, !IO),
|
|
output_lval_for_java(Info, Lval, Stream, !IO),
|
|
io.write_string(Stream, " = ", !IO),
|
|
output_unboxed_result(Info, Stream, Type, ResultIndex, !IO),
|
|
io.write_string(Stream, ";\n", !IO),
|
|
output_assign_results(Info, Stream, Lvals, Types,
|
|
ResultIndex + 1, Indent, Context, !IO).
|
|
|
|
:- pred output_unboxed_result(java_out_info::in, io.text_output_stream::in,
|
|
mlds_type::in, int::in, io::di, io::uo) is det.
|
|
|
|
output_unboxed_result(Info, Stream, Type, ResultIndex, !IO) :-
|
|
( if java_builtin_type(Type, _, JavaBoxedTypeName, UnboxMethod) then
|
|
io.format(Stream, "((%s) result[%d]).%s()",
|
|
[s(JavaBoxedTypeName), i(ResultIndex), s(UnboxMethod)], !IO)
|
|
else
|
|
TypeName = type_to_string_for_java(Info, Type),
|
|
io.format(Stream, "(%s) result[%d]",
|
|
[s(TypeName), i(ResultIndex)], !IO)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Extra code for outputting switch statements.
|
|
%
|
|
|
|
:- pred output_switch_cases_for_java(java_out_info::in,
|
|
io.text_output_stream::in, indent::in, func_info_csj::in, prog_context::in,
|
|
list(mlds_switch_case)::in, mlds_switch_default::in, exit_methods::out,
|
|
io::di, io::uo) is det.
|
|
|
|
output_switch_cases_for_java(Info, Stream, Indent, FuncInfo, Context,
|
|
[], Default, ExitMethods, !IO) :-
|
|
output_switch_default_for_java(Info, Stream, Indent, FuncInfo, Context,
|
|
Default, ExitMethods, !IO).
|
|
output_switch_cases_for_java(Info, Stream, Indent, FuncInfo, Context,
|
|
[Case | Cases], Default, ExitMethods, !IO) :-
|
|
output_switch_case_for_java(Info, Stream, Indent, FuncInfo, Context,
|
|
Case, CaseExitMethods0, !IO),
|
|
output_switch_cases_for_java(Info, Stream, Indent, FuncInfo, Context,
|
|
Cases, Default, CasesExitMethods, !IO),
|
|
( if set.member(can_break, CaseExitMethods0) then
|
|
set.delete(can_break, CaseExitMethods0, CaseExitMethods1),
|
|
set.insert(can_fall_through, CaseExitMethods1, CaseExitMethods)
|
|
else
|
|
CaseExitMethods = CaseExitMethods0
|
|
),
|
|
ExitMethods = set.union(CaseExitMethods, CasesExitMethods).
|
|
|
|
:- pred output_switch_case_for_java(java_out_info::in,
|
|
io.text_output_stream::in, indent::in, func_info_csj::in, prog_context::in,
|
|
mlds_switch_case::in, exit_methods::out, io::di, io::uo) is det.
|
|
|
|
output_switch_case_for_java(Info, Stream, Indent, FuncInfo, Context, Case,
|
|
ExitMethods, !IO) :-
|
|
Case = mlds_switch_case(FirstCond, LaterConds, Stmt),
|
|
output_case_cond_for_java(Info, Stream, Indent, Context, FirstCond, !IO),
|
|
list.foldl(output_case_cond_for_java(Info, Stream, Indent, Context),
|
|
LaterConds, !IO),
|
|
output_statement_for_java(Info, Stream, Indent + 1, FuncInfo, Stmt,
|
|
StmtExitMethods, !IO),
|
|
( if set.member(can_fall_through, StmtExitMethods) then
|
|
indent_line_after_context(Stream, Info ^ joi_line_numbers,
|
|
marker_comment, Context, Indent + 1, !IO),
|
|
io.write_string(Stream, "break;\n", !IO),
|
|
ExitMethods = set.delete(set.insert(StmtExitMethods, can_break),
|
|
can_fall_through)
|
|
else
|
|
% Don't output `break' since it would be unreachable.
|
|
ExitMethods = StmtExitMethods
|
|
).
|
|
|
|
:- pred output_case_cond_for_java(java_out_info::in, io.text_output_stream::in,
|
|
indent::in, prog_context::in, mlds_case_match_cond::in,
|
|
io::di, io::uo) is det.
|
|
|
|
output_case_cond_for_java(Info, Stream, Indent, Context, Match, !IO) :-
|
|
(
|
|
Match = match_value(Rval),
|
|
indent_line_after_context(Stream, Info ^ joi_line_numbers,
|
|
marker_comment, Context, Indent, !IO),
|
|
io.write_string(Stream, "case ", !IO),
|
|
( if Rval = ml_const(mlconst_enum(N, _)) then
|
|
io.write_int(Stream, N, !IO)
|
|
else
|
|
output_rval_for_java(Info, Rval, Stream, !IO)
|
|
),
|
|
io.write_string(Stream, ":\n", !IO)
|
|
;
|
|
Match = match_range(_, _),
|
|
unexpected($pred, "cannot match ranges in Java cases")
|
|
).
|
|
|
|
:- pred output_switch_default_for_java(java_out_info::in,
|
|
io.text_output_stream::in, indent::in, func_info_csj::in, prog_context::in,
|
|
mlds_switch_default::in, exit_methods::out, io::di, io::uo) is det.
|
|
|
|
output_switch_default_for_java(Info, Stream, Indent, FuncInfo, Context,
|
|
Default, ExitMethods, !IO) :-
|
|
(
|
|
Default = default_do_nothing,
|
|
ExitMethods = set.make_singleton_set(can_fall_through)
|
|
;
|
|
Default = default_case(Stmt),
|
|
indent_line_after_context(Stream, Info ^ joi_line_numbers,
|
|
marker_comment, Context, Indent, !IO),
|
|
io.write_string(Stream, "default:\n", !IO),
|
|
output_statement_for_java(Info, Stream, Indent + 1, FuncInfo, Stmt,
|
|
ExitMethods, !IO)
|
|
;
|
|
Default = default_is_unreachable,
|
|
indent_line_after_context(Stream, Info ^ joi_line_numbers,
|
|
marker_comment, Context, Indent, !IO),
|
|
io.write_string(Stream, "default: /*NOTREACHED*/\n", !IO),
|
|
indent_line_after_context(Stream, Info ^ joi_line_numbers,
|
|
marker_comment, Context, Indent + 1, !IO),
|
|
io.write_string(Stream,
|
|
"throw new jmercury.runtime.UnreachableDefault();\n", !IO),
|
|
ExitMethods = set.make_singleton_set(can_throw)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Code for outputting atomic statements.
|
|
%
|
|
|
|
:- pred output_atomic_stmt_for_java(java_out_info::in,
|
|
io.text_output_stream::in, indent::in, mlds_atomic_statement::in,
|
|
prog_context::in, io::di, io::uo) is det.
|
|
|
|
output_atomic_stmt_for_java(Info, Stream, Indent, AtomicStmt, Context, !IO) :-
|
|
IndentStr = indent2_string(Indent),
|
|
(
|
|
AtomicStmt = comment(Comment),
|
|
( if Comment = "" then
|
|
io.nl(Stream, !IO)
|
|
else
|
|
% XXX We should escape any "*/"'s in the Comment. We should also
|
|
% split the comment into lines and indent each line appropriately.
|
|
io.format(Stream, "%s/* %s */\n",
|
|
[s(IndentStr), s(Comment)], !IO)
|
|
)
|
|
;
|
|
AtomicStmt = assign(Lval, Rval),
|
|
write_indent2(Stream, Indent, !IO),
|
|
output_lval_for_java(Info, Lval, Stream, !IO),
|
|
io.write_string(Stream, " = ", !IO),
|
|
output_rval_for_java(Info, Rval, Stream, !IO),
|
|
io.write_string(Stream, ";\n", !IO)
|
|
;
|
|
AtomicStmt = assign_if_in_heap(_, _),
|
|
sorry($pred, "assign_if_in_heap")
|
|
;
|
|
AtomicStmt = delete_object(_Lval),
|
|
unexpected($pred, "delete_object not supported in Java.")
|
|
;
|
|
AtomicStmt = new_object(Target, _Ptag, ExplicitSecTag, Type,
|
|
_MaybeSize, MaybeCtorName, ArgRvalsTypes, _MayUseAtomic, _AllocId),
|
|
(
|
|
ExplicitSecTag = yes,
|
|
unexpected($pred, "explicit secondary tag")
|
|
;
|
|
ExplicitSecTag = no
|
|
),
|
|
|
|
io.format(Stream, "%s{\n", [s(IndentStr)], !IO),
|
|
indent_line_after_context(Stream, Info ^ joi_line_numbers,
|
|
marker_comment, Context, Indent + 1, !IO),
|
|
output_lval_for_java(Info, Target, Stream, !IO),
|
|
io.write_string(Stream, " = new ", !IO),
|
|
% Generate class constructor name.
|
|
( if
|
|
MaybeCtorName = yes(QualifiedCtorId),
|
|
not (
|
|
Type = mercury_nb_type(MerType, CtorCat),
|
|
hand_defined_type_for_java(MerType, CtorCat, _, _)
|
|
)
|
|
then
|
|
output_type_for_java(Info, Stream, Type, ArrayDims, !IO),
|
|
QualifiedCtorId = qual_ctor_id(_ModuleName, _QualKind, CtorDefn),
|
|
CtorDefn = ctor_id(CtorName, CtorArity),
|
|
UnqualClassNameStr =
|
|
unqual_class_name_to_string_for_java(CtorName, CtorArity),
|
|
io.format(Stream, ".%s", [s(UnqualClassNameStr)], !IO)
|
|
else
|
|
output_type_for_java(Info, Stream, Type, ArrayDims, !IO)
|
|
),
|
|
init_arg_wrappers_cs_java(ArrayDims, Start, End),
|
|
% Generate constructor arguments.
|
|
(
|
|
ArgRvalsTypes = [],
|
|
io.format(Stream, "%s%s;\n", [s(Start), s(End)], !IO)
|
|
;
|
|
ArgRvalsTypes = [HeadArgRvalType | TailArgRvalsTypes],
|
|
io.format(Stream, "%s\n", [s(Start)], !IO),
|
|
output_init_args_for_java(Info, Stream, Indent + 2,
|
|
HeadArgRvalType, TailArgRvalsTypes, !IO),
|
|
write_indent2(Stream, Indent + 1, !IO),
|
|
io.format(Stream, "%s;\n", [s(End)], !IO)
|
|
),
|
|
io.format(Stream, "%s}\n", [s(IndentStr)], !IO)
|
|
;
|
|
AtomicStmt = gc_check,
|
|
unexpected($pred, "gc_check not implemented.")
|
|
;
|
|
AtomicStmt = mark_hp(_Lval),
|
|
unexpected($pred, "mark_hp not implemented.")
|
|
;
|
|
AtomicStmt = restore_hp(_Rval),
|
|
unexpected($pred, "restore_hp not implemented.")
|
|
;
|
|
AtomicStmt = trail_op(_TrailOp),
|
|
unexpected($pred, "trail_ops not implemented.")
|
|
;
|
|
AtomicStmt = inline_target_code(TargetLang, Components),
|
|
(
|
|
TargetLang = ml_target_java,
|
|
write_indent2(Stream, Indent, !IO),
|
|
list.foldl(output_target_code_component_for_java(Info, Stream),
|
|
Components, !IO)
|
|
;
|
|
( TargetLang = ml_target_c
|
|
; TargetLang = ml_target_csharp
|
|
),
|
|
unexpected($pred, "inline_target_code only works for lang_java")
|
|
)
|
|
;
|
|
AtomicStmt = outline_foreign_proc(_TargetLang, _Vs, _Lvals, _Code),
|
|
unexpected($pred, "foreign language interfacing not implemented")
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
% Output initial values of an object's fields as arguments for the
|
|
% object's class constructor.
|
|
%
|
|
:- pred output_init_args_for_java(java_out_info::in, io.text_output_stream::in,
|
|
int::in, mlds_typed_rval::in, list(mlds_typed_rval)::in,
|
|
io::di, io::uo) is det.
|
|
|
|
output_init_args_for_java(Info, Stream, Indent, HeadArg, TailArgs, !IO) :-
|
|
HeadArg = ml_typed_rval(HeadArgRval, _HeadArgType),
|
|
write_indent2(Stream, Indent, !IO),
|
|
output_rval_for_java(Info, HeadArgRval, Stream, !IO),
|
|
(
|
|
TailArgs = [],
|
|
io.write_string(Stream, "\n", !IO)
|
|
;
|
|
TailArgs = [HeadTailArg | TailTailArgs],
|
|
io.write_string(Stream, ",\n", !IO),
|
|
output_init_args_for_java(Info, Stream, Indent,
|
|
HeadTailArg, TailTailArgs, !IO)
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
:- pred output_target_code_component_for_java(java_out_info::in,
|
|
io.text_output_stream::in, target_code_component::in,
|
|
io::di, io::uo) is det.
|
|
|
|
output_target_code_component_for_java(Info, Stream, TargetCode, !IO) :-
|
|
(
|
|
TargetCode = user_target_code(CodeString, MaybeUserContext),
|
|
(
|
|
MaybeUserContext = yes(ProgContext),
|
|
write_string_with_context_block(Info, Stream, 0, CodeString,
|
|
ProgContext, !IO)
|
|
;
|
|
MaybeUserContext = no,
|
|
io.write_string(Stream, CodeString, !IO)
|
|
)
|
|
;
|
|
TargetCode = raw_target_code(CodeString),
|
|
io.write_string(Stream, CodeString, !IO)
|
|
;
|
|
TargetCode = target_code_input(Rval),
|
|
output_rval_for_java(Info, Rval, Stream, !IO)
|
|
;
|
|
TargetCode = target_code_output(Lval),
|
|
output_lval_for_java(Info, Lval, Stream, !IO)
|
|
;
|
|
TargetCode = target_code_type(Type),
|
|
InfoGenerics = Info ^ joi_output_generics := do_output_generics,
|
|
output_type_for_java(InfoGenerics, Stream, Type, !IO)
|
|
;
|
|
TargetCode = target_code_function_name(FuncName),
|
|
FuncNameStr = maybe_qualified_function_name_to_string_for_java(Info,
|
|
FuncName),
|
|
io.write_string(Stream, FuncNameStr, !IO)
|
|
;
|
|
TargetCode = target_code_alloc_id(_),
|
|
unexpected($pred, "target_code_alloc_id not implemented")
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module ml_backend.mlds_to_java_stmt.
|
|
%---------------------------------------------------------------------------%
|