| /* Parser for Java(TM) .class files. |
| Copyright (C) 1996, 1998, 1999 Free Software Foundation, Inc. |
| |
| This file is part of GNU CC. |
| |
| GNU CC is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU CC is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU CC; see the file COPYING. If not, write to |
| the Free Software Foundation, 59 Temple Place - Suite 330, |
| Boston, MA 02111-1307, USA. |
| |
| Java and all Java-based marks are trademarks or registered trademarks |
| of Sun Microsystems, Inc. in the United States and other countries. |
| The Free Software Foundation is independent of Sun Microsystems, Inc. */ |
| |
| /* Written by Per Bothner <bothner@cygnus.com> */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "tree.h" |
| #include "obstack.h" |
| #include "flags.h" |
| #include "java-except.h" |
| #include "input.h" |
| #include "java-tree.h" |
| #include "toplev.h" |
| #include "parse.h" |
| |
| /* A CONSTANT_Utf8 element is converted to an IDENTIFIER_NODE at parse time. */ |
| #define JPOOL_UTF(JCF, INDEX) CPOOL_UTF(&(JCF)->cpool, INDEX) |
| #define JPOOL_UTF_LENGTH(JCF, INDEX) IDENTIFIER_LENGTH (JPOOL_UTF (JCF, INDEX)) |
| #define JPOOL_UTF_DATA(JCF, INDEX) \ |
| ((unsigned char*) IDENTIFIER_POINTER (JPOOL_UTF (JCF, INDEX))) |
| #define HANDLE_CONSTANT_Utf8(JCF, INDEX, LENGTH) \ |
| do { \ |
| unsigned char save; unsigned char *text; \ |
| JCF_FILL (JCF, (LENGTH)+1); /* Make sure we read 1 byte beyond string. */ \ |
| text = (JCF)->read_ptr; \ |
| save = text[LENGTH]; \ |
| text[LENGTH] = 0; \ |
| (JCF)->cpool.data[INDEX] = (jword) get_identifier (text); \ |
| text[LENGTH] = save; \ |
| JCF_SKIP (JCF, LENGTH); } while (0) |
| |
| #include "jcf.h" |
| |
| extern struct obstack *saveable_obstack; |
| extern struct obstack temporary_obstack; |
| extern struct obstack permanent_obstack; |
| |
| /* This is true if the user specified a `.java' file on the command |
| line. Otherwise it is 0. FIXME: this is temporary, until our |
| .java parser is fully working. */ |
| int saw_java_source = 0; |
| |
| /* The class we are currently processing. */ |
| tree current_class = NULL_TREE; |
| |
| /* The class we started with. */ |
| tree main_class = NULL_TREE; |
| |
| /* List of all class DECL seen so far. */ |
| tree all_class_list = NULL_TREE; |
| |
| /* The FIELD_DECL for the current field. */ |
| static tree current_field = NULL_TREE; |
| |
| /* The METHOD_DECL for the current method. */ |
| static tree current_method = NULL_TREE; |
| |
| /* The Java .class file that provides main_class; the main input file. */ |
| static struct JCF main_jcf[1]; |
| |
| /* Declarations of some functions used here. */ |
| static tree give_name_to_class PROTO ((JCF *jcf, int index)); |
| static void parse_zip_file_entries PROTO ((void)); |
| static void process_zip_dir PROTO ((void)); |
| static void parse_source_file PROTO ((tree)); |
| static void jcf_parse_source PROTO ((void)); |
| static int jcf_figure_file_type PROTO ((JCF *)); |
| static int find_in_current_zip PROTO ((char *, struct JCF **)); |
| static void parse_class_file PROTO ((void)); |
| |
| /* Handle "SourceFile" attribute. */ |
| |
| void |
| set_source_filename (jcf, index) |
| JCF *jcf; |
| int index; |
| { |
| tree sfname_id = get_name_constant (jcf, index); |
| char *sfname = IDENTIFIER_POINTER (sfname_id); |
| if (input_filename != NULL) |
| { |
| int old_len = strlen (input_filename); |
| int new_len = IDENTIFIER_LENGTH (sfname_id); |
| /* Use the current input_filename (derived from the class name) |
| if it has a directory prefix, but otherwise matches sfname. */ |
| if (old_len > new_len |
| && strcmp (sfname, input_filename + old_len - new_len) == 0 |
| && (input_filename[old_len - new_len - 1] == '/' |
| || input_filename[old_len - new_len - 1] == '\\')) |
| return; |
| } |
| input_filename = sfname; |
| DECL_SOURCE_FILE (TYPE_NAME (current_class)) = sfname; |
| if (current_class == main_class) main_input_filename = input_filename; |
| } |
| |
| #define HANDLE_SOURCEFILE(INDEX) set_source_filename (jcf, INDEX) |
| |
| #define HANDLE_CLASS_INFO(ACCESS_FLAGS, THIS, SUPER, INTERFACES_COUNT) \ |
| { tree super_class = SUPER==0 ? NULL_TREE : get_class_constant (jcf, SUPER); \ |
| current_class = give_name_to_class (jcf, THIS); \ |
| set_super_info (ACCESS_FLAGS, current_class, super_class, INTERFACES_COUNT);} |
| |
| #define HANDLE_CLASS_INTERFACE(INDEX) \ |
| add_interface (current_class, get_class_constant (jcf, INDEX)) |
| |
| #define HANDLE_START_FIELD(ACCESS_FLAGS, NAME, SIGNATURE, ATTRIBUTE_COUNT) \ |
| { int sig_index = SIGNATURE; \ |
| current_field = add_field (current_class, get_name_constant (jcf, NAME), \ |
| parse_signature (jcf, sig_index), ACCESS_FLAGS); \ |
| set_java_signature (TREE_TYPE (current_field), JPOOL_UTF (jcf, sig_index)); } |
| |
| #define HANDLE_END_FIELDS() \ |
| (current_field = NULL_TREE) |
| |
| #define HANDLE_CONSTANTVALUE(INDEX) \ |
| { tree constant; int index = INDEX; \ |
| if (! flag_emit_class_files && JPOOL_TAG (jcf, index) == CONSTANT_String) { \ |
| tree name = get_name_constant (jcf, JPOOL_USHORT1 (jcf, index)); \ |
| constant = build_utf8_ref (name); \ |
| } \ |
| else \ |
| constant = get_constant (jcf, index); \ |
| set_constant_value (current_field, constant); } |
| |
| #define HANDLE_METHOD(ACCESS_FLAGS, NAME, SIGNATURE, ATTRIBUTE_COUNT) \ |
| (current_method = add_method (current_class, ACCESS_FLAGS, \ |
| get_name_constant (jcf, NAME), \ |
| get_name_constant (jcf, SIGNATURE)), \ |
| DECL_LOCALVARIABLES_OFFSET (current_method) = 0, \ |
| DECL_LINENUMBERS_OFFSET (current_method) = 0) |
| |
| #define HANDLE_END_METHODS() \ |
| { tree handle_type = CLASS_TO_HANDLE_TYPE (current_class); \ |
| if (handle_type != current_class) layout_type (handle_type); } |
| |
| #define HANDLE_CODE_ATTRIBUTE(MAX_STACK, MAX_LOCALS, CODE_LENGTH) \ |
| { DECL_MAX_STACK (current_method) = (MAX_STACK); \ |
| DECL_MAX_LOCALS (current_method) = (MAX_LOCALS); \ |
| DECL_CODE_LENGTH (current_method) = (CODE_LENGTH); \ |
| DECL_CODE_OFFSET (current_method) = JCF_TELL (jcf); } |
| |
| #define HANDLE_LOCALVARIABLETABLE_ATTRIBUTE(COUNT) \ |
| { int n = (COUNT); \ |
| DECL_LOCALVARIABLES_OFFSET (current_method) = JCF_TELL (jcf) - 2; \ |
| JCF_SKIP (jcf, n * 10); } |
| |
| #define HANDLE_LINENUMBERTABLE_ATTRIBUTE(COUNT) \ |
| { int n = (COUNT); \ |
| DECL_LINENUMBERS_OFFSET (current_method) = JCF_TELL (jcf) - 2; \ |
| JCF_SKIP (jcf, n * 4); } |
| |
| #define HANDLE_EXCEPTIONS_ATTRIBUTE(COUNT) \ |
| { \ |
| int n = COUNT; \ |
| tree list = DECL_FUNCTION_THROWS (current_method); \ |
| while (--n >= 0) \ |
| { \ |
| tree thrown_class = get_class_constant (jcf, JCF_readu2 (jcf)); \ |
| list = tree_cons (NULL_TREE, thrown_class, list); \ |
| } \ |
| DECL_FUNCTION_THROWS (current_method) = nreverse (list); \ |
| } |
| |
| #include "jcf-reader.c" |
| |
| static int yydebug; |
| |
| tree |
| parse_signature (jcf, sig_index) |
| JCF *jcf; |
| int sig_index; |
| { |
| if (sig_index <= 0 || sig_index >= JPOOL_SIZE(jcf) |
| || JPOOL_TAG (jcf, sig_index) != CONSTANT_Utf8) |
| fatal ("invalid field/method signature"); |
| else |
| { |
| return parse_signature_string (JPOOL_UTF_DATA (jcf, sig_index), |
| JPOOL_UTF_LENGTH (jcf, sig_index)); |
| } |
| } |
| |
| void |
| init_lex () |
| { |
| /* Make identifier nodes long enough for the language-specific slots. */ |
| set_identifier_size (sizeof (struct lang_identifier)); |
| } |
| |
| void |
| set_yydebug (value) |
| int value; |
| { |
| yydebug = value; |
| } |
| |
| tree |
| get_constant (jcf, index) |
| JCF *jcf; |
| int index; |
| { |
| tree value; |
| int tag; |
| if (index <= 0 || index >= JPOOL_SIZE(jcf)) |
| goto bad; |
| tag = JPOOL_TAG (jcf, index); |
| if ((tag & CONSTANT_ResolvedFlag) || tag == CONSTANT_Utf8) |
| return (tree) jcf->cpool.data[index]; |
| push_obstacks (&permanent_obstack, &permanent_obstack); |
| switch (tag) |
| { |
| case CONSTANT_Integer: |
| { |
| jint num = JPOOL_INT(jcf, index); |
| value = build_int_2 (num, num < 0 ? -1 : 0); |
| TREE_TYPE (value) = int_type_node; |
| break; |
| } |
| case CONSTANT_Long: |
| { |
| jint num = JPOOL_INT (jcf, index); |
| HOST_WIDE_INT lo, hi; |
| lshift_double (num, 0, 32, 64, &lo, &hi, 0); |
| num = JPOOL_INT (jcf, index+1); |
| add_double (lo, hi, num, 0, &lo, &hi); |
| value = build_int_2 (lo, hi); |
| TREE_TYPE (value) = long_type_node; |
| force_fit_type (value, 0); |
| break; |
| } |
| #if TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT |
| case CONSTANT_Float: |
| { |
| jint num = JPOOL_INT(jcf, index); |
| REAL_VALUE_TYPE d; |
| #ifdef REAL_ARITHMETIC |
| d = REAL_VALUE_FROM_TARGET_SINGLE (num); |
| #else |
| union { float f; jint i; } u; |
| u.i = num; |
| d = u.f; |
| #endif |
| value = build_real (float_type_node, d); |
| break; |
| } |
| case CONSTANT_Double: |
| { |
| HOST_WIDE_INT num[2]; |
| REAL_VALUE_TYPE d; |
| HOST_WIDE_INT lo, hi; |
| num[0] = JPOOL_INT (jcf, index); |
| lshift_double (num[0], 0, 32, 64, &lo, &hi, 0); |
| num[0] = JPOOL_INT (jcf, index+1); |
| add_double (lo, hi, num[0], 0, &lo, &hi); |
| if (FLOAT_WORDS_BIG_ENDIAN) |
| { |
| num[0] = hi; |
| num[1] = lo; |
| } |
| else |
| { |
| num[0] = lo; |
| num[1] = hi; |
| } |
| #ifdef REAL_ARITHMETIC |
| d = REAL_VALUE_FROM_TARGET_DOUBLE (num); |
| #else |
| { |
| union { double d; jint i[2]; } u; |
| u.i[0] = (jint) num[0]; |
| u.i[1] = (jint) num[1]; |
| d = u.d; |
| } |
| #endif |
| value = build_real (double_type_node, d); |
| break; |
| } |
| #endif /* TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT */ |
| case CONSTANT_String: |
| { |
| extern struct obstack *expression_obstack; |
| tree name = get_name_constant (jcf, JPOOL_USHORT1 (jcf, index)); |
| char *utf8_ptr = IDENTIFIER_POINTER (name); |
| unsigned char *str_ptr; |
| int utf8_len = IDENTIFIER_LENGTH (name); |
| unsigned char *str = (unsigned char*)utf8_ptr; |
| int i = utf8_len; |
| int str_len; |
| |
| /* Count the number of Unicode characters in the string, |
| while checking for a malformed Utf8 string. */ |
| for (str_len = 0; i > 0; str_len++) |
| { |
| int char_len = UT8_CHAR_LENGTH (*str); |
| if (char_len < 0 || char_len > 2 || char_len > i) |
| fatal ("bad string constant"); |
| str += char_len; |
| i -= char_len; |
| } |
| |
| value = make_node (STRING_CST); |
| TREE_TYPE (value) = build_pointer_type (string_type_node); |
| TREE_STRING_LENGTH (value) = 2 * str_len; |
| TREE_STRING_POINTER (value) |
| = obstack_alloc (expression_obstack, 2 * str_len); |
| str_ptr = (unsigned char *) TREE_STRING_POINTER (value); |
| str = (unsigned char*)utf8_ptr; |
| for (i = 0; i < str_len; i++) |
| { |
| int char_value; |
| int char_len = UT8_CHAR_LENGTH (*str); |
| switch (char_len) |
| { |
| case 1: |
| char_value = *str++; |
| break; |
| case 2: |
| char_value = *str++ & 0x1F; |
| char_value = (char_value << 6) | (*str++ & 0x3F); |
| break; |
| case 3: |
| char_value = *str_ptr++ & 0x0F; |
| char_value = (char_value << 6) | (*str++ & 0x3F); |
| char_value = (char_value << 6) | (*str++ & 0x3F); |
| break; |
| default: |
| goto bad; |
| } |
| if (BYTES_BIG_ENDIAN) |
| { |
| *str_ptr++ = char_value >> 8; |
| *str_ptr++ = char_value & 0xFF; |
| } |
| else |
| { |
| *str_ptr++ = char_value & 0xFF; |
| *str_ptr++ = char_value >> 8; |
| } |
| } |
| } |
| break; |
| default: |
| goto bad; |
| } |
| pop_obstacks (); |
| JPOOL_TAG(jcf, index) = tag | CONSTANT_ResolvedFlag; |
| jcf->cpool.data [index] = (jword) value; |
| return value; |
| bad: |
| fatal ("bad value constant type %d, index %d", |
| JPOOL_TAG( jcf, index ), index); |
| } |
| |
| tree |
| get_name_constant (jcf, index) |
| JCF *jcf; |
| int index; |
| { |
| tree name = get_constant (jcf, index); |
| if (TREE_CODE (name) != IDENTIFIER_NODE) |
| fatal ("bad nameandtype index %d", index); |
| return name; |
| } |
| |
| static tree |
| give_name_to_class (jcf, i) |
| JCF *jcf; |
| int i; |
| { |
| if (i <= 0 || i >= JPOOL_SIZE(jcf) |
| || JPOOL_TAG (jcf, i) != CONSTANT_Class) |
| fatal ("bad class index %d", i); |
| else |
| { |
| tree this_class; |
| int j = JPOOL_USHORT1 (jcf, i); |
| /* verify_constant_pool confirmed that j is a CONSTANT_Utf8. */ |
| tree class_name = unmangle_classname (JPOOL_UTF_DATA (jcf, j), |
| JPOOL_UTF_LENGTH (jcf, j)); |
| this_class = lookup_class (class_name); |
| input_filename = DECL_SOURCE_FILE (TYPE_NAME (this_class)); |
| lineno = 0; |
| if (main_input_filename == NULL && jcf == main_jcf) |
| main_input_filename = input_filename; |
| |
| jcf->cpool.data[i] = (jword) this_class; |
| JPOOL_TAG (jcf, i) = CONSTANT_ResolvedClass; |
| return this_class; |
| } |
| } |
| |
| /* Get the class of the CONSTANT_Class whose constant pool index is I. */ |
| |
| tree |
| get_class_constant (JCF *jcf , int i) |
| { |
| tree type; |
| if (i <= 0 || i >= JPOOL_SIZE(jcf) |
| || (JPOOL_TAG (jcf, i) & ~CONSTANT_ResolvedFlag) != CONSTANT_Class) |
| fatal ("bad class index %d", i); |
| |
| if (JPOOL_TAG (jcf, i) != CONSTANT_ResolvedClass) |
| { |
| int name_index = JPOOL_USHORT1 (jcf, i); |
| /* verify_constant_pool confirmed that name_index is a CONSTANT_Utf8. */ |
| char *name = JPOOL_UTF_DATA (jcf, name_index); |
| int nlength = JPOOL_UTF_LENGTH (jcf, name_index); |
| if (name[0] == '[') /* Handle array "classes". */ |
| type = TREE_TYPE (parse_signature_string (name, nlength)); |
| else |
| { |
| tree cname = unmangle_classname (name, nlength); |
| type = lookup_class (cname); |
| } |
| jcf->cpool.data[i] = (jword) type; |
| JPOOL_TAG (jcf, i) = CONSTANT_ResolvedClass; |
| } |
| else |
| type = (tree) jcf->cpool.data[i]; |
| return type; |
| } |
| |
| void |
| DEFUN(jcf_out_of_synch, (jcf), |
| JCF *jcf) |
| { |
| char *source = strdup (jcf->filename); |
| int i = strlen (source); |
| |
| while (source[i] != '.') |
| i--; |
| |
| source [i] = '\0'; |
| warning ("Class file `%s' out of synch with `%s.java'", |
| jcf->filename, source); |
| free (source); |
| } |
| |
| /* Read a class with the fully qualified-name NAME. |
| Return 1 iff we read the requested file. |
| (It is still possible we failed if the file did not |
| define the class it is supposed to.) */ |
| |
| int |
| read_class (name) |
| tree name; |
| { |
| JCF this_jcf, *jcf; |
| tree save_current_class = current_class; |
| char *save_input_filename = input_filename; |
| JCF *save_current_jcf = current_jcf; |
| long saved_pos = 0; |
| if (current_jcf->read_state) |
| saved_pos = ftell (current_jcf->read_state); |
| |
| push_obstacks (&permanent_obstack, &permanent_obstack); |
| |
| /* Search in current zip first. */ |
| if (find_in_current_zip (IDENTIFIER_POINTER (name), &jcf) == 0) |
| /* FIXME: until the `.java' parser is fully working, we only |
| look for a .java file when one was mentioned on the |
| command line. This lets us test the .java parser fairly |
| easily, without compromising our ability to use the |
| .class parser without fear. */ |
| if (find_class (IDENTIFIER_POINTER (name), IDENTIFIER_LENGTH (name), |
| &this_jcf, saw_java_source) == 0) |
| { |
| pop_obstacks (); /* FIXME: one pop_obstack() per function */ |
| return 0; |
| } |
| else |
| { |
| this_jcf.seen_in_zip = 0; |
| current_jcf = &this_jcf; |
| if (this_jcf.outofsynch) |
| jcf_out_of_synch (current_jcf); |
| } |
| else |
| current_jcf = jcf; |
| |
| if (current_jcf->java_source) |
| jcf_parse_source (); |
| else { |
| java_parser_context_save_global (); |
| java_push_parser_context (); |
| input_filename = current_jcf->filename; |
| jcf_parse (current_jcf); |
| java_pop_parser_context (0); |
| java_parser_context_restore_global (); |
| } |
| |
| if (!current_jcf->seen_in_zip) |
| JCF_FINISH (current_jcf); |
| pop_obstacks (); |
| |
| current_class = save_current_class; |
| input_filename = save_input_filename; |
| current_jcf = save_current_jcf; |
| if (current_jcf->read_state) |
| fseek (current_jcf->read_state, saved_pos, SEEK_SET); |
| return 1; |
| } |
| |
| /* Load CLASS_OR_NAME. CLASS_OR_NAME can be a mere identifier if |
| called from the parser, otherwise it's a RECORD_TYPE node. If |
| VERBOSE is 1, print error message on failure to load a class. */ |
| |
| /* Replace calls to load_class by having callers call read_class directly |
| - and then perhaps rename read_class to load_class. FIXME */ |
| |
| void |
| load_class (class_or_name, verbose) |
| tree class_or_name; |
| int verbose; |
| { |
| tree name; |
| |
| /* class_or_name can be the name of the class we want to load */ |
| if (TREE_CODE (class_or_name) == IDENTIFIER_NODE) |
| name = class_or_name; |
| /* In some cases, it's a dependency that we process earlier that |
| we though */ |
| else if (TREE_CODE (class_or_name) == TREE_LIST) |
| name = TYPE_NAME (TREE_PURPOSE (class_or_name)); |
| /* Or it's a type in the making */ |
| else |
| name = DECL_NAME (TYPE_NAME (class_or_name)); |
| |
| if (read_class (name) == 0 && verbose) |
| { |
| error ("Cannot find file for class %s.", |
| IDENTIFIER_POINTER (name)); |
| if (TREE_CODE (class_or_name) == RECORD_TYPE) |
| TYPE_SIZE (class_or_name) = error_mark_node; |
| #if 0 |
| /* FIXME: what to do here? */ |
| if (!strcmp (classpath, DEFAULT_CLASS_PATH)) |
| fatal ("giving up"); |
| #endif |
| return; |
| } |
| } |
| |
| /* Parse a source file when JCF refers to a source file. */ |
| |
| static void |
| jcf_parse_source () |
| { |
| tree file; |
| |
| java_parser_context_save_global (); |
| java_push_parser_context (); |
| input_filename = current_jcf->filename; |
| file = get_identifier (input_filename); |
| if (!HAS_BEEN_ALREADY_PARSED_P (file)) |
| { |
| if (!(finput = fopen (input_filename, "r"))) |
| fatal ("input file `%s' just disappeared - jcf_parse_source", |
| input_filename); |
| parse_source_file (file); |
| if (fclose (finput)) |
| fatal ("can't close input file `%s' stream - jcf_parse_source", |
| input_filename); |
| } |
| java_pop_parser_context (IS_A_COMMAND_LINE_FILENAME_P (file)); |
| java_parser_context_restore_global (); |
| } |
| |
| /* Parse the .class file JCF. */ |
| |
| void |
| jcf_parse (jcf) |
| JCF* jcf; |
| { |
| int i, code; |
| |
| if (jcf_parse_preamble (jcf) != 0) |
| fatal ("Not a valid Java .class file.\n"); |
| code = jcf_parse_constant_pool (jcf); |
| if (code != 0) |
| fatal ("error while parsing constant pool"); |
| code = verify_constant_pool (jcf); |
| if (code > 0) |
| fatal ("error in constant pool entry #%d\n", code); |
| |
| jcf_parse_class (jcf); |
| if (main_class == NULL_TREE) |
| main_class = current_class; |
| if (! quiet_flag && TYPE_NAME (current_class)) |
| fprintf (stderr, " class %s", |
| IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (current_class)))); |
| if (CLASS_LOADED_P (current_class)) |
| return; |
| CLASS_LOADED_P (current_class) = 1; |
| |
| for (i = 1; i < JPOOL_SIZE(jcf); i++) |
| { |
| switch (JPOOL_TAG (jcf, i)) |
| { |
| case CONSTANT_Class: |
| get_class_constant (jcf, i); |
| break; |
| } |
| } |
| |
| code = jcf_parse_fields (jcf); |
| if (code != 0) |
| fatal ("error while parsing fields"); |
| code = jcf_parse_methods (jcf); |
| if (code != 0) |
| fatal ("error while parsing methods"); |
| code = jcf_parse_final_attributes (jcf); |
| if (code != 0) |
| fatal ("error while parsing final attributes"); |
| |
| /* The fields of class_type_node are already in correct order. */ |
| if (current_class != class_type_node && current_class != object_type_node) |
| TYPE_FIELDS (current_class) = nreverse (TYPE_FIELDS (current_class)); |
| |
| push_obstacks (&permanent_obstack, &permanent_obstack); |
| layout_class (current_class); |
| if (current_class == object_type_node) |
| layout_class_methods (object_type_node); |
| else |
| all_class_list = tree_cons (NULL_TREE, |
| TYPE_NAME (current_class), all_class_list ); |
| pop_obstacks (); |
| } |
| |
| void |
| init_outgoing_cpool () |
| { |
| current_constant_pool_data_ref = NULL_TREE; |
| if (outgoing_cpool == NULL) |
| { |
| static CPool outgoing_cpool_buffer; |
| outgoing_cpool = &outgoing_cpool_buffer; |
| CPOOL_INIT(outgoing_cpool); |
| } |
| else |
| { |
| CPOOL_REINIT(outgoing_cpool); |
| } |
| } |
| |
| static void |
| parse_class_file () |
| { |
| tree method; |
| char *save_input_filename = input_filename; |
| int save_lineno = lineno; |
| |
| java_layout_seen_class_methods (); |
| |
| input_filename = DECL_SOURCE_FILE (TYPE_NAME (current_class)); |
| lineno = 0; |
| debug_start_source_file (input_filename); |
| init_outgoing_cpool (); |
| |
| for ( method = TYPE_METHODS (CLASS_TO_HANDLE_TYPE (current_class)); |
| method != NULL_TREE; method = TREE_CHAIN (method)) |
| { |
| JCF *jcf = current_jcf; |
| |
| if (METHOD_NATIVE (method) || METHOD_ABSTRACT (method)) |
| continue; |
| |
| if (DECL_CODE_OFFSET (method) == 0) |
| { |
| error ("missing Code attribute"); |
| continue; |
| } |
| |
| lineno = 0; |
| if (DECL_LINENUMBERS_OFFSET (method)) |
| { |
| register int i; |
| register unsigned char *ptr; |
| JCF_SEEK (jcf, DECL_LINENUMBERS_OFFSET (method)); |
| linenumber_count = i = JCF_readu2 (jcf); |
| linenumber_table = ptr = jcf->read_ptr; |
| |
| for (ptr += 2; --i >= 0; ptr += 4) |
| { |
| int line = GET_u2 (ptr); |
| /* Set initial lineno lineno to smallest linenumber. |
| * Needs to be set before init_function_start. */ |
| if (lineno == 0 || line < lineno) |
| lineno = line; |
| } |
| } |
| else |
| { |
| linenumber_table = NULL; |
| linenumber_count = 0; |
| } |
| |
| start_java_method (method); |
| |
| give_name_to_locals (jcf); |
| |
| /* Actually generate code. */ |
| expand_byte_code (jcf, method); |
| |
| end_java_method (); |
| } |
| |
| if (flag_emit_class_files) |
| write_classfile (current_class); |
| |
| finish_class (current_class); |
| |
| debug_end_source_file (save_lineno); |
| input_filename = save_input_filename; |
| lineno = save_lineno; |
| } |
| |
| /* Parse a source file, as pointed by the current value of INPUT_FILENAME. */ |
| |
| static void |
| parse_source_file (file) |
| tree file; |
| { |
| int save_error_count = java_error_count; |
| /* Mark the file as parsed */ |
| HAS_BEEN_ALREADY_PARSED_P (file) = 1; |
| |
| lang_init_source (1); /* Error msgs have no method prototypes */ |
| |
| java_init_lex (); /* Initialize the parser */ |
| java_parse_abort_on_error (); |
| |
| java_parse (); /* Parse and build partial tree nodes. */ |
| java_parse_abort_on_error (); |
| java_complete_class (); /* Parse unsatisfied class decl. */ |
| java_parse_abort_on_error (); |
| java_check_circular_reference (); /* Check on circular references */ |
| java_parse_abort_on_error (); |
| } |
| |
| int |
| yyparse () |
| { |
| int several_files = 0; |
| char *list = strdup (input_filename), *next; |
| tree node, current_file_list = NULL_TREE; |
| |
| do |
| { |
| next = strchr (list, '&'); |
| if (next) |
| { |
| *next++ = '\0'; |
| several_files = 1; |
| } |
| |
| if (list[0]) |
| { |
| char *value; |
| |
| int len = strlen (list); |
| /* FIXME: this test is only needed until our .java parser is |
| fully capable. */ |
| if (len > 5 && ! strcmp (&list[len - 5], ".java")) |
| saw_java_source = 1; |
| |
| if (*list != '/' && several_files) |
| obstack_grow (&temporary_obstack, "./", 2); |
| |
| obstack_grow0 (&temporary_obstack, list, len); |
| value = obstack_finish (&temporary_obstack); |
| node = get_identifier (value); |
| IS_A_COMMAND_LINE_FILENAME_P (node) = 1; |
| current_file_list = tree_cons (NULL_TREE, node, current_file_list); |
| } |
| list = next; |
| } |
| while (next); |
| |
| current_jcf = main_jcf; |
| current_file_list = nreverse (current_file_list); |
| for (node = current_file_list; node; node = TREE_CHAIN (node)) |
| { |
| tree name = TREE_VALUE (node); |
| |
| /* Skip already parsed files */ |
| if (HAS_BEEN_ALREADY_PARSED_P (name)) |
| continue; |
| |
| /* Close previous descriptor, if any */ |
| if (main_jcf->read_state && fclose (main_jcf->read_state)) |
| fatal ("failed to close input file `%s' - yyparse", |
| (main_jcf->filename ? main_jcf->filename : "<unknown>")); |
| |
| /* Set jcf up and open a new file */ |
| JCF_ZERO (main_jcf); |
| main_jcf->read_state = fopen (IDENTIFIER_POINTER (name), "rb"); |
| if (main_jcf->read_state == NULL) |
| pfatal_with_name (IDENTIFIER_POINTER (name)); |
| |
| /* Set new input_filename and finput */ |
| finput = main_jcf->read_state; |
| #ifdef IO_BUFFER_SIZE |
| setvbuf (finput, (char *) xmalloc (IO_BUFFER_SIZE), |
| _IOFBF, IO_BUFFER_SIZE); |
| #endif |
| input_filename = IDENTIFIER_POINTER (name); |
| main_jcf->filbuf = jcf_filbuf_from_stdio; |
| |
| switch (jcf_figure_file_type (current_jcf)) |
| { |
| case JCF_ZIP: |
| parse_zip_file_entries (); |
| break; |
| case JCF_CLASS: |
| jcf_parse (current_jcf); |
| parse_class_file (); |
| break; |
| case JCF_SOURCE: |
| java_push_parser_context (); |
| java_parser_context_save_global (); |
| parse_source_file (name); |
| java_parser_context_restore_global (); |
| java_pop_parser_context (1); |
| break; |
| } |
| } |
| |
| java_expand_classes (); |
| if (!java_report_errors () && !flag_syntax_only) |
| emit_register_classes (); |
| return 0; |
| } |
| |
| static struct ZipFileCache *localToFile; |
| |
| /* Process all class entries found in the zip file. */ |
| static void |
| parse_zip_file_entries (void) |
| { |
| struct ZipDirectory *zdir; |
| int i; |
| |
| for (i = 0, zdir = (ZipDirectory *)localToFile->z.central_directory; |
| i < localToFile->z.count; i++, zdir = ZIPDIR_NEXT (zdir)) |
| { |
| tree class; |
| |
| /* We don't need to consider those files. */ |
| if (!zdir->size || !zdir->filename_offset) |
| continue; |
| |
| class = lookup_class (get_identifier (ZIPDIR_FILENAME (zdir))); |
| current_jcf = TYPE_LANG_SPECIFIC (class)->jcf; |
| current_class = class; |
| |
| if ( !CLASS_LOADED_P (class)) |
| { |
| fseek (current_jcf->read_state, current_jcf->zip_offset, SEEK_SET); |
| jcf_parse (current_jcf); |
| } |
| |
| if (TYPE_SIZE (current_class) != error_mark_node) |
| { |
| input_filename = current_jcf->filename; |
| parse_class_file (); |
| FREE (current_jcf->buffer); /* No longer necessary */ |
| /* Note: there is a way to free this buffer right after a |
| class seen in a zip file has been parsed. The idea is the |
| set its jcf in such a way that buffer will be reallocated |
| the time the code for the class will be generated. FIXME. */ |
| } |
| } |
| } |
| |
| /* Read all the entries of the zip file, creates a class and a JCF. Sets the |
| jcf up for further processing and link it to the created class. */ |
| |
| static void process_zip_dir() |
| { |
| int i; |
| ZipDirectory *zdir; |
| |
| for (i = 0, zdir = (ZipDirectory *)localToFile->z.central_directory; |
| i < localToFile->z.count; i++, zdir = ZIPDIR_NEXT (zdir)) |
| { |
| char *class_name, *file_name, *class_name_in_zip_dir; |
| tree class; |
| JCF *jcf; |
| int j; |
| |
| class_name_in_zip_dir = ZIPDIR_FILENAME (zdir); |
| |
| /* We choose to not to process entries with a zero size or entries |
| not bearing the .class extention. */ |
| if (!zdir->size || !zdir->filename_offset || |
| strncmp (&class_name_in_zip_dir[zdir->filename_length-6], |
| ".class", 6)) |
| { |
| /* So it will be skipped in parse_zip_file_entries */ |
| zdir->size = 0; |
| continue; |
| } |
| |
| class_name = ALLOC (zdir->filename_length+1-6); |
| file_name = ALLOC (zdir->filename_length+1); |
| jcf = ALLOC (sizeof (JCF)); |
| JCF_ZERO (jcf); |
| |
| strncpy (class_name, class_name_in_zip_dir, zdir->filename_length-6); |
| class_name [zdir->filename_length-6] = '\0'; |
| strncpy (file_name, class_name_in_zip_dir, zdir->filename_length); |
| file_name [zdir->filename_length] = '\0'; |
| |
| for (j=0; class_name[j]; j++) |
| class_name [j] = (class_name [j] == '/' ? '.' : class_name [j]); |
| |
| /* Yes, we write back the true class name into the zip directory. */ |
| strcpy (class_name_in_zip_dir, class_name); |
| zdir->filename_length = j; |
| class = lookup_class (get_identifier (class_name)); |
| |
| jcf->read_state = finput; |
| jcf->filbuf = jcf_filbuf_from_stdio; |
| jcf->seen_in_zip = 1; |
| jcf->java_source = 0; |
| jcf->zip_offset = zdir->filestart; |
| jcf->classname = class_name; |
| jcf->filename = file_name; |
| |
| TYPE_LANG_SPECIFIC (class) = |
| (struct lang_type *) perm_calloc (1, sizeof (struct lang_type)); |
| TYPE_LANG_SPECIFIC (class)->jcf = jcf; |
| } |
| } |
| |
| /* Lookup class NAME and figure whether is a class already found in the current |
| zip file. */ |
| static int |
| DEFUN(find_in_current_zip, (name, length, jcf), |
| char *name AND JCF **jcf) |
| { |
| JCF *local_jcf; |
| tree class_name = maybe_get_identifier (name), class, icv; |
| |
| if (!class_name) |
| return 0; |
| |
| if (!(icv = IDENTIFIER_CLASS_VALUE (class_name))) |
| return 0; |
| |
| class = TREE_TYPE (icv); |
| |
| /* Doesn't have jcf specific info ? It's not ours */ |
| if (!TYPE_LANG_SPECIFIC (class) || !TYPE_LANG_SPECIFIC (class)->jcf) |
| return 0; |
| |
| *jcf = local_jcf = TYPE_LANG_SPECIFIC (class)->jcf; |
| fseek (local_jcf->read_state, local_jcf->zip_offset, SEEK_SET); |
| return 1; |
| } |
| |
| /* Figure what kind of file we're dealing with */ |
| static int |
| DEFUN(jcf_figure_file_type, (jcf), |
| JCF *jcf) |
| { |
| unsigned char magic_string[4]; |
| uint32 magic; |
| |
| if (fread (magic_string, 1, 4, jcf->read_state) != 4) |
| jcf_unexpected_eof (jcf, 4); |
| |
| fseek (jcf->read_state, 0L, SEEK_SET); |
| magic = GET_u4 (magic_string); |
| |
| if (magic == 0xcafebabe) |
| return JCF_CLASS; |
| |
| /* FIXME: is it a system file? */ |
| if (magic == (JCF_u4)ZIPMAGIC |
| && !open_in_zip (jcf, input_filename, NULL, 0)) |
| { |
| localToFile = ALLOC (sizeof (struct ZipFileCache)); |
| bcopy ((PTR) SeenZipFiles, (PTR) localToFile, sizeof (struct ZipFileCache)); |
| process_zip_dir (); /* Register all the class defined there */ |
| return JCF_ZIP; |
| } |
| |
| return JCF_SOURCE; |
| } |
| |