| /* 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 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 |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU CC; see the file COPYING. If not, write to |
| the Free Software Foundation, 59 Temple Place - Suite 330, |
| Boston, MA 02111-1307, USA. |
| |
| Java and all Java-based marks are trademarks or registered trademarks |
| of Sun Microsystems, Inc. in the United States and other countries. |
| The Free Software Foundation is independent of Sun Microsystems, Inc. */ |
| |
| /* Written by Per Bothner <bothner@cygnus.com>, February 1996. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include <math.h> |
| |
| #include "jcf.h" |
| #include "tree.h" |
| #include "java-tree.h" |
| #include "java-opcodes.h" |
| |
| /* The output file. */ |
| FILE *out = NULL; |
| |
| /* Nonzero on failure. */ |
| static int found_error = 0; |
| |
| /* Directory to place resulting files in. Set by -d option. */ |
| const char *output_directory = ""; |
| |
| /* Directory to place temporary file. Set by -td option. Currently unused. */ |
| 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; |
| |
| #define ACC_VISIBILITY (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED) |
| |
| /* Pass this macro the flags for a class and for a method. It will |
| return true if the method should be considered `final'. */ |
| #define METHOD_IS_FINAL(Class, Method) \ |
| (((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, int, const char *)); |
| static void decompile_method PROTO ((FILE *, JCF *, int)); |
| static void add_class_decl PROTO ((FILE *, JCF *, JCF_u2)); |
| |
| static int java_float_finite PROTO ((jfloat)); |
| static int java_double_finite PROTO ((jdouble)); |
| |
| 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) \ |
| 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) |
| |
| 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" |
| |
| /* Some useful constants. */ |
| #define F_NAN_MASK 0x7f800000 |
| #define D_NAN_MASK 0x7ff0000000000000LL |
| |
| /* Return 1 if F is not Inf or NaN. */ |
| static int |
| java_float_finite (f) |
| jfloat 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 (u.i & F_NAN_MASK) != F_NAN_MASK; |
| } |
| |
| /* Return 1 if D is not Inf or NaN. */ |
| static int |
| java_double_finite (d) |
| jdouble d; |
| { |
| union { |
| jdouble d; |
| int64 i; |
| } u; |
| u.d = d; |
| |
| /* Now check for all NaNs. */ |
| return (u.i & D_NAN_MASK) != D_NAN_MASK; |
| } |
| |
| void |
| DEFUN(print_name, (stream, jcf, name_index), |
| FILE* stream AND JCF* jcf AND int name_index) |
| { |
| if (JPOOL_TAG (jcf, name_index) != CONSTANT_Utf8) |
| fprintf (stream, "<not a UTF8 constant>"); |
| else |
| jcf_print_utf8 (stream, JPOOL_UTF_DATA (jcf, name_index), |
| JPOOL_UTF_LENGTH (jcf, name_index)); |
| } |
| |
| /* Print base name of class. The base name is everything after the |
| final separator. */ |
| |
| static void |
| print_base_classname (stream, jcf, index) |
| FILE *stream; |
| JCF *jcf; |
| int index; |
| { |
| int name_index = JPOOL_USHORT1 (jcf, index); |
| int len; |
| 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, nonzero otherwise. */ |
| |
| static int |
| utf8_cmp (str, length, name) |
| unsigned char *str; |
| int length; |
| char *name; |
| { |
| unsigned char *limit = str + length; |
| int i; |
| |
| for (i = 0; name[i]; ++i) |
| { |
| int ch = UTF8_GET (str, limit); |
| if (ch != name[i]) |
| return 1; |
| } |
| |
| return str != limit; |
| } |
| |
| /* If NAME is the name of a C++ keyword, then return an override name. |
| This is a name that can be used in place of the keyword. |
| Otherwise, return NULL. FIXME: for now, we only handle those |
| keywords we know to be a problem for libgcj. */ |
| |
| static char * |
| cxx_keyword_subst (str, length) |
| unsigned char *str; |
| int length; |
| { |
| if (! utf8_cmp (str, length, "delete")) |
| return "__dummy_delete"; |
| else if (! utf8_cmp (str, length, "enum")) |
| return "__dummy_enum"; |
| return NULL; |
| } |
| |
| /* Generate an access control keyword based on FLAGS. Returns 0 if |
| FLAGS matches the saved access information, nonzero otherwise. */ |
| |
| static void |
| generate_access (stream, flags) |
| 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 (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; |
| } |
| |
| /* Get name of a field. This handles renamings due to C++ clash. */ |
| static char * |
| get_field_name (jcf, name_index, flags) |
| JCF *jcf; |
| int name_index; |
| JCF_u2 flags; |
| { |
| 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)) |
| { |
| fprintf (stderr, "static field has same name as method\n"); |
| found_error = 1; |
| return NULL; |
| } |
| |
| override = (char *) malloc (length + 3); |
| memcpy (override, name, length); |
| strcpy (override + length, "__"); |
| } |
| else if ((override = cxx_keyword_subst (name, length)) != NULL) |
| { |
| /* Must malloc OVERRIDE. */ |
| char *o2 = (char *) malloc (strlen (override) + 1); |
| strcpy (o2, override); |
| override = o2; |
| } |
| |
| return override; |
| } |
| |
| /* Print a field name. Convenience function for use with |
| get_field_name. */ |
| static void |
| print_field_name (stream, jcf, name_index, flags) |
| FILE *stream; |
| JCF *jcf; |
| int name_index; |
| JCF_u2 flags; |
| { |
| 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 |
| 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; |
| |
| generate_access (stream, flags); |
| if (JPOOL_TAG (jcf, name_index) != CONSTANT_Utf8) |
| { |
| fprintf (stream, "<not a UTF8 constant>"); |
| found_error = 1; |
| return; |
| } |
| |
| if (flags & ACC_FINAL) |
| { |
| if (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; |
| fputs (" static const jint ", out); |
| print_field_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: |
| { |
| jlong num; |
| int most_negative = 0; |
| fputs (" static const jlong ", out); |
| print_field_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: |
| { |
| jfloat fnum = JPOOL_FLOAT (jcf, current_field_value); |
| fputs (" static const jfloat ", out); |
| print_field_name (out, jcf, name_index); |
| if (! java_float_finite (fnum)) |
| fputs (";\n", out); |
| else |
| fprintf (out, " = %.10g;\n", fnum); |
| } |
| break; |
| case CONSTANT_Double: |
| { |
| jdouble dnum = JPOOL_DOUBLE (jcf, current_field_value); |
| fputs (" static const jdouble ", out); |
| print_field_name (out, jcf, name_index); |
| if (! java_double_finite (dnum)) |
| fputs (";\n", out); |
| else |
| fprintf (out, " = %.17g;\n", dnum); |
| } |
| break; |
| default: |
| /* We can't print this as a constant, but we can still |
| print something sensible. */ |
| done = 0; |
| break; |
| } |
| |
| if (done) |
| return; |
| } |
| } |
| |
| fputs (" ", out); |
| if ((flags & ACC_STATIC)) |
| fputs ("static ", out); |
| |
| override = get_field_name (jcf, name_index, flags); |
| print_c_decl (out, jcf, name_index, sig_index, 0, override); |
| fputs (";\n", out); |
| |
| if (override) |
| free (override); |
| } |
| |
| static void |
| DEFUN(print_method_info, (stream, jcf, name_index, sig_index, flags), |
| FILE *stream AND JCF* jcf |
| AND int name_index AND int sig_index AND JCF_u2 flags) |
| { |
| 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] == '<' || str[0] == '$') |
| { |
| /* Ignore internally generated methods like <clinit> and |
| $finit$. 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; |
| } |
| 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. We |
| can't just ignore the function, because that will cause incorrect |
| code to be generated if the function is virtual (not only for |
| calls to this function for for other functions after it in the |
| vtbl). So we give it a dummy name instead. */ |
| override = cxx_keyword_subst (str, length); |
| if (override) |
| { |
| /* 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; |
| } |
| |
| method_printed = 1; |
| generate_access (stream, flags); |
| |
| fputs (" ", out); |
| if ((flags & ACC_STATIC)) |
| fputs ("static ", out); |
| 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, 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; |
| |
| /* 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'. */ |
| 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, is_init, |
| name_override), |
| FILE* stream AND JCF* jcf |
| 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>"); |
| 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 need_space = 0; |
| int is_method = str[0] == '('; |
| 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) |
| { |
| fprintf (stderr, "unparseable signature: `%s'\n", str0); |
| found_error = 1; |
| return; |
| } |
| } |
| |
| /* Now print the name of the thing. */ |
| if (need_space) |
| fputs (" ", stream); |
| 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 (is_method) |
| { |
| /* Have a method or a constructor. Print signature pieces |
| until done. */ |
| fputs (" (", stream); |
| str = str0 + 1; |
| 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); |
| } |
| } |
| } |
| |
| void |
| DEFUN(print_mangled_classname, (stream, jcf, prefix, index), |
| FILE *stream AND JCF *jcf AND const char *prefix AND 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 (stream, prefix, jcf, index) |
| FILE *stream; |
| char *prefix; |
| JCF *jcf; |
| int index; |
| { |
| int name_index = JPOOL_USHORT1 (jcf, index); |
| int len, c; |
| 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); |
| while (s < limit) |
| { |
| c = UTF8_GET (s, limit); |
| if (c == '/') |
| fputs ("::", stream); |
| else |
| jcf_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 unsigned char * |
| super_class_name (derived_jcf, len) |
| 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); |
| unsigned char *supername = JPOOL_UTF_DATA (derived_jcf, supername_index); |
| |
| if (len) |
| *len = supername_length; |
| |
| return supername; |
| } |
| |
| |
| |
| /* 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 (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) |
| { |
| /* We check the length because we might have a proper prefix. */ |
| if (len == (int) strlen (incl->name) |
| && ! 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) |
| { |
| /* We check the length because we might have a proper prefix. */ |
| if ((int) strlen (np->name) == p - name && |
| ! 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; |
| { |
| 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 = 0; i < len; ++i) |
| { |
| int start, saw_dollar; |
| |
| /* If we see an array, then we include the array header. */ |
| if (s[i] == '[') |
| { |
| 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 (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 = jcf; |
| |
| last_access = -1; |
| |
| if (jcf_parse_preamble (jcf) != 0) |
| { |
| fprintf (stderr, "Not a valid Java .class file.\n"); |
| found_error = 1; |
| return; |
| } |
| |
| /* Parse and possibly print constant pool */ |
| code = jcf_parse_constant_pool (jcf); |
| if (code != 0) |
| { |
| fprintf (stderr, "error while parsing constant pool\n"); |
| found_error = 1; |
| return; |
| } |
| code = verify_constant_pool (jcf); |
| if (code > 0) |
| { |
| fprintf (stderr, "error in constant pool entry #%d\n", code); |
| found_error = 1; |
| return; |
| } |
| |
| jcf_parse_class (jcf); |
| |
| if (written_class_count++ == 0 && out) |
| fputs ("// DO NOT EDIT THIS FILE - it is machine generated -*- c++ -*-\n\n", |
| out); |
| |
| 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"); |
| |
| /* 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 && out) |
| { |
| int super_length; |
| unsigned char *supername = super_class_name (jcf, &super_length); |
| |
| fputs ("\n", out); |
| print_include (out, supername, super_length); |
| } |
| |
| /* 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); |
| } |
| |
| if (out && ! print_cxx_classname (out, "class ", jcf, jcf->this_class)) |
| { |
| fprintf (stderr, "class is of array type\n"); |
| found_error = 1; |
| return; |
| } |
| if (out && jcf->super_class) |
| { |
| if (! print_cxx_classname (out, " : public ", jcf, jcf->super_class)) |
| { |
| fprintf (stderr, "base class is of array type\n"); |
| found_error = 1; |
| return; |
| } |
| } |
| if (out) |
| fputs ("\n{\n", out); |
| |
| /* 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); |
| |
| 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]); |
| |
| 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"); |
| } |
| } |
| |
| static void |
| usage () |
| { |
| fprintf (stderr, "gcjh: no classes specified\n"); |
| exit (1); |
| } |
| |
| static void |
| help () |
| { |
| 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"); |
| printf (" -td DIRECTORY Set temporary directory name\n"); |
| printf (" -v, --verbose Print extra information while running\n"); |
| printf (" --version Print version number, then exit\n"); |
| /* FIXME: print bug-report information. */ |
| exit (0); |
| } |
| |
| static void |
| java_no_argument (opt) |
| char *opt; |
| { |
| fprintf (stderr, "gcjh: no argument given for option `%s'\n", opt); |
| exit (1); |
| } |
| |
| static void |
| version () |
| { |
| /* FIXME: use version.c? */ |
| 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"); |
| exit (0); |
| } |
| |
| int |
| DEFUN(main, (argc, argv), |
| int argc AND char** 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]; |
| |
| if (arg[0] != '-' || ! strcmp (arg, "--")) |
| break; |
| |
| /* Just let all arguments be given in either "-" or "--" form. */ |
| if (arg[1] == '-') |
| ++arg; |
| |
| if (strcmp (arg, "-o") == 0) |
| { |
| if (argi + 1 < argc) |
| output_file = argv[++argi]; |
| else |
| java_no_argument (argv[argi]); |
| } |
| else if (strcmp (arg, "-d") == 0) |
| { |
| if (argi + 1 < argc) |
| output_directory = argv[++argi]; |
| else |
| java_no_argument (argv[argi]); |
| } |
| else if (strcmp (arg, "-td") == 0) |
| { |
| if (argi + 1 < argc) |
| temp_directory = argv[++argi]; |
| else |
| java_no_argument (argv[argi]); |
| } |
| else if (strcmp (arg, "-prepend") == 0) |
| { |
| if (argi + 1 < argc) |
| { |
| if (prepend_count == 0) |
| prepend_specs = (char**) ALLOC ((argc-argi) * sizeof (char*)); |
| prepend_specs[prepend_count++] = argv[++argi]; |
| } |
| else |
| java_no_argument (argv[argi]); |
| } |
| else if (strcmp (arg, "-friend") == 0) |
| { |
| if (argi + 1 < argc) |
| { |
| if (friend_count == 0) |
| friend_specs = (char**) ALLOC ((argc-argi) * sizeof (char*)); |
| friend_specs[friend_count++] = argv[++argi]; |
| } |
| else |
| java_no_argument (argv[argi]); |
| } |
| else if (strcmp (arg, "-add") == 0) |
| { |
| if (argi + 1 < argc) |
| { |
| if (add_count == 0) |
| add_specs = (char**) ALLOC ((argc-argi) * sizeof (char*)); |
| add_specs[add_count++] = argv[++argi]; |
| } |
| else |
| java_no_argument (argv[argi]); |
| } |
| else if (strcmp (arg, "-append") == 0) |
| { |
| if (argi + 1 < argc) |
| { |
| if (append_count == 0) |
| append_specs = (char**) ALLOC ((argc-argi) * sizeof (char*)); |
| append_specs[append_count++] = 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 (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) |
| stubs++; |
| else if (strcmp (arg, "-help") == 0) |
| 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]); |
| exit (1); |
| } |
| } |
| |
| if (argi == argc) |
| usage (); |
| |
| jcf_path_seal (); |
| |
| if (output_file && emit_dependencies) |
| { |
| fprintf (stderr, "gcjh: can't specify both -o and -MD\n"); |
| exit (1); |
| } |
| |
| for (; argi < argc; argi++) |
| { |
| char *classname = argv[argi]; |
| char *classfile_name, *current_output_file; |
| |
| if (verbose) |
| fprintf (stderr, "Processing %s\n", classname); |
| if (! output_file) |
| jcf_dependency_reset (); |
| classfile_name = find_class (classname, strlen (classname), &jcf, 0); |
| if (classfile_name == NULL) |
| { |
| fprintf (stderr, "%s: no such class\n", classname); |
| exit (1); |
| } |
| if (verbose) |
| fprintf (stderr, "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 = (char*) ALLOC (dir_len + classname_length + 4); |
| 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 = '/'; |
| 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, ".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) |
| fclose (out); |
| |
| return found_error; |
| } |
| |
| /* TODO: |
| |
| * Do whatever the javah -stubs flag does. |
| |
| * Emit "structure forward declarations" when needed. |
| |
| * Generate C headers, like javah |
| |
| */ |