#include "ggc.h"
#include "tm_p.h"
-#ifndef DIR_SEPARATOR
-#define DIR_SEPARATOR '/'
-#endif
-
extern struct obstack temporary_obstack;
/* Base directory in which `.class' files should be written.
NULL means to put the file into the same directory as the
corresponding .java file. */
-char *jcf_write_base_directory = NULL;
+const char *jcf_write_base_directory = NULL;
/* Make sure bytecode.data is big enough for at least N more bytes. */
If the label has been defined:
Until perform_relocations is finished, this is the maximum possible
- value of the bytecode offset at the begnning of this block.
+ value of the bytecode offset at the beginning of this block.
After perform_relocations, it is the actual offset (pc). */
int pc;
/* Information about the current switch statement. */
struct jcf_switch_state *sw_state;
+
+ /* The count of jsr instructions that have been emmitted. */
+ long num_jsrs;
};
static void generate_bytecode_insns (tree, int, struct jcf_partial *);
static struct jcf_block * get_jcf_label_here (struct jcf_partial *);
static void put_linenumber (int, struct jcf_partial *);
static void localvar_alloc (tree, struct jcf_partial *);
-static void localvar_free (tree, struct jcf_partial *);
+static void maybe_free_localvar (tree, struct jcf_partial *, int);
static int get_access_flags (tree);
static void write_chunks (FILE *, struct chunk *);
static int adjust_typed_op (tree, int);
static void call_cleanups (struct jcf_block *, struct jcf_partial *);
static char *make_class_file_name (tree);
static unsigned char *append_synthetic_attribute (struct jcf_partial *);
+static void append_deprecated_attribute (struct jcf_partial *);
static void append_innerclasses_attribute (struct jcf_partial *, tree);
static void append_innerclasses_attribute_entry (struct jcf_partial *, tree, tree);
static void append_gcj_attribute (struct jcf_partial *, tree);
alloc_chunk (struct chunk *last, unsigned char *data,
int size, struct obstack *work)
{
- struct chunk *chunk = (struct chunk *)
- obstack_alloc (work, sizeof(struct chunk));
+ struct chunk *chunk = obstack_alloc (work, sizeof(struct chunk));
if (data == NULL && size > 0)
data = obstack_alloc (work, size);
static struct jcf_block *
gen_jcf_label (struct jcf_partial *state)
{
- struct jcf_block *block = (struct jcf_block *)
- obstack_alloc (state->chunk_obstack, sizeof (struct jcf_block));
+ struct jcf_block *block
+ = obstack_alloc (state->chunk_obstack, sizeof (struct jcf_block));
block->next = NULL;
block->linenumber = -1;
block->pc = UNDEFINED_PC;
alloc_handler (struct jcf_block *start_label, struct jcf_block *end_label,
struct jcf_partial *state)
{
- struct jcf_handler *handler = (struct jcf_handler *)
- obstack_alloc (state->chunk_obstack, sizeof (struct jcf_handler));
+ struct jcf_handler *handler
+ = obstack_alloc (state->chunk_obstack, sizeof (struct jcf_handler));
handler->start_label = start_label;
handler->end_label = end_label;
handler->handler_label = get_jcf_label_here (state);
/* The index of jvm local variable allocated for this DECL.
This is assigned when generating .class files;
contrast DECL_LOCAL_SLOT_NUMBER which is set when *reading* a .class file.
- (We don't allocate DECL_LANG_SPECIFIC for locals from Java sourc code.) */
+ (We don't allocate DECL_LANG_SPECIFIC for locals from Java source code.) */
#define DECL_LOCAL_INDEX(DECL) DECL_ALIGN(DECL)
ptr = (struct localvar_info**) state->localvars.data + index;
state->localvars.ptr = (unsigned char *) (ptr + 1 + wide);
}
- info = (struct localvar_info *)
- obstack_alloc (state->chunk_obstack, sizeof (struct localvar_info));
+ info = obstack_alloc (state->chunk_obstack, sizeof (struct localvar_info));
ptr[0] = info;
if (wide)
ptr[1] = (struct localvar_info *)(~0);
}
static void
-localvar_free (tree decl, struct jcf_partial *state)
+maybe_free_localvar (tree decl, struct jcf_partial *state, int really)
{
struct jcf_block *end_label = get_jcf_label_here (state);
int index = DECL_LOCAL_INDEX (decl);
if (info->decl != decl)
abort ();
+ if (! really)
+ return;
ptr[0] = NULL;
if (wide)
{
emit_reloc (HOST_WIDE_INT value, int kind,
struct jcf_block *target, struct jcf_partial *state)
{
- struct jcf_relocation *reloc = (struct jcf_relocation *)
- obstack_alloc (state->chunk_obstack, sizeof (struct jcf_relocation));
+ struct jcf_relocation *reloc
+ = obstack_alloc (state->chunk_obstack, sizeof (struct jcf_relocation));
struct jcf_block *block = state->last_block;
reloc->next = block->u.relocations;
block->u.relocations = reloc;
OP1 (OPCODE_jsr);
/* Value is 1 byte from reloc back to start of instruction. */
emit_reloc (RELOCATION_VALUE_1, OPCODE_jsr_w, target, state);
+ state->num_jsrs++;
}
/* Generate code to evaluate EXP. If the result is true,
branch to TRUE_LABEL; otherwise, branch to FALSE_LABEL.
- TRUE_BRANCH_FIRST is a code geneation hint that the
+ TRUE_BRANCH_FIRST is a code generation hint that the
TRUE_LABEL may follow right after this. (The idea is that we
may be able to optimize away GOTO TRUE_LABEL; TRUE_LABEL:) */
OP1 (OPCODE_lcmp);
goto compare_1;
}
- /* FALLTHOUGH */
+ /* FALLTHROUGH */
default:
if (integer_zerop (exp1))
{
emit_store (state->return_value_decl, state);
call_cleanups (NULL, state);
emit_load (state->return_value_decl, state);
- /* If we call localvar_free (state->return_value_decl, state),
+ /* If we call maybe_free_localvar (state->return_value_decl, state, 1),
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
{
tree local;
tree body = BLOCK_EXPR_BODY (exp);
+ long jsrs = state->num_jsrs;
for (local = BLOCK_EXPR_DECLS (exp); local; )
{
tree next = TREE_CHAIN (local);
body = TREE_OPERAND (body, 1);
}
generate_bytecode_insns (body, target, state);
+
for (local = BLOCK_EXPR_DECLS (exp); local; )
{
tree next = TREE_CHAIN (local);
- localvar_free (local, state);
+ maybe_free_localvar (local, state, state->num_jsrs <= jsrs);
local = next;
}
}
{
const char *saved_input_filename = input_filename;
tree body = EXPR_WFL_NODE (exp);
- int saved_lineno = lineno;
+ int saved_lineno = input_line;
if (body == empty_stmt_node)
break;
input_filename = EXPR_WFL_FILENAME (exp);
- lineno = EXPR_WFL_LINENO (exp);
- if (EXPR_WFL_EMIT_LINE_NOTE (exp) && lineno > 0
+ input_line = EXPR_WFL_LINENO (exp);
+ if (EXPR_WFL_EMIT_LINE_NOTE (exp) && input_line > 0
&& debug_info_level > DINFO_LEVEL_NONE)
- put_linenumber (lineno, state);
+ put_linenumber (input_line, state);
generate_bytecode_insns (body, target, state);
input_filename = saved_input_filename;
- lineno = saved_lineno;
+ input_line = saved_lineno;
}
break;
case INTEGER_CST:
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
+ else if (prec == 1 && real_twop (exp))
+ OP1 (OPCODE_fconst_2);
+ /* ??? We could also use iconst_3/ldc followed by i2f/i2d
for other float/double when the value is a small integer. */
else
{
case CASE_EXPR:
{
struct jcf_switch_state *sw_state = state->sw_state;
- struct jcf_relocation *reloc = (struct jcf_relocation *)
- obstack_alloc (state->chunk_obstack, sizeof (struct jcf_relocation));
+ struct jcf_relocation *reloc
+ = obstack_alloc (state->chunk_obstack, sizeof (struct jcf_relocation));
HOST_WIDE_INT case_value = TREE_INT_CST_LOW (TREE_OPERAND (exp, 0));
reloc->kind = 0;
reloc->label = get_jcf_label_here (state);
HOST_WIDE_INT i;
unsigned HOST_WIDE_INT delta;
/* Copy the chain of relocs into a sorted array. */
- struct jcf_relocation **relocs = (struct jcf_relocation **)
- xmalloc (sw_state.num_cases * sizeof (struct jcf_relocation *));
+ struct jcf_relocation **relocs
+ = xmalloc (sw_state.num_cases * sizeof (struct jcf_relocation *));
/* The relocs arrays is a buffer with a gap.
The assumption is that cases will normally come in "runs". */
int gap_start = 0;
emit_dup (TYPE_IS_WIDE (type) ? 2 : 1 , offset, state);
exp = lhs;
}
- /* FALLTHOUGH */
+ /* FALLTHROUGH */
finish_assignment:
if (TREE_CODE (exp) == COMPONENT_REF)
tree arg0 = TREE_OPERAND (exp, 0);
tree arg1 = TREE_OPERAND (exp, 1);
jopcode += adjust_typed_op (type, 3);
- if (arg0 == arg1 && TREE_CODE (arg0) == SAVE_EXPR)
+ if (arg0 != NULL_TREE && operand_equal_p (arg0, arg1, 0))
{
/* fold may (e.g) convert 2*x to x+x. */
- generate_bytecode_insns (TREE_OPERAND (arg0, 0), target, state);
+ generate_bytecode_insns (arg0, target, state);
emit_dup (TYPE_PRECISION (TREE_TYPE (arg0)) > 32 ? 2 : 1, 0, state);
}
else
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);
+ maybe_free_localvar (exception_decl, state, 1);
+ maybe_free_localvar (return_link, state, 1);
define_jcf_label (finished_label, state);
}
}
case JAVA_EXC_OBJ_EXPR:
NOTE_PUSH (1); /* Pushed by exception system. */
break;
+ case MIN_EXPR:
+ case MAX_EXPR:
+ {
+ /* This copes with cases where fold() has created MIN or MAX
+ from a conditional expression. */
+ enum tree_code code = TREE_CODE (exp) == MIN_EXPR ? LT_EXPR : GT_EXPR;
+ tree op0 = TREE_OPERAND (exp, 0);
+ tree op1 = TREE_OPERAND (exp, 1);
+ tree x;
+ if (TREE_SIDE_EFFECTS (op0) || TREE_SIDE_EFFECTS (op1))
+ abort ();
+ x = build (COND_EXPR, TREE_TYPE (exp),
+ build (code, boolean_type_node, op0, op1),
+ op0, op1);
+ generate_bytecode_insns (x, target, state);
+ break;
+ }
case NEW_CLASS_EXPR:
{
tree class = TREE_TYPE (TREE_TYPE (exp));
unsigned char *old_ptr = old_buffer + old_size;
if (new_size != old_size)
{
- chunk->data = (unsigned char *)
- obstack_alloc (state->chunk_obstack, new_size);
+ chunk->data = obstack_alloc (state->chunk_obstack, new_size);
chunk->size = new_size;
}
new_ptr = chunk->data + new_size;
append_chunk (NULL, 0, state);
cpool_chunk = state->chunk;
- /* Next allocate the chunk containing acces_flags through fields_counr. */
+ /* Next allocate the chunk containing acces_flags through fields_count. */
if (clas == object_type_node)
i = 10;
else
if (have_value)
attr_count++;
- if (FIELD_THISN (part) || FIELD_LOCAL_ALIAS (part) || FIELD_SYNTHETIC (part))
+ if (FIELD_THISN (part) || FIELD_LOCAL_ALIAS (part)
+ || FIELD_SYNTHETIC (part))
attr_count++;
+ if (FIELD_DEPRECATED (part))
+ attr_count++;
PUT2 (attr_count); /* attributes_count */
if (have_value)
if (FIELD_THISN (part) || FIELD_LOCAL_ALIAS (part)
|| FIELD_SYNTHETIC (part))
ptr = append_synthetic_attribute (state);
+ if (FIELD_DEPRECATED (part))
+ append_deprecated_attribute (state);
fields_count++;
}
ptr = fields_count_ptr; UNSAFE_PUT2 (fields_count);
tree type = TREE_TYPE (part);
tree save_function = current_function_decl;
int synthetic_p = 0;
+
+ /* Invisible Miranda methods shouldn't end up in the .class
+ file. */
+ if (METHOD_INVISIBLE (part))
+ continue;
+
current_function_decl = part;
ptr = append_chunk (NULL, 8, state);
i = get_access_flags (part); PUT2 (i);
i++;
synthetic_p = 1;
}
+ /* Make room for Deprecated attribute. */
+ if (METHOD_DEPRECATED (part))
+ i++;
PUT2 (i); /* attributes_count */
get_jcf_label_here (state); /* Force a first block. */
for (t = DECL_ARGUMENTS (part); t != NULL_TREE; t = TREE_CHAIN (t))
localvar_alloc (t, state);
+ state->num_jsrs = 0;
generate_bytecode_insns (body, IGNORE_TARGET, state);
if (CAN_COMPLETE_NORMALLY (body))
{
OP1 (OPCODE_return);
}
for (t = DECL_ARGUMENTS (part); t != NULL_TREE; t = TREE_CHAIN (t))
- localvar_free (t, state);
+ maybe_free_localvar (t, state, 1);
if (state->return_value_decl != NULL_TREE)
- localvar_free (state->return_value_decl, state);
+ maybe_free_localvar (state->return_value_decl, state, 1);
finish_jcf_block (state);
perform_relocations (state);
PUT2 (i);
}
}
+
+ if (METHOD_DEPRECATED (part))
+ append_deprecated_attribute (state);
+
methods_count++;
current_function_decl = save_function;
}
i++;
if (clas == object_type_node)
i++;
+ if (CLASS_DEPRECATED (TYPE_NAME (clas)))
+ i++;
+
PUT2 (i); /* attributes_count */
/* generate the SourceFile attribute. */
PUT2 (i);
append_gcj_attribute (state, clas);
append_innerclasses_attribute (state, clas);
+ if (CLASS_DEPRECATED (TYPE_NAME (clas)))
+ append_deprecated_attribute (state);
/* New finally generate the contents of the constant pool chunk. */
i = count_constant_pool_bytes (&state->cpool);
}
static void
+append_deprecated_attribute (struct jcf_partial *state)
+{
+ unsigned char *ptr = append_chunk (NULL, 6, state);
+ int i;
+
+ i = find_utf8_constant (&state->cpool, get_identifier ("Deprecated"));
+ PUT2 (i); /* Attribute string index */
+ PUT4 (0); /* Attribute length */
+}
+
+static void
append_gcj_attribute (struct jcf_partial *state, tree class)
{
unsigned char *ptr;
const char *dname, *cname, *slash;
char *r;
struct stat sb;
+ char sep;
cname = IDENTIFIER_POINTER (identifier_subst (DECL_NAME (TYPE_NAME (clas)),
"", '.', DIR_SEPARATOR,
char *t;
dname = DECL_SOURCE_FILE (TYPE_NAME (clas));
slash = strrchr (dname, DIR_SEPARATOR);
+#ifdef DIR_SEPARATOR_2
if (! slash)
- {
- dname = ".";
- slash = dname + 1;
- }
+ slash = strrchr (dname, DIR_SEPARATOR_2);
+#endif
+ if (! slash)
+ {
+ dname = ".";
+ slash = dname + 1;
+ sep = DIR_SEPARATOR;
+ }
+ else
+ sep = *slash;
+
t = strrchr (cname, DIR_SEPARATOR);
if (t)
cname = t + 1;
}
else
{
+ char *s;
+
dname = jcf_write_base_directory;
+
+ s = strrchr (dname, DIR_SEPARATOR);
+#ifdef DIR_SEPARATOR_2
+ if (! s)
+ s = strrchr (dname, DIR_SEPARATOR_2);
+#endif
+ if (s)
+ sep = *s;
+ else
+ sep = DIR_SEPARATOR;
+
slash = dname + strlen (dname);
}
r = xmalloc (slash - dname + strlen (cname) + 2);
strncpy (r, dname, slash - dname);
- r[slash - dname] = DIR_SEPARATOR;
+ r[slash - dname] = sep;
strcpy (&r[slash - dname + 1], cname);
/* We try to make new directories when we need them. We only do
dname = r + (slash - dname) + 1;
while (1)
{
- char *s = strchr (dname, DIR_SEPARATOR);
+ char *s = strchr (dname, sep);
if (s == NULL)
break;
*s = '\0';
if (stat (r, &sb) == -1
/* Try to make it. */
&& mkdir (r, 0755) == -1)
- fatal_io_error ("can't create directory %s", r);
+ fatal_error ("can't create directory %s: %m", r);
- *s = DIR_SEPARATOR;
+ *s = sep;
/* Skip consecutive separators. */
- for (dname = s + 1; *dname && *dname == DIR_SEPARATOR; ++dname)
+ for (dname = s + 1; *dname && *dname == sep; ++dname)
;
}
return r;
}
-/* Write out the contens of a class (RECORD_TYPE) CLAS, as a .class file.
+/* Write out the contents of a class (RECORD_TYPE) CLAS, as a .class file.
The output .class file name is make_class_file_name(CLAS). */
void
temporary_file_name = concat (class_file_name, ".tmp", NULL);
stream = fopen (temporary_file_name, "wb");
if (stream == NULL)
- fatal_io_error ("can't open %s for writing", temporary_file_name);
+ fatal_error ("can't open %s for writing: %m", temporary_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_io_error ("error closing %s", temporary_file_name);
+ fatal_error ("error closing %s: %m", temporary_file_name);
/* If a file named by the string pointed to by `new' exists
prior to the call to the `rename' function, the bahaviour
if (rename (temporary_file_name, class_file_name) == -1)
{
remove (temporary_file_name);
- fatal_io_error ("can't create %s", class_file_name);
+ fatal_error ("can't create %s: %m", class_file_name);
}
free (temporary_file_name);
free (class_file_name);