/* Program to write C++-suitable header files from a Java(TM) .class
file. This is similar to SUN's javah.
-Copyright (C) 1996, 1998, 1999, 2000 Free Software Foundation, Inc.
+Copyright (C) 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004
+Free Software Foundation, Inc.
-This program is free software; you can redistribute it and/or modify
+This file is part of GCC.
+
+GCC 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.
-This program is distributed in the hope that it will be useful,
+GCC 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
+along with GCC; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
#include "config.h"
#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
#include <math.h>
#include "jcf.h"
#include "tree.h"
+#include "version.h"
#include "javaop.h"
#include "java-tree.h"
#include "java-opcodes.h"
+#include "ggc.h"
+#include "hashtab.h"
+#include "intl.h"
#include <getopt.h>
static int found_error = 0;
/* Nonzero if we're generating JNI output. */
-static int flag_jni = 0;
+int flag_jni = 0;
+
+/* When nonzero, warn when source file is newer than matching class
+ file. */
+int flag_newer = 1;
/* Directory to place resulting files in. Set by -d option. */
const char *output_directory = "";
If 0 then we haven't previously examined any field. */
static JCF_u2 last_access;
-#define ACC_VISIBILITY (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED)
-
/* 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) \
int length;
unsigned char *signature;
int sig_length;
+ int is_native;
struct method_name *next;
};
/* List of method names we've seen. */
static struct method_name *method_name_list;
-static void print_field_info PARAMS ((FILE*, JCF*, int, int, JCF_u2));
-static void print_mangled_classname PARAMS ((FILE*, JCF*, const char*, int));
-static int print_cxx_classname PARAMS ((FILE*, const char*, JCF*, int));
-static void print_method_info PARAMS ((FILE*, JCF*, int, int, JCF_u2));
-static void print_c_decl PARAMS ((FILE*, JCF*, int, int, int, const char *,
- int));
-static void print_stub_or_jni PARAMS ((FILE*, JCF*, int, int, int,
- const char *, int));
-static void print_full_cxx_name PARAMS ((FILE*, JCF*, int, int, int,
- const char *, int));
-static void decompile_method PARAMS ((FILE*, JCF*, int));
-static void add_class_decl PARAMS ((FILE*, JCF*, JCF_u2));
-
-static int java_float_finite PARAMS ((jfloat));
-static int java_double_finite PARAMS ((jdouble));
-static void print_name PARAMS ((FILE *, JCF *, int));
-static void print_base_classname PARAMS ((FILE *, JCF *, int));
-static int utf8_cmp PARAMS ((const unsigned char *, int, const char *));
-static const char *cxx_keyword_subst PARAMS ((const unsigned char *, int));
-static void generate_access PARAMS ((FILE *, JCF_u2));
-static int name_is_method_p PARAMS ((const unsigned char *, int));
-static char *get_field_name PARAMS ((JCF *, int, JCF_u2));
-static void print_field_name PARAMS ((FILE *, JCF *, int, JCF_u2));
-static const unsigned char *super_class_name PARAMS ((JCF *, int *));
-static void print_include PARAMS ((FILE *, const unsigned char *, int));
-static const unsigned char *decode_signature_piece
- PARAMS ((FILE *, const unsigned char *, const unsigned char *, int *));
-static void print_class_decls PARAMS ((FILE *, JCF *, int));
-static void usage PARAMS ((void)) ATTRIBUTE_NORETURN;
-static void help PARAMS ((void)) ATTRIBUTE_NORETURN;
-static void version PARAMS ((void)) ATTRIBUTE_NORETURN;
-static int overloaded_jni_method_exists_p PARAMS ((const unsigned char *, int,
- const char *, int));
+static void print_field_info (FILE*, JCF*, int, int, JCF_u2);
+static void print_mangled_classname (FILE*, JCF*, const char*, int);
+static int print_cxx_classname (FILE*, const char*, JCF*, int, int);
+static void print_method_info (FILE*, JCF*, int, int, JCF_u2);
+static void print_c_decl (FILE*, JCF*, int, int, int, const char *, int);
+static void print_stub_or_jni (FILE*, JCF*, int, int, int, const char *, int);
+static void print_full_cxx_name (FILE*, JCF*, int, int, int, const char *, int);
+static void decompile_method (FILE*, JCF*, int);
+static void add_class_decl (FILE*, JCF*, JCF_u2);
+
+static void print_name (FILE *, JCF *, int);
+static void print_base_classname (FILE *, JCF *, int);
+static int utf8_cmp (const unsigned char *, int, const char *);
+static char *cxx_keyword_subst (const unsigned char *, int);
+static void generate_access (FILE *, JCF_u2);
+static int name_is_method_p (const unsigned char *, int);
+static char *get_field_name (JCF *, int, JCF_u2);
+static void print_field_name (FILE *, JCF *, int, JCF_u2);
+static const unsigned char *super_class_name (JCF *, int *);
+static void print_include (FILE *, const unsigned char *, int);
+static int gcjh_streq (const void *p1, const void *p2);
+static int throwable_p (const unsigned char *signature);
+static const unsigned char *
+ decode_signature_piece (FILE *, const unsigned char *,
+ const unsigned char *, int *);
+static void print_class_decls (FILE *, JCF *, int);
+static void error (const char *msgid, ...);
+static void usage (void) ATTRIBUTE_NORETURN;
+static void help (void) ATTRIBUTE_NORETURN;
+static void version (void) ATTRIBUTE_NORETURN;
+static int overloaded_jni_method_exists_p (const unsigned char *, int,
+ const char *, int);
+static void jni_print_char (FILE *, int);
+static void jni_print_float (FILE *, jfloat);
+static void jni_print_double (FILE *, jdouble);
+static void decompile_return_statement (FILE *, JCF *, int, int, int);
+
+static void handle_inner_classes (int);
JCF_u2 current_field_name;
JCF_u2 current_field_value;
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 if (flag_jni) \
- print_method_info (NULL, jcf, NAME, SIGNATURE, ACCESS_FLAGS); \
- else if (! stubs) add_class_decl (out, jcf, SIGNATURE);
-
-#define HANDLE_CODE_ATTRIBUTE(MAX_STACK, MAX_LOCALS, CODE_LENGTH) \
+static int method_synthetic = 0;
+static int method_signature = 0;
+
+/* Set to 1 while the very first data member of a class is being handled. */
+static int is_first_data_member = 0;
+
+#define HANDLE_METHOD(ACCESS_FLAGS, NAME, SIGNATURE, ATTRIBUTE_COUNT) \
+ { \
+ method_synthetic = 0; \
+ method_printed = 0; \
+ decompiled = 0; \
+ method_signature = SIGNATURE; \
+ if (ATTRIBUTE_COUNT) \
+ method_synthetic = peek_attribute (jcf, ATTRIBUTE_COUNT, \
+ (const char *)"Synthetic", 9); \
+ /* If a synthetic methods have been declared, its attribute aren't \
+ worth reading (and triggering side-effects). We skip them an \
+ set ATTRIBUTE_COUNT to zero so that they'll be skipped in \
+ jcf_parse_one_method. */ \
+ if (method_synthetic) \
+ { \
+ skip_attribute (jcf, ATTRIBUTE_COUNT); \
+ ATTRIBUTE_COUNT = 0; \
+ } \
+ if (method_pass && !method_synthetic) \
+ { \
+ if (out) \
+ print_method_info (out, jcf, NAME, SIGNATURE, \
+ ACCESS_FLAGS); \
+ } \
+ else if (!method_synthetic) \
+ { \
+ print_method_info (NULL, jcf, NAME, SIGNATURE, \
+ ACCESS_FLAGS); \
+ if (! stubs && ! flag_jni) \
+ 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 || stubs ? "\n" : ";\n", out);
+#define HANDLE_END_METHOD() \
+ if (out && method_printed && !method_synthetic) \
+ fputs (decompiled || stubs ? "\n" : ";\n", out);
-#include "jcf-reader.c"
+#define HANDLE_INNERCLASSES_ATTRIBUTE(COUNT) handle_inner_classes (COUNT)
-/* Some useful constants. */
-#define F_NAN_MASK 0x7f800000
-#if (1 == HOST_FLOAT_WORDS_BIG_ENDIAN)
-#define D_NAN_MASK 0x000000007ff00000LL
-#else
-#define D_NAN_MASK 0x7ff0000000000000LL
-#endif
+/* We're going to need {peek,skip}_attribute, enable their definition. */
+#define NEED_PEEK_ATTRIBUTE
+#define NEED_SKIP_ATTRIBUTE
-/* Return 1 if F is not Inf or NaN. */
-static int
-java_float_finite (f)
- jfloat f;
+#include "jcf-reader.c"
+
+/* Print an error message and set found_error. */
+static void
+error (const char *msgid, ...)
{
- union Word u;
- u.f = f;
+ va_list ap;
+
+ va_start (ap, msgid);
- /* 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 (u.i & F_NAN_MASK) != F_NAN_MASK;
+ fprintf (stderr, "gcjh: ");
+ vfprintf (stderr, _(msgid), ap);
+ va_end (ap);
+ fprintf (stderr, "\n");
+ found_error = 1;
}
-/* Return 1 if D is not Inf or NaN. */
-static int
-java_double_finite (d)
- jdouble d;
+/* Print a single-precision float, suitable for parsing by g++. */
+static void
+jni_print_float (FILE *stream, jfloat f)
{
- union DWord u;
- u.d = d;
+ /* It'd be nice to use __builtin_nan/__builtin_inf here but they don't
+ work in data initializers. FIXME. */
+ if (JFLOAT_FINITE (f))
+ {
+ fputs (" = ", stream);
+ if (f.negative)
+ putc ('-', stream);
+ if (f.exponent)
+ fprintf (stream, "0x1.%.6xp%+df",
+ ((unsigned int)f.mantissa) << 1,
+ f.exponent - JFLOAT_EXP_BIAS);
+ else
+ /* Exponent of 0x01 is -125; exponent of 0x00 is *also* -125,
+ because the implicit leading 1 bit is no longer present. */
+ fprintf (stream, "0x0.%.6xp%+df",
+ ((unsigned int)f.mantissa) << 1,
+ f.exponent + 1 - JFLOAT_EXP_BIAS);
+ }
+ fputs (";\n", stream);
+}
- /* Now check for all NaNs. */
- return (u.l & D_NAN_MASK) != D_NAN_MASK;
+/* Print a double-precision float, suitable for parsing by g++. */
+static void
+jni_print_double (FILE *stream, jdouble f)
+{
+ /* It'd be nice to use __builtin_nan/__builtin_inf here but they don't
+ work in data initializers. FIXME. */
+ if (JDOUBLE_FINITE (f))
+ {
+ fputs (" = ", stream);
+ if (f.negative)
+ putc ('-', stream);
+ if (f.exponent)
+ fprintf (stream, "0x1.%.5x%.8xp%+d",
+ f.mantissa0, f.mantissa1,
+ f.exponent - JDOUBLE_EXP_BIAS);
+ else
+ /* Exponent of 0x001 is -1022; exponent of 0x000 is *also* -1022,
+ because the implicit leading 1 bit is no longer present. */
+ fprintf (stream, "0x0.%.5x%.8xp%+d",
+ f.mantissa0, f.mantissa1,
+ f.exponent + 1 - JDOUBLE_EXP_BIAS);
+ }
+ fputs (";\n", stream);
}
+/* Print a character, appropriately mangled for JNI. */
+
static void
-DEFUN(print_name, (stream, jcf, name_index),
- FILE* stream AND JCF* jcf AND int name_index)
+jni_print_char (FILE *stream, int ch)
{
- if (JPOOL_TAG (jcf, name_index) != CONSTANT_Utf8)
- fprintf (stream, "<not a UTF8 constant>");
+ if (! flag_jni)
+ jcf_print_char (stream, ch);
+ else if (ch == '(' || ch == ')')
+ {
+ /* Ignore. */
+ }
+ else if (ch == '_')
+ fputs ("_1", stream);
+ else if (ch == ';')
+ fputs ("_2", stream);
+ else if (ch == '[')
+ fputs ("_3", stream);
+ else if (ch == '/')
+ fputs ("_", stream);
+ else if (ISALNUM (ch))
+ fputc (ch, stream);
else
+ {
+ /* "Unicode" character. */
+ fprintf (stream, "_0%04x", ch);
+ }
+}
+
+/* Print a name from the class data. If the index does not point to a
+ string, an error results. */
+
+static void
+print_name (FILE* stream, JCF* jcf, int name_index)
+{
+ if (JPOOL_TAG (jcf, name_index) != CONSTANT_Utf8)
+ {
+ fprintf (stream, "<not a UTF8 constant>");
+ found_error = 1;
+ }
+ else if (! flag_jni)
jcf_print_utf8 (stream, JPOOL_UTF_DATA (jcf, name_index),
JPOOL_UTF_LENGTH (jcf, name_index));
+ else
+ {
+ /* For JNI we must correctly quote each character. */
+ const unsigned char *str = JPOOL_UTF_DATA (jcf, name_index);
+ int length = JPOOL_UTF_LENGTH (jcf, name_index);
+ const unsigned char *limit = str + length;
+ while (str < limit)
+ {
+ int ch = UTF8_GET (str, limit);
+ if (ch < 0)
+ {
+ fprintf (stream, "\\<invalid>");
+ return;
+ }
+ jni_print_char (stream, ch);
+ }
+ }
}
/* Print base name of class. The base name is everything after the
final separator. */
static void
-print_base_classname (stream, jcf, index)
- FILE *stream;
- JCF *jcf;
- int index;
+print_base_classname (FILE *stream, JCF *jcf, int index)
{
int name_index = JPOOL_USHORT1 (jcf, index);
int len;
}
}
-/* Return 0 if NAME is equal to STR, nonzero otherwise. */
+/* Return 0 if NAME is equal to STR, -1 if STR is "less" than NAME,
+ and 1 if STR is "greater" than NAME. */
static int
-utf8_cmp (str, length, name)
- const unsigned char *str;
- int length;
- const char *name;
+utf8_cmp (const unsigned char *str, int length, const char *name)
{
const unsigned char *limit = str + length;
int i;
{
int ch = UTF8_GET (str, limit);
if (ch != name[i])
- return 1;
+ return ch - name[i];
}
- return str != limit;
+ return str == limit ? 0 : 1;
}
+/* This is a sorted list of all C++ keywords. */
+
+static const char *const cxx_keywords[] =
+{
+ "_Complex",
+ "__alignof",
+ "__alignof__",
+ "__asm",
+ "__asm__",
+ "__attribute",
+ "__attribute__",
+ "__builtin_va_arg",
+ "__complex",
+ "__complex__",
+ "__const",
+ "__const__",
+ "__extension__",
+ "__imag",
+ "__imag__",
+ "__inline",
+ "__inline__",
+ "__label__",
+ "__null",
+ "__real",
+ "__real__",
+ "__restrict",
+ "__restrict__",
+ "__signed",
+ "__signed__",
+ "__typeof",
+ "__typeof__",
+ "__volatile",
+ "__volatile__",
+ "and",
+ "and_eq",
+ "asm",
+ "auto",
+ "bitand",
+ "bitor",
+ "bool",
+ "break",
+ "case",
+ "catch",
+ "char",
+ "class",
+ "compl",
+ "const",
+ "const_cast",
+ "continue",
+ "default",
+ "delete",
+ "do",
+ "double",
+ "dynamic_cast",
+ "else",
+ "enum",
+ "explicit",
+ "export",
+ "extern",
+ "false",
+ "float",
+ "for",
+ "friend",
+ "goto",
+ "if",
+ "inline",
+ "int",
+ "long",
+ "mutable",
+ "namespace",
+ "new",
+ "not",
+ "not_eq",
+ "operator",
+ "or",
+ "or_eq",
+ "private",
+ "protected",
+ "public",
+ "register",
+ "reinterpret_cast",
+ "return",
+ "short",
+ "signed",
+ "sizeof",
+ "static",
+ "static_cast",
+ "struct",
+ "switch",
+ "template",
+ "this",
+ "throw",
+ "true",
+ "try",
+ "typedef",
+ "typeid",
+ "typename",
+ "typeof",
+ "union",
+ "unsigned",
+ "using",
+ "virtual",
+ "void",
+ "volatile",
+ "wchar_t",
+ "while",
+ "xor",
+ "xor_eq"
+};
+
+
/* If NAME is the name of a C++ keyword, then return an override name.
This is a name that can be used in place of the keyword.
- Otherwise, return NULL. FIXME: for now, we only handle those
- keywords we know to be a problem for libgcj. */
+ Otherwise, return NULL. The return value is malloc()d. */
-static const char *
-cxx_keyword_subst (str, length)
- const unsigned char *str;
- int length;
+static char *
+cxx_keyword_subst (const unsigned char *str, int length)
{
- if (! utf8_cmp (str, length, "delete"))
- return "__dummy_delete";
- else if (! utf8_cmp (str, length, "enum"))
- return "__dummy_enum";
+ int last = ARRAY_SIZE (cxx_keywords);
+ int first = 0;
+ int mid = (last + first) / 2;
+ int old = -1;
+
+ for (mid = (last + first) / 2;
+ mid != old;
+ old = mid, mid = (last + first) / 2)
+ {
+ int kwl = strlen (cxx_keywords[mid]);
+ int min_length = kwl > length ? length : kwl;
+ int r = utf8_cmp (str, min_length, cxx_keywords[mid]);
+
+ if (r == 0)
+ {
+ int i;
+
+ /* Skip all trailing `$'. */
+ for (i = min_length; i < length && str[i] == '$'; ++i)
+ ;
+ /* We've only found a match if all the remaining characters
+ are `$'. */
+ if (i == length)
+ {
+ char *dup = xmalloc (2 + length - min_length + kwl);
+ strcpy (dup, cxx_keywords[mid]);
+ for (i = kwl; i < length + 1; ++i)
+ dup[i] = '$';
+ dup[i] = '\0';
+ return dup;
+ }
+ r = 1;
+ }
+
+ if (r < 0)
+ last = mid;
+ else
+ first = mid;
+ }
return NULL;
}
/* Generate an access control keyword based on FLAGS. */
static void
-generate_access (stream, flags)
- FILE *stream;
- JCF_u2 flags;
+generate_access (FILE *stream, JCF_u2 flags)
{
if ((flags & ACC_VISIBILITY) == last_access)
return;
/* See if NAME is already the name of a method. */
static int
-name_is_method_p (name, length)
- const unsigned char *name;
- int length;
+name_is_method_p (const unsigned char *name, int length)
{
struct method_name *p;
return 0;
}
-/* If there is already a method named NAME, whose signature is not
+/* Free the method name list. */
+static void
+free_method_name_list ()
+{
+ struct method_name *p = method_name_list;
+ while (p != NULL)
+ {
+ struct method_name *next = p->next;
+ free (p->name);
+ free (p->signature);
+ free (p);
+ p = next;
+ }
+ method_name_list = NULL;
+}
+
+/* If there is already a native method named NAME, whose signature is not
SIGNATURE, then return true. Otherwise return false. */
static int
-overloaded_jni_method_exists_p (name, length, signature, sig_length)
- const unsigned char *name;
- int length;
- const char *signature;
- int sig_length;
+overloaded_jni_method_exists_p (const unsigned char *name, int length,
+ const char *signature, int sig_length)
{
struct method_name *p;
for (p = method_name_list; p != NULL; p = p->next)
{
- if (p->length == length
+ if (p->is_native
+ && p->length == length
&& ! memcmp (p->name, name, length)
&& (p->sig_length != sig_length
|| memcmp (p->signature, signature, sig_length)))
/* Get name of a field. This handles renamings due to C++ clash. */
static char *
-get_field_name (jcf, name_index, flags)
- JCF *jcf;
- int name_index;
- JCF_u2 flags;
+get_field_name (JCF *jcf, int name_index, JCF_u2 flags)
{
unsigned char *name = JPOOL_UTF_DATA (jcf, name_index);
int length = JPOOL_UTF_LENGTH (jcf, name_index);
char *override;
- const char *tmpconstptr;
-
if (name_is_method_p (name, length))
{
trouble. */
if ((flags & ACC_STATIC))
{
- fprintf (stderr, "static field has same name as method\n");
- found_error = 1;
+ error ("static field has same name as method");
return NULL;
}
memcpy (override, name, length);
strcpy (override + length, "__");
}
- else if ((tmpconstptr = cxx_keyword_subst (name, length)) != NULL)
- {
- /* Must malloc OVERRIDE. */
- override = xstrdup (tmpconstptr);
- }
else
- override = NULL;
-
+ override = cxx_keyword_subst (name, length);
+
return override;
}
/* Print a field name. Convenience function for use with
get_field_name. */
static void
-print_field_name (stream, jcf, name_index, flags)
- FILE *stream;
- JCF *jcf;
- int name_index;
- JCF_u2 flags;
+print_field_name (FILE *stream, JCF *jcf, int name_index, JCF_u2 flags)
{
char *override = get_field_name (jcf, name_index, flags);
}
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)
+print_field_info (FILE *stream, JCF* jcf, int name_index, int sig_index,
+ JCF_u2 flags)
{
char *override = NULL;
fputs (" ", out);
if ((flags & ACC_STATIC))
- fputs ("static ", out);
-
- if ((flags & ACC_FINAL))
{
- if (current_field_value > 0)
+ fputs ("static ", out);
+
+ if ((flags & ACC_FINAL) && current_field_value > 0)
{
char buffer[25];
int done = 1;
jfloat fnum = JPOOL_FLOAT (jcf, current_field_value);
fputs ("const jfloat ", out);
print_field_name (out, jcf, name_index, 0);
- if (! java_float_finite (fnum))
- fputs (";\n", out);
- else
- fprintf (out, " = %.10g;\n", fnum);
+ jni_print_float (out, fnum);
}
break;
case CONSTANT_Double:
jdouble dnum = JPOOL_DOUBLE (jcf, current_field_value);
fputs ("const jdouble ", out);
print_field_name (out, jcf, name_index, 0);
- if (! java_double_finite (dnum))
- fputs (";\n", out);
- else
- fprintf (out, " = %.17g;\n", dnum);
+ jni_print_double (out, dnum);
}
break;
default:
free (override);
}
+
static void
-DEFUN(print_method_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)
+print_method_info (FILE *stream, JCF* jcf, int name_index, int sig_index,
+ JCF_u2 flags)
{
const unsigned char *str;
int length, is_init = 0;
- const char *override = NULL;
+ char *override = NULL;
method_declared = 0;
method_access = flags;
fprintf (stream, "<not a UTF8 constant>");
str = JPOOL_UTF_DATA (jcf, name_index);
length = JPOOL_UTF_LENGTH (jcf, name_index);
- if (str[0] == '<' || str[0] == '$')
+
+ if (str[0] == '<')
{
- /* Ignore internally generated methods like <clinit> and
- $finit$. However, treat <init> as a constructor. */
+ /* Ignore the internally generated method <clinit>. However,
+ treat <init> as a constructor. */
if (! utf8_cmp (str, length, "<init>"))
is_init = 1;
else if (! METHOD_IS_FINAL (jcf->access_flags, flags)
{
/* FIXME: i18n bug here. Order of prints should not be
fixed. */
- fprintf (stderr, "ignored method `");
+ fprintf (stderr, _("ignored method `"));
jcf_print_utf8 (stderr, str, length);
- fprintf (stderr, "' marked virtual\n");
+ fprintf (stderr, _("' marked virtual\n"));
found_error = 1;
return;
}
else
return;
}
- else
+
+ /* During the first method pass, build a list of method names. This will
+ be used to determine if field names conflict with method names. */
+ if (! stream)
{
struct method_name *nn;
- nn = (struct method_name *) xmalloc (sizeof (struct method_name));
- nn->name = (char *) xmalloc (length);
+ nn = xmalloc (sizeof (struct method_name));
+ nn->name = xmalloc (length);
memcpy (nn->name, str, length);
nn->length = length;
nn->next = method_name_list;
nn->sig_length = JPOOL_UTF_LENGTH (jcf, sig_index);
- nn->signature = (char *) xmalloc (nn->sig_length);
+ nn->signature = xmalloc (nn->sig_length);
+ nn->is_native = METHOD_IS_NATIVE (flags);
memcpy (nn->signature, JPOOL_UTF_DATA (jcf, sig_index),
nn->sig_length);
method_name_list = nn;
+
+ /* The rest of this function doesn't matter. */
+ return;
}
- /* If we're not printing, then the rest of this function doesn't
- matter. This happens during the first method pass in JNI mode.
- Eww. */
- if (! stream)
- return;
-
- /* We can't generate a method whose name is a C++ reserved word. 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. */
- override = cxx_keyword_subst (str, length);
- if (override)
+ /* We don't worry about overrides in JNI mode. */
+ if (! flag_jni)
{
- /* 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;
+ /* We can't generate a method whose name is a C++ reserved word.
+ 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. */
+ override = cxx_keyword_subst (str, length);
}
if (! stubs && ! flag_jni)
is_init, override, flags);
}
}
+
+ if (override)
+ free (override);
+}
+
+/* A helper for the decompiler which prints a `return' statement where
+ the type is a reference type. If METHODTYPE and OBJECTTYPE are not
+ identical, we emit a cast. We do this because the C++ compiler
+ doesn't know that a reference can be cast to the type of an
+ interface it implements. METHODTYPE is the index of the method's
+ signature. NAMEINDEX is the index of the field name; -1 for
+ `this'. OBJECTTYPE is the index of the object's type. */
+static void
+decompile_return_statement (FILE *out, JCF *jcf, int methodtype,
+ int nameindex, int objecttype)
+{
+ int cast = 0;
+ int obj_name_len, method_name_len;
+ const unsigned char *obj_data, *method_data;
+
+ obj_name_len = JPOOL_UTF_LENGTH (jcf, objecttype);
+ obj_data = JPOOL_UTF_DATA (jcf, objecttype);
+
+ method_name_len = JPOOL_UTF_LENGTH (jcf, methodtype);
+ method_data = JPOOL_UTF_DATA (jcf, methodtype);
+
+ /* Skip forward to return type part of method. */
+ while (*method_data != ')')
+ {
+ ++method_data;
+ --method_name_len;
+ }
+ /* Skip past `)'. */
+ ++method_data;
+ --method_name_len;
+
+ /* If we see an `L', skip it and the trailing `;'. */
+ if (method_data[0] == 'L' && method_data[method_name_len - 1] == ';')
+ {
+ ++method_data;
+ method_name_len -= 2;
+ }
+ if (obj_data[0] == 'L' && obj_data[obj_name_len - 1] == ';')
+ {
+ ++obj_data;
+ obj_name_len -= 2;
+ }
+
+ /* FIXME: if METHODTYPE is a superclass of OBJECTTYPE then we don't
+ need a cast. Right now there is no way to determine if this is
+ the case. */
+ if (method_name_len != obj_name_len)
+ cast = 1;
+ else
+ {
+ int i;
+ for (i = 0; i < method_name_len; ++i)
+ {
+ if (method_data[i] != obj_data[i])
+ {
+ cast = 1;
+ break;
+ }
+ }
+ }
+
+ fputs (" { return ", out);
+
+ if (cast)
+ {
+ int array_depth = 0;
+ const unsigned char *limit;
+
+ fputs ("reinterpret_cast<", out);
+
+ while (*method_data == '[')
+ {
+ ++method_data;
+ ++array_depth;
+ --method_name_len;
+ fputs ("JArray<", out);
+ }
+
+ /* Leading space to avoid C++ digraphs. */
+ fputs (" ::", out);
+
+ /* If we see an `L', skip it and the trailing `;'. Only do this
+ if we've seen an array specification. If we don't have an
+ array then the `L' was stripped earlier. */
+ if (array_depth && method_data[0] == 'L'
+ && method_data[method_name_len - 1] == ';')
+ {
+ ++method_data;
+ method_name_len -= 2;
+ }
+
+ limit = method_data + method_name_len;
+ while (method_data < limit)
+ {
+ int ch = UTF8_GET (method_data, limit);
+ if (ch == '/')
+ fputs ("::", out);
+ else
+ jcf_print_char (out, ch);
+ }
+ fputs (" *", out);
+
+ /* Close each array. */
+ while (array_depth > 0)
+ {
+ fputs ("> *", out);
+ --array_depth;
+ }
+
+ /* Close the cast. */
+ fputs ("> (", out);
+ }
+
+ if (nameindex == -1)
+ fputs ("this", out);
+ else
+ print_field_name (out, jcf, nameindex, 0);
+
+ if (cast)
+ fputs (")", out);
+
+ fputs ("; }", out);
}
+
/* 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;
+decompile_method (FILE *out, JCF *jcf, int code_len)
{
const unsigned char *codes = jcf->read_ptr;
int index;
|| 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);
+ if (codes[4] == OPCODE_areturn)
+ decompile_return_statement (out, jcf, method_signature,
+ name, JPOOL_USHORT2 (jcf, name_and_type));
+ else
+ {
+ fputs (" { return ", out);
+ /* FIXME: flags. */
+ print_field_name (out, jcf, name, 0);
+ fputs ("; }", out);
+ }
decompiled = 1;
}
else if (code_len == 2
&& codes[0] == OPCODE_aload_0
- && codes[1] == OPCODE_areturn)
+ && codes[1] == OPCODE_areturn
+ /* We're going to generate `return this'. This only makes
+ sense for non-static methods. */
+ && ! (method_access & ACC_STATIC))
{
- /* Found `return this'. */
- fputs (" { return this; }", out);
+ decompile_return_statement (out, jcf, method_signature, -1,
+ JPOOL_USHORT1 (jcf, jcf->this_class));
decompiled = 1;
}
else if (code_len == 1 && codes[0] == OPCODE_return)
}
}
+/* Like strcmp, but invert the return result for the hash table. This
+ should probably be in hashtab.c to complement the existing string
+ hash function. */
+static int
+gcjh_streq (const void *p1, const void *p2)
+{
+ return ! strcmp ((char *) p1, (char *) p2);
+}
+
+/* Return 1 if the initial part of CLNAME names a subclass of throwable,
+ or 0 if not. CLNAME may be extracted from a signature, and can be
+ terminated with either `;' or NULL. */
+static int
+throwable_p (const unsigned char *clname)
+{
+ int length;
+ unsigned char *current;
+ int i;
+ int result = 0;
+
+ /* We keep two hash tables of class names. In one we list all the
+ classes which are subclasses of Throwable. In the other we will
+ all other classes. We keep two tables to make the code a bit
+ simpler; we don't have to have a structure mapping class name to
+ a `throwable?' bit. */
+ static htab_t throw_hash;
+ static htab_t non_throw_hash;
+ static int init_done = 0;
+
+ if (! init_done)
+ {
+ void **slot;
+ unsigned char *str;
+
+ /* Self-initializing. The cost of this really doesn't matter.
+ We also don't care about freeing these, either. */
+ throw_hash = htab_create (10, htab_hash_string, gcjh_streq,
+ (htab_del) free);
+ non_throw_hash = htab_create (10, htab_hash_string, gcjh_streq,
+ (htab_del) free);
+
+ /* Make sure the root classes show up in the tables. */
+ str = (unsigned char *) xstrdup ("java.lang.Throwable");
+ slot = htab_find_slot (throw_hash, str, INSERT);
+ *slot = str;
+
+ str = (unsigned char *) xstrdup ("java.lang.Object");
+ slot = htab_find_slot (non_throw_hash, str, INSERT);
+ *slot = str;
+
+ init_done = 1;
+ }
+
+ for (length = 0; clname[length] != ';' && clname[length] != '\0'; ++length)
+ ;
+ current = ALLOC (length + 1);
+ for (i = 0; i < length; ++i)
+ current[i] = clname[i] == '/' ? '.' : clname[i];
+ current[length] = '\0';
+
+ /* We don't compute the hash slot here because the table might be
+ modified by the recursion. In that case the slot could be
+ invalidated. */
+ if (htab_find (throw_hash, current))
+ result = 1;
+ else if (htab_find (non_throw_hash, current))
+ result = 0;
+ else
+ {
+ JCF jcf;
+ void **slot;
+ unsigned char *super, *tmp;
+ int super_length = -1;
+ const char *classfile_name = find_class ((char *) current, strlen ((const char *) current),
+ &jcf, 0);
+
+ if (! classfile_name)
+ {
+ error ("couldn't find class %s", current);
+ return 0;
+ }
+ if (jcf_parse_preamble (&jcf) != 0
+ || jcf_parse_constant_pool (&jcf) != 0
+ || verify_constant_pool (&jcf) > 0)
+ {
+ error ("parse error while reading %s", classfile_name);
+ return 0;
+ }
+ jcf_parse_class (&jcf);
+
+ tmp = (unsigned char *) super_class_name (&jcf, &super_length);
+ super = ALLOC (super_length + 1);
+ memcpy (super, tmp, super_length);
+ super[super_length] = '\0';
+
+ result = throwable_p (super);
+ slot = htab_find_slot (result ? throw_hash : non_throw_hash,
+ current, INSERT);
+ *slot = current;
+ current = NULL;
+
+ JCF_FINISH (&jcf);
+ }
+
+ return result;
+}
+
/* Print one piece of a signature. Returns pointer to next parseable
character on success, NULL on error. */
static const unsigned char *
-decode_signature_piece (stream, signature, limit, need_space)
- FILE *stream;
- const unsigned char *signature, *limit;
- int *need_space;
+decode_signature_piece (FILE *stream, const unsigned char *signature,
+ const unsigned char *limit, int *need_space)
{
const char *ctype;
int array_depth = 0;
array_loop:
for (signature++; (signature < limit
- && *signature >= '0'
- && *signature <= '9'); signature++)
+ && ISDIGIT (*signature)); signature++)
;
switch (*signature)
{
/* If the previous iterations left us with something to print,
print it. For JNI, we always print `jobjectArray' in the
nested cases. */
- if (flag_jni && ctype == NULL)
+ if (flag_jni && (ctype == NULL || array_depth > 0))
{
ctype = "jobjectArray";
*need_space = 1;
case 'L':
if (flag_jni)
{
- /* We know about certain types and special-case their
- names.
- FIXME: something like java.lang.Exception should be
- printed as `jthrowable', because it is a subclass. This
- means that gcjh must read the entire hierarchy and
- comprehend it. */
- if (! strncmp (signature, "Ljava/lang/String;",
+ /* We know about certain types and special-case their names. */
+ if (! strncmp ((const char *) signature, "Ljava/lang/String;",
sizeof ("Ljava/lang/String;") -1))
ctype = "jstring";
- else if (! strncmp (signature, "Ljava/lang/Class;",
+ else if (! strncmp ((const char *) signature, "Ljava/lang/Class;",
sizeof ("Ljava/lang/Class;") - 1))
ctype = "jclass";
- else if (! strncmp (signature, "Ljava/lang/Throwable;",
- sizeof ("Ljava/lang/Throwable;") - 1))
+ /* Skip leading 'L' for throwable_p call. */
+ else if (throwable_p (signature + 1))
ctype = "jthrowable";
- else if (! strncmp (signature, "Ljava/lang/ref/WeakReference;",
- sizeof ("Ljava/lang/ref/WeakReference;") - 1))
- ctype = "jweak";
else
ctype = "jobject";
while (*signature && *signature != ';')
{
int ch = UTF8_GET (signature, limit);
- /* `$' is the separator for an inner class. */
- if (ch == '/' || ch == '$')
+ if (ch == '/')
fputs ("::", stream);
else
jcf_print_char (stream, ch);
break;
default:
*need_space = 1;
- jcf_print_char (stream, *signature++);
+ jni_print_char (stream, *signature++);
break;
printit:
signature++;
}
static void
-DEFUN(print_c_decl, (stream, jcf, name_index, signature_index, is_init,
- name_override, flags),
- FILE* stream AND JCF* jcf
- AND int name_index AND int signature_index
- AND int is_init AND const char *name_override AND int flags)
+print_c_decl (FILE* stream, JCF* jcf, int name_index, int signature_index,
+ int is_init, const char *name_override, int flags)
{
if (JPOOL_TAG (jcf, signature_index) != CONSTANT_Utf8)
{
{
int length = JPOOL_UTF_LENGTH (jcf, signature_index);
const unsigned char *str0 = JPOOL_UTF_DATA (jcf, signature_index);
- register const unsigned char *str = str0;
+ const unsigned char *str = str0;
const unsigned char *limit = str + length;
int need_space = 0;
int is_method = str[0] == '(';
next = decode_signature_piece (stream, str, limit, &need_space);
if (! next)
{
- fprintf (stderr, "unparseable signature: `%s'\n", str0);
- found_error = 1;
+ error ("unparseable signature: `%s'", str0);
return;
}
}
+ /* Force the alignment of the first data member. This is
+ because the "new" C++ ABI changed the alignment of non-POD
+ classes. gcj, however, still uses the "old" alignment. */
+ if (is_first_data_member && ! (flags & ACC_STATIC) && ! is_method)
+ {
+ is_first_data_member = 0;
+ print_cxx_classname (out, " __attribute__((aligned(__alignof__( ",
+ jcf, jcf->super_class, 1);
+ fputs (" )))) ", stream);
+ }
+
/* Now print the name of the thing. */
if (need_space)
fputs (" ", stream);
/* Print the unqualified method name followed by the signature. */
static void
-DEFUN(print_full_cxx_name, (stream, jcf, name_index, signature_index,
- is_init, name_override, flags),
- FILE* stream AND JCF* jcf
- AND int name_index AND int signature_index AND int is_init
- AND const char *name_override AND int flags)
+print_full_cxx_name (FILE* stream, JCF* jcf, int name_index,
+ int signature_index, int is_init,
+ const char *name_override, int flags)
{
int length = JPOOL_UTF_LENGTH (jcf, signature_index);
const unsigned char *str0 = JPOOL_UTF_DATA (jcf, signature_index);
- register const unsigned char *str = str0;
+ const unsigned char *str = str0;
const unsigned char *limit = str + length;
int need_space = 0;
int is_method = str[0] == '(';
int sig_len = JPOOL_UTF_LENGTH (jcf, signature_index);
if (overloaded_jni_method_exists_p (JPOOL_UTF_DATA (jcf, name_index),
JPOOL_UTF_LENGTH (jcf, name_index),
- signature, sig_len))
+ (const char *) signature, sig_len))
{
/* If this method is overloaded by another native method,
then include the argument information in the mangled
while (signature < limit)
{
int ch = UTF8_GET (signature, limit);
- if (ch == '(')
- {
- /* Ignore. */
- }
- else if (ch == ')')
+ jni_print_char (stream, ch);
+ if (ch == ')')
{
/* Done. */
break;
}
- else if (ch == '_')
- fputs ("_1", stream);
- else if (ch == ';')
- fputs ("_2", stream);
- else if (ch == '[')
- fputs ("_3", stream);
- else if (ch == '/')
- fputs ("_", stream);
- else if ((ch >= '0' && ch <= '9')
- || (ch >= 'a' && ch <= 'z')
- || (ch >= 'A' && ch <= 'Z'))
- fputc (ch, stream);
- else
- {
- /* "Unicode" character. FIXME: upper or lower case
- letters? */
- fprintf (stream, "_0%04x", ch);
- }
}
}
}
next = decode_signature_piece (stream, str, limit, &need_space);
if (! next)
{
- fprintf (stderr, "unparseable signature: `%s'\n", str0);
- found_error = 1;
+ error ("unparseable signature: `%s'", str0);
return;
}
/* This is a helper for print_stub_or_jni. */
static void
-DEFUN (print_name_for_stub_or_jni, (stream, jcf, name_index, signature_index,
- is_init, name_override, flags),
- FILE *stream AND JCF *jcf
- AND int name_index AND int signature_index
- AND int is_init AND const char *name_override AND int flags)
+print_name_for_stub_or_jni (FILE *stream, JCF *jcf, int name_index,
+ int signature_index, int is_init,
+ const char *name_override, int flags)
{
- const char *const prefix = flag_jni ? "Java_" : "\n";
- print_cxx_classname (stream, prefix, jcf, jcf->this_class);
+ const char *const prefix = flag_jni ? "Java_" : "";
+ print_cxx_classname (stream, prefix, jcf, jcf->this_class, 1);
fputs (flag_jni ? "_" : "::", stream);
print_full_cxx_name (stream, jcf, name_index,
signature_index, is_init, name_override,
}
static void
-DEFUN(print_stub_or_jni, (stream, jcf, name_index, signature_index, is_init,
- name_override, flags),
- FILE* stream AND JCF* jcf
- AND int name_index AND int signature_index
- AND int is_init AND const char *name_override AND int flags)
+print_stub_or_jni (FILE* stream, JCF* jcf, int name_index,
+ int signature_index, int is_init,
+ const char *name_override, int flags)
{
if (JPOOL_TAG (jcf, signature_index) != CONSTANT_Utf8)
{
{
int length = JPOOL_UTF_LENGTH (jcf, signature_index);
const unsigned char *str0 = JPOOL_UTF_DATA (jcf, signature_index);
- register const unsigned char *str = str0;
+ const unsigned char *str = str0;
const unsigned char *limit = str + length;
int need_space = 0;
int is_method = str[0] == '(';
return;
if (flag_jni && ! stubs)
- fputs ("extern ", stream);
+ fputs ("JNIEXPORT ", stream);
/* If printing a method, skip to the return signature and print
that first. However, there is no return value if this is a
next = decode_signature_piece (stream, str, limit, &need_space);
if (! next)
{
- fprintf (stderr, "unparseable signature: `%s'\n", str0);
- found_error = 1;
+ error ("unparseable signature: `%s'", str0);
return;
}
}
/* When printing a JNI header we need to respect the space. In
other cases we're just going to insert a newline anyway. */
- if (flag_jni)
- fputs (need_space && ! stubs ? " " : "\n", stream);
+ fputs (need_space && ! stubs ? " " : "\n", stream);
+ if (flag_jni && ! stubs)
+ fputs ("JNICALL ", stream);
+
/* Now print the name of the thing. */
print_name_for_stub_or_jni (stream, jcf, name_index,
signature_index, is_init, name_override,
if (stubs)
{
if (flag_jni)
- fputs ("\n{\n (*env)->FatalError (\"", stream);
+ fputs ("\n{\n (*env)->FatalError (env, \"", stream);
else
- fputs ("\n{\n JvFail (\"", stream);
+ fputs ("\n{\n throw new ::java::lang::UnsupportedOperationException (JvNewStringLatin1 (\"", stream);
print_name_for_stub_or_jni (stream, jcf, name_index,
signature_index, is_init,
name_override,
flags);
- fputs (" not implemented\");\n}\n\n", stream);
+ fprintf (stream, " not implemented\")%s;\n}\n\n",
+ flag_jni ? "" : ")");
}
}
}
static void
-DEFUN(print_mangled_classname, (stream, jcf, prefix, index),
- FILE *stream AND JCF *jcf AND const char *prefix AND int index)
+print_mangled_classname (FILE *stream, JCF *jcf, const char *prefix, int index)
{
int name_index = JPOOL_USHORT1 (jcf, index);
fputs (prefix, stream);
to an array, ignore it and don't print PREFIX. Returns 1 if
something was printed, 0 otherwise. */
static int
-print_cxx_classname (stream, prefix, jcf, index)
- FILE *stream;
- const char *prefix;
- JCF *jcf;
- int index;
+print_cxx_classname (FILE *stream, const char *prefix,
+ JCF *jcf, int index, int add_scope)
{
int name_index = JPOOL_USHORT1 (jcf, index);
int len, c;
fputs (prefix, stream);
/* Print a leading "::" so we look in the right namespace. */
- if (! flag_jni)
+ if (! flag_jni && ! stubs && add_scope)
fputs ("::", stream);
while (s < limit)
if (c == '/')
fputs (flag_jni ? "_" : "::", stream);
else
- jcf_print_char (stream, c);
+ jni_print_char (stream, c);
}
return 1;
/* Return name of superclass. If LEN is not NULL, fill it with length
of name. */
static const unsigned char *
-super_class_name (derived_jcf, len)
- JCF *derived_jcf;
- int *len;
+super_class_name (JCF *derived_jcf, int *len)
{
int supername_index = JPOOL_USHORT1 (derived_jcf, derived_jcf->super_class);
int supername_length = JPOOL_UTF_LENGTH (derived_jcf, supername_index);
return supername;
}
+static void
+handle_inner_classes (int count)
+{
+ int i;
+
+ if (out && ! flag_jni && ! stubs && count > 0)
+ fprintf (out, "\n");
+
+ for (i = 0; i < count; ++i)
+ {
+ JCF_u2 inner_info_index = JCF_readu2 (current_jcf);
+
+ /* There are a few more values here, but we don't care about
+ them. The (void) cast is apparently the only way to avoid a
+ warning here. */
+ (void) JCF_readu2 (current_jcf);
+ (void) JCF_readu2 (current_jcf);
+ (void) JCF_readu2 (current_jcf);
+
+ if (out && ! flag_jni && ! stubs)
+ {
+ print_mangled_classname (out, current_jcf, " friend class ",
+ inner_info_index);
+ fprintf (out, ";\n");
+ }
+ }
+}
+
\f
/* We keep track of all the `#include's we generate, so we can avoid
/* Generate a #include. */
static void
-print_include (out, utf8, len)
- FILE *out;
- const unsigned char *utf8;
- int len;
+print_include (FILE *out, const unsigned char *utf8, int len)
{
struct include *incl;
return;
if (len == -1)
- len = strlen (utf8);
+ len = strlen ((const char *) utf8);
for (incl = all_includes; incl; incl = incl->next)
{
/* We check the length because we might have a proper prefix. */
if (len == (int) strlen (incl->name)
- && ! strncmp (incl->name, utf8, len))
+ && ! strncmp (incl->name, (const char *) utf8, len))
return;
}
- incl = (struct include *) xmalloc (sizeof (struct include));
+ incl = xmalloc (sizeof (struct include));
incl->name = xmalloc (len + 1);
- strncpy (incl->name, utf8, len);
+ strncpy (incl->name, (const char *) utf8, len);
incl->name[len] = '\0';
incl->next = all_includes;
all_includes = incl;
struct namelet *next;
};
-static void add_namelet PARAMS ((const unsigned char *,
- const unsigned char *, struct namelet *));
-static void print_namelet PARAMS ((FILE *, struct namelet *, int));
+static void add_namelet (const unsigned char *, const unsigned char *,
+ struct namelet *);
+static void print_namelet (FILE *, struct namelet *, int);
/* The special root namelet. */
static struct namelet root =
package or class name and links it into the tree. It does this
recursively. */
static void
-add_namelet (name, name_limit, parent)
- const unsigned char *name, *name_limit;
- struct namelet *parent;
+add_namelet (const unsigned char *name, const unsigned char *name_limit,
+ struct namelet *parent)
{
const unsigned char *p;
struct namelet *n = NULL, *np;
#define JAVAIO "java/io/"
#define JAVAUTIL "java/util/"
if ((name_limit - name >= (int) sizeof (JAVALANG) - 1
- && ! strncmp (name, JAVALANG, sizeof (JAVALANG) - 1))
+ && ! strncmp ((const char *) name, JAVALANG, sizeof (JAVALANG) - 1))
|| (name_limit - name >= (int) sizeof (JAVAUTIL) - 1
- && ! strncmp (name, JAVAUTIL, sizeof (JAVAUTIL) - 1))
+ && ! strncmp ((const char *) name, JAVAUTIL, sizeof (JAVAUTIL) - 1))
|| (name_limit - name >= (int) sizeof (JAVAIO) - 1
- && ! strncmp (name, JAVAIO, sizeof (JAVAIO) - 1)))
+ && ! strncmp ((const char *) name, JAVAIO, sizeof (JAVAIO) - 1)))
return;
}
- for (p = name; p < name_limit && *p != '/' && *p != '$'; ++p)
+ for (p = name; p < name_limit && *p != '/'; ++p)
;
/* Search for this name beneath the PARENT node. */
{
/* We check the length because we might have a proper prefix. */
if ((int) strlen (np->name) == p - name &&
- ! strncmp (name, np->name, p - name))
+ ! strncmp ((const char *) name, np->name, p - name))
{
n = np;
break;
if (n == NULL)
{
- n = (struct namelet *) xmalloc (sizeof (struct namelet));
+ n = xmalloc (sizeof (struct namelet));
n->name = xmalloc (p - name + 1);
- strncpy (n->name, name, p - name);
+ strncpy (n->name, (const char *) name, p - name);
n->name[p - name] = '\0';
- n->is_class = (p == name_limit || *p == '$');
+ n->is_class = (p == name_limit);
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 != '$')
+ if (p < name_limit)
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;
+print_namelet (FILE *out, struct namelet *name, int depth)
{
int i, term = 0;
struct namelet *c;
print_namelet (out, c, depth + 2);
c = next;
}
+ name->subnamelets = NULL;
if (name->name)
{
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;
+add_class_decl (FILE *out, JCF *jcf, JCF_u2 signature)
{
const 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);
- const char *tname = JPOOL_UTF_DATA (jcf, name_index);
for (i = 0; i < len; ++i)
{
- int start, saw_dollar;
+ int start;
/* If we see an array, then we include the array header. */
if (s[i] == '[')
{
- print_include (out, "gcj/array", -1);
+ print_include (out, (const unsigned char *) "gcj/array", -1);
continue;
}
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);
+ add_namelet (&s[start], &s[i], &root);
}
}
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;
+print_class_decls (FILE *out, JCF *jcf, int self)
{
/* Make sure to always add the current class to the list of things
that should be declared. */
/* 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);
+ fputs ("}\n\n", out);
}
}
\f
static void
-DEFUN(process_file, (jcf, out),
- JCF *jcf AND FILE *out)
+process_file (JCF *jcf, FILE *out)
{
int code, i;
uint32 field_start, method_end, method_start;
if (jcf_parse_preamble (jcf) != 0)
{
- fprintf (stderr, "Not a valid Java .class file.\n");
- found_error = 1;
+ error ("Not a valid Java .class file.");
return;
}
code = jcf_parse_constant_pool (jcf);
if (code != 0)
{
- fprintf (stderr, "error while parsing constant pool\n");
- found_error = 1;
+ error ("error while parsing constant pool");
return;
}
code = verify_constant_pool (jcf);
if (code > 0)
{
- fprintf (stderr, "error in constant pool entry #%d\n", code);
- found_error = 1;
+ error ("error in constant pool entry #%d", code);
return;
}
{
/* Strip off the ".class" portion of the name when printing
the include file name. */
- int len = strlen (jcf->classname);
+ char *name;
+ int i, len = strlen (jcf->classname);
if (len > 6 && ! strcmp (&jcf->classname[len - 6], ".class"))
len -= 6;
- print_include (out, jcf->classname, len);
+ /* Turn the class name into a file name. */
+ name = xmalloc (len + 1);
+ for (i = 0; i < len; ++i)
+ name[i] = jcf->classname[i] == '.' ? '/' : jcf->classname[i];
+ name[i] = '\0';
+ print_include (out, (const unsigned char *) name, len);
+ free (name);
+
+ if (! flag_jni)
+ {
+ print_include (out, (const unsigned char *) "gcj/cni", -1);
+ print_include (out, (const unsigned char *) "java/lang/UnsupportedOperationException",
+ -1);
+ }
}
}
if (! stubs)
{
- if (! print_cxx_classname (out, "class ", jcf, jcf->this_class))
+ if (! print_cxx_classname (out, "class ", jcf,
+ jcf->this_class, 0))
{
- fprintf (stderr, "class is of array type\n");
- found_error = 1;
+ error ("class is of array type\n");
return;
}
if (jcf->super_class)
{
if (! print_cxx_classname (out, " : public ",
- jcf, jcf->super_class))
+ jcf, jcf->super_class, 1))
{
- fprintf (stderr, "base class is of array type\n");
- found_error = 1;
+ error ("base class is of array type");
return;
}
}
}
/* Now go back for second pass over methods and fields. */
+ is_first_data_member = 1;
+
JCF_SEEK (jcf, method_start);
method_pass = 1;
jcf_parse_methods (jcf);
{
if (flag_jni)
{
- fprintf (out, "\n#ifdef __cplusplus\n");
- fprintf (out, "}\n");
- fprintf (out, "#endif\n");
+ fprintf (out, "\n#ifdef __cplusplus\n");
+ fprintf (out, "}\n");
+ fprintf (out, "#endif\n");
}
else
{
for (i = 0; i < add_count; ++i)
fprintf (out, " %s\n", add_specs[i]);
- if (! stubs)
- fputs ("};\n", out);
+ /* Generate an entry for the class object. */
+ generate_access (out, ACC_PUBLIC);
+ fprintf (out, "\n static ::java::lang::Class class$;\n");
+
+ fputs ("}", out);
+
+ if (jcf->access_flags & ACC_INTERFACE)
+ fputs (" __attribute__ ((java_interface))", out);
+
+ fputs (";\n", out);
if (append_count > 0)
fputc ('\n', out);
/* This is used to mark options with no short value. */
#define LONG_OPT(Num) ((Num) + 128)
-#define OPT_classpath LONG_OPT (0)
-#define OPT_CLASSPATH LONG_OPT (1)
-#define OPT_HELP LONG_OPT (2)
-#define OPT_TEMP LONG_OPT (3)
-#define OPT_VERSION LONG_OPT (4)
-#define OPT_PREPEND LONG_OPT (5)
-#define OPT_FRIEND LONG_OPT (6)
-#define OPT_ADD LONG_OPT (7)
-#define OPT_APPEND LONG_OPT (8)
-#define OPT_M LONG_OPT (9)
-#define OPT_MM LONG_OPT (10)
-#define OPT_MG LONG_OPT (11)
-#define OPT_MD LONG_OPT (12)
-#define OPT_MMD LONG_OPT (13)
-
-static struct option options[] =
+#define OPT_classpath LONG_OPT (0)
+#define OPT_CLASSPATH OPT_classpath
+#define OPT_bootclasspath LONG_OPT (1)
+#define OPT_extdirs LONG_OPT (2)
+#define OPT_HELP LONG_OPT (3)
+#define OPT_TEMP LONG_OPT (4)
+#define OPT_VERSION LONG_OPT (5)
+#define OPT_PREPEND LONG_OPT (6)
+#define OPT_FRIEND LONG_OPT (7)
+#define OPT_ADD LONG_OPT (8)
+#define OPT_APPEND LONG_OPT (9)
+#define OPT_M LONG_OPT (10)
+#define OPT_MM LONG_OPT (11)
+#define OPT_MG LONG_OPT (12)
+#define OPT_MD LONG_OPT (13)
+#define OPT_MMD LONG_OPT (14)
+
+static const struct option options[] =
{
- { "classpath", required_argument, NULL, OPT_classpath },
- { "CLASSPATH", required_argument, NULL, OPT_CLASSPATH },
- { "help", no_argument, NULL, OPT_HELP },
- { "stubs", no_argument, &stubs, 1 },
- { "td", required_argument, NULL, OPT_TEMP },
- { "verbose", no_argument, NULL, 'v' },
- { "version", no_argument, NULL, OPT_VERSION },
- { "prepend", required_argument, NULL, OPT_PREPEND },
- { "friend", required_argument, NULL, OPT_FRIEND },
- { "add", required_argument, NULL, OPT_ADD },
- { "append", required_argument, NULL, OPT_APPEND },
- { "M", no_argument, NULL, OPT_M },
- { "MM", no_argument, NULL, OPT_MM },
- { "MG", no_argument, NULL, OPT_MG },
- { "MD", no_argument, NULL, OPT_MD },
- { "MMD", no_argument, NULL, OPT_MMD },
- { "jni", no_argument, &flag_jni, 1 },
- { NULL, no_argument, NULL, 0 }
+ { "classpath", required_argument, NULL, OPT_classpath },
+ { "bootclasspath", required_argument, NULL, OPT_bootclasspath },
+ { "extdirs", required_argument, NULL, OPT_extdirs },
+ { "CLASSPATH", required_argument, NULL, OPT_CLASSPATH },
+ { "help", no_argument, NULL, OPT_HELP },
+ { "stubs", no_argument, &stubs, 1 },
+ { "td", required_argument, NULL, OPT_TEMP },
+ { "verbose", no_argument, NULL, 'v' },
+ { "version", no_argument, NULL, OPT_VERSION },
+ { "prepend", required_argument, NULL, OPT_PREPEND },
+ { "friend", required_argument, NULL, OPT_FRIEND },
+ { "add", required_argument, NULL, OPT_ADD },
+ { "append", required_argument, NULL, OPT_APPEND },
+ { "M", no_argument, NULL, OPT_M },
+ { "MM", no_argument, NULL, OPT_MM },
+ { "MG", no_argument, NULL, OPT_MG },
+ { "MD", no_argument, NULL, OPT_MD },
+ { "MMD", no_argument, NULL, OPT_MMD },
+ { "jni", no_argument, &flag_jni, 1 },
+ { NULL, no_argument, NULL, 0 }
};
static void
-usage ()
+usage (void)
{
- fprintf (stderr, "Try `gcjh --help' for more information.\n");
+ fprintf (stderr, _("Try `gcjh --help' for more information.\n"));
exit (1);
}
static void
-help ()
+help (void)
{
- printf ("Usage: gcjh [OPTION]... CLASS...\n\n");
- printf ("Generate C++ header files from .class files\n\n");
- printf (" -stubs Generate an implementation stub file\n");
- printf (" -jni Generate a JNI header or stub\n");
+ printf (_("Usage: gcjh [OPTION]... CLASS...\n\n"));
+ printf (_("Generate C++ header files from .class files\n\n"));
+ printf (_(" -stubs Generate an implementation stub file\n"));
+ printf (_(" -jni Generate a JNI header or stub\n"));
printf ("\n");
- printf (" -add TEXT Insert TEXT into class body\n");
- printf (" -append TEXT Insert TEXT after class declaration\n");
- printf (" -friend TEXT Insert TEXT as `friend' declaration\n");
- printf (" -prepend TEXT Insert TEXT before start of class\n");
+ printf (_(" -add TEXT Insert TEXT into class body\n"));
+ printf (_(" -append TEXT Insert TEXT after class declaration\n"));
+ printf (_(" -friend TEXT Insert TEXT as `friend' declaration\n"));
+ printf (_(" -prepend TEXT Insert TEXT before start of class\n"));
printf ("\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 (" -o FILE Set output file name\n");
- printf (" -td DIRECTORY Set temporary directory name\n");
+ printf (_(" --classpath PATH Set path to find .class files\n"));
+ printf (_(" -IDIR Append directory to class path\n"));
+ printf (_(" --bootclasspath PATH Override built-in class path\n"));
+ printf (_(" --extdirs PATH Set extensions directory path\n"));
+ printf (_(" -d DIRECTORY Set output directory name\n"));
+ printf (_(" -o FILE Set output file name\n"));
+ printf (_(" -td DIRECTORY Set temporary directory name\n"));
printf ("\n");
- printf (" --help Print this help, then exit\n");
- printf (" --version Print version number, then exit\n");
- printf (" -v, --verbose Print extra information while running\n");
+ printf (_(" --help Print this help, then exit\n"));
+ printf (_(" --version Print version number, then exit\n"));
+ printf (_(" -v, --verbose Print extra information while running\n"));
printf ("\n");
- printf (" -M Print all dependencies to stdout;\n");
- printf (" suppress ordinary output\n");
- printf (" -MM Print non-system dependencies to stdout;\n");
- printf (" suppress ordinary output\n");
- printf (" -MD Print all dependencies to stdout\n");
- printf (" -MMD Print non-system dependencies to stdout\n");
+ printf (_(" -M Print all dependencies to stdout;\n"
+ " suppress ordinary output\n"));
+ printf (_(" -MM Print non-system dependencies to stdout;\n"
+ " suppress ordinary output\n"));
+ printf (_(" -MD Print all dependencies to stdout\n"));
+ printf (_(" -MMD Print non-system dependencies to stdout\n"));
/* We omit -MG until it is implemented. */
printf ("\n");
- printf ("For bug reporting instructions, please see:\n");
- printf ("<URL:http://www.gnu.org/software/gcc/faq.html#bugreport>.\n");
+ printf (_("For bug reporting instructions, please see:\n"
+ "%s.\n"), bug_report_url);
exit (0);
}
static void
-version ()
+version (void)
{
- printf ("gcjh (%s)\n\n", version_string);
- printf ("Copyright (C) 1998, 1999 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");
+ printf ("gcjh (GCC) %s\n\n", version_string);
+ printf ("Copyright %s 2004 Free Software Foundation, Inc.\n", _("(C)"));
+ printf (_("This is free software; see the source for copying conditions. There is NO\n"
+ "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
exit (0);
}
int
-DEFUN(main, (argc, argv),
- int argc AND char** argv)
+main (int argc, char** argv)
{
JCF jcf;
int argi;
int emit_dependencies = 0, suppress_output = 0;
int opt;
+ gcc_init_libintl ();
+
if (argc <= 1)
{
- fprintf (stderr, "gcjh: no classes specified\n");
+ error ("no classes specified");
usage ();
}
jcf_path_classpath_arg (optarg);
break;
- case OPT_CLASSPATH:
- jcf_path_CLASSPATH_arg (optarg);
+ case OPT_bootclasspath:
+ jcf_path_bootclasspath_arg (optarg);
+ break;
+
+ case OPT_extdirs:
+ jcf_path_extdirs_arg (optarg);
break;
case OPT_HELP:
case OPT_PREPEND:
if (prepend_count == 0)
- prepend_specs = (char**) ALLOC (argc * sizeof (char*));
+ prepend_specs = ALLOC (argc * sizeof (char*));
prepend_specs[prepend_count++] = optarg;
break;
case OPT_FRIEND:
if (friend_count == 0)
- friend_specs = (char**) ALLOC (argc * sizeof (char*));
+ friend_specs = ALLOC (argc * sizeof (char*));
friend_specs[friend_count++] = optarg;
break;
case OPT_ADD:
if (add_count == 0)
- add_specs = (char**) ALLOC (argc * sizeof (char*));
+ add_specs = ALLOC (argc * sizeof (char*));
add_specs[add_count++] = optarg;
break;
case OPT_APPEND:
if (append_count == 0)
- append_specs = (char**) ALLOC (argc * sizeof (char*));
+ append_specs = ALLOC (argc * sizeof (char*));
append_specs[append_count++] = optarg;
break;
break;
case OPT_MG:
- fprintf (stderr, "gcjh: `%s' option is unimplemented\n", argv[argi]);
+ error ("`-MG' option is unimplemented");
exit (1);
case OPT_MD:
if (optind == argc)
{
- fprintf (stderr, "gcjh: no classes specified\n");
+ error ("no classes specified");
usage ();
}
- jcf_path_seal ();
+ jcf_path_seal (verbose);
if (output_file && emit_dependencies)
{
- fprintf (stderr, "gcjh: can't specify both -o and -MD\n");
+ error ("can't specify both -o and -MD");
exit (1);
}
const char *classfile_name;
if (verbose)
- fprintf (stderr, "Processing %s\n", classname);
+ printf (_("Processing %s\n"), classname);
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);
+ error ("%s: no such class", classname);
exit (1);
}
if (verbose)
- fprintf (stderr, "Found in %s\n", classfile_name);
+ printf (_("Found in %s\n"), classfile_name);
if (output_file)
{
if (strcmp (output_file, "-") == 0)
{
int dir_len = strlen (output_directory);
int i, classname_length = strlen (classname);
- current_output_file = (char*) ALLOC (dir_len + classname_length + 5);
+ current_output_file = ALLOC (dir_len + classname_length + 5);
strcpy (current_output_file, output_directory);
if (dir_len > 0 && output_directory[dir_len-1] != '/')
current_output_file[dir_len++] = '/';
}
}
}
+ free_method_name_list ();
process_file (&jcf, out);
JCF_FINISH (&jcf);
if (current_output_file != output_file)