]>
Commit | Line | Data |
---|---|---|
e04a16fb AG |
1 | /* Program to write C++-suitable header files from a Java(TM) .class |
2 | file. This is similar to SUN's javah. | |
3 | ||
a6d6c2c0 | 4 | Copyright (C) 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 |
f309ff0a | 5 | Free Software Foundation, Inc. |
e04a16fb | 6 | |
f309ff0a SB |
7 | This file is part of GCC. |
8 | ||
9 | GCC is free software; you can redistribute it and/or modify | |
e04a16fb AG |
10 | it under the terms of the GNU General Public License as published by |
11 | the Free Software Foundation; either version 2, or (at your option) | |
12 | any later version. | |
13 | ||
f309ff0a | 14 | GCC is distributed in the hope that it will be useful, |
e04a16fb AG |
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | GNU General Public License for more details. | |
18 | ||
19 | You should have received a copy of the GNU General Public License | |
f309ff0a | 20 | along with GCC; see the file COPYING. If not, write to |
f12c30a8 KC |
21 | the Free Software Foundation, 51 Franklin Street, Fifth Floor, |
22 | Boston, MA 02110-1301, USA. | |
e04a16fb AG |
23 | |
24 | Java and all Java-based marks are trademarks or registered trademarks | |
25 | of Sun Microsystems, Inc. in the United States and other countries. | |
26 | The Free Software Foundation is independent of Sun Microsystems, Inc. */ | |
27 | ||
28 | /* Written by Per Bothner <bothner@cygnus.com>, February 1996. */ | |
29 | ||
9caaf519 KG |
30 | #include "config.h" |
31 | #include "system.h" | |
4977bab6 ZW |
32 | #include "coretypes.h" |
33 | #include "tm.h" | |
4f65832d KG |
34 | #include <math.h> |
35 | ||
e04a16fb | 36 | #include "jcf.h" |
4f65832d | 37 | #include "tree.h" |
a757585a | 38 | #include "version.h" |
9b4a08f9 | 39 | #include "javaop.h" |
4f65832d | 40 | #include "java-tree.h" |
92c068d1 | 41 | #include "java-opcodes.h" |
17211ab5 | 42 | #include "ggc.h" |
6b9b5de2 | 43 | #include "hashtab.h" |
0237b2dc | 44 | #include "intl.h" |
92c068d1 | 45 | |
c89c53eb TT |
46 | #include <getopt.h> |
47 | ||
48 | \f | |
49 | ||
e04a16fb AG |
50 | /* The output file. */ |
51 | FILE *out = NULL; | |
52 | ||
53 | /* Nonzero on failure. */ | |
54 | static int found_error = 0; | |
55 | ||
d8be0aab TF |
56 | #ifdef JNI_DEFAULT |
57 | #define TOOLNAME "gjnih" | |
58 | ||
de380723 | 59 | /* Nonzero if we're generating JNI output. */ |
d8be0aab TF |
60 | int flag_jni = 1; |
61 | #else | |
62 | #define TOOLNAME "gcjh" | |
63 | ||
a6d682a0 | 64 | int flag_jni = 0; |
d8be0aab | 65 | #endif |
de380723 | 66 | |
ee142fe7 | 67 | /* When nonzero, warn when source file is newer than matching class |
b7436b72 TT |
68 | file. */ |
69 | int flag_newer = 1; | |
70 | ||
e04a16fb | 71 | /* Directory to place resulting files in. Set by -d option. */ |
82698cde | 72 | static const char *output_directory = ""; |
e04a16fb | 73 | |
e04a16fb | 74 | /* Directory to place temporary file. Set by -td option. Currently unused. */ |
82698cde | 75 | static const char *temp_directory = "/tmp"; |
e04a16fb AG |
76 | |
77 | /* Number of friend functions we have to declare. */ | |
78 | static int friend_count; | |
79 | ||
80 | /* A class can optionally have a `friend' function declared. If | |
81 | non-NULL, this is that function. */ | |
82 | static char **friend_specs = NULL; | |
83 | ||
84 | /* Number of lines we are prepending before the class. */ | |
85 | static int prepend_count; | |
86 | ||
87 | /* We can prepend extra lines before the class's start. */ | |
88 | static char **prepend_specs = NULL; | |
89 | ||
90 | /* Number of lines we are appending at the end of the class. */ | |
91 | static int add_count; | |
92 | ||
93 | /* We can append extra lines just before the class's end. */ | |
94 | static char **add_specs = NULL; | |
95 | ||
96 | /* Number of lines we are appending after the class. */ | |
97 | static int append_count; | |
98 | ||
99 | /* We can append extra lines after the class's end. */ | |
100 | static char **append_specs = NULL; | |
101 | ||
102 | int verbose = 0; | |
103 | ||
104 | int stubs = 0; | |
105 | ||
106 | struct JCF *current_jcf; | |
e04a16fb AG |
107 | |
108 | /* This holds access information for the last field we examined. They | |
109 | let us generate "private:", "public:", and "protected:" properly. | |
110 | If 0 then we haven't previously examined any field. */ | |
111 | static JCF_u2 last_access; | |
112 | ||
bdb59aec TT |
113 | /* Pass this macro the flags for a class and for a method. It will |
114 | return true if the method should be considered `final'. */ | |
115 | #define METHOD_IS_FINAL(Class, Method) \ | |
116 | (((Class) & ACC_FINAL) || ((Method) & (ACC_FINAL | ACC_PRIVATE))) | |
117 | ||
6c9c5c1f AG |
118 | /* Pass this macro the flags for a method. It will return true if the |
119 | method is native. */ | |
120 | #define METHOD_IS_NATIVE(Method) \ | |
121 | ((Method) & ACC_NATIVE) | |
122 | ||
36739040 TT |
123 | #define METHOD_IS_PRIVATE(Class, Method) \ |
124 | (((Method) & ACC_PRIVATE) != 0) | |
125 | ||
e4de5a10 PB |
126 | /* We keep a linked list of all method names we have seen. This lets |
127 | us determine if a method name and a field name are in conflict. */ | |
128 | struct method_name | |
129 | { | |
130 | unsigned char *name; | |
131 | int length; | |
de380723 TT |
132 | unsigned char *signature; |
133 | int sig_length; | |
3fb577a5 | 134 | int is_native; |
e4de5a10 PB |
135 | struct method_name *next; |
136 | }; | |
137 | ||
138 | /* List of method names we've seen. */ | |
139 | static struct method_name *method_name_list; | |
e04a16fb | 140 | |
d2097937 KG |
141 | static void print_field_info (FILE*, JCF*, int, int, JCF_u2); |
142 | static void print_mangled_classname (FILE*, JCF*, const char*, int); | |
143 | static int print_cxx_classname (FILE*, const char*, JCF*, int, int); | |
144 | static void print_method_info (FILE*, JCF*, int, int, JCF_u2); | |
145 | static void print_c_decl (FILE*, JCF*, int, int, int, const char *, int); | |
146 | static void print_stub_or_jni (FILE*, JCF*, int, int, int, const char *, int); | |
147 | static void print_full_cxx_name (FILE*, JCF*, int, int, int, const char *, int); | |
e263bc93 | 148 | static void decompile_method (FILE*, JCF*, int) ATTRIBUTE_UNUSED; |
d2097937 KG |
149 | static void add_class_decl (FILE*, JCF*, JCF_u2); |
150 | ||
d2097937 KG |
151 | static void print_name (FILE *, JCF *, int); |
152 | static void print_base_classname (FILE *, JCF *, int); | |
153 | static int utf8_cmp (const unsigned char *, int, const char *); | |
154 | static char *cxx_keyword_subst (const unsigned char *, int); | |
155 | static void generate_access (FILE *, JCF_u2); | |
156 | static int name_is_method_p (const unsigned char *, int); | |
157 | static char *get_field_name (JCF *, int, JCF_u2); | |
158 | static void print_field_name (FILE *, JCF *, int, JCF_u2); | |
159 | static const unsigned char *super_class_name (JCF *, int *); | |
160 | static void print_include (FILE *, const unsigned char *, int); | |
161 | static int gcjh_streq (const void *p1, const void *p2); | |
162 | static int throwable_p (const unsigned char *signature); | |
163 | static const unsigned char * | |
164 | decode_signature_piece (FILE *, const unsigned char *, | |
165 | const unsigned char *, int *); | |
166 | static void print_class_decls (FILE *, JCF *, int); | |
74118f15 | 167 | static void error (const char *gmsgid, ...) ATTRIBUTE_PRINTF_1; |
d2097937 KG |
168 | static void usage (void) ATTRIBUTE_NORETURN; |
169 | static void help (void) ATTRIBUTE_NORETURN; | |
170 | static void version (void) ATTRIBUTE_NORETURN; | |
171 | static int overloaded_jni_method_exists_p (const unsigned char *, int, | |
172 | const char *, int); | |
173 | static void jni_print_char (FILE *, int); | |
649eaf9b ZW |
174 | static void jni_print_float (FILE *, jfloat); |
175 | static void jni_print_double (FILE *, jdouble); | |
d2097937 | 176 | static void decompile_return_statement (FILE *, JCF *, int, int, int); |
08967681 | 177 | |
73cb3b5a TT |
178 | static void handle_inner_classes (int); |
179 | ||
e04a16fb AG |
180 | JCF_u2 current_field_name; |
181 | JCF_u2 current_field_value; | |
182 | JCF_u2 current_field_signature; | |
183 | JCF_u2 current_field_flags; | |
184 | ||
185 | #define HANDLE_START_FIELD(ACCESS_FLAGS, NAME, SIGNATURE, ATTRIBUTE_COUNT) \ | |
186 | ( current_field_name = (NAME), current_field_signature = (SIGNATURE), \ | |
187 | current_field_flags = (ACCESS_FLAGS), current_field_value = 0) | |
188 | ||
bdb59aec TT |
189 | /* We pass over fields twice. The first time we just note the types |
190 | of the fields and then the start of the methods. Then we go back | |
191 | and parse the fields for real. This is ugly. */ | |
e4de5a10 | 192 | static int field_pass; |
bdb59aec TT |
193 | /* Likewise we pass over methods twice. The first time we generate |
194 | class decl information; the second time we generate actual method | |
195 | decls. */ | |
196 | static int method_pass; | |
197 | ||
198 | #define HANDLE_END_FIELD() \ | |
199 | if (field_pass) \ | |
200 | { \ | |
5e74a4eb | 201 | if (out && ! stubs) \ |
bdb59aec TT |
202 | print_field_info (out, jcf, current_field_name, \ |
203 | current_field_signature, \ | |
c89c53eb | 204 | current_field_flags); \ |
bdb59aec | 205 | } \ |
de380723 TT |
206 | else if (! stubs && ! flag_jni) \ |
207 | add_class_decl (out, jcf, current_field_signature); | |
e04a16fb AG |
208 | |
209 | #define HANDLE_CONSTANTVALUE(VALUEINDEX) current_field_value = (VALUEINDEX) | |
210 | ||
8c2dfb32 | 211 | static int method_declared = 0; |
cb975f38 | 212 | static int method_access = 0; |
bdb59aec | 213 | static int method_printed = 0; |
f0f3a777 | 214 | static int method_synthetic = 0; |
150f086d TT |
215 | static int method_signature = 0; |
216 | ||
68566610 AH |
217 | /* Set to 1 while the very first data member of a class is being handled. */ |
218 | static int is_first_data_member = 0; | |
219 | ||
f0f3a777 APB |
220 | #define HANDLE_METHOD(ACCESS_FLAGS, NAME, SIGNATURE, ATTRIBUTE_COUNT) \ |
221 | { \ | |
222 | method_synthetic = 0; \ | |
4440ffc4 | 223 | method_printed = 0; \ |
897c9e66 | 224 | decompiled = 0; \ |
150f086d | 225 | method_signature = SIGNATURE; \ |
f0f3a777 APB |
226 | if (ATTRIBUTE_COUNT) \ |
227 | method_synthetic = peek_attribute (jcf, ATTRIBUTE_COUNT, \ | |
228 | (const char *)"Synthetic", 9); \ | |
229 | /* If a synthetic methods have been declared, its attribute aren't \ | |
230 | worth reading (and triggering side-effects). We skip them an \ | |
231 | set ATTRIBUTE_COUNT to zero so that they'll be skipped in \ | |
232 | jcf_parse_one_method. */ \ | |
233 | if (method_synthetic) \ | |
234 | { \ | |
235 | skip_attribute (jcf, ATTRIBUTE_COUNT); \ | |
236 | ATTRIBUTE_COUNT = 0; \ | |
237 | } \ | |
238 | if (method_pass && !method_synthetic) \ | |
239 | { \ | |
f0f3a777 APB |
240 | if (out) \ |
241 | print_method_info (out, jcf, NAME, SIGNATURE, \ | |
150f086d | 242 | ACCESS_FLAGS); \ |
f0f3a777 APB |
243 | } \ |
244 | else if (!method_synthetic) \ | |
245 | { \ | |
246 | print_method_info (NULL, jcf, NAME, SIGNATURE, \ | |
150f086d | 247 | ACCESS_FLAGS); \ |
f0f3a777 APB |
248 | if (! stubs && ! flag_jni) \ |
249 | add_class_decl (out, jcf, SIGNATURE); \ | |
250 | } \ | |
251 | } | |
252 | ||
e263bc93 TL |
253 | /* Only include byte-code decompilation optimizations for ELF targets |
254 | since the generated headers are only known to work with ELF weak | |
f48c58e8 | 255 | symbol semantics. Specifically, these optimizations are known to |
e263bc93 TL |
256 | not work on PE-COFF and possibly others. */ |
257 | #ifdef OBJECT_FORMAT_ELF | |
f0f3a777 | 258 | #define HANDLE_CODE_ATTRIBUTE(MAX_STACK, MAX_LOCALS, CODE_LENGTH) \ |
8c2dfb32 | 259 | if (out && method_declared) decompile_method (out, jcf, CODE_LENGTH); |
e263bc93 | 260 | #endif |
92c068d1 TT |
261 | |
262 | static int decompiled = 0; | |
f0f3a777 APB |
263 | #define HANDLE_END_METHOD() \ |
264 | if (out && method_printed && !method_synthetic) \ | |
265 | fputs (decompiled || stubs ? "\n" : ";\n", out); | |
e04a16fb | 266 | |
73cb3b5a TT |
267 | #define HANDLE_INNERCLASSES_ATTRIBUTE(COUNT) handle_inner_classes (COUNT) |
268 | ||
80393943 KG |
269 | /* We're going to need {peek,skip}_attribute, enable their definition. */ |
270 | #define NEED_PEEK_ATTRIBUTE | |
271 | #define NEED_SKIP_ATTRIBUTE | |
272 | ||
e04a16fb AG |
273 | #include "jcf-reader.c" |
274 | ||
4b794eaf JJ |
275 | /* Print an error message and set found_error. |
276 | Not really gcc-internal-format message, but as error elsewhere | |
277 | uses it, assume all users will use intersection between | |
278 | c-format and gcc-internal-format. */ | |
0237b2dc | 279 | static void |
4b794eaf | 280 | error (const char *gmsgid, ...) |
0237b2dc JM |
281 | { |
282 | va_list ap; | |
283 | ||
4b794eaf | 284 | va_start (ap, gmsgid); |
0237b2dc | 285 | |
d8be0aab | 286 | fprintf (stderr, TOOLNAME ": "); |
4b794eaf | 287 | vfprintf (stderr, _(gmsgid), ap); |
0237b2dc JM |
288 | va_end (ap); |
289 | fprintf (stderr, "\n"); | |
290 | found_error = 1; | |
291 | } | |
292 | ||
649eaf9b ZW |
293 | /* Print a single-precision float, suitable for parsing by g++. */ |
294 | static void | |
295 | jni_print_float (FILE *stream, jfloat f) | |
e04a16fb | 296 | { |
649eaf9b ZW |
297 | /* It'd be nice to use __builtin_nan/__builtin_inf here but they don't |
298 | work in data initializers. FIXME. */ | |
299 | if (JFLOAT_FINITE (f)) | |
300 | { | |
f619dcab RM |
301 | if (flag_jni) |
302 | { | |
303 | fputs (" ", out); | |
304 | if (f.negative) | |
305 | putc ('-', stream); | |
306 | if (f.exponent) | |
307 | fprintf (stream, "0x1.%.6xp%+df", | |
308 | ((unsigned int)f.mantissa) << 1, | |
309 | f.exponent - JFLOAT_EXP_BIAS); | |
310 | else | |
311 | /* Exponent of 0x01 is -125; exponent of 0x00 is *also* -125, | |
312 | because the implicit leading 1 bit is no longer present. */ | |
313 | fprintf (stream, "0x0.%.6xp%+df", | |
314 | ((unsigned int)f.mantissa) << 1, | |
315 | f.exponent + 1 - JFLOAT_EXP_BIAS); | |
316 | } | |
649eaf9b | 317 | } |
5e74a4eb TT |
318 | if (! flag_jni) |
319 | fputs (";\n", stream); | |
e04a16fb AG |
320 | } |
321 | ||
649eaf9b ZW |
322 | /* Print a double-precision float, suitable for parsing by g++. */ |
323 | static void | |
324 | jni_print_double (FILE *stream, jdouble f) | |
e04a16fb | 325 | { |
649eaf9b ZW |
326 | /* It'd be nice to use __builtin_nan/__builtin_inf here but they don't |
327 | work in data initializers. FIXME. */ | |
328 | if (JDOUBLE_FINITE (f)) | |
329 | { | |
f619dcab RM |
330 | if (flag_jni) |
331 | { | |
332 | fputs (" ", out); | |
333 | if (f.negative) | |
334 | putc ('-', stream); | |
335 | if (f.exponent) | |
336 | fprintf (stream, "0x1.%.5x%.8xp%+d", | |
337 | f.mantissa0, f.mantissa1, | |
338 | f.exponent - JDOUBLE_EXP_BIAS); | |
339 | else | |
340 | /* Exponent of 0x001 is -1022; exponent of 0x000 is *also* -1022, | |
341 | because the implicit leading 1 bit is no longer present. */ | |
342 | fprintf (stream, "0x0.%.5x%.8xp%+d", | |
343 | f.mantissa0, f.mantissa1, | |
344 | f.exponent + 1 - JDOUBLE_EXP_BIAS); | |
345 | } | |
649eaf9b | 346 | } |
5e74a4eb | 347 | fputs (flag_jni ? "\n" : ";\n", stream); |
e04a16fb AG |
348 | } |
349 | ||
78de74be TT |
350 | /* Print a character, appropriately mangled for JNI. */ |
351 | ||
352 | static void | |
0a2f0c54 | 353 | jni_print_char (FILE *stream, int ch) |
78de74be TT |
354 | { |
355 | if (! flag_jni) | |
356 | jcf_print_char (stream, ch); | |
357 | else if (ch == '(' || ch == ')') | |
358 | { | |
359 | /* Ignore. */ | |
360 | } | |
361 | else if (ch == '_') | |
362 | fputs ("_1", stream); | |
363 | else if (ch == ';') | |
364 | fputs ("_2", stream); | |
365 | else if (ch == '[') | |
366 | fputs ("_3", stream); | |
367 | else if (ch == '/') | |
368 | fputs ("_", stream); | |
299b5f9e | 369 | else if (ISALNUM (ch)) |
78de74be TT |
370 | fputc (ch, stream); |
371 | else | |
372 | { | |
373 | /* "Unicode" character. */ | |
374 | fprintf (stream, "_0%04x", ch); | |
375 | } | |
376 | } | |
377 | ||
c45c5e09 TT |
378 | /* Print a name from the class data. If the index does not point to a |
379 | string, an error results. */ | |
380 | ||
381 | static void | |
6ff2fe39 | 382 | print_name (FILE* stream, JCF* jcf, int name_index) |
c45c5e09 TT |
383 | { |
384 | if (JPOOL_TAG (jcf, name_index) != CONSTANT_Utf8) | |
385 | { | |
386 | fprintf (stream, "<not a UTF8 constant>"); | |
387 | found_error = 1; | |
388 | } | |
389 | else if (! flag_jni) | |
390 | jcf_print_utf8 (stream, JPOOL_UTF_DATA (jcf, name_index), | |
391 | JPOOL_UTF_LENGTH (jcf, name_index)); | |
392 | else | |
393 | { | |
394 | /* For JNI we must correctly quote each character. */ | |
395 | const unsigned char *str = JPOOL_UTF_DATA (jcf, name_index); | |
396 | int length = JPOOL_UTF_LENGTH (jcf, name_index); | |
397 | const unsigned char *limit = str + length; | |
398 | while (str < limit) | |
399 | { | |
400 | int ch = UTF8_GET (str, limit); | |
401 | if (ch < 0) | |
402 | { | |
403 | fprintf (stream, "\\<invalid>"); | |
404 | return; | |
405 | } | |
406 | jni_print_char (stream, ch); | |
407 | } | |
408 | } | |
409 | } | |
410 | ||
e04a16fb AG |
411 | /* Print base name of class. The base name is everything after the |
412 | final separator. */ | |
413 | ||
414 | static void | |
0a2f0c54 | 415 | print_base_classname (FILE *stream, JCF *jcf, int index) |
e04a16fb AG |
416 | { |
417 | int name_index = JPOOL_USHORT1 (jcf, index); | |
fc45c7ef | 418 | int len; |
be245ac0 | 419 | const unsigned char *s, *p, *limit; |
e04a16fb AG |
420 | |
421 | s = JPOOL_UTF_DATA (jcf, name_index); | |
422 | len = JPOOL_UTF_LENGTH (jcf, name_index); | |
423 | limit = s + len; | |
424 | p = s; | |
425 | while (s < limit) | |
426 | { | |
427 | int c = UTF8_GET (s, limit); | |
428 | if (c == '/') | |
429 | p = s; | |
430 | } | |
431 | ||
432 | while (p < limit) | |
433 | { | |
434 | int ch = UTF8_GET (p, limit); | |
435 | if (ch == '/') | |
436 | fputs ("::", stream); | |
437 | else | |
438 | jcf_print_char (stream, ch); | |
439 | } | |
440 | } | |
441 | ||
83c1f628 TT |
442 | /* Return 0 if NAME is equal to STR, -1 if STR is "less" than NAME, |
443 | and 1 if STR is "greater" than NAME. */ | |
e04a16fb AG |
444 | |
445 | static int | |
0a2f0c54 | 446 | utf8_cmp (const unsigned char *str, int length, const char *name) |
e04a16fb | 447 | { |
be245ac0 | 448 | const unsigned char *limit = str + length; |
e04a16fb AG |
449 | int i; |
450 | ||
451 | for (i = 0; name[i]; ++i) | |
452 | { | |
453 | int ch = UTF8_GET (str, limit); | |
454 | if (ch != name[i]) | |
83c1f628 | 455 | return ch - name[i]; |
e04a16fb AG |
456 | } |
457 | ||
83c1f628 | 458 | return str == limit ? 0 : 1; |
e04a16fb AG |
459 | } |
460 | ||
83c1f628 TT |
461 | /* This is a sorted list of all C++ keywords. */ |
462 | ||
fd033052 | 463 | static const char *const cxx_keywords[] = |
83c1f628 | 464 | { |
f2a29271 BM |
465 | "_Complex", |
466 | "__alignof", | |
467 | "__alignof__", | |
468 | "__asm", | |
469 | "__asm__", | |
470 | "__attribute", | |
471 | "__attribute__", | |
472 | "__builtin_va_arg", | |
473 | "__complex", | |
474 | "__complex__", | |
475 | "__const", | |
476 | "__const__", | |
477 | "__extension__", | |
478 | "__imag", | |
479 | "__imag__", | |
480 | "__inline", | |
481 | "__inline__", | |
482 | "__label__", | |
483 | "__null", | |
484 | "__real", | |
485 | "__real__", | |
486 | "__restrict", | |
487 | "__restrict__", | |
488 | "__signed", | |
489 | "__signed__", | |
490 | "__typeof", | |
491 | "__typeof__", | |
492 | "__volatile", | |
493 | "__volatile__", | |
f2a29271 BM |
494 | "and", |
495 | "and_eq", | |
7b35f6c9 | 496 | "asm", |
83c1f628 | 497 | "auto", |
f2a29271 BM |
498 | "bitand", |
499 | "bitor", | |
83c1f628 | 500 | "bool", |
f2a29271 BM |
501 | "break", |
502 | "case", | |
503 | "catch", | |
504 | "char", | |
505 | "class", | |
506 | "compl", | |
507 | "const", | |
83c1f628 | 508 | "const_cast", |
f2a29271 BM |
509 | "continue", |
510 | "default", | |
83c1f628 | 511 | "delete", |
f2a29271 BM |
512 | "do", |
513 | "double", | |
83c1f628 | 514 | "dynamic_cast", |
f2a29271 | 515 | "else", |
83c1f628 TT |
516 | "enum", |
517 | "explicit", | |
f2a29271 | 518 | "export", |
83c1f628 | 519 | "extern", |
f2a29271 BM |
520 | "false", |
521 | "float", | |
522 | "for", | |
83c1f628 | 523 | "friend", |
f2a29271 BM |
524 | "goto", |
525 | "if", | |
83c1f628 | 526 | "inline", |
f2a29271 BM |
527 | "int", |
528 | "long", | |
83c1f628 TT |
529 | "mutable", |
530 | "namespace", | |
f2a29271 BM |
531 | "new", |
532 | "not", | |
533 | "not_eq", | |
534 | "operator", | |
535 | "or", | |
536 | "or_eq", | |
537 | "private", | |
538 | "protected", | |
539 | "public", | |
83c1f628 TT |
540 | "register", |
541 | "reinterpret_cast", | |
f2a29271 BM |
542 | "return", |
543 | "short", | |
83c1f628 TT |
544 | "signed", |
545 | "sizeof", | |
f2a29271 | 546 | "static", |
83c1f628 TT |
547 | "static_cast", |
548 | "struct", | |
f2a29271 | 549 | "switch", |
83c1f628 | 550 | "template", |
f2a29271 BM |
551 | "this", |
552 | "throw", | |
553 | "true", | |
554 | "try", | |
83c1f628 | 555 | "typedef", |
f2a29271 | 556 | "typeid", |
7b35f6c9 | 557 | "typename", |
f2a29271 | 558 | "typeof", |
83c1f628 TT |
559 | "union", |
560 | "unsigned", | |
561 | "using", | |
562 | "virtual", | |
f2a29271 | 563 | "void", |
83c1f628 | 564 | "volatile", |
f2a29271 BM |
565 | "wchar_t", |
566 | "while", | |
567 | "xor", | |
568 | "xor_eq" | |
83c1f628 TT |
569 | }; |
570 | ||
571 | ||
5877286e TT |
572 | /* If NAME is the name of a C++ keyword, then return an override name. |
573 | This is a name that can be used in place of the keyword. | |
83c1f628 | 574 | Otherwise, return NULL. The return value is malloc()d. */ |
5877286e | 575 | |
83c1f628 | 576 | static char * |
0a2f0c54 | 577 | cxx_keyword_subst (const unsigned char *str, int length) |
5877286e | 578 | { |
94574545 | 579 | int last = ARRAY_SIZE (cxx_keywords); |
83c1f628 | 580 | int first = 0; |
ecb36604 TT |
581 | int mid = (last + first) / 2; |
582 | int old = -1; | |
83c1f628 | 583 | |
ecb36604 TT |
584 | for (mid = (last + first) / 2; |
585 | mid != old; | |
586 | old = mid, mid = (last + first) / 2) | |
83c1f628 | 587 | { |
0160fbe1 TT |
588 | int kwl = strlen (cxx_keywords[mid]); |
589 | int min_length = kwl > length ? length : kwl; | |
590 | int r = utf8_cmp (str, min_length, cxx_keywords[mid]); | |
ecb36604 | 591 | |
83c1f628 TT |
592 | if (r == 0) |
593 | { | |
0160fbe1 TT |
594 | int i; |
595 | ||
596 | /* Skip all trailing `$'. */ | |
597 | for (i = min_length; i < length && str[i] == '$'; ++i) | |
598 | ; | |
599 | /* We've only found a match if all the remaining characters | |
600 | are `$'. */ | |
601 | if (i == length) | |
602 | { | |
5ed6ace5 | 603 | char *dup = XNEWVEC (char, 2 + length - min_length + kwl); |
0160fbe1 TT |
604 | strcpy (dup, cxx_keywords[mid]); |
605 | for (i = kwl; i < length + 1; ++i) | |
606 | dup[i] = '$'; | |
607 | dup[i] = '\0'; | |
608 | return dup; | |
609 | } | |
610 | r = 1; | |
83c1f628 | 611 | } |
0160fbe1 TT |
612 | |
613 | if (r < 0) | |
83c1f628 TT |
614 | last = mid; |
615 | else | |
616 | first = mid; | |
617 | } | |
5877286e TT |
618 | return NULL; |
619 | } | |
620 | ||
de380723 | 621 | /* Generate an access control keyword based on FLAGS. */ |
e04a16fb AG |
622 | |
623 | static void | |
0a2f0c54 | 624 | generate_access (FILE *stream, JCF_u2 flags) |
e04a16fb | 625 | { |
e04a16fb AG |
626 | if ((flags & ACC_VISIBILITY) == last_access) |
627 | return; | |
628 | last_access = (flags & ACC_VISIBILITY); | |
629 | ||
630 | switch (last_access) | |
631 | { | |
3217b0f3 PB |
632 | case 0: |
633 | fputs ("public: // actually package-private\n", stream); | |
634 | break; | |
e04a16fb AG |
635 | case ACC_PUBLIC: |
636 | fputs ("public:\n", stream); | |
637 | break; | |
638 | case ACC_PRIVATE: | |
639 | fputs ("private:\n", stream); | |
640 | break; | |
641 | case ACC_PROTECTED: | |
3217b0f3 | 642 | fputs ("public: // actually protected\n", stream); |
e04a16fb AG |
643 | break; |
644 | default: | |
645 | found_error = 1; | |
646 | fprintf (stream, "#error unrecognized visibility %d\n", | |
647 | (flags & ACC_VISIBILITY)); | |
648 | break; | |
649 | } | |
650 | } | |
651 | ||
e4de5a10 PB |
652 | /* See if NAME is already the name of a method. */ |
653 | static int | |
0a2f0c54 | 654 | name_is_method_p (const unsigned char *name, int length) |
e4de5a10 PB |
655 | { |
656 | struct method_name *p; | |
657 | ||
658 | for (p = method_name_list; p != NULL; p = p->next) | |
659 | { | |
660 | if (p->length == length && ! memcmp (p->name, name, length)) | |
661 | return 1; | |
662 | } | |
663 | return 0; | |
664 | } | |
665 | ||
b8b639c9 TT |
666 | /* Free the method name list. */ |
667 | static void | |
e72222ac | 668 | free_method_name_list (void) |
b8b639c9 TT |
669 | { |
670 | struct method_name *p = method_name_list; | |
671 | while (p != NULL) | |
672 | { | |
673 | struct method_name *next = p->next; | |
674 | free (p->name); | |
675 | free (p->signature); | |
676 | free (p); | |
677 | p = next; | |
678 | } | |
679 | method_name_list = NULL; | |
680 | } | |
681 | ||
3fb577a5 | 682 | /* If there is already a native method named NAME, whose signature is not |
de380723 TT |
683 | SIGNATURE, then return true. Otherwise return false. */ |
684 | static int | |
0a2f0c54 KG |
685 | overloaded_jni_method_exists_p (const unsigned char *name, int length, |
686 | const char *signature, int sig_length) | |
de380723 TT |
687 | { |
688 | struct method_name *p; | |
689 | ||
690 | for (p = method_name_list; p != NULL; p = p->next) | |
691 | { | |
3fb577a5 RM |
692 | if (p->is_native |
693 | && p->length == length | |
de380723 TT |
694 | && ! memcmp (p->name, name, length) |
695 | && (p->sig_length != sig_length | |
696 | || memcmp (p->signature, signature, sig_length))) | |
697 | return 1; | |
698 | } | |
699 | return 0; | |
700 | } | |
701 | ||
5877286e TT |
702 | /* Get name of a field. This handles renamings due to C++ clash. */ |
703 | static char * | |
0a2f0c54 | 704 | get_field_name (JCF *jcf, int name_index, JCF_u2 flags) |
5877286e TT |
705 | { |
706 | unsigned char *name = JPOOL_UTF_DATA (jcf, name_index); | |
707 | int length = JPOOL_UTF_LENGTH (jcf, name_index); | |
708 | char *override; | |
be245ac0 | 709 | |
5877286e TT |
710 | if (name_is_method_p (name, length)) |
711 | { | |
712 | /* This field name matches a method. So override the name with | |
713 | a dummy name. This is yucky, but it isn't clear what else to | |
714 | do. FIXME: if the field is static, then we'll be in real | |
715 | trouble. */ | |
716 | if ((flags & ACC_STATIC)) | |
717 | { | |
0237b2dc | 718 | error ("static field has same name as method"); |
5877286e TT |
719 | return NULL; |
720 | } | |
721 | ||
5ed6ace5 | 722 | override = XNEWVEC (char, length + 3); |
5877286e TT |
723 | memcpy (override, name, length); |
724 | strcpy (override + length, "__"); | |
725 | } | |
3d4da704 TT |
726 | else if (flag_jni) |
727 | override = NULL; | |
be245ac0 | 728 | else |
83c1f628 TT |
729 | override = cxx_keyword_subst (name, length); |
730 | ||
5877286e TT |
731 | return override; |
732 | } | |
733 | ||
734 | /* Print a field name. Convenience function for use with | |
735 | get_field_name. */ | |
736 | static void | |
0a2f0c54 | 737 | print_field_name (FILE *stream, JCF *jcf, int name_index, JCF_u2 flags) |
5877286e TT |
738 | { |
739 | char *override = get_field_name (jcf, name_index, flags); | |
740 | ||
741 | if (override) | |
742 | { | |
743 | fputs (override, stream); | |
744 | free (override); | |
745 | } | |
746 | else | |
747 | jcf_print_utf8 (stream, JPOOL_UTF_DATA (jcf, name_index), | |
748 | JPOOL_UTF_LENGTH (jcf, name_index)); | |
749 | } | |
750 | ||
e04a16fb | 751 | static void |
6ff2fe39 KG |
752 | print_field_info (FILE *stream, JCF* jcf, int name_index, int sig_index, |
753 | JCF_u2 flags) | |
e04a16fb | 754 | { |
e4de5a10 PB |
755 | char *override = NULL; |
756 | ||
5e74a4eb TT |
757 | if (! flag_jni) |
758 | generate_access (stream, flags); | |
5877286e TT |
759 | if (JPOOL_TAG (jcf, name_index) != CONSTANT_Utf8) |
760 | { | |
761 | fprintf (stream, "<not a UTF8 constant>"); | |
762 | found_error = 1; | |
763 | return; | |
764 | } | |
765 | ||
5e74a4eb TT |
766 | if (flag_jni) |
767 | { | |
768 | /* For JNI we only want to print real constants. */ | |
769 | int val; | |
770 | if (! (flags & ACC_STATIC) | |
771 | || ! (flags & ACC_FINAL) | |
772 | || current_field_value <= 0) | |
773 | return; | |
774 | val = JPOOL_TAG (jcf, current_field_value); | |
775 | if (val != CONSTANT_Integer && val != CONSTANT_Long | |
776 | && val != CONSTANT_Float && val != CONSTANT_Double) | |
777 | return; | |
778 | } | |
779 | else | |
780 | { | |
781 | /* Initial indentation. */ | |
782 | fputs (" ", stream); | |
783 | } | |
784 | ||
9b4579b3 | 785 | if ((flags & ACC_STATIC)) |
e04a16fb | 786 | { |
5e74a4eb TT |
787 | if (flag_jni) |
788 | { | |
789 | print_cxx_classname (stream, "#undef ", jcf, jcf->this_class, 1); | |
790 | fputs ("_", stream); | |
791 | print_field_name (stream, jcf, name_index, 0); | |
792 | fputs ("\n", stream); | |
793 | print_cxx_classname (stream, "#define ", jcf, jcf->this_class, 1); | |
794 | fputs ("_", stream); | |
795 | } | |
22f8a068 MM |
796 | else |
797 | fputs ("static ", stream); | |
dd4e1500 BM |
798 | |
799 | if ((flags & ACC_FINAL) && current_field_value > 0) | |
e04a16fb | 800 | { |
e04a16fb | 801 | char buffer[25]; |
5877286e | 802 | int done = 1; |
e04a16fb | 803 | |
e04a16fb AG |
804 | switch (JPOOL_TAG (jcf, current_field_value)) |
805 | { | |
806 | case CONSTANT_Integer: | |
11b89622 TT |
807 | { |
808 | jint num; | |
06d84d69 | 809 | int most_negative = 0; |
5e74a4eb | 810 | if (! flag_jni) |
22f8a068 | 811 | fputs ("const jint ", stream); |
5e74a4eb TT |
812 | print_field_name (stream, jcf, name_index, 0); |
813 | fputs (flag_jni ? " " : " = ", stream); | |
11b89622 | 814 | num = JPOOL_INT (jcf, current_field_value); |
06d84d69 TT |
815 | /* We single out the most negative number to print |
816 | specially. This avoids later warnings from g++. */ | |
7e21fe59 | 817 | if (num == (jint) 0x80000000) |
11b89622 | 818 | { |
06d84d69 TT |
819 | most_negative = 1; |
820 | ++num; | |
11b89622 | 821 | } |
06d84d69 | 822 | format_int (buffer, (jlong) num, 10); |
5e74a4eb TT |
823 | fprintf (stream, "%sL%s%s\n", buffer, |
824 | most_negative ? " - 1" : "", | |
825 | flag_jni ? "" : ";"); | |
11b89622 | 826 | } |
e04a16fb AG |
827 | break; |
828 | case CONSTANT_Long: | |
11b89622 TT |
829 | { |
830 | jlong num; | |
06d84d69 | 831 | int most_negative = 0; |
5e74a4eb | 832 | if (! flag_jni) |
22f8a068 | 833 | fputs ("const jlong ", stream); |
5e74a4eb TT |
834 | print_field_name (stream, jcf, name_index, 0); |
835 | fputs (flag_jni ? " " : " = ", stream); | |
11b89622 | 836 | num = JPOOL_LONG (jcf, current_field_value); |
06d84d69 TT |
837 | /* We single out the most negative number to print |
838 | specially.. This avoids later warnings from g++. */ | |
7e21fe59 | 839 | if (num == (jlong) 0x8000000000000000LL) |
11b89622 | 840 | { |
06d84d69 TT |
841 | most_negative = 1; |
842 | ++num; | |
11b89622 | 843 | } |
06d84d69 | 844 | format_int (buffer, num, 10); |
5e74a4eb TT |
845 | fprintf (stream, "%sLL%s%s\n", buffer, |
846 | most_negative ? " - 1" :"", | |
847 | flag_jni ? "" : ";"); | |
11b89622 | 848 | } |
e04a16fb AG |
849 | break; |
850 | case CONSTANT_Float: | |
851 | { | |
852 | jfloat fnum = JPOOL_FLOAT (jcf, current_field_value); | |
5e74a4eb | 853 | if (! flag_jni) |
22f8a068 | 854 | fputs ("const jfloat ", stream); |
5e74a4eb TT |
855 | print_field_name (stream, jcf, name_index, 0); |
856 | jni_print_float (stream, fnum); | |
e04a16fb AG |
857 | } |
858 | break; | |
859 | case CONSTANT_Double: | |
860 | { | |
861 | jdouble dnum = JPOOL_DOUBLE (jcf, current_field_value); | |
5e74a4eb | 862 | if (! flag_jni) |
22f8a068 | 863 | fputs ("const jdouble ", stream); |
5e74a4eb TT |
864 | print_field_name (stream, jcf, name_index, 0); |
865 | jni_print_double (stream, dnum); | |
e04a16fb AG |
866 | } |
867 | break; | |
868 | default: | |
5877286e TT |
869 | /* We can't print this as a constant, but we can still |
870 | print something sensible. */ | |
871 | done = 0; | |
872 | break; | |
e04a16fb AG |
873 | } |
874 | ||
5877286e TT |
875 | if (done) |
876 | return; | |
e04a16fb AG |
877 | } |
878 | } | |
879 | ||
5e74a4eb | 880 | /* assert (! flag_jni); */ |
5877286e | 881 | override = get_field_name (jcf, name_index, flags); |
5e74a4eb TT |
882 | print_c_decl (stream, jcf, name_index, sig_index, 0, override, flags); |
883 | fputs (";\n", stream); | |
e4de5a10 PB |
884 | |
885 | if (override) | |
886 | free (override); | |
e04a16fb AG |
887 | } |
888 | ||
1bbee75b | 889 | |
e04a16fb | 890 | static void |
6ff2fe39 KG |
891 | print_method_info (FILE *stream, JCF* jcf, int name_index, int sig_index, |
892 | JCF_u2 flags) | |
e04a16fb | 893 | { |
be245ac0 | 894 | const unsigned char *str; |
e04a16fb | 895 | int length, is_init = 0; |
83c1f628 | 896 | char *override = NULL; |
e04a16fb | 897 | |
8c2dfb32 | 898 | method_declared = 0; |
cb975f38 | 899 | method_access = flags; |
de380723 | 900 | if (stream && JPOOL_TAG (jcf, name_index) != CONSTANT_Utf8) |
e04a16fb AG |
901 | fprintf (stream, "<not a UTF8 constant>"); |
902 | str = JPOOL_UTF_DATA (jcf, name_index); | |
903 | length = JPOOL_UTF_LENGTH (jcf, name_index); | |
f0f3a777 | 904 | |
f0f3a777 | 905 | if (str[0] == '<') |
e04a16fb | 906 | { |
f0f3a777 APB |
907 | /* Ignore the internally generated method <clinit>. However, |
908 | treat <init> as a constructor. */ | |
e04a16fb AG |
909 | if (! utf8_cmp (str, length, "<init>")) |
910 | is_init = 1; | |
bdb59aec TT |
911 | else if (! METHOD_IS_FINAL (jcf->access_flags, flags) |
912 | && ! (flags & ACC_STATIC)) | |
913 | { | |
914 | /* FIXME: i18n bug here. Order of prints should not be | |
915 | fixed. */ | |
597cdf4f | 916 | fprintf (stderr, _("ignored method '")); |
bdb59aec | 917 | jcf_print_utf8 (stderr, str, length); |
0237b2dc | 918 | fprintf (stderr, _("' marked virtual\n")); |
bdb59aec TT |
919 | found_error = 1; |
920 | return; | |
921 | } | |
922 | else | |
e04a16fb AG |
923 | return; |
924 | } | |
1bbee75b BM |
925 | |
926 | /* During the first method pass, build a list of method names. This will | |
927 | be used to determine if field names conflict with method names. */ | |
928 | if (! stream) | |
e4de5a10 PB |
929 | { |
930 | struct method_name *nn; | |
931 | ||
5ed6ace5 MD |
932 | nn = XNEW (struct method_name); |
933 | nn->name = XNEWVEC (unsigned char, length); | |
e4de5a10 PB |
934 | memcpy (nn->name, str, length); |
935 | nn->length = length; | |
936 | nn->next = method_name_list; | |
de380723 | 937 | nn->sig_length = JPOOL_UTF_LENGTH (jcf, sig_index); |
5ed6ace5 | 938 | nn->signature = XNEWVEC (unsigned char, nn->sig_length); |
3fb577a5 | 939 | nn->is_native = METHOD_IS_NATIVE (flags); |
de380723 TT |
940 | memcpy (nn->signature, JPOOL_UTF_DATA (jcf, sig_index), |
941 | nn->sig_length); | |
e4de5a10 | 942 | method_name_list = nn; |
1bbee75b BM |
943 | |
944 | /* The rest of this function doesn't matter. */ | |
945 | return; | |
e4de5a10 | 946 | } |
e04a16fb | 947 | |
c45c5e09 TT |
948 | /* We don't worry about overrides in JNI mode. */ |
949 | if (! flag_jni) | |
e4de5a10 | 950 | { |
c45c5e09 TT |
951 | /* We can't generate a method whose name is a C++ reserved word. |
952 | We can't just ignore the function, because that will cause | |
953 | incorrect code to be generated if the function is virtual | |
954 | (not only for calls to this function for for other functions | |
955 | after it in the vtbl). So we give it a dummy name instead. */ | |
956 | override = cxx_keyword_subst (str, length); | |
e4de5a10 | 957 | } |
e04a16fb | 958 | |
de380723 | 959 | if (! stubs && ! flag_jni) |
e04a16fb | 960 | { |
6c9c5c1f | 961 | method_printed = 1; |
8c2dfb32 | 962 | |
6c9c5c1f AG |
963 | generate_access (stream, flags); |
964 | ||
965 | fputs (" ", out); | |
966 | if ((flags & ACC_STATIC)) | |
967 | fputs ("static ", out); | |
36739040 | 968 | else if (! METHOD_IS_PRIVATE (jcf->access_flags, flags)) |
6c9c5c1f AG |
969 | { |
970 | /* Don't print `virtual' if we have a constructor. */ | |
971 | if (! is_init) | |
972 | fputs ("virtual ", out); | |
973 | } | |
de380723 | 974 | print_c_decl (out, jcf, name_index, sig_index, is_init, override, flags); |
6c9c5c1f AG |
975 | |
976 | if ((flags & ACC_ABSTRACT)) | |
977 | fputs (" = 0", out); | |
978 | else | |
979 | method_declared = 1; | |
980 | } | |
8c2dfb32 | 981 | else |
6c9c5c1f | 982 | { |
de380723 | 983 | if (METHOD_IS_NATIVE (flags)) |
6c9c5c1f AG |
984 | { |
985 | method_printed = 1; | |
de380723 TT |
986 | print_stub_or_jni (out, jcf, name_index, sig_index, |
987 | is_init, override, flags); | |
6c9c5c1f AG |
988 | } |
989 | } | |
83c1f628 TT |
990 | |
991 | if (override) | |
992 | free (override); | |
92c068d1 | 993 | } |
e04a16fb | 994 | |
150f086d TT |
995 | /* A helper for the decompiler which prints a `return' statement where |
996 | the type is a reference type. If METHODTYPE and OBJECTTYPE are not | |
997 | identical, we emit a cast. We do this because the C++ compiler | |
998 | doesn't know that a reference can be cast to the type of an | |
999 | interface it implements. METHODTYPE is the index of the method's | |
1000 | signature. NAMEINDEX is the index of the field name; -1 for | |
1001 | `this'. OBJECTTYPE is the index of the object's type. */ | |
1002 | static void | |
0a2f0c54 KG |
1003 | decompile_return_statement (FILE *out, JCF *jcf, int methodtype, |
1004 | int nameindex, int objecttype) | |
150f086d TT |
1005 | { |
1006 | int cast = 0; | |
1007 | int obj_name_len, method_name_len; | |
1008 | const unsigned char *obj_data, *method_data; | |
1009 | ||
1010 | obj_name_len = JPOOL_UTF_LENGTH (jcf, objecttype); | |
1011 | obj_data = JPOOL_UTF_DATA (jcf, objecttype); | |
1012 | ||
1013 | method_name_len = JPOOL_UTF_LENGTH (jcf, methodtype); | |
1014 | method_data = JPOOL_UTF_DATA (jcf, methodtype); | |
1015 | ||
1016 | /* Skip forward to return type part of method. */ | |
1017 | while (*method_data != ')') | |
1018 | { | |
1019 | ++method_data; | |
1020 | --method_name_len; | |
1021 | } | |
1022 | /* Skip past `)'. */ | |
1023 | ++method_data; | |
1024 | --method_name_len; | |
1025 | ||
1026 | /* If we see an `L', skip it and the trailing `;'. */ | |
1027 | if (method_data[0] == 'L' && method_data[method_name_len - 1] == ';') | |
1028 | { | |
1029 | ++method_data; | |
1030 | method_name_len -= 2; | |
1031 | } | |
1032 | if (obj_data[0] == 'L' && obj_data[obj_name_len - 1] == ';') | |
1033 | { | |
1034 | ++obj_data; | |
1035 | obj_name_len -= 2; | |
1036 | } | |
1037 | ||
1038 | /* FIXME: if METHODTYPE is a superclass of OBJECTTYPE then we don't | |
1039 | need a cast. Right now there is no way to determine if this is | |
1040 | the case. */ | |
1041 | if (method_name_len != obj_name_len) | |
1042 | cast = 1; | |
1043 | else | |
1044 | { | |
1045 | int i; | |
1046 | for (i = 0; i < method_name_len; ++i) | |
1047 | { | |
1048 | if (method_data[i] != obj_data[i]) | |
1049 | { | |
1050 | cast = 1; | |
1051 | break; | |
1052 | } | |
1053 | } | |
1054 | } | |
1055 | ||
1056 | fputs (" { return ", out); | |
1057 | ||
1058 | if (cast) | |
1059 | { | |
1060 | int array_depth = 0; | |
1061 | const unsigned char *limit; | |
1062 | ||
1063 | fputs ("reinterpret_cast<", out); | |
1064 | ||
1065 | while (*method_data == '[') | |
1066 | { | |
1067 | ++method_data; | |
1068 | ++array_depth; | |
1069 | --method_name_len; | |
1070 | fputs ("JArray<", out); | |
1071 | } | |
1072 | ||
1073 | /* Leading space to avoid C++ digraphs. */ | |
1074 | fputs (" ::", out); | |
1075 | ||
1076 | /* If we see an `L', skip it and the trailing `;'. Only do this | |
1077 | if we've seen an array specification. If we don't have an | |
1078 | array then the `L' was stripped earlier. */ | |
1079 | if (array_depth && method_data[0] == 'L' | |
1080 | && method_data[method_name_len - 1] == ';') | |
1081 | { | |
1082 | ++method_data; | |
1083 | method_name_len -= 2; | |
1084 | } | |
1085 | ||
1086 | limit = method_data + method_name_len; | |
1087 | while (method_data < limit) | |
1088 | { | |
1089 | int ch = UTF8_GET (method_data, limit); | |
1090 | if (ch == '/') | |
1091 | fputs ("::", out); | |
1092 | else | |
1093 | jcf_print_char (out, ch); | |
1094 | } | |
1095 | fputs (" *", out); | |
1096 | ||
1097 | /* Close each array. */ | |
1098 | while (array_depth > 0) | |
1099 | { | |
1100 | fputs ("> *", out); | |
1101 | --array_depth; | |
1102 | } | |
1103 | ||
1104 | /* Close the cast. */ | |
1105 | fputs ("> (", out); | |
1106 | } | |
1107 | ||
1108 | if (nameindex == -1) | |
1109 | fputs ("this", out); | |
1110 | else | |
1111 | print_field_name (out, jcf, nameindex, 0); | |
1112 | ||
1113 | if (cast) | |
1114 | fputs (")", out); | |
1115 | ||
1116 | fputs ("; }", out); | |
1117 | } | |
1118 | ||
1119 | ||
92c068d1 TT |
1120 | /* Try to decompile a method body. Right now we just try to handle a |
1121 | simple case that we can do. Expand as desired. */ | |
1122 | static void | |
0a2f0c54 | 1123 | decompile_method (FILE *out, JCF *jcf, int code_len) |
92c068d1 | 1124 | { |
be245ac0 | 1125 | const unsigned char *codes = jcf->read_ptr; |
92c068d1 TT |
1126 | int index; |
1127 | uint16 name_and_type, name; | |
1128 | ||
cb975f38 TT |
1129 | /* If the method is synchronized, don't touch it. */ |
1130 | if ((method_access & ACC_SYNCHRONIZED)) | |
1131 | return; | |
1132 | ||
92c068d1 TT |
1133 | if (code_len == 5 |
1134 | && codes[0] == OPCODE_aload_0 | |
1135 | && codes[1] == OPCODE_getfield | |
cb975f38 TT |
1136 | && (codes[4] == OPCODE_areturn |
1137 | || codes[4] == OPCODE_dreturn | |
1138 | || codes[4] == OPCODE_freturn | |
1139 | || codes[4] == OPCODE_ireturn | |
1140 | || codes[4] == OPCODE_lreturn)) | |
92c068d1 | 1141 | { |
cb975f38 | 1142 | /* Found code like `return FIELD'. */ |
92c068d1 TT |
1143 | index = (codes[2] << 8) | codes[3]; |
1144 | /* FIXME: ensure that tag is CONSTANT_Fieldref. */ | |
92c068d1 TT |
1145 | name_and_type = JPOOL_USHORT2 (jcf, index); |
1146 | /* FIXME: ensure that tag is CONSTANT_NameAndType. */ | |
1147 | name = JPOOL_USHORT1 (jcf, name_and_type); | |
150f086d TT |
1148 | if (codes[4] == OPCODE_areturn) |
1149 | decompile_return_statement (out, jcf, method_signature, | |
1150 | name, JPOOL_USHORT2 (jcf, name_and_type)); | |
1151 | else | |
1152 | { | |
1153 | fputs (" { return ", out); | |
1154 | /* FIXME: flags. */ | |
1155 | print_field_name (out, jcf, name, 0); | |
1156 | fputs ("; }", out); | |
1157 | } | |
92c068d1 TT |
1158 | decompiled = 1; |
1159 | } | |
cb975f38 TT |
1160 | else if (code_len == 2 |
1161 | && codes[0] == OPCODE_aload_0 | |
5fcab235 TT |
1162 | && codes[1] == OPCODE_areturn |
1163 | /* We're going to generate `return this'. This only makes | |
1164 | sense for non-static methods. */ | |
1165 | && ! (method_access & ACC_STATIC)) | |
cb975f38 | 1166 | { |
150f086d TT |
1167 | decompile_return_statement (out, jcf, method_signature, -1, |
1168 | JPOOL_USHORT1 (jcf, jcf->this_class)); | |
cb975f38 TT |
1169 | decompiled = 1; |
1170 | } | |
1171 | else if (code_len == 1 && codes[0] == OPCODE_return) | |
1172 | { | |
1173 | /* Found plain `return'. */ | |
1174 | fputs (" { }", out); | |
1175 | decompiled = 1; | |
1176 | } | |
8c2dfb32 TT |
1177 | else if (code_len == 2 |
1178 | && codes[0] == OPCODE_aconst_null | |
1179 | && codes[1] == OPCODE_areturn) | |
1180 | { | |
1181 | /* Found `return null'. We don't want to depend on NULL being | |
1182 | defined. */ | |
1183 | fputs (" { return 0; }", out); | |
1184 | decompiled = 1; | |
1185 | } | |
e04a16fb AG |
1186 | } |
1187 | ||
6b9b5de2 TT |
1188 | /* Like strcmp, but invert the return result for the hash table. This |
1189 | should probably be in hashtab.c to complement the existing string | |
1190 | hash function. */ | |
1191 | static int | |
0a2f0c54 | 1192 | gcjh_streq (const void *p1, const void *p2) |
6b9b5de2 TT |
1193 | { |
1194 | return ! strcmp ((char *) p1, (char *) p2); | |
1195 | } | |
1196 | ||
64e68197 BM |
1197 | /* Return 1 if the initial part of CLNAME names a subclass of throwable, |
1198 | or 0 if not. CLNAME may be extracted from a signature, and can be | |
1199 | terminated with either `;' or NULL. */ | |
6b9b5de2 | 1200 | static int |
0a2f0c54 | 1201 | throwable_p (const unsigned char *clname) |
6b9b5de2 TT |
1202 | { |
1203 | int length; | |
1204 | unsigned char *current; | |
1205 | int i; | |
1206 | int result = 0; | |
1207 | ||
1208 | /* We keep two hash tables of class names. In one we list all the | |
1209 | classes which are subclasses of Throwable. In the other we will | |
1210 | all other classes. We keep two tables to make the code a bit | |
1211 | simpler; we don't have to have a structure mapping class name to | |
1212 | a `throwable?' bit. */ | |
1213 | static htab_t throw_hash; | |
1214 | static htab_t non_throw_hash; | |
1215 | static int init_done = 0; | |
1216 | ||
1217 | if (! init_done) | |
1218 | { | |
77d3109b KG |
1219 | void **slot; |
1220 | unsigned char *str; | |
6b9b5de2 TT |
1221 | |
1222 | /* Self-initializing. The cost of this really doesn't matter. | |
1223 | We also don't care about freeing these, either. */ | |
1224 | throw_hash = htab_create (10, htab_hash_string, gcjh_streq, | |
1225 | (htab_del) free); | |
1226 | non_throw_hash = htab_create (10, htab_hash_string, gcjh_streq, | |
1227 | (htab_del) free); | |
1228 | ||
1229 | /* Make sure the root classes show up in the tables. */ | |
0e7d217a | 1230 | str = (unsigned char *) xstrdup ("java.lang.Throwable"); |
6b9b5de2 | 1231 | slot = htab_find_slot (throw_hash, str, INSERT); |
77d3109b | 1232 | *slot = str; |
6b9b5de2 | 1233 | |
0e7d217a | 1234 | str = (unsigned char *) xstrdup ("java.lang.Object"); |
6b9b5de2 | 1235 | slot = htab_find_slot (non_throw_hash, str, INSERT); |
77d3109b | 1236 | *slot = str; |
6b9b5de2 TT |
1237 | |
1238 | init_done = 1; | |
1239 | } | |
1240 | ||
64e68197 | 1241 | for (length = 0; clname[length] != ';' && clname[length] != '\0'; ++length) |
6b9b5de2 | 1242 | ; |
5ed6ace5 | 1243 | current = XNEWVEC (unsigned char, length + 1); |
64e68197 BM |
1244 | for (i = 0; i < length; ++i) |
1245 | current[i] = clname[i] == '/' ? '.' : clname[i]; | |
1246 | current[length] = '\0'; | |
6b9b5de2 TT |
1247 | |
1248 | /* We don't compute the hash slot here because the table might be | |
1249 | modified by the recursion. In that case the slot could be | |
1250 | invalidated. */ | |
1251 | if (htab_find (throw_hash, current)) | |
1252 | result = 1; | |
1253 | else if (htab_find (non_throw_hash, current)) | |
1254 | result = 0; | |
1255 | else | |
1256 | { | |
1257 | JCF jcf; | |
77d3109b | 1258 | void **slot; |
64e68197 BM |
1259 | unsigned char *super, *tmp; |
1260 | int super_length = -1; | |
0e7d217a | 1261 | const char *classfile_name = find_class ((char *) current, strlen ((const char *) current), |
6b9b5de2 TT |
1262 | &jcf, 0); |
1263 | ||
1264 | if (! classfile_name) | |
1265 | { | |
0237b2dc | 1266 | error ("couldn't find class %s", current); |
6b9b5de2 TT |
1267 | return 0; |
1268 | } | |
1269 | if (jcf_parse_preamble (&jcf) != 0 | |
1270 | || jcf_parse_constant_pool (&jcf) != 0 | |
1271 | || verify_constant_pool (&jcf) > 0) | |
1272 | { | |
0237b2dc | 1273 | error ("parse error while reading %s", classfile_name); |
6b9b5de2 TT |
1274 | return 0; |
1275 | } | |
1276 | jcf_parse_class (&jcf); | |
1277 | ||
64e68197 | 1278 | tmp = (unsigned char *) super_class_name (&jcf, &super_length); |
5ed6ace5 | 1279 | super = XNEWVEC (unsigned char, super_length + 1); |
64e68197 BM |
1280 | memcpy (super, tmp, super_length); |
1281 | super[super_length] = '\0'; | |
1282 | ||
1283 | result = throwable_p (super); | |
6b9b5de2 TT |
1284 | slot = htab_find_slot (result ? throw_hash : non_throw_hash, |
1285 | current, INSERT); | |
1286 | *slot = current; | |
1287 | current = NULL; | |
1288 | ||
1289 | JCF_FINISH (&jcf); | |
1290 | } | |
1291 | ||
6b9b5de2 TT |
1292 | return result; |
1293 | } | |
1294 | ||
e4de5a10 PB |
1295 | /* Print one piece of a signature. Returns pointer to next parseable |
1296 | character on success, NULL on error. */ | |
be245ac0 | 1297 | static const unsigned char * |
0a2f0c54 KG |
1298 | decode_signature_piece (FILE *stream, const unsigned char *signature, |
1299 | const unsigned char *limit, int *need_space) | |
e4de5a10 | 1300 | { |
d4476be2 | 1301 | const char *ctype; |
3a5395a3 | 1302 | int array_depth = 0; |
e4de5a10 PB |
1303 | |
1304 | switch (signature[0]) | |
1305 | { | |
1306 | case '[': | |
3a5395a3 | 1307 | /* More spaghetti. */ |
de380723 | 1308 | |
3a5395a3 | 1309 | array_loop: |
e4de5a10 | 1310 | for (signature++; (signature < limit |
0df6c2c7 | 1311 | && ISDIGIT (*signature)); signature++) |
e4de5a10 PB |
1312 | ; |
1313 | switch (*signature) | |
1314 | { | |
de380723 TT |
1315 | case 'B': |
1316 | ctype = "jbyteArray"; | |
1317 | break; | |
1318 | case 'C': | |
1319 | ctype = "jcharArray"; | |
1320 | break; | |
1321 | case 'D': | |
1322 | ctype = "jdoubleArray"; | |
1323 | break; | |
1324 | case 'F': | |
1325 | ctype = "jfloatArray"; | |
1326 | break; | |
1327 | case 'I': | |
1328 | ctype = "jintArray"; | |
1329 | break; | |
1330 | case 'S': | |
1331 | ctype = "jshortArray"; | |
1332 | break; | |
1333 | case 'J': | |
1334 | ctype = "jlongArray"; | |
1335 | break; | |
1336 | case 'Z': | |
1337 | ctype = "jbooleanArray"; | |
1338 | break; | |
3a5395a3 TT |
1339 | case '[': |
1340 | /* We have a nested array. */ | |
1341 | ++array_depth; | |
de380723 TT |
1342 | if (! flag_jni) |
1343 | fputs ("JArray<", stream); | |
3a5395a3 TT |
1344 | goto array_loop; |
1345 | ||
e4de5a10 | 1346 | case 'L': |
3a5395a3 TT |
1347 | /* We have to generate a reference to JArray here, so that |
1348 | our output matches what the compiler does. */ | |
e4de5a10 | 1349 | ++signature; |
6f19291a | 1350 | /* Space between `<' and `:' to avoid C++ digraphs. */ |
de380723 TT |
1351 | if (! flag_jni) |
1352 | fputs ("JArray< ::", stream); | |
e4de5a10 PB |
1353 | while (signature < limit && *signature != ';') |
1354 | { | |
1355 | int ch = UTF8_GET (signature, limit); | |
de380723 TT |
1356 | if (! flag_jni) |
1357 | { | |
1358 | if (ch == '/') | |
1359 | fputs ("::", stream); | |
1360 | else | |
1361 | jcf_print_char (stream, ch); | |
1362 | } | |
e4de5a10 | 1363 | } |
de380723 TT |
1364 | if (! flag_jni) |
1365 | fputs (" *> *", stream); | |
e4de5a10 | 1366 | *need_space = 0; |
de380723 | 1367 | ctype = NULL; |
e4de5a10 PB |
1368 | break; |
1369 | default: | |
1370 | /* Unparseable signature. */ | |
1371 | return NULL; | |
1372 | } | |
de380723 TT |
1373 | |
1374 | /* If the previous iterations left us with something to print, | |
1375 | print it. For JNI, we always print `jobjectArray' in the | |
1376 | nested cases. */ | |
0d3911c9 | 1377 | if (flag_jni && (ctype == NULL || array_depth > 0)) |
de380723 TT |
1378 | { |
1379 | ctype = "jobjectArray"; | |
1380 | *need_space = 1; | |
1381 | } | |
1382 | /* The `printit' case will advance SIGNATURE for us. If we | |
1383 | don't go there, we must advance past the `;' ourselves. */ | |
1384 | if (ctype != NULL) | |
1385 | goto printit; | |
1386 | ++signature; | |
e4de5a10 PB |
1387 | break; |
1388 | ||
1389 | case '(': | |
1390 | case ')': | |
1391 | /* This shouldn't happen. */ | |
1392 | return NULL; | |
1393 | ||
1394 | case 'B': ctype = "jbyte"; goto printit; | |
1395 | case 'C': ctype = "jchar"; goto printit; | |
1396 | case 'D': ctype = "jdouble"; goto printit; | |
1397 | case 'F': ctype = "jfloat"; goto printit; | |
1398 | case 'I': ctype = "jint"; goto printit; | |
1399 | case 'J': ctype = "jlong"; goto printit; | |
1400 | case 'S': ctype = "jshort"; goto printit; | |
1401 | case 'Z': ctype = "jboolean"; goto printit; | |
1402 | case 'V': ctype = "void"; goto printit; | |
1403 | case 'L': | |
de380723 TT |
1404 | if (flag_jni) |
1405 | { | |
6b9b5de2 | 1406 | /* We know about certain types and special-case their names. */ |
0e7d217a | 1407 | if (! strncmp ((const char *) signature, "Ljava/lang/String;", |
de380723 TT |
1408 | sizeof ("Ljava/lang/String;") -1)) |
1409 | ctype = "jstring"; | |
0e7d217a | 1410 | else if (! strncmp ((const char *) signature, "Ljava/lang/Class;", |
de380723 TT |
1411 | sizeof ("Ljava/lang/Class;") - 1)) |
1412 | ctype = "jclass"; | |
64e68197 BM |
1413 | /* Skip leading 'L' for throwable_p call. */ |
1414 | else if (throwable_p (signature + 1)) | |
de380723 | 1415 | ctype = "jthrowable"; |
de380723 TT |
1416 | else |
1417 | ctype = "jobject"; | |
1418 | ||
1419 | while (*signature && *signature != ';') | |
1420 | ++signature; | |
1421 | ||
1422 | goto printit; | |
1423 | } | |
12207a87 TT |
1424 | /* Print a leading "::" so we look in the right namespace. */ |
1425 | fputs ("::", stream); | |
e4de5a10 PB |
1426 | ++signature; |
1427 | while (*signature && *signature != ';') | |
1428 | { | |
1429 | int ch = UTF8_GET (signature, limit); | |
dd5d6281 | 1430 | if (ch == '/') |
e4de5a10 PB |
1431 | fputs ("::", stream); |
1432 | else | |
1433 | jcf_print_char (stream, ch); | |
1434 | } | |
1435 | fputs (" *", stream); | |
1436 | if (*signature == ';') | |
1437 | signature++; | |
1438 | *need_space = 0; | |
1439 | break; | |
1440 | default: | |
1441 | *need_space = 1; | |
78de74be | 1442 | jni_print_char (stream, *signature++); |
e4de5a10 PB |
1443 | break; |
1444 | printit: | |
1445 | signature++; | |
1446 | *need_space = 1; | |
1447 | fputs (ctype, stream); | |
1448 | break; | |
1449 | } | |
1450 | ||
de380723 TT |
1451 | if (! flag_jni) |
1452 | { | |
1453 | while (array_depth-- > 0) | |
1454 | fputs ("> *", stream); | |
1455 | } | |
3a5395a3 | 1456 | |
e4de5a10 PB |
1457 | return signature; |
1458 | } | |
1459 | ||
e04a16fb | 1460 | static void |
6ff2fe39 KG |
1461 | print_c_decl (FILE* stream, JCF* jcf, int name_index, int signature_index, |
1462 | int is_init, const char *name_override, int flags) | |
e04a16fb AG |
1463 | { |
1464 | if (JPOOL_TAG (jcf, signature_index) != CONSTANT_Utf8) | |
e4de5a10 PB |
1465 | { |
1466 | fprintf (stream, "<not a UTF8 constant>"); | |
1467 | found_error = 1; | |
1468 | } | |
e04a16fb AG |
1469 | else |
1470 | { | |
1471 | int length = JPOOL_UTF_LENGTH (jcf, signature_index); | |
be245ac0 | 1472 | const unsigned char *str0 = JPOOL_UTF_DATA (jcf, signature_index); |
3a976c72 | 1473 | const unsigned char *str = str0; |
be245ac0 | 1474 | const unsigned char *limit = str + length; |
e04a16fb AG |
1475 | int need_space = 0; |
1476 | int is_method = str[0] == '('; | |
be245ac0 | 1477 | const unsigned char *next; |
e04a16fb | 1478 | |
e4de5a10 PB |
1479 | /* If printing a method, skip to the return signature and print |
1480 | that first. However, there is no return value if this is a | |
1481 | constructor. */ | |
1482 | if (is_method && ! is_init) | |
e04a16fb | 1483 | { |
e4de5a10 | 1484 | while (str < limit) |
e04a16fb | 1485 | { |
e4de5a10 PB |
1486 | int ch = *str++; |
1487 | if (ch == ')') | |
1488 | break; | |
e04a16fb AG |
1489 | } |
1490 | } | |
1491 | ||
e4de5a10 PB |
1492 | /* If printing a field or an ordinary method, then print the |
1493 | "return value" now. */ | |
1494 | if (! is_method || ! is_init) | |
e04a16fb | 1495 | { |
e4de5a10 PB |
1496 | next = decode_signature_piece (stream, str, limit, &need_space); |
1497 | if (! next) | |
e04a16fb | 1498 | { |
597cdf4f | 1499 | error ("unparseable signature: '%s'", str0); |
e04a16fb | 1500 | return; |
e04a16fb | 1501 | } |
e04a16fb | 1502 | } |
e4de5a10 | 1503 | |
68566610 | 1504 | /* Force the alignment of the first data member. This is |
c048d56d | 1505 | because the "new" C++ ABI changed the alignment of non-POD |
68566610 AH |
1506 | classes. gcj, however, still uses the "old" alignment. */ |
1507 | if (is_first_data_member && ! (flags & ACC_STATIC) && ! is_method) | |
e6311b9e TT |
1508 | { |
1509 | is_first_data_member = 0; | |
1510 | print_cxx_classname (out, " __attribute__((aligned(__alignof__( ", | |
1511 | jcf, jcf->super_class, 1); | |
1512 | fputs (" )))) ", stream); | |
1513 | } | |
68566610 | 1514 | |
e4de5a10 PB |
1515 | /* Now print the name of the thing. */ |
1516 | if (need_space) | |
1517 | fputs (" ", stream); | |
6c9c5c1f | 1518 | print_full_cxx_name (stream, jcf, name_index, |
de380723 TT |
1519 | signature_index, is_init, name_override, |
1520 | flags); | |
6c9c5c1f AG |
1521 | } |
1522 | } | |
1523 | ||
5f9ee695 | 1524 | /* Print the unqualified method name followed by the signature. */ |
6c9c5c1f | 1525 | static void |
6ff2fe39 KG |
1526 | print_full_cxx_name (FILE* stream, JCF* jcf, int name_index, |
1527 | int signature_index, int is_init, | |
1528 | const char *name_override, int flags) | |
6c9c5c1f AG |
1529 | { |
1530 | int length = JPOOL_UTF_LENGTH (jcf, signature_index); | |
be245ac0 | 1531 | const unsigned char *str0 = JPOOL_UTF_DATA (jcf, signature_index); |
3a976c72 | 1532 | const unsigned char *str = str0; |
be245ac0 | 1533 | const unsigned char *limit = str + length; |
6c9c5c1f AG |
1534 | int need_space = 0; |
1535 | int is_method = str[0] == '('; | |
be245ac0 | 1536 | const unsigned char *next; |
6c9c5c1f AG |
1537 | |
1538 | if (name_override) | |
1539 | fputs (name_override, stream); | |
1540 | else if (name_index) | |
1541 | { | |
1542 | /* Declare constructors specially. */ | |
1543 | if (is_init) | |
1544 | print_base_classname (stream, jcf, jcf->this_class); | |
1545 | else | |
1546 | print_name (stream, jcf, name_index); | |
1547 | } | |
de380723 TT |
1548 | |
1549 | if (flag_jni) | |
1550 | { | |
1551 | unsigned char *signature = JPOOL_UTF_DATA (jcf, signature_index); | |
1552 | int sig_len = JPOOL_UTF_LENGTH (jcf, signature_index); | |
1553 | if (overloaded_jni_method_exists_p (JPOOL_UTF_DATA (jcf, name_index), | |
1554 | JPOOL_UTF_LENGTH (jcf, name_index), | |
0e7d217a | 1555 | (const char *) signature, sig_len)) |
de380723 TT |
1556 | { |
1557 | /* If this method is overloaded by another native method, | |
1558 | then include the argument information in the mangled | |
1559 | name. */ | |
1560 | unsigned char *limit = signature + sig_len; | |
1561 | fputs ("__", stream); | |
1562 | while (signature < limit) | |
1563 | { | |
1564 | int ch = UTF8_GET (signature, limit); | |
78de74be TT |
1565 | jni_print_char (stream, ch); |
1566 | if (ch == ')') | |
de380723 TT |
1567 | { |
1568 | /* Done. */ | |
1569 | break; | |
1570 | } | |
de380723 TT |
1571 | } |
1572 | } | |
1573 | } | |
1574 | ||
6c9c5c1f AG |
1575 | if (is_method) |
1576 | { | |
1577 | /* Have a method or a constructor. Print signature pieces | |
1578 | until done. */ | |
1579 | fputs (" (", stream); | |
de380723 | 1580 | |
6c9c5c1f | 1581 | str = str0 + 1; |
de380723 TT |
1582 | |
1583 | /* In JNI mode, add extra arguments. */ | |
1584 | if (flag_jni) | |
1585 | { | |
1586 | /* FIXME: it would be nice to know if we are printing a decl | |
1587 | or a definition, and only print `env' for the latter. */ | |
1588 | fputs ("JNIEnv *env", stream); | |
1589 | ||
1590 | fputs ((flags & ACC_STATIC) ? ", jclass" : ", jobject", stream); | |
1591 | ||
1592 | if (*str != ')') | |
1593 | fputs (", ", stream); | |
1594 | } | |
1595 | ||
6c9c5c1f | 1596 | while (str < limit && *str != ')') |
e04a16fb | 1597 | { |
6c9c5c1f AG |
1598 | next = decode_signature_piece (stream, str, limit, &need_space); |
1599 | if (! next) | |
1600 | { | |
597cdf4f | 1601 | error ("unparseable signature: '%s'", str0); |
6c9c5c1f AG |
1602 | return; |
1603 | } | |
1604 | ||
1605 | if (next < limit && *next != ')') | |
1606 | fputs (", ", stream); | |
1607 | str = next; | |
e04a16fb | 1608 | } |
6c9c5c1f AG |
1609 | |
1610 | fputs (")", stream); | |
1611 | } | |
1612 | } | |
c89c53eb | 1613 | |
de380723 | 1614 | /* This is a helper for print_stub_or_jni. */ |
6c9c5c1f | 1615 | static void |
6ff2fe39 KG |
1616 | print_name_for_stub_or_jni (FILE *stream, JCF *jcf, int name_index, |
1617 | int signature_index, int is_init, | |
1618 | const char *name_override, int flags) | |
de380723 | 1619 | { |
a7384840 | 1620 | const char *const prefix = flag_jni ? "Java_" : ""; |
239b7dea | 1621 | print_cxx_classname (stream, prefix, jcf, jcf->this_class, 1); |
de380723 TT |
1622 | fputs (flag_jni ? "_" : "::", stream); |
1623 | print_full_cxx_name (stream, jcf, name_index, | |
1624 | signature_index, is_init, name_override, | |
1625 | flags); | |
1626 | } | |
1627 | ||
1628 | static void | |
6ff2fe39 KG |
1629 | print_stub_or_jni (FILE* stream, JCF* jcf, int name_index, |
1630 | int signature_index, int is_init, | |
1631 | const char *name_override, int flags) | |
6c9c5c1f AG |
1632 | { |
1633 | if (JPOOL_TAG (jcf, signature_index) != CONSTANT_Utf8) | |
1634 | { | |
1635 | fprintf (stream, "<not a UTF8 constant>"); | |
1636 | found_error = 1; | |
1637 | } | |
1638 | else | |
1639 | { | |
1640 | int length = JPOOL_UTF_LENGTH (jcf, signature_index); | |
be245ac0 | 1641 | const unsigned char *str0 = JPOOL_UTF_DATA (jcf, signature_index); |
3a976c72 | 1642 | const unsigned char *str = str0; |
be245ac0 | 1643 | const unsigned char *limit = str + length; |
6c9c5c1f AG |
1644 | int need_space = 0; |
1645 | int is_method = str[0] == '('; | |
be245ac0 | 1646 | const unsigned char *next; |
e4de5a10 | 1647 | |
de380723 TT |
1648 | /* Don't print fields in the JNI case. */ |
1649 | if (! is_method && flag_jni) | |
1650 | return; | |
1651 | ||
1652 | if (flag_jni && ! stubs) | |
f1865cde | 1653 | fputs ("JNIEXPORT ", stream); |
de380723 | 1654 | |
6c9c5c1f AG |
1655 | /* If printing a method, skip to the return signature and print |
1656 | that first. However, there is no return value if this is a | |
1657 | constructor. */ | |
1658 | if (is_method && ! is_init) | |
e04a16fb | 1659 | { |
6c9c5c1f | 1660 | while (str < limit) |
e4de5a10 | 1661 | { |
6c9c5c1f AG |
1662 | int ch = *str++; |
1663 | if (ch == ')') | |
1664 | break; | |
e4de5a10 | 1665 | } |
6c9c5c1f | 1666 | } |
e4de5a10 | 1667 | |
6c9c5c1f | 1668 | /* If printing a field or an ordinary method, then print the |
de380723 TT |
1669 | "return value" now. Note that a constructor can't be native, |
1670 | so we don't bother checking this in the JNI case. */ | |
6c9c5c1f AG |
1671 | if (! is_method || ! is_init) |
1672 | { | |
1673 | next = decode_signature_piece (stream, str, limit, &need_space); | |
1674 | if (! next) | |
1675 | { | |
597cdf4f | 1676 | error ("unparseable signature: '%s'", str0); |
6c9c5c1f AG |
1677 | return; |
1678 | } | |
e04a16fb | 1679 | } |
6c9c5c1f | 1680 | |
de380723 TT |
1681 | /* When printing a JNI header we need to respect the space. In |
1682 | other cases we're just going to insert a newline anyway. */ | |
a7384840 | 1683 | fputs (need_space && ! stubs ? " " : "\n", stream); |
5bd717f1 TT |
1684 | |
1685 | if (flag_jni && ! stubs) | |
1686 | fputs ("JNICALL ", stream); | |
a7384840 | 1687 | |
6c9c5c1f | 1688 | /* Now print the name of the thing. */ |
de380723 TT |
1689 | print_name_for_stub_or_jni (stream, jcf, name_index, |
1690 | signature_index, is_init, name_override, | |
1691 | flags); | |
1692 | ||
1693 | /* Print the body. */ | |
1694 | if (stubs) | |
1695 | { | |
1696 | if (flag_jni) | |
45c00e22 | 1697 | fputs ("\n{\n (*env)->FatalError (env, \"", stream); |
de380723 | 1698 | else |
0df29596 | 1699 | fputs ("\n{\n throw new ::java::lang::UnsupportedOperationException (JvNewStringLatin1 (\"", stream); |
de380723 TT |
1700 | print_name_for_stub_or_jni (stream, jcf, name_index, |
1701 | signature_index, is_init, | |
1702 | name_override, | |
1703 | flags); | |
0df29596 TT |
1704 | fprintf (stream, " not implemented\")%s;\n}\n\n", |
1705 | flag_jni ? "" : ")"); | |
de380723 | 1706 | } |
e04a16fb AG |
1707 | } |
1708 | } | |
1709 | ||
be245ac0 | 1710 | static void |
6ff2fe39 | 1711 | print_mangled_classname (FILE *stream, JCF *jcf, const char *prefix, int index) |
e04a16fb AG |
1712 | { |
1713 | int name_index = JPOOL_USHORT1 (jcf, index); | |
1714 | fputs (prefix, stream); | |
1715 | jcf_print_utf8_replace (out, | |
1716 | JPOOL_UTF_DATA (jcf, name_index), | |
1717 | JPOOL_UTF_LENGTH (jcf, name_index), | |
1718 | '/', '_'); | |
1719 | } | |
1720 | ||
1721 | /* Print PREFIX, then a class name in C++ format. If the name refers | |
1722 | to an array, ignore it and don't print PREFIX. Returns 1 if | |
1723 | something was printed, 0 otherwise. */ | |
1724 | static int | |
0a2f0c54 KG |
1725 | print_cxx_classname (FILE *stream, const char *prefix, |
1726 | JCF *jcf, int index, int add_scope) | |
e04a16fb AG |
1727 | { |
1728 | int name_index = JPOOL_USHORT1 (jcf, index); | |
fc45c7ef | 1729 | int len, c; |
be245ac0 | 1730 | const unsigned char *s, *p, *limit; |
e04a16fb AG |
1731 | |
1732 | s = JPOOL_UTF_DATA (jcf, name_index); | |
1733 | len = JPOOL_UTF_LENGTH (jcf, name_index); | |
1734 | limit = s + len; | |
1735 | ||
1736 | /* Explicitly omit arrays here. */ | |
1737 | p = s; | |
1738 | c = UTF8_GET (p, limit); | |
1739 | if (c == '[') | |
1740 | return 0; | |
1741 | ||
1742 | fputs (prefix, stream); | |
12207a87 TT |
1743 | |
1744 | /* Print a leading "::" so we look in the right namespace. */ | |
239b7dea | 1745 | if (! flag_jni && ! stubs && add_scope) |
de380723 | 1746 | fputs ("::", stream); |
12207a87 | 1747 | |
e04a16fb AG |
1748 | while (s < limit) |
1749 | { | |
1750 | c = UTF8_GET (s, limit); | |
1751 | if (c == '/') | |
de380723 | 1752 | fputs (flag_jni ? "_" : "::", stream); |
e04a16fb | 1753 | else |
78de74be | 1754 | jni_print_char (stream, c); |
e04a16fb AG |
1755 | } |
1756 | ||
1757 | return 1; | |
1758 | } | |
1759 | ||
1760 | int written_class_count = 0; | |
1761 | ||
1762 | /* Return name of superclass. If LEN is not NULL, fill it with length | |
1763 | of name. */ | |
be245ac0 | 1764 | static const unsigned char * |
0a2f0c54 | 1765 | super_class_name (JCF *derived_jcf, int *len) |
e04a16fb AG |
1766 | { |
1767 | int supername_index = JPOOL_USHORT1 (derived_jcf, derived_jcf->super_class); | |
1768 | int supername_length = JPOOL_UTF_LENGTH (derived_jcf, supername_index); | |
be245ac0 KG |
1769 | const unsigned char *supername = |
1770 | JPOOL_UTF_DATA (derived_jcf, supername_index); | |
e04a16fb AG |
1771 | |
1772 | if (len) | |
1773 | *len = supername_length; | |
1774 | ||
1775 | return supername; | |
1776 | } | |
1777 | ||
73cb3b5a TT |
1778 | static void |
1779 | handle_inner_classes (int count) | |
1780 | { | |
1781 | int i; | |
1782 | ||
1783 | if (out && ! flag_jni && ! stubs && count > 0) | |
1784 | fprintf (out, "\n"); | |
1785 | ||
1786 | for (i = 0; i < count; ++i) | |
1787 | { | |
1788 | JCF_u2 inner_info_index = JCF_readu2 (current_jcf); | |
1789 | ||
1790 | /* There are a few more values here, but we don't care about | |
1791 | them. The (void) cast is apparently the only way to avoid a | |
1792 | warning here. */ | |
1793 | (void) JCF_readu2 (current_jcf); | |
1794 | (void) JCF_readu2 (current_jcf); | |
1795 | (void) JCF_readu2 (current_jcf); | |
1796 | ||
1797 | if (out && ! flag_jni && ! stubs) | |
1798 | { | |
1799 | print_mangled_classname (out, current_jcf, " friend class ", | |
1800 | inner_info_index); | |
1801 | fprintf (out, ";\n"); | |
1802 | } | |
1803 | } | |
1804 | } | |
1805 | ||
bdb59aec TT |
1806 | \f |
1807 | ||
686fb236 TT |
1808 | /* We keep track of all the `#include's we generate, so we can avoid |
1809 | duplicates. */ | |
1810 | struct include | |
1811 | { | |
1812 | char *name; | |
1813 | struct include *next; | |
1814 | }; | |
1815 | ||
1816 | /* List of all includes. */ | |
1817 | static struct include *all_includes = NULL; | |
1818 | ||
1819 | /* Generate a #include. */ | |
1820 | static void | |
0a2f0c54 | 1821 | print_include (FILE *out, const unsigned char *utf8, int len) |
686fb236 TT |
1822 | { |
1823 | struct include *incl; | |
1824 | ||
1825 | if (! out) | |
1826 | return; | |
1827 | ||
1828 | if (len == -1) | |
0e7d217a | 1829 | len = strlen ((const char *) utf8); |
686fb236 TT |
1830 | |
1831 | for (incl = all_includes; incl; incl = incl->next) | |
1832 | { | |
5877286e | 1833 | /* We check the length because we might have a proper prefix. */ |
1b01250d | 1834 | if (len == (int) strlen (incl->name) |
0e7d217a | 1835 | && ! strncmp (incl->name, (const char *) utf8, len)) |
686fb236 TT |
1836 | return; |
1837 | } | |
1838 | ||
5ed6ace5 MD |
1839 | incl = XNEW (struct include); |
1840 | incl->name = XNEWVEC (char, len + 1); | |
0e7d217a | 1841 | strncpy (incl->name, (const char *) utf8, len); |
686fb236 TT |
1842 | incl->name[len] = '\0'; |
1843 | incl->next = all_includes; | |
1844 | all_includes = incl; | |
1845 | ||
1846 | fputs ("#include <", out); | |
de380723 TT |
1847 | jcf_print_utf8_replace (out, utf8, len, |
1848 | '/', | |
1849 | flag_jni ? '_' : '/'); | |
686fb236 TT |
1850 | fputs (".h>\n", out); |
1851 | } | |
1852 | ||
1853 | \f | |
1854 | ||
bdb59aec TT |
1855 | /* This is used to represent part of a package or class name. */ |
1856 | struct namelet | |
1857 | { | |
1858 | /* The text of this part of the name. */ | |
1859 | char *name; | |
1860 | /* True if this represents a class. */ | |
1861 | int is_class; | |
1862 | /* Linked list of all classes and packages inside this one. */ | |
1863 | struct namelet *subnamelets; | |
1864 | /* Pointer to next sibling. */ | |
1865 | struct namelet *next; | |
1866 | }; | |
1867 | ||
d2097937 KG |
1868 | static void add_namelet (const unsigned char *, const unsigned char *, |
1869 | struct namelet *); | |
1870 | static void print_namelet (FILE *, struct namelet *, int); | |
be245ac0 | 1871 | |
bdb59aec TT |
1872 | /* The special root namelet. */ |
1873 | static struct namelet root = | |
1874 | { | |
1875 | NULL, | |
1876 | 0, | |
1877 | NULL, | |
1878 | NULL | |
1879 | }; | |
1880 | ||
1881 | /* This extracts the next name segment from the full UTF-8 encoded | |
1882 | package or class name and links it into the tree. It does this | |
1883 | recursively. */ | |
e04a16fb | 1884 | static void |
0a2f0c54 KG |
1885 | add_namelet (const unsigned char *name, const unsigned char *name_limit, |
1886 | struct namelet *parent) | |
bdb59aec | 1887 | { |
be245ac0 | 1888 | const unsigned char *p; |
bdb59aec TT |
1889 | struct namelet *n = NULL, *np; |
1890 | ||
f3d50f42 TT |
1891 | /* We want to skip the standard namespaces that we assume the |
1892 | runtime already knows about. We only do this at the top level, | |
1893 | though, hence the check for `root'. */ | |
1894 | if (parent == &root) | |
1895 | { | |
1896 | #define JAVALANG "java/lang/" | |
1897 | #define JAVAIO "java/io/" | |
1898 | #define JAVAUTIL "java/util/" | |
7e21fe59 | 1899 | if ((name_limit - name >= (int) sizeof (JAVALANG) - 1 |
0e7d217a | 1900 | && ! strncmp ((const char *) name, JAVALANG, sizeof (JAVALANG) - 1)) |
7e21fe59 | 1901 | || (name_limit - name >= (int) sizeof (JAVAUTIL) - 1 |
0e7d217a | 1902 | && ! strncmp ((const char *) name, JAVAUTIL, sizeof (JAVAUTIL) - 1)) |
7e21fe59 | 1903 | || (name_limit - name >= (int) sizeof (JAVAIO) - 1 |
0e7d217a | 1904 | && ! strncmp ((const char *) name, JAVAIO, sizeof (JAVAIO) - 1))) |
f3d50f42 TT |
1905 | return; |
1906 | } | |
1907 | ||
3c1d1e7b | 1908 | for (p = name; p < name_limit && *p != '/'; ++p) |
bdb59aec TT |
1909 | ; |
1910 | ||
1911 | /* Search for this name beneath the PARENT node. */ | |
1912 | for (np = parent->subnamelets; np != NULL; np = np->next) | |
1913 | { | |
5877286e | 1914 | /* We check the length because we might have a proper prefix. */ |
1b01250d | 1915 | if ((int) strlen (np->name) == p - name && |
0e7d217a | 1916 | ! strncmp ((const char *) name, np->name, p - name)) |
bdb59aec TT |
1917 | { |
1918 | n = np; | |
1919 | break; | |
1920 | } | |
1921 | } | |
1922 | ||
1923 | if (n == NULL) | |
1924 | { | |
5ed6ace5 MD |
1925 | n = XNEW (struct namelet); |
1926 | n->name = XNEWVEC (char, p - name + 1); | |
0e7d217a | 1927 | strncpy (n->name, (const char *) name, p - name); |
bdb59aec | 1928 | n->name[p - name] = '\0'; |
3c1d1e7b | 1929 | n->is_class = (p == name_limit); |
bdb59aec TT |
1930 | n->subnamelets = NULL; |
1931 | n->next = parent->subnamelets; | |
1932 | parent->subnamelets = n; | |
1933 | } | |
1934 | ||
1935 | /* We recurse if there is more text, and if the trailing piece does | |
1936 | not represent an inner class. */ | |
3c1d1e7b | 1937 | if (p < name_limit) |
bdb59aec TT |
1938 | add_namelet (p + 1, name_limit, n); |
1939 | } | |
1940 | ||
1941 | /* Print a single namelet. Destroys namelets while printing. */ | |
1942 | static void | |
0a2f0c54 | 1943 | print_namelet (FILE *out, struct namelet *name, int depth) |
bdb59aec TT |
1944 | { |
1945 | int i, term = 0; | |
1946 | struct namelet *c; | |
1947 | ||
1948 | if (name->name) | |
1949 | { | |
1950 | for (i = 0; i < depth; ++i) | |
1951 | fputc (' ', out); | |
1952 | fprintf (out, "%s %s", name->is_class ? "class" : "namespace", | |
1953 | name->name); | |
1954 | if (name->is_class && name->subnamelets == NULL) | |
1955 | fputs (";\n", out); | |
1956 | else | |
1957 | { | |
1958 | term = 1; | |
1959 | fputs ("\n", out); | |
1960 | for (i = 0; i < depth; ++i) | |
1961 | fputc (' ', out); | |
1962 | fputs ("{\n", out); | |
1963 | } | |
1964 | } | |
1965 | ||
1966 | c = name->subnamelets; | |
1967 | while (c != NULL) | |
1968 | { | |
1969 | struct namelet *next = c->next; | |
1970 | print_namelet (out, c, depth + 2); | |
1971 | c = next; | |
1972 | } | |
4440ffc4 | 1973 | name->subnamelets = NULL; |
bdb59aec TT |
1974 | |
1975 | if (name->name) | |
1976 | { | |
1977 | if (term) | |
1978 | { | |
1979 | for (i = 0; i < depth; ++i) | |
1980 | fputc (' ', out); | |
512d594b TT |
1981 | fputs ("}\n", out); |
1982 | /* Only print a `;' when printing a class. C++ is evil. */ | |
1983 | if (name->is_class) | |
1984 | fputs (";", out); | |
bdb59aec TT |
1985 | } |
1986 | ||
1987 | free (name->name); | |
1988 | free (name); | |
1989 | } | |
1990 | } | |
1991 | ||
1992 | /* This is called to add some classes to the list of classes for which | |
1993 | we need decls. The signature argument can be a function | |
1994 | signature. */ | |
1995 | static void | |
0a2f0c54 | 1996 | add_class_decl (FILE *out, JCF *jcf, JCF_u2 signature) |
e04a16fb | 1997 | { |
be245ac0 | 1998 | const unsigned char *s = JPOOL_UTF_DATA (jcf, signature); |
bdb59aec TT |
1999 | int len = JPOOL_UTF_LENGTH (jcf, signature); |
2000 | int i; | |
e04a16fb | 2001 | |
bdb59aec | 2002 | for (i = 0; i < len; ++i) |
e04a16fb | 2003 | { |
3c1d1e7b | 2004 | int start; |
686fb236 TT |
2005 | |
2006 | /* If we see an array, then we include the array header. */ | |
2007 | if (s[i] == '[') | |
2008 | { | |
0e7d217a | 2009 | print_include (out, (const unsigned char *) "gcj/array", -1); |
686fb236 TT |
2010 | continue; |
2011 | } | |
2012 | ||
bdb59aec TT |
2013 | /* We're looking for `L<stuff>;' -- everything else is |
2014 | ignorable. */ | |
2015 | if (s[i] != 'L') | |
2016 | continue; | |
686fb236 | 2017 | |
bdb59aec | 2018 | for (start = ++i; i < len && s[i] != ';'; ++i) |
3c1d1e7b | 2019 | ; |
bdb59aec | 2020 | |
3c1d1e7b | 2021 | add_namelet (&s[start], &s[i], &root); |
e04a16fb | 2022 | } |
bdb59aec TT |
2023 | } |
2024 | ||
2025 | /* Print declarations for all classes required by this class. Any | |
2026 | class or package in the `java' package is assumed to be handled | |
2027 | statically in libjava; we don't generate declarations for these. | |
2028 | This makes the generated headers a bit easier to read. */ | |
2029 | static void | |
0a2f0c54 | 2030 | print_class_decls (FILE *out, JCF *jcf, int self) |
bdb59aec TT |
2031 | { |
2032 | /* Make sure to always add the current class to the list of things | |
2033 | that should be declared. */ | |
2034 | int name_index = JPOOL_USHORT1 (jcf, self); | |
2035 | int len; | |
be245ac0 | 2036 | const unsigned char *s; |
bdb59aec TT |
2037 | |
2038 | s = JPOOL_UTF_DATA (jcf, name_index); | |
2039 | len = JPOOL_UTF_LENGTH (jcf, name_index); | |
2040 | add_namelet (s, s + len, &root); | |
e04a16fb | 2041 | |
bdb59aec TT |
2042 | if (root.subnamelets) |
2043 | { | |
2044 | fputs ("extern \"Java\"\n{\n", out); | |
2045 | /* We use an initial offset of 0 because the root namelet | |
2046 | doesn't cause anything to print. */ | |
2047 | print_namelet (out, &root, 0); | |
239b7dea | 2048 | fputs ("}\n\n", out); |
bdb59aec | 2049 | } |
e04a16fb AG |
2050 | } |
2051 | ||
bdb59aec TT |
2052 | \f |
2053 | ||
e04a16fb | 2054 | static void |
6ff2fe39 | 2055 | process_file (JCF *jcf, FILE *out) |
e04a16fb AG |
2056 | { |
2057 | int code, i; | |
bdb59aec | 2058 | uint32 field_start, method_end, method_start; |
e04a16fb | 2059 | |
4f65832d | 2060 | current_jcf = jcf; |
e04a16fb | 2061 | |
3217b0f3 | 2062 | last_access = -1; |
e04a16fb AG |
2063 | |
2064 | if (jcf_parse_preamble (jcf) != 0) | |
2065 | { | |
0237b2dc | 2066 | error ("Not a valid Java .class file."); |
e04a16fb AG |
2067 | return; |
2068 | } | |
2069 | ||
2070 | /* Parse and possibly print constant pool */ | |
2071 | code = jcf_parse_constant_pool (jcf); | |
2072 | if (code != 0) | |
2073 | { | |
0237b2dc | 2074 | error ("error while parsing constant pool"); |
e04a16fb AG |
2075 | return; |
2076 | } | |
2077 | code = verify_constant_pool (jcf); | |
2078 | if (code > 0) | |
2079 | { | |
0237b2dc | 2080 | error ("error in constant pool entry #%d", code); |
e04a16fb AG |
2081 | return; |
2082 | } | |
2083 | ||
2084 | jcf_parse_class (jcf); | |
2085 | ||
fc45c7ef | 2086 | if (written_class_count++ == 0 && out) |
cd531a2e | 2087 | { |
5ca1627b | 2088 | const char *cstart, *cstart2, *mode, *cend, *what, *jflag; |
de380723 TT |
2089 | if (flag_jni) |
2090 | { | |
2091 | cstart = "/*"; | |
2092 | cstart2 = " "; | |
2093 | cend = " */"; | |
2094 | mode = ""; | |
2095 | what = "JNI"; | |
2096 | jflag = " -jni"; | |
2097 | } | |
2098 | else | |
2099 | { | |
2100 | cstart = "//"; | |
2101 | cstart2 = "//"; | |
2102 | cend = ""; | |
2103 | mode = " -*- c++ -*-"; | |
2104 | what = "CNI"; | |
2105 | jflag = ""; | |
2106 | } | |
2107 | ||
cd531a2e | 2108 | if (! stubs) |
de380723 TT |
2109 | fprintf (out, "%s DO NOT EDIT THIS FILE - it is machine generated%s%s\n\n", |
2110 | cstart, mode, cend); | |
cd531a2e KG |
2111 | else |
2112 | { | |
d8be0aab | 2113 | fprintf (out, "%s This file was created by `" TOOLNAME " -stubs%s'.%s\n\ |
de380723 TT |
2114 | %s\n\ |
2115 | %s This file is intended to give you a head start on implementing native\n\ | |
2116 | %s methods using %s.\n\ | |
d8be0aab | 2117 | %s Be aware: running `" TOOLNAME " -stubs %s' once more for this class may\n\ |
de380723 TT |
2118 | %s overwrite any edits you have made to this file.%s\n\n", |
2119 | cstart, jflag, mode, | |
2120 | cstart2, | |
2121 | cstart2, | |
2122 | cstart2, | |
2123 | what, | |
2124 | cstart2, | |
2125 | jflag, | |
2126 | cstart2, | |
2127 | cend); | |
cd531a2e KG |
2128 | } |
2129 | } | |
e04a16fb | 2130 | |
fc45c7ef TT |
2131 | if (out) |
2132 | { | |
6c9c5c1f AG |
2133 | if (! stubs) |
2134 | { | |
2135 | print_mangled_classname (out, jcf, "#ifndef __", jcf->this_class); | |
2136 | fprintf (out, "__\n"); | |
de380723 | 2137 | |
6c9c5c1f AG |
2138 | print_mangled_classname (out, jcf, "#define __", jcf->this_class); |
2139 | fprintf (out, "__\n\n"); | |
de380723 TT |
2140 | |
2141 | if (flag_jni) | |
6c9c5c1f | 2142 | { |
de380723 TT |
2143 | fprintf (out, "#include <jni.h>\n\n"); |
2144 | fprintf (out, "#ifdef __cplusplus\n"); | |
2145 | fprintf (out, "extern \"C\"\n"); | |
2146 | fprintf (out, "{\n"); | |
2147 | fprintf (out, "#endif\n"); | |
2148 | } | |
2149 | else | |
2150 | { | |
2151 | /* We do this to ensure that inline methods won't be | |
2152 | `outlined' by g++. This works as long as method and | |
2153 | fields are not added by the user. */ | |
2154 | fprintf (out, "#pragma interface\n"); | |
2155 | ||
2156 | if (jcf->super_class) | |
2157 | { | |
2158 | int super_length; | |
2159 | const unsigned char *supername = | |
2160 | super_class_name (jcf, &super_length); | |
2161 | ||
2162 | fputs ("\n", out); | |
2163 | print_include (out, supername, super_length); | |
2164 | } | |
6c9c5c1f AG |
2165 | } |
2166 | } | |
2167 | else | |
2168 | { | |
2169 | /* Strip off the ".class" portion of the name when printing | |
2170 | the include file name. */ | |
9c874875 TT |
2171 | char *name; |
2172 | int i, len = strlen (jcf->classname); | |
de380723 TT |
2173 | if (len > 6 && ! strcmp (&jcf->classname[len - 6], ".class")) |
2174 | len -= 6; | |
9c874875 | 2175 | /* Turn the class name into a file name. */ |
5ed6ace5 | 2176 | name = XNEWVEC (char, len + 1); |
9c874875 TT |
2177 | for (i = 0; i < len; ++i) |
2178 | name[i] = jcf->classname[i] == '.' ? '/' : jcf->classname[i]; | |
2179 | name[i] = '\0'; | |
0e7d217a | 2180 | print_include (out, (const unsigned char *) name, len); |
9c874875 TT |
2181 | free (name); |
2182 | ||
136c35df | 2183 | if (! flag_jni) |
0df29596 | 2184 | { |
0e7d217a MS |
2185 | print_include (out, (const unsigned char *) "gcj/cni", -1); |
2186 | print_include (out, (const unsigned char *) "java/lang/UnsupportedOperationException", | |
0df29596 TT |
2187 | -1); |
2188 | } | |
6c9c5c1f | 2189 | } |
e04a16fb AG |
2190 | } |
2191 | ||
bdb59aec TT |
2192 | /* We want to parse the methods first. But we need to find where |
2193 | they start. So first we skip the fields, then parse the methods. | |
2194 | Then we parse the fields and skip the methods. This is ugly, but | |
2195 | not too bad since we need two full passes to get class decl | |
2196 | information anyway. */ | |
2197 | field_pass = 0; | |
2198 | field_start = JCF_TELL (jcf); | |
2199 | jcf_parse_fields (jcf); | |
2200 | ||
2201 | method_start = JCF_TELL (jcf); | |
2202 | method_pass = 0; | |
2203 | jcf_parse_methods (jcf); | |
2204 | ||
fc45c7ef | 2205 | if (out) |
de380723 | 2206 | fputs ("\n", out); |
6c9c5c1f | 2207 | |
de380723 TT |
2208 | if (out && ! flag_jni) |
2209 | { | |
6c9c5c1f AG |
2210 | if (! stubs) |
2211 | print_class_decls (out, jcf, jcf->this_class); | |
e04a16fb | 2212 | |
fc45c7ef TT |
2213 | for (i = 0; i < prepend_count; ++i) |
2214 | fprintf (out, "%s\n", prepend_specs[i]); | |
2215 | if (prepend_count > 0) | |
2216 | fputc ('\n', out); | |
de380723 | 2217 | |
6c9c5c1f | 2218 | if (! stubs) |
e04a16fb | 2219 | { |
239b7dea MM |
2220 | if (! print_cxx_classname (out, "class ", jcf, |
2221 | jcf->this_class, 0)) | |
6c9c5c1f | 2222 | { |
0237b2dc | 2223 | error ("class is of array type\n"); |
6c9c5c1f AG |
2224 | return; |
2225 | } | |
2226 | if (jcf->super_class) | |
2227 | { | |
2228 | if (! print_cxx_classname (out, " : public ", | |
239b7dea | 2229 | jcf, jcf->super_class, 1)) |
6c9c5c1f | 2230 | { |
0237b2dc | 2231 | error ("base class is of array type"); |
6c9c5c1f AG |
2232 | return; |
2233 | } | |
2234 | } | |
2235 | ||
2236 | fputs ("\n{\n", out); | |
e04a16fb AG |
2237 | } |
2238 | } | |
e04a16fb | 2239 | |
bdb59aec | 2240 | /* Now go back for second pass over methods and fields. */ |
68566610 AH |
2241 | is_first_data_member = 1; |
2242 | ||
bdb59aec TT |
2243 | JCF_SEEK (jcf, method_start); |
2244 | method_pass = 1; | |
e04a16fb | 2245 | jcf_parse_methods (jcf); |
e4de5a10 PB |
2246 | method_end = JCF_TELL (jcf); |
2247 | ||
2248 | field_pass = 1; | |
2249 | JCF_SEEK (jcf, field_start); | |
2250 | jcf_parse_fields (jcf); | |
2251 | JCF_SEEK (jcf, method_end); | |
2252 | ||
e04a16fb AG |
2253 | jcf_parse_final_attributes (jcf); |
2254 | ||
de380723 | 2255 | if (out && ! stubs) |
fc45c7ef | 2256 | { |
de380723 | 2257 | if (flag_jni) |
6c9c5c1f | 2258 | { |
e0a0c416 TT |
2259 | fprintf (out, "\n#ifdef __cplusplus\n"); |
2260 | fprintf (out, "}\n"); | |
2261 | fprintf (out, "#endif\n"); | |
6c9c5c1f | 2262 | } |
de380723 TT |
2263 | else |
2264 | { | |
2265 | /* Generate friend decl if we still must. */ | |
2266 | for (i = 0; i < friend_count; ++i) | |
2267 | fprintf (out, " friend %s\n", friend_specs[i]); | |
2268 | ||
2269 | /* Generate extra declarations. */ | |
2270 | if (add_count > 0) | |
2271 | fputc ('\n', out); | |
2272 | for (i = 0; i < add_count; ++i) | |
2273 | fprintf (out, " %s\n", add_specs[i]); | |
2274 | ||
e0a0c416 TT |
2275 | /* Generate an entry for the class object. */ |
2276 | generate_access (out, ACC_PUBLIC); | |
2277 | fprintf (out, "\n static ::java::lang::Class class$;\n"); | |
2278 | ||
60c87482 BM |
2279 | fputs ("}", out); |
2280 | ||
2281 | if (jcf->access_flags & ACC_INTERFACE) | |
2282 | fputs (" __attribute__ ((java_interface))", out); | |
2283 | ||
2284 | fputs (";\n", out); | |
de380723 TT |
2285 | |
2286 | if (append_count > 0) | |
2287 | fputc ('\n', out); | |
2288 | for (i = 0; i < append_count; ++i) | |
2289 | fprintf (out, "%s\n", append_specs[i]); | |
2290 | } | |
2291 | ||
2292 | print_mangled_classname (out, jcf, | |
2293 | "\n#endif /* __", jcf->this_class); | |
2294 | fprintf (out, "__ */\n"); | |
fc45c7ef | 2295 | } |
e04a16fb AG |
2296 | } |
2297 | ||
c89c53eb TT |
2298 | \f |
2299 | ||
2300 | /* This is used to mark options with no short value. */ | |
2301 | #define LONG_OPT(Num) ((Num) + 128) | |
2302 | ||
db444fbe | 2303 | #define OPT_classpath LONG_OPT (0) |
2a85660d PB |
2304 | #define OPT_CLASSPATH OPT_classpath |
2305 | #define OPT_bootclasspath LONG_OPT (1) | |
9fef1fe3 AG |
2306 | #define OPT_extdirs LONG_OPT (2) |
2307 | #define OPT_HELP LONG_OPT (3) | |
2308 | #define OPT_TEMP LONG_OPT (4) | |
2309 | #define OPT_VERSION LONG_OPT (5) | |
2310 | #define OPT_PREPEND LONG_OPT (6) | |
2311 | #define OPT_FRIEND LONG_OPT (7) | |
2312 | #define OPT_ADD LONG_OPT (8) | |
2313 | #define OPT_APPEND LONG_OPT (9) | |
2314 | #define OPT_M LONG_OPT (10) | |
2315 | #define OPT_MM LONG_OPT (11) | |
2316 | #define OPT_MG LONG_OPT (12) | |
2317 | #define OPT_MD LONG_OPT (13) | |
2318 | #define OPT_MMD LONG_OPT (14) | |
d8be0aab TF |
2319 | #define OPT_FORCE LONG_OPT (15) |
2320 | #define OPT_OLD LONG_OPT (16) | |
2321 | #define OPT_TRACE LONG_OPT (17) | |
c89c53eb | 2322 | |
c083a819 | 2323 | static const struct option options[] = |
c89c53eb | 2324 | { |
db444fbe | 2325 | { "classpath", required_argument, NULL, OPT_classpath }, |
2a85660d | 2326 | { "bootclasspath", required_argument, NULL, OPT_bootclasspath }, |
9fef1fe3 | 2327 | { "extdirs", required_argument, NULL, OPT_extdirs }, |
db444fbe NF |
2328 | { "CLASSPATH", required_argument, NULL, OPT_CLASSPATH }, |
2329 | { "help", no_argument, NULL, OPT_HELP }, | |
2330 | { "stubs", no_argument, &stubs, 1 }, | |
2331 | { "td", required_argument, NULL, OPT_TEMP }, | |
2332 | { "verbose", no_argument, NULL, 'v' }, | |
2333 | { "version", no_argument, NULL, OPT_VERSION }, | |
2334 | { "prepend", required_argument, NULL, OPT_PREPEND }, | |
2335 | { "friend", required_argument, NULL, OPT_FRIEND }, | |
2336 | { "add", required_argument, NULL, OPT_ADD }, | |
2337 | { "append", required_argument, NULL, OPT_APPEND }, | |
2338 | { "M", no_argument, NULL, OPT_M }, | |
2339 | { "MM", no_argument, NULL, OPT_MM }, | |
2340 | { "MG", no_argument, NULL, OPT_MG }, | |
2341 | { "MD", no_argument, NULL, OPT_MD }, | |
2342 | { "MMD", no_argument, NULL, OPT_MMD }, | |
2343 | { "jni", no_argument, &flag_jni, 1 }, | |
d8be0aab TF |
2344 | { "force", no_argument, NULL, OPT_FORCE }, |
2345 | /* If the output file should be named "ld" then a space is needed | |
2346 | between -o and its argument, ld. */ | |
2347 | { "old", no_argument, NULL, OPT_OLD }, | |
2348 | { "trace", no_argument, NULL, OPT_TRACE }, | |
2349 | { NULL, required_argument, NULL, 'J' }, | |
db444fbe | 2350 | { NULL, no_argument, NULL, 0 } |
c89c53eb TT |
2351 | }; |
2352 | ||
e04a16fb | 2353 | static void |
0a2f0c54 | 2354 | usage (void) |
e04a16fb | 2355 | { |
d8be0aab | 2356 | fprintf (stderr, _("Try '" TOOLNAME " --help' for more information.\n")); |
e04a16fb AG |
2357 | exit (1); |
2358 | } | |
2359 | ||
2360 | static void | |
0a2f0c54 | 2361 | help (void) |
e04a16fb | 2362 | { |
d8be0aab TF |
2363 | printf (_("Usage: " TOOLNAME " [OPTION]... CLASS...\n\n")); |
2364 | printf (_("Generate C or C++ header files from .class files\n\n")); | |
0237b2dc JM |
2365 | printf (_(" -stubs Generate an implementation stub file\n")); |
2366 | printf (_(" -jni Generate a JNI header or stub\n")); | |
d8be0aab TF |
2367 | printf (_(" -force Always overwrite output files\n")); |
2368 | printf (_(" -old Unused compatibility option\n")); | |
2369 | printf (_(" -trace Unused compatibility option\n")); | |
2370 | printf (_(" -J OPTION Unused compatibility option\n")); | |
c89c53eb | 2371 | printf ("\n"); |
0237b2dc JM |
2372 | printf (_(" -add TEXT Insert TEXT into class body\n")); |
2373 | printf (_(" -append TEXT Insert TEXT after class declaration\n")); | |
597cdf4f | 2374 | printf (_(" -friend TEXT Insert TEXT as 'friend' declaration\n")); |
0237b2dc | 2375 | printf (_(" -prepend TEXT Insert TEXT before start of class\n")); |
c89c53eb | 2376 | printf ("\n"); |
0237b2dc JM |
2377 | printf (_(" --classpath PATH Set path to find .class files\n")); |
2378 | printf (_(" -IDIR Append directory to class path\n")); | |
2379 | printf (_(" --bootclasspath PATH Override built-in class path\n")); | |
2380 | printf (_(" --extdirs PATH Set extensions directory path\n")); | |
2381 | printf (_(" -d DIRECTORY Set output directory name\n")); | |
2382 | printf (_(" -o FILE Set output file name\n")); | |
2383 | printf (_(" -td DIRECTORY Set temporary directory name\n")); | |
c89c53eb | 2384 | printf ("\n"); |
0237b2dc JM |
2385 | printf (_(" --help Print this help, then exit\n")); |
2386 | printf (_(" --version Print version number, then exit\n")); | |
2387 | printf (_(" -v, --verbose Print extra information while running\n")); | |
c89c53eb | 2388 | printf ("\n"); |
0237b2dc JM |
2389 | printf (_(" -M Print all dependencies to stdout;\n" |
2390 | " suppress ordinary output\n")); | |
2391 | printf (_(" -MM Print non-system dependencies to stdout;\n" | |
2392 | " suppress ordinary output\n")); | |
2393 | printf (_(" -MD Print all dependencies to stdout\n")); | |
2394 | printf (_(" -MMD Print non-system dependencies to stdout\n")); | |
c89c53eb TT |
2395 | /* We omit -MG until it is implemented. */ |
2396 | printf ("\n"); | |
0237b2dc JM |
2397 | printf (_("For bug reporting instructions, please see:\n" |
2398 | "%s.\n"), bug_report_url); | |
e04a16fb AG |
2399 | exit (0); |
2400 | } | |
2401 | ||
e04a16fb | 2402 | static void |
0a2f0c54 | 2403 | version (void) |
e04a16fb | 2404 | { |
d8be0aab | 2405 | printf (TOOLNAME " (GCC) %s\n\n", version_string); |
a6d6c2c0 | 2406 | printf ("Copyright %s 2006 Free Software Foundation, Inc.\n", _("(C)")); |
0237b2dc JM |
2407 | printf (_("This is free software; see the source for copying conditions. There is NO\n" |
2408 | "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n")); | |
e04a16fb AG |
2409 | exit (0); |
2410 | } | |
2411 | ||
2412 | int | |
6ff2fe39 | 2413 | main (int argc, char** argv) |
e04a16fb AG |
2414 | { |
2415 | JCF jcf; | |
2416 | int argi; | |
fc45c7ef TT |
2417 | char *output_file = NULL; |
2418 | int emit_dependencies = 0, suppress_output = 0; | |
c89c53eb | 2419 | int opt; |
a67aa338 | 2420 | int local_found_error; |
e04a16fb | 2421 | |
98a3dad4 | 2422 | /* Unlock the stdio streams. */ |
2653bb0c | 2423 | unlock_std_streams (); |
98a3dad4 | 2424 | |
0237b2dc JM |
2425 | gcc_init_libintl (); |
2426 | ||
e04a16fb | 2427 | if (argc <= 1) |
c89c53eb | 2428 | { |
0237b2dc | 2429 | error ("no classes specified"); |
c89c53eb TT |
2430 | usage (); |
2431 | } | |
e04a16fb | 2432 | |
8603f9c5 TT |
2433 | jcf_path_init (); |
2434 | ||
c89c53eb TT |
2435 | /* We use getopt_long_only to allow single `-' long options. For |
2436 | some of our options this is more natural. */ | |
d8be0aab | 2437 | while ((opt = getopt_long_only (argc, argv, "J:I:d:o:v", options, NULL)) != -1) |
e04a16fb | 2438 | { |
c89c53eb TT |
2439 | switch (opt) |
2440 | { | |
2441 | case 0: | |
2442 | /* Already handled. */ | |
2443 | break; | |
e04a16fb | 2444 | |
c89c53eb TT |
2445 | case 'o': |
2446 | output_file = optarg; | |
2447 | break; | |
e04a16fb | 2448 | |
c89c53eb TT |
2449 | case 'd': |
2450 | output_directory = optarg; | |
2451 | break; | |
e04a16fb | 2452 | |
c89c53eb TT |
2453 | case 'I': |
2454 | jcf_path_include_arg (optarg); | |
2455 | break; | |
2456 | ||
2457 | case 'v': | |
2458 | verbose++; | |
2459 | break; | |
2460 | ||
2461 | case OPT_classpath: | |
2462 | jcf_path_classpath_arg (optarg); | |
2463 | break; | |
2464 | ||
2a85660d PB |
2465 | case OPT_bootclasspath: |
2466 | jcf_path_bootclasspath_arg (optarg); | |
c89c53eb TT |
2467 | break; |
2468 | ||
9fef1fe3 AG |
2469 | case OPT_extdirs: |
2470 | jcf_path_extdirs_arg (optarg); | |
2471 | break; | |
2472 | ||
c89c53eb TT |
2473 | case OPT_HELP: |
2474 | help (); | |
2475 | break; | |
2476 | ||
2477 | case OPT_TEMP: | |
2478 | temp_directory = optarg; | |
2479 | break; | |
2480 | ||
2481 | case OPT_VERSION: | |
2482 | version (); | |
2483 | break; | |
2484 | ||
2485 | case OPT_PREPEND: | |
2486 | if (prepend_count == 0) | |
5ed6ace5 | 2487 | prepend_specs = XNEWVEC (char *, argc); |
c89c53eb TT |
2488 | prepend_specs[prepend_count++] = optarg; |
2489 | break; | |
2490 | ||
2491 | case OPT_FRIEND: | |
2492 | if (friend_count == 0) | |
5ed6ace5 | 2493 | friend_specs = XNEWVEC (char *, argc); |
c89c53eb TT |
2494 | friend_specs[friend_count++] = optarg; |
2495 | break; | |
2496 | ||
2497 | case OPT_ADD: | |
2498 | if (add_count == 0) | |
5ed6ace5 | 2499 | add_specs = XNEWVEC (char *, argc); |
c89c53eb TT |
2500 | add_specs[add_count++] = optarg; |
2501 | break; | |
2502 | ||
2503 | case OPT_APPEND: | |
2504 | if (append_count == 0) | |
5ed6ace5 | 2505 | append_specs = XNEWVEC (char *, argc); |
c89c53eb TT |
2506 | append_specs[append_count++] = optarg; |
2507 | break; | |
2508 | ||
2509 | case OPT_M: | |
fc45c7ef TT |
2510 | emit_dependencies = 1; |
2511 | suppress_output = 1; | |
2512 | jcf_dependency_init (1); | |
c89c53eb TT |
2513 | break; |
2514 | ||
2515 | case OPT_MM: | |
fc45c7ef TT |
2516 | emit_dependencies = 1; |
2517 | suppress_output = 1; | |
2518 | jcf_dependency_init (0); | |
c89c53eb TT |
2519 | break; |
2520 | ||
2521 | case OPT_MG: | |
597cdf4f | 2522 | error ("'-MG' option is unimplemented"); |
fc45c7ef | 2523 | exit (1); |
c89c53eb TT |
2524 | |
2525 | case OPT_MD: | |
fc45c7ef TT |
2526 | emit_dependencies = 1; |
2527 | jcf_dependency_init (1); | |
c89c53eb TT |
2528 | break; |
2529 | ||
2530 | case OPT_MMD: | |
fc45c7ef TT |
2531 | emit_dependencies = 1; |
2532 | jcf_dependency_init (0); | |
c89c53eb TT |
2533 | break; |
2534 | ||
d8be0aab TF |
2535 | case OPT_FORCE: |
2536 | break; | |
2537 | ||
2538 | case OPT_OLD: | |
2539 | break; | |
2540 | ||
2541 | case OPT_TRACE: | |
2542 | break; | |
2543 | ||
2544 | case 'J': | |
2545 | /* Ignore -J options. */ | |
2546 | break; | |
2547 | ||
c89c53eb TT |
2548 | default: |
2549 | usage (); | |
2550 | break; | |
e04a16fb AG |
2551 | } |
2552 | } | |
2553 | ||
c89c53eb TT |
2554 | if (optind == argc) |
2555 | { | |
0237b2dc | 2556 | error ("no classes specified"); |
c89c53eb TT |
2557 | usage (); |
2558 | } | |
e04a16fb | 2559 | |
4266d0b2 | 2560 | jcf_path_seal (verbose); |
8603f9c5 | 2561 | |
fc45c7ef TT |
2562 | if (output_file && emit_dependencies) |
2563 | { | |
0237b2dc | 2564 | error ("can't specify both -o and -MD"); |
fc45c7ef TT |
2565 | exit (1); |
2566 | } | |
2567 | ||
a67aa338 | 2568 | local_found_error = 0; |
c89c53eb | 2569 | for (argi = optind; argi < argc; argi++) |
e04a16fb AG |
2570 | { |
2571 | char *classname = argv[argi]; | |
a67aa338 | 2572 | char *current_output_file = NULL; |
c8e7d2e6 | 2573 | const char *classfile_name; |
e04a16fb | 2574 | |
a67aa338 TT |
2575 | /* We reset the error state here so that we can detect errors |
2576 | that occur when processing this file, so the output can be | |
2577 | unlinked if need be. */ | |
2578 | found_error = 0; | |
2579 | ||
e04a16fb | 2580 | if (verbose) |
0237b2dc | 2581 | printf (_("Processing %s\n"), classname); |
fc45c7ef TT |
2582 | if (! output_file) |
2583 | jcf_dependency_reset (); | |
4cc4a45b | 2584 | classfile_name = find_class (classname, strlen (classname), &jcf, 0); |
e04a16fb AG |
2585 | if (classfile_name == NULL) |
2586 | { | |
0237b2dc | 2587 | error ("%s: no such class", classname); |
e04a16fb AG |
2588 | exit (1); |
2589 | } | |
2590 | if (verbose) | |
0237b2dc | 2591 | printf (_("Found in %s\n"), classfile_name); |
e04a16fb AG |
2592 | if (output_file) |
2593 | { | |
2594 | if (strcmp (output_file, "-") == 0) | |
2595 | out = stdout; | |
2596 | else if (out == NULL) | |
fc45c7ef TT |
2597 | { |
2598 | out = fopen (output_file, "w"); | |
2599 | } | |
e04a16fb AG |
2600 | if (out == NULL) |
2601 | { | |
2602 | perror (output_file); | |
2603 | exit (1); | |
2604 | } | |
2605 | current_output_file = output_file; | |
2606 | } | |
2607 | else | |
2608 | { | |
2609 | int dir_len = strlen (output_directory); | |
2610 | int i, classname_length = strlen (classname); | |
5ed6ace5 | 2611 | current_output_file = XNEWVEC (char, dir_len + classname_length + 5); |
e04a16fb AG |
2612 | strcpy (current_output_file, output_directory); |
2613 | if (dir_len > 0 && output_directory[dir_len-1] != '/') | |
2614 | current_output_file[dir_len++] = '/'; | |
2615 | for (i = 0; classname[i] != '\0'; i++) | |
2616 | { | |
2617 | char ch = classname[i]; | |
2618 | if (ch == '.') | |
2619 | ch = '/'; | |
de380723 TT |
2620 | if (flag_jni && ch == '/') |
2621 | ch = '_'; | |
e04a16fb AG |
2622 | current_output_file[dir_len++] = ch; |
2623 | } | |
fc45c7ef | 2624 | if (emit_dependencies) |
e04a16fb | 2625 | { |
fc45c7ef TT |
2626 | if (suppress_output) |
2627 | { | |
2628 | jcf_dependency_set_dep_file ("-"); | |
2629 | out = NULL; | |
2630 | } | |
2631 | else | |
2632 | { | |
2633 | /* We use `.hd' and not `.d' to avoid clashes with | |
2634 | dependency tracking from straight compilation. */ | |
2635 | strcpy (current_output_file + dir_len, ".hd"); | |
2636 | jcf_dependency_set_dep_file (current_output_file); | |
2637 | } | |
2638 | } | |
6c9c5c1f | 2639 | strcpy (current_output_file + dir_len, |
de380723 | 2640 | stubs ? (flag_jni ? ".c" : ".cc") : ".h"); |
fc45c7ef TT |
2641 | jcf_dependency_set_target (current_output_file); |
2642 | if (! suppress_output) | |
2643 | { | |
2644 | out = fopen (current_output_file, "w"); | |
2645 | if (out == NULL) | |
2646 | { | |
2647 | perror (current_output_file); | |
2648 | exit (1); | |
2649 | } | |
e04a16fb AG |
2650 | } |
2651 | } | |
b8b639c9 | 2652 | free_method_name_list (); |
e04a16fb AG |
2653 | process_file (&jcf, out); |
2654 | JCF_FINISH (&jcf); | |
a67aa338 TT |
2655 | |
2656 | /* If we found an error and we're writing to a real file, | |
2657 | delete it. */ | |
2658 | if (found_error && ! suppress_output && current_output_file != NULL | |
2659 | && strcmp (current_output_file, "-")) | |
2660 | unlink (current_output_file); | |
2661 | ||
e04a16fb AG |
2662 | if (current_output_file != output_file) |
2663 | free (current_output_file); | |
fc45c7ef | 2664 | jcf_dependency_write (); |
a67aa338 TT |
2665 | |
2666 | local_found_error |= found_error; | |
e04a16fb AG |
2667 | } |
2668 | ||
2669 | if (out != NULL && out != stdout) | |
2670 | fclose (out); | |
2671 | ||
a67aa338 | 2672 | return local_found_error; |
e04a16fb | 2673 | } |