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