aboutsummaryrefslogtreecommitdiff
path: root/gcc/java/gjavah.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/java/gjavah.c')
-rw-r--r--gcc/java/gjavah.c1086
1 files changed, 834 insertions, 252 deletions
diff --git a/gcc/java/gjavah.c b/gcc/java/gjavah.c
index fd39df6b8fc..0b030f22e72 100644
--- a/gcc/java/gjavah.c
+++ b/gcc/java/gjavah.c
@@ -1,7 +1,7 @@
/* Program to write C++-suitable header files from a Java(TM) .class
file. This is similar to SUN's javah.
-Copyright (C) 1996, 1998 Free Software Foundation, Inc.
+Copyright (C) 1996, 1998, 1999 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,11 +24,10 @@ The Free Software Foundation is independent of Sun Microsystems, Inc. */
/* Written by Per Bothner <bothner@cygnus.com>, February 1996. */
-#include <stdio.h>
+#include "config.h"
+#include "system.h"
#include "jcf.h"
-#ifdef __STDC__
-#include <stdlib.h>
-#endif
+#include "java-opcodes.h"
#include <math.h>
/* The output file. */
@@ -38,12 +37,10 @@ FILE *out = NULL;
static int found_error = 0;
/* Directory to place resulting files in. Set by -d option. */
-char *output_directory = "";
-
-char *output_file = NULL;
+const char *output_directory = "";
/* Directory to place temporary file. Set by -td option. Currently unused. */
-char *temp_directory = "/tmp";
+const char *temp_directory = "/tmp";
/* Number of friend functions we have to declare. */
static int friend_count;
@@ -84,11 +81,28 @@ static JCF_u2 last_access;
#define ACC_VISIBILITY (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED)
-int seen_fields = 0;
+/* Pass this macro the flags for a class and for a method. It will
+ return true if the method should be considered `final'. */
+#define METHOD_IS_FINAL(Class, Method) \
+ (((Class) & ACC_FINAL) || ((Method) & (ACC_FINAL | ACC_PRIVATE)))
+
+/* We keep a linked list of all method names we have seen. This lets
+ us determine if a method name and a field name are in conflict. */
+struct method_name
+{
+ unsigned char *name;
+ int length;
+ struct method_name *next;
+};
+
+/* List of method names we've seen. */
+static struct method_name *method_name_list;
static void print_field_info PROTO ((FILE *, JCF*, int, int, JCF_u2));
static void print_method_info PROTO ((FILE *, JCF*, int, int, JCF_u2));
-static void print_c_decl PROTO ((FILE*, JCF*, int, int, JCF_u2, int));
+static void print_c_decl PROTO ((FILE*, JCF*, int, int, int, const char *));
+static void decompile_method PROTO ((FILE *, JCF *, int));
+static void add_class_decl PROTO ((FILE *, JCF *, JCF_u2));
JCF_u2 current_field_name;
JCF_u2 current_field_value;
@@ -99,14 +113,47 @@ JCF_u2 current_field_flags;
( current_field_name = (NAME), current_field_signature = (SIGNATURE), \
current_field_flags = (ACCESS_FLAGS), current_field_value = 0)
-#define HANDLE_END_FIELD() \
- print_field_info (out, jcf, current_field_name, current_field_signature, \
- current_field_flags);
+/* We pass over fields twice. The first time we just note the types
+ of the fields and then the start of the methods. Then we go back
+ and parse the fields for real. This is ugly. */
+static int field_pass;
+/* Likewise we pass over methods twice. The first time we generate
+ class decl information; the second time we generate actual method
+ decls. */
+static int method_pass;
+
+#define HANDLE_END_FIELD() \
+ if (field_pass) \
+ { \
+ if (out) \
+ print_field_info (out, jcf, current_field_name, \
+ current_field_signature, \
+ current_field_flags); \
+ } \
+ else \
+ add_class_decl (out, jcf, current_field_signature);
#define HANDLE_CONSTANTVALUE(VALUEINDEX) current_field_value = (VALUEINDEX)
-#define HANDLE_METHOD(ACCESS_FLAGS, NAME, SIGNATURE, ATTRIBUTE_COUNT) \
- print_method_info (out, jcf, NAME, SIGNATURE, ACCESS_FLAGS)
+static int method_declared = 0;
+static int method_access = 0;
+static int method_printed = 0;
+#define HANDLE_METHOD(ACCESS_FLAGS, NAME, SIGNATURE, ATTRIBUTE_COUNT) \
+ if (method_pass) \
+ { \
+ decompiled = 0; method_printed = 0; \
+ if (out) \
+ print_method_info (out, jcf, NAME, SIGNATURE, ACCESS_FLAGS); \
+ } \
+ else \
+ add_class_decl (out, jcf, SIGNATURE);
+
+#define HANDLE_CODE_ATTRIBUTE(MAX_STACK, MAX_LOCALS, CODE_LENGTH) \
+ if (out && method_declared) decompile_method (out, jcf, CODE_LENGTH);
+
+static int decompiled = 0;
+#define HANDLE_END_METHOD() \
+ if (out && method_printed) fputs (decompiled ? "\n" : ";\n", out);
#include "jcf-reader.c"
@@ -119,12 +166,16 @@ static int
java_float_finite (f)
jfloat f;
{
- int32 *ip = (int32 *) &f;
+ union {
+ jfloat f;
+ int32 i;
+ } u;
+ u.f = f;
/* We happen to know that F_NAN_MASK will match all NaN values, and
also positive and negative infinity. That's why we only need one
test here. See The Java Language Specification, section 20.9. */
- return (*ip & F_NAN_MASK) != F_NAN_MASK;
+ return (u.i & F_NAN_MASK) != F_NAN_MASK;
}
/* Return 1 if D is not Inf or NaN. */
@@ -132,10 +183,14 @@ static int
java_double_finite (d)
jdouble d;
{
- int64 *ip = (int64 *) &d;
+ union {
+ jdouble d;
+ int64 i;
+ } u;
+ u.d = d;
/* Now check for all NaNs. */
- return (*ip & D_NAN_MASK) != D_NAN_MASK;
+ return (u.i & D_NAN_MASK) != D_NAN_MASK;
}
void
@@ -159,7 +214,7 @@ print_base_classname (stream, jcf, index)
int index;
{
int name_index = JPOOL_USHORT1 (jcf, index);
- int i, len;
+ int len;
unsigned char *s, *p, *limit;
s = JPOOL_UTF_DATA (jcf, name_index);
@@ -212,19 +267,15 @@ generate_access (stream, flags)
FILE *stream;
JCF_u2 flags;
{
- /* FIXME: Java's "protected" and "no access specifier" modes don't
- actually map to C++ "protected". That's how we map them for now,
- though. */
-
- if (! (flags & ACC_VISIBILITY))
- flags = ACC_PROTECTED;
-
if ((flags & ACC_VISIBILITY) == last_access)
return;
last_access = (flags & ACC_VISIBILITY);
switch (last_access)
{
+ case 0:
+ fputs ("public: // actually package-private\n", stream);
+ break;
case ACC_PUBLIC:
fputs ("public:\n", stream);
break;
@@ -232,7 +283,7 @@ generate_access (stream, flags)
fputs ("private:\n", stream);
break;
case ACC_PROTECTED:
- fputs ("protected:\n", stream);
+ fputs ("public: // actually protected\n", stream);
break;
default:
found_error = 1;
@@ -242,36 +293,75 @@ generate_access (stream, flags)
}
}
+/* See if NAME is already the name of a method. */
+static int
+name_is_method_p (name, length)
+ unsigned char *name;
+ int length;
+{
+ struct method_name *p;
+
+ for (p = method_name_list; p != NULL; p = p->next)
+ {
+ if (p->length == length && ! memcmp (p->name, name, length))
+ return 1;
+ }
+ return 0;
+}
+
static void
DEFUN(print_field_info, (stream, jcf, name_index, sig_index, flags),
FILE *stream AND JCF* jcf
AND int name_index AND int sig_index AND JCF_u2 flags)
{
+ char *override = NULL;
+
if (flags & ACC_FINAL)
{
if (current_field_value > 0)
{
- jlong num;
char buffer[25];
generate_access (stream, flags);
switch (JPOOL_TAG (jcf, current_field_value))
{
case CONSTANT_Integer:
- fputs (" static const jint ", out);
- print_name (out, jcf, name_index);
- fputs (" = ", out);
- num = JPOOL_INT (jcf, current_field_value);
- format_int (buffer, num, 10);
- fprintf (out, "%sL;\n", buffer);
+ {
+ jint num;
+ int most_negative = 0;
+ fputs (" static const jint ", out);
+ print_name (out, jcf, name_index);
+ fputs (" = ", out);
+ num = JPOOL_INT (jcf, current_field_value);
+ /* We single out the most negative number to print
+ specially. This avoids later warnings from g++. */
+ if (num == (jint) 0x80000000)
+ {
+ most_negative = 1;
+ ++num;
+ }
+ format_int (buffer, (jlong) num, 10);
+ fprintf (out, "%sL%s;\n", buffer, most_negative ? " - 1" : "");
+ }
break;
case CONSTANT_Long:
- fputs (" static const jlong ", out);
- print_name (out, jcf, name_index);
- fputs (" = ", out);
- num = JPOOL_LONG (jcf, current_field_value);
- format_int (buffer, num, 10);
- fprintf (out, "%sLL;\n", buffer);
+ {
+ jlong num;
+ int most_negative = 0;
+ fputs (" static const jlong ", out);
+ print_name (out, jcf, name_index);
+ fputs (" = ", out);
+ num = JPOOL_LONG (jcf, current_field_value);
+ /* We single out the most negative number to print
+ specially.. This avoids later warnings from g++. */
+ if (num == (jlong) 0x8000000000000000LL)
+ {
+ most_negative = 1;
+ ++num;
+ }
+ format_int (buffer, num, 10);
+ fprintf (out, "%sLL%s;\n", buffer, most_negative ? " - 1" :"");
+ }
break;
case CONSTANT_Float:
{
@@ -305,12 +395,42 @@ DEFUN(print_field_info, (stream, jcf, name_index, sig_index, flags),
generate_access (stream, flags);
fputs (" ", out);
- if (flags & ACC_STATIC)
+ if ((flags & ACC_STATIC))
fputs ("static ", out);
- print_c_decl (out, jcf, name_index, sig_index, flags, 0);
+
+ if (JPOOL_TAG (jcf, name_index) != CONSTANT_Utf8)
+ {
+ fprintf (stream, "<not a UTF8 constant>");
+ found_error = 1;
+ }
+ else
+ {
+ unsigned char *name = JPOOL_UTF_DATA (jcf, name_index);
+ int length = JPOOL_UTF_LENGTH (jcf, name_index);
+
+ if (name_is_method_p (name, length))
+ {
+ /* This field name matches a method. So override the name
+ with a dummy name. This is yucky, but it isn't clear
+ what else to do. FIXME: if the field is static, then
+ we'll be in real trouble. */
+ if ((flags & ACC_STATIC))
+ {
+ fprintf (stderr, "static field has same name as method\n");
+ found_error = 1;
+ }
+
+ override = (char *) malloc (length + 3);
+ memcpy (override, name, length);
+ strcpy (override + length, "__");
+ }
+ }
+
+ print_c_decl (out, jcf, name_index, sig_index, 0, override);
fputs (";\n", out);
- if (! (flags & ACC_STATIC))
- seen_fields++;
+
+ if (override)
+ free (override);
}
static void
@@ -320,202 +440,332 @@ DEFUN(print_method_info, (stream, jcf, name_index, sig_index, flags),
{
unsigned char *str;
int length, is_init = 0;
+ const char *override = NULL;
+ method_declared = 0;
+ method_access = flags;
if (JPOOL_TAG (jcf, name_index) != CONSTANT_Utf8)
fprintf (stream, "<not a UTF8 constant>");
str = JPOOL_UTF_DATA (jcf, name_index);
length = JPOOL_UTF_LENGTH (jcf, name_index);
- if (str[0] == '<')
+ if (str[0] == '<' || str[0] == '$')
{
- /* Ignore internally generated methods like <clinit>. However,
- treat <init> as a constructor. */
+ /* Ignore internally generated methods like <clinit> and
+ $finit$. However, treat <init> as a constructor. */
if (! utf8_cmp (str, length, "<init>"))
is_init = 1;
- else
+ else if (! METHOD_IS_FINAL (jcf->access_flags, flags)
+ && ! (flags & ACC_STATIC))
+ {
+ /* FIXME: i18n bug here. Order of prints should not be
+ fixed. */
+ fprintf (stderr, "ignored method `");
+ jcf_print_utf8 (stderr, str, length);
+ fprintf (stderr, "' marked virtual\n");
+ found_error = 1;
+ return;
+ }
+ else
return;
}
+ else
+ {
+ struct method_name *nn;
+
+ nn = (struct method_name *) malloc (sizeof (struct method_name));
+ nn->name = (char *) malloc (length);
+ memcpy (nn->name, str, length);
+ nn->length = length;
+ nn->next = method_name_list;
+ method_name_list = nn;
+ }
/* We can't generate a method whose name is a C++ reserved word.
For now the only problem has been `delete'; add more here as
- required. FIXME: we need a better solution than just ignoring
- the method. */
+ required. We can't just ignore the function, because that will
+ cause incorrect code to be generated if the function is virtual
+ (not only for calls to this function for for other functions
+ after it in the vtbl). So we give it a dummy name instead. */
if (! utf8_cmp (str, length, "delete"))
- return;
+ {
+ /* If the method is static or final, we can safely skip it. If
+ we don't skip it then we'll have problems since the mangling
+ will be wrong. FIXME. */
+ if (METHOD_IS_FINAL (jcf->access_flags, flags)
+ || (flags & ACC_STATIC))
+ return;
+ override = "__dummy_delete";
+ }
+ method_printed = 1;
generate_access (stream, flags);
fputs (" ", out);
if ((flags & ACC_STATIC))
fputs ("static ", out);
- else if (! (flags & ACC_FINAL) && ! (jcf->access_flags & ACC_FINAL))
+ else if (! METHOD_IS_FINAL (jcf->access_flags, flags))
{
/* Don't print `virtual' if we have a constructor. */
if (! is_init)
fputs ("virtual ", out);
}
- print_c_decl (out, jcf, name_index, sig_index, flags, is_init);
+ print_c_decl (out, jcf, name_index, sig_index, is_init, override);
+
+ if ((flags & ACC_ABSTRACT))
+ fputs (" = 0", out);
+ else
+ method_declared = 1;
+}
+
+/* Try to decompile a method body. Right now we just try to handle a
+ simple case that we can do. Expand as desired. */
+static void
+decompile_method (out, jcf, code_len)
+ FILE *out;
+ JCF *jcf;
+ int code_len;
+{
+ unsigned char *codes = jcf->read_ptr;
+ int index;
+ uint16 name_and_type, name;
- /* FIXME: it would be nice to decompile small methods here. That
- would allow for inlining. */
+ /* If the method is synchronized, don't touch it. */
+ if ((method_access & ACC_SYNCHRONIZED))
+ return;
- fprintf(out, ";\n");
+ if (code_len == 5
+ && codes[0] == OPCODE_aload_0
+ && codes[1] == OPCODE_getfield
+ && (codes[4] == OPCODE_areturn
+ || codes[4] == OPCODE_dreturn
+ || codes[4] == OPCODE_freturn
+ || codes[4] == OPCODE_ireturn
+ || codes[4] == OPCODE_lreturn))
+ {
+ /* Found code like `return FIELD'. */
+ fputs (" { return ", out);
+ index = (codes[2] << 8) | codes[3];
+ /* FIXME: ensure that tag is CONSTANT_Fieldref. */
+ /* FIXME: ensure that the field's class is this class. */
+ name_and_type = JPOOL_USHORT2 (jcf, index);
+ /* FIXME: ensure that tag is CONSTANT_NameAndType. */
+ name = JPOOL_USHORT1 (jcf, name_and_type);
+ print_name (out, jcf, name);
+ fputs ("; }", out);
+ decompiled = 1;
+ }
+ else if (code_len == 2
+ && codes[0] == OPCODE_aload_0
+ && codes[1] == OPCODE_areturn)
+ {
+ /* Found `return this'. */
+ fputs (" { return this; }", out);
+ decompiled = 1;
+ }
+ else if (code_len == 1 && codes[0] == OPCODE_return)
+ {
+ /* Found plain `return'. */
+ fputs (" { }", out);
+ decompiled = 1;
+ }
+ else if (code_len == 2
+ && codes[0] == OPCODE_aconst_null
+ && codes[1] == OPCODE_areturn)
+ {
+ /* Found `return null'. We don't want to depend on NULL being
+ defined. */
+ fputs (" { return 0; }", out);
+ decompiled = 1;
+ }
+}
+
+/* Print one piece of a signature. Returns pointer to next parseable
+ character on success, NULL on error. */
+static unsigned char *
+decode_signature_piece (stream, signature, limit, need_space)
+ FILE *stream;
+ unsigned char *signature, *limit;
+ int *need_space;
+{
+ const char *ctype;
+
+ switch (signature[0])
+ {
+ case '[':
+ for (signature++; (signature < limit
+ && *signature >= '0'
+ && *signature <= '9'); signature++)
+ ;
+ switch (*signature)
+ {
+ case 'B': ctype = "jbyteArray"; goto printit;
+ case 'C': ctype = "jcharArray"; goto printit;
+ case 'D': ctype = "jdoubleArray"; goto printit;
+ case 'F': ctype = "jfloatArray"; goto printit;
+ case 'I': ctype = "jintArray"; goto printit;
+ case 'S': ctype = "jshortArray"; goto printit;
+ case 'J': ctype = "jlongArray"; goto printit;
+ case 'Z': ctype = "jbooleanArray"; goto printit;
+ case '[': ctype = "jobjectArray"; goto printit;
+ case 'L':
+ /* We have to generate a reference to JArray here,
+ so that our output matches what the compiler
+ does. */
+ ++signature;
+ fputs ("JArray<", stream);
+ while (signature < limit && *signature != ';')
+ {
+ int ch = UTF8_GET (signature, limit);
+ if (ch == '/')
+ fputs ("::", stream);
+ else
+ jcf_print_char (stream, ch);
+ }
+ fputs (" *> *", stream);
+ *need_space = 0;
+ ++signature;
+ break;
+ default:
+ /* Unparseable signature. */
+ return NULL;
+ }
+ break;
+
+ case '(':
+ case ')':
+ /* This shouldn't happen. */
+ return NULL;
+
+ case 'B': ctype = "jbyte"; goto printit;
+ case 'C': ctype = "jchar"; goto printit;
+ case 'D': ctype = "jdouble"; goto printit;
+ case 'F': ctype = "jfloat"; goto printit;
+ case 'I': ctype = "jint"; goto printit;
+ case 'J': ctype = "jlong"; goto printit;
+ case 'S': ctype = "jshort"; goto printit;
+ case 'Z': ctype = "jboolean"; goto printit;
+ case 'V': ctype = "void"; goto printit;
+ case 'L':
+ ++signature;
+ while (*signature && *signature != ';')
+ {
+ int ch = UTF8_GET (signature, limit);
+ /* `$' is the separator for an inner class. */
+ if (ch == '/' || ch == '$')
+ fputs ("::", stream);
+ else
+ jcf_print_char (stream, ch);
+ }
+ fputs (" *", stream);
+ if (*signature == ';')
+ signature++;
+ *need_space = 0;
+ break;
+ default:
+ *need_space = 1;
+ jcf_print_char (stream, *signature++);
+ break;
+ printit:
+ signature++;
+ *need_space = 1;
+ fputs (ctype, stream);
+ break;
+ }
+
+ return signature;
}
static void
-DEFUN(print_c_decl, (stream, jcf, name_index, signature_index, flags, is_init),
+DEFUN(print_c_decl, (stream, jcf, name_index, signature_index, is_init,
+ name_override),
FILE* stream AND JCF* jcf
- AND int name_index AND int signature_index AND JCF_u2 flags
- AND int is_init)
+ AND int name_index AND int signature_index
+ AND int is_init AND const char *name_override)
{
if (JPOOL_TAG (jcf, signature_index) != CONSTANT_Utf8)
- fprintf (stream, "<not a UTF8 constant>");
+ {
+ fprintf (stream, "<not a UTF8 constant>");
+ found_error = 1;
+ }
else
{
int length = JPOOL_UTF_LENGTH (jcf, signature_index);
unsigned char *str0 = JPOOL_UTF_DATA (jcf, signature_index);
register unsigned char *str = str0;
unsigned char *limit = str + length;
- int j;
- char *ctype;
int need_space = 0;
int is_method = str[0] == '(';
+ unsigned char *next;
- if (is_method)
+ /* If printing a method, skip to the return signature and print
+ that first. However, there is no return value if this is a
+ constructor. */
+ if (is_method && ! is_init)
{
- /* Skip to the return signature, and print that first.
- However, don't do this is we are printing a construtcor.
- */
- if (is_init)
- {
- str = str0 + 1;
- /* FIXME: Most programmers love Celtic knots because
- they see their own code in the interconnected loops.
- That is, this is spaghetti. */
- goto have_constructor;
- }
- else
+ while (str < limit)
{
- while (str < limit)
- {
- int ch = *str++;
- if (ch == ')')
- break;
- }
+ int ch = *str++;
+ if (ch == ')')
+ break;
}
}
- again:
- while (str < limit)
+ /* If printing a field or an ordinary method, then print the
+ "return value" now. */
+ if (! is_method || ! is_init)
{
- switch (str[0])
+ next = decode_signature_piece (stream, str, limit, &need_space);
+ if (! next)
{
- case '[':
- for (str++; str < limit && *str >= '0' && *str <= '9'; str++)
- ;
- switch (*str)
- {
- case 'B': ctype = "jbyteArray"; goto printit;
- case 'C': ctype = "jcharArray"; goto printit;
- case 'D': ctype = "jdoubleArray"; goto printit;
- case 'F': ctype = "jfloatArray"; goto printit;
- case 'I': ctype = "jintArray"; goto printit;
- case 'S': ctype = "jshortArray"; goto printit;
- case 'J': ctype = "jlongArray"; goto printit;
- case 'Z': ctype = "jbooleanArray"; goto printit;
- case '[': ctype = "jobjectArray"; goto printit;
- case 'L':
- /* We have to generate a reference to JArray here,
- so that our output matches what the compiler
- does. */
- ++str;
- fputs ("JArray<", out);
- while (str < limit && *str != ';')
- {
- int ch = UTF8_GET (str, limit);
- if (ch == '/')
- fputs ("::", stream);
- else
- jcf_print_char (stream, ch);
- }
- fputs (" *> *", out);
- need_space = 0;
- ++str;
- break;
- default:
- fprintf (stderr, "unparseable signature `%s'\n", str0);
- found_error = 1;
- ctype = "???"; goto printit;
- }
- break;
- case '(':
- fputc (*str++, stream);
- continue;
- case ')':
- fputc (*str++, stream);
- /* the return signature was printed in the first pass. */
+ fprintf (stderr, "unparseable signature: `%s'\n", str0);
+ found_error = 1;
return;
- case 'B': ctype = "jbyte"; goto printit;
- case 'C': ctype = "jchar"; goto printit;
- case 'D': ctype = "jdouble"; goto printit;
- case 'F': ctype = "jfloat"; goto printit;
- case 'I': ctype = "jint"; goto printit;
- case 'J': ctype = "jlong"; goto printit;
- case 'S': ctype = "jshort"; goto printit;
- case 'Z': ctype = "jboolean"; goto printit;
- case 'V': ctype = "void"; goto printit;
- case 'L':
- ++str;
- while (*str && *str != ';')
- {
- int ch = UTF8_GET (str, limit);
- if (ch == '/')
- fputs ("::", stream);
- else
- jcf_print_char (stream, ch);
- }
- fputs (" *", stream);
- if (*str == ';')
- str++;
- need_space = 0;
- break;
- default:
- need_space = 1;
- jcf_print_char (stream, *str++);
- break;
- printit:
- str++;
- need_space = 1;
- fputs (ctype, stream);
- break;
}
-
- if (is_method && str < limit && *str != ')')
- fputs (", ", stream);
}
- have_constructor:
- if (name_index)
+
+ /* Now print the name of the thing. */
+ if (need_space)
+ fputs (" ", stream);
+ if (name_override)
+ fputs (name_override, stream);
+ else if (name_index)
{
- if (need_space)
- fprintf (stream, " ");
/* Declare constructors specially. */
if (is_init)
print_base_classname (stream, jcf, jcf->this_class);
else
print_name (stream, jcf, name_index);
}
+
if (is_method)
{
+ /* Have a method or a constructor. Print signature pieces
+ until done. */
fputs (" (", stream);
- /* Go to beginning, skipping '('. */
str = str0 + 1;
- goto again; /* To handle argument signatures. */
+ while (str < limit && *str != ')')
+ {
+ next = decode_signature_piece (stream, str, limit, &need_space);
+ if (! next)
+ {
+ fprintf (stderr, "unparseable signature: `%s'\n", str0);
+ found_error = 1;
+ return;
+ }
+
+ if (next < limit && *next != ')')
+ fputs (", ", stream);
+ str = next;
+ }
+
+ fputs (")", stream);
}
}
}
-int
+void
DEFUN(print_mangled_classname, (stream, jcf, prefix, index),
- FILE *stream AND JCF *jcf AND char *prefix AND int index)
+ FILE *stream AND JCF *jcf AND const char *prefix AND int index)
{
int name_index = JPOOL_USHORT1 (jcf, index);
fputs (prefix, stream);
@@ -536,7 +786,7 @@ print_cxx_classname (stream, prefix, jcf, index)
int index;
{
int name_index = JPOOL_USHORT1 (jcf, index);
- int i, len, c;
+ int len, c;
unsigned char *s, *p, *limit;
s = JPOOL_UTF_DATA (jcf, name_index);
@@ -581,42 +831,283 @@ super_class_name (derived_jcf, len)
return supername;
}
-/* Print declarations for all classes required by this class. FIXME:
- the current implementation just prints every class name from the
- constant pool. This is too much. We really only need to print a
- declaration for each class which is the type of a return value, a
- field, or an argument. */
+
+
+/* We keep track of all the `#include's we generate, so we can avoid
+ duplicates. */
+struct include
+{
+ char *name;
+ struct include *next;
+};
+
+/* List of all includes. */
+static struct include *all_includes = NULL;
+
+/* Generate a #include. */
static void
-print_class_decls (out, jcf)
+print_include (out, utf8, len)
+ FILE *out;
+ unsigned char *utf8;
+ int len;
+{
+ struct include *incl;
+
+ if (! out)
+ return;
+
+ if (len == -1)
+ len = strlen (utf8);
+
+ for (incl = all_includes; incl; incl = incl->next)
+ {
+ if (! strncmp (incl->name, utf8, len))
+ return;
+ }
+
+ incl = (struct include *) malloc (sizeof (struct include));
+ incl->name = malloc (len + 1);
+ strncpy (incl->name, utf8, len);
+ incl->name[len] = '\0';
+ incl->next = all_includes;
+ all_includes = incl;
+
+ fputs ("#include <", out);
+ jcf_print_utf8 (out, utf8, len);
+ fputs (".h>\n", out);
+}
+
+
+
+/* This is used to represent part of a package or class name. */
+struct namelet
+{
+ /* The text of this part of the name. */
+ char *name;
+ /* True if this represents a class. */
+ int is_class;
+ /* Linked list of all classes and packages inside this one. */
+ struct namelet *subnamelets;
+ /* Pointer to next sibling. */
+ struct namelet *next;
+};
+
+/* The special root namelet. */
+static struct namelet root =
+{
+ NULL,
+ 0,
+ NULL,
+ NULL
+};
+
+/* This extracts the next name segment from the full UTF-8 encoded
+ package or class name and links it into the tree. It does this
+ recursively. */
+static void
+add_namelet (name, name_limit, parent)
+ unsigned char *name, *name_limit;
+ struct namelet *parent;
+{
+ unsigned char *p;
+ struct namelet *n = NULL, *np;
+
+ /* We want to skip the standard namespaces that we assume the
+ runtime already knows about. We only do this at the top level,
+ though, hence the check for `root'. */
+ if (parent == &root)
+ {
+#define JAVALANG "java/lang/"
+#define JAVAIO "java/io/"
+#define JAVAUTIL "java/util/"
+ if ((name_limit - name >= (int) sizeof (JAVALANG) - 1
+ && ! strncmp (name, JAVALANG, sizeof (JAVALANG) - 1))
+ || (name_limit - name >= (int) sizeof (JAVAUTIL) - 1
+ && ! strncmp (name, JAVAUTIL, sizeof (JAVAUTIL) - 1))
+ || (name_limit - name >= (int) sizeof (JAVAIO) - 1
+ && ! strncmp (name, JAVAIO, sizeof (JAVAIO) - 1)))
+ return;
+ }
+
+ for (p = name; p < name_limit && *p != '/' && *p != '$'; ++p)
+ ;
+
+ /* Search for this name beneath the PARENT node. */
+ for (np = parent->subnamelets; np != NULL; np = np->next)
+ {
+ if (! strncmp (name, np->name, p - name))
+ {
+ n = np;
+ break;
+ }
+ }
+
+ if (n == NULL)
+ {
+ n = (struct namelet *) malloc (sizeof (struct namelet));
+ n->name = malloc (p - name + 1);
+ strncpy (n->name, name, p - name);
+ n->name[p - name] = '\0';
+ n->is_class = (p == name_limit || *p == '$');
+ n->subnamelets = NULL;
+ n->next = parent->subnamelets;
+ parent->subnamelets = n;
+ }
+
+ /* We recurse if there is more text, and if the trailing piece does
+ not represent an inner class. */
+ if (p < name_limit && *p != '$')
+ add_namelet (p + 1, name_limit, n);
+}
+
+/* Print a single namelet. Destroys namelets while printing. */
+static void
+print_namelet (out, name, depth)
+ FILE *out;
+ struct namelet *name;
+ int depth;
+{
+ int i, term = 0;
+ struct namelet *c;
+
+ if (name->name)
+ {
+ for (i = 0; i < depth; ++i)
+ fputc (' ', out);
+ fprintf (out, "%s %s", name->is_class ? "class" : "namespace",
+ name->name);
+ if (name->is_class && name->subnamelets == NULL)
+ fputs (";\n", out);
+ else
+ {
+ term = 1;
+ fputs ("\n", out);
+ for (i = 0; i < depth; ++i)
+ fputc (' ', out);
+ fputs ("{\n", out);
+ }
+ }
+
+ c = name->subnamelets;
+ while (c != NULL)
+ {
+ struct namelet *next = c->next;
+ print_namelet (out, c, depth + 2);
+ c = next;
+ }
+
+ if (name->name)
+ {
+ if (term)
+ {
+ for (i = 0; i < depth; ++i)
+ fputc (' ', out);
+ fputs ("};\n", out);
+ }
+
+ free (name->name);
+ free (name);
+ }
+}
+
+/* This is called to add some classes to the list of classes for which
+ we need decls. The signature argument can be a function
+ signature. */
+static void
+add_class_decl (out, jcf, signature)
FILE *out;
JCF *jcf;
+ JCF_u2 signature;
{
- int i, seen_one = 0;
+ unsigned char *s = JPOOL_UTF_DATA (jcf, signature);
+ int len = JPOOL_UTF_LENGTH (jcf, signature);
+ int i;
+ /* Name of class we are processing. */
+ int name_index = JPOOL_USHORT1 (jcf, jcf->this_class);
+ int tlen = JPOOL_UTF_LENGTH (jcf, name_index);
+ char *tname = JPOOL_UTF_DATA (jcf, name_index);
- for (i = 1; i < JPOOL_SIZE (jcf); ++i)
+ for (i = 0; i < len; ++i)
{
- int kind = JPOOL_TAG (jcf, i);
- if (kind == CONSTANT_Class)
+ int start, saw_dollar;
+
+ /* If we see an array, then we include the array header. */
+ if (s[i] == '[')
{
- if (print_cxx_classname (out, "class ", jcf, i))
- fputs (";\n", out);
- seen_one = 1;
+ print_include (out, "java-array", -1);
+ continue;
+ }
+
+ /* We're looking for `L<stuff>;' -- everything else is
+ ignorable. */
+ if (s[i] != 'L')
+ continue;
+
+ saw_dollar = 0;
+ for (start = ++i; i < len && s[i] != ';'; ++i)
+ {
+ if (! saw_dollar && s[i] == '$' && out)
+ {
+ saw_dollar = 1;
+ /* If this class represents an inner class, then
+ generate a `#include' for the outer class. However,
+ don't generate the include if the outer class is the
+ class we are processing. */
+ if (i - start < tlen || strncmp (&s[start], tname, i - start))
+ print_include (out, &s[start], i - start);
+ break;
+ }
}
+
+ /* If we saw an inner class, then the generated #include will
+ declare the class. So in this case we needn't bother. */
+ if (! saw_dollar)
+ add_namelet (&s[start], &s[i], &root);
}
+}
+
+/* Print declarations for all classes required by this class. Any
+ class or package in the `java' package is assumed to be handled
+ statically in libjava; we don't generate declarations for these.
+ This makes the generated headers a bit easier to read. */
+static void
+print_class_decls (out, jcf, self)
+ FILE *out;
+ JCF *jcf;
+ int self;
+{
+ /* Make sure to always add the current class to the list of things
+ that should be declared. */
+ int name_index = JPOOL_USHORT1 (jcf, self);
+ int len;
+ unsigned char *s;
+
+ s = JPOOL_UTF_DATA (jcf, name_index);
+ len = JPOOL_UTF_LENGTH (jcf, name_index);
+ add_namelet (s, s + len, &root);
- if (seen_one)
- fputs ("\n", out);
+ if (root.subnamelets)
+ {
+ fputs ("extern \"Java\"\n{\n", out);
+ /* We use an initial offset of 0 because the root namelet
+ doesn't cause anything to print. */
+ print_namelet (out, &root, 0);
+ fputs ("};\n\n", out);
+ }
}
+
+
static void
DEFUN(process_file, (jcf, out),
JCF *jcf AND FILE *out)
{
int code, i;
+ uint32 field_start, method_end, method_start;
current_jcf = main_jcf = jcf;
- last_access = 0;
+ last_access = -1;
if (jcf_parse_preamble (jcf) != 0)
{
@@ -643,49 +1134,64 @@ DEFUN(process_file, (jcf, out),
jcf_parse_class (jcf);
- if (written_class_count++ == 0)
+ if (written_class_count++ == 0 && out)
fputs ("// DO NOT EDIT THIS FILE - it is machine generated -*- c++ -*-\n\n",
out);
- print_mangled_classname (out, jcf, "#ifndef __", jcf->this_class);
- fprintf (out, "__\n");
+ if (out)
+ {
+ print_mangled_classname (out, jcf, "#ifndef __", jcf->this_class);
+ fprintf (out, "__\n");
+
+ print_mangled_classname (out, jcf, "#define __", jcf->this_class);
+ fprintf (out, "__\n\n");
- print_mangled_classname (out, jcf, "#define __", jcf->this_class);
- fprintf (out, "__\n\n");
+ /* We do this to ensure that inline methods won't be `outlined'
+ by g++. This works as long as method and fields are not
+ added by the user. */
+ fprintf (out, "#pragma interface\n");
+ }
- if (jcf->super_class)
+ if (jcf->super_class && out)
{
int super_length;
unsigned char *supername = super_class_name (jcf, &super_length);
- fputs ("#include <", out);
- jcf_print_utf8 (out, supername, super_length);
- fputs (".h>\n", out);
-
- /* FIXME: If our superclass is Object, then we include
- java-array.h. The right thing to do here is look at all the
- methods and fields and see if an array is in use. Only then
- would we need to include java-array.h. */
- if (! utf8_cmp (supername, super_length, "java/lang/Object"))
- fputs ("#include <java-array.h>\n", out);
-
fputs ("\n", out);
+ print_include (out, supername, super_length);
}
- print_class_decls (out, jcf);
+ /* We want to parse the methods first. But we need to find where
+ they start. So first we skip the fields, then parse the methods.
+ Then we parse the fields and skip the methods. This is ugly, but
+ not too bad since we need two full passes to get class decl
+ information anyway. */
+ field_pass = 0;
+ field_start = JCF_TELL (jcf);
+ jcf_parse_fields (jcf);
+
+ method_start = JCF_TELL (jcf);
+ method_pass = 0;
+ jcf_parse_methods (jcf);
+
+ if (out)
+ {
+ fputs ("\n", out);
+ print_class_decls (out, jcf, jcf->this_class);
- for (i = 0; i < prepend_count; ++i)
- fprintf (out, "%s\n", prepend_specs[i]);
- if (prepend_count > 0)
- fputc ('\n', out);
+ for (i = 0; i < prepend_count; ++i)
+ fprintf (out, "%s\n", prepend_specs[i]);
+ if (prepend_count > 0)
+ fputc ('\n', out);
+ }
- if (! print_cxx_classname (out, "class ", jcf, jcf->this_class))
+ if (out && ! print_cxx_classname (out, "class ", jcf, jcf->this_class))
{
fprintf (stderr, "class is of array type\n");
found_error = 1;
return;
}
- if (jcf->super_class)
+ if (out && jcf->super_class)
{
if (! print_cxx_classname (out, " : public ", jcf, jcf->super_class))
{
@@ -694,50 +1200,61 @@ DEFUN(process_file, (jcf, out),
return;
}
}
- fputs ("\n{\n", out);
+ if (out)
+ fputs ("\n{\n", out);
- /* We make a single pass over the file, printing methods and fields
- as we see them. We have to list the methods in the same order
- that they appear in the class file, so that the Java and C++
- vtables have the same layout. */
- jcf_parse_fields (jcf);
+ /* Now go back for second pass over methods and fields. */
+ JCF_SEEK (jcf, method_start);
+ method_pass = 1;
jcf_parse_methods (jcf);
+ method_end = JCF_TELL (jcf);
+
+ field_pass = 1;
+ JCF_SEEK (jcf, field_start);
+ jcf_parse_fields (jcf);
+ JCF_SEEK (jcf, method_end);
+
jcf_parse_final_attributes (jcf);
- /* Generate friend decl if we still must. */
- for (i = 0; i < friend_count; ++i)
- fprintf (out, " friend %s\n", friend_specs[i]);
+ if (out)
+ {
+ /* Generate friend decl if we still must. */
+ for (i = 0; i < friend_count; ++i)
+ fprintf (out, " friend %s\n", friend_specs[i]);
- /* Generate extra declarations. */
- if (add_count > 0)
- fputc ('\n', out);
- for (i = 0; i < add_count; ++i)
- fprintf (out, " %s\n", add_specs[i]);
+ /* Generate extra declarations. */
+ if (add_count > 0)
+ fputc ('\n', out);
+ for (i = 0; i < add_count; ++i)
+ fprintf (out, " %s\n", add_specs[i]);
- fputs ("};\n", out);
+ fputs ("};\n", out);
- if (append_count > 0)
- fputc ('\n', out);
- for (i = 0; i < append_count; ++i)
- fprintf (out, "%s\n", append_specs[i]);
+ if (append_count > 0)
+ fputc ('\n', out);
+ for (i = 0; i < append_count; ++i)
+ fprintf (out, "%s\n", append_specs[i]);
- print_mangled_classname (out, jcf, "\n#endif /* __", jcf->this_class);
- fprintf (out, "__ */\n");
+ print_mangled_classname (out, jcf, "\n#endif /* __", jcf->this_class);
+ fprintf (out, "__ */\n");
+ }
}
static void
usage ()
{
- fprintf (stderr, "gjavah: no classes specified\n");
+ fprintf (stderr, "gcjh: no classes specified\n");
exit (1);
}
static void
help ()
{
- printf ("Usage: gjavah [OPTION]... CLASS...\n\n");
+ printf ("Usage: gcjh [OPTION]... CLASS...\n\n");
printf ("Generate C++ header files from .class files\n\n");
printf (" --classpath PATH Set path to find .class files\n");
+ printf (" --CLASSPATH PATH Set path to find .class files\n");
+ printf (" -IDIR Append directory to class path\n");
printf (" -d DIRECTORY Set output directory name\n");
printf (" --help Print this help, then exit\n");
printf (" -o FILE Set output file name\n");
@@ -752,7 +1269,7 @@ static void
java_no_argument (opt)
char *opt;
{
- fprintf (stderr, "gjavah: no argument given for option `%s'\n", opt);
+ fprintf (stderr, "gcjh: no argument given for option `%s'\n", opt);
exit (1);
}
@@ -760,7 +1277,7 @@ static void
version ()
{
/* FIXME: use version.c? */
- printf ("gjavah (GNU gcc) 0.0\n\n");
+ printf ("gcjh (GNU gcc) 0.0\n\n");
printf ("Copyright (C) 1998 Free Software Foundation, Inc.\n");
printf ("This is free software; see the source for copying conditions. There is NO\n");
printf ("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n");
@@ -773,10 +1290,14 @@ DEFUN(main, (argc, argv),
{
JCF jcf;
int argi;
+ char *output_file = NULL;
+ int emit_dependencies = 0, suppress_output = 0;
if (argc <= 1)
usage ();
+ jcf_path_init ();
+
for (argi = 1; argi < argc; argi++)
{
char *arg = argv[argi];
@@ -856,10 +1377,19 @@ DEFUN(main, (argc, argv),
else if (strcmp (arg, "-classpath") == 0)
{
if (argi + 1 < argc)
- classpath = argv[++argi];
+ jcf_path_classpath_arg (argv[++argi]);
+ else
+ java_no_argument (argv[argi]);
+ }
+ else if (strcmp (arg, "-CLASSPATH") == 0)
+ {
+ if (argi + 1 < argc)
+ jcf_path_CLASSPATH_arg (argv[++argi]);
else
java_no_argument (argv[argi]);
}
+ else if (strncmp (arg, "-I", 2) == 0)
+ jcf_path_include_arg (arg + 2);
else if (strcmp (arg, "-verbose") == 0 || strcmp (arg, "-v") == 0)
verbose++;
else if (strcmp (arg, "-stubs") == 0)
@@ -868,6 +1398,33 @@ DEFUN(main, (argc, argv),
help ();
else if (strcmp (arg, "-version") == 0)
version ();
+ else if (strcmp (arg, "-M") == 0)
+ {
+ emit_dependencies = 1;
+ suppress_output = 1;
+ jcf_dependency_init (1);
+ }
+ else if (strcmp (arg, "-MM") == 0)
+ {
+ emit_dependencies = 1;
+ suppress_output = 1;
+ jcf_dependency_init (0);
+ }
+ else if (strcmp (arg, "-MG") == 0)
+ {
+ fprintf (stderr, "gcjh: `%s' option is unimplemented\n", argv[argi]);
+ exit (1);
+ }
+ else if (strcmp (arg, "-MD") == 0)
+ {
+ emit_dependencies = 1;
+ jcf_dependency_init (1);
+ }
+ else if (strcmp (arg, "-MMD") == 0)
+ {
+ emit_dependencies = 1;
+ jcf_dependency_init (0);
+ }
else
{
fprintf (stderr, "%s: illegal argument\n", argv[argi]);
@@ -878,11 +1435,12 @@ DEFUN(main, (argc, argv),
if (argi == argc)
usage ();
- if (classpath == NULL)
+ jcf_path_seal ();
+
+ if (output_file && emit_dependencies)
{
- classpath = (char *) getenv ("CLASSPATH");
- if (classpath == NULL)
- classpath = "";
+ fprintf (stderr, "gcjh: can't specify both -o and -MD\n");
+ exit (1);
}
for (; argi < argc; argi++)
@@ -892,7 +1450,9 @@ DEFUN(main, (argc, argv),
if (verbose)
fprintf (stderr, "Processing %s\n", classname);
- classfile_name = find_class (classname, strlen (classname), &jcf, 1);
+ if (! output_file)
+ jcf_dependency_reset ();
+ classfile_name = find_class (classname, strlen (classname), &jcf, 0);
if (classfile_name == NULL)
{
fprintf (stderr, "%s: no such class\n", classname);
@@ -905,7 +1465,9 @@ DEFUN(main, (argc, argv),
if (strcmp (output_file, "-") == 0)
out = stdout;
else if (out == NULL)
- out = fopen (output_file, "w");
+ {
+ out = fopen (output_file, "w");
+ }
if (out == NULL)
{
perror (output_file);
@@ -928,18 +1490,38 @@ DEFUN(main, (argc, argv),
ch = '/';
current_output_file[dir_len++] = ch;
}
- strcpy (current_output_file+dir_len, ".h");
- out = fopen (current_output_file, "w");
- if (out == NULL)
+ if (emit_dependencies)
{
- perror (current_output_file);
- exit (1);
+ if (suppress_output)
+ {
+ jcf_dependency_set_dep_file ("-");
+ out = NULL;
+ }
+ else
+ {
+ /* We use `.hd' and not `.d' to avoid clashes with
+ dependency tracking from straight compilation. */
+ strcpy (current_output_file + dir_len, ".hd");
+ jcf_dependency_set_dep_file (current_output_file);
+ }
+ }
+ strcpy (current_output_file + dir_len, ".h");
+ jcf_dependency_set_target (current_output_file);
+ if (! suppress_output)
+ {
+ out = fopen (current_output_file, "w");
+ if (out == NULL)
+ {
+ perror (current_output_file);
+ exit (1);
+ }
}
}
process_file (&jcf, out);
JCF_FINISH (&jcf);
if (current_output_file != output_file)
free (current_output_file);
+ jcf_dependency_write ();
}
if (out != NULL && out != stdout)