/* Write out a Java(TM) class file.
- Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
+ Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
This file is part of GNU CC.
#include "system.h"
#include "jcf.h"
#include "tree.h"
+#include "real.h"
#include "java-tree.h"
#include "obstack.h"
#undef AND
#include "parse.h" /* for BLOCK_EXPR_BODY */
#include "buffer.h"
#include "toplev.h"
+#include "ggc.h"
#ifndef DIR_SEPARATOR
#define DIR_SEPARATOR '/'
to the beginning of the block.
If (pc < 0), the jcf_block is not an actual block (i.e. it has no
- assocated code yet), but it is an undefined label.
+ associated code yet), but it is an undefined label.
*/
struct jcf_block
{
/* For blocks that that are defined, the next block (in pc order).
- For blocks that are the not-yet-defined end label of a LABELED_BLOCK_EXPR
- or a cleanup expression (from a WITH_CLEANUP_EXPR),
+ For blocks that are not-yet-defined the end label of a LABELED_BLOCK_EXPR
+ or a cleanup expression (from a TRY_FINALLY_EXPR),
this is the next (outer) such end label, in a stack headed by
labeled_blocks in jcf_partial. */
struct jcf_block *next;
int linenumber;
- /* After finish_jcf_block is called, The actual instructions contained in this block.
- Before than NULL, and the instructions are in state->bytecode. */
+ /* After finish_jcf_block is called, the actual instructions
+ contained in this block. Before that NULL, and the instructions
+ are in state->bytecode. */
union {
struct chunk *chunk;
/* If pc==PENDING_CLEANUP_PC, start_label is the start of the region
- coveed by the cleanup. */
+ covered by the cleanup. */
struct jcf_block *start_label;
} v;
/* If non-NULL, use this for the return value. */
tree return_value_decl;
- /* Information about the current switch statemenet. */
+ /* Information about the current switch statement. */
struct jcf_switch_state *sw_state;
};
static unsigned char *append_synthetic_attribute PARAMS ((struct jcf_partial *));
static void append_innerclasses_attribute PARAMS ((struct jcf_partial *, tree));
static void append_innerclasses_attribute_entry PARAMS ((struct jcf_partial *, tree, tree));
+static void append_gcj_attribute PARAMS ((struct jcf_partial *, tree));
/* Utility macros for appending (big-endian) data to a buffer.
We assume a local variable 'ptr' points into where we want to
- write next, and we assume enoygh space has been allocated. */
+ write next, and we assume enough space has been allocated. */
-#ifdef ENABLE_CHECKING
-int
-CHECK_PUT(ptr, state, i)
+#ifdef ENABLE_JC1_CHECKING
+static int CHECK_PUT PARAMS ((void *, struct jcf_partial *, int));
+
+static int
+CHECK_PUT (ptr, state, i)
void *ptr;
struct jcf_partial *state;
int i;
{
- if (ptr < state->chunk->data
- || (char*)ptr + i > state->chunk->data + state->chunk->size)
- fatal ("internal error - CHECK_PUT failed");
+ if ((unsigned char *) ptr < state->chunk->data
+ || (unsigned char *) ptr + i > state->chunk->data + state->chunk->size)
+ abort ();
+
return 0;
}
#else
return chunk;
}
-#ifdef ENABLE_CHECKING
-int
-CHECK_OP(struct jcf_partial *state)
+#ifdef ENABLE_JC1_CHECKING
+static int CHECK_OP PARAMS ((struct jcf_partial *));
+
+static int
+CHECK_OP (state)
+ struct jcf_partial *state;
{
if (state->bytecode.ptr > state->bytecode.limit)
- {
- fatal("internal error - CHECK_OP failed");
- }
+ abort ();
+
return 0;
}
#else
-#define CHECK_OP(STATE) ((void)0)
+#define CHECK_OP(STATE) ((void) 0)
#endif
static unsigned char *
if (ANONYMOUS_CLASS_P (TREE_TYPE (decl))
|| LOCAL_CLASS_P (TREE_TYPE (decl)))
flags |= ACC_PRIVATE;
+ if (CLASS_STRICTFP (decl))
+ flags |= ACC_STRICT;
}
else
- fatal ("internal error - bad argument to get_access_flags");
+ abort ();
+
if (TREE_CODE (decl) == FUNCTION_DECL)
{
if (METHOD_NATIVE (decl))
flags |= ACC_SYNCHRONIZED;
if (METHOD_ABSTRACT (decl))
flags |= ACC_ABSTRACT;
+ if (METHOD_STRICTFP (decl))
+ flags |= ACC_STRICT;
}
if (isfield)
{
}
}
else if (TREE_CODE (value) == STRING_CST)
- {
- return find_string_constant (&state->cpool, value);
- }
+ return find_string_constant (&state->cpool, value);
+
else
- fatal ("find_constant_index - bad type");
+ abort ();
}
/* Push 64-bit long constant on VM stack.
HOST_WIDE_INT lo, hi;
struct jcf_partial *state;
{
- if (hi == 0 && lo >= 0 && lo <= 1)
+ HOST_WIDE_INT highpart, dummy;
+ jint lowpart = WORD_TO_INT (lo);
+
+ rshift_double (lo, hi, 32, 64, &highpart, &dummy, 1);
+
+ if (highpart == 0 && (lowpart == 0 || lowpart == 1))
{
RESERVE(1);
- OP1(OPCODE_lconst_0 + lo);
+ OP1(OPCODE_lconst_0 + lowpart);
}
- else if ((hi == 0 && (jword)(lo & 0xFFFFFFFF) < 32768)
- || (hi == -1 && (jword)(lo & 0xFFFFFFFF) >= -32768))
+ else if ((highpart == 0 && lowpart > 0 && lowpart < 32768)
+ || (highpart == -1 && lowpart < 0 && lowpart >= -32768))
{
- push_int_const (lo, state);
+ push_int_const (lowpart, state);
RESERVE (1);
OP1 (OPCODE_i2l);
}
true_label, false_label,
true_branch_first, state);
if (state->code_SP != save_SP_after)
- fatal ("internal error non-matching SP");
+ abort ();
}
break;
case TRUTH_NOT_EXPR:
- generate_bytecode_conditional (TREE_OPERAND (exp, 0), false_label, true_label,
- ! true_branch_first, state);
+ 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 (exp0) ? exp1 : exp0,
STACK_TARGET, state);
op = op + (OPCODE_ifnull - OPCODE_if_acmpeq);
negop = (op & 1) ? op - 1 : op + 1;
break;
}
if (save_SP != state->code_SP)
- fatal ("internal error - SP mismatch");
+ abort ();
}
-/* Call pending cleanups i.e. those for surrounding CLEANUP_POINT_EXPRs
+/* Call pending cleanups i.e. those for surrounding TRY_FINALLY_EXPRs.
but only as far out as LIMIT (since we are about to jump to the
emit label that is LIMIT). */
if (returns_void)
{
op = OPCODE_return;
- call_cleanups (NULL_PTR, state);
+ call_cleanups (NULL, state);
}
else
{
localvar_alloc (state->return_value_decl, state);
}
emit_store (state->return_value_decl, state);
- call_cleanups (NULL_PTR, state);
+ call_cleanups (NULL, 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
int target;
struct jcf_partial *state;
{
- tree type;
+ tree type, arg;
enum java_opcode jopcode;
int op;
HOST_WIDE_INT value;
}
}
break;
- case COMPOUND_EXPR:
+ case COMPOUND_EXPR:
generate_bytecode_insns (TREE_OPERAND (exp, 0), IGNORE_TARGET, state);
- generate_bytecode_insns (TREE_OPERAND (exp, 1), target, state);
+ /* Normally the first operand to a COMPOUND_EXPR must complete
+ normally. However, in the special case of a do-while
+ statement this is not necessarily the case. */
+ if (CAN_COMPLETE_NORMALLY (TREE_OPERAND (exp, 0)))
+ generate_bytecode_insns (TREE_OPERAND (exp, 1), target, state);
break;
case EXPR_WITH_FILE_LOCATION:
{
{
int prec = TYPE_PRECISION (type) >> 5;
RESERVE(1);
- if (real_zerop (exp))
+ if (real_zerop (exp) && ! REAL_VALUE_MINUS_ZERO (TREE_REAL_CST (exp)))
OP1 (prec == 1 ? OPCODE_fconst_0 : OPCODE_dconst_0);
else if (real_onep (exp))
OP1 (prec == 1 ? OPCODE_fconst_1 : OPCODE_dconst_1);
1. the switch_expression (the value used to select the correct case);
2. the switch_body;
3. the switch_instruction (the tableswitch/loopupswitch instruction.).
- After code generation, we will re-order then in the order 1, 3, 2.
- This is to avoid an extra GOTOs. */
+ After code generation, we will re-order them in the order 1, 3, 2.
+ This is to avoid any extra GOTOs. */
struct jcf_switch_state sw_state;
struct jcf_block *expression_last; /* Last block of the switch_expression. */
struct jcf_block *body_last; /* Last block of the switch_body. */
sw_state.default_label = NULL;
generate_bytecode_insns (TREE_OPERAND (exp, 0), STACK_TARGET, state);
expression_last = state->last_block;
- body_block = get_jcf_label_here (state); /* Force a new block here. */
+ /* Force a new block here. */
+ body_block = gen_jcf_label (state);
+ define_jcf_label (body_block, state);
generate_bytecode_insns (TREE_OPERAND (exp, 1), IGNORE_TARGET, state);
body_last = state->last_block;
else
{
push_int_const (sw_state.cases->offset, state);
+ NOTE_PUSH (1);
emit_if (sw_state.cases->label,
- OPCODE_ifeq, OPCODE_ifne, state);
+ OPCODE_if_icmpeq, OPCODE_if_icmpne, state);
}
emit_goto (sw_state.default_label, state);
}
gap_start--;
}
relocs[gap_start++] = reloc;
- /* Note we don't check for duplicates. FIXME! */
+ /* Note we don't check for duplicates. This is
+ handled by the parser. */
}
if (2 * sw_state.num_cases
{
struct jcf_block *head_label = get_jcf_label_here (state);
generate_bytecode_insns (body, IGNORE_TARGET, state);
- emit_goto (head_label, state);
+ if (CAN_COMPLETE_NORMALLY (body))
+ emit_goto (head_label, state);
}
}
break;
case POSTINCREMENT_EXPR: value = 1; post_op = 1; goto increment;
increment:
+ arg = TREE_OPERAND (exp, 1);
exp = TREE_OPERAND (exp, 0);
type = TREE_TYPE (exp);
size = TYPE_IS_WIDE (type) ? 2 : 1;
/* Stack, if ARRAY_REF: ..., [result, ] array, index, oldvalue. */
/* Stack, if COMPONENT_REF: ..., [result, ] objectref, oldvalue. */
/* Stack, otherwise: ..., [result, ] oldvalue. */
- if (size == 1)
- push_int_const (value, state);
- else
- 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);
+ generate_bytecode_insns (arg, STACK_TARGET, state);
+ emit_binop ((value >= 0 ? OPCODE_iadd : OPCODE_isub)
+ + adjust_typed_op (type, 3),
+ type, state);
if (target != IGNORE_TARGET && ! post_op)
emit_dup (size, offset, state);
/* Stack, if ARRAY_REF: ..., [result, ] array, index, newvalue. */
if (TREE_CODE (rhs) == MINUS_EXPR)
value = -value;
emit_iinc (lhs, value, state);
+ if (target != IGNORE_TARGET)
+ emit_load (lhs, state);
break;
}
}
}
else
offset = 0;
+
+ /* If the rhs is a binary expression and the left operand is
+ `==' to the lhs then we have an OP= expression. In this
+ case we must do some special processing. */
+ if (TREE_CODE_CLASS (TREE_CODE (rhs)) == '2'
+ && lhs == TREE_OPERAND (rhs, 0))
+ {
+ if (TREE_CODE (lhs) == COMPONENT_REF)
+ {
+ tree field = TREE_OPERAND (lhs, 1);
+ if (! FIELD_STATIC (field))
+ {
+ /* Duplicate the object reference so we can get
+ the field. */
+ emit_dup (TYPE_IS_WIDE (field) ? 2 : 1, 0, state);
+ NOTE_POP (1);
+ }
+ field_op (field, (FIELD_STATIC (field)
+ ? OPCODE_getstatic
+ : OPCODE_getfield),
+ state);
+
+ NOTE_PUSH (TYPE_IS_WIDE (TREE_TYPE (field)) ? 2 : 1);
+ }
+ else if (TREE_CODE (lhs) == VAR_DECL
+ || TREE_CODE (lhs) == PARM_DECL)
+ {
+ if (FIELD_STATIC (lhs))
+ {
+ field_op (lhs, OPCODE_getstatic, state);
+ NOTE_PUSH (TYPE_IS_WIDE (TREE_TYPE (lhs)) ? 2 : 1);
+ }
+ else
+ emit_load (lhs, state);
+ }
+ else if (TREE_CODE (lhs) == ARRAY_REF)
+ {
+ /* Duplicate the array and index, which are on the
+ stack, so that we can load the old value. */
+ emit_dup (2, 0, state);
+ NOTE_POP (2);
+ jopcode = OPCODE_iaload + adjust_typed_op (TREE_TYPE (lhs), 7);
+ RESERVE (1);
+ OP1 (jopcode);
+ NOTE_PUSH (TYPE_IS_WIDE (TREE_TYPE (lhs)) ? 2 : 1);
+ }
+ else
+ abort ();
+
+ /* This function correctly handles the case where the LHS
+ of a binary expression is NULL_TREE. */
+ rhs = build (TREE_CODE (rhs), TREE_TYPE (rhs),
+ NULL_TREE, TREE_OPERAND (rhs, 1));
+ }
+
generate_bytecode_insns (rhs, STACK_TARGET, state);
if (target != IGNORE_TARGET)
emit_dup (TYPE_IS_WIDE (type) ? 2 : 1 , offset, state);
else if (TREE_CODE (exp) == ARRAY_REF)
{
jopcode = OPCODE_iastore + adjust_typed_op (TREE_TYPE (exp), 7);
- RESERVE(1);
+ RESERVE (1);
OP1 (jopcode);
NOTE_POP (TYPE_IS_WIDE (TREE_TYPE (exp)) ? 4 : 3);
}
else
- fatal ("internal error (bad lhs to MODIFY_EXPR)");
+ abort ();
break;
case PLUS_EXPR:
jopcode = OPCODE_iadd;
}
else
{
- generate_bytecode_insns (arg0, target, state);
+ /* ARG0 will be NULL_TREE if we're handling an `OP='
+ expression. In this case the stack already holds the
+ LHS. See the MODIFY_EXPR case. */
+ if (arg0 != NULL_TREE)
+ generate_bytecode_insns (arg0, target, state);
if (jopcode >= OPCODE_lshl && jopcode <= OPCODE_lushr)
arg1 = convert (int_type_node, arg1);
generate_bytecode_insns (arg1, target, state);
}
break;
- case CLEANUP_POINT_EXPR:
- {
- struct jcf_block *save_labeled_blocks = state->labeled_blocks;
- int can_complete = CAN_COMPLETE_NORMALLY (TREE_OPERAND (exp, 0));
- generate_bytecode_insns (TREE_OPERAND (exp, 0), IGNORE_TARGET, state);
- if (target != IGNORE_TARGET)
- abort ();
- while (state->labeled_blocks != save_labeled_blocks)
- {
- struct jcf_block *finished_label = NULL;
- 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_block *end_label = get_jcf_label_here (state);
- struct jcf_block *label = state->labeled_blocks;
- struct jcf_handler *handler;
- tree cleanup = label->u.labeled_block;
- state->labeled_blocks = label->next;
- state->num_finalizers--;
- if (can_complete)
- {
- finished_label = gen_jcf_label (state);
- emit_jsr (label, state);
- emit_goto (finished_label, state);
- if (! CAN_COMPLETE_NORMALLY (cleanup))
- can_complete = 0;
- }
- handler = alloc_handler (label->v.start_label, end_label, state);
- handler->type = NULL_TREE;
- localvar_alloc (exception_decl, state);
- NOTE_PUSH (1);
- emit_store (exception_decl, state);
- emit_jsr (label, state);
- emit_load (exception_decl, state);
- RESERVE (1);
- OP1 (OPCODE_athrow);
- NOTE_POP (1);
-
- /* The finally block. */
- return_link = build_decl (VAR_DECL, NULL_TREE,
- return_address_type_node);
- define_jcf_label (label, state);
- NOTE_PUSH (1);
- localvar_alloc (return_link, state);
- emit_store (return_link, state);
- generate_bytecode_insns (cleanup, IGNORE_TARGET, state);
- maybe_wide (OPCODE_ret, DECL_LOCAL_INDEX (return_link), state);
- localvar_free (return_link, state);
- localvar_free (exception_decl, state);
- if (finished_label != NULL)
- define_jcf_label (finished_label, state);
- }
- }
- break;
-
- case WITH_CLEANUP_EXPR:
- {
- struct jcf_block *label;
- generate_bytecode_insns (TREE_OPERAND (exp, 0), IGNORE_TARGET, state);
- label = gen_jcf_label (state);
- label->pc = PENDING_CLEANUP_PC;
- label->next = state->labeled_blocks;
- state->labeled_blocks = label;
- state->num_finalizers++;
- label->u.labeled_block = TREE_OPERAND (exp, 2);
- label->v.start_label = get_jcf_label_here (state);
- if (target != IGNORE_TARGET)
- abort ();
- }
- break;
-
case TRY_EXPR:
{
tree try_clause = TREE_OPERAND (exp, 0);
abort ();
generate_bytecode_insns (try_clause, IGNORE_TARGET, state);
end_label = get_jcf_label_here (state);
+ if (end_label == start_label)
+ break;
if (CAN_COMPLETE_NORMALLY (try_clause))
emit_goto (finished_label, state);
while (clause != NULL_TREE)
{
tree catch_clause = TREE_OPERAND (clause, 0);
tree exception_decl = BLOCK_EXPR_DECLS (catch_clause);
- struct jcf_handler *handler = alloc_handler (start_label, end_label, state);
+ struct jcf_handler *handler = alloc_handler (start_label,
+ end_label, state);
if (exception_decl == NULL_TREE)
handler->type = NULL_TREE;
else
define_jcf_label (finished_label, state);
}
break;
+
case TRY_FINALLY_EXPR:
{
- struct jcf_block *finished_label, *finally_label, *start_label;
+ struct jcf_block *finished_label = NULL;
+ struct jcf_block *finally_label, *start_label, *end_label;
struct jcf_handler *handler;
- int worthwhile_finally = 1;
tree try_block = TREE_OPERAND (exp, 0);
tree finally = TREE_OPERAND (exp, 1);
- tree return_link, exception_decl;
-
- finally_label = start_label = NULL;
- return_link = exception_decl = NULL_TREE;
- finished_label = gen_jcf_label (state);
+ tree return_link = NULL_TREE, exception_decl = NULL_TREE;
- /* If the finally clause happens to be empty, set a flag so we
- remember to just skip it. */
- if (BLOCK_EXPR_BODY (finally) == empty_stmt_node)
- worthwhile_finally = 0;
+ tree exception_type;
- if (worthwhile_finally)
+ finally_label = gen_jcf_label (state);
+ start_label = get_jcf_label_here (state);
+ /* If the `finally' clause can complete normally, we emit it
+ as a subroutine and let the other clauses call it via
+ `jsr'. If it can't complete normally, then we simply emit
+ `goto's directly to it. */
+ if (CAN_COMPLETE_NORMALLY (finally))
{
- tree exception_type;
- return_link = build_decl (VAR_DECL, NULL_TREE,
- return_address_type_node);
- exception_type = build_pointer_type (throwable_type_node);
- exception_decl = build_decl (VAR_DECL, NULL_TREE, exception_type);
-
- finally_label = gen_jcf_label (state);
- start_label = get_jcf_label_here (state);
finally_label->pc = PENDING_CLEANUP_PC;
finally_label->next = state->labeled_blocks;
state->labeled_blocks = finally_label;
generate_bytecode_insns (try_block, target, state);
- if (worthwhile_finally)
+ if (CAN_COMPLETE_NORMALLY (finally))
{
if (state->labeled_blocks != finally_label)
abort();
state->labeled_blocks = finally_label->next;
- emit_jsr (finally_label, state);
}
+ end_label = get_jcf_label_here (state);
- if (CAN_COMPLETE_NORMALLY (try_block)
- && BLOCK_EXPR_BODY (try_block) != empty_stmt_node)
- emit_goto (finished_label, state);
+ if (end_label == start_label)
+ {
+ state->num_finalizers--;
+ define_jcf_label (finally_label, state);
+ generate_bytecode_insns (finally, IGNORE_TARGET, state);
+ break;
+ }
- /* Handle exceptions. */
+ if (CAN_COMPLETE_NORMALLY (finally))
+ {
+ return_link = build_decl (VAR_DECL, NULL_TREE,
+ return_address_type_node);
+ finished_label = gen_jcf_label (state);
+ }
- if (!worthwhile_finally)
- break;
+ if (CAN_COMPLETE_NORMALLY (try_block))
+ {
+ if (CAN_COMPLETE_NORMALLY (finally))
+ {
+ emit_jsr (finally_label, state);
+ emit_goto (finished_label, state);
+ }
+ else
+ emit_goto (finally_label, state);
+ }
- localvar_alloc (return_link, state);
- handler = alloc_handler (start_label, NULL_PTR, state);
- handler->end_label = handler->handler_label;
+ /* Handle exceptions. */
+
+ exception_type = build_pointer_type (throwable_type_node);
+ if (CAN_COMPLETE_NORMALLY (finally))
+ {
+ /* We're going to generate a subroutine, so we'll need to
+ save and restore the exception around the `jsr'. */
+ exception_decl = build_decl (VAR_DECL, NULL_TREE, exception_type);
+ localvar_alloc (return_link, state);
+ }
+ handler = alloc_handler (start_label, end_label, state);
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. */
+ if (CAN_COMPLETE_NORMALLY (finally))
+ {
+ 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);
+ }
+ else
+ {
+ /* We're not generating a subroutine. In this case we can
+ simply have the exception handler pop the exception and
+ then fall through to the `finally' block. */
+ NOTE_PUSH (1);
+ emit_pop (1, state);
+ NOTE_POP (1);
+ }
+
+ /* The finally block. If we're generating a subroutine, first
+ save return PC into return_link. Otherwise, just generate
+ the code for the `finally' block. */
define_jcf_label (finally_label, state);
- NOTE_PUSH (1);
- emit_store (return_link, state);
+ if (CAN_COMPLETE_NORMALLY (finally))
+ {
+ 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 (CAN_COMPLETE_NORMALLY (finally))
+ {
+ maybe_wide (OPCODE_ret, DECL_LOCAL_INDEX (return_link), state);
+ localvar_free (exception_decl, state);
+ localvar_free (return_link, state);
+ define_jcf_label (finished_label, state);
+ }
}
break;
case THROW_EXPR:
}
}
break;
+ case JAVA_EXC_OBJ_EXPR:
+ NOTE_PUSH (1); /* Pushed by exception system. */
+ break;
case NEW_CLASS_EXPR:
{
tree class = TREE_TYPE (TREE_TYPE (exp));
}
else if (f == soft_monitorenter_node
|| f == soft_monitorexit_node
- || f == throw_node[0]
- || f == throw_node[1])
+ || f == throw_node)
{
if (f == soft_monitorenter_node)
op = OPCODE_monitorenter;
NOTE_POP (1);
break;
}
- else if (exp == soft_exceptioninfo_call_node)
- {
- NOTE_PUSH (1); /* Pushed by exception system. */
- break;
- }
for ( ; x != NULL_TREE; x = TREE_CHAIN (x))
{
generate_bytecode_insns (TREE_VALUE (x), STACK_TARGET, state);
NOTE_POP (1); /* Pop implicit this. */
if (TREE_CODE (f) == FUNCTION_DECL && DECL_CONTEXT (f) != NULL_TREE)
{
- tree saved_context = NULL_TREE;
+ tree context = DECL_CONTEXT (f);
int index, interface = 0;
RESERVE (5);
if (METHOD_STATIC (f))
else if (DECL_CONSTRUCTOR_P (f) || CALL_USING_SUPER (exp)
|| METHOD_PRIVATE (f))
OP1 (OPCODE_invokespecial);
- else if (CLASS_INTERFACE (TYPE_NAME (DECL_CONTEXT (f))))
+ else
{
- OP1 (OPCODE_invokeinterface);
- interface = 1;
+ if (CLASS_INTERFACE (TYPE_NAME (context)))
+ {
+ tree arg1 = TREE_VALUE (TREE_OPERAND (exp, 1));
+ context = TREE_TYPE (TREE_TYPE (arg1));
+ if (CLASS_INTERFACE (TYPE_NAME (context)))
+ interface = 1;
+ }
+ if (interface)
+ OP1 (OPCODE_invokeinterface);
+ else
+ OP1 (OPCODE_invokevirtual);
}
- else
- OP1 (OPCODE_invokevirtual);
+ index = find_methodref_with_class_index (&state->cpool, f, context);
+ OP2 (index);
if (interface)
{
- saved_context = DECL_CONTEXT (f);
- DECL_CONTEXT (f) =
- TREE_TYPE (TREE_TYPE (TREE_VALUE (TREE_OPERAND (exp, 1))));
+ if (nargs <= 0)
+ abort ();
+
+ OP1 (nargs);
+ OP1 (0);
}
- index = find_methodref_index (&state->cpool, f);
- if (interface)
- DECL_CONTEXT (f) = saved_context;
- OP2 (index);
f = TREE_TYPE (TREE_TYPE (f));
if (TREE_CODE (f) != VOID_TYPE)
{
else
NOTE_PUSH (size);
}
- if (interface)
- {
- OP1 (nargs);
- OP1 (0);
- }
break;
}
}
/* new_ptr and old_ptr point into the old and new buffers,
respectively. (If no relocations cause the buffer to
grow, the buffer will be the same buffer, and new_ptr==old_ptr.)
- The bytes at higher adress have been copied and relocations
+ The bytes at higher address have been copied and relocations
handled; those at lower addresses remain to process. */
/* Lower old index of piece to be copied with no relocation.
}
}
if (new_ptr != chunk->data)
- fatal ("internal error - perform_relocations");
+ abort ();
}
state->code_length = pc;
}
i = find_utf8_constant (&state->cpool,
build_java_signature (TREE_TYPE (part)));
PUT2(i);
- have_value = DECL_INITIAL (part) != NULL_TREE && FIELD_STATIC (part)
- && TREE_CODE (TREE_TYPE (part)) != POINTER_TYPE;
+ have_value = DECL_INITIAL (part) != NULL_TREE
+ && FIELD_STATIC (part) && CONSTANT_VALUE_P (DECL_INITIAL (part))
+ && FIELD_FINAL (part)
+ && (JPRIMITIVE_TYPE_P (TREE_TYPE (part))
+ || TREE_TYPE (part) == string_ptr_type_node);
if (have_value)
attr_count++;
{
tree init = DECL_INITIAL (part);
static tree ConstantValue_node = NULL_TREE;
+ if (TREE_TYPE (part) != TREE_TYPE (init))
+ fatal_error ("field initializer type mismatch");
ptr = append_chunk (NULL, 8, state);
if (ConstantValue_node == NULL_TREE)
ConstantValue_node = get_identifier ("ConstantValue");
i = (body != NULL_TREE) + (DECL_FUNCTION_THROWS (part) != NULL_TREE);
/* Make room for the Synthetic attribute (of zero length.) */
- if (DECL_FINIT_P (part)
+ if (DECL_FINIT_P (part)
+ || DECL_INSTINIT_P (part)
|| OUTER_FIELD_ACCESS_IDENTIFIER_P (DECL_NAME (part))
|| TYPE_DOT_CLASS (clas) == part)
{
}
ptr = append_chunk (NULL, 10, state);
- i = ((INNER_CLASS_TYPE_P (clas)
- || DECL_INNER_CLASS_LIST (TYPE_NAME (clas))) ? 2 : 1);
+ i = 1; /* Source file always exists as an attribute */
+ if (INNER_CLASS_TYPE_P (clas) || DECL_INNER_CLASS_LIST (TYPE_NAME (clas)))
+ i++;
+ if (clas == object_type_node)
+ i++;
PUT2 (i); /* attributes_count */
/* generate the SourceFile attribute. */
if (SourceFile_node == NULL_TREE)
- SourceFile_node = get_identifier ("SourceFile");
+ {
+ SourceFile_node = get_identifier ("SourceFile");
+ ggc_add_tree_root (&SourceFile_node, 1);
+ }
+
i = find_utf8_constant (&state->cpool, SourceFile_node);
PUT2 (i); /* attribute_name_index */
PUT4 (2);
i = find_utf8_constant (&state->cpool, get_identifier (source_file));
PUT2 (i);
+ append_gcj_attribute (state, clas);
append_innerclasses_attribute (state, clas);
/* New finally generate the contents of the constant pool chunk. */
int i;
if (Synthetic_node == NULL_TREE)
- Synthetic_node = get_identifier ("Synthetic");
+ {
+ Synthetic_node = get_identifier ("Synthetic");
+ ggc_add_tree_root (&Synthetic_node, 1);
+ }
i = find_utf8_constant (&state->cpool, Synthetic_node);
PUT2 (i); /* Attribute string index */
PUT4 (0); /* Attribute length */
}
static void
+append_gcj_attribute (state, class)
+ struct jcf_partial *state;
+ tree class;
+{
+ unsigned char *ptr;
+ int i;
+
+ if (class != object_type_node)
+ return;
+
+ ptr = append_chunk (NULL, 6, state); /* 2+4 */
+ i = find_utf8_constant (&state->cpool,
+ get_identifier ("gnu.gcj.gcj-compiled"));
+ PUT2 (i); /* Attribute string index */
+ PUT4 (0); /* Attribute length */
+}
+
+static void
append_innerclasses_attribute (state, class)
struct jcf_partial *state;
tree class;
ptr = append_chunk (NULL, 8, state); /* 2+4+2 */
- if (InnerClasses_node == NULL_TREE)
- InnerClasses_node = get_identifier ("InnerClasses");
+ if (InnerClasses_node == NULL_TREE)
+ {
+ InnerClasses_node = get_identifier ("InnerClasses");
+ ggc_add_tree_root (&InnerClasses_node, 1);
+ }
i = find_utf8_constant (&state->cpool, InnerClasses_node);
PUT2 (i);
length_marker = ptr; PUT4 (0); /* length, to be later patched */
process: itself, up and down. */
while (class && INNER_CLASS_TYPE_P (class))
{
- char *n;
+ const char *n;
decl = TYPE_NAME (class);
n = IDENTIFIER_POINTER (DECL_NAME (decl)) +
struct jcf_partial *state;
tree decl, name;
{
- static tree anonymous_name = NULL_TREE;
- int icii, ocii, ini, icaf;
+ int icii, icaf;
+ int ocii = 0, ini = 0;
unsigned char *ptr = append_chunk (NULL, 8, state);
- if (!anonymous_name)
- anonymous_name = get_identifier ("");
-
icii = find_class_constant (&state->cpool, TREE_TYPE (decl));
- ocii = find_class_constant (&state->cpool, TREE_TYPE (DECL_CONTEXT (decl)));
-
- /* The specs are saying that if the class is anonymous,
- inner_name_index must be zero. But the implementation makes it
- point to an empty string. */
- ini = find_utf8_constant (&state->cpool,
- (ANONYMOUS_CLASS_P (TREE_TYPE (decl)) ?
- anonymous_name : name));
+
+ /* Sun's implementation seems to generate ocii to 0 for inner
+ classes (which aren't considered members of the class they're
+ in.) The specs are saying that if the class is anonymous,
+ inner_name_index must be zero. */
+ if (!ANONYMOUS_CLASS_P (TREE_TYPE (decl)))
+ {
+ ocii = find_class_constant (&state->cpool,
+ TREE_TYPE (DECL_CONTEXT (decl)));
+ ini = find_utf8_constant (&state->cpool, name);
+ }
icaf = get_access_flags (decl);
-
+
PUT2 (icii); PUT2 (ocii); PUT2 (ini); PUT2 (icaf);
}
make_class_file_name (clas)
tree clas;
{
- const char *dname, *slash;
- char *cname, *r;
+ const char *dname, *cname, *slash;
+ char *r;
struct stat sb;
cname = IDENTIFIER_POINTER (identifier_subst (DECL_NAME (TYPE_NAME (clas)),
dname = r + (slash - dname) + 1;
while (1)
{
- cname = strchr (dname, DIR_SEPARATOR);
- if (cname == NULL)
+ char *s = strchr (dname, DIR_SEPARATOR);
+ if (s == NULL)
break;
- *cname = '\0';
- if (stat (r, &sb) == -1)
- {
+ *s = '\0';
+ if (stat (r, &sb) == -1
/* Try to make it. */
- if (mkdir (r, 0755) == -1)
- {
- fatal ("failed to create directory `%s'", r);
- free (r);
- return NULL;
- }
- }
- *cname = DIR_SEPARATOR;
+ && mkdir (r, 0755) == -1)
+ fatal_io_error ("can't create directory %s", r);
+
+ *s = DIR_SEPARATOR;
/* Skip consecutive separators. */
- for (dname = cname + 1; *dname && *dname == DIR_SEPARATOR; ++dname)
+ for (dname = s + 1; *dname && *dname == DIR_SEPARATOR; ++dname)
;
}
if (class_file_name != NULL)
{
- FILE* stream = fopen (class_file_name, "wb");
+ FILE *stream = fopen (class_file_name, "wb");
if (stream == NULL)
- fatal ("failed to open `%s' for writing", class_file_name);
+ fatal_io_error ("can't open %s for writing", class_file_name);
+
jcf_dependency_add_target (class_file_name);
init_jcf_state (state, work);
chunks = generate_classfile (clas, state);
write_chunks (stream, chunks);
if (fclose (stream))
- fatal ("failed to close after writing `%s'", class_file_name);
+ fatal_io_error ("error closing %s", class_file_name);
free (class_file_name);
}
release_jcf_state (state);