/* Write out a Java(TM) class file.
- Copyright (C) 1998, 1999 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 void generate_bytecode_insns PROTO ((tree, int, struct jcf_partial *));
-static struct chunk * alloc_chunk PROTO ((struct chunk *, unsigned char *,
+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 PROTO ((unsigned char *, int,
+static unsigned char * append_chunk PARAMS ((unsigned char *, int,
struct jcf_partial *));
-static void append_chunk_copy PROTO ((unsigned char *, int,
+static void append_chunk_copy PARAMS ((unsigned char *, int,
struct jcf_partial *));
-static struct jcf_block * gen_jcf_label PROTO ((struct jcf_partial *));
-static void finish_jcf_block PROTO ((struct jcf_partial *));
-static void define_jcf_label PROTO ((struct jcf_block *,
+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 PROTO ((struct jcf_partial *));
-static void put_linenumber PROTO ((int, struct jcf_partial *));
-static void localvar_alloc PROTO ((tree, struct jcf_partial *));
-static void localvar_free PROTO ((tree, struct jcf_partial *));
-static int get_access_flags PROTO ((tree));
-static void write_chunks PROTO ((FILE *, struct chunk *));
-static int adjust_typed_op PROTO ((tree, int));
-static void generate_bytecode_conditional PROTO ((tree, struct jcf_block *,
+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 PROTO ((tree, struct jcf_partial *));
-static void perform_relocations PROTO ((struct jcf_partial *));
-static void init_jcf_state PROTO ((struct jcf_partial *, struct obstack *));
-static void init_jcf_method PROTO ((struct jcf_partial *, tree));
-static void release_jcf_state PROTO ((struct jcf_partial *));
-static struct chunk * generate_classfile PROTO ((tree, struct jcf_partial *));
-static struct jcf_handler *alloc_handler PROTO ((struct jcf_block *,
+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 PROTO ((tree, HOST_WIDE_INT, struct jcf_partial *));
-static void emit_reloc PROTO ((HOST_WIDE_INT, int, struct jcf_block *,
+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 PROTO ((HOST_WIDE_INT, struct jcf_partial *));
-static void push_constant2 PROTO ((HOST_WIDE_INT, struct jcf_partial *));
-static void push_int_const PROTO ((HOST_WIDE_INT, struct jcf_partial *));
-static int find_constant_wide PROTO ((HOST_WIDE_INT, HOST_WIDE_INT,
+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 PROTO ((HOST_WIDE_INT, HOST_WIDE_INT,
+static void push_long_const PARAMS ((HOST_WIDE_INT, HOST_WIDE_INT,
struct jcf_partial *));
-static int find_constant_index PROTO ((tree, struct jcf_partial *));
-static void push_long_const PROTO ((HOST_WIDE_INT, HOST_WIDE_INT,
+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 PROTO ((tree, int, struct jcf_partial *));
-static void maybe_wide PROTO ((int, int, struct jcf_partial *));
-static void emit_dup PROTO ((int, int, struct jcf_partial *));
-static void emit_pop PROTO ((int, struct jcf_partial *));
-static void emit_iinc PROTO ((tree, int, struct jcf_partial *));
-static void emit_load_or_store PROTO ((tree, int, struct jcf_partial *));
-static void emit_load PROTO ((tree, struct jcf_partial *));
-static void emit_store PROTO ((tree, struct jcf_partial *));
-static void emit_unop PROTO ((enum java_opcode, tree, struct jcf_partial *));
-static void emit_binop PROTO ((enum java_opcode, tree, struct jcf_partial *));
-static void emit_reloc PROTO ((HOST_WIDE_INT, int, struct jcf_block *,
+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 PROTO ((struct jcf_block *,
+static void emit_switch_reloc PARAMS ((struct jcf_block *,
struct jcf_partial *));
-static void emit_case_reloc PROTO ((struct jcf_relocation *,
+static void emit_case_reloc PARAMS ((struct jcf_relocation *,
struct jcf_partial *));
-static void emit_if PROTO ((struct jcf_block *, int, int,
+static void emit_if PARAMS ((struct jcf_block *, int, int,
struct jcf_partial *));
-static void emit_goto PROTO ((struct jcf_block *, struct jcf_partial *));
-static void emit_jsr PROTO ((struct jcf_block *, struct jcf_partial *));
-static void call_cleanups PROTO ((struct jcf_block *, struct jcf_partial *));
-static char *make_class_file_name PROTO ((tree));
+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));
+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 *
flags |= ACC_ABSTRACT;
if (CLASS_INTERFACE (decl))
flags |= ACC_INTERFACE;
+ if (CLASS_STATIC (decl))
+ flags |= ACC_STATIC;
+ 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 && lo < 32768) || (hi == -1 && lo >= -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);
}
int opcode, inv_opcode;
struct jcf_partial *state;
{
+ RESERVE(3);
OP1 (opcode);
/* value is 1 byte from reloc back to start of instruction. */
emit_reloc (RELOCATION_VALUE_1, - inv_opcode, target, state);
struct jcf_block *target;
struct jcf_partial *state;
{
+ RESERVE(3);
OP1 (OPCODE_goto);
/* Value is 1 byte from reloc back to start of instruction. */
emit_reloc (RELOCATION_VALUE_1, OPCODE_goto_w, target, state);
struct jcf_block *target;
struct jcf_partial *state;
{
+ RESERVE(3);
OP1 (OPCODE_jsr);
/* Value is 1 byte from reloc back to start of instruction. */
emit_reloc (RELOCATION_VALUE_1, OPCODE_jsr_w, target, state);
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:
- generate_bytecode_insns (TREE_OPERAND (exp, 0), IGNORE_TARGET, state);
+ case COMPOUND_EXPR:
+ generate_bytecode_insns (TREE_OPERAND (exp, 0), IGNORE_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:
{
- char *saved_input_filename = input_filename;
+ const char *saved_input_filename = input_filename;
tree body = EXPR_WFL_NODE (exp);
int saved_lineno = lineno;
if (body == empty_stmt_node)
{
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);
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_IS_WIDE (TREE_TYPE (exp)) ? 2 : 1);
}
break;
case CASE_EXPR:
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);
}
/* For most binary operations, both operands and the result have the
same type. Shift operations are different. Using arg1's type
- gets us the correct SP adjustment in all casesd. */
+ gets us the correct SP adjustment in all cases. */
if (target == STACK_TARGET)
emit_binop (jopcode, TREE_TYPE (arg1), state);
break;
OP2 (index);
}
break;
+ case SAVE_EXPR:
+ generate_bytecode_insns (TREE_OPERAND (exp, 0), STACK_TARGET, state);
+ break;
case CONVERT_EXPR:
case NOP_EXPR:
case FLOAT_EXPR:
{
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);
}
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 = NULL;
+ struct jcf_block *finally_label, *start_label, *end_label;
+ struct jcf_handler *handler;
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;
+ tree return_link = NULL_TREE, exception_decl = NULL_TREE;
+
+ tree exception_type;
- finally_label->pc = PENDING_CLEANUP_PC;
- finally_label->next = state->labeled_blocks;
- state->labeled_blocks = finally_label;
- state->num_finalizers++;
+ 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))
+ {
+ finally_label->pc = PENDING_CLEANUP_PC;
+ finally_label->next = state->labeled_blocks;
+ state->labeled_blocks = finally_label;
+ state->num_finalizers++;
+ }
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 (finally))
+ {
+ if (state->labeled_blocks != finally_label)
+ abort();
+ state->labeled_blocks = finally_label->next;
+ }
+ end_label = get_jcf_label_here (state);
+
+ if (end_label == start_label)
+ {
+ state->num_finalizers--;
+ define_jcf_label (finally_label, state);
+ generate_bytecode_insns (finally, IGNORE_TARGET, state);
+ break;
+ }
+
+ if (CAN_COMPLETE_NORMALLY (finally))
+ {
+ return_link = build_decl (VAR_DECL, NULL_TREE,
+ return_address_type_node);
+ finished_label = gen_jcf_label (state);
+ }
+
if (CAN_COMPLETE_NORMALLY (try_block))
- emit_goto (finished_label, state);
+ {
+ if (CAN_COMPLETE_NORMALLY (finally))
+ {
+ emit_jsr (finally_label, state);
+ emit_goto (finished_label, state);
+ }
+ else
+ emit_goto (finally_label, state);
+ }
+
+ /* Handle exceptions. */
- /* Handle exceptions. */
- localvar_alloc (return_link, state);
- handler = alloc_handler (start_label, NULL_PTR, state);
- handler->end_label = handler->handler_label;
+ 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));
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)
{
- int index = find_methodref_index (&state->cpool, f);
- int interface = 0;
+ tree context = DECL_CONTEXT (f);
+ int index, interface = 0;
RESERVE (5);
if (METHOD_STATIC (f))
OP1 (OPCODE_invokestatic);
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)
+ {
+ if (nargs <= 0)
+ abort ();
+
+ 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;
}
}
/* fall through */
notimpl:
default:
- error("internal error - tree code not implemented: %s",
+ error("internal error in generate_bytecode_insn - tree code not implemented: %s",
tree_code_name [(int) TREE_CODE (exp)]);
}
}
/* 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;
}
struct jcf_partial *state;
{
struct chunk *cpool_chunk;
- char *source_file;
+ const char *source_file, *s;
char *ptr;
int i;
char *fields_count_ptr;
for (part = TYPE_FIELDS (clas); part; part = TREE_CHAIN (part))
{
- int have_value;
+ int have_value, attr_count = 0;
if (DECL_NAME (part) == NULL_TREE || DECL_ARTIFICIAL (part))
continue;
ptr = append_chunk (NULL, 8, state);
i = get_access_flags (part); PUT2 (i);
i = find_utf8_constant (&state->cpool, DECL_NAME (part)); PUT2 (i);
- i = find_utf8_constant (&state->cpool, build_java_signature (TREE_TYPE (part)));
+ i = find_utf8_constant (&state->cpool,
+ build_java_signature (TREE_TYPE (part)));
PUT2(i);
- have_value = DECL_INITIAL (part) != NULL_TREE && FIELD_STATIC (part);
- PUT2 (have_value); /* attributes_count */
+ 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++;
+
+ if (FIELD_THISN (part) || FIELD_LOCAL_ALIAS (part))
+ attr_count++;
+
+ PUT2 (attr_count); /* attributes_count */
if (have_value)
{
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");
PUT4 (2); /* attribute_length */
i = find_constant_index (init, state); PUT2 (i);
}
+ /* Emit the "Synthetic" attribute for val$<x> and this$<n> fields. */
+ if (FIELD_THISN (part) || FIELD_LOCAL_ALIAS (part))
+ ptr = append_synthetic_attribute (state);
fields_count++;
}
ptr = fields_count_ptr; UNSAFE_PUT2 (fields_count);
: DECL_NAME (part);
tree type = TREE_TYPE (part);
tree save_function = current_function_decl;
+ int synthetic_p = 0;
current_function_decl = part;
ptr = append_chunk (NULL, 8, state);
i = get_access_flags (part); PUT2 (i);
i = find_utf8_constant (&state->cpool, build_java_signature (type));
PUT2 (i);
i = (body != NULL_TREE) + (DECL_FUNCTION_THROWS (part) != NULL_TREE);
+
+ /* Make room for the Synthetic attribute (of zero length.) */
+ if (DECL_FINIT_P (part)
+ || DECL_INSTINIT_P (part)
+ || OUTER_FIELD_ACCESS_IDENTIFIER_P (DECL_NAME (part))
+ || TYPE_DOT_CLASS (clas) == part)
+ {
+ i++;
+ synthetic_p = 1;
+ }
+
PUT2 (i); /* attributes_count */
+
+ if (synthetic_p)
+ ptr = append_synthetic_attribute (state);
+
if (body != NULL_TREE)
{
int code_attributes_count = 0;
if (state->linenumber_count > 0)
{
static tree LineNumberTable_node = NULL_TREE;
- ptr = append_chunk (NULL, 8 + 4 * state->linenumber_count, state);
+ ptr = append_chunk (NULL,
+ 8 + 4 * state->linenumber_count, state);
if (LineNumberTable_node == NULL_TREE)
LineNumberTable_node = get_identifier ("LineNumberTable");
i = find_utf8_constant (&state->cpool, LineNumberTable_node);
ptr = methods_count_ptr; UNSAFE_PUT2 (methods_count);
source_file = DECL_SOURCE_FILE (TYPE_NAME (clas));
- for (ptr = source_file; ; ptr++)
+ for (s = source_file; ; s++)
{
- char ch = *ptr;
+ char ch = *s;
if (ch == '\0')
break;
if (ch == '/' || ch == '\\')
- source_file = ptr+1;
+ source_file = s+1;
}
ptr = append_chunk (NULL, 10, state);
- PUT2 (1); /* attributes_count */
+
+ 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. */
i = count_constant_pool_bytes (&state->cpool);
return state->first;
}
+static unsigned char *
+append_synthetic_attribute (state)
+ struct jcf_partial *state;
+{
+ static tree Synthetic_node = NULL_TREE;
+ unsigned char *ptr = append_chunk (NULL, 6, state);
+ int i;
+
+ if (Synthetic_node == NULL_TREE)
+ {
+ 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 */
+
+ return ptr;
+}
+
+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;
+{
+ static tree InnerClasses_node = NULL_TREE;
+ tree orig_decl = TYPE_NAME (class);
+ tree current, decl;
+ int length = 0, i;
+ unsigned char *ptr, *length_marker, *number_marker;
+
+ if (!INNER_CLASS_TYPE_P (class) && !DECL_INNER_CLASS_LIST (orig_decl))
+ return;
+
+ ptr = append_chunk (NULL, 8, state); /* 2+4+2 */
+
+ 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 */
+ number_marker = ptr; PUT2 (0); /* number of classes, tblp */
+
+ /* Generate the entries: all inner classes visible from the one we
+ process: itself, up and down. */
+ while (class && INNER_CLASS_TYPE_P (class))
+ {
+ const char *n;
+
+ decl = TYPE_NAME (class);
+ n = IDENTIFIER_POINTER (DECL_NAME (decl)) +
+ IDENTIFIER_LENGTH (DECL_NAME (decl));
+
+ while (n[-1] != '$')
+ n--;
+ append_innerclasses_attribute_entry (state, decl, get_identifier (n));
+ length++;
+
+ class = TREE_TYPE (DECL_CONTEXT (TYPE_NAME (class)));
+ }
+
+ decl = orig_decl;
+ for (current = DECL_INNER_CLASS_LIST (decl);
+ current; current = TREE_CHAIN (current))
+ {
+ append_innerclasses_attribute_entry (state, TREE_PURPOSE (current),
+ TREE_VALUE (current));
+ length++;
+ }
+
+ ptr = length_marker; PUT4 (8*length+2);
+ ptr = number_marker; PUT2 (length);
+}
+
+static void
+append_innerclasses_attribute_entry (state, decl, name)
+ struct jcf_partial *state;
+ tree decl, name;
+{
+ int icii, icaf;
+ int ocii = 0, ini = 0;
+ unsigned char *ptr = append_chunk (NULL, 8, state);
+
+ icii = find_class_constant (&state->cpool, TREE_TYPE (decl));
+
+ /* 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);
+}
+
static char *
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);