diff options
Diffstat (limited to 'gcc/java/gjavah.c')
-rw-r--r-- | gcc/java/gjavah.c | 2673 |
1 files changed, 0 insertions, 2673 deletions
diff --git a/gcc/java/gjavah.c b/gcc/java/gjavah.c deleted file mode 100644 index c79a0a918c7..00000000000 --- a/gcc/java/gjavah.c +++ /dev/null @@ -1,2673 +0,0 @@ -/* 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, 2001, 2002, 2003, 2004, 2005, 2006 -Free Software Foundation, Inc. - -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. - -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 GCC; see the file COPYING. If not, write to -the Free Software Foundation, 51 Franklin Street, Fifth Floor, -Boston, MA 02110-1301, USA. - -Java and all Java-based marks are trademarks or registered trademarks -of Sun Microsystems, Inc. in the United States and other countries. -The Free Software Foundation is independent of Sun Microsystems, Inc. */ - -/* Written by Per Bothner <bothner@cygnus.com>, February 1996. */ - -#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> - - - -/* The output file. */ -FILE *out = NULL; - -/* Nonzero on failure. */ -static int found_error = 0; - -#ifdef JNI_DEFAULT -#define TOOLNAME "gjnih" - -/* Nonzero if we're generating JNI output. */ -int flag_jni = 1; -#else -#define TOOLNAME "gcjh" - -int flag_jni = 0; -#endif - -/* 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. */ -static const char *output_directory = ""; - -/* Directory to place temporary file. Set by -td option. Currently unused. */ -static const char *temp_directory = "/tmp"; - -/* Number of friend functions we have to declare. */ -static int friend_count; - -/* A class can optionally have a `friend' function declared. If - non-NULL, this is that function. */ -static char **friend_specs = NULL; - -/* Number of lines we are prepending before the class. */ -static int prepend_count; - -/* We can prepend extra lines before the class's start. */ -static char **prepend_specs = NULL; - -/* Number of lines we are appending at the end of the class. */ -static int add_count; - -/* We can append extra lines just before the class's end. */ -static char **add_specs = NULL; - -/* Number of lines we are appending after the class. */ -static int append_count; - -/* We can append extra lines after the class's end. */ -static char **append_specs = NULL; - -int verbose = 0; - -int stubs = 0; - -struct JCF *current_jcf; - -/* This holds access information for the last field we examined. They - let us generate "private:", "public:", and "protected:" properly. - If 0 then we haven't previously examined any field. */ -static JCF_u2 last_access; - -/* 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))) - -/* Pass this macro the flags for a method. It will return true if the - method is native. */ -#define METHOD_IS_NATIVE(Method) \ - ((Method) & ACC_NATIVE) - -#define METHOD_IS_PRIVATE(Class, Method) \ - (((Method) & ACC_PRIVATE) != 0) - -/* 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; - 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 (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) ATTRIBUTE_UNUSED; -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 *gmsgid, ...) ATTRIBUTE_PRINTF_1; -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; -JCF_u2 current_field_signature; -JCF_u2 current_field_flags; - -#define HANDLE_START_FIELD(ACCESS_FLAGS, NAME, SIGNATURE, ATTRIBUTE_COUNT) \ -( current_field_name = (NAME), current_field_signature = (SIGNATURE), \ - current_field_flags = (ACCESS_FLAGS), current_field_value = 0) - -/* 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 && ! stubs) \ - print_field_info (out, jcf, current_field_name, \ - current_field_signature, \ - current_field_flags); \ - } \ - else if (! stubs && ! flag_jni) \ - add_class_decl (out, jcf, current_field_signature); - -#define HANDLE_CONSTANTVALUE(VALUEINDEX) current_field_value = (VALUEINDEX) - -static int method_declared = 0; -static int method_access = 0; -static int method_printed = 0; -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); \ - } \ - } - -/* Only include byte-code decompilation optimizations for ELF targets - since the generated headers are only known to work with ELF weak - symbol semantics. Specifically, these optimizations are known to - not work on PE-COFF and possibly others. */ -#ifdef OBJECT_FORMAT_ELF -#define HANDLE_CODE_ATTRIBUTE(MAX_STACK, MAX_LOCALS, CODE_LENGTH) \ - if (out && method_declared) decompile_method (out, jcf, CODE_LENGTH); -#endif - -static int decompiled = 0; -#define HANDLE_END_METHOD() \ - if (out && method_printed && !method_synthetic) \ - fputs (decompiled || stubs ? "\n" : ";\n", out); - -#define HANDLE_INNERCLASSES_ATTRIBUTE(COUNT) handle_inner_classes (COUNT) - -/* We're going to need {peek,skip}_attribute, enable their definition. */ -#define NEED_PEEK_ATTRIBUTE -#define NEED_SKIP_ATTRIBUTE - -#include "jcf-reader.c" - -/* Print an error message and set found_error. - Not really gcc-internal-format message, but as error elsewhere - uses it, assume all users will use intersection between - c-format and gcc-internal-format. */ -static void -error (const char *gmsgid, ...) -{ - va_list ap; - - va_start (ap, gmsgid); - - fprintf (stderr, TOOLNAME ": "); - vfprintf (stderr, _(gmsgid), ap); - va_end (ap); - fprintf (stderr, "\n"); - found_error = 1; -} - -/* Print a single-precision float, suitable for parsing by g++. */ -static void -jni_print_float (FILE *stream, jfloat f) -{ - /* It'd be nice to use __builtin_nan/__builtin_inf here but they don't - work in data initializers. FIXME. */ - if (JFLOAT_FINITE (f)) - { - if (flag_jni) - { - fputs (" ", out); - 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); - } - } - if (! flag_jni) - fputs (";\n", stream); -} - -/* 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)) - { - if (flag_jni) - { - fputs (" ", out); - 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 (flag_jni ? "\n" : ";\n", stream); -} - -/* Print a character, appropriately mangled for JNI. */ - -static void -jni_print_char (FILE *stream, int ch) -{ - 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 (FILE *stream, JCF *jcf, int index) -{ - int name_index = JPOOL_USHORT1 (jcf, index); - int len; - const unsigned char *s, *p, *limit; - - s = JPOOL_UTF_DATA (jcf, name_index); - len = JPOOL_UTF_LENGTH (jcf, name_index); - limit = s + len; - p = s; - while (s < limit) - { - int c = UTF8_GET (s, limit); - if (c == '/') - p = s; - } - - while (p < limit) - { - int ch = UTF8_GET (p, limit); - if (ch == '/') - fputs ("::", stream); - else - jcf_print_char (stream, ch); - } -} - -/* 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 (const unsigned char *str, int length, const char *name) -{ - const unsigned char *limit = str + length; - int i; - - for (i = 0; name[i]; ++i) - { - int ch = UTF8_GET (str, limit); - if (ch != name[i]) - return ch - name[i]; - } - - 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. The return value is malloc()d. */ - -static char * -cxx_keyword_subst (const unsigned char *str, int length) -{ - 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 = XNEWVEC (char, 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 (FILE *stream, JCF_u2 flags) -{ - 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; - case ACC_PRIVATE: - fputs ("private:\n", stream); - break; - case ACC_PROTECTED: - fputs ("public: // actually protected\n", stream); - break; - default: - found_error = 1; - fprintf (stream, "#error unrecognized visibility %d\n", - (flags & ACC_VISIBILITY)); - break; - } -} - -/* See if NAME is already the name of a method. */ -static int -name_is_method_p (const 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; -} - -/* Free the method name list. */ -static void -free_method_name_list (void) -{ - 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 (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->is_native - && p->length == length - && ! memcmp (p->name, name, length) - && (p->sig_length != sig_length - || memcmp (p->signature, signature, sig_length))) - return 1; - } - return 0; -} - -/* Get name of a field. This handles renamings due to C++ clash. */ -static char * -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; - - 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)) - { - error ("static field has same name as method"); - return NULL; - } - - override = XNEWVEC (char, length + 3); - memcpy (override, name, length); - strcpy (override + length, "__"); - } - else if (flag_jni) - override = NULL; - else - 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 (FILE *stream, JCF *jcf, int name_index, JCF_u2 flags) -{ - char *override = get_field_name (jcf, name_index, flags); - - if (override) - { - fputs (override, stream); - free (override); - } - else - jcf_print_utf8 (stream, JPOOL_UTF_DATA (jcf, name_index), - JPOOL_UTF_LENGTH (jcf, name_index)); -} - -static void -print_field_info (FILE *stream, JCF* jcf, int name_index, int sig_index, - JCF_u2 flags) -{ - char *override = NULL; - - if (! flag_jni) - generate_access (stream, flags); - if (JPOOL_TAG (jcf, name_index) != CONSTANT_Utf8) - { - fprintf (stream, "<not a UTF8 constant>"); - found_error = 1; - return; - } - - if (flag_jni) - { - /* For JNI we only want to print real constants. */ - int val; - if (! (flags & ACC_STATIC) - || ! (flags & ACC_FINAL) - || current_field_value <= 0) - return; - val = JPOOL_TAG (jcf, current_field_value); - if (val != CONSTANT_Integer && val != CONSTANT_Long - && val != CONSTANT_Float && val != CONSTANT_Double) - return; - } - else - { - /* Initial indentation. */ - fputs (" ", stream); - } - - if ((flags & ACC_STATIC)) - { - if (flag_jni) - { - print_cxx_classname (stream, "#undef ", jcf, jcf->this_class, 1); - fputs ("_", stream); - print_field_name (stream, jcf, name_index, 0); - fputs ("\n", stream); - print_cxx_classname (stream, "#define ", jcf, jcf->this_class, 1); - fputs ("_", stream); - } - else - fputs ("static ", stream); - - if ((flags & ACC_FINAL) && current_field_value > 0) - { - char buffer[25]; - int done = 1; - - switch (JPOOL_TAG (jcf, current_field_value)) - { - case CONSTANT_Integer: - { - jint num; - int most_negative = 0; - if (! flag_jni) - fputs ("const jint ", stream); - print_field_name (stream, jcf, name_index, 0); - fputs (flag_jni ? " " : " = ", stream); - 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 (stream, "%sL%s%s\n", buffer, - most_negative ? " - 1" : "", - flag_jni ? "" : ";"); - } - break; - case CONSTANT_Long: - { - jlong num; - int most_negative = 0; - if (! flag_jni) - fputs ("const jlong ", stream); - print_field_name (stream, jcf, name_index, 0); - fputs (flag_jni ? " " : " = ", stream); - 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 (stream, "%sLL%s%s\n", buffer, - most_negative ? " - 1" :"", - flag_jni ? "" : ";"); - } - break; - case CONSTANT_Float: - { - jfloat fnum = JPOOL_FLOAT (jcf, current_field_value); - if (! flag_jni) - fputs ("const jfloat ", stream); - print_field_name (stream, jcf, name_index, 0); - jni_print_float (stream, fnum); - } - break; - case CONSTANT_Double: - { - jdouble dnum = JPOOL_DOUBLE (jcf, current_field_value); - if (! flag_jni) - fputs ("const jdouble ", stream); - print_field_name (stream, jcf, name_index, 0); - jni_print_double (stream, dnum); - } - break; - default: - /* We can't print this as a constant, but we can still - print something sensible. */ - done = 0; - break; - } - - if (done) - return; - } - } - - /* assert (! flag_jni); */ - override = get_field_name (jcf, name_index, flags); - print_c_decl (stream, jcf, name_index, sig_index, 0, override, flags); - fputs (";\n", stream); - - if (override) - free (override); -} - - -static void -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; - char *override = NULL; - - method_declared = 0; - method_access = flags; - if (stream && 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] == '<') - { - /* 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) - && ! (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; - } - - /* 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 = XNEW (struct method_name); - nn->name = XNEWVEC (unsigned char, 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 = XNEWVEC (unsigned char, 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; - } - - /* We don't worry about overrides in JNI mode. */ - if (! flag_jni) - { - /* 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) - { - method_printed = 1; - - generate_access (stream, flags); - - fputs (" ", out); - if ((flags & ACC_STATIC)) - fputs ("static ", out); - else if (! METHOD_IS_PRIVATE (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, is_init, override, flags); - - if ((flags & ACC_ABSTRACT)) - fputs (" = 0", out); - else - method_declared = 1; - } - else - { - if (METHOD_IS_NATIVE (flags)) - { - method_printed = 1; - print_stub_or_jni (out, jcf, name_index, sig_index, - 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 (FILE *out, JCF *jcf, int code_len) -{ - const unsigned char *codes = jcf->read_ptr; - int index; - uint16 name_and_type, name; - - /* If the method is synchronized, don't touch it. */ - if ((method_access & ACC_SYNCHRONIZED)) - return; - - 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'. */ - index = (codes[2] << 8) | codes[3]; - /* FIXME: ensure that tag is CONSTANT_Fieldref. */ - name_and_type = JPOOL_USHORT2 (jcf, index); - /* FIXME: ensure that tag is CONSTANT_NameAndType. */ - name = JPOOL_USHORT1 (jcf, name_and_type); - 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 - /* We're going to generate `return this'. This only makes - sense for non-static methods. */ - && ! (method_access & ACC_STATIC)) - { - 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) - { - /* 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; - } -} - -/* 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 = XNEWVEC (unsigned char, 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 = XNEWVEC (unsigned char, 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 (FILE *stream, const unsigned char *signature, - const unsigned char *limit, int *need_space) -{ - const char *ctype; - int array_depth = 0; - - switch (signature[0]) - { - case '[': - /* More spaghetti. */ - - array_loop: - for (signature++; (signature < limit - && ISDIGIT (*signature)); signature++) - ; - switch (*signature) - { - case 'B': - ctype = "jbyteArray"; - break; - case 'C': - ctype = "jcharArray"; - break; - case 'D': - ctype = "jdoubleArray"; - break; - case 'F': - ctype = "jfloatArray"; - break; - case 'I': - ctype = "jintArray"; - break; - case 'S': - ctype = "jshortArray"; - break; - case 'J': - ctype = "jlongArray"; - break; - case 'Z': - ctype = "jbooleanArray"; - break; - case '[': - /* We have a nested array. */ - ++array_depth; - if (! flag_jni) - fputs ("JArray<", stream); - goto array_loop; - - case 'L': - /* We have to generate a reference to JArray here, so that - our output matches what the compiler does. */ - ++signature; - /* Space between `<' and `:' to avoid C++ digraphs. */ - if (! flag_jni) - fputs ("JArray< ::", stream); - while (signature < limit && *signature != ';') - { - int ch = UTF8_GET (signature, limit); - if (! flag_jni) - { - if (ch == '/') - fputs ("::", stream); - else - jcf_print_char (stream, ch); - } - } - if (! flag_jni) - fputs (" *> *", stream); - *need_space = 0; - ctype = NULL; - break; - default: - /* Unparseable signature. */ - return NULL; - } - - /* 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 || array_depth > 0)) - { - ctype = "jobjectArray"; - *need_space = 1; - } - /* The `printit' case will advance SIGNATURE for us. If we - don't go there, we must advance past the `;' ourselves. */ - if (ctype != NULL) - goto printit; - ++signature; - 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': - if (flag_jni) - { - /* 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 ((const char *) signature, "Ljava/lang/Class;", - sizeof ("Ljava/lang/Class;") - 1)) - ctype = "jclass"; - /* Skip leading 'L' for throwable_p call. */ - else if (throwable_p (signature + 1)) - ctype = "jthrowable"; - else - ctype = "jobject"; - - while (*signature && *signature != ';') - ++signature; - - goto printit; - } - /* Print a leading "::" so we look in the right namespace. */ - fputs ("::", stream); - ++signature; - while (*signature && *signature != ';') - { - int ch = UTF8_GET (signature, limit); - if (ch == '/') - fputs ("::", stream); - else - jcf_print_char (stream, ch); - } - fputs (" *", stream); - if (*signature == ';') - signature++; - *need_space = 0; - break; - default: - *need_space = 1; - jni_print_char (stream, *signature++); - break; - printit: - signature++; - *need_space = 1; - fputs (ctype, stream); - break; - } - - if (! flag_jni) - { - while (array_depth-- > 0) - fputs ("> *", stream); - } - - return signature; -} - -static void -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) - { - fprintf (stream, "<not a UTF8 constant>"); - found_error = 1; - } - else - { - int length = JPOOL_UTF_LENGTH (jcf, signature_index); - const unsigned char *str0 = JPOOL_UTF_DATA (jcf, signature_index); - const unsigned char *str = str0; - const unsigned char *limit = str + length; - int need_space = 0; - int is_method = str[0] == '('; - const unsigned char *next; - - /* 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) - { - while (str < limit) - { - int ch = *str++; - if (ch == ')') - break; - } - } - - /* If printing a field or an ordinary method, then print the - "return value" now. */ - if (! is_method || ! is_init) - { - next = decode_signature_piece (stream, str, limit, &need_space); - if (! next) - { - 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_full_cxx_name (stream, jcf, name_index, - signature_index, is_init, name_override, - flags); - } -} - -/* Print the unqualified method name followed by the signature. */ -static void -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); - const unsigned char *str = str0; - const unsigned char *limit = str + length; - int need_space = 0; - int is_method = str[0] == '('; - const unsigned char *next; - - if (name_override) - fputs (name_override, stream); - else if (name_index) - { - /* Declare constructors specially. */ - if (is_init) - print_base_classname (stream, jcf, jcf->this_class); - else - print_name (stream, jcf, name_index); - } - - if (flag_jni) - { - unsigned char *signature = JPOOL_UTF_DATA (jcf, signature_index); - 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), - (const char *) signature, sig_len)) - { - /* If this method is overloaded by another native method, - then include the argument information in the mangled - name. */ - unsigned char *limit = signature + sig_len; - fputs ("__", stream); - while (signature < limit) - { - int ch = UTF8_GET (signature, limit); - jni_print_char (stream, ch); - if (ch == ')') - { - /* Done. */ - break; - } - } - } - } - - if (is_method) - { - /* Have a method or a constructor. Print signature pieces - until done. */ - fputs (" (", stream); - - str = str0 + 1; - - /* In JNI mode, add extra arguments. */ - if (flag_jni) - { - /* FIXME: it would be nice to know if we are printing a decl - or a definition, and only print `env' for the latter. */ - fputs ("JNIEnv *env", stream); - - fputs ((flags & ACC_STATIC) ? ", jclass" : ", jobject", stream); - - if (*str != ')') - fputs (", ", stream); - } - - while (str < limit && *str != ')') - { - next = decode_signature_piece (stream, str, limit, &need_space); - if (! next) - { - error ("unparseable signature: '%s'", str0); - return; - } - - if (next < limit && *next != ')') - fputs (", ", stream); - str = next; - } - - fputs (")", stream); - } -} - -/* This is a helper for print_stub_or_jni. */ -static void -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_" : ""; - 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, - flags); -} - -static void -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) - { - fprintf (stream, "<not a UTF8 constant>"); - found_error = 1; - } - else - { - int length = JPOOL_UTF_LENGTH (jcf, signature_index); - const unsigned char *str0 = JPOOL_UTF_DATA (jcf, signature_index); - const unsigned char *str = str0; - const unsigned char *limit = str + length; - int need_space = 0; - int is_method = str[0] == '('; - const unsigned char *next; - - /* Don't print fields in the JNI case. */ - if (! is_method && flag_jni) - return; - - if (flag_jni && ! stubs) - 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 - constructor. */ - if (is_method && ! is_init) - { - while (str < limit) - { - int ch = *str++; - if (ch == ')') - break; - } - } - - /* If printing a field or an ordinary method, then print the - "return value" now. Note that a constructor can't be native, - so we don't bother checking this in the JNI case. */ - if (! is_method || ! is_init) - { - next = decode_signature_piece (stream, str, limit, &need_space); - if (! next) - { - 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. */ - 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, - flags); - - /* Print the body. */ - if (stubs) - { - if (flag_jni) - fputs ("\n{\n (*env)->FatalError (env, \"", stream); - else - 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); - fprintf (stream, " not implemented\")%s;\n}\n\n", - flag_jni ? "" : ")"); - } - } -} - -static void -print_mangled_classname (FILE *stream, JCF *jcf, const char *prefix, int index) -{ - int name_index = JPOOL_USHORT1 (jcf, index); - fputs (prefix, stream); - jcf_print_utf8_replace (out, - JPOOL_UTF_DATA (jcf, name_index), - JPOOL_UTF_LENGTH (jcf, name_index), - '/', '_'); -} - -/* Print PREFIX, then a class name in C++ format. If the name refers - to an array, ignore it and don't print PREFIX. Returns 1 if - something was printed, 0 otherwise. */ -static int -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; - const unsigned char *s, *p, *limit; - - s = JPOOL_UTF_DATA (jcf, name_index); - len = JPOOL_UTF_LENGTH (jcf, name_index); - limit = s + len; - - /* Explicitly omit arrays here. */ - p = s; - c = UTF8_GET (p, limit); - if (c == '[') - return 0; - - fputs (prefix, stream); - - /* Print a leading "::" so we look in the right namespace. */ - if (! flag_jni && ! stubs && add_scope) - fputs ("::", stream); - - while (s < limit) - { - c = UTF8_GET (s, limit); - if (c == '/') - fputs (flag_jni ? "_" : "::", stream); - else - jni_print_char (stream, c); - } - - return 1; -} - -int written_class_count = 0; - -/* Return name of superclass. If LEN is not NULL, fill it with length - of name. */ -static const unsigned char * -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); - const unsigned char *supername = - JPOOL_UTF_DATA (derived_jcf, supername_index); - - if (len) - *len = supername_length; - - 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"); - } - } -} - - - -/* 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_include (FILE *out, const unsigned char *utf8, int len) -{ - struct include *incl; - - if (! out) - return; - - if (len == -1) - 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, (const char *) utf8, len)) - return; - } - - incl = XNEW (struct include); - incl->name = XNEWVEC (char, len + 1); - strncpy (incl->name, (const char *) utf8, len); - incl->name[len] = '\0'; - incl->next = all_includes; - all_includes = incl; - - fputs ("#include <", out); - jcf_print_utf8_replace (out, utf8, len, - '/', - flag_jni ? '_' : '/'); - 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; -}; - -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 = -{ - 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 (const unsigned char *name, const unsigned char *name_limit, - struct namelet *parent) -{ - const 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 ((const char *) name, JAVALANG, sizeof (JAVALANG) - 1)) - || (name_limit - name >= (int) sizeof (JAVAUTIL) - 1 - && ! strncmp ((const char *) name, JAVAUTIL, sizeof (JAVAUTIL) - 1)) - || (name_limit - name >= (int) sizeof (JAVAIO) - 1 - && ! strncmp ((const char *) name, JAVAIO, sizeof (JAVAIO) - 1))) - return; - } - - for (p = name; p < name_limit && *p != '/'; ++p) - ; - - /* Search for this name beneath the PARENT node. */ - for (np = parent->subnamelets; np != NULL; np = np->next) - { - /* We check the length because we might have a proper prefix. */ - if ((int) strlen (np->name) == p - name && - ! strncmp ((const char *) name, np->name, p - name)) - { - n = np; - break; - } - } - - if (n == NULL) - { - n = XNEW (struct namelet); - n->name = XNEWVEC (char, p - name + 1); - strncpy (n->name, (const char *) name, p - name); - n->name[p - name] = '\0'; - 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) - add_namelet (p + 1, name_limit, n); -} - -/* Print a single namelet. Destroys namelets while printing. */ -static void -print_namelet (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; - } - name->subnamelets = NULL; - - if (name->name) - { - if (term) - { - for (i = 0; i < depth; ++i) - fputc (' ', out); - fputs ("}\n", out); - /* Only print a `;' when printing a class. C++ is evil. */ - if (name->is_class) - fputs (";", 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 (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; - - for (i = 0; i < len; ++i) - { - int start; - - /* If we see an array, then we include the array header. */ - if (s[i] == '[') - { - print_include (out, (const unsigned char *) "gcj/array", -1); - continue; - } - - /* We're looking for `L<stuff>;' -- everything else is - ignorable. */ - if (s[i] != 'L') - continue; - - for (start = ++i; i < len && s[i] != ';'; ++i) - ; - - 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 (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; - const unsigned char *s; - - s = JPOOL_UTF_DATA (jcf, name_index); - len = JPOOL_UTF_LENGTH (jcf, name_index); - add_namelet (s, s + len, &root); - - 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 -process_file (JCF *jcf, FILE *out) -{ - int code, i; - uint32 field_start, method_end, method_start; - - current_jcf = jcf; - - last_access = -1; - - if (jcf_parse_preamble (jcf) != 0) - { - error ("Not a valid Java .class file."); - return; - } - - /* Parse and possibly print constant pool */ - code = jcf_parse_constant_pool (jcf); - if (code != 0) - { - error ("error while parsing constant pool"); - return; - } - code = verify_constant_pool (jcf); - if (code > 0) - { - error ("error in constant pool entry #%d", code); - return; - } - - jcf_parse_class (jcf); - - if (written_class_count++ == 0 && out) - { - const char *cstart, *cstart2, *mode, *cend, *what, *jflag; - if (flag_jni) - { - cstart = "/*"; - cstart2 = " "; - cend = " */"; - mode = ""; - what = "JNI"; - jflag = " -jni"; - } - else - { - cstart = "//"; - cstart2 = "//"; - cend = ""; - mode = " -*- c++ -*-"; - what = "CNI"; - jflag = ""; - } - - if (! stubs) - fprintf (out, "%s DO NOT EDIT THIS FILE - it is machine generated%s%s\n\n", - cstart, mode, cend); - else - { - fprintf (out, "%s This file was created by `" TOOLNAME " -stubs%s'.%s\n\ -%s\n\ -%s This file is intended to give you a head start on implementing native\n\ -%s methods using %s.\n\ -%s Be aware: running `" TOOLNAME " -stubs %s' once more for this class may\n\ -%s overwrite any edits you have made to this file.%s\n\n", - cstart, jflag, mode, - cstart2, - cstart2, - cstart2, - what, - cstart2, - jflag, - cstart2, - cend); - } - } - - if (out) - { - if (! stubs) - { - 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"); - - if (flag_jni) - { - fprintf (out, "#include <jni.h>\n\n"); - fprintf (out, "#ifdef __cplusplus\n"); - fprintf (out, "extern \"C\"\n"); - fprintf (out, "{\n"); - fprintf (out, "#endif\n"); - } - else - { - /* 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) - { - int super_length; - const unsigned char *supername = - super_class_name (jcf, &super_length); - - fputs ("\n", out); - print_include (out, supername, super_length); - } - } - } - else - { - /* Strip off the ".class" portion of the name when printing - the include file name. */ - char *name; - int i, len = strlen (jcf->classname); - if (len > 6 && ! strcmp (&jcf->classname[len - 6], ".class")) - len -= 6; - /* Turn the class name into a file name. */ - name = XNEWVEC (char, 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); - } - } - } - - /* 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); - - if (out && ! flag_jni) - { - if (! stubs) - 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); - - if (! stubs) - { - if (! print_cxx_classname (out, "class ", jcf, - jcf->this_class, 0)) - { - error ("class is of array type\n"); - return; - } - if (jcf->super_class) - { - if (! print_cxx_classname (out, " : public ", - jcf, jcf->super_class, 1)) - { - error ("base class is of array type"); - return; - } - } - - fputs ("\n{\n", out); - } - } - - /* 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); - 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); - - if (out && ! stubs) - { - if (flag_jni) - { - fprintf (out, "\n#ifdef __cplusplus\n"); - fprintf (out, "}\n"); - fprintf (out, "#endif\n"); - } - else - { - /* 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 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); - 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"); - } -} - - - -/* 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 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) -#define OPT_FORCE LONG_OPT (15) -#define OPT_OLD LONG_OPT (16) -#define OPT_TRACE LONG_OPT (17) - -static const struct option options[] = -{ - { "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 }, - { "force", no_argument, NULL, OPT_FORCE }, - /* If the output file should be named "ld" then a space is needed - between -o and its argument, ld. */ - { "old", no_argument, NULL, OPT_OLD }, - { "trace", no_argument, NULL, OPT_TRACE }, - { NULL, required_argument, NULL, 'J' }, - { NULL, no_argument, NULL, 0 } -}; - -static void -usage (void) -{ - fprintf (stderr, _("Try '" TOOLNAME " --help' for more information.\n")); - exit (1); -} - -static void -help (void) -{ - printf (_("Usage: " TOOLNAME " [OPTION]... CLASS...\n\n")); - printf (_("Generate C or 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 (_(" -force Always overwrite output files\n")); - printf (_(" -old Unused compatibility option\n")); - printf (_(" -trace Unused compatibility option\n")); - printf (_(" -J OPTION Unused compatibility option\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 ("\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 ("\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" - "%s.\n"), bug_report_url); - exit (0); -} - -static void -version (void) -{ - printf (TOOLNAME " (GCC) %s\n\n", version_string); - printf ("Copyright %s 2006 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 -main (int argc, char** argv) -{ - JCF jcf; - int argi; - char *output_file = NULL; - int emit_dependencies = 0, suppress_output = 0; - int opt; - int local_found_error; - - /* Unlock the stdio streams. */ - unlock_std_streams (); - - gcc_init_libintl (); - - if (argc <= 1) - { - error ("no classes specified"); - usage (); - } - - jcf_path_init (); - - /* We use getopt_long_only to allow single `-' long options. For - some of our options this is more natural. */ - while ((opt = getopt_long_only (argc, argv, "J:I:d:o:v", options, NULL)) != -1) - { - switch (opt) - { - case 0: - /* Already handled. */ - break; - - case 'o': - output_file = optarg; - break; - - case 'd': - output_directory = optarg; - break; - - case 'I': - jcf_path_include_arg (optarg); - break; - - case 'v': - verbose++; - break; - - case OPT_classpath: - jcf_path_classpath_arg (optarg); - break; - - case OPT_bootclasspath: - jcf_path_bootclasspath_arg (optarg); - break; - - case OPT_extdirs: - jcf_path_extdirs_arg (optarg); - break; - - case OPT_HELP: - help (); - break; - - case OPT_TEMP: - temp_directory = optarg; - break; - - case OPT_VERSION: - version (); - break; - - case OPT_PREPEND: - if (prepend_count == 0) - prepend_specs = XNEWVEC (char *, argc); - prepend_specs[prepend_count++] = optarg; - break; - - case OPT_FRIEND: - if (friend_count == 0) - friend_specs = XNEWVEC (char *, argc); - friend_specs[friend_count++] = optarg; - break; - - case OPT_ADD: - if (add_count == 0) - add_specs = XNEWVEC (char *, argc); - add_specs[add_count++] = optarg; - break; - - case OPT_APPEND: - if (append_count == 0) - append_specs = XNEWVEC (char *, argc); - append_specs[append_count++] = optarg; - break; - - case OPT_M: - emit_dependencies = 1; - suppress_output = 1; - jcf_dependency_init (1); - break; - - case OPT_MM: - emit_dependencies = 1; - suppress_output = 1; - jcf_dependency_init (0); - break; - - case OPT_MG: - error ("'-MG' option is unimplemented"); - exit (1); - - case OPT_MD: - emit_dependencies = 1; - jcf_dependency_init (1); - break; - - case OPT_MMD: - emit_dependencies = 1; - jcf_dependency_init (0); - break; - - case OPT_FORCE: - break; - - case OPT_OLD: - break; - - case OPT_TRACE: - break; - - case 'J': - /* Ignore -J options. */ - break; - - default: - usage (); - break; - } - } - - if (optind == argc) - { - error ("no classes specified"); - usage (); - } - - jcf_path_seal (verbose); - - if (output_file && emit_dependencies) - { - error ("can't specify both -o and -MD"); - exit (1); - } - - local_found_error = 0; - for (argi = optind; argi < argc; argi++) - { - char *classname = argv[argi]; - char *current_output_file = NULL; - const char *classfile_name; - - /* We reset the error state here so that we can detect errors - that occur when processing this file, so the output can be - unlinked if need be. */ - found_error = 0; - - if (verbose) - printf (_("Processing %s\n"), classname); - if (! output_file) - jcf_dependency_reset (); - classfile_name = find_class (classname, strlen (classname), &jcf, 0); - if (classfile_name == NULL) - { - error ("%s: no such class", classname); - exit (1); - } - if (verbose) - printf (_("Found in %s\n"), classfile_name); - if (output_file) - { - if (strcmp (output_file, "-") == 0) - out = stdout; - else if (out == NULL) - { - out = fopen (output_file, "w"); - } - if (out == NULL) - { - perror (output_file); - exit (1); - } - current_output_file = output_file; - } - else - { - int dir_len = strlen (output_directory); - int i, classname_length = strlen (classname); - current_output_file = XNEWVEC (char, 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++] = '/'; - for (i = 0; classname[i] != '\0'; i++) - { - char ch = classname[i]; - if (ch == '.') - ch = '/'; - if (flag_jni && ch == '/') - ch = '_'; - current_output_file[dir_len++] = ch; - } - if (emit_dependencies) - { - 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, - stubs ? (flag_jni ? ".c" : ".cc") : ".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); - } - } - } - free_method_name_list (); - process_file (&jcf, out); - JCF_FINISH (&jcf); - - /* If we found an error and we're writing to a real file, - delete it. */ - if (found_error && ! suppress_output && current_output_file != NULL - && strcmp (current_output_file, "-")) - unlink (current_output_file); - - if (current_output_file != output_file) - free (current_output_file); - jcf_dependency_write (); - - local_found_error |= found_error; - } - - if (out != NULL && out != stdout) - fclose (out); - - return local_found_error; -} |