| /* Functions related to building classes and their related objects. |
| Copyright (C) 1996, 97-98, 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 "rtl.h" |
| #include "flags.h" |
| #include "java-tree.h" |
| #include "jcf.h" |
| #include "obstack.h" |
| #include "toplev.h" |
| #include "output.h" |
| #include "parse.h" |
| |
| static tree mangle_class_field PROTO ((tree class)); |
| static tree make_method_value PROTO ((tree)); |
| static tree build_java_method_type PROTO ((tree, tree, int)); |
| static int32 hashUtf8String PROTO ((const char *, int)); |
| static tree make_field_value PROTO ((tree)); |
| static tree get_dispatch_vector PROTO ((tree)); |
| static tree get_dispatch_table PROTO ((tree, tree)); |
| static void append_gpp_mangled_type PROTO ((struct obstack *, tree)); |
| static tree mangle_static_field PROTO ((tree)); |
| |
| static rtx registerClass_libfunc; |
| |
| extern struct obstack permanent_obstack; |
| extern struct obstack temporary_obstack; |
| |
| /* Return an IDENTIFIER_NODE the same as (OLD_NAME, OLD_LENGTH). |
| except that characters matching OLD_CHAR are substituted by NEW_CHAR. |
| Also, PREFIX is prepended, and SUFFIX is appended. */ |
| |
| tree |
| ident_subst (old_name, old_length, prefix, old_char, new_char, suffix) |
| const char* old_name; |
| int old_length; |
| const char *prefix; |
| int old_char; |
| int new_char; |
| const char *suffix; |
| { |
| int prefix_len = strlen (prefix); |
| int suffix_len = strlen (suffix); |
| int i = prefix_len + old_length + suffix_len + 1; |
| #ifdef __GNUC__ |
| char buffer[i]; |
| #else |
| char *buffer = (char *)alloca (i); |
| #endif |
| strcpy (buffer, prefix); |
| for (i = 0; i < old_length; i++) |
| { |
| char ch = old_name[i]; |
| if (ch == old_char) |
| ch = new_char; |
| buffer[prefix_len + i] = ch; |
| } |
| strcpy (buffer + prefix_len + old_length, suffix); |
| return get_identifier (buffer); |
| } |
| |
| /* Return an IDENTIFIER_NODE the same as OLD_ID, |
| except that characters matching OLD_CHAR are substituted by NEW_CHAR. |
| Also, PREFIX is prepended, and SUFFIX is appended. */ |
| |
| tree |
| identifier_subst (old_id, prefix, old_char, new_char, suffix) |
| const tree old_id; |
| const char *prefix; |
| int old_char; |
| int new_char; |
| const char *suffix; |
| { |
| return ident_subst (IDENTIFIER_POINTER (old_id), IDENTIFIER_LENGTH (old_id), |
| prefix, old_char, new_char, suffix); |
| } |
| |
| /* Generate a valid C identifier from the name of the class TYPE, |
| prefixed by PREFIX. */ |
| |
| tree |
| mangled_classname (prefix, type) |
| const char *prefix; |
| tree type; |
| { |
| tree ident = TYPE_NAME (type); |
| if (TREE_CODE (ident) != IDENTIFIER_NODE) |
| ident = DECL_NAME (ident); |
| return identifier_subst (ident, prefix, '.', '_', ""); |
| } |
| |
| tree |
| make_class () |
| { |
| tree type; |
| push_obstacks (&permanent_obstack, &permanent_obstack); |
| type = make_node (RECORD_TYPE); |
| #ifdef JAVA_USE_HANDLES |
| tree field1 = build_decl (FIELD_DECL, get_identifier ("obj"), |
| build_pointer_type (type)); |
| tree field2 = build_decl (FIELD_DECL, get_identifier ("methods"), |
| methodtable_ptr_type); |
| tree handle_type = make_node (RECORD_TYPE); |
| TREE_CHAIN (field1) = field2; |
| TYPE_FIELDS (handle_type) = field1; |
| TYPE_BINFO (type) = make_tree_vec (7); |
| TYPE_BINFO (handle_type) = make_tree_vec (7); |
| BINFO_HANDLE (TYPE_BINFO (handle_type)) = type; |
| BINFO_HANDLE (TYPE_BINFO (type)) = handle_type; |
| #else |
| TYPE_BINFO (type) = make_tree_vec (6); |
| #endif |
| pop_obstacks (); |
| |
| return type; |
| } |
| |
| /* Given a fully-qualified classname in NAME (whose length is NAME_LENGTH), |
| and where each of the constituents is separated by '/', |
| return a corresponding IDENTIFIER_NODE, except using '.' as separator. */ |
| |
| tree |
| unmangle_classname (name, name_length) |
| const char *name; int name_length; |
| { |
| tree to_return = ident_subst (name, name_length, "", '/', '.', ""); |
| /* It's not sufficient to compare to_return and get_identifier |
| (name) to determine whether to_return is qualified. There are |
| cases in signature analysis where name will be stripped of a |
| trailing ';'. */ |
| name = IDENTIFIER_POINTER (to_return); |
| while (*name) |
| if (*name++ == '.') |
| { |
| QUALIFIED_P (to_return) = 1; |
| break; |
| } |
| |
| return to_return; |
| } |
| |
| tree |
| push_class (class_type, class_name) |
| tree class_type, class_name; |
| { |
| tree decl, signature; |
| char *save_input_filename = input_filename; |
| int save_lineno = lineno; |
| tree source_name = identifier_subst (class_name, "", '.', '/', ".java"); |
| push_obstacks (&permanent_obstack, &permanent_obstack); |
| CLASS_P (class_type) = 1; |
| input_filename = IDENTIFIER_POINTER (source_name); |
| lineno = 0; |
| decl = build_decl (TYPE_DECL, class_name, class_type); |
| input_filename = save_input_filename; |
| lineno = save_lineno; |
| signature = identifier_subst (class_name, "L", '.', '/', ";"); |
| IDENTIFIER_SIGNATURE_TYPE (signature) = build_pointer_type (class_type); |
| |
| /* Setting DECL_ARTIFICAL forces dbxout.c to specific the type is |
| both a typedef and in the struct name-space. We may want to re-visit |
| this later, but for now it reduces the changes needed for gdb. */ |
| DECL_ARTIFICIAL (decl) = 1; |
| |
| pushdecl_top_level (decl); |
| #ifdef JAVA_USE_HANDLES |
| { |
| tree handle_name = identifier_subst (class_name, |
| "Handle$", '.', '.', ""); |
| tree handle_decl = build_decl (TYPE_DECL, handle_name, |
| CLASS_TO_HANDLE_TYPE (class_type)); |
| pushdecl (handle_decl); |
| } |
| #endif |
| |
| pop_obstacks (); |
| return decl; |
| } |
| |
| /* Finds the (global) class named NAME. Creates the class if not found. |
| Also creates associated TYPE_DECL. |
| Does not check if the class actually exists, load the class, |
| fill in field or methods, or do layout_type. */ |
| |
| tree |
| lookup_class (name) |
| tree name; |
| { |
| tree decl = IDENTIFIER_CLASS_VALUE (name); |
| if (decl == NULL_TREE) |
| decl = push_class (make_class (), name); |
| return TREE_TYPE (decl); |
| } |
| |
| void |
| set_super_info (access_flags, this_class, super_class, interfaces_count) |
| int access_flags; |
| tree this_class; |
| tree super_class; |
| int interfaces_count; |
| { |
| int total_supers = interfaces_count; |
| tree class_decl = TYPE_NAME (this_class); |
| if (super_class) |
| total_supers++; |
| |
| push_obstacks (&permanent_obstack, &permanent_obstack); |
| TYPE_BINFO_BASETYPES (this_class) = make_tree_vec (total_supers); |
| if (super_class) |
| { |
| tree super_binfo = make_tree_vec (6); |
| BINFO_TYPE (super_binfo) = super_class; |
| BINFO_OFFSET (super_binfo) = integer_zero_node; |
| TREE_VIA_PUBLIC (super_binfo) = 1; |
| TREE_VEC_ELT (BINFO_BASETYPES (TYPE_BINFO (this_class)), 0) |
| = super_binfo; |
| CLASS_HAS_SUPER (this_class) = 1; |
| } |
| pop_obstacks (); |
| |
| if (access_flags & ACC_PUBLIC) CLASS_PUBLIC (class_decl) = 1; |
| if (access_flags & ACC_FINAL) CLASS_FINAL (class_decl) = 1; |
| if (access_flags & ACC_SUPER) CLASS_SUPER (class_decl) = 1; |
| if (access_flags & ACC_INTERFACE) CLASS_INTERFACE (class_decl) = 1; |
| if (access_flags & ACC_ABSTRACT) CLASS_ABSTRACT (class_decl) = 1; |
| } |
| |
| /* Return length of inheritance chain of CLAS, where java.lang.Object is 0, |
| direct sub-classes of Object are 1, and so on. */ |
| |
| int |
| class_depth (clas) |
| tree clas; |
| { |
| int depth = 0; |
| if (! CLASS_LOADED_P (clas)) |
| load_class (clas, 1); |
| while (clas != object_type_node) |
| { |
| depth++; |
| clas = TYPE_BINFO_BASETYPE (clas, 0); |
| } |
| return depth; |
| } |
| |
| /* Return true iff TYPE2 is an interface that extends interface TYPE1 */ |
| |
| int |
| interface_of_p (type1, type2) |
| tree type1, type2; |
| { |
| int n, i; |
| tree basetype_vec; |
| |
| if (!(basetype_vec = TYPE_BINFO_BASETYPES (type2))) |
| return 0; |
| n = TREE_VEC_LENGTH (basetype_vec); |
| for (i = 0; i < n; i++) |
| { |
| tree vec_elt = TREE_VEC_ELT (basetype_vec, i); |
| if (vec_elt && BINFO_TYPE (vec_elt) == type1) |
| return 1; |
| } |
| for (i = 0; i < n; i++) |
| { |
| tree vec_elt = TREE_VEC_ELT (basetype_vec, i); |
| if (vec_elt && BINFO_TYPE (vec_elt) |
| && interface_of_p (type1, BINFO_TYPE (vec_elt))) |
| return 1; |
| } |
| return 0; |
| } |
| |
| /* Return true iff TYPE1 inherits from TYPE2. */ |
| |
| int |
| inherits_from_p (type1, type2) |
| tree type1, type2; |
| { |
| while (type1 != NULL_TREE && TREE_CODE (type1) == RECORD_TYPE) |
| { |
| if (type1 == type2) |
| return 1; |
| type1 = CLASSTYPE_SUPER (type1); |
| } |
| return 0; |
| } |
| |
| static void |
| add_interface_do (basetype_vec, interface_class, i) |
| tree basetype_vec, interface_class; |
| int i; |
| { |
| tree interface_binfo = make_tree_vec (6); |
| BINFO_TYPE (interface_binfo) = interface_class; |
| BINFO_OFFSET (interface_binfo) = integer_zero_node; |
| TREE_VIA_VIRTUAL (interface_binfo) = 1; |
| TREE_VIA_PUBLIC (interface_binfo) = 1; |
| TREE_VEC_ELT (basetype_vec, i) = interface_binfo; |
| } |
| |
| /* Add INTERFACE_CLASS to THIS_CLASS iff INTERFACE_CLASS can't be |
| found in THIS_CLASS. Returns NULL_TREE upon success, INTERFACE_CLASS |
| if attempt is made to add it twice. */ |
| |
| tree |
| maybe_add_interface (this_class, interface_class) |
| tree this_class, interface_class; |
| { |
| tree basetype_vec = TYPE_BINFO_BASETYPES (this_class); |
| int i; |
| int n = TREE_VEC_LENGTH (basetype_vec); |
| for (i = 0; ; i++) |
| { |
| if (i >= n) |
| { |
| error ("internal error - too many interface type"); |
| return NULL_TREE; |
| } |
| else if (TREE_VEC_ELT (basetype_vec, i) == NULL_TREE) |
| break; |
| else if (BINFO_TYPE (TREE_VEC_ELT (basetype_vec, i)) == interface_class) |
| return interface_class; |
| } |
| add_interface_do (basetype_vec, interface_class, i); |
| return NULL_TREE; |
| } |
| |
| /* Add the INTERFACE_CLASS as one of the interfaces of THIS_CLASS. */ |
| |
| void |
| add_interface (this_class, interface_class) |
| tree this_class, interface_class; |
| { |
| tree basetype_vec = TYPE_BINFO_BASETYPES (this_class); |
| int i; |
| int n = TREE_VEC_LENGTH (basetype_vec); |
| for (i = 0; ; i++) |
| { |
| if (i >= n) |
| { |
| error ("internal error - too many interface type"); |
| return; |
| } |
| else if (TREE_VEC_ELT (basetype_vec, i) == NULL_TREE) |
| break; |
| } |
| add_interface_do (basetype_vec, interface_class, i); |
| } |
| |
| #if 0 |
| /* Return the address of a pointer to the first FUNCTION_DECL |
| in the list (*LIST) whose DECL_NAME is NAME. */ |
| |
| static tree * |
| find_named_method (list, name) |
| tree *list; |
| tree name; |
| { |
| while (*list && DECL_NAME (*list) != name) |
| list = &TREE_CHAIN (*list); |
| return list; |
| } |
| #endif |
| |
| static tree |
| build_java_method_type (fntype, this_class, access_flags) |
| tree fntype; |
| tree this_class; |
| int access_flags; |
| { |
| if (access_flags & ACC_STATIC) |
| return fntype; |
| return build_method_type (CLASS_TO_HANDLE_TYPE (this_class), fntype); |
| } |
| |
| tree |
| add_method_1 (handle_class, access_flags, name, function_type) |
| tree handle_class; |
| int access_flags; |
| tree name; |
| tree function_type; |
| { |
| tree method_type, fndecl; |
| push_obstacks (&permanent_obstack, &permanent_obstack); |
| |
| method_type = build_java_method_type (function_type, |
| handle_class, access_flags); |
| |
| fndecl = build_decl (FUNCTION_DECL, name, method_type); |
| DECL_CONTEXT (fndecl) = handle_class; |
| |
| DECL_LANG_SPECIFIC (fndecl) |
| = (struct lang_decl *) permalloc (sizeof (struct lang_decl)); |
| bzero ((PTR) DECL_LANG_SPECIFIC (fndecl), sizeof (struct lang_decl)); |
| |
| TREE_CHAIN (fndecl) = TYPE_METHODS (handle_class); |
| TYPE_METHODS (handle_class) = fndecl; |
| pop_obstacks (); |
| |
| if (access_flags & ACC_PUBLIC) METHOD_PUBLIC (fndecl) = 1; |
| if (access_flags & ACC_PROTECTED) METHOD_PROTECTED (fndecl) = 1; |
| if (access_flags & ACC_PRIVATE) METHOD_PRIVATE (fndecl) = 1; |
| if (access_flags & ACC_NATIVE) METHOD_NATIVE (fndecl) = 1; |
| if (access_flags & ACC_STATIC) METHOD_STATIC (fndecl) = 1; |
| if (access_flags & ACC_FINAL) METHOD_FINAL (fndecl) = 1; |
| if (access_flags & ACC_SYNCHRONIZED) METHOD_SYNCHRONIZED (fndecl) = 1; |
| if (access_flags & ACC_ABSTRACT) METHOD_ABSTRACT (fndecl) = 1; |
| if (access_flags & ACC_TRANSIENT) METHOD_TRANSIENT (fndecl) = 1; |
| return fndecl; |
| } |
| |
| /* Add a method to THIS_CLASS. |
| The method's name is NAME. |
| Its signature (mangled type) is METHOD_SIG (an IDENTIFIER_NODE). */ |
| |
| tree |
| add_method (this_class, access_flags, name, method_sig) |
| tree this_class; |
| int access_flags; |
| tree name; |
| tree method_sig; |
| { |
| tree handle_class = CLASS_TO_HANDLE_TYPE (this_class); |
| tree function_type, fndecl; |
| unsigned char *sig = (unsigned char*)IDENTIFIER_POINTER (method_sig); |
| push_obstacks (&permanent_obstack, &permanent_obstack); |
| if (sig[0] != '(') |
| fatal ("bad method signature"); |
| function_type = get_type_from_signature (method_sig); |
| fndecl = add_method_1 (handle_class, access_flags, name, function_type); |
| set_java_signature (TREE_TYPE (fndecl), method_sig); |
| pop_obstacks (); |
| return fndecl; |
| } |
| |
| tree |
| add_field (class, name, field_type, flags) |
| tree class; |
| tree name; |
| tree field_type; |
| int flags; |
| { |
| int is_static = (flags & ACC_STATIC) != 0; |
| tree field; |
| /* Push the obstack of field_type ? FIXME */ |
| push_obstacks (&permanent_obstack, &permanent_obstack); |
| field = build_decl (is_static ? VAR_DECL : FIELD_DECL, name, field_type); |
| pop_obstacks (); |
| TREE_CHAIN (field) = TYPE_FIELDS (class); |
| TYPE_FIELDS (class) = field; |
| DECL_CONTEXT (field) = class; |
| |
| if (flags & ACC_PUBLIC) FIELD_PUBLIC (field) = 1; |
| if (flags & ACC_PROTECTED) FIELD_PROTECTED (field) = 1; |
| if (flags & ACC_PRIVATE) FIELD_PRIVATE (field) = 1; |
| if (flags & ACC_FINAL) FIELD_FINAL (field) = 1; |
| if (flags & ACC_VOLATILE) FIELD_VOLATILE (field) = 1; |
| if (flags & ACC_TRANSIENT) FIELD_TRANSIENT (field) = 1; |
| if (is_static) |
| { |
| FIELD_STATIC (field) = 1; |
| /* Always make field externally visible. This is required so |
| that native methods can always access the field. */ |
| TREE_PUBLIC (field) = 1; |
| } |
| return field; |
| } |
| |
| /* Associate a constant value CONSTANT with VAR_DECL FIELD. */ |
| |
| void |
| set_constant_value (field, constant) |
| tree field, constant; |
| { |
| if (field == NULL_TREE) |
| warning ("misplaced ConstantValue attribute (not in any field)"); |
| else if (DECL_INITIAL (field) != NULL_TREE) |
| warning ("duplicate ConstanValue atribute for field '%s'", |
| IDENTIFIER_POINTER (DECL_NAME (field))); |
| else |
| DECL_INITIAL (field) = constant; |
| } |
| |
| /* Count the number of Unicode chars encoded in a given Ut8 string. */ |
| |
| #if 0 |
| int |
| strLengthUtf8 (str, len) |
| char *str; |
| int len; |
| { |
| register unsigned char* ptr = (unsigned char*) str; |
| register unsigned char *limit = ptr + len; |
| int str_length = 0; |
| for (; ptr < limit; str_length++) { |
| if (UTF8_GET (ptr, limit) < 0) |
| return -1; |
| } |
| return str_length; |
| } |
| #endif |
| |
| |
| /* Calculate a hash value for a string encoded in Utf8 format. |
| * This returns the same hash value as specified for java.lang.String.hashCode. |
| */ |
| |
| static int32 |
| hashUtf8String (str, len) |
| const char *str; |
| int len; |
| { |
| register const unsigned char* ptr = (const unsigned char*) str; |
| register const unsigned char *limit = ptr + len; |
| int32 hash = 0; |
| for (; ptr < limit;) |
| { |
| int ch = UTF8_GET (ptr, limit); |
| /* Updated specification from |
| http://www.javasoft.com/docs/books/jls/clarify.html. */ |
| hash = (31 * hash) + ch; |
| } |
| return hash; |
| } |
| |
| tree utf8_decl_list = NULL_TREE; |
| |
| tree |
| build_utf8_ref (name) |
| tree name; |
| { |
| char* name_ptr = IDENTIFIER_POINTER(name); |
| int name_len = IDENTIFIER_LENGTH(name); |
| char buf[60]; |
| char *buf_ptr; |
| tree ctype, field, str_type, cinit, string; |
| static int utf8_count = 0; |
| int name_hash; |
| tree ref = IDENTIFIER_UTF8_REF (name); |
| tree decl; |
| if (ref != NULL_TREE) |
| return ref; |
| |
| push_obstacks (&permanent_obstack, &permanent_obstack); |
| ctype = make_node (RECORD_TYPE); |
| str_type = build_prim_array_type (unsigned_byte_type_node, |
| name_len + 1); /* Allow for final '\0'. */ |
| PUSH_FIELD (ctype, field, "hash", unsigned_short_type_node); |
| PUSH_FIELD (ctype, field, "length", unsigned_short_type_node); |
| PUSH_FIELD (ctype, field, "data", str_type); |
| FINISH_RECORD (ctype); |
| START_RECORD_CONSTRUCTOR (cinit, ctype); |
| name_hash = hashUtf8String (name_ptr, name_len) & 0xFFFF; |
| PUSH_FIELD_VALUE (cinit, "hash", build_int_2 (name_hash, 0)); |
| PUSH_FIELD_VALUE (cinit, "length", build_int_2 (name_len, 0)); |
| string = build_string (name_len, name_ptr); |
| TREE_TYPE (string) = str_type; |
| PUSH_FIELD_VALUE (cinit, "data", string); |
| FINISH_RECORD_CONSTRUCTOR (cinit); |
| |
| /* Build a unique identifier based on buf. */ |
| sprintf(buf, "_Utf%d", ++utf8_count); |
| buf_ptr = &buf[strlen (buf)]; |
| if (name_len > 0 && name_ptr[0] >= '0' && name_ptr[0] <= '9') |
| *buf_ptr++ = '_'; |
| while (--name_len >= 0) |
| { |
| unsigned char c = *name_ptr++; |
| if (c & 0x80) |
| continue; |
| if (!ISALPHA(c) && !ISDIGIT(c)) |
| c = '_'; |
| *buf_ptr++ = c; |
| if (buf_ptr >= buf + 50) |
| break; |
| } |
| *buf_ptr = '\0'; |
| |
| decl = build_decl (VAR_DECL, get_identifier (buf), utf8const_type); |
| /* FIXME get some way to force this into .text, not .data. */ |
| TREE_STATIC (decl) = 1; |
| DECL_ARTIFICIAL (decl) = 1; |
| DECL_IGNORED_P (decl) = 1; |
| TREE_READONLY (decl) = 1; |
| DECL_INITIAL (decl) = cinit; |
| TREE_CHAIN (decl) = utf8_decl_list; |
| layout_decl (decl, 0); |
| pushdecl (decl); |
| rest_of_decl_compilation (decl, (char*) 0, global_bindings_p (), 0); |
| utf8_decl_list = decl; |
| make_decl_rtl (decl, (char*) 0, 1); |
| ref = build1 (ADDR_EXPR, utf8const_ptr_type, decl); |
| IDENTIFIER_UTF8_REF (name) = ref; |
| pop_obstacks (); |
| return ref; |
| } |
| |
| /* Build a reference to the class TYPE. |
| Also handles primitive types and array types. */ |
| |
| tree |
| build_class_ref (type) |
| tree type; |
| { |
| int is_compiled = is_compiled_class (type); |
| if (is_compiled) |
| { |
| tree ref, decl_name, decl; |
| if (TREE_CODE (type) == POINTER_TYPE) |
| type = TREE_TYPE (type); |
| if (TREE_CODE (type) == RECORD_TYPE) |
| { |
| if (TYPE_SIZE (type) == error_mark_node) |
| return null_pointer_node; |
| decl_name = identifier_subst (DECL_NAME (TYPE_NAME (type)), |
| "", '/', '/', ".class"); |
| decl = IDENTIFIER_GLOBAL_VALUE (decl_name); |
| if (decl == NULL_TREE) |
| { |
| push_obstacks (&permanent_obstack, &permanent_obstack); |
| decl = build_decl (VAR_DECL, decl_name, class_type_node); |
| DECL_SIZE (decl) = TYPE_SIZE (class_type_node); |
| TREE_STATIC (decl) = 1; |
| TREE_PUBLIC (decl) = 1; |
| DECL_IGNORED_P (decl) = 1; |
| DECL_ARTIFICIAL (decl) = 1; |
| DECL_ASSEMBLER_NAME (decl) = mangle_class_field (type); |
| make_decl_rtl (decl, NULL, 1); |
| pushdecl_top_level (decl); |
| if (is_compiled == 1) |
| DECL_EXTERNAL (decl) = 1; |
| pop_obstacks (); |
| } |
| } |
| else |
| { |
| char *name; |
| char buffer[25]; |
| if (flag_emit_class_files) |
| { |
| const char *prim_class_name; |
| tree prim_class; |
| if (type == char_type_node) |
| prim_class_name = "java.lang.Character"; |
| else if (type == boolean_type_node) |
| prim_class_name = "java.lang.Boolean"; |
| else if (type == byte_type_node) |
| prim_class_name = "java.lang.Byte"; |
| else if (type == short_type_node) |
| prim_class_name = "java.lang.Short"; |
| else if (type == int_type_node) |
| prim_class_name = "java.lang.Integer"; |
| else if (type == long_type_node) |
| prim_class_name = "java.lang.Long"; |
| else if (type == float_type_node) |
| prim_class_name = "java.lang.Float"; |
| else if (type == double_type_node) |
| prim_class_name = "java.lang.Double"; |
| else if (type == void_type_node) |
| prim_class_name = "java.lang.Void"; |
| else |
| fatal ("internal error - bad type to build_class_ref"); |
| prim_class = lookup_class (get_identifier (prim_class_name)); |
| return build (COMPONENT_REF, NULL_TREE, |
| prim_class, TYPE_identifier_node); |
| } |
| decl_name = TYPE_NAME (type); |
| if (TREE_CODE (decl_name) == TYPE_DECL) |
| decl_name = DECL_NAME (decl_name); |
| name = IDENTIFIER_POINTER (decl_name); |
| if (strncmp (name, "promoted_", 9) == 0) |
| name += 9; |
| sprintf (buffer, "_Jv_%sClass", name); |
| decl_name = get_identifier (buffer); |
| decl = IDENTIFIER_GLOBAL_VALUE (decl_name); |
| if (decl == NULL_TREE) |
| { |
| push_obstacks (&permanent_obstack, &permanent_obstack); |
| decl = build_decl (VAR_DECL, decl_name, class_type_node); |
| TREE_STATIC (decl) = 1; |
| TREE_PUBLIC (decl) = 1; |
| make_decl_rtl (decl, NULL, 1); |
| pushdecl_top_level (decl); |
| if (is_compiled == 1) |
| DECL_EXTERNAL (decl) = 1; |
| pop_obstacks (); |
| } |
| } |
| |
| ref = build1 (ADDR_EXPR, class_ptr_type, decl); |
| return ref; |
| } |
| else |
| { |
| int index; |
| tree cl; |
| push_obstacks (&permanent_obstack, &permanent_obstack); |
| index = alloc_class_constant (type); |
| cl = build_ref_from_constant_pool (index); |
| TREE_TYPE (cl) = promote_type (class_ptr_type); |
| pop_obstacks (); |
| return cl; |
| } |
| } |
| |
| tree |
| build_static_field_ref (fdecl) |
| tree fdecl; |
| { |
| tree fclass = DECL_CONTEXT (fdecl); |
| int is_compiled = is_compiled_class (fclass); |
| if (is_compiled) |
| { |
| if (DECL_RTL (fdecl) == 0) |
| { |
| push_obstacks (&permanent_obstack, &permanent_obstack); |
| make_decl_rtl (fdecl, NULL, 1); |
| pop_obstacks (); |
| if (is_compiled == 1) |
| DECL_EXTERNAL (fdecl) = 1; |
| } |
| return fdecl; |
| } |
| else |
| { |
| /* Compile as: |
| * *(FTYPE*)build_class_ref(FCLASS)->fields[INDEX].info.addr |
| */ |
| static tree fields_ident = NULL_TREE; |
| static tree info_ident = NULL_TREE; |
| tree ref = build_class_ref (fclass); |
| tree fld; |
| int field_index = 0; |
| ref = build1 (INDIRECT_REF, class_type_node, ref); |
| if (fields_ident == NULL_TREE) |
| fields_ident = get_identifier ("fields"); |
| if (info_ident == NULL_TREE) |
| info_ident = get_identifier ("info"); |
| ref = build (COMPONENT_REF, field_ptr_type_node, ref, |
| lookup_field (&class_type_node, fields_ident)); |
| |
| for (fld = TYPE_FIELDS (fclass); ; fld = TREE_CHAIN (fld)) |
| { |
| if (fld == fdecl) |
| break; |
| if (fld == NULL_TREE) |
| fatal ("field '%s' not found in class", |
| IDENTIFIER_POINTER (DECL_NAME (fdecl))); |
| if (FIELD_STATIC (fld)) |
| field_index++; |
| } |
| field_index *= int_size_in_bytes (field_type_node); |
| ref = fold (build (PLUS_EXPR, field_ptr_type_node, |
| ref, build_int_2 (field_index, 0))); |
| ref = build1 (INDIRECT_REF, field_type_node, ref); |
| ref = build (COMPONENT_REF, field_info_union_node, |
| ref, lookup_field (&field_type_node, info_ident)); |
| ref = build (COMPONENT_REF, ptr_type_node, |
| ref, TREE_CHAIN (TYPE_FIELDS (field_info_union_node))); |
| return fold (build1 (INDIRECT_REF, TREE_TYPE(fdecl), ref)); |
| } |
| } |
| |
| int |
| get_access_flags_from_decl (decl) |
| tree decl; |
| { |
| int access_flags = 0; |
| if (TREE_CODE (decl) == FIELD_DECL || TREE_CODE (decl) == VAR_DECL) |
| { |
| if (FIELD_STATIC (decl)) |
| access_flags |= ACC_STATIC; |
| if (FIELD_PUBLIC (decl)) |
| access_flags |= ACC_PUBLIC; |
| if (FIELD_PROTECTED (decl)) |
| access_flags |= ACC_PROTECTED; |
| if (FIELD_PRIVATE (decl)) |
| access_flags |= ACC_PRIVATE; |
| if (FIELD_FINAL (decl)) |
| access_flags |= ACC_FINAL; |
| if (FIELD_VOLATILE (decl)) |
| access_flags |= ACC_VOLATILE; |
| if (FIELD_TRANSIENT (decl)) |
| access_flags |= ACC_TRANSIENT; |
| return access_flags; |
| } |
| if (TREE_CODE (decl) == TYPE_DECL) |
| { |
| if (CLASS_PUBLIC (decl)) |
| access_flags |= ACC_PUBLIC; |
| if (CLASS_FINAL (decl)) |
| access_flags |= ACC_FINAL; |
| if (CLASS_SUPER (decl)) |
| access_flags |= ACC_SUPER; |
| if (CLASS_INTERFACE (decl)) |
| access_flags |= ACC_INTERFACE; |
| if (CLASS_ABSTRACT (decl)) |
| access_flags |= ACC_ABSTRACT; |
| return access_flags; |
| } |
| if (TREE_CODE (decl) == FUNCTION_DECL) |
| { |
| if (METHOD_PUBLIC (decl)) |
| access_flags |= ACC_PUBLIC; |
| if (METHOD_PRIVATE (decl)) |
| access_flags |= ACC_PRIVATE; |
| if (METHOD_PROTECTED (decl)) |
| access_flags |= ACC_PROTECTED; |
| if (METHOD_STATIC (decl)) |
| access_flags |= ACC_STATIC; |
| if (METHOD_FINAL (decl)) |
| access_flags |= ACC_FINAL; |
| if (METHOD_SYNCHRONIZED (decl)) |
| access_flags |= ACC_SYNCHRONIZED; |
| if (METHOD_NATIVE (decl)) |
| access_flags |= ACC_NATIVE; |
| if (METHOD_ABSTRACT (decl)) |
| access_flags |= ACC_ABSTRACT; |
| if (METHOD_TRANSIENT (decl)) |
| access_flags |= ACC_TRANSIENT; |
| return access_flags; |
| } |
| abort (); |
| } |
| |
| static tree |
| make_field_value (fdecl) |
| tree fdecl; |
| { |
| tree finit, info; |
| int bsize, flags; |
| tree type = TREE_TYPE (fdecl); |
| int resolved = is_compiled_class (type); |
| START_RECORD_CONSTRUCTOR (finit, field_type_node); |
| PUSH_FIELD_VALUE (finit, "name", build_utf8_ref (DECL_NAME (fdecl))); |
| if (resolved) |
| type = build_class_ref (type); |
| else |
| { |
| tree signature = build_java_signature (type); |
| type = build_utf8_ref (unmangle_classname |
| (IDENTIFIER_POINTER(signature), |
| IDENTIFIER_LENGTH(signature))); |
| } |
| PUSH_FIELD_VALUE (finit, "type", type); |
| flags = get_access_flags_from_decl (fdecl); |
| if (! resolved) |
| flags |= 0x8000 /* FIELD_UNRESOLVED_FLAG */; |
| PUSH_FIELD_VALUE (finit, "accflags", build_int_2 (flags, 0)); |
| bsize = TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (fdecl))) / BITS_PER_UNIT; |
| PUSH_FIELD_VALUE (finit, "bsize", build_int_2 (bsize, 0)); |
| if (FIELD_STATIC (fdecl)) |
| { |
| tree cfield = TREE_CHAIN (TYPE_FIELDS(field_info_union_node)); |
| tree faddr = build_address_of (build_static_field_ref (fdecl)); |
| info = build (CONSTRUCTOR, field_info_union_node, NULL_TREE, |
| build_tree_list (cfield, faddr)); |
| } |
| else |
| { |
| int boffset |
| = TREE_INT_CST_LOW (DECL_FIELD_BITPOS (fdecl)) / BITS_PER_UNIT; |
| info = build (CONSTRUCTOR, field_info_union_node, NULL_TREE, |
| build_tree_list (TYPE_FIELDS(field_info_union_node), |
| build_int_2 (boffset, 0))); |
| } |
| PUSH_FIELD_VALUE (finit, "info", info); |
| |
| FINISH_RECORD_CONSTRUCTOR (finit); |
| return finit; |
| } |
| |
| static tree |
| make_method_value (mdecl) |
| tree mdecl; |
| { |
| tree minit; |
| tree code; |
| #define ACC_TRANSLATED 0x4000 |
| int accflags = get_access_flags_from_decl (mdecl) | ACC_TRANSLATED; |
| code = null_pointer_node; |
| if (DECL_RTL (mdecl)) |
| code = build1 (ADDR_EXPR, nativecode_ptr_type_node, mdecl); |
| START_RECORD_CONSTRUCTOR (minit, method_type_node); |
| PUSH_FIELD_VALUE (minit, "name", |
| build_utf8_ref (DECL_CONSTRUCTOR_P (mdecl) ? |
| init_identifier_node |
| : DECL_NAME (mdecl))); |
| { |
| tree signature = build_java_signature (TREE_TYPE (mdecl)); |
| PUSH_FIELD_VALUE (minit, "signature", |
| (build_utf8_ref |
| (unmangle_classname |
| (IDENTIFIER_POINTER(signature), |
| IDENTIFIER_LENGTH(signature))))); |
| } |
| PUSH_FIELD_VALUE (minit, "accflags", build_int_2 (accflags, 0)); |
| PUSH_FIELD_VALUE (minit, "ncode", code); |
| FINISH_RECORD_CONSTRUCTOR (minit); |
| return minit; |
| } |
| |
| static tree |
| get_dispatch_vector (type) |
| tree type; |
| { |
| tree vtable = TYPE_VTABLE (type); |
| if (vtable == NULL) |
| { |
| int i; |
| tree method; |
| tree super = CLASSTYPE_SUPER (type); |
| int nvirtuals = TREE_INT_CST_LOW (TYPE_NVIRTUALS (type)); |
| vtable = make_tree_vec (nvirtuals); |
| TYPE_VTABLE (type) = vtable; |
| if (super != NULL_TREE) |
| { |
| tree super_vtable = get_dispatch_vector (super); |
| for ( i = TREE_INT_CST_LOW (TYPE_NVIRTUALS (super)); --i >= 0; ) |
| TREE_VEC_ELT (vtable, i) = TREE_VEC_ELT (super_vtable, i); |
| } |
| for (method = TYPE_METHODS (type); method != NULL_TREE; |
| method = TREE_CHAIN (method)) |
| { |
| if (DECL_VINDEX (method) != NULL_TREE |
| && TREE_CODE (DECL_VINDEX (method)) == INTEGER_CST) |
| { |
| TREE_VEC_ELT (vtable, TREE_INT_CST_LOW (DECL_VINDEX (method))) |
| = method; |
| } |
| } |
| } |
| return vtable; |
| } |
| |
| static tree |
| get_dispatch_table (type, this_class_addr) |
| tree type, this_class_addr; |
| { |
| tree vtable = get_dispatch_vector (type); |
| int i; |
| tree list = NULL_TREE; |
| int nvirtuals = TREE_VEC_LENGTH (vtable); |
| for (i = nvirtuals; --i >= 0; ) |
| { |
| tree method = TREE_VEC_ELT (vtable, i); |
| if (METHOD_ABSTRACT (method)) |
| warning_with_decl (method, "abstract method in non-abstract class"); |
| if (DECL_RTL (method) == 0) |
| make_decl_rtl (method, NULL, 1); |
| list = tree_cons (NULL_TREE /*DECL_VINDEX (method) + 2*/, |
| build1 (ADDR_EXPR, nativecode_ptr_type_node, method), |
| list); |
| } |
| /* Dummy entry for compatibility with G++ -fvtable-thunks. */ |
| list = tree_cons (integer_zero_node, null_pointer_node, list); |
| list = tree_cons (integer_zero_node, this_class_addr, list); |
| return build (CONSTRUCTOR, build_prim_array_type (nativecode_ptr_type_node, |
| nvirtuals + 2), |
| NULL_TREE, list); |
| } |
| |
| void |
| make_class_data (type) |
| tree type; |
| { |
| tree decl, cons, temp; |
| tree field, fields_decl; |
| tree static_fields = NULL_TREE; |
| tree instance_fields = NULL_TREE; |
| HOST_WIDE_INT static_field_count = 0; |
| HOST_WIDE_INT instance_field_count = 0; |
| HOST_WIDE_INT field_count; |
| tree field_array_type; |
| tree method; |
| tree methods = NULL_TREE; |
| tree dtable_decl = NULL_TREE; |
| HOST_WIDE_INT method_count = 0; |
| tree method_array_type; |
| tree methods_decl; |
| tree super; |
| tree this_class_addr; |
| tree constant_pool_constructor; |
| tree interfaces = null_pointer_node; |
| int interface_len = 0; |
| tree type_decl = TYPE_NAME (type); |
| |
| this_class_addr = build_class_ref (type); |
| decl = TREE_OPERAND (this_class_addr, 0); |
| |
| /* Build Field array. */ |
| field = TYPE_FIELDS (type); |
| if (DECL_NAME (field) == NULL_TREE) |
| field = TREE_CHAIN (field); /* Skip dummy field for inherited data. */ |
| for ( ; field != NULL_TREE; field = TREE_CHAIN (field)) |
| { |
| if (! DECL_ARTIFICIAL (field)) |
| { |
| tree init = make_field_value (field); |
| if (FIELD_STATIC (field)) |
| { |
| static_field_count++; |
| static_fields = tree_cons (NULL_TREE, init, static_fields); |
| rest_of_decl_compilation (field, (char*) 0, 1, 1); |
| } |
| else |
| { |
| instance_field_count++; |
| instance_fields = tree_cons (NULL_TREE, init, instance_fields); |
| } |
| } |
| } |
| field_count = static_field_count + instance_field_count; |
| if (field_count > 0) |
| { |
| static_fields = nreverse (static_fields); |
| instance_fields = nreverse (instance_fields); |
| static_fields = chainon (static_fields, instance_fields); |
| field_array_type = build_prim_array_type (field_type_node, field_count); |
| fields_decl = build_decl (VAR_DECL, mangled_classname ("_FL_", type), |
| field_array_type); |
| DECL_INITIAL (fields_decl) = build (CONSTRUCTOR, field_array_type, |
| NULL_TREE, static_fields); |
| TREE_STATIC (fields_decl) = 1; |
| DECL_ARTIFICIAL (fields_decl) = 1; |
| DECL_IGNORED_P (fields_decl) = 1; |
| rest_of_decl_compilation (fields_decl, (char*) 0, 1, 0); |
| } |
| else |
| fields_decl = NULL_TREE; |
| |
| /* Build Method array. */ |
| for (method = TYPE_METHODS (CLASS_TO_HANDLE_TYPE (type)); |
| method != NULL_TREE; method = TREE_CHAIN (method)) |
| { |
| tree init; |
| if (METHOD_PRIVATE (method) |
| && (flag_inline_functions || optimize)) |
| continue; |
| init = make_method_value (method); |
| method_count++; |
| methods = tree_cons (NULL_TREE, init, methods); |
| } |
| method_array_type = build_prim_array_type (method_type_node, method_count); |
| methods_decl = build_decl (VAR_DECL, mangled_classname ("_MT_", type), |
| method_array_type); |
| DECL_INITIAL (methods_decl) = build (CONSTRUCTOR, method_array_type, |
| NULL_TREE, nreverse (methods)); |
| TREE_STATIC (methods_decl) = 1; |
| DECL_ARTIFICIAL (methods_decl) = 1; |
| DECL_IGNORED_P (methods_decl) = 1; |
| rest_of_decl_compilation (methods_decl, (char*) 0, 1, 0); |
| |
| if (flag_assume_compiled |
| && ! CLASS_ABSTRACT (type_decl) && ! CLASS_INTERFACE (type_decl)) |
| { |
| tree dtable = get_dispatch_table (type, this_class_addr); |
| dtable_decl = build_dtable_decl (type); |
| DECL_INITIAL (dtable_decl) = dtable; |
| TREE_STATIC (dtable_decl) = 1; |
| DECL_ARTIFICIAL (dtable_decl) = 1; |
| DECL_IGNORED_P (dtable_decl) = 1; |
| TREE_PUBLIC (dtable_decl) = 1; |
| rest_of_decl_compilation (dtable_decl, (char*) 0, 1, 0); |
| } |
| |
| super = CLASSTYPE_SUPER (type); |
| if (super == NULL_TREE) |
| super = null_pointer_node; |
| else if (flag_assume_compiled) |
| super = build_class_ref (super); |
| else |
| { |
| int super_index = alloc_class_constant (super); |
| super = build_int_2 (super_index, 0); |
| TREE_TYPE (super) = ptr_type_node; |
| } |
| |
| /* Build and emit the array of implemented interfaces. */ |
| if (type != object_type_node) |
| interface_len = TREE_VEC_LENGTH (TYPE_BINFO_BASETYPES (type)) - 1; |
| if (interface_len > 0) |
| { |
| tree init = NULL_TREE; |
| int i; |
| tree interface_array_type, idecl; |
| interface_array_type |
| = build_prim_array_type (class_ptr_type, interface_len); |
| idecl = build_decl (VAR_DECL, mangled_classname ("_IF_", type), |
| interface_array_type); |
| for (i = interface_len; i > 0; i--) |
| { |
| tree child = TREE_VEC_ELT (TYPE_BINFO_BASETYPES (type), i); |
| tree iclass = BINFO_TYPE (child); |
| tree index; |
| if (flag_assume_compiled) |
| index = build_class_ref (iclass); |
| else |
| { |
| int int_index = alloc_class_constant (iclass); |
| index = build_int_2 (int_index, 0); |
| TREE_TYPE (index) = ptr_type_node; |
| } |
| init = tree_cons (NULL_TREE, index, init); |
| } |
| DECL_INITIAL (idecl) = build (CONSTRUCTOR, interface_array_type, |
| NULL_TREE, init); |
| TREE_STATIC (idecl) = 1; |
| DECL_ARTIFICIAL (idecl) = 1; |
| DECL_IGNORED_P (idecl) = 1; |
| interfaces = build1 (ADDR_EXPR, ptr_type_node, idecl); |
| rest_of_decl_compilation (idecl, (char*) 0, 1, 0); |
| } |
| |
| constant_pool_constructor = build_constants_constructor (); |
| |
| START_RECORD_CONSTRUCTOR (temp, object_type_node); |
| #if 0 |
| PUSH_FIELD_VALUE (temp, "vtable", NULL_TREE); |
| #else |
| PUSH_FIELD_VALUE (temp, "vtable", |
| build1 (ADDR_EXPR, dtable_ptr_type, class_dtable_decl)); |
| #endif |
| PUSH_FIELD_VALUE (temp, "sync_info", null_pointer_node); |
| FINISH_RECORD_CONSTRUCTOR (temp); |
| START_RECORD_CONSTRUCTOR (cons, class_type_node); |
| PUSH_SUPER_VALUE (cons, temp); |
| PUSH_FIELD_VALUE (cons, "next", null_pointer_node); |
| PUSH_FIELD_VALUE (cons, "name", build_utf8_ref (DECL_NAME (type_decl))); |
| PUSH_FIELD_VALUE (cons, "accflags", |
| build_int_2 (get_access_flags_from_decl (type_decl), 0)); |
| |
| PUSH_FIELD_VALUE (cons, "superclass", |
| CLASS_INTERFACE (type_decl) ? null_pointer_node : super); |
| PUSH_FIELD_VALUE (cons, "constants", constant_pool_constructor); |
| PUSH_FIELD_VALUE (cons, "methods", |
| build1 (ADDR_EXPR, method_ptr_type_node, methods_decl)); |
| PUSH_FIELD_VALUE (cons, "method_count", build_int_2 (method_count, 0)); |
| PUSH_FIELD_VALUE (cons, "vtable_method_count", TYPE_NVIRTUALS (type)); |
| PUSH_FIELD_VALUE (cons, "fields", |
| fields_decl == NULL_TREE ? null_pointer_node |
| : build1 (ADDR_EXPR, field_ptr_type_node, fields_decl)); |
| PUSH_FIELD_VALUE (cons, "size_in_bytes", size_in_bytes (type)); |
| PUSH_FIELD_VALUE (cons, "field_count", build_int_2 (field_count, 0)); |
| PUSH_FIELD_VALUE (cons, "static_field_count", |
| build_int_2 (static_field_count, 0)); |
| PUSH_FIELD_VALUE (cons, "vtable", |
| dtable_decl == NULL_TREE ? null_pointer_node |
| : build1 (ADDR_EXPR, dtable_ptr_type, dtable_decl)); |
| PUSH_FIELD_VALUE (cons, "interfaces", interfaces); |
| PUSH_FIELD_VALUE (cons, "loader", null_pointer_node); |
| PUSH_FIELD_VALUE (cons, "interface_count", build_int_2 (interface_len, 0)); |
| PUSH_FIELD_VALUE (cons, "state", integer_zero_node); |
| |
| PUSH_FIELD_VALUE (cons, "thread", null_pointer_node); |
| |
| FINISH_RECORD_CONSTRUCTOR (cons); |
| |
| DECL_INITIAL (decl) = cons; |
| rest_of_decl_compilation (decl, (char*) 0, 1, 0); |
| } |
| |
| void |
| finish_class (cl) |
| tree cl; |
| { |
| tree method; |
| |
| /* Emit deferred inline methods. */ |
| for ( method = TYPE_METHODS (CLASS_TO_HANDLE_TYPE (current_class)); |
| method != NULL_TREE; method = TREE_CHAIN (method)) |
| { |
| if (! TREE_ASM_WRITTEN (method) && DECL_SAVED_INSNS (method) != 0) |
| { |
| /* It's a deferred inline method. Decide if we need to emit it. */ |
| if (flag_keep_inline_functions |
| || TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (method)) |
| || ! METHOD_PRIVATE (method)) |
| { |
| temporary_allocation (); |
| output_inline_function (method); |
| permanent_allocation (1); |
| } |
| } |
| } |
| |
| make_class_data (current_class); |
| register_class (); |
| rest_of_decl_compilation (TYPE_NAME (current_class), (char*) 0, 1, 0); |
| } |
| |
| /* Return 2 if CLASS is compiled by this compilation job; |
| return 1 if CLASS can otherwise be assumed to be compiled; |
| return 0 if we cannot assume that CLASS is compiled. |
| Returns 1 for primitive and 0 for array types. */ |
| int |
| is_compiled_class (class) |
| tree class; |
| { |
| int seen_in_zip; |
| if (TREE_CODE (class) == POINTER_TYPE) |
| class = TREE_TYPE (class); |
| if (TREE_CODE (class) != RECORD_TYPE) /* Primitive types are static. */ |
| return 1; |
| if (TYPE_ARRAY_P (class)) |
| return 0; |
| if (class == current_class) |
| return 2; |
| |
| seen_in_zip = (TYPE_LANG_SPECIFIC (class) && TYPE_LANG_SPECIFIC (class)->jcf |
| && TYPE_LANG_SPECIFIC (class)->jcf->seen_in_zip); |
| if (CLASS_FROM_CURRENTLY_COMPILED_SOURCE_P (class) || seen_in_zip) |
| { |
| /* The class was seen in the current ZIP file and will be |
| available as a compiled class in the future but may not have |
| been loaded already. Load it if necessary. This prevent |
| build_class_ref () from crashing. */ |
| |
| if (seen_in_zip && !CLASS_LOADED_P (class)) |
| load_class (class, 1); |
| |
| /* We return 2 for class seen in ZIP and class from files |
| belonging to the same compilation unit */ |
| return 2; |
| } |
| |
| if (flag_assume_compiled) |
| { |
| if (!CLASS_LOADED_P (class)) |
| { |
| if (CLASS_FROM_SOURCE_P (class)) |
| safe_layout_class (class); |
| else |
| load_class (class, 1); |
| } |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| /* Append the mangled name of TYPE onto OBSTACK. */ |
| |
| static void |
| append_gpp_mangled_type (obstack, type) |
| struct obstack *obstack; |
| tree type; |
| { |
| switch (TREE_CODE (type)) |
| { |
| char code; |
| case BOOLEAN_TYPE: code = 'b'; goto primitive; |
| case CHAR_TYPE: code = 'w'; goto primitive; |
| case VOID_TYPE: code = 'v'; goto primitive; |
| case INTEGER_TYPE: |
| /* Get the original type instead of the arguments promoted type. |
| Avoid symbol name clashes. Should call a function to do that. |
| FIXME. */ |
| if (type == promoted_short_type_node) |
| type = short_type_node; |
| if (type == promoted_byte_type_node) |
| type = byte_type_node; |
| switch (TYPE_PRECISION (type)) |
| { |
| case 8: code = 'c'; goto primitive; |
| case 16: code = 's'; goto primitive; |
| case 32: code = 'i'; goto primitive; |
| case 64: code = 'x'; goto primitive; |
| default: goto bad_type; |
| } |
| primitive: |
| obstack_1grow (obstack, code); |
| break; |
| case REAL_TYPE: |
| switch (TYPE_PRECISION (type)) |
| { |
| case 32: code = 'f'; goto primitive; |
| case 64: code = 'd'; goto primitive; |
| default: goto bad_type; |
| } |
| case POINTER_TYPE: |
| type = TREE_TYPE (type); |
| obstack_1grow (obstack, 'P'); |
| case RECORD_TYPE: |
| if (TYPE_ARRAY_P (type)) |
| { |
| obstack_grow (obstack, "t6JArray1Z", sizeof("t6JArray1Z")-1); |
| append_gpp_mangled_type (obstack, TYPE_ARRAY_ELEMENT (type)); |
| } |
| else |
| { |
| char *class_name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))); |
| append_gpp_mangled_classtype (obstack, class_name); |
| } |
| break; |
| bad_type: |
| default: |
| fatal ("internal error - trying to mangle unknown type"); |
| } |
| } |
| |
| /* Build the mangled name of the `class' field. */ |
| |
| static tree |
| mangle_class_field (class) |
| tree class; |
| { |
| tree name; |
| obstack_grow (&temporary_obstack, "_CL_", 4); |
| append_gpp_mangled_type (&temporary_obstack, class); |
| obstack_1grow (&temporary_obstack, '\0'); |
| name = get_identifier (obstack_base (&temporary_obstack)); |
| obstack_free (&temporary_obstack, obstack_base (&temporary_obstack)); |
| return name; |
| } |
| |
| /* Build the mangled (assembly-level) name of the static field FIELD. */ |
| |
| static tree |
| mangle_static_field (field) |
| tree field; |
| { |
| tree class = DECL_CONTEXT (field); |
| tree name = DECL_NAME (field); |
| int encoded_len; |
| #if ! defined (NO_DOLLAR_IN_LABEL) || ! defined (NO_DOT_IN_LABEL) |
| obstack_1grow (&temporary_obstack, '_'); |
| #else |
| obstack_grow (&temporary_obstack, "__static_", 9); |
| #endif |
| append_gpp_mangled_type (&temporary_obstack, class); |
| encoded_len = unicode_mangling_length (IDENTIFIER_POINTER (name), |
| IDENTIFIER_LENGTH (name)); |
| if (encoded_len > 0) |
| { |
| obstack_1grow (&temporary_obstack, 'U'); |
| } |
| #ifndef NO_DOLLAR_IN_LABEL |
| obstack_1grow (&temporary_obstack, '$'); |
| #else /* NO_DOLLAR_IN_LABEL */ |
| #ifndef NO_DOT_IN_LABEL |
| obstack_1grow (&temporary_obstack, '.'); |
| #else /* NO_DOT_IN_LABEL */ |
| obstack_1grow (&temporary_obstack, '_'); |
| #endif /* NO_DOT_IN_LABEL */ |
| #endif /* NO_DOLLAR_IN_LABEL */ |
| if (encoded_len > 0) |
| { |
| emit_unicode_mangled_name (&temporary_obstack, |
| IDENTIFIER_POINTER (name), |
| IDENTIFIER_LENGTH (name)); |
| } |
| else |
| { |
| obstack_grow (&temporary_obstack, |
| IDENTIFIER_POINTER (name), |
| IDENTIFIER_LENGTH (name)); |
| } |
| obstack_1grow (&temporary_obstack, '\0'); |
| name = get_identifier (obstack_base (&temporary_obstack)); |
| obstack_free (&temporary_obstack, obstack_base (&temporary_obstack)); |
| return name; |
| } |
| |
| /* Build a VAR_DECL for the dispatch table (vtable) for class TYPE. */ |
| |
| tree |
| build_dtable_decl (type) |
| tree type; |
| { |
| tree name; |
| obstack_grow (&temporary_obstack, "__vt_", 5); |
| append_gpp_mangled_type (&temporary_obstack, type); |
| obstack_1grow (&temporary_obstack, '\0'); |
| name = get_identifier (obstack_base (&temporary_obstack)); |
| obstack_free (&temporary_obstack, obstack_base (&temporary_obstack)); |
| return build_decl (VAR_DECL, name, dtable_type); |
| } |
| |
| /* Pre-pend the TYPE_FIELDS of THIS_CLASS with a dummy FIELD_DECL for the |
| fields inherited from SUPER_CLASS. */ |
| |
| void |
| push_super_field (this_class, super_class) |
| tree this_class, super_class; |
| { |
| tree base_decl; |
| push_obstacks (&permanent_obstack, &permanent_obstack); |
| base_decl = build_decl (FIELD_DECL, NULL_TREE, super_class); |
| pop_obstacks (); |
| DECL_IGNORED_P (base_decl) = 1; |
| TREE_CHAIN (base_decl) = TYPE_FIELDS (this_class); |
| TYPE_FIELDS (this_class) = base_decl; |
| DECL_SIZE (base_decl) = TYPE_SIZE (super_class); |
| } |
| |
| /* Handle the different manners we may have to lay out a super class. */ |
| |
| static tree |
| maybe_layout_super_class (super_class, this_class) |
| tree super_class; |
| tree this_class; |
| { |
| if (TREE_CODE (super_class) == RECORD_TYPE) |
| { |
| if (!CLASS_LOADED_P (super_class) |
| && CLASS_FROM_SOURCE_P (super_class)) |
| safe_layout_class (super_class); |
| if (!CLASS_LOADED_P (super_class)) |
| load_class (super_class, 1); |
| } |
| /* We might have to layout the class before its dependency on |
| the super class gets resolved by java_complete_class */ |
| else if (TREE_CODE (super_class) == POINTER_TYPE) |
| { |
| if (TREE_TYPE (super_class) != NULL_TREE) |
| super_class = TREE_TYPE (super_class); |
| else |
| { |
| super_class = do_resolve_class (super_class, NULL_TREE, this_class); |
| if (!super_class) |
| return NULL_TREE; /* FIXME, NULL_TREE not checked by caller. */ |
| super_class = TREE_TYPE (super_class); |
| } |
| } |
| if (!TYPE_SIZE (super_class)) |
| safe_layout_class (super_class); |
| |
| return super_class; |
| } |
| |
| void |
| layout_class (this_class) |
| tree this_class; |
| { |
| tree super_class = CLASSTYPE_SUPER (this_class); |
| tree field; |
| |
| if (super_class) |
| { |
| super_class = maybe_layout_super_class (super_class, this_class); |
| if (TREE_CODE (TYPE_SIZE (super_class)) == ERROR_MARK) |
| { |
| TYPE_SIZE (this_class) = error_mark_node; |
| return; |
| } |
| if (TYPE_SIZE (this_class) == NULL_TREE) |
| push_super_field (this_class, super_class); |
| } |
| |
| for (field = TYPE_FIELDS (this_class); |
| field != NULL_TREE; field = TREE_CHAIN (field)) |
| { |
| if (FIELD_STATIC (field)) |
| { |
| /* Set DECL_ASSEMBLER_NAME to something suitably mangled. */ |
| DECL_ASSEMBLER_NAME (field) = mangle_static_field (field); |
| } |
| } |
| |
| layout_type (this_class); |
| } |
| |
| void |
| layout_class_methods (this_class) |
| tree this_class; |
| { |
| tree method_decl, dtable_count; |
| tree super_class, handle_type; |
| |
| if (TYPE_NVIRTUALS (this_class)) |
| return; |
| |
| push_obstacks (&permanent_obstack, &permanent_obstack); |
| super_class = CLASSTYPE_SUPER (this_class); |
| handle_type = CLASS_TO_HANDLE_TYPE (this_class); |
| |
| if (super_class) |
| { |
| super_class = maybe_layout_super_class (super_class, this_class); |
| if (!TYPE_NVIRTUALS (super_class)) |
| layout_class_methods (super_class); |
| dtable_count = TYPE_NVIRTUALS (super_class); |
| } |
| else |
| dtable_count = integer_zero_node; |
| |
| TYPE_METHODS (handle_type) = nreverse (TYPE_METHODS (handle_type)); |
| |
| for (method_decl = TYPE_METHODS (handle_type); |
| method_decl; method_decl = TREE_CHAIN (method_decl)) |
| dtable_count = layout_class_method (this_class, super_class, |
| method_decl, dtable_count); |
| |
| TYPE_NVIRTUALS (this_class) = dtable_count; |
| |
| #ifdef JAVA_USE_HANDLES |
| layout_type (handle_type); |
| #endif |
| pop_obstacks (); |
| } |
| |
| /* Lay METHOD_DECL out, returning a possibly new value of |
| DTABLE_COUNT. */ |
| |
| tree |
| layout_class_method (this_class, super_class, method_decl, dtable_count) |
| tree this_class, super_class, method_decl, dtable_count; |
| { |
| char *ptr; |
| char *asm_name; |
| tree arg, arglist, t; |
| int method_name_needs_escapes = 0; |
| tree method_name = DECL_NAME (method_decl); |
| int method_name_is_wfl = |
| (TREE_CODE (method_name) == EXPR_WITH_FILE_LOCATION); |
| if (method_name_is_wfl) |
| method_name = java_get_real_method_name (method_decl); |
| |
| if (method_name != init_identifier_node |
| && method_name != finit_identifier_node) |
| { |
| int encoded_len |
| = unicode_mangling_length (IDENTIFIER_POINTER (method_name), |
| IDENTIFIER_LENGTH (method_name)); |
| if (encoded_len > 0) |
| { |
| method_name_needs_escapes = 1; |
| emit_unicode_mangled_name (&temporary_obstack, |
| IDENTIFIER_POINTER (method_name), |
| IDENTIFIER_LENGTH (method_name)); |
| } |
| else |
| { |
| obstack_grow (&temporary_obstack, |
| IDENTIFIER_POINTER (method_name), |
| IDENTIFIER_LENGTH (method_name)); |
| } |
| } |
| |
| obstack_grow (&temporary_obstack, "__", 2); |
| if (method_name == finit_identifier_node) |
| obstack_grow (&temporary_obstack, "finit", 5); |
| append_gpp_mangled_type (&temporary_obstack, this_class); |
| TREE_PUBLIC (method_decl) = 1; |
| |
| t = TREE_TYPE (method_decl); |
| arglist = TYPE_ARG_TYPES (t); |
| if (TREE_CODE (t) == METHOD_TYPE) |
| arglist = TREE_CHAIN (arglist); |
| for (arg = arglist; arg != end_params_node; ) |
| { |
| tree a = arglist; |
| tree argtype = TREE_VALUE (arg); |
| int tindex = 1; |
| if (TREE_CODE (argtype) == POINTER_TYPE) |
| { |
| /* This is O(N**2). Do we care? Cfr gcc/cp/method.c. */ |
| while (a != arg && argtype != TREE_VALUE (a)) |
| a = TREE_CHAIN (a), tindex++; |
| } |
| else |
| a = arg; |
| if (a != arg) |
| { |
| char buf[12]; |
| int nrepeats = 0; |
| do |
| { |
| arg = TREE_CHAIN (arg); nrepeats++; |
| } |
| while (arg != end_params_node && argtype == TREE_VALUE (arg)); |
| if (nrepeats > 1) |
| { |
| obstack_1grow (&temporary_obstack, 'N'); |
| sprintf (buf, "%d", nrepeats); |
| obstack_grow (&temporary_obstack, buf, strlen (buf)); |
| if (nrepeats > 9) |
| obstack_1grow (&temporary_obstack, '_'); |
| } |
| else |
| obstack_1grow (&temporary_obstack, 'T'); |
| sprintf (buf, "%d", tindex); |
| obstack_grow (&temporary_obstack, buf, strlen (buf)); |
| if (tindex > 9) |
| obstack_1grow (&temporary_obstack, '_'); |
| } |
| else |
| { |
| append_gpp_mangled_type (&temporary_obstack, argtype); |
| arg = TREE_CHAIN (arg); |
| } |
| } |
| if (method_name_needs_escapes) |
| obstack_1grow (&temporary_obstack, 'U'); |
| |
| obstack_1grow (&temporary_obstack, '\0'); |
| asm_name = obstack_finish (&temporary_obstack); |
| DECL_ASSEMBLER_NAME (method_decl) = get_identifier (asm_name); |
| if (! METHOD_ABSTRACT (method_decl) |
| && ! CLASS_INTERFACE (TYPE_NAME (this_class))) |
| make_function_rtl (method_decl); |
| obstack_free (&temporary_obstack, asm_name); |
| |
| if (method_name == init_identifier_node) |
| { |
| char *p = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (this_class))); |
| for (ptr = p; *ptr; ) |
| { |
| if (*ptr++ == '.') |
| p = ptr; |
| } |
| if (method_name_is_wfl) |
| EXPR_WFL_NODE (DECL_NAME (method_decl)) = get_identifier (p); |
| else |
| DECL_NAME (method_decl) = get_identifier (p); |
| DECL_CONSTRUCTOR_P (method_decl) = 1; |
| build_java_argument_signature (TREE_TYPE (method_decl)); |
| } |
| else if (! METHOD_STATIC (method_decl) && !DECL_ARTIFICIAL (method_decl)) |
| { |
| tree method_sig = |
| build_java_argument_signature (TREE_TYPE (method_decl)); |
| tree super_method = lookup_argument_method (super_class, method_name, |
| method_sig); |
| if (super_method != NULL_TREE && ! METHOD_PRIVATE (super_method)) |
| { |
| DECL_VINDEX (method_decl) = DECL_VINDEX (super_method); |
| if (DECL_VINDEX (method_decl) == NULL_TREE) |
| error_with_decl (method_decl, |
| "non-static method '%s' overrides static method"); |
| #if 0 |
| else if (TREE_TYPE (TREE_TYPE (method_decl)) |
| != TREE_TYPE (TREE_TYPE (super_method))) |
| { |
| error_with_decl (method_decl, |
| "Method `%s' redefined with different return type"); |
| error_with_decl (super_method, |
| "Overridden decl is here"); |
| } |
| #endif |
| } |
| else if (! METHOD_FINAL (method_decl) |
| && ! METHOD_PRIVATE (method_decl) |
| && ! CLASS_FINAL (TYPE_NAME (this_class)) |
| && dtable_count) |
| { |
| DECL_VINDEX (method_decl) = dtable_count; |
| dtable_count = build_int_2 (1+TREE_INT_CST_LOW (dtable_count), 0); |
| } |
| } |
| return dtable_count; |
| } |
| |
| static tree registered_class = NULL_TREE; |
| |
| void |
| register_class () |
| { |
| static tree end; |
| tree node = TREE_OPERAND (build_class_ref (current_class), 0); |
| tree current = copy_node (node); |
| |
| XEXP (DECL_RTL (current), 0) = copy_rtx (XEXP (DECL_RTL(node), 0)); |
| if (!registered_class) |
| registered_class = current; |
| else |
| TREE_CHAIN (end) = current; |
| |
| end = current; |
| } |
| |
| /* Generate a function that gets called at start-up (static contructor) time, |
| which calls registerClass for all the compiled classes. */ |
| |
| void |
| emit_register_classes () |
| { |
| extern tree get_file_function_name PROTO((int)); |
| tree init_name = get_file_function_name ('I'); |
| tree init_type = build_function_type (void_type_node, end_params_node); |
| tree init_decl; |
| tree t; |
| |
| start_sequence (); |
| init_decl = build_decl (FUNCTION_DECL, init_name, init_type); |
| DECL_ASSEMBLER_NAME (init_decl) = init_name; |
| TREE_STATIC (init_decl) = 1; |
| current_function_decl = init_decl; |
| DECL_RESULT (init_decl) = build_decl(RESULT_DECL, NULL_TREE, void_type_node); |
| /* DECL_EXTERNAL (init_decl) = 1;*/ |
| TREE_PUBLIC (init_decl) = 1; |
| pushlevel (0); |
| make_function_rtl (init_decl); |
| init_function_start (init_decl, input_filename, 0); |
| expand_function_start (init_decl, 0); |
| |
| for ( t = registered_class; t; t = TREE_CHAIN (t)) |
| emit_library_call (registerClass_libfunc, 0, VOIDmode, 1, |
| XEXP (DECL_RTL (t), 0), Pmode); |
| |
| expand_function_end (input_filename, 0, 0); |
| poplevel (1, 0, 1); |
| { |
| /* Force generation, even with -O3 or deeper. Gross hack. FIXME */ |
| int saved_flag = flag_inline_functions; |
| flag_inline_functions = 0; |
| rest_of_compilation (init_decl); |
| flag_inline_functions = saved_flag; |
| } |
| current_function_decl = NULL_TREE; |
| assemble_constructor (IDENTIFIER_POINTER (init_name)); |
| } |
| |
| void |
| init_class_processing () |
| { |
| registerClass_libfunc = gen_rtx (SYMBOL_REF, Pmode, "_Jv_RegisterClass"); |
| } |