aboutsummaryrefslogtreecommitdiff
path: root/gcc/java/jcf-write.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/java/jcf-write.c')
-rw-r--r--gcc/java/jcf-write.c964
1 files changed, 964 insertions, 0 deletions
diff --git a/gcc/java/jcf-write.c b/gcc/java/jcf-write.c
new file mode 100644
index 00000000000..931c9d582c1
--- /dev/null
+++ b/gcc/java/jcf-write.c
@@ -0,0 +1,964 @@
+/* Write out a Java(TM) class file.
+ Copyright (C) 1998 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+
+Java and all Java-based marks are trademarks or registered trademarks
+of Sun Microsystems, Inc. in the United States and other countries.
+The Free Software Foundation is independent of Sun Microsystems, Inc. */
+
+#include "config.h"
+#include "tree.h"
+#include "java-tree.h"
+#include "jcf.h"
+#include <stdio.h>
+#include "obstack.h"
+#undef AND
+#include "rtl.h"
+#include "java-opcodes.h"
+#include "parse.h" /* for BLOCK_EXPR_BODY */
+#include "buffer.h"
+
+extern struct obstack temporary_obstack;
+
+/* The buffer allocated for bytecode for the current method. */
+
+struct buffer bytecode = NULL_BUFFER;
+
+/* Make sure bytecode.data is big enough for at least N more bytes. */
+
+#define RESERVE(N) \
+ do { if (bytecode.ptr + (N) > bytecode.limit) buffer_grow (&bytecode, N); } while (0)
+
+/* Add a 1-byte instruction/operand I to bytecode.data,
+ assuming space has already been RESERVE'd. */
+
+#define OP1(I) (*bytecode.ptr++ = (I))
+
+/* Like OP1, but I is a 2-byte big endian integer. */
+
+#define OP2(I) \
+ do { int _I = (I); OP1 (_I >> 8); OP1 (_I); } while (0)
+
+/* Like OP1, but I is a 4-byte big endian integer. */
+
+#define OP4(I) \
+ do { int _I = (I); OP1 (_I >> 24); OP1 (_I >> 16); \
+ OP1 (_I >> 8); OP1 (_I); } while (0)
+
+/* The current stack size (stack pointer) in the current method. */
+
+int code_SP = 0;
+
+/* The largest extent of stack size (stack pointer) in the current method. */
+
+int code_SP_max = 0;
+
+CPool *code_cpool;
+
+/* Macro to call each time we push I words on the JVM stack. */
+
+#define NOTE_PUSH(I) \
+ do { code_SP += (I); if (code_SP > code_SP_max) code_SP_max = code_SP; } while (0)
+
+/* Macro to call each time we pop I words from the JVM stack. */
+
+#define NOTE_POP(I) \
+ do { code_SP -= (I); if (code_SP < 0) abort(); } while (0)
+
+/* A chunk or segment of a .class file. */
+
+struct chunk
+{
+ /* The next segment of this .class file. */
+ struct chunk *next;
+
+ /* The actual data in this segment to be written to the .class file. */
+ unsigned char *data;
+
+ /* The size of the segment to be written to the .class file. */
+ int size;
+};
+
+/* 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. */
+
+#define PUT1(X) (*ptr++ = (X))
+#define PUT2(X) (PUT1((X) >> 8), PUT1((X) & 0xFF))
+#define PUT4(X) (PUT2((X) >> 16), PUT2((X) & 0xFFFF))
+#define PUTN(P, N) (bcopy(P, ptr, N), ptr += (N))
+
+
+/* A buffer for storing line number entries for the current method. */
+struct buffer linenumbers = NULL_BUFFER;
+
+/* Append a line number entry for the given PC and LINE into
+ linenumbers.data. This will later before a LineNumberTable attribute. */
+
+void
+put_linenumber (pc, line)
+ int pc, line;
+{
+ register unsigned char *ptr;
+ if (linenumbers.ptr == linenumbers.limit)
+ buffer_grow (&linenumbers, 4);
+ ptr = linenumbers.ptr;
+ PUT2 (pc);
+ PUT2 (line);
+ linenumbers.ptr = ptr;
+}
+
+/* The index of jvm local variable allocated for this DECL.
+ This is assign when generating .class files;
+ contrast DECL_LOCAL_SLOT_NUMBER whcih is set when *reading* a .class file.
+ (We don't allocate DECL_LANG_SPECIFIC for locals from Java sourc code.) */
+
+#define DECL_LOCAL_INDEX(DECL) DECL_ALIGN(DECL)
+
+struct localvar_info
+{
+ tree decl;
+
+ int start_pc;
+
+ /* Offset in LocalVariableTable. */
+ int debug_offset;
+};
+
+struct buffer localvars = NULL_BUFFER;
+
+#define localvar_buffer ((struct localvar_info*) localvars.data)
+#define localvar_max ((struct localvar_info*) localvars.ptr - localvar_buffer)
+
+/* A buffer for storing LocalVariableTable entries entries. */
+
+struct buffer localvartable = NULL_BUFFER;
+
+int
+localvar_alloc (decl, start_pc)
+ tree decl;
+ int start_pc;
+{
+ int wide = TYPE_IS_WIDE (TREE_TYPE (decl));
+ int index;
+ register struct localvar_info *info = (struct localvar_info*)localvars.data;
+ register struct localvar_info *limit = (struct localvar_info*)localvars.ptr;
+ for (index = 0; info < limit; index++, info++)
+ {
+ if (info->decl == NULL_TREE
+ && (! wide || (info+1)->decl == NULL_TREE))
+ break;
+ }
+ if (info == limit)
+ {
+ buffer_grow (&localvars, sizeof (struct localvar_info));
+ info = (struct localvar_info*)localvars.data + index;
+ localvars.ptr = (unsigned char *) (info + 1 + wide);
+ }
+ info->decl = decl;
+ if (wide)
+ (info+1)->decl = TYPE_SECOND;
+ DECL_LOCAL_INDEX (decl) = index;
+ info->start_pc = start_pc;
+
+ if (DECL_NAME (decl) != NULL_TREE)
+ {
+ /* Generate debugging info. */
+ int i;
+ register unsigned char *ptr;
+ buffer_grow (&localvartable, 10);
+ ptr = localvartable.ptr;
+ info->debug_offset = ptr - localvartable.data;
+ PUT2 (start_pc);
+ PUT2 (0); /* length - fill in later */
+ i = find_utf8_constant (code_cpool, DECL_NAME (decl));
+ PUT2 (i); /* name_index*/
+ i = find_utf8_constant (code_cpool,
+ build_java_signature (TREE_TYPE (decl)));
+ PUT2 (i); /* descriptor_index */
+ PUT2 (index);
+ localvartable.ptr = ptr;
+ }
+ else
+ info->debug_offset = -1;
+}
+
+int
+localvar_free (decl, end_pc)
+ tree decl;
+ int end_pc;
+{
+ register unsigned char *ptr;
+ int index = DECL_LOCAL_INDEX (decl);
+ register struct localvar_info *info = &localvar_buffer [index];
+ int wide = TYPE_IS_WIDE (TREE_TYPE (decl));
+ int i;
+
+ i = info->debug_offset;
+ if (i >= 0)
+ {
+ register unsigned char *ptr;
+ /* Point to length field of local_variable_table. */
+ ptr = localvartable.data + i + 2;
+ i = end_pc - info->start_pc;
+ PUT2 (i);
+ }
+
+ if (info->decl != decl)
+ abort ();
+ info->decl = NULL_TREE;
+ if (wide)
+ {
+ info++;
+ if (info->decl != TYPE_SECOND)
+ abort ();
+ info->decl = NULL_TREE;
+ }
+
+}
+
+
+#define STACK_TARGET 1
+#define IGNORE_TARGET 2
+
+/* 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 *
+alloc_chunk (last, data, size, work)
+ struct chunk *last;
+ unsigned char *data;
+ int size;
+ struct obstack *work;
+{
+ struct chunk *chunk = (struct chunk *)
+ obstack_alloc (work, sizeof(struct chunk));
+
+ if (data == NULL && size > 0)
+ data = obstack_alloc (work, size);
+
+ chunk->next = NULL;
+ chunk->data = data;
+ chunk->size = size;
+ last->next = chunk;
+ return chunk;
+}
+
+/* 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
+get_access_flags (decl)
+ tree decl;
+{
+ int flags = 0;
+ int isfield = TREE_CODE (decl) == FIELD_DECL || TREE_CODE (decl) == VAR_DECL;
+ if (CLASS_PUBLIC (decl)) /* same as FIELD_PUBLIC and METHOD_PUBLIC */
+ flags |= ACC_PUBLIC;
+ if (CLASS_FINAL (decl)) /* same as FIELD_FINAL and METHOD_FINAL */
+ flags |= ACC_PUBLIC;
+ if (isfield || TREE_CODE (decl) == FUNCTION_DECL)
+ {
+ if (TREE_PROTECTED (decl))
+ flags |= ACC_PROTECTED;
+ if (TREE_PRIVATE (decl))
+ flags |= ACC_PRIVATE;
+ }
+ else if (TREE_CODE (decl) == TYPE_DECL)
+ {
+ if (CLASS_SUPER (decl))
+ flags |= ACC_SUPER;
+ if (CLASS_ABSTRACT (decl))
+ flags |= ACC_ABSTRACT;
+ if (CLASS_INTERFACE (decl))
+ flags |= ACC_INTERFACE;
+ }
+ else
+ fatal ("internal error - bad argument to get_access_flags");
+ if (TREE_CODE (decl) == FUNCTION_DECL)
+ {
+ if (METHOD_NATIVE (decl))
+ flags |= ACC_NATIVE;
+ if (METHOD_STATIC (decl))
+ flags |= ACC_STATIC;
+ if (METHOD_FINAL (decl))
+ flags |= ACC_FINAL;
+ if (METHOD_SYNCHRONIZED (decl))
+ flags |= ACC_SYNCHRONIZED;
+ if (METHOD_ABSTRACT (decl))
+ flags |= ACC_ABSTRACT;
+ }
+ if (isfield)
+ {
+ if (FIELD_STATIC (decl))
+ flags |= ACC_STATIC;
+ if (FIELD_VOLATILE (decl))
+ flags |= ACC_VOLATILE;
+ if (FIELD_TRANSIENT (decl))
+ flags |= ACC_TRANSIENT;
+ }
+ return flags;
+}
+
+/* Write the list of segments starting at CHUNKS to STREAM. */
+
+void
+write_chunks (stream, chunks)
+ FILE* stream;
+ struct chunk *chunks;
+{
+ for (; chunks != NULL; chunks = chunks->next)
+ fwrite (chunks->data, chunks->size, 1, stream);
+}
+
+void
+push_constant1 (index)
+ int index;
+{
+ if (index < 256)
+ {
+ OP1 (OPCODE_ldc);
+ OP1 (index);
+ }
+ else
+ {
+ OP1 (OPCODE_ldc_w);
+ OP2 (index);
+ }
+}
+
+void
+push_constant2 (index)
+ int index;
+{
+ RESERVE (3);
+ OP1 (OPCODE_ldc2_w);
+ OP2 (index);
+}
+
+void
+push_int_const (i)
+ HOST_WIDE_INT i;
+{
+ RESERVE(3);
+ if (i >= -1 && i <= 5)
+ OP1(OPCODE_iconst_0 + i);
+ else if (i >= -128 && i < 128)
+ {
+ OP1(OPCODE_bipush);
+ OP1(i);
+ }
+ else if (i >= -32768 && i < 32768)
+ {
+ OP1(OPCODE_sipush);
+ OP2(i);
+ }
+ else
+ {
+ i = find_constant1 (code_cpool, CONSTANT_Integer, i & 0xFFFFFFFF);
+ push_constant1 (i);
+ }
+}
+
+void
+push_long_const (lo, hi)
+ HOST_WIDE_INT lo, hi;
+{
+ if (hi == 0 && lo >= 0 && lo <= 1)
+ {
+ RESERVE(1);
+ OP1(OPCODE_lconst_0 + lo);
+ }
+#if 0
+ else if ((jlong) (jint) i == i)
+ {
+ push_int_const ((jint) i);
+ RESERVE (1);
+ OP1 (OPCODE_i2l);
+ }
+#endif
+ else
+ {
+ HOST_WIDE_INT w1, w2;
+ lshift_double (lo, hi, -32, 64, &w1, &w2, 1);
+ hi = find_constant1 (code_cpool, CONSTANT_Long,
+ w1 & 0xFFFFFFFF, lo & 0xFFFFFFFF);
+ push_constant2 (hi);
+ }
+}
+
+void
+field_op (field, opcode)
+ tree field;
+ int opcode;
+{
+ int index = find_fieldref_index (code_cpool, field);
+ RESERVE (3);
+ OP1 (opcode);
+ OP2 (index);
+}
+
+/* Returns an integer in the range 0 (for 'int') through 4 (for object
+ reference) to 7 (for 'short') which matches the pattern of how JVM
+ opcodes typically depend on the operand type. */
+
+int
+adjust_typed_op (type)
+ tree type;
+{
+ switch (TREE_CODE (type))
+ {
+ case BOOLEAN_TYPE: return 5;
+ case CHAR_TYPE: return 6;
+ case POINTER_TYPE:
+ case RECORD_TYPE: return 4;
+ case INTEGER_TYPE:
+ switch (TYPE_PRECISION (type))
+ {
+ case 8: return 5;
+ case 16: return 7;
+ case 32: return 0;
+ case 64: return 1;
+ }
+ break;
+ case REAL_TYPE:
+ switch (TYPE_PRECISION (type))
+ {
+ case 32: return 2;
+ case 64: return 3;
+ }
+ break;
+ }
+ abort ();
+}
+
+void
+maybe_wide (opcode, index)
+ int opcode, index;
+{
+ if (index >= 256)
+ {
+ RESERVE (4);
+ OP1 (196); /* wide */
+ OP1 (opcode);
+ OP2 (index);
+ }
+ else
+ {
+ RESERVE (2);
+ OP1 (opcode);
+ OP1 (index);
+ }
+}
+
+#define PC BUFFER_LENGTH(&bytecode)
+
+/* Generate byetcode for sub-expression EXP of METHOD.
+ TARGET is one of STACK_TARGET or IGNORE_TARGET. */
+
+void
+generate_bytecode_insns (method, exp, target)
+ tree method;
+ tree exp;
+ int target;
+{
+ rtx value;
+ tree type = TREE_TYPE (exp);
+ enum java_opcode jopcode;
+ int op;
+ switch (TREE_CODE (exp))
+ {
+ case BLOCK:
+ if (BLOCK_EXPR_BODY (exp))
+ {
+ tree local;
+ for (local = BLOCK_EXPR_DECLS (exp); local; )
+ {
+ tree next = TREE_CHAIN (local);
+ localvar_alloc (local, PC);
+ local = next;
+ }
+ generate_bytecode_insns (method, BLOCK_EXPR_BODY (exp), target);
+ for (local = BLOCK_EXPR_DECLS (exp); local; )
+ {
+ tree next = TREE_CHAIN (local);
+ localvar_free (local, PC);
+ local = next;
+ }
+ }
+ break;
+ case COMPOUND_EXPR:
+ generate_bytecode_insns (method, TREE_OPERAND (exp, 0), IGNORE_TARGET);
+ generate_bytecode_insns (method, TREE_OPERAND (exp, 1), target);
+ break;
+ case EXPR_WITH_FILE_LOCATION:
+ {
+ char *saved_input_filename = input_filename;
+ int saved_lineno = lineno;
+ input_filename = EXPR_WFL_FILENAME (exp);
+ lineno = EXPR_WFL_LINENO (exp);
+ if (EXPR_WFL_EMIT_LINE_NOTE (exp))
+ put_linenumber (PC, EXPR_WFL_LINENO (exp));
+ generate_bytecode_insns (method, EXPR_WFL_NODE (exp), target);
+ input_filename = saved_input_filename;
+ lineno = saved_lineno;
+ }
+ break;
+ case INTEGER_CST:
+ if (target == IGNORE_TARGET) ; /* do nothing */
+ else if (TREE_CODE (type) == POINTER_TYPE)
+ {
+ if (! integer_zerop (exp))
+ abort();
+ RESERVE(1);
+ OP1 (OPCODE_aconst_null);
+ NOTE_PUSH (1);
+ }
+ else if (TYPE_PRECISION (type) <= 32)
+ {
+ push_int_const (TREE_INT_CST_LOW (exp));
+ NOTE_PUSH (1);
+ }
+ else
+ {
+ push_long_const (TREE_INT_CST_LOW (exp), TREE_INT_CST_HIGH (exp));
+ NOTE_PUSH (2);
+ }
+ break;
+ case VAR_DECL:
+ if (TREE_STATIC (exp))
+ {
+ field_op (exp, OPCODE_getstatic);
+ break;
+ }
+ /* ... fall through ... */
+ case PARM_DECL:
+ {
+ int kind = adjust_typed_op (type);
+ int index = DECL_LOCAL_INDEX (exp);
+ if (index <= 3)
+ {
+ RESERVE (1);
+ OP1 (26 + 4 * kind + index); /* [ilfda]load_[0123] */
+ }
+ else
+ maybe_wide (21 + kind, index); /* [ilfda]load */
+ }
+ break;
+ case INDIRECT_REF:
+ generate_bytecode_insns (method, TREE_OPERAND (exp, 0), target);
+ break;
+ case ARRAY_REF:
+ generate_bytecode_insns (method, TREE_OPERAND (exp, 0), target);
+ generate_bytecode_insns (method, TREE_OPERAND (exp, 1), target);
+ if (target != IGNORE_TARGET)
+ {
+ jopcode = OPCODE_iaload + adjust_typed_op (type);
+ RESERVE(1);
+ OP1 (jopcode);
+ }
+ break;
+ case COMPONENT_REF:
+ {
+ tree obj = TREE_OPERAND (exp, 0);
+ tree field = TREE_OPERAND (exp, 1);
+ int is_static = FIELD_STATIC (field);
+ generate_bytecode_insns (method, obj,
+ is_static ? IGNORE_TARGET : target);
+ if (target != IGNORE_TARGET)
+ {
+ if (DECL_NAME (field) == length_identifier_node && !is_static
+ && TYPE_ARRAY_P (TREE_TYPE (obj)))
+ {
+ RESERVE (1);
+ OP1 (OPCODE_arraylength);
+ }
+ else
+ field_op (field, is_static ? OPCODE_getstatic : OPCODE_getfield);
+ }
+ }
+ break;
+ case RETURN_EXPR:
+ if (!TREE_OPERAND (exp, 0))
+ op = OPCODE_return;
+ 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));
+ generate_bytecode_insns (method, exp, STACK_TARGET);
+ }
+ RESERVE (1);
+ OP1 (op);
+ break;
+ case MODIFY_EXPR:
+ {
+ tree lhs = TREE_OPERAND (exp, 0);
+ tree rhs = TREE_OPERAND (exp, 1);
+ HOST_WIDE_INT value;
+#if 0
+ if (TREE_CODE (rhs) == PLUS_EXPR
+ && TREE_CODE (lhs) == VAR_DECL
+ /* && FIXME lhs is a local variable */
+ && TYPE_MODE (TREE)TYPE (lhs) == SImode /* ??? */
+ && TREE_OPERAND (rhs, 0) == lhs
+ && TREE_CODE (TREE_OPERAND (rhs, 1)) == INTEGER_CST
+ /* or vice versa FIXME */
+ && (value = TREE_INT_CST_LOW (TREE_OPERAND (rhs, 1)),
+ (value >= -32768 && value <= 32767)))
+ {
+ emit_insn (gen_rtx (SET, SImode,
+ DECL_RTL (lhs),
+ gen_rtx (PLUS, SImode,
+ DECL_RTL (lhs),
+ gen_rtx_CONST_INT (SImode, value))));
+ return DECL_RTL (lhs);
+ }
+#endif
+ if (TREE_CODE (lhs) == COMPONENT_REF)
+ generate_bytecode_insns (method, TREE_OPERAND (lhs, 0), STACK_TARGET);
+ else if (TREE_CODE (lhs) == ARRAY_REF)
+ {
+ generate_bytecode_insns (method,
+ TREE_OPERAND (lhs, 0), STACK_TARGET);
+ generate_bytecode_insns (method,
+ TREE_OPERAND (lhs, 1), STACK_TARGET);
+ }
+ generate_bytecode_insns (method, rhs, STACK_TARGET);
+ if (target != IGNORE_TARGET)
+ {
+ RESERVE (1);
+ OP1 (TYPE_IS_WIDE (type) ? OPCODE_dup2_x1 : OPCODE_dup_x1);
+ }
+ if (TREE_CODE (lhs) == COMPONENT_REF)
+ {
+ tree field = TREE_OPERAND (lhs, 1);
+ field_op (field,
+ FIELD_STATIC (field) ? OPCODE_putstatic
+ : OPCODE_putfield);
+ }
+ else if (TREE_CODE (lhs) == VAR_DECL
+ || TREE_CODE (lhs) == PARM_DECL)
+ {
+ if (FIELD_STATIC (lhs))
+ {
+ field_op (lhs, OPCODE_putstatic);
+ }
+ else
+ {
+ int index = DECL_LOCAL_INDEX (lhs);
+ int opcode = adjust_typed_op (TREE_TYPE (lhs));
+ if (index <= 3)
+ {
+ RESERVE (1);
+ opcode = 59 + 4 * opcode + index;
+ OP1 (opcode); /* [ilfda]store_[0123] */
+ }
+ else
+ {
+ maybe_wide (54 + opcode, index); /* [ilfda]store */
+ }
+ }
+ }
+ else if (TREE_CODE (lhs) == ARRAY_REF)
+ {
+ jopcode = OPCODE_iastore + adjust_typed_op (TREE_TYPE (lhs));
+ RESERVE(1);
+ OP1 (jopcode);
+ }
+ else
+ fatal ("internal error (bad lhs to MODIFY_EXPR)");
+ }
+ break;
+ case PLUS_EXPR:
+ jopcode = OPCODE_iadd + adjust_typed_op (type);
+ goto binop;
+ case MINUS_EXPR:
+ jopcode = OPCODE_isub + adjust_typed_op (type);
+ goto binop;
+ case MULT_EXPR:
+ jopcode = OPCODE_imul + adjust_typed_op (type);
+ goto binop;
+ case TRUNC_DIV_EXPR:
+ case RDIV_EXPR:
+ jopcode = OPCODE_idiv + adjust_typed_op (type);
+ goto binop;
+ binop:
+ generate_bytecode_insns (method, TREE_OPERAND (exp, 0), target);
+ generate_bytecode_insns (method, TREE_OPERAND (exp, 1), target);
+ if (target == STACK_TARGET)
+ {
+ RESERVE(1);
+ OP1 (jopcode);
+ }
+ break;
+ case CALL_EXPR:
+ {
+ tree t;
+ for (t = TREE_OPERAND (exp, 1); t != NULL_TREE; t = TREE_CHAIN (t))
+ {
+ generate_bytecode_insns (method, TREE_VALUE (t), STACK_TARGET);
+ }
+ t = TREE_OPERAND (exp, 0);
+ if (TREE_CODE (t) == FUNCTION_DECL)
+ {
+ int index = find_methodref_index (code_cpool, t);
+ RESERVE (3);
+ if (DECL_CONSTRUCTOR_P (t))
+ OP1 (OPCODE_invokespecial);
+ else if (METHOD_STATIC (t))
+ OP1 (OPCODE_invokestatic);
+ else
+ OP1 (OPCODE_invokevirtual);
+ OP2 (index);
+ break;
+ }
+ }
+ /* fall through */
+ default:
+ error("internal error - tree code not implemented: ", TREE_CODE (exp));
+ }
+}
+
+/* Generate and return a list of chunks containing the class CLAS
+ in the .class file representation. The list can be written to a
+ .class file using write_chunks. Allocate chunks from obstack WORK. */
+
+/* Currently does not write any attributes i.e. no code. */
+
+struct chunk *
+generate_classfile (clas, work)
+ tree clas;
+ struct obstack *work;
+{
+ CPool cpool;
+ struct chunk head;
+ struct chunk *chunk;
+ struct chunk *cpool_chunk;
+ char *ptr;
+ int i;
+ char *fields_count_ptr;
+ int fields_count = 0;
+ char *methods_count_ptr;
+ int methods_count = 0;
+ tree part;
+ int total_supers
+ = clas == object_type_node ? 0
+ : TREE_VEC_LENGTH (TYPE_BINFO_BASETYPES (clas));
+
+ chunk = alloc_chunk (&head, NULL, 8, work);
+ ptr = chunk->data;
+ PUT4 (0xCafeBabe); /* Magic number */
+ PUT2 (3); /* Minor version */
+ PUT2 (45); /* Major version */
+
+ CPOOL_INIT(&cpool);
+ cpool_chunk = chunk = alloc_chunk (chunk, NULL, 0, work);
+
+ /* Next allocate the chunk containing acces_flags through fields_counr. */
+ if (clas == object_type_node)
+ i = 10;
+ else
+ i = 8 + 2 * total_supers;
+ chunk = alloc_chunk (chunk, NULL, i, work);
+ ptr = chunk->data;
+ i = get_access_flags (TYPE_NAME (clas)); PUT2 (i); /* acces_flags */
+ i = find_class_constant (&cpool, clas); PUT2 (i); /* this_class */
+ if (clas == object_type_node)
+ {
+ PUT2(0); /* super_class */
+ PUT2(0); /* interfaces_count */
+ }
+ else
+ {
+ tree basetypes = TYPE_BINFO_BASETYPES (clas);
+ tree base = BINFO_TYPE (TREE_VEC_ELT (basetypes, 0));
+ int j = find_class_constant (&cpool, base); PUT2 (j); /* super_class */
+ PUT2 (total_supers - 1); /* interfaces_count */
+ for (i = 1; i < total_supers; i++)
+ {
+ base = BINFO_TYPE (TREE_VEC_ELT (basetypes, i));
+ j = find_class_constant (&cpool, base);
+ PUT2 (j);
+ }
+ }
+ fields_count_ptr = ptr;
+
+ for (part = TYPE_FIELDS (clas); part; part = TREE_CHAIN (part))
+ {
+ if (DECL_NAME (part) == NULL_TREE)
+ continue;
+ chunk = alloc_chunk (chunk, NULL, 8, work);
+ ptr = chunk->data;
+ i = get_access_flags (part); PUT2 (i);
+ i = find_utf8_constant (&cpool, DECL_NAME (part)); PUT2 (i);
+ i = find_utf8_constant (&cpool, build_java_signature (TREE_TYPE (part)));
+ PUT2(i);
+ PUT2 (0); /* attributes_count */
+ /* FIXME - emit ConstantValue attribute when appropriate */
+ fields_count++;
+ }
+ ptr = fields_count_ptr; PUT2 (fields_count);
+
+ chunk = alloc_chunk (chunk, NULL, 2, work);
+ ptr = methods_count_ptr = chunk->data;
+ PUT2 (0);
+
+ for (part = TYPE_METHODS (clas); part; part = TREE_CHAIN (part))
+ {
+ tree body = BLOCK_EXPR_BODY (DECL_FUNCTION_BODY (part));
+ int linenumber_size; /* 4 * number of line number entries */
+ chunk = alloc_chunk (chunk, NULL, 8, work);
+ ptr = chunk->data;
+ i = get_access_flags (part); PUT2 (i);
+ i = find_utf8_constant (&cpool, DECL_NAME (part)); PUT2 (i);
+ i = find_utf8_constant (&cpool, build_java_signature (TREE_TYPE (part)));
+ PUT2 (i);
+ PUT2 (body != NULL_TREE ? 1 : 0); /* attributes_count */
+ if (body != NULL_TREE)
+ {
+ int code_attributes_count = 0;
+ int linenumber_size; /* 4 * number of line number entries */
+ int localvartable_size; /* 10 * number of local variable entries */
+ static tree Code_node = NULL_TREE;
+ tree t;
+ char *attr_len_ptr;
+ int code_length;
+ if (Code_node == NULL_TREE)
+ Code_node = get_identifier ("Code");
+ chunk = alloc_chunk (chunk, NULL, 14, work);
+ ptr = chunk->data;
+ i = find_utf8_constant (&cpool, Code_node); PUT2 (i);
+ attr_len_ptr = ptr;
+ BUFFER_RESET (&bytecode);
+ BUFFER_RESET (&localvartable);
+ BUFFER_RESET (&linenumbers);
+ BUFFER_RESET (&localvars);
+ code_SP = 0;
+ code_SP_max = 0;
+ code_cpool = &cpool;
+ for (t = DECL_ARGUMENTS (part); t != NULL_TREE; t = TREE_CHAIN (t))
+ localvar_alloc (t, 0);
+ generate_bytecode_insns (part, body, IGNORE_TARGET);
+ code_length = PC;
+ for (t = DECL_ARGUMENTS (part); t != NULL_TREE; t = TREE_CHAIN (t))
+ localvar_free (t, code_length);
+ linenumber_size = BUFFER_LENGTH (&linenumbers);
+ localvartable_size = BUFFER_LENGTH (&localvartable);
+ chunk = alloc_chunk (chunk, NULL, code_length, work);
+ bcopy (bytecode.data, chunk->data, code_length);
+ ptr = attr_len_ptr;
+ i = 8 + code_length + 4;
+ if (linenumber_size > 0)
+ {
+ code_attributes_count++;
+ i += 8 + linenumber_size;
+ }
+ if (localvartable_size > 0)
+ {
+ code_attributes_count++;
+ i += 8 + localvartable_size;
+ }
+ PUT4 (i); /* attribute_length */
+ PUT2 (code_SP_max); /* max_stack */
+ PUT2 (localvar_max); /* max_locals */
+ PUT4 (code_length);
+ chunk = alloc_chunk (chunk, NULL, 4, work);
+ ptr = chunk->data;
+ PUT2 (0); /* exception_table_length */
+ PUT2 (code_attributes_count);
+
+ /* Write the LineNumberTable attribute. */
+ if (linenumber_size > 0)
+ {
+ static tree LineNumberTable_node = NULL_TREE;
+ chunk = alloc_chunk (chunk, NULL, 8 + linenumber_size, work);
+ ptr = chunk->data;
+ if (LineNumberTable_node == NULL_TREE)
+ LineNumberTable_node = get_identifier ("LineNumberTable");
+ i = find_utf8_constant (&cpool, LineNumberTable_node);
+ PUT2 (i); /* attribute_name_index */
+ i = 2 + linenumber_size; PUT4 (i); /* attribute_length */
+ i = linenumber_size >> 2; PUT2 (i);
+ PUTN (linenumbers.data, linenumber_size);
+ }
+
+ /* Write the LocalVariableTable attribute. */
+ if (localvartable_size > 0)
+ {
+ static tree LocalVariableTable_node = NULL_TREE;
+ chunk = alloc_chunk (chunk, NULL, 8 + localvartable_size, work);
+ ptr = chunk->data;
+ if (LocalVariableTable_node == NULL_TREE)
+ LocalVariableTable_node = get_identifier("LocalVariableTable");
+ i = find_utf8_constant (&cpool, LocalVariableTable_node);
+ PUT2 (i); /* attribute_name_index */
+ i = 2 + localvartable_size; PUT4 (i); /* attribute_length */
+ i = localvartable_size / 10; PUT2 (i);
+ PUTN (localvartable.data, localvartable_size);
+ }
+ }
+ methods_count++;
+ }
+ ptr = methods_count_ptr; PUT2 (methods_count);
+
+ chunk = alloc_chunk (chunk, NULL, 2, work);
+ ptr = chunk->data;
+ PUT2 (0); /* attributes_count */
+
+ /* New finally generate the contents of the constant pool chunk. */
+ i = count_constant_pool_bytes (&cpool);
+ ptr = obstack_alloc (work, i);
+ cpool_chunk->data = ptr;
+ cpool_chunk->size = i;
+ write_constant_pool (&cpool, ptr, i);
+ CPOOL_FINISH (&cpool);
+ return head.next;
+}
+
+char*
+make_class_file_name (clas)
+ tree clas;
+{
+ /* Should prepend an output directly, but need an option to specify it. */
+ return IDENTIFIER_POINTER (identifier_subst (DECL_NAME (TYPE_NAME (clas)),
+ "", '.', '/', ".class"));
+}
+
+/* Write out the contens of a class (RECORD_TYPE) CLAS, as a .class file.
+ The output .class file name is make_class_file_name(CLAS). */
+
+void
+write_classfile (clas)
+ tree clas;
+{
+ struct obstack *work = &temporary_obstack;
+ char *class_file_name = make_class_file_name (clas);
+ struct chunk *chunks;
+ FILE* stream = fopen (class_file_name, "wb");
+ if (stream == NULL)
+ fatal ("failed to open `%s' for writing", class_file_name);
+ chunks = generate_classfile (clas, work);
+ write_chunks (stream, chunks);
+ if (fclose (stream))
+ fatal ("failed to close after writing `%s'", class_file_name);
+ obstack_free (work, chunks);
+}