/* Write out a Java(TM) class file.
- Copyright (C) 1998, 1999 Free Software Foundation, Inc.
+ Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
This file is part of GNU CC.
#include "config.h"
#include "system.h"
+#include "jcf.h"
#include "tree.h"
#include "java-tree.h"
-#include "jcf.h"
#include "obstack.h"
#undef AND
#include "rtl.h"
+#include "flags.h"
#include "java-opcodes.h"
#include "parse.h" /* for BLOCK_EXPR_BODY */
#include "buffer.h"
#include "toplev.h"
-#ifndef DIR_SEPARATOR
-#define DIR_SEPARATOR '/'
-#endif
-
extern struct obstack temporary_obstack;
/* Base directory in which `.class' files should be written.
struct jcf_block *label;
};
+#define RELOCATION_VALUE_0 ((HOST_WIDE_INT)0)
+#define RELOCATION_VALUE_1 ((HOST_WIDE_INT)1)
+
/* State for single catch clause. */
struct jcf_handler
struct jcf_switch_state *sw_state;
};
-static void generate_bytecode_insns PROTO ((tree, int, struct jcf_partial *));
+static void generate_bytecode_insns PARAMS ((tree, int, struct jcf_partial *));
+static struct chunk * alloc_chunk PARAMS ((struct chunk *, unsigned char *,
+ int, struct obstack *));
+static unsigned char * append_chunk PARAMS ((unsigned char *, int,
+ struct jcf_partial *));
+static void append_chunk_copy PARAMS ((unsigned char *, int,
+ struct jcf_partial *));
+static struct jcf_block * gen_jcf_label PARAMS ((struct jcf_partial *));
+static void finish_jcf_block PARAMS ((struct jcf_partial *));
+static void define_jcf_label PARAMS ((struct jcf_block *,
+ struct jcf_partial *));
+static struct jcf_block * get_jcf_label_here PARAMS ((struct jcf_partial *));
+static void put_linenumber PARAMS ((int, struct jcf_partial *));
+static void localvar_alloc PARAMS ((tree, struct jcf_partial *));
+static void localvar_free PARAMS ((tree, struct jcf_partial *));
+static int get_access_flags PARAMS ((tree));
+static void write_chunks PARAMS ((FILE *, struct chunk *));
+static int adjust_typed_op PARAMS ((tree, int));
+static void generate_bytecode_conditional PARAMS ((tree, struct jcf_block *,
+ struct jcf_block *, int,
+ struct jcf_partial *));
+static void generate_bytecode_return PARAMS ((tree, struct jcf_partial *));
+static void perform_relocations PARAMS ((struct jcf_partial *));
+static void init_jcf_state PARAMS ((struct jcf_partial *, struct obstack *));
+static void init_jcf_method PARAMS ((struct jcf_partial *, tree));
+static void release_jcf_state PARAMS ((struct jcf_partial *));
+static struct chunk * generate_classfile PARAMS ((tree, struct jcf_partial *));
+static struct jcf_handler *alloc_handler PARAMS ((struct jcf_block *,
+ struct jcf_block *,
+ struct jcf_partial *));
+static void emit_iinc PARAMS ((tree, HOST_WIDE_INT, struct jcf_partial *));
+static void emit_reloc PARAMS ((HOST_WIDE_INT, int, struct jcf_block *,
+ struct jcf_partial *));
+static void push_constant1 PARAMS ((HOST_WIDE_INT, struct jcf_partial *));
+static void push_constant2 PARAMS ((HOST_WIDE_INT, struct jcf_partial *));
+static void push_int_const PARAMS ((HOST_WIDE_INT, struct jcf_partial *));
+static int find_constant_wide PARAMS ((HOST_WIDE_INT, HOST_WIDE_INT,
+ struct jcf_partial *));
+static void push_long_const PARAMS ((HOST_WIDE_INT, HOST_WIDE_INT,
+ struct jcf_partial *));
+static int find_constant_index PARAMS ((tree, struct jcf_partial *));
+static void push_long_const PARAMS ((HOST_WIDE_INT, HOST_WIDE_INT,
+ struct jcf_partial *));
+static void field_op PARAMS ((tree, int, struct jcf_partial *));
+static void maybe_wide PARAMS ((int, int, struct jcf_partial *));
+static void emit_dup PARAMS ((int, int, struct jcf_partial *));
+static void emit_pop PARAMS ((int, struct jcf_partial *));
+static void emit_load_or_store PARAMS ((tree, int, struct jcf_partial *));
+static void emit_load PARAMS ((tree, struct jcf_partial *));
+static void emit_store PARAMS ((tree, struct jcf_partial *));
+static void emit_unop PARAMS ((enum java_opcode, tree, struct jcf_partial *));
+static void emit_binop PARAMS ((enum java_opcode, tree, struct jcf_partial *));
+static void emit_reloc PARAMS ((HOST_WIDE_INT, int, struct jcf_block *,
+ struct jcf_partial *));
+static void emit_switch_reloc PARAMS ((struct jcf_block *,
+ struct jcf_partial *));
+static void emit_case_reloc PARAMS ((struct jcf_relocation *,
+ struct jcf_partial *));
+static void emit_if PARAMS ((struct jcf_block *, int, int,
+ struct jcf_partial *));
+static void emit_goto PARAMS ((struct jcf_block *, struct jcf_partial *));
+static void emit_jsr PARAMS ((struct jcf_block *, struct jcf_partial *));
+static void call_cleanups PARAMS ((struct jcf_block *, struct jcf_partial *));
+static char *make_class_file_name PARAMS ((tree));
/* Utility macros for appending (big-endian) data to a buffer.
We assume a local variable 'ptr' points into where we want to
#define PUT4(X) (PUT2((X) >> 16), PUT2((X) & 0xFFFF))
#define PUTN(P, N) (CHECK_PUT(ptr, state, N), memcpy(ptr, P, N), ptr += (N))
+/* There are some cases below where CHECK_PUT is guaranteed to fail.
+ Use the following macros in those specific cases. */
+#define UNSAFE_PUT1(X) (*ptr++ = (X))
+#define UNSAFE_PUT2(X) (UNSAFE_PUT1((X) >> 8), UNSAFE_PUT1((X) & 0xFF))
+#define UNSAFE_PUT4(X) (UNSAFE_PUT2((X) >> 16), UNSAFE_PUT2((X) & 0xFFFF))
+#define UNSAFE_PUTN(P, N) (memcpy(ptr, P, N), ptr += (N))
+
\f
/* Allocate a new chunk on obstack WORK, and link it in after LAST.
Set the data and size fields to DATA and SIZE, respectively.
However, if DATA is NULL and SIZE>0, allocate a buffer as well. */
-struct chunk *
+static struct chunk *
alloc_chunk (last, data, size, work)
struct chunk *last;
unsigned char *data;
#define CHECK_OP(STATE) ((void)0)
#endif
-unsigned char *
+static unsigned char *
append_chunk (data, size, state)
unsigned char *data;
int size;
return state->chunk->data;
}
-void
+static void
append_chunk_copy (data, size, state)
unsigned char *data;
int size;
memcpy (ptr, data, size);
}
\f
-struct jcf_block *
+static struct jcf_block *
gen_jcf_label (state)
struct jcf_partial *state;
{
return block;
}
-void
+static void
finish_jcf_block (state)
struct jcf_partial *state;
{
state->code_length = pc;
}
-void
+static void
define_jcf_label (label, state)
struct jcf_block *label;
struct jcf_partial *state;
label->u.relocations = NULL;
}
-struct jcf_block *
+static struct jcf_block *
get_jcf_label_here (state)
struct jcf_partial *state;
{
/* Note a line number entry for the current PC and given LINE. */
-void
+static void
put_linenumber (line, state)
int line;
struct jcf_partial *state;
#define localvar_max \
((struct localvar_info**) state->localvars.ptr - localvar_buffer)
-void
+static void
localvar_alloc (decl, state)
tree decl;
struct jcf_partial *state;
info->decl = decl;
info->start_label = start_label;
- if (DECL_NAME (decl) != NULL_TREE)
+ if (debug_info_level > DINFO_LEVEL_TERSE
+ && DECL_NAME (decl) != NULL_TREE)
{
/* Generate debugging info. */
info->next = NULL;
}
}
-int
+static void
localvar_free (decl, state)
tree decl;
struct jcf_partial *state;
/* Get the access flags of a class (TYPE_DECL), a method (FUNCTION_DECL), or
a field (FIELD_DECL or VAR_DECL, if static), as encoded in a .class file. */
-int
+static int
get_access_flags (decl)
tree decl;
{
/* Write the list of segments starting at CHUNKS to STREAM. */
-void
+static void
write_chunks (stream, chunks)
FILE* stream;
struct chunk *chunks;
static void
push_constant1 (index, state)
- int index;
+ HOST_WIDE_INT index;
struct jcf_partial *state;
{
RESERVE (3);
static void
push_constant2 (index, state)
- int index;
+ HOST_WIDE_INT index;
struct jcf_partial *state;
{
RESERVE (3);
}
else
{
- i = find_constant1 (&state->cpool, CONSTANT_Integer, i & 0xFFFFFFFF);
+ i = find_constant1 (&state->cpool, CONSTANT_Integer,
+ (jword)(i & 0xFFFFFFFF));
push_constant1 (i, state);
}
}
HOST_WIDE_INT w1, w2;
lshift_double (lo, hi, -32, 64, &w1, &w2, 1);
return find_constant2 (&state->cpool, CONSTANT_Long,
- w1 & 0xFFFFFFFF, lo & 0xFFFFFFFF);
+ (jword)(w1 & 0xFFFFFFFF), (jword)(lo & 0xFFFFFFFF));
}
/* Find or allocate a constant pool entry for the given VALUE.
{
if (TYPE_PRECISION (TREE_TYPE (value)) <= 32)
return find_constant1 (&state->cpool, CONSTANT_Integer,
- TREE_INT_CST_LOW (value) & 0xFFFFFFFF);
+ (jword)(TREE_INT_CST_LOW (value) & 0xFFFFFFFF));
else
return find_constant_wide (TREE_INT_CST_LOW (value),
TREE_INT_CST_HIGH (value), state);
if (TYPE_PRECISION (TREE_TYPE (value)) == 32)
{
words[0] = etarsingle (TREE_REAL_CST (value)) & 0xFFFFFFFF;
- return find_constant1 (&state->cpool, CONSTANT_Float, words[0]);
+ return find_constant1 (&state->cpool, CONSTANT_Float,
+ (jword)words[0]);
}
else
{
etardouble (TREE_REAL_CST (value), words);
return find_constant2 (&state->cpool, CONSTANT_Double,
- words[1-FLOAT_WORDS_BIG_ENDIAN] & 0xFFFFFFFF,
- words[FLOAT_WORDS_BIG_ENDIAN] & 0xFFFFFFFF);
+ (jword)(words[1-FLOAT_WORDS_BIG_ENDIAN] &
+ 0xFFFFFFFF),
+ (jword)(words[FLOAT_WORDS_BIG_ENDIAN] &
+ 0xFFFFFFFF));
}
}
else if (TREE_CODE (value) == STRING_CST)
reference) to 7 (for 'short') which matches the pattern of how JVM
opcodes typically depend on the operand type. */
-int
+static int
adjust_typed_op (type, max)
tree type;
int max;
static void
emit_iinc (var, value, state)
tree var;
- int value;
+ HOST_WIDE_INT value;
struct jcf_partial *state;
{
int slot = DECL_LOCAL_INDEX (var);
static void
emit_unop (opcode, type, state)
enum java_opcode opcode;
- tree type;
+ tree type ATTRIBUTE_UNUSED;
struct jcf_partial *state;
{
RESERVE(1);
struct jcf_block *label;
struct jcf_partial *state;
{
- emit_reloc (0, BLOCK_START_RELOC, label, state);
+ emit_reloc (RELOCATION_VALUE_0, BLOCK_START_RELOC, label, state);
}
/* Similar to emit_switch_reloc,
struct jcf_partial *state;
{
OP1 (opcode);
- // value is 1 byte from reloc back to start of instruction.
- emit_reloc (1, - inv_opcode, target, state);
+ /* value is 1 byte from reloc back to start of instruction. */
+ emit_reloc (RELOCATION_VALUE_1, - inv_opcode, target, state);
}
static void
struct jcf_partial *state;
{
OP1 (OPCODE_goto);
- // Value is 1 byte from reloc back to start of instruction.
- emit_reloc (1, OPCODE_goto_w, target, state);
+ /* Value is 1 byte from reloc back to start of instruction. */
+ emit_reloc (RELOCATION_VALUE_1, OPCODE_goto_w, target, state);
}
static void
struct jcf_partial *state;
{
OP1 (OPCODE_jsr);
- // Value is 1 byte from reloc back to start of instruction.
- emit_reloc (1, OPCODE_jsr_w, target, state);
+ /* Value is 1 byte from reloc back to start of instruction. */
+ emit_reloc (RELOCATION_VALUE_1, OPCODE_jsr_w, target, state);
}
/* Generate code to evaluate EXP. If the result is true,
TRUE_LABEL may follow right after this. (The idea is that we
may be able to optimize away GOTO TRUE_LABEL; TRUE_LABEL:) */
-void
+static void
generate_bytecode_conditional (exp, true_label, false_label,
true_branch_first, state)
tree exp;
}
break;
case TRUTH_NOT_EXPR:
- generate_bytecode_conditional (TREE_OPERAND (exp, 0), false_label, true_label,
+ generate_bytecode_conditional (TREE_OPERAND (exp, 0),
+ false_label, true_label,
! true_branch_first, state);
break;
case TRUTH_ANDIF_EXPR:
}
if (integer_zerop (exp1) || integer_zerop (exp0))
{
- generate_bytecode_insns (integer_zerop (exp1) ? exp0 : exp0,
+ generate_bytecode_insns (integer_zerop (exp1) ? exp0 : exp1,
STACK_TARGET, state);
op = op + (OPCODE_ifnull - OPCODE_if_acmpeq);
negop = (op & 1) ? op - 1 : op + 1;
}
}
+static void
+generate_bytecode_return (exp, state)
+ tree exp;
+ struct jcf_partial *state;
+{
+ tree return_type = TREE_TYPE (TREE_TYPE (state->current_method));
+ int returns_void = TREE_CODE (return_type) == VOID_TYPE;
+ int op;
+ again:
+ if (exp != NULL)
+ {
+ switch (TREE_CODE (exp))
+ {
+ case COMPOUND_EXPR:
+ generate_bytecode_insns (TREE_OPERAND (exp, 0), IGNORE_TARGET,
+ state);
+ exp = TREE_OPERAND (exp, 1);
+ goto again;
+ case COND_EXPR:
+ {
+ struct jcf_block *then_label = gen_jcf_label (state);
+ struct jcf_block *else_label = gen_jcf_label (state);
+ generate_bytecode_conditional (TREE_OPERAND (exp, 0),
+ then_label, else_label, 1, state);
+ define_jcf_label (then_label, state);
+ generate_bytecode_return (TREE_OPERAND (exp, 1), state);
+ define_jcf_label (else_label, state);
+ generate_bytecode_return (TREE_OPERAND (exp, 2), state);
+ }
+ return;
+ default:
+ generate_bytecode_insns (exp,
+ returns_void ? IGNORE_TARGET
+ : STACK_TARGET, state);
+ }
+ }
+ if (returns_void)
+ {
+ op = OPCODE_return;
+ call_cleanups (NULL_PTR, state);
+ }
+ else
+ {
+ op = OPCODE_ireturn + adjust_typed_op (return_type, 4);
+ if (state->num_finalizers > 0)
+ {
+ if (state->return_value_decl == NULL_TREE)
+ {
+ state->return_value_decl
+ = build_decl (VAR_DECL, NULL_TREE, TREE_TYPE (exp));
+ localvar_alloc (state->return_value_decl, state);
+ }
+ emit_store (state->return_value_decl, state);
+ call_cleanups (NULL_PTR, state);
+ emit_load (state->return_value_decl, state);
+ /* If we call localvar_free (state->return_value_decl, state),
+ then we risk the save decl erroneously re-used in the
+ finalizer. Instead, we keep the state->return_value_decl
+ allocated through the rest of the method. This is not
+ the greatest solution, but it is at least simple and safe. */
+ }
+ }
+ RESERVE (1);
+ OP1 (op);
+}
+
/* Generate bytecode for sub-expression EXP of METHOD.
TARGET is one of STACK_TARGET or IGNORE_TARGET. */
}
break;
case COMPOUND_EXPR:
- generate_bytecode_insns (TREE_OPERAND (exp, 0), IGNORE_TARGET, state);
- generate_bytecode_insns (TREE_OPERAND (exp, 1), target, state);
+ generate_bytecode_insns (TREE_OPERAND (exp, 0), IGNORE_TARGET, state);
+ generate_bytecode_insns (TREE_OPERAND (exp, 1), target, state);
break;
case EXPR_WITH_FILE_LOCATION:
{
break;
input_filename = EXPR_WFL_FILENAME (exp);
lineno = EXPR_WFL_LINENO (exp);
- if (EXPR_WFL_EMIT_LINE_NOTE (exp) && lineno > 0)
+ if (EXPR_WFL_EMIT_LINE_NOTE (exp) && lineno > 0
+ && debug_info_level > DINFO_LEVEL_NONE)
put_linenumber (lineno, state);
generate_bytecode_insns (body, target, state);
input_filename = saved_input_filename;
}
break;
case REAL_CST:
- offset = find_constant_index (exp, state);
- switch (TYPE_PRECISION (type))
- {
- case 32:
- push_constant1 (offset, state);
- NOTE_PUSH (1);
- break;
- case 64:
- push_constant2 (offset, state);
- NOTE_PUSH (2);
- break;
- default:
- abort ();
- }
+ {
+ int prec = TYPE_PRECISION (type) >> 5;
+ RESERVE(1);
+ if (real_zerop (exp))
+ OP1 (prec == 1 ? OPCODE_fconst_0 : OPCODE_dconst_0);
+ else if (real_onep (exp))
+ OP1 (prec == 1 ? OPCODE_fconst_1 : OPCODE_dconst_1);
+ /* FIXME Should also use fconst_2 for 2.0f.
+ Also, should use iconst_2/ldc followed by i2f/i2d
+ for other float/double when the value is a small integer. */
+ else
+ {
+ offset = find_constant_index (exp, state);
+ if (prec == 1)
+ push_constant1 (offset, state);
+ else
+ push_constant2 (offset, state);
+ }
+ NOTE_PUSH (prec);
+ }
break;
case STRING_CST:
push_constant1 (find_string_constant (&state->cpool, exp), state);
define_jcf_label (else_label, state);
generate_bytecode_insns (TREE_OPERAND (exp, 2), target, state);
define_jcf_label (end_label, state);
+
+ /* COND_EXPR can be used in a binop. The stack must be adjusted. */
+ if (TREE_TYPE (exp) != void_type_node)
+ NOTE_POP (TYPE_PRECISION (TREE_TYPE (exp)) > 32 ? 2 : 1);
}
break;
case CASE_EXPR:
int index = 0;
RESERVE (13 + 4 * (sw_state.max_case - sw_state.min_case + 1));
OP1 (OPCODE_tableswitch);
- emit_reloc (0, SWITCH_ALIGN_RELOC, NULL, state);
+ emit_reloc (RELOCATION_VALUE_0,
+ SWITCH_ALIGN_RELOC, NULL, state);
emit_switch_reloc (sw_state.default_label, state);
OP4 (sw_state.min_case);
OP4 (sw_state.max_case);
{ /* Use lookupswitch. */
RESERVE(9 + 8 * sw_state.num_cases);
OP1 (OPCODE_lookupswitch);
- emit_reloc (0, SWITCH_ALIGN_RELOC, NULL, state);
+ emit_reloc (RELOCATION_VALUE_0,
+ SWITCH_ALIGN_RELOC, NULL, state);
emit_switch_reloc (sw_state.default_label, state);
OP4 (sw_state.num_cases);
for (i = 0; i < sw_state.num_cases; i++)
}
case RETURN_EXPR:
- if (!TREE_OPERAND (exp, 0))
- {
- op = OPCODE_return;
- call_cleanups (NULL_TREE, state);
- }
+ exp = TREE_OPERAND (exp, 0);
+ if (exp == NULL_TREE)
+ exp = empty_stmt_node;
+ else if (TREE_CODE (exp) != MODIFY_EXPR)
+ abort ();
else
- {
- exp = TREE_OPERAND (exp, 0);
- if (TREE_CODE (exp) != MODIFY_EXPR)
- abort ();
- exp = TREE_OPERAND (exp, 1);
- op = OPCODE_ireturn + adjust_typed_op (TREE_TYPE (exp), 4);
- generate_bytecode_insns (exp, STACK_TARGET, state);
- if (state->num_finalizers > 0)
- {
- if (state->return_value_decl == NULL_TREE)
- {
- state->return_value_decl
- = build_decl (VAR_DECL, NULL_TREE, TREE_TYPE (exp));
- localvar_alloc (state->return_value_decl, state);
- }
- emit_store (state->return_value_decl, state);
- call_cleanups (NULL_TREE, state);
- emit_load (state->return_value_decl, state);
- /* If we call localvar_free (state->return_value_decl, state),
- then we risk the save decl erroneously re-used in the
- finalizer. Instead, we keep the state->return_value_decl
- allocated through the rest of the method. This is not
- the greatest solution, but it is at least simple and safe. */
- }
- }
- RESERVE (1);
- OP1 (op);
+ exp = TREE_OPERAND (exp, 1);
+ generate_bytecode_return (exp, state);
break;
case LABELED_BLOCK_EXPR:
{
if (size == 1)
push_int_const (value, state);
else
- push_long_const (value, value >= 0 ? 0 : -1, state);
+ push_long_const (value, (HOST_WIDE_INT)(value >= 0 ? 0 : -1), state);
NOTE_PUSH (size);
emit_binop (OPCODE_iadd + adjust_typed_op (type, 3), type, state);
if (target != IGNORE_TARGET && ! post_op)
case LSHIFT_EXPR: jopcode = OPCODE_ishl; goto binop;
case RSHIFT_EXPR: jopcode = OPCODE_ishr; goto binop;
case URSHIFT_EXPR: jopcode = OPCODE_iushr; goto binop;
+ case TRUTH_AND_EXPR:
case BIT_AND_EXPR: jopcode = OPCODE_iand; goto binop;
+ case TRUTH_OR_EXPR:
case BIT_IOR_EXPR: jopcode = OPCODE_ior; goto binop;
+ case TRUTH_XOR_EXPR:
case BIT_XOR_EXPR: jopcode = OPCODE_ixor; goto binop;
binop:
{
{
if (TREE_CODE (exp) == CONVERT_EXPR)
{
- int index = find_class_constant (&state->cpool, TREE_TYPE (dst_type));
+ int index = find_class_constant (&state->cpool,
+ TREE_TYPE (dst_type));
RESERVE (3);
OP1 (OPCODE_checkcast);
OP2 (index);
case TRY_EXPR:
{
tree try_clause = TREE_OPERAND (exp, 0);
- tree finally = TREE_OPERAND (exp, 2);
struct jcf_block *start_label = get_jcf_label_here (state);
struct jcf_block *end_label; /* End of try clause. */
- struct jcf_block *finally_label; /* Finally subroutine. */
struct jcf_block *finished_label = gen_jcf_label (state);
tree clause = TREE_OPERAND (exp, 1);
- if (finally)
- {
- finally = FINALLY_EXPR_BLOCK (finally);
- finally_label = gen_jcf_label (state);
- }
if (target != IGNORE_TARGET)
abort ();
generate_bytecode_insns (try_clause, IGNORE_TARGET, state);
end_label = get_jcf_label_here (state);
if (CAN_COMPLETE_NORMALLY (try_clause))
emit_goto (finished_label, state);
- for ( ; clause != NULL_TREE; clause = TREE_CHAIN (clause))
+ while (clause != NULL_TREE)
{
tree catch_clause = TREE_OPERAND (clause, 0);
tree exception_decl = BLOCK_EXPR_DECLS (catch_clause);
else
handler->type = TREE_TYPE (TREE_TYPE (exception_decl));
generate_bytecode_insns (catch_clause, IGNORE_TARGET, state);
- if (CAN_COMPLETE_NORMALLY (catch_clause))
+ clause = TREE_CHAIN (clause);
+ if (CAN_COMPLETE_NORMALLY (catch_clause) && clause != NULL_TREE)
emit_goto (finished_label, state);
}
- if (finally)
- {
- tree return_link;
- tree exception_type = build_pointer_type (throwable_type_node);
- tree exception_decl = build_decl (VAR_DECL, NULL_TREE,
- exception_type);
- struct jcf_handler *handler
- = alloc_handler (start_label, NULL_TREE, state);
- handler->end_label = handler->handler_label;
- handler->type = NULL_TREE;
- localvar_alloc (exception_decl, state);
- NOTE_PUSH (1);
- emit_store (exception_decl, state);
- emit_jsr (finally_label, state);
- emit_load (exception_decl, state);
- RESERVE (1);
- OP1 (OPCODE_athrow);
- NOTE_POP (1);
- localvar_free (exception_decl, state);
+ define_jcf_label (finished_label, state);
+ }
+ break;
+ case TRY_FINALLY_EXPR:
+ {
+ tree try_block = TREE_OPERAND (exp, 0);
+ tree finally = TREE_OPERAND (exp, 1);
+ struct jcf_block *finished_label = gen_jcf_label (state);
+ struct jcf_block *finally_label = gen_jcf_label (state);
+ struct jcf_block *start_label = get_jcf_label_here (state);
+ tree return_link = build_decl (VAR_DECL, NULL_TREE,
+ return_address_type_node);
+ tree exception_type = build_pointer_type (throwable_type_node);
+ tree exception_decl = build_decl (VAR_DECL, NULL_TREE, exception_type);
+ struct jcf_handler *handler;
+
+ finally_label->pc = PENDING_CLEANUP_PC;
+ finally_label->next = state->labeled_blocks;
+ state->labeled_blocks = finally_label;
+ state->num_finalizers++;
- /* The finally block. */
- return_link = build_decl (VAR_DECL, NULL_TREE,
- return_address_type_node);
- define_jcf_label (finally_label, state);
- NOTE_PUSH (1);
- localvar_alloc (return_link, state);
- emit_store (return_link, state);
- generate_bytecode_insns (finally, IGNORE_TARGET, state);
- maybe_wide (OPCODE_ret, DECL_LOCAL_INDEX (return_link), state);
- localvar_free (return_link, state);
- }
+ generate_bytecode_insns (try_block, target, state);
+ if (state->labeled_blocks != finally_label)
+ abort();
+ state->labeled_blocks = finally_label->next;
+ emit_jsr (finally_label, state);
+ if (CAN_COMPLETE_NORMALLY (try_block))
+ emit_goto (finished_label, state);
+
+ /* Handle exceptions. */
+ localvar_alloc (return_link, state);
+ handler = alloc_handler (start_label, NULL_PTR, state);
+ handler->end_label = handler->handler_label;
+ handler->type = NULL_TREE;
+ localvar_alloc (exception_decl, state);
+ NOTE_PUSH (1);
+ emit_store (exception_decl, state);
+ emit_jsr (finally_label, state);
+ emit_load (exception_decl, state);
+ RESERVE (1);
+ OP1 (OPCODE_athrow);
+ NOTE_POP (1);
+ localvar_free (exception_decl, state);
+
+ /* The finally block. First save return PC into return_link. */
+ define_jcf_label (finally_label, state);
+ NOTE_PUSH (1);
+ emit_store (return_link, state);
+
+ generate_bytecode_insns (finally, IGNORE_TARGET, state);
+ maybe_wide (OPCODE_ret, DECL_LOCAL_INDEX (return_link), state);
+ localvar_free (return_link, state);
define_jcf_label (finished_label, state);
- if (finally)
- emit_jsr (finally_label, state);
}
break;
case THROW_EXPR:
}
else if (f == soft_monitorenter_node
|| f == soft_monitorexit_node
- || f == throw_node)
+ || f == throw_node[0]
+ || f == throw_node[1])
{
if (f == soft_monitorenter_node)
op = OPCODE_monitorenter;
}
nargs = state->code_SP - save_SP;
state->code_SP = save_SP;
+ if (f == soft_fmod_node)
+ {
+ RESERVE (1);
+ OP1 (OPCODE_drem);
+ NOTE_PUSH (2);
+ break;
+ }
if (TREE_CODE (exp) == NEW_CLASS_EXPR)
NOTE_POP (1); /* Pop implicit this. */
if (TREE_CODE (f) == FUNCTION_DECL && DECL_CONTEXT (f) != NULL_TREE)
else
OP1 (OPCODE_invokevirtual);
OP2 (index);
+ if (interface)
+ {
+ OP1 (nargs);
+ OP1 (0);
+ }
f = TREE_TYPE (TREE_TYPE (f));
if (TREE_CODE (f) != VOID_TYPE)
{
else
NOTE_PUSH (size);
}
- if (interface)
- {
- OP1 (nargs);
- OP1 (0);
- }
break;
}
}
}
}
-void
+static void
perform_relocations (state)
struct jcf_partial *state;
{
state->code_length = pc;
}
-void
+static void
init_jcf_state (state, work)
struct jcf_partial *state;
struct obstack *work;
BUFFER_INIT (&state->bytecode);
}
-void
+static void
init_jcf_method (state, method)
struct jcf_partial *state;
tree method;
state->return_value_decl = NULL_TREE;
}
-void
+static void
release_jcf_state (state)
struct jcf_partial *state;
{
in the .class file representation. The list can be written to a
.class file using write_chunks. Allocate chunks from obstack WORK. */
-struct chunk *
+static struct chunk *
generate_classfile (clas, state)
tree clas;
struct jcf_partial *state;
else
i = 8 + 2 * total_supers;
ptr = append_chunk (NULL, i, state);
- i = get_access_flags (TYPE_NAME (clas)) | ACC_SUPER;
+ i = get_access_flags (TYPE_NAME (clas));
+ if (! (i & ACC_INTERFACE))
+ i |= ACC_SUPER;
PUT2 (i); /* acces_flags */
i = find_class_constant (&state->cpool, clas); PUT2 (i); /* this_class */
if (clas == object_type_node)
}
fields_count++;
}
- ptr = fields_count_ptr; PUT2 (fields_count);
+ ptr = fields_count_ptr; UNSAFE_PUT2 (fields_count);
ptr = methods_count_ptr = append_chunk (NULL, 2, state);
PUT2 (0);
i = find_utf8_constant (&state->cpool, name); PUT2 (i);
i = find_utf8_constant (&state->cpool, build_java_signature (type));
PUT2 (i);
- PUT2 (body != NULL_TREE ? 1 : 0); /* attributes_count */
+ i = (body != NULL_TREE) + (DECL_FUNCTION_THROWS (part) != NULL_TREE);
+ PUT2 (i); /* attributes_count */
if (body != NULL_TREE)
{
int code_attributes_count = 0;
code_attributes_count++;
i += 8 + 10 * state->lvar_count;
}
- PUT4 (i); /* attribute_length */
- PUT2 (state->code_SP_max); /* max_stack */
- PUT2 (localvar_max); /* max_locals */
- PUT4 (state->code_length);
+ UNSAFE_PUT4 (i); /* attribute_length */
+ UNSAFE_PUT2 (state->code_SP_max); /* max_stack */
+ UNSAFE_PUT2 (localvar_max); /* max_locals */
+ UNSAFE_PUT4 (state->code_length);
/* Emit the exception table. */
ptr = append_chunk (NULL, 2 + 8 * state->num_handlers, state);
}
}
}
+ if (DECL_FUNCTION_THROWS (part) != NULL_TREE)
+ {
+ tree t = DECL_FUNCTION_THROWS (part);
+ int throws_count = list_length (t);
+ static tree Exceptions_node = NULL_TREE;
+ if (Exceptions_node == NULL_TREE)
+ Exceptions_node = get_identifier ("Exceptions");
+ ptr = append_chunk (NULL, 8 + 2 * throws_count, state);
+ i = find_utf8_constant (&state->cpool, Exceptions_node);
+ PUT2 (i); /* attribute_name_index */
+ i = 2 + 2 * throws_count; PUT4(i); /* attribute_length */
+ i = throws_count; PUT2 (i);
+ for (; t != NULL_TREE; t = TREE_CHAIN (t))
+ {
+ i = find_class_constant (&state->cpool, TREE_VALUE (t));
+ PUT2 (i);
+ }
+ }
methods_count++;
current_function_decl = save_function;
}
- ptr = methods_count_ptr; PUT2 (methods_count);
+ ptr = methods_count_ptr; UNSAFE_PUT2 (methods_count);
source_file = DECL_SOURCE_FILE (TYPE_NAME (clas));
for (ptr = source_file; ; ptr++)
make_class_file_name (clas)
tree clas;
{
- char *cname, *dname, *slash, *r;
+ const char *dname, *slash;
+ char *cname, *r;
struct stat sb;
cname = IDENTIFIER_POINTER (identifier_subst (DECL_NAME (TYPE_NAME (clas)),