]> gcc.gnu.org Git - gcc.git/blob - gcc/java/gjavah.c
Warning fixes:
[gcc.git] / gcc / java / gjavah.c
1 /* Program to write C++-suitable header files from a Java(TM) .class
2 file. This is similar to SUN's javah.
3
4 Copyright (C) 1996, 1998, 1999 Free Software Foundation, Inc.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU CC; see the file COPYING. If not, write to
18 the Free Software Foundation, 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA.
20
21 Java and all Java-based marks are trademarks or registered trademarks
22 of Sun Microsystems, Inc. in the United States and other countries.
23 The Free Software Foundation is independent of Sun Microsystems, Inc. */
24
25 /* Written by Per Bothner <bothner@cygnus.com>, February 1996. */
26
27 #include "config.h"
28 #include "system.h"
29 #include "jcf.h"
30 #include "java-opcodes.h"
31 #include <math.h>
32
33 /* The output file. */
34 FILE *out = NULL;
35
36 /* Nonzero on failure. */
37 static int found_error = 0;
38
39 /* Directory to place resulting files in. Set by -d option. */
40 const char *output_directory = "";
41
42 /* Directory to place temporary file. Set by -td option. Currently unused. */
43 const char *temp_directory = "/tmp";
44
45 /* Number of friend functions we have to declare. */
46 static int friend_count;
47
48 /* A class can optionally have a `friend' function declared. If
49 non-NULL, this is that function. */
50 static char **friend_specs = NULL;
51
52 /* Number of lines we are prepending before the class. */
53 static int prepend_count;
54
55 /* We can prepend extra lines before the class's start. */
56 static char **prepend_specs = NULL;
57
58 /* Number of lines we are appending at the end of the class. */
59 static int add_count;
60
61 /* We can append extra lines just before the class's end. */
62 static char **add_specs = NULL;
63
64 /* Number of lines we are appending after the class. */
65 static int append_count;
66
67 /* We can append extra lines after the class's end. */
68 static char **append_specs = NULL;
69
70 int verbose = 0;
71
72 int stubs = 0;
73
74 struct JCF *current_jcf;
75 struct JCF *main_jcf;
76
77 /* This holds access information for the last field we examined. They
78 let us generate "private:", "public:", and "protected:" properly.
79 If 0 then we haven't previously examined any field. */
80 static JCF_u2 last_access;
81
82 #define ACC_VISIBILITY (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED)
83
84 /* Pass this macro the flags for a class and for a method. It will
85 return true if the method should be considered `final'. */
86 #define METHOD_IS_FINAL(Class, Method) \
87 (((Class) & ACC_FINAL) || ((Method) & (ACC_FINAL | ACC_PRIVATE)))
88
89 /* We keep a linked list of all method names we have seen. This lets
90 us determine if a method name and a field name are in conflict. */
91 struct method_name
92 {
93 unsigned char *name;
94 int length;
95 struct method_name *next;
96 };
97
98 /* List of method names we've seen. */
99 static struct method_name *method_name_list;
100
101 static void print_field_info PROTO ((FILE *, JCF*, int, int, JCF_u2));
102 static void print_method_info PROTO ((FILE *, JCF*, int, int, JCF_u2));
103 static void print_c_decl PROTO ((FILE*, JCF*, int, int, JCF_u2, int, const char *));
104 static void decompile_method PROTO ((FILE *, JCF *, int));
105 static void add_class_decl PROTO ((FILE *, JCF *, JCF_u2));
106
107 JCF_u2 current_field_name;
108 JCF_u2 current_field_value;
109 JCF_u2 current_field_signature;
110 JCF_u2 current_field_flags;
111
112 #define HANDLE_START_FIELD(ACCESS_FLAGS, NAME, SIGNATURE, ATTRIBUTE_COUNT) \
113 ( current_field_name = (NAME), current_field_signature = (SIGNATURE), \
114 current_field_flags = (ACCESS_FLAGS), current_field_value = 0)
115
116 /* We pass over fields twice. The first time we just note the types
117 of the fields and then the start of the methods. Then we go back
118 and parse the fields for real. This is ugly. */
119 static int field_pass;
120 /* Likewise we pass over methods twice. The first time we generate
121 class decl information; the second time we generate actual method
122 decls. */
123 static int method_pass;
124
125 #define HANDLE_END_FIELD() \
126 if (field_pass) \
127 { \
128 if (out) \
129 print_field_info (out, jcf, current_field_name, \
130 current_field_signature, \
131 current_field_flags); \
132 } \
133 else \
134 add_class_decl (out, jcf, current_field_signature);
135
136 #define HANDLE_CONSTANTVALUE(VALUEINDEX) current_field_value = (VALUEINDEX)
137
138 static int method_declared = 0;
139 static int method_access = 0;
140 static int method_printed = 0;
141 #define HANDLE_METHOD(ACCESS_FLAGS, NAME, SIGNATURE, ATTRIBUTE_COUNT) \
142 if (method_pass) \
143 { \
144 decompiled = 0; method_printed = 0; \
145 if (out) \
146 print_method_info (out, jcf, NAME, SIGNATURE, ACCESS_FLAGS); \
147 } \
148 else \
149 add_class_decl (out, jcf, SIGNATURE);
150
151 #define HANDLE_CODE_ATTRIBUTE(MAX_STACK, MAX_LOCALS, CODE_LENGTH) \
152 if (out && method_declared) decompile_method (out, jcf, CODE_LENGTH);
153
154 static int decompiled = 0;
155 #define HANDLE_END_METHOD() \
156 if (out && method_printed) fputs (decompiled ? "\n" : ";\n", out);
157
158 #include "jcf-reader.c"
159
160 /* Some useful constants. */
161 #define F_NAN_MASK 0x7f800000
162 #define D_NAN_MASK 0x7ff0000000000000LL
163
164 /* Return 1 if F is not Inf or NaN. */
165 static int
166 java_float_finite (f)
167 jfloat f;
168 {
169 union {
170 jfloat f;
171 int32 i;
172 } u;
173 u.f = f;
174
175 /* We happen to know that F_NAN_MASK will match all NaN values, and
176 also positive and negative infinity. That's why we only need one
177 test here. See The Java Language Specification, section 20.9. */
178 return (u.i & F_NAN_MASK) != F_NAN_MASK;
179 }
180
181 /* Return 1 if D is not Inf or NaN. */
182 static int
183 java_double_finite (d)
184 jdouble d;
185 {
186 union {
187 jdouble d;
188 int64 i;
189 } u;
190 u.d = d;
191
192 /* Now check for all NaNs. */
193 return (u.i & D_NAN_MASK) != D_NAN_MASK;
194 }
195
196 void
197 DEFUN(print_name, (stream, jcf, name_index),
198 FILE* stream AND JCF* jcf AND int name_index)
199 {
200 if (JPOOL_TAG (jcf, name_index) != CONSTANT_Utf8)
201 fprintf (stream, "<not a UTF8 constant>");
202 else
203 jcf_print_utf8 (stream, JPOOL_UTF_DATA (jcf, name_index),
204 JPOOL_UTF_LENGTH (jcf, name_index));
205 }
206
207 /* Print base name of class. The base name is everything after the
208 final separator. */
209
210 static void
211 print_base_classname (stream, jcf, index)
212 FILE *stream;
213 JCF *jcf;
214 int index;
215 {
216 int name_index = JPOOL_USHORT1 (jcf, index);
217 int len;
218 unsigned char *s, *p, *limit;
219
220 s = JPOOL_UTF_DATA (jcf, name_index);
221 len = JPOOL_UTF_LENGTH (jcf, name_index);
222 limit = s + len;
223 p = s;
224 while (s < limit)
225 {
226 int c = UTF8_GET (s, limit);
227 if (c == '/')
228 p = s;
229 }
230
231 while (p < limit)
232 {
233 int ch = UTF8_GET (p, limit);
234 if (ch == '/')
235 fputs ("::", stream);
236 else
237 jcf_print_char (stream, ch);
238 }
239 }
240
241 /* Return 0 if NAME is equal to STR, nonzero otherwise. */
242
243 static int
244 utf8_cmp (str, length, name)
245 unsigned char *str;
246 int length;
247 char *name;
248 {
249 unsigned char *limit = str + length;
250 int i;
251
252 for (i = 0; name[i]; ++i)
253 {
254 int ch = UTF8_GET (str, limit);
255 if (ch != name[i])
256 return 1;
257 }
258
259 return str != limit;
260 }
261
262 /* Generate an access control keyword based on FLAGS. Returns 0 if
263 FLAGS matches the saved access information, nonzero otherwise. */
264
265 static void
266 generate_access (stream, flags)
267 FILE *stream;
268 JCF_u2 flags;
269 {
270 if ((flags & ACC_VISIBILITY) == last_access)
271 return;
272 last_access = (flags & ACC_VISIBILITY);
273
274 switch (last_access)
275 {
276 case 0:
277 fputs ("public: // actually package-private\n", stream);
278 break;
279 case ACC_PUBLIC:
280 fputs ("public:\n", stream);
281 break;
282 case ACC_PRIVATE:
283 fputs ("private:\n", stream);
284 break;
285 case ACC_PROTECTED:
286 fputs ("public: // actually protected\n", stream);
287 break;
288 default:
289 found_error = 1;
290 fprintf (stream, "#error unrecognized visibility %d\n",
291 (flags & ACC_VISIBILITY));
292 break;
293 }
294 }
295
296 /* See if NAME is already the name of a method. */
297 static int
298 name_is_method_p (name, length)
299 unsigned char *name;
300 int length;
301 {
302 struct method_name *p;
303
304 for (p = method_name_list; p != NULL; p = p->next)
305 {
306 if (p->length == length && ! memcmp (p->name, name, length))
307 return 1;
308 }
309 return 0;
310 }
311
312 static void
313 DEFUN(print_field_info, (stream, jcf, name_index, sig_index, flags),
314 FILE *stream AND JCF* jcf
315 AND int name_index AND int sig_index AND JCF_u2 flags)
316 {
317 char *override = NULL;
318
319 if (flags & ACC_FINAL)
320 {
321 if (current_field_value > 0)
322 {
323 char buffer[25];
324
325 generate_access (stream, flags);
326 switch (JPOOL_TAG (jcf, current_field_value))
327 {
328 case CONSTANT_Integer:
329 {
330 jint num;
331 int most_negative = 0;
332 fputs (" static const jint ", out);
333 print_name (out, jcf, name_index);
334 fputs (" = ", out);
335 num = JPOOL_INT (jcf, current_field_value);
336 /* We single out the most negative number to print
337 specially. This avoids later warnings from g++. */
338 if (num == (jint) 0x80000000)
339 {
340 most_negative = 1;
341 ++num;
342 }
343 format_int (buffer, (jlong) num, 10);
344 fprintf (out, "%sL%s;\n", buffer, most_negative ? " - 1" : "");
345 }
346 break;
347 case CONSTANT_Long:
348 {
349 jlong num;
350 int most_negative = 0;
351 fputs (" static const jlong ", out);
352 print_name (out, jcf, name_index);
353 fputs (" = ", out);
354 num = JPOOL_LONG (jcf, current_field_value);
355 /* We single out the most negative number to print
356 specially.. This avoids later warnings from g++. */
357 if (num == (jlong) 0x8000000000000000LL)
358 {
359 most_negative = 1;
360 ++num;
361 }
362 format_int (buffer, num, 10);
363 fprintf (out, "%sLL%s;\n", buffer, most_negative ? " - 1" :"");
364 }
365 break;
366 case CONSTANT_Float:
367 {
368 jfloat fnum = JPOOL_FLOAT (jcf, current_field_value);
369 fputs (" static const jfloat ", out);
370 print_name (out, jcf, name_index);
371 if (! java_float_finite (fnum))
372 fputs (";\n", out);
373 else
374 fprintf (out, " = %.10g;\n", fnum);
375 }
376 break;
377 case CONSTANT_Double:
378 {
379 jdouble dnum = JPOOL_DOUBLE (jcf, current_field_value);
380 fputs (" static const jdouble ", out);
381 print_name (out, jcf, name_index);
382 if (! java_double_finite (dnum))
383 fputs (";\n", out);
384 else
385 fprintf (out, " = %.17g;\n", dnum);
386 }
387 break;
388 default:
389 fprintf(out, " <<inappropriate constant type>>\n");
390 }
391
392 return;
393 }
394 }
395
396 generate_access (stream, flags);
397 fputs (" ", out);
398 if ((flags & ACC_STATIC))
399 fputs ("static ", out);
400
401 if (JPOOL_TAG (jcf, name_index) != CONSTANT_Utf8)
402 {
403 fprintf (stream, "<not a UTF8 constant>");
404 found_error = 1;
405 }
406 else
407 {
408 unsigned char *name = JPOOL_UTF_DATA (jcf, name_index);
409 int length = JPOOL_UTF_LENGTH (jcf, name_index);
410
411 if (name_is_method_p (name, length))
412 {
413 /* This field name matches a method. So override the name
414 with a dummy name. This is yucky, but it isn't clear
415 what else to do. FIXME: if the field is static, then
416 we'll be in real trouble. */
417 if ((flags & ACC_STATIC))
418 {
419 fprintf (stderr, "static field has same name as method\n");
420 found_error = 1;
421 }
422
423 override = (char *) malloc (length + 3);
424 memcpy (override, name, length);
425 strcpy (override + length, "__");
426 }
427 }
428
429 print_c_decl (out, jcf, name_index, sig_index, flags, 0, override);
430 fputs (";\n", out);
431
432 if (override)
433 free (override);
434 }
435
436 static void
437 DEFUN(print_method_info, (stream, jcf, name_index, sig_index, flags),
438 FILE *stream AND JCF* jcf
439 AND int name_index AND int sig_index AND JCF_u2 flags)
440 {
441 unsigned char *str;
442 int length, is_init = 0;
443 const char *override = NULL;
444
445 method_declared = 0;
446 method_access = flags;
447 if (JPOOL_TAG (jcf, name_index) != CONSTANT_Utf8)
448 fprintf (stream, "<not a UTF8 constant>");
449 str = JPOOL_UTF_DATA (jcf, name_index);
450 length = JPOOL_UTF_LENGTH (jcf, name_index);
451 if (str[0] == '<' || str[0] == '$')
452 {
453 /* Ignore internally generated methods like <clinit> and
454 $finit$. However, treat <init> as a constructor. */
455 if (! utf8_cmp (str, length, "<init>"))
456 is_init = 1;
457 else if (! METHOD_IS_FINAL (jcf->access_flags, flags)
458 && ! (flags & ACC_STATIC))
459 {
460 /* FIXME: i18n bug here. Order of prints should not be
461 fixed. */
462 fprintf (stderr, "ignored method `");
463 jcf_print_utf8 (stderr, str, length);
464 fprintf (stderr, "' marked virtual\n");
465 found_error = 1;
466 return;
467 }
468 else
469 return;
470 }
471 else
472 {
473 struct method_name *nn;
474
475 nn = (struct method_name *) malloc (sizeof (struct method_name));
476 nn->name = (char *) malloc (length);
477 memcpy (nn->name, str, length);
478 nn->length = length;
479 nn->next = method_name_list;
480 method_name_list = nn;
481 }
482
483 /* We can't generate a method whose name is a C++ reserved word.
484 For now the only problem has been `delete'; add more here as
485 required. We can't just ignore the function, because that will
486 cause incorrect code to be generated if the function is virtual
487 (not only for calls to this function for for other functions
488 after it in the vtbl). So we give it a dummy name instead. */
489 if (! utf8_cmp (str, length, "delete"))
490 {
491 /* If the method is static or final, we can safely skip it. If
492 we don't skip it then we'll have problems since the mangling
493 will be wrong. FIXME. */
494 if (METHOD_IS_FINAL (jcf->access_flags, flags)
495 || (flags & ACC_STATIC))
496 return;
497 override = "__dummy_delete";
498 }
499
500 method_printed = 1;
501 generate_access (stream, flags);
502
503 fputs (" ", out);
504 if ((flags & ACC_STATIC))
505 fputs ("static ", out);
506 else if (! METHOD_IS_FINAL (jcf->access_flags, flags))
507 {
508 /* Don't print `virtual' if we have a constructor. */
509 if (! is_init)
510 fputs ("virtual ", out);
511 }
512 print_c_decl (out, jcf, name_index, sig_index, flags, is_init, override);
513
514 if ((flags & ACC_ABSTRACT))
515 fputs (" = 0", out);
516 else
517 method_declared = 1;
518 }
519
520 /* Try to decompile a method body. Right now we just try to handle a
521 simple case that we can do. Expand as desired. */
522 static void
523 decompile_method (out, jcf, code_len)
524 FILE *out;
525 JCF *jcf;
526 int code_len;
527 {
528 unsigned char *codes = jcf->read_ptr;
529 int index;
530 uint16 name_and_type, name;
531
532 /* If the method is synchronized, don't touch it. */
533 if ((method_access & ACC_SYNCHRONIZED))
534 return;
535
536 if (code_len == 5
537 && codes[0] == OPCODE_aload_0
538 && codes[1] == OPCODE_getfield
539 && (codes[4] == OPCODE_areturn
540 || codes[4] == OPCODE_dreturn
541 || codes[4] == OPCODE_freturn
542 || codes[4] == OPCODE_ireturn
543 || codes[4] == OPCODE_lreturn))
544 {
545 /* Found code like `return FIELD'. */
546 fputs (" { return ", out);
547 index = (codes[2] << 8) | codes[3];
548 /* FIXME: ensure that tag is CONSTANT_Fieldref. */
549 /* FIXME: ensure that the field's class is this class. */
550 name_and_type = JPOOL_USHORT2 (jcf, index);
551 /* FIXME: ensure that tag is CONSTANT_NameAndType. */
552 name = JPOOL_USHORT1 (jcf, name_and_type);
553 print_name (out, jcf, name);
554 fputs ("; }", out);
555 decompiled = 1;
556 }
557 else if (code_len == 2
558 && codes[0] == OPCODE_aload_0
559 && codes[1] == OPCODE_areturn)
560 {
561 /* Found `return this'. */
562 fputs (" { return this; }", out);
563 decompiled = 1;
564 }
565 else if (code_len == 1 && codes[0] == OPCODE_return)
566 {
567 /* Found plain `return'. */
568 fputs (" { }", out);
569 decompiled = 1;
570 }
571 else if (code_len == 2
572 && codes[0] == OPCODE_aconst_null
573 && codes[1] == OPCODE_areturn)
574 {
575 /* Found `return null'. We don't want to depend on NULL being
576 defined. */
577 fputs (" { return 0; }", out);
578 decompiled = 1;
579 }
580 }
581
582 /* Print one piece of a signature. Returns pointer to next parseable
583 character on success, NULL on error. */
584 static unsigned char *
585 decode_signature_piece (stream, signature, limit, need_space)
586 FILE *stream;
587 unsigned char *signature, *limit;
588 int *need_space;
589 {
590 const char *ctype;
591
592 switch (signature[0])
593 {
594 case '[':
595 for (signature++; (signature < limit
596 && *signature >= '0'
597 && *signature <= '9'); signature++)
598 ;
599 switch (*signature)
600 {
601 case 'B': ctype = "jbyteArray"; goto printit;
602 case 'C': ctype = "jcharArray"; goto printit;
603 case 'D': ctype = "jdoubleArray"; goto printit;
604 case 'F': ctype = "jfloatArray"; goto printit;
605 case 'I': ctype = "jintArray"; goto printit;
606 case 'S': ctype = "jshortArray"; goto printit;
607 case 'J': ctype = "jlongArray"; goto printit;
608 case 'Z': ctype = "jbooleanArray"; goto printit;
609 case '[': ctype = "jobjectArray"; goto printit;
610 case 'L':
611 /* We have to generate a reference to JArray here,
612 so that our output matches what the compiler
613 does. */
614 ++signature;
615 fputs ("JArray<", stream);
616 while (signature < limit && *signature != ';')
617 {
618 int ch = UTF8_GET (signature, limit);
619 if (ch == '/')
620 fputs ("::", stream);
621 else
622 jcf_print_char (stream, ch);
623 }
624 fputs (" *> *", stream);
625 *need_space = 0;
626 ++signature;
627 break;
628 default:
629 /* Unparseable signature. */
630 return NULL;
631 }
632 break;
633
634 case '(':
635 case ')':
636 /* This shouldn't happen. */
637 return NULL;
638
639 case 'B': ctype = "jbyte"; goto printit;
640 case 'C': ctype = "jchar"; goto printit;
641 case 'D': ctype = "jdouble"; goto printit;
642 case 'F': ctype = "jfloat"; goto printit;
643 case 'I': ctype = "jint"; goto printit;
644 case 'J': ctype = "jlong"; goto printit;
645 case 'S': ctype = "jshort"; goto printit;
646 case 'Z': ctype = "jboolean"; goto printit;
647 case 'V': ctype = "void"; goto printit;
648 case 'L':
649 ++signature;
650 while (*signature && *signature != ';')
651 {
652 int ch = UTF8_GET (signature, limit);
653 /* `$' is the separator for an inner class. */
654 if (ch == '/' || ch == '$')
655 fputs ("::", stream);
656 else
657 jcf_print_char (stream, ch);
658 }
659 fputs (" *", stream);
660 if (*signature == ';')
661 signature++;
662 *need_space = 0;
663 break;
664 default:
665 *need_space = 1;
666 jcf_print_char (stream, *signature++);
667 break;
668 printit:
669 signature++;
670 *need_space = 1;
671 fputs (ctype, stream);
672 break;
673 }
674
675 return signature;
676 }
677
678 static void
679 DEFUN(print_c_decl, (stream, jcf, name_index, signature_index, flags, is_init,
680 name_override),
681 FILE* stream AND JCF* jcf
682 AND int name_index AND int signature_index AND JCF_u2 flags
683 AND int is_init AND const char *name_override)
684 {
685 if (JPOOL_TAG (jcf, signature_index) != CONSTANT_Utf8)
686 {
687 fprintf (stream, "<not a UTF8 constant>");
688 found_error = 1;
689 }
690 else
691 {
692 int length = JPOOL_UTF_LENGTH (jcf, signature_index);
693 unsigned char *str0 = JPOOL_UTF_DATA (jcf, signature_index);
694 register unsigned char *str = str0;
695 unsigned char *limit = str + length;
696 int need_space = 0;
697 int is_method = str[0] == '(';
698 unsigned char *next;
699
700 /* If printing a method, skip to the return signature and print
701 that first. However, there is no return value if this is a
702 constructor. */
703 if (is_method && ! is_init)
704 {
705 while (str < limit)
706 {
707 int ch = *str++;
708 if (ch == ')')
709 break;
710 }
711 }
712
713 /* If printing a field or an ordinary method, then print the
714 "return value" now. */
715 if (! is_method || ! is_init)
716 {
717 next = decode_signature_piece (stream, str, limit, &need_space);
718 if (! next)
719 {
720 fprintf (stderr, "unparseable signature: `%s'\n", str0);
721 found_error = 1;
722 return;
723 }
724 }
725
726 /* Now print the name of the thing. */
727 if (need_space)
728 fputs (" ", stream);
729 if (name_override)
730 fputs (name_override, stream);
731 else if (name_index)
732 {
733 /* Declare constructors specially. */
734 if (is_init)
735 print_base_classname (stream, jcf, jcf->this_class);
736 else
737 print_name (stream, jcf, name_index);
738 }
739
740 if (is_method)
741 {
742 /* Have a method or a constructor. Print signature pieces
743 until done. */
744 fputs (" (", stream);
745 str = str0 + 1;
746 while (str < limit && *str != ')')
747 {
748 next = decode_signature_piece (stream, str, limit, &need_space);
749 if (! next)
750 {
751 fprintf (stderr, "unparseable signature: `%s'\n", str0);
752 found_error = 1;
753 return;
754 }
755
756 if (next < limit && *next != ')')
757 fputs (", ", stream);
758 str = next;
759 }
760
761 fputs (")", stream);
762 }
763 }
764 }
765
766 void
767 DEFUN(print_mangled_classname, (stream, jcf, prefix, index),
768 FILE *stream AND JCF *jcf AND const char *prefix AND int index)
769 {
770 int name_index = JPOOL_USHORT1 (jcf, index);
771 fputs (prefix, stream);
772 jcf_print_utf8_replace (out,
773 JPOOL_UTF_DATA (jcf, name_index),
774 JPOOL_UTF_LENGTH (jcf, name_index),
775 '/', '_');
776 }
777
778 /* Print PREFIX, then a class name in C++ format. If the name refers
779 to an array, ignore it and don't print PREFIX. Returns 1 if
780 something was printed, 0 otherwise. */
781 static int
782 print_cxx_classname (stream, prefix, jcf, index)
783 FILE *stream;
784 char *prefix;
785 JCF *jcf;
786 int index;
787 {
788 int name_index = JPOOL_USHORT1 (jcf, index);
789 int len, c;
790 unsigned char *s, *p, *limit;
791
792 s = JPOOL_UTF_DATA (jcf, name_index);
793 len = JPOOL_UTF_LENGTH (jcf, name_index);
794 limit = s + len;
795
796 /* Explicitly omit arrays here. */
797 p = s;
798 c = UTF8_GET (p, limit);
799 if (c == '[')
800 return 0;
801
802 fputs (prefix, stream);
803 while (s < limit)
804 {
805 c = UTF8_GET (s, limit);
806 if (c == '/')
807 fputs ("::", stream);
808 else
809 jcf_print_char (stream, c);
810 }
811
812 return 1;
813 }
814
815 int written_class_count = 0;
816
817 /* Return name of superclass. If LEN is not NULL, fill it with length
818 of name. */
819 static unsigned char *
820 super_class_name (derived_jcf, len)
821 JCF *derived_jcf;
822 int *len;
823 {
824 int supername_index = JPOOL_USHORT1 (derived_jcf, derived_jcf->super_class);
825 int supername_length = JPOOL_UTF_LENGTH (derived_jcf, supername_index);
826 unsigned char *supername = JPOOL_UTF_DATA (derived_jcf, supername_index);
827
828 if (len)
829 *len = supername_length;
830
831 return supername;
832 }
833
834 \f
835
836 /* We keep track of all the `#include's we generate, so we can avoid
837 duplicates. */
838 struct include
839 {
840 char *name;
841 struct include *next;
842 };
843
844 /* List of all includes. */
845 static struct include *all_includes = NULL;
846
847 /* Generate a #include. */
848 static void
849 print_include (out, utf8, len)
850 FILE *out;
851 unsigned char *utf8;
852 int len;
853 {
854 struct include *incl;
855
856 if (! out)
857 return;
858
859 if (len == -1)
860 len = strlen (utf8);
861
862 for (incl = all_includes; incl; incl = incl->next)
863 {
864 if (! strncmp (incl->name, utf8, len))
865 return;
866 }
867
868 incl = (struct include *) malloc (sizeof (struct include));
869 incl->name = malloc (len + 1);
870 strncpy (incl->name, utf8, len);
871 incl->name[len] = '\0';
872 incl->next = all_includes;
873 all_includes = incl;
874
875 fputs ("#include <", out);
876 jcf_print_utf8 (out, utf8, len);
877 fputs (".h>\n", out);
878 }
879
880 \f
881
882 /* This is used to represent part of a package or class name. */
883 struct namelet
884 {
885 /* The text of this part of the name. */
886 char *name;
887 /* True if this represents a class. */
888 int is_class;
889 /* Linked list of all classes and packages inside this one. */
890 struct namelet *subnamelets;
891 /* Pointer to next sibling. */
892 struct namelet *next;
893 };
894
895 /* The special root namelet. */
896 static struct namelet root =
897 {
898 NULL,
899 0,
900 NULL,
901 NULL
902 };
903
904 /* This extracts the next name segment from the full UTF-8 encoded
905 package or class name and links it into the tree. It does this
906 recursively. */
907 static void
908 add_namelet (name, name_limit, parent)
909 unsigned char *name, *name_limit;
910 struct namelet *parent;
911 {
912 unsigned char *p;
913 struct namelet *n = NULL, *np;
914
915 /* We want to skip the standard namespaces that we assume the
916 runtime already knows about. We only do this at the top level,
917 though, hence the check for `root'. */
918 if (parent == &root)
919 {
920 #define JAVALANG "java/lang/"
921 #define JAVAIO "java/io/"
922 #define JAVAUTIL "java/util/"
923 if ((name_limit - name >= (int) sizeof (JAVALANG) - 1
924 && ! strncmp (name, JAVALANG, sizeof (JAVALANG) - 1))
925 || (name_limit - name >= (int) sizeof (JAVAUTIL) - 1
926 && ! strncmp (name, JAVAUTIL, sizeof (JAVAUTIL) - 1))
927 || (name_limit - name >= (int) sizeof (JAVAIO) - 1
928 && ! strncmp (name, JAVAIO, sizeof (JAVAIO) - 1)))
929 return;
930 }
931
932 for (p = name; p < name_limit && *p != '/' && *p != '$'; ++p)
933 ;
934
935 /* Search for this name beneath the PARENT node. */
936 for (np = parent->subnamelets; np != NULL; np = np->next)
937 {
938 if (! strncmp (name, np->name, p - name))
939 {
940 n = np;
941 break;
942 }
943 }
944
945 if (n == NULL)
946 {
947 n = (struct namelet *) malloc (sizeof (struct namelet));
948 n->name = malloc (p - name + 1);
949 strncpy (n->name, name, p - name);
950 n->name[p - name] = '\0';
951 n->is_class = (p == name_limit || *p == '$');
952 n->subnamelets = NULL;
953 n->next = parent->subnamelets;
954 parent->subnamelets = n;
955 }
956
957 /* We recurse if there is more text, and if the trailing piece does
958 not represent an inner class. */
959 if (p < name_limit && *p != '$')
960 add_namelet (p + 1, name_limit, n);
961 }
962
963 /* Print a single namelet. Destroys namelets while printing. */
964 static void
965 print_namelet (out, name, depth)
966 FILE *out;
967 struct namelet *name;
968 int depth;
969 {
970 int i, term = 0;
971 struct namelet *c;
972
973 if (name->name)
974 {
975 for (i = 0; i < depth; ++i)
976 fputc (' ', out);
977 fprintf (out, "%s %s", name->is_class ? "class" : "namespace",
978 name->name);
979 if (name->is_class && name->subnamelets == NULL)
980 fputs (";\n", out);
981 else
982 {
983 term = 1;
984 fputs ("\n", out);
985 for (i = 0; i < depth; ++i)
986 fputc (' ', out);
987 fputs ("{\n", out);
988 }
989 }
990
991 c = name->subnamelets;
992 while (c != NULL)
993 {
994 struct namelet *next = c->next;
995 print_namelet (out, c, depth + 2);
996 c = next;
997 }
998
999 if (name->name)
1000 {
1001 if (term)
1002 {
1003 for (i = 0; i < depth; ++i)
1004 fputc (' ', out);
1005 fputs ("};\n", out);
1006 }
1007
1008 free (name->name);
1009 free (name);
1010 }
1011 }
1012
1013 /* This is called to add some classes to the list of classes for which
1014 we need decls. The signature argument can be a function
1015 signature. */
1016 static void
1017 add_class_decl (out, jcf, signature)
1018 FILE *out;
1019 JCF *jcf;
1020 JCF_u2 signature;
1021 {
1022 unsigned char *s = JPOOL_UTF_DATA (jcf, signature);
1023 int len = JPOOL_UTF_LENGTH (jcf, signature);
1024 int i;
1025 /* Name of class we are processing. */
1026 int name_index = JPOOL_USHORT1 (jcf, jcf->this_class);
1027 int tlen = JPOOL_UTF_LENGTH (jcf, name_index);
1028 char *tname = JPOOL_UTF_DATA (jcf, name_index);
1029
1030 for (i = 0; i < len; ++i)
1031 {
1032 int start, saw_dollar;
1033
1034 /* If we see an array, then we include the array header. */
1035 if (s[i] == '[')
1036 {
1037 print_include (out, "java-array", -1);
1038 continue;
1039 }
1040
1041 /* We're looking for `L<stuff>;' -- everything else is
1042 ignorable. */
1043 if (s[i] != 'L')
1044 continue;
1045
1046 saw_dollar = 0;
1047 for (start = ++i; i < len && s[i] != ';'; ++i)
1048 {
1049 if (! saw_dollar && s[i] == '$' && out)
1050 {
1051 saw_dollar = 1;
1052 /* If this class represents an inner class, then
1053 generate a `#include' for the outer class. However,
1054 don't generate the include if the outer class is the
1055 class we are processing. */
1056 if (i - start < tlen || strncmp (&s[start], tname, i - start))
1057 print_include (out, &s[start], i - start);
1058 break;
1059 }
1060 }
1061
1062 /* If we saw an inner class, then the generated #include will
1063 declare the class. So in this case we needn't bother. */
1064 if (! saw_dollar)
1065 add_namelet (&s[start], &s[i], &root);
1066 }
1067 }
1068
1069 /* Print declarations for all classes required by this class. Any
1070 class or package in the `java' package is assumed to be handled
1071 statically in libjava; we don't generate declarations for these.
1072 This makes the generated headers a bit easier to read. */
1073 static void
1074 print_class_decls (out, jcf, self)
1075 FILE *out;
1076 JCF *jcf;
1077 int self;
1078 {
1079 /* Make sure to always add the current class to the list of things
1080 that should be declared. */
1081 int name_index = JPOOL_USHORT1 (jcf, self);
1082 int len;
1083 unsigned char *s;
1084
1085 s = JPOOL_UTF_DATA (jcf, name_index);
1086 len = JPOOL_UTF_LENGTH (jcf, name_index);
1087 add_namelet (s, s + len, &root);
1088
1089 if (root.subnamelets)
1090 {
1091 fputs ("extern \"Java\"\n{\n", out);
1092 /* We use an initial offset of 0 because the root namelet
1093 doesn't cause anything to print. */
1094 print_namelet (out, &root, 0);
1095 fputs ("};\n\n", out);
1096 }
1097 }
1098
1099 \f
1100
1101 static void
1102 DEFUN(process_file, (jcf, out),
1103 JCF *jcf AND FILE *out)
1104 {
1105 int code, i;
1106 uint32 field_start, method_end, method_start;
1107
1108 current_jcf = main_jcf = jcf;
1109
1110 last_access = -1;
1111
1112 if (jcf_parse_preamble (jcf) != 0)
1113 {
1114 fprintf (stderr, "Not a valid Java .class file.\n");
1115 found_error = 1;
1116 return;
1117 }
1118
1119 /* Parse and possibly print constant pool */
1120 code = jcf_parse_constant_pool (jcf);
1121 if (code != 0)
1122 {
1123 fprintf (stderr, "error while parsing constant pool\n");
1124 found_error = 1;
1125 return;
1126 }
1127 code = verify_constant_pool (jcf);
1128 if (code > 0)
1129 {
1130 fprintf (stderr, "error in constant pool entry #%d\n", code);
1131 found_error = 1;
1132 return;
1133 }
1134
1135 jcf_parse_class (jcf);
1136
1137 if (written_class_count++ == 0 && out)
1138 fputs ("// DO NOT EDIT THIS FILE - it is machine generated -*- c++ -*-\n\n",
1139 out);
1140
1141 if (out)
1142 {
1143 print_mangled_classname (out, jcf, "#ifndef __", jcf->this_class);
1144 fprintf (out, "__\n");
1145
1146 print_mangled_classname (out, jcf, "#define __", jcf->this_class);
1147 fprintf (out, "__\n\n");
1148
1149 /* We do this to ensure that inline methods won't be `outlined'
1150 by g++. This works as long as method and fields are not
1151 added by the user. */
1152 fprintf (out, "#pragma interface\n");
1153 }
1154
1155 if (jcf->super_class && out)
1156 {
1157 int super_length;
1158 unsigned char *supername = super_class_name (jcf, &super_length);
1159
1160 fputs ("\n", out);
1161 print_include (out, supername, super_length);
1162 }
1163
1164 /* We want to parse the methods first. But we need to find where
1165 they start. So first we skip the fields, then parse the methods.
1166 Then we parse the fields and skip the methods. This is ugly, but
1167 not too bad since we need two full passes to get class decl
1168 information anyway. */
1169 field_pass = 0;
1170 field_start = JCF_TELL (jcf);
1171 jcf_parse_fields (jcf);
1172
1173 method_start = JCF_TELL (jcf);
1174 method_pass = 0;
1175 jcf_parse_methods (jcf);
1176
1177 if (out)
1178 {
1179 fputs ("\n", out);
1180 print_class_decls (out, jcf, jcf->this_class);
1181
1182 for (i = 0; i < prepend_count; ++i)
1183 fprintf (out, "%s\n", prepend_specs[i]);
1184 if (prepend_count > 0)
1185 fputc ('\n', out);
1186 }
1187
1188 if (out && ! print_cxx_classname (out, "class ", jcf, jcf->this_class))
1189 {
1190 fprintf (stderr, "class is of array type\n");
1191 found_error = 1;
1192 return;
1193 }
1194 if (out && jcf->super_class)
1195 {
1196 if (! print_cxx_classname (out, " : public ", jcf, jcf->super_class))
1197 {
1198 fprintf (stderr, "base class is of array type\n");
1199 found_error = 1;
1200 return;
1201 }
1202 }
1203 if (out)
1204 fputs ("\n{\n", out);
1205
1206 /* Now go back for second pass over methods and fields. */
1207 JCF_SEEK (jcf, method_start);
1208 method_pass = 1;
1209 jcf_parse_methods (jcf);
1210 method_end = JCF_TELL (jcf);
1211
1212 field_pass = 1;
1213 JCF_SEEK (jcf, field_start);
1214 jcf_parse_fields (jcf);
1215 JCF_SEEK (jcf, method_end);
1216
1217 jcf_parse_final_attributes (jcf);
1218
1219 if (out)
1220 {
1221 /* Generate friend decl if we still must. */
1222 for (i = 0; i < friend_count; ++i)
1223 fprintf (out, " friend %s\n", friend_specs[i]);
1224
1225 /* Generate extra declarations. */
1226 if (add_count > 0)
1227 fputc ('\n', out);
1228 for (i = 0; i < add_count; ++i)
1229 fprintf (out, " %s\n", add_specs[i]);
1230
1231 fputs ("};\n", out);
1232
1233 if (append_count > 0)
1234 fputc ('\n', out);
1235 for (i = 0; i < append_count; ++i)
1236 fprintf (out, "%s\n", append_specs[i]);
1237
1238 print_mangled_classname (out, jcf, "\n#endif /* __", jcf->this_class);
1239 fprintf (out, "__ */\n");
1240 }
1241 }
1242
1243 static void
1244 usage ()
1245 {
1246 fprintf (stderr, "gcjh: no classes specified\n");
1247 exit (1);
1248 }
1249
1250 static void
1251 help ()
1252 {
1253 printf ("Usage: gcjh [OPTION]... CLASS...\n\n");
1254 printf ("Generate C++ header files from .class files\n\n");
1255 printf (" --classpath PATH Set path to find .class files\n");
1256 printf (" --CLASSPATH PATH Set path to find .class files\n");
1257 printf (" -IDIR Append directory to class path\n");
1258 printf (" -d DIRECTORY Set output directory name\n");
1259 printf (" --help Print this help, then exit\n");
1260 printf (" -o FILE Set output file name\n");
1261 printf (" -td DIRECTORY Set temporary directory name\n");
1262 printf (" -v, --verbose Print extra information while running\n");
1263 printf (" --version Print version number, then exit\n");
1264 /* FIXME: print bug-report information. */
1265 exit (0);
1266 }
1267
1268 static void
1269 java_no_argument (opt)
1270 char *opt;
1271 {
1272 fprintf (stderr, "gcjh: no argument given for option `%s'\n", opt);
1273 exit (1);
1274 }
1275
1276 static void
1277 version ()
1278 {
1279 /* FIXME: use version.c? */
1280 printf ("gcjh (GNU gcc) 0.0\n\n");
1281 printf ("Copyright (C) 1998 Free Software Foundation, Inc.\n");
1282 printf ("This is free software; see the source for copying conditions. There is NO\n");
1283 printf ("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n");
1284 exit (0);
1285 }
1286
1287 int
1288 DEFUN(main, (argc, argv),
1289 int argc AND char** argv)
1290 {
1291 JCF jcf;
1292 int argi;
1293 char *output_file = NULL;
1294 int emit_dependencies = 0, suppress_output = 0;
1295
1296 if (argc <= 1)
1297 usage ();
1298
1299 jcf_path_init ();
1300
1301 for (argi = 1; argi < argc; argi++)
1302 {
1303 char *arg = argv[argi];
1304
1305 if (arg[0] != '-' || ! strcmp (arg, "--"))
1306 break;
1307
1308 /* Just let all arguments be given in either "-" or "--" form. */
1309 if (arg[1] == '-')
1310 ++arg;
1311
1312 if (strcmp (arg, "-o") == 0)
1313 {
1314 if (argi + 1 < argc)
1315 output_file = argv[++argi];
1316 else
1317 java_no_argument (argv[argi]);
1318 }
1319 else if (strcmp (arg, "-d") == 0)
1320 {
1321 if (argi + 1 < argc)
1322 output_directory = argv[++argi];
1323 else
1324 java_no_argument (argv[argi]);
1325 }
1326 else if (strcmp (arg, "-td") == 0)
1327 {
1328 if (argi + 1 < argc)
1329 temp_directory = argv[++argi];
1330 else
1331 java_no_argument (argv[argi]);
1332 }
1333 else if (strcmp (arg, "-prepend") == 0)
1334 {
1335 if (argi + 1 < argc)
1336 {
1337 if (prepend_count == 0)
1338 prepend_specs = (char**) ALLOC ((argc-argi) * sizeof (char*));
1339 prepend_specs[prepend_count++] = argv[++argi];
1340 }
1341 else
1342 java_no_argument (argv[argi]);
1343 }
1344 else if (strcmp (arg, "-friend") == 0)
1345 {
1346 if (argi + 1 < argc)
1347 {
1348 if (friend_count == 0)
1349 friend_specs = (char**) ALLOC ((argc-argi) * sizeof (char*));
1350 friend_specs[friend_count++] = argv[++argi];
1351 }
1352 else
1353 java_no_argument (argv[argi]);
1354 }
1355 else if (strcmp (arg, "-add") == 0)
1356 {
1357 if (argi + 1 < argc)
1358 {
1359 if (add_count == 0)
1360 add_specs = (char**) ALLOC ((argc-argi) * sizeof (char*));
1361 add_specs[add_count++] = argv[++argi];
1362 }
1363 else
1364 java_no_argument (argv[argi]);
1365 }
1366 else if (strcmp (arg, "-append") == 0)
1367 {
1368 if (argi + 1 < argc)
1369 {
1370 if (append_count == 0)
1371 append_specs = (char**) ALLOC ((argc-argi) * sizeof (char*));
1372 append_specs[append_count++] = argv[++argi];
1373 }
1374 else
1375 java_no_argument (argv[argi]);
1376 }
1377 else if (strcmp (arg, "-classpath") == 0)
1378 {
1379 if (argi + 1 < argc)
1380 jcf_path_classpath_arg (argv[++argi]);
1381 else
1382 java_no_argument (argv[argi]);
1383 }
1384 else if (strcmp (arg, "-CLASSPATH") == 0)
1385 {
1386 if (argi + 1 < argc)
1387 jcf_path_CLASSPATH_arg (argv[++argi]);
1388 else
1389 java_no_argument (argv[argi]);
1390 }
1391 else if (strncmp (arg, "-I", 2) == 0)
1392 jcf_path_include_arg (arg + 2);
1393 else if (strcmp (arg, "-verbose") == 0 || strcmp (arg, "-v") == 0)
1394 verbose++;
1395 else if (strcmp (arg, "-stubs") == 0)
1396 stubs++;
1397 else if (strcmp (arg, "-help") == 0)
1398 help ();
1399 else if (strcmp (arg, "-version") == 0)
1400 version ();
1401 else if (strcmp (arg, "-M") == 0)
1402 {
1403 emit_dependencies = 1;
1404 suppress_output = 1;
1405 jcf_dependency_init (1);
1406 }
1407 else if (strcmp (arg, "-MM") == 0)
1408 {
1409 emit_dependencies = 1;
1410 suppress_output = 1;
1411 jcf_dependency_init (0);
1412 }
1413 else if (strcmp (arg, "-MG") == 0)
1414 {
1415 fprintf (stderr, "gcjh: `%s' option is unimplemented\n", argv[argi]);
1416 exit (1);
1417 }
1418 else if (strcmp (arg, "-MD") == 0)
1419 {
1420 emit_dependencies = 1;
1421 jcf_dependency_init (1);
1422 }
1423 else if (strcmp (arg, "-MMD") == 0)
1424 {
1425 emit_dependencies = 1;
1426 jcf_dependency_init (0);
1427 }
1428 else
1429 {
1430 fprintf (stderr, "%s: illegal argument\n", argv[argi]);
1431 exit (1);
1432 }
1433 }
1434
1435 if (argi == argc)
1436 usage ();
1437
1438 jcf_path_seal ();
1439
1440 if (output_file && emit_dependencies)
1441 {
1442 fprintf (stderr, "gcjh: can't specify both -o and -MD\n");
1443 exit (1);
1444 }
1445
1446 for (; argi < argc; argi++)
1447 {
1448 char *classname = argv[argi];
1449 char *classfile_name, *current_output_file;
1450
1451 if (verbose)
1452 fprintf (stderr, "Processing %s\n", classname);
1453 if (! output_file)
1454 jcf_dependency_reset ();
1455 classfile_name = find_class (classname, strlen (classname), &jcf, 0);
1456 if (classfile_name == NULL)
1457 {
1458 fprintf (stderr, "%s: no such class\n", classname);
1459 exit (1);
1460 }
1461 if (verbose)
1462 fprintf (stderr, "Found in %s\n", classfile_name);
1463 if (output_file)
1464 {
1465 if (strcmp (output_file, "-") == 0)
1466 out = stdout;
1467 else if (out == NULL)
1468 {
1469 out = fopen (output_file, "w");
1470 }
1471 if (out == NULL)
1472 {
1473 perror (output_file);
1474 exit (1);
1475 }
1476 current_output_file = output_file;
1477 }
1478 else
1479 {
1480 int dir_len = strlen (output_directory);
1481 int i, classname_length = strlen (classname);
1482 current_output_file = (char*) ALLOC (dir_len + classname_length + 4);
1483 strcpy (current_output_file, output_directory);
1484 if (dir_len > 0 && output_directory[dir_len-1] != '/')
1485 current_output_file[dir_len++] = '/';
1486 for (i = 0; classname[i] != '\0'; i++)
1487 {
1488 char ch = classname[i];
1489 if (ch == '.')
1490 ch = '/';
1491 current_output_file[dir_len++] = ch;
1492 }
1493 if (emit_dependencies)
1494 {
1495 if (suppress_output)
1496 {
1497 jcf_dependency_set_dep_file ("-");
1498 out = NULL;
1499 }
1500 else
1501 {
1502 /* We use `.hd' and not `.d' to avoid clashes with
1503 dependency tracking from straight compilation. */
1504 strcpy (current_output_file + dir_len, ".hd");
1505 jcf_dependency_set_dep_file (current_output_file);
1506 }
1507 }
1508 strcpy (current_output_file + dir_len, ".h");
1509 jcf_dependency_set_target (current_output_file);
1510 if (! suppress_output)
1511 {
1512 out = fopen (current_output_file, "w");
1513 if (out == NULL)
1514 {
1515 perror (current_output_file);
1516 exit (1);
1517 }
1518 }
1519 }
1520 process_file (&jcf, out);
1521 JCF_FINISH (&jcf);
1522 if (current_output_file != output_file)
1523 free (current_output_file);
1524 jcf_dependency_write ();
1525 }
1526
1527 if (out != NULL && out != stdout)
1528 fclose (out);
1529
1530 return found_error;
1531 }
1532
1533 /* TODO:
1534
1535 * Do whatever the javah -stubs flag does.
1536
1537 * Emit "structure forward declarations" when needed.
1538
1539 * Generate C headers, like javah
1540
1541 */
This page took 0.111708 seconds and 5 git commands to generate.