| /* Implement classes and message passing for Objective C. |
| Copyright (C) 1992, 1993, 1994, 1995, 1997, 1998, 1999, 2000, |
| 2001, 2002, 2003, 2004, 2005, 2007, 2008 Free Software Foundation, Inc. |
| Contributed by Steve Naroff. |
| |
| This file is part of GCC. |
| |
| GCC 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 3, or (at your option) |
| any later version. |
| |
| GCC 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 GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| |
| /* Purpose: This module implements the Objective-C 4.0 language. |
| |
| compatibility issues (with the Stepstone translator): |
| |
| - does not recognize the following 3.3 constructs. |
| @requires, @classes, @messages, = (...) |
| - methods with variable arguments must conform to ANSI standard. |
| - tagged structure definitions that appear in BOTH the interface |
| and implementation are not allowed. |
| - public/private: all instance variables are public within the |
| context of the implementation...I consider this to be a bug in |
| the translator. |
| - statically allocated objects are not supported. the user will |
| receive an error if this service is requested. |
| |
| code generation `options': |
| |
| */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "tm.h" |
| #include "tree.h" |
| #include "rtl.h" |
| #include "tm_p.h" |
| #include "expr.h" |
| |
| #ifdef OBJCPLUS |
| #include "cp-tree.h" |
| #else |
| #include "c-tree.h" |
| #endif |
| |
| #include "c-common.h" |
| #include "c-pragma.h" |
| #include "flags.h" |
| #include "langhooks.h" |
| #include "objc-act.h" |
| #include "input.h" |
| #include "except.h" |
| #include "function.h" |
| #include "output.h" |
| #include "toplev.h" |
| #include "ggc.h" |
| #include "varray.h" |
| #include "debug.h" |
| #include "target.h" |
| #include "diagnostic.h" |
| #include "cgraph.h" |
| #include "tree-iterator.h" |
| #include "libfuncs.h" |
| #include "hashtab.h" |
| #include "langhooks-def.h" |
| |
| #define OBJC_VOID_AT_END void_list_node |
| |
| static unsigned int should_call_super_dealloc = 0; |
| |
| /* When building Objective-C++, we are not linking against the C front-end |
| and so need to replicate the C tree-construction functions in some way. */ |
| #ifdef OBJCPLUS |
| #define OBJCP_REMAP_FUNCTIONS |
| #include "objcp-decl.h" |
| #endif /* OBJCPLUS */ |
| |
| /* This is the default way of generating a method name. */ |
| /* I am not sure it is really correct. |
| Perhaps there's a danger that it will make name conflicts |
| if method names contain underscores. -- rms. */ |
| #ifndef OBJC_GEN_METHOD_LABEL |
| #define OBJC_GEN_METHOD_LABEL(BUF, IS_INST, CLASS_NAME, CAT_NAME, SEL_NAME, NUM) \ |
| do { \ |
| char *temp; \ |
| sprintf ((BUF), "_%s_%s_%s_%s", \ |
| ((IS_INST) ? "i" : "c"), \ |
| (CLASS_NAME), \ |
| ((CAT_NAME)? (CAT_NAME) : ""), \ |
| (SEL_NAME)); \ |
| for (temp = (BUF); *temp; temp++) \ |
| if (*temp == ':') *temp = '_'; \ |
| } while (0) |
| #endif |
| |
| /* These need specifying. */ |
| #ifndef OBJC_FORWARDING_STACK_OFFSET |
| #define OBJC_FORWARDING_STACK_OFFSET 0 |
| #endif |
| |
| #ifndef OBJC_FORWARDING_MIN_OFFSET |
| #define OBJC_FORWARDING_MIN_OFFSET 0 |
| #endif |
| |
| /* Set up for use of obstacks. */ |
| |
| #include "obstack.h" |
| |
| /* This obstack is used to accumulate the encoding of a data type. */ |
| static struct obstack util_obstack; |
| |
| /* This points to the beginning of obstack contents, so we can free |
| the whole contents. */ |
| char *util_firstobj; |
| |
| /* The version identifies which language generation and runtime |
| the module (file) was compiled for, and is recorded in the |
| module descriptor. */ |
| |
| #define OBJC_VERSION (flag_next_runtime ? 6 : 8) |
| #define PROTOCOL_VERSION 2 |
| |
| /* (Decide if these can ever be validly changed.) */ |
| #define OBJC_ENCODE_INLINE_DEFS 0 |
| #define OBJC_ENCODE_DONT_INLINE_DEFS 1 |
| |
| /*** Private Interface (procedures) ***/ |
| |
| /* Used by compile_file. */ |
| |
| static void init_objc (void); |
| static void finish_objc (void); |
| |
| /* Code generation. */ |
| |
| static tree objc_build_constructor (tree, tree); |
| static tree build_objc_method_call (int, tree, tree, tree, tree); |
| static tree get_proto_encoding (tree); |
| static tree lookup_interface (tree); |
| static tree objc_add_static_instance (tree, tree); |
| |
| static tree start_class (enum tree_code, tree, tree, tree); |
| static tree continue_class (tree); |
| static void finish_class (tree); |
| static void start_method_def (tree); |
| #ifdef OBJCPLUS |
| static void objc_start_function (tree, tree, tree, tree); |
| #else |
| static void objc_start_function (tree, tree, tree, struct c_arg_info *); |
| #endif |
| static tree start_protocol (enum tree_code, tree, tree); |
| static tree build_method_decl (enum tree_code, tree, tree, tree, bool); |
| static tree objc_add_method (tree, tree, int); |
| static tree add_instance_variable (tree, int, tree); |
| static tree build_ivar_reference (tree); |
| static tree is_ivar (tree, tree); |
| |
| static void build_objc_exception_stuff (void); |
| static void build_next_objc_exception_stuff (void); |
| |
| /* We only need the following for ObjC; ObjC++ will use C++'s definition |
| of DERIVED_FROM_P. */ |
| #ifndef OBJCPLUS |
| static bool objc_derived_from_p (tree, tree); |
| #define DERIVED_FROM_P(PARENT, CHILD) objc_derived_from_p (PARENT, CHILD) |
| #endif |
| static void objc_xref_basetypes (tree, tree); |
| |
| static void build_class_template (void); |
| static void build_selector_template (void); |
| static void build_category_template (void); |
| static void build_super_template (void); |
| static tree build_protocol_initializer (tree, tree, tree, tree, tree); |
| static tree get_class_ivars (tree, bool); |
| static tree generate_protocol_list (tree); |
| static void build_protocol_reference (tree); |
| |
| #ifdef OBJCPLUS |
| static void objc_generate_cxx_cdtors (void); |
| #endif |
| |
| static const char *synth_id_with_class_suffix (const char *, tree); |
| |
| /* Hash tables to manage the global pool of method prototypes. */ |
| |
| hash *nst_method_hash_list = 0; |
| hash *cls_method_hash_list = 0; |
| |
| static hash hash_lookup (hash *, tree); |
| static tree lookup_method (tree, tree); |
| static tree lookup_method_static (tree, tree, int); |
| |
| enum string_section |
| { |
| class_names, /* class, category, protocol, module names */ |
| meth_var_names, /* method and variable names */ |
| meth_var_types /* method and variable type descriptors */ |
| }; |
| |
| static tree add_objc_string (tree, enum string_section); |
| static tree build_objc_string_decl (enum string_section); |
| static void build_selector_table_decl (void); |
| |
| /* Protocol additions. */ |
| |
| static tree lookup_protocol (tree); |
| static tree lookup_and_install_protocols (tree); |
| |
| /* Type encoding. */ |
| |
| static void encode_type_qualifiers (tree); |
| static void encode_type (tree, int, int); |
| static void encode_field_decl (tree, int, int); |
| |
| #ifdef OBJCPLUS |
| static void really_start_method (tree, tree); |
| #else |
| static void really_start_method (tree, struct c_arg_info *); |
| #endif |
| static int comp_proto_with_proto (tree, tree, int); |
| static void objc_push_parm (tree); |
| #ifdef OBJCPLUS |
| static tree objc_get_parm_info (int); |
| #else |
| static struct c_arg_info *objc_get_parm_info (int); |
| #endif |
| |
| /* Utilities for debugging and error diagnostics. */ |
| |
| static void warn_with_method (const char *, int, tree); |
| static char *gen_type_name (tree); |
| static char *gen_type_name_0 (tree); |
| static char *gen_method_decl (tree); |
| static char *gen_declaration (tree); |
| |
| /* Everything else. */ |
| |
| static tree create_field_decl (tree, const char *); |
| static void add_class_reference (tree); |
| static void build_protocol_template (void); |
| static tree encode_method_prototype (tree); |
| static void generate_classref_translation_entry (tree); |
| static void handle_class_ref (tree); |
| static void generate_struct_by_value_array (void) |
| ATTRIBUTE_NORETURN; |
| static void mark_referenced_methods (void); |
| static void generate_objc_image_info (void); |
| |
| /*** Private Interface (data) ***/ |
| |
| /* Reserved tag definitions. */ |
| |
| #define OBJECT_TYPEDEF_NAME "id" |
| #define CLASS_TYPEDEF_NAME "Class" |
| |
| #define TAG_OBJECT "objc_object" |
| #define TAG_CLASS "objc_class" |
| #define TAG_SUPER "objc_super" |
| #define TAG_SELECTOR "objc_selector" |
| |
| #define UTAG_CLASS "_objc_class" |
| #define UTAG_IVAR "_objc_ivar" |
| #define UTAG_IVAR_LIST "_objc_ivar_list" |
| #define UTAG_METHOD "_objc_method" |
| #define UTAG_METHOD_LIST "_objc_method_list" |
| #define UTAG_CATEGORY "_objc_category" |
| #define UTAG_MODULE "_objc_module" |
| #define UTAG_SYMTAB "_objc_symtab" |
| #define UTAG_SUPER "_objc_super" |
| #define UTAG_SELECTOR "_objc_selector" |
| |
| #define UTAG_PROTOCOL "_objc_protocol" |
| #define UTAG_METHOD_PROTOTYPE "_objc_method_prototype" |
| #define UTAG_METHOD_PROTOTYPE_LIST "_objc__method_prototype_list" |
| |
| /* Note that the string object global name is only needed for the |
| NeXT runtime. */ |
| #define STRING_OBJECT_GLOBAL_FORMAT "_%sClassReference" |
| |
| #define PROTOCOL_OBJECT_CLASS_NAME "Protocol" |
| |
| static const char *TAG_GETCLASS; |
| static const char *TAG_GETMETACLASS; |
| static const char *TAG_MSGSEND; |
| static const char *TAG_MSGSENDSUPER; |
| /* The NeXT Objective-C messenger may have two extra entry points, for use |
| when returning a structure. */ |
| static const char *TAG_MSGSEND_STRET; |
| static const char *TAG_MSGSENDSUPER_STRET; |
| static const char *default_constant_string_class_name; |
| |
| /* Runtime metadata flags. */ |
| #define CLS_FACTORY 0x0001L |
| #define CLS_META 0x0002L |
| #define CLS_HAS_CXX_STRUCTORS 0x2000L |
| |
| #define OBJC_MODIFIER_STATIC 0x00000001 |
| #define OBJC_MODIFIER_FINAL 0x00000002 |
| #define OBJC_MODIFIER_PUBLIC 0x00000004 |
| #define OBJC_MODIFIER_PRIVATE 0x00000008 |
| #define OBJC_MODIFIER_PROTECTED 0x00000010 |
| #define OBJC_MODIFIER_NATIVE 0x00000020 |
| #define OBJC_MODIFIER_SYNCHRONIZED 0x00000040 |
| #define OBJC_MODIFIER_ABSTRACT 0x00000080 |
| #define OBJC_MODIFIER_VOLATILE 0x00000100 |
| #define OBJC_MODIFIER_TRANSIENT 0x00000200 |
| #define OBJC_MODIFIER_NONE_SPECIFIED 0x80000000 |
| |
| /* NeXT-specific tags. */ |
| |
| #define TAG_MSGSEND_NONNIL "objc_msgSendNonNil" |
| #define TAG_MSGSEND_NONNIL_STRET "objc_msgSendNonNil_stret" |
| #define TAG_EXCEPTIONEXTRACT "objc_exception_extract" |
| #define TAG_EXCEPTIONTRYENTER "objc_exception_try_enter" |
| #define TAG_EXCEPTIONTRYEXIT "objc_exception_try_exit" |
| #define TAG_EXCEPTIONMATCH "objc_exception_match" |
| #define TAG_EXCEPTIONTHROW "objc_exception_throw" |
| #define TAG_SYNCENTER "objc_sync_enter" |
| #define TAG_SYNCEXIT "objc_sync_exit" |
| #define TAG_SETJMP "_setjmp" |
| #define UTAG_EXCDATA "_objc_exception_data" |
| |
| #define TAG_ASSIGNIVAR "objc_assign_ivar" |
| #define TAG_ASSIGNGLOBAL "objc_assign_global" |
| #define TAG_ASSIGNSTRONGCAST "objc_assign_strongCast" |
| |
| /* Branch entry points. All that matters here are the addresses; |
| functions with these names do not really exist in libobjc. */ |
| |
| #define TAG_MSGSEND_FAST "objc_msgSend_Fast" |
| #define TAG_ASSIGNIVAR_FAST "objc_assign_ivar_Fast" |
| |
| #define TAG_CXX_CONSTRUCT ".cxx_construct" |
| #define TAG_CXX_DESTRUCT ".cxx_destruct" |
| |
| /* GNU-specific tags. */ |
| |
| #define TAG_EXECCLASS "__objc_exec_class" |
| #define TAG_GNUINIT "__objc_gnu_init" |
| |
| /* Flags for lookup_method_static(). */ |
| #define OBJC_LOOKUP_CLASS 1 /* Look for class methods. */ |
| #define OBJC_LOOKUP_NO_SUPER 2 /* Do not examine superclasses. */ |
| |
| /* The OCTI_... enumeration itself is in objc/objc-act.h. */ |
| tree objc_global_trees[OCTI_MAX]; |
| |
| static void handle_impent (struct imp_entry *); |
| |
| struct imp_entry *imp_list = 0; |
| int imp_count = 0; /* `@implementation' */ |
| int cat_count = 0; /* `@category' */ |
| |
| enum tree_code objc_inherit_code; |
| int objc_public_flag; |
| |
| /* Use to generate method labels. */ |
| static int method_slot = 0; |
| |
| #define BUFSIZE 1024 |
| |
| static char *errbuf; /* Buffer for error diagnostics */ |
| |
| /* Data imported from tree.c. */ |
| |
| extern enum debug_info_type write_symbols; |
| |
| /* Data imported from toplev.c. */ |
| |
| extern const char *dump_base_name; |
| |
| static int flag_typed_selectors; |
| |
| /* Store all constructed constant strings in a hash table so that |
| they get uniqued properly. */ |
| |
| struct string_descriptor GTY(()) |
| { |
| /* The literal argument . */ |
| tree literal; |
| |
| /* The resulting constant string. */ |
| tree constructor; |
| }; |
| |
| static GTY((param_is (struct string_descriptor))) htab_t string_htab; |
| |
| /* Store the EH-volatilized types in a hash table, for easy retrieval. */ |
| struct volatilized_type GTY(()) |
| { |
| tree type; |
| }; |
| |
| static GTY((param_is (struct volatilized_type))) htab_t volatilized_htab; |
| |
| FILE *gen_declaration_file; |
| |
| /* Tells "encode_pointer/encode_aggregate" whether we are generating |
| type descriptors for instance variables (as opposed to methods). |
| Type descriptors for instance variables contain more information |
| than methods (for static typing and embedded structures). */ |
| |
| static int generating_instance_variables = 0; |
| |
| /* Some platforms pass small structures through registers versus |
| through an invisible pointer. Determine at what size structure is |
| the transition point between the two possibilities. */ |
| |
| static void |
| generate_struct_by_value_array (void) |
| { |
| tree type; |
| tree field_decl, field_decl_chain; |
| int i, j; |
| int aggregate_in_mem[32]; |
| int found = 0; |
| |
| /* Presumably no platform passes 32 byte structures in a register. */ |
| for (i = 1; i < 32; i++) |
| { |
| char buffer[5]; |
| |
| /* Create an unnamed struct that has `i' character components */ |
| type = start_struct (RECORD_TYPE, NULL_TREE); |
| |
| strcpy (buffer, "c1"); |
| field_decl = create_field_decl (char_type_node, |
| buffer); |
| field_decl_chain = field_decl; |
| |
| for (j = 1; j < i; j++) |
| { |
| sprintf (buffer, "c%d", j + 1); |
| field_decl = create_field_decl (char_type_node, |
| buffer); |
| chainon (field_decl_chain, field_decl); |
| } |
| finish_struct (type, field_decl_chain, NULL_TREE); |
| |
| aggregate_in_mem[i] = aggregate_value_p (type, 0); |
| if (!aggregate_in_mem[i]) |
| found = 1; |
| } |
| |
| /* We found some structures that are returned in registers instead of memory |
| so output the necessary data. */ |
| if (found) |
| { |
| for (i = 31; i >= 0; i--) |
| if (!aggregate_in_mem[i]) |
| break; |
| printf ("#define OBJC_MAX_STRUCT_BY_VALUE %d\n\n", i); |
| |
| /* The first member of the structure is always 0 because we don't handle |
| structures with 0 members */ |
| printf ("static int struct_forward_array[] = {\n 0"); |
| |
| for (j = 1; j <= i; j++) |
| printf (", %d", aggregate_in_mem[j]); |
| printf ("\n};\n"); |
| } |
| |
| exit (0); |
| } |
| |
| bool |
| objc_init (void) |
| { |
| #ifdef OBJCPLUS |
| if (cxx_init () == false) |
| #else |
| if (c_objc_common_init () == false) |
| #endif |
| return false; |
| |
| /* If gen_declaration desired, open the output file. */ |
| if (flag_gen_declaration) |
| { |
| register char * const dumpname = concat (dump_base_name, ".decl", NULL); |
| gen_declaration_file = fopen (dumpname, "w"); |
| if (gen_declaration_file == 0) |
| fatal_error ("can't open %s: %m", dumpname); |
| free (dumpname); |
| } |
| |
| if (flag_next_runtime) |
| { |
| TAG_GETCLASS = "objc_getClass"; |
| TAG_GETMETACLASS = "objc_getMetaClass"; |
| TAG_MSGSEND = "objc_msgSend"; |
| TAG_MSGSENDSUPER = "objc_msgSendSuper"; |
| TAG_MSGSEND_STRET = "objc_msgSend_stret"; |
| TAG_MSGSENDSUPER_STRET = "objc_msgSendSuper_stret"; |
| default_constant_string_class_name = "NSConstantString"; |
| } |
| else |
| { |
| TAG_GETCLASS = "objc_get_class"; |
| TAG_GETMETACLASS = "objc_get_meta_class"; |
| TAG_MSGSEND = "objc_msg_lookup"; |
| TAG_MSGSENDSUPER = "objc_msg_lookup_super"; |
| /* GNU runtime does not provide special functions to support |
| structure-returning methods. */ |
| default_constant_string_class_name = "NXConstantString"; |
| flag_typed_selectors = 1; |
| } |
| |
| init_objc (); |
| |
| if (print_struct_values) |
| generate_struct_by_value_array (); |
| |
| return true; |
| } |
| |
| void |
| objc_finish_file (void) |
| { |
| mark_referenced_methods (); |
| |
| #ifdef OBJCPLUS |
| /* We need to instantiate templates _before_ we emit ObjC metadata; |
| if we do not, some metadata (such as selectors) may go missing. */ |
| at_eof = 1; |
| instantiate_pending_templates (0); |
| #endif |
| |
| /* Finalize Objective-C runtime data. No need to generate tables |
| and code if only checking syntax, or if generating a PCH file. */ |
| if (!flag_syntax_only && !pch_file) |
| finish_objc (); |
| |
| if (gen_declaration_file) |
| fclose (gen_declaration_file); |
| } |
| |
| /* Return the first occurrence of a method declaration corresponding |
| to sel_name in rproto_list. Search rproto_list recursively. |
| If is_class is 0, search for instance methods, otherwise for class |
| methods. */ |
| static tree |
| lookup_method_in_protocol_list (tree rproto_list, tree sel_name, |
| int is_class) |
| { |
| tree rproto, p; |
| tree fnd = 0; |
| |
| for (rproto = rproto_list; rproto; rproto = TREE_CHAIN (rproto)) |
| { |
| p = TREE_VALUE (rproto); |
| |
| if (TREE_CODE (p) == PROTOCOL_INTERFACE_TYPE) |
| { |
| if ((fnd = lookup_method (is_class |
| ? PROTOCOL_CLS_METHODS (p) |
| : PROTOCOL_NST_METHODS (p), sel_name))) |
| ; |
| else if (PROTOCOL_LIST (p)) |
| fnd = lookup_method_in_protocol_list (PROTOCOL_LIST (p), |
| sel_name, is_class); |
| } |
| else |
| { |
| ; /* An identifier...if we could not find a protocol. */ |
| } |
| |
| if (fnd) |
| return fnd; |
| } |
| |
| return 0; |
| } |
| |
| static tree |
| lookup_protocol_in_reflist (tree rproto_list, tree lproto) |
| { |
| tree rproto, p; |
| |
| /* Make sure the protocol is supported by the object on the rhs. */ |
| if (TREE_CODE (lproto) == PROTOCOL_INTERFACE_TYPE) |
| { |
| tree fnd = 0; |
| for (rproto = rproto_list; rproto; rproto = TREE_CHAIN (rproto)) |
| { |
| p = TREE_VALUE (rproto); |
| |
| if (TREE_CODE (p) == PROTOCOL_INTERFACE_TYPE) |
| { |
| if (lproto == p) |
| fnd = lproto; |
| |
| else if (PROTOCOL_LIST (p)) |
| fnd = lookup_protocol_in_reflist (PROTOCOL_LIST (p), lproto); |
| } |
| |
| if (fnd) |
| return fnd; |
| } |
| } |
| else |
| { |
| ; /* An identifier...if we could not find a protocol. */ |
| } |
| |
| return 0; |
| } |
| |
| void |
| objc_start_class_interface (tree klass, tree super_class, tree protos) |
| { |
| objc_interface_context |
| = objc_ivar_context |
| = start_class (CLASS_INTERFACE_TYPE, klass, super_class, protos); |
| objc_public_flag = 0; |
| } |
| |
| void |
| objc_start_category_interface (tree klass, tree categ, tree protos) |
| { |
| objc_interface_context |
| = start_class (CATEGORY_INTERFACE_TYPE, klass, categ, protos); |
| objc_ivar_chain |
| = continue_class (objc_interface_context); |
| } |
| |
| void |
| objc_start_protocol (tree name, tree protos) |
| { |
| objc_interface_context |
| = start_protocol (PROTOCOL_INTERFACE_TYPE, name, protos); |
| } |
| |
| void |
| objc_continue_interface (void) |
| { |
| objc_ivar_chain |
| = continue_class (objc_interface_context); |
| } |
| |
| void |
| objc_finish_interface (void) |
| { |
| finish_class (objc_interface_context); |
| objc_interface_context = NULL_TREE; |
| } |
| |
| void |
| objc_start_class_implementation (tree klass, tree super_class) |
| { |
| objc_implementation_context |
| = objc_ivar_context |
| = start_class (CLASS_IMPLEMENTATION_TYPE, klass, super_class, NULL_TREE); |
| objc_public_flag = 0; |
| } |
| |
| void |
| objc_start_category_implementation (tree klass, tree categ) |
| { |
| objc_implementation_context |
| = start_class (CATEGORY_IMPLEMENTATION_TYPE, klass, categ, NULL_TREE); |
| objc_ivar_chain |
| = continue_class (objc_implementation_context); |
| } |
| |
| void |
| objc_continue_implementation (void) |
| { |
| objc_ivar_chain |
| = continue_class (objc_implementation_context); |
| } |
| |
| void |
| objc_finish_implementation (void) |
| { |
| #ifdef OBJCPLUS |
| if (flag_objc_call_cxx_cdtors) |
| objc_generate_cxx_cdtors (); |
| #endif |
| |
| if (objc_implementation_context) |
| { |
| finish_class (objc_implementation_context); |
| objc_ivar_chain = NULL_TREE; |
| objc_implementation_context = NULL_TREE; |
| } |
| else |
| warning (0, "%<@end%> must appear in an @implementation context"); |
| } |
| |
| void |
| objc_set_visibility (int visibility) |
| { |
| objc_public_flag = visibility; |
| } |
| |
| void |
| objc_set_method_type (enum tree_code type) |
| { |
| objc_inherit_code = (type == PLUS_EXPR |
| ? CLASS_METHOD_DECL |
| : INSTANCE_METHOD_DECL); |
| } |
| |
| tree |
| objc_build_method_signature (tree rettype, tree selector, |
| tree optparms, bool ellipsis) |
| { |
| return build_method_decl (objc_inherit_code, rettype, selector, |
| optparms, ellipsis); |
| } |
| |
| void |
| objc_add_method_declaration (tree decl) |
| { |
| if (!objc_interface_context) |
| fatal_error ("method declaration not in @interface context"); |
| |
| objc_add_method (objc_interface_context, |
| decl, |
| objc_inherit_code == CLASS_METHOD_DECL); |
| } |
| |
| void |
| objc_start_method_definition (tree decl) |
| { |
| if (!objc_implementation_context) |
| fatal_error ("method definition not in @implementation context"); |
| |
| objc_add_method (objc_implementation_context, |
| decl, |
| objc_inherit_code == CLASS_METHOD_DECL); |
| start_method_def (decl); |
| } |
| |
| void |
| objc_add_instance_variable (tree decl) |
| { |
| (void) add_instance_variable (objc_ivar_context, |
| objc_public_flag, |
| decl); |
| } |
| |
| /* Return 1 if IDENT is an ObjC/ObjC++ reserved keyword in the context of |
| an '@'. */ |
| |
| int |
| objc_is_reserved_word (tree ident) |
| { |
| unsigned char code = C_RID_CODE (ident); |
| |
| return (OBJC_IS_AT_KEYWORD (code) |
| || code == RID_CLASS || code == RID_PUBLIC |
| || code == RID_PROTECTED || code == RID_PRIVATE |
| || code == RID_TRY || code == RID_THROW || code == RID_CATCH); |
| } |
| |
| /* Return true if TYPE is 'id'. */ |
| |
| static bool |
| objc_is_object_id (tree type) |
| { |
| return OBJC_TYPE_NAME (type) == objc_object_id; |
| } |
| |
| static bool |
| objc_is_class_id (tree type) |
| { |
| return OBJC_TYPE_NAME (type) == objc_class_id; |
| } |
| |
| /* Construct a C struct with same name as KLASS, a base struct with tag |
| SUPER_NAME (if any), and FIELDS indicated. */ |
| |
| static tree |
| objc_build_struct (tree klass, tree fields, tree super_name) |
| { |
| tree name = CLASS_NAME (klass); |
| tree s = start_struct (RECORD_TYPE, name); |
| tree super = (super_name ? xref_tag (RECORD_TYPE, super_name) : NULL_TREE); |
| tree t, objc_info = NULL_TREE; |
| |
| if (super) |
| { |
| /* Prepend a packed variant of the base class into the layout. This |
| is necessary to preserve ObjC ABI compatibility. */ |
| tree base = build_decl (FIELD_DECL, NULL_TREE, super); |
| tree field = TYPE_FIELDS (super); |
| |
| while (field && TREE_CHAIN (field) |
| && TREE_CODE (TREE_CHAIN (field)) == FIELD_DECL) |
| field = TREE_CHAIN (field); |
| |
| /* For ObjC ABI purposes, the "packed" size of a base class is |
| the sum of the offset and the size (in bits) of the last field |
| in the class. */ |
| DECL_SIZE (base) |
| = (field && TREE_CODE (field) == FIELD_DECL |
| ? size_binop (PLUS_EXPR, |
| size_binop (PLUS_EXPR, |
| size_binop |
| (MULT_EXPR, |
| convert (bitsizetype, |
| DECL_FIELD_OFFSET (field)), |
| bitsize_int (BITS_PER_UNIT)), |
| DECL_FIELD_BIT_OFFSET (field)), |
| DECL_SIZE (field)) |
| : bitsize_zero_node); |
| DECL_SIZE_UNIT (base) |
| = size_binop (FLOOR_DIV_EXPR, convert (sizetype, DECL_SIZE (base)), |
| size_int (BITS_PER_UNIT)); |
| DECL_ARTIFICIAL (base) = 1; |
| DECL_ALIGN (base) = 1; |
| DECL_FIELD_CONTEXT (base) = s; |
| #ifdef OBJCPLUS |
| DECL_FIELD_IS_BASE (base) = 1; |
| |
| if (fields) |
| TREE_NO_WARNING (fields) = 1; /* Suppress C++ ABI warnings -- we */ |
| #endif /* are following the ObjC ABI here. */ |
| TREE_CHAIN (base) = fields; |
| fields = base; |
| } |
| |
| /* NB: Calling finish_struct() may cause type TYPE_LANG_SPECIFIC fields |
| in all variants of this RECORD_TYPE to be clobbered, but it is therein |
| that we store protocol conformance info (e.g., 'NSObject <MyProtocol>'). |
| Hence, we must squirrel away the ObjC-specific information before calling |
| finish_struct(), and then reinstate it afterwards. */ |
| |
| for (t = TYPE_NEXT_VARIANT (s); t; t = TYPE_NEXT_VARIANT (t)) |
| objc_info |
| = chainon (objc_info, |
| build_tree_list (NULL_TREE, TYPE_OBJC_INFO (t))); |
| |
| /* Point the struct at its related Objective-C class. */ |
| INIT_TYPE_OBJC_INFO (s); |
| TYPE_OBJC_INTERFACE (s) = klass; |
| |
| s = finish_struct (s, fields, NULL_TREE); |
| |
| for (t = TYPE_NEXT_VARIANT (s); t; |
| t = TYPE_NEXT_VARIANT (t), objc_info = TREE_CHAIN (objc_info)) |
| { |
| TYPE_OBJC_INFO (t) = TREE_VALUE (objc_info); |
| /* Replace the IDENTIFIER_NODE with an actual @interface. */ |
| TYPE_OBJC_INTERFACE (t) = klass; |
| } |
| |
| /* Use TYPE_BINFO structures to point at the super class, if any. */ |
| objc_xref_basetypes (s, super); |
| |
| /* Mark this struct as a class template. */ |
| CLASS_STATIC_TEMPLATE (klass) = s; |
| |
| return s; |
| } |
| |
| /* Build a type differing from TYPE only in that TYPE_VOLATILE is set. |
| Unlike tree.c:build_qualified_type(), preserve TYPE_LANG_SPECIFIC in the |
| process. */ |
| static tree |
| objc_build_volatilized_type (tree type) |
| { |
| tree t; |
| |
| /* Check if we have not constructed the desired variant already. */ |
| for (t = TYPE_MAIN_VARIANT (type); t; t = TYPE_NEXT_VARIANT (t)) |
| { |
| /* The type qualifiers must (obviously) match up. */ |
| if (!TYPE_VOLATILE (t) |
| || (TYPE_READONLY (t) != TYPE_READONLY (type)) |
| || (TYPE_RESTRICT (t) != TYPE_RESTRICT (type))) |
| continue; |
| |
| /* For pointer types, the pointees (and hence their TYPE_LANG_SPECIFIC |
| info, if any) must match up. */ |
| if (POINTER_TYPE_P (t) |
| && (TREE_TYPE (t) != TREE_TYPE (type))) |
| continue; |
| |
| /* Everything matches up! */ |
| return t; |
| } |
| |
| /* Ok, we could not re-use any of the pre-existing variants. Create |
| a new one. */ |
| t = build_variant_type_copy (type); |
| TYPE_VOLATILE (t) = 1; |
| |
| /* Set up the canonical type information. */ |
| if (TYPE_STRUCTURAL_EQUALITY_P (type)) |
| SET_TYPE_STRUCTURAL_EQUALITY (t); |
| else if (TYPE_CANONICAL (type) != type) |
| TYPE_CANONICAL (t) = objc_build_volatilized_type (TYPE_CANONICAL (type)); |
| else |
| TYPE_CANONICAL (t) = t; |
| |
| return t; |
| } |
| |
| /* Mark DECL as being 'volatile' for purposes of Darwin |
| _setjmp()/_longjmp() exception handling. Called from |
| objc_mark_locals_volatile(). */ |
| void |
| objc_volatilize_decl (tree decl) |
| { |
| /* Do not mess with variables that are 'static' or (already) |
| 'volatile'. */ |
| if (!TREE_THIS_VOLATILE (decl) && !TREE_STATIC (decl) |
| && (TREE_CODE (decl) == VAR_DECL |
| || TREE_CODE (decl) == PARM_DECL)) |
| { |
| tree t = TREE_TYPE (decl); |
| struct volatilized_type key; |
| void **loc; |
| |
| t = objc_build_volatilized_type (t); |
| key.type = t; |
| loc = htab_find_slot (volatilized_htab, &key, INSERT); |
| |
| if (!*loc) |
| { |
| *loc = ggc_alloc (sizeof (key)); |
| ((struct volatilized_type *) *loc)->type = t; |
| } |
| |
| TREE_TYPE (decl) = t; |
| TREE_THIS_VOLATILE (decl) = 1; |
| TREE_SIDE_EFFECTS (decl) = 1; |
| DECL_REGISTER (decl) = 0; |
| #ifndef OBJCPLUS |
| C_DECL_REGISTER (decl) = 0; |
| #endif |
| } |
| } |
| |
| /* Check if protocol PROTO is adopted (directly or indirectly) by class CLS |
| (including its categories and superclasses) or by object type TYP. |
| Issue a warning if PROTO is not adopted anywhere and WARN is set. */ |
| |
| static bool |
| objc_lookup_protocol (tree proto, tree cls, tree typ, bool warn) |
| { |
| bool class_type = (cls != NULL_TREE); |
| |
| while (cls) |
| { |
| tree c; |
| |
| /* Check protocols adopted by the class and its categories. */ |
| for (c = cls; c; c = CLASS_CATEGORY_LIST (c)) |
| { |
| if (lookup_protocol_in_reflist (CLASS_PROTOCOL_LIST (c), proto)) |
| return true; |
| } |
| |
| /* Repeat for superclasses. */ |
| cls = lookup_interface (CLASS_SUPER_NAME (cls)); |
| } |
| |
| /* Check for any protocols attached directly to the object type. */ |
| if (TYPE_HAS_OBJC_INFO (typ)) |
| { |
| if (lookup_protocol_in_reflist (TYPE_OBJC_PROTOCOL_LIST (typ), proto)) |
| return true; |
| } |
| |
| if (warn) |
| { |
| strcpy (errbuf, class_type ? "class \'" : "type \'"); |
| gen_type_name_0 (class_type ? typ : TYPE_POINTER_TO (typ)); |
| strcat (errbuf, "\' does not "); |
| /* NB: Types 'id' and 'Class' cannot reasonably be described as |
| "implementing" a given protocol, since they do not have an |
| implementation. */ |
| strcat (errbuf, class_type ? "implement" : "conform to"); |
| strcat (errbuf, " the \'"); |
| strcat (errbuf, IDENTIFIER_POINTER (PROTOCOL_NAME (proto))); |
| strcat (errbuf, "\' protocol"); |
| warning (0, errbuf); |
| } |
| |
| return false; |
| } |
| |
| /* Check if class RCLS and instance struct type RTYP conform to at least the |
| same protocols that LCLS and LTYP conform to. */ |
| |
| static bool |
| objc_compare_protocols (tree lcls, tree ltyp, tree rcls, tree rtyp, bool warn) |
| { |
| tree p; |
| bool have_lproto = false; |
| |
| while (lcls) |
| { |
| /* NB: We do _not_ look at categories defined for LCLS; these may or |
| may not get loaded in, and therefore it is unreasonable to require |
| that RCLS/RTYP must implement any of their protocols. */ |
| for (p = CLASS_PROTOCOL_LIST (lcls); p; p = TREE_CHAIN (p)) |
| { |
| have_lproto = true; |
| |
| if (!objc_lookup_protocol (TREE_VALUE (p), rcls, rtyp, warn)) |
| return warn; |
| } |
| |
| /* Repeat for superclasses. */ |
| lcls = lookup_interface (CLASS_SUPER_NAME (lcls)); |
| } |
| |
| /* Check for any protocols attached directly to the object type. */ |
| if (TYPE_HAS_OBJC_INFO (ltyp)) |
| { |
| for (p = TYPE_OBJC_PROTOCOL_LIST (ltyp); p; p = TREE_CHAIN (p)) |
| { |
| have_lproto = true; |
| |
| if (!objc_lookup_protocol (TREE_VALUE (p), rcls, rtyp, warn)) |
| return warn; |
| } |
| } |
| |
| /* NB: If LTYP and LCLS have no protocols to search for, return 'true' |
| vacuously, _unless_ RTYP is a protocol-qualified 'id'. We can get |
| away with simply checking for 'id' or 'Class' (!RCLS), since this |
| routine will not get called in other cases. */ |
| return have_lproto || (rcls != NULL_TREE); |
| } |
| |
| /* Determine if it is permissible to assign (if ARGNO is greater than -3) |
| an instance of RTYP to an instance of LTYP or to compare the two |
| (if ARGNO is equal to -3), per ObjC type system rules. Before |
| returning 'true', this routine may issue warnings related to, e.g., |
| protocol conformance. When returning 'false', the routine must |
| produce absolutely no warnings; the C or C++ front-end will do so |
| instead, if needed. If either LTYP or RTYP is not an Objective-C type, |
| the routine must return 'false'. |
| |
| The ARGNO parameter is encoded as follows: |
| >= 1 Parameter number (CALLEE contains function being called); |
| 0 Return value; |
| -1 Assignment; |
| -2 Initialization; |
| -3 Comparison (LTYP and RTYP may match in either direction). */ |
| |
| bool |
| objc_compare_types (tree ltyp, tree rtyp, int argno, tree callee) |
| { |
| tree lcls, rcls, lproto, rproto; |
| bool pointers_compatible; |
| |
| /* We must be dealing with pointer types */ |
| if (!POINTER_TYPE_P (ltyp) || !POINTER_TYPE_P (rtyp)) |
| return false; |
| |
| do |
| { |
| ltyp = TREE_TYPE (ltyp); /* Remove indirections. */ |
| rtyp = TREE_TYPE (rtyp); |
| } |
| while (POINTER_TYPE_P (ltyp) && POINTER_TYPE_P (rtyp)); |
| |
| /* Past this point, we are only interested in ObjC class instances, |
| or 'id' or 'Class'. */ |
| if (TREE_CODE (ltyp) != RECORD_TYPE || TREE_CODE (rtyp) != RECORD_TYPE) |
| return false; |
| |
| if (!objc_is_object_id (ltyp) && !objc_is_class_id (ltyp) |
| && !TYPE_HAS_OBJC_INFO (ltyp)) |
| return false; |
| |
| if (!objc_is_object_id (rtyp) && !objc_is_class_id (rtyp) |
| && !TYPE_HAS_OBJC_INFO (rtyp)) |
| return false; |
| |
| /* Past this point, we are committed to returning 'true' to the caller. |
| However, we can still warn about type and/or protocol mismatches. */ |
| |
| if (TYPE_HAS_OBJC_INFO (ltyp)) |
| { |
| lcls = TYPE_OBJC_INTERFACE (ltyp); |
| lproto = TYPE_OBJC_PROTOCOL_LIST (ltyp); |
| } |
| else |
| lcls = lproto = NULL_TREE; |
| |
| if (TYPE_HAS_OBJC_INFO (rtyp)) |
| { |
| rcls = TYPE_OBJC_INTERFACE (rtyp); |
| rproto = TYPE_OBJC_PROTOCOL_LIST (rtyp); |
| } |
| else |
| rcls = rproto = NULL_TREE; |
| |
| /* If we could not find an @interface declaration, we must have |
| only seen a @class declaration; for purposes of type comparison, |
| treat it as a stand-alone (root) class. */ |
| |
| if (lcls && TREE_CODE (lcls) == IDENTIFIER_NODE) |
| lcls = NULL_TREE; |
| |
| if (rcls && TREE_CODE (rcls) == IDENTIFIER_NODE) |
| rcls = NULL_TREE; |
| |
| /* If either type is an unqualified 'id', we're done. */ |
| if ((!lproto && objc_is_object_id (ltyp)) |
| || (!rproto && objc_is_object_id (rtyp))) |
| return true; |
| |
| pointers_compatible = (TYPE_MAIN_VARIANT (ltyp) == TYPE_MAIN_VARIANT (rtyp)); |
| |
| /* If the underlying types are the same, and at most one of them has |
| a protocol list, we do not need to issue any diagnostics. */ |
| if (pointers_compatible && (!lproto || !rproto)) |
| return true; |
| |
| /* If exactly one of the types is 'Class', issue a diagnostic; any |
| exceptions of this rule have already been handled. */ |
| if (objc_is_class_id (ltyp) ^ objc_is_class_id (rtyp)) |
| pointers_compatible = false; |
| /* Otherwise, check for inheritance relations. */ |
| else |
| { |
| if (!pointers_compatible) |
| pointers_compatible |
| = (objc_is_object_id (ltyp) || objc_is_object_id (rtyp)); |
| |
| if (!pointers_compatible) |
| pointers_compatible = DERIVED_FROM_P (ltyp, rtyp); |
| |
| if (!pointers_compatible && argno == -3) |
| pointers_compatible = DERIVED_FROM_P (rtyp, ltyp); |
| } |
| |
| /* If the pointers match modulo protocols, check for protocol conformance |
| mismatches. */ |
| if (pointers_compatible) |
| { |
| pointers_compatible = objc_compare_protocols (lcls, ltyp, rcls, rtyp, |
| argno != -3); |
| |
| if (!pointers_compatible && argno == -3) |
| pointers_compatible = objc_compare_protocols (rcls, rtyp, lcls, ltyp, |
| argno != -3); |
| } |
| |
| if (!pointers_compatible) |
| { |
| /* NB: For the time being, we shall make our warnings look like their |
| C counterparts. In the future, we may wish to make them more |
| ObjC-specific. */ |
| switch (argno) |
| { |
| case -3: |
| warning (0, "comparison of distinct Objective-C types lacks a cast"); |
| break; |
| |
| case -2: |
| warning (0, "initialization from distinct Objective-C type"); |
| break; |
| |
| case -1: |
| warning (0, "assignment from distinct Objective-C type"); |
| break; |
| |
| case 0: |
| warning (0, "distinct Objective-C type in return"); |
| break; |
| |
| default: |
| warning (0, "passing argument %d of %qE from distinct " |
| "Objective-C type", argno, callee); |
| break; |
| } |
| } |
| |
| return true; |
| } |
| |
| /* Check if LTYP and RTYP have the same type qualifiers. If either type |
| lives in the volatilized hash table, ignore the 'volatile' bit when |
| making the comparison. */ |
| |
| bool |
| objc_type_quals_match (tree ltyp, tree rtyp) |
| { |
| int lquals = TYPE_QUALS (ltyp), rquals = TYPE_QUALS (rtyp); |
| struct volatilized_type key; |
| |
| key.type = ltyp; |
| |
| if (htab_find_slot (volatilized_htab, &key, NO_INSERT)) |
| lquals &= ~TYPE_QUAL_VOLATILE; |
| |
| key.type = rtyp; |
| |
| if (htab_find_slot (volatilized_htab, &key, NO_INSERT)) |
| rquals &= ~TYPE_QUAL_VOLATILE; |
| |
| return (lquals == rquals); |
| } |
| |
| #ifndef OBJCPLUS |
| /* Determine if CHILD is derived from PARENT. The routine assumes that |
| both parameters are RECORD_TYPEs, and is non-reflexive. */ |
| |
| static bool |
| objc_derived_from_p (tree parent, tree child) |
| { |
| parent = TYPE_MAIN_VARIANT (parent); |
| |
| for (child = TYPE_MAIN_VARIANT (child); |
| TYPE_BINFO (child) && BINFO_N_BASE_BINFOS (TYPE_BINFO (child));) |
| { |
| child = TYPE_MAIN_VARIANT (BINFO_TYPE (BINFO_BASE_BINFO |
| (TYPE_BINFO (child), |
| 0))); |
| |
| if (child == parent) |
| return true; |
| } |
| |
| return false; |
| } |
| #endif |
| |
| static tree |
| objc_build_component_ref (tree datum, tree component) |
| { |
| /* If COMPONENT is NULL, the caller is referring to the anonymous |
| base class field. */ |
| if (!component) |
| { |
| tree base = TYPE_FIELDS (TREE_TYPE (datum)); |
| |
| return build3 (COMPONENT_REF, TREE_TYPE (base), datum, base, NULL_TREE); |
| } |
| |
| /* The 'build_component_ref' routine has been removed from the C++ |
| front-end, but 'finish_class_member_access_expr' seems to be |
| a worthy substitute. */ |
| #ifdef OBJCPLUS |
| return finish_class_member_access_expr (datum, component, false, |
| tf_warning_or_error); |
| #else |
| return build_component_ref (datum, component); |
| #endif |
| } |
| |
| /* Recursively copy inheritance information rooted at BINFO. To do this, |
| we emulate the song and dance performed by cp/tree.c:copy_binfo(). */ |
| |
| static tree |
| objc_copy_binfo (tree binfo) |
| { |
| tree btype = BINFO_TYPE (binfo); |
| tree binfo2 = make_tree_binfo (BINFO_N_BASE_BINFOS (binfo)); |
| tree base_binfo; |
| int ix; |
| |
| BINFO_TYPE (binfo2) = btype; |
| BINFO_OFFSET (binfo2) = BINFO_OFFSET (binfo); |
| BINFO_BASE_ACCESSES (binfo2) = BINFO_BASE_ACCESSES (binfo); |
| |
| /* Recursively copy base binfos of BINFO. */ |
| for (ix = 0; BINFO_BASE_ITERATE (binfo, ix, base_binfo); ix++) |
| { |
| tree base_binfo2 = objc_copy_binfo (base_binfo); |
| |
| BINFO_INHERITANCE_CHAIN (base_binfo2) = binfo2; |
| BINFO_BASE_APPEND (binfo2, base_binfo2); |
| } |
| |
| return binfo2; |
| } |
| |
| /* Record superclass information provided in BASETYPE for ObjC class REF. |
| This is loosely based on cp/decl.c:xref_basetypes(). */ |
| |
| static void |
| objc_xref_basetypes (tree ref, tree basetype) |
| { |
| tree binfo = make_tree_binfo (basetype ? 1 : 0); |
| |
| TYPE_BINFO (ref) = binfo; |
| BINFO_OFFSET (binfo) = size_zero_node; |
| BINFO_TYPE (binfo) = ref; |
| |
| if (basetype) |
| { |
| tree base_binfo = objc_copy_binfo (TYPE_BINFO (basetype)); |
| |
| BINFO_INHERITANCE_CHAIN (base_binfo) = binfo; |
| BINFO_BASE_ACCESSES (binfo) = VEC_alloc (tree, gc, 1); |
| BINFO_BASE_APPEND (binfo, base_binfo); |
| BINFO_BASE_ACCESS_APPEND (binfo, access_public_node); |
| } |
| } |
| |
| static hashval_t |
| volatilized_hash (const void *ptr) |
| { |
| const_tree const typ = ((const struct volatilized_type *)ptr)->type; |
| |
| return htab_hash_pointer(typ); |
| } |
| |
| static int |
| volatilized_eq (const void *ptr1, const void *ptr2) |
| { |
| const_tree const typ1 = ((const struct volatilized_type *)ptr1)->type; |
| const_tree const typ2 = ((const struct volatilized_type *)ptr2)->type; |
| |
| return typ1 == typ2; |
| } |
| |
| /* Called from finish_decl. */ |
| |
| void |
| objc_check_decl (tree decl) |
| { |
| tree type = TREE_TYPE (decl); |
| |
| if (TREE_CODE (type) != RECORD_TYPE) |
| return; |
| if (OBJC_TYPE_NAME (type) && (type = objc_is_class_name (OBJC_TYPE_NAME (type)))) |
| error ("statically allocated instance of Objective-C class %qs", |
| IDENTIFIER_POINTER (type)); |
| } |
| |
| /* Construct a PROTOCOLS-qualified variant of INTERFACE, where INTERFACE may |
| either name an Objective-C class, or refer to the special 'id' or 'Class' |
| types. If INTERFACE is not a valid ObjC type, just return it unchanged. */ |
| |
| tree |
| objc_get_protocol_qualified_type (tree interface, tree protocols) |
| { |
| /* If INTERFACE is not provided, default to 'id'. */ |
| tree type = (interface ? objc_is_id (interface) : objc_object_type); |
| bool is_ptr = (type != NULL_TREE); |
| |
| if (!is_ptr) |
| { |
| type = objc_is_class_name (interface); |
| |
| if (type) |
| type = xref_tag (RECORD_TYPE, type); |
| else |
| return interface; |
| } |
| |
| if (protocols) |
| { |
| type = build_variant_type_copy (type); |
| |
| /* For pointers (i.e., 'id' or 'Class'), attach the protocol(s) |
| to the pointee. */ |
| if (is_ptr) |
| { |
| tree orig_pointee_type = TREE_TYPE (type); |
| TREE_TYPE (type) = build_variant_type_copy (orig_pointee_type); |
| |
| /* Set up the canonical type information. */ |
| TYPE_CANONICAL (type) |
| = TYPE_CANONICAL (TYPE_POINTER_TO (orig_pointee_type)); |
| |
| TYPE_POINTER_TO (TREE_TYPE (type)) = type; |
| type = TREE_TYPE (type); |
| } |
| |
| /* Look up protocols and install in lang specific list. */ |
| DUP_TYPE_OBJC_INFO (type, TYPE_MAIN_VARIANT (type)); |
| TYPE_OBJC_PROTOCOL_LIST (type) = lookup_and_install_protocols (protocols); |
| |
| /* For RECORD_TYPEs, point to the @interface; for 'id' and 'Class', |
| return the pointer to the new pointee variant. */ |
| if (is_ptr) |
| type = TYPE_POINTER_TO (type); |
| else |
| TYPE_OBJC_INTERFACE (type) |
| = TYPE_OBJC_INTERFACE (TYPE_MAIN_VARIANT (type)); |
| } |
| |
| return type; |
| } |
| |
| /* Check for circular dependencies in protocols. The arguments are |
| PROTO, the protocol to check, and LIST, a list of protocol it |
| conforms to. */ |
| |
| static void |
| check_protocol_recursively (tree proto, tree list) |
| { |
| tree p; |
| |
| for (p = list; p; p = TREE_CHAIN (p)) |
| { |
| tree pp = TREE_VALUE (p); |
| |
| if (TREE_CODE (pp) == IDENTIFIER_NODE) |
| pp = lookup_protocol (pp); |
| |
| if (pp == proto) |
| fatal_error ("protocol %qs has circular dependency", |
| IDENTIFIER_POINTER (PROTOCOL_NAME (pp))); |
| if (pp) |
| check_protocol_recursively (proto, PROTOCOL_LIST (pp)); |
| } |
| } |
| |
| /* Look up PROTOCOLS, and return a list of those that are found. |
| If none are found, return NULL. */ |
| |
| static tree |
| lookup_and_install_protocols (tree protocols) |
| { |
| tree proto; |
| tree return_value = NULL_TREE; |
| |
| for (proto = protocols; proto; proto = TREE_CHAIN (proto)) |
| { |
| tree ident = TREE_VALUE (proto); |
| tree p = lookup_protocol (ident); |
| |
| if (p) |
| return_value = chainon (return_value, |
| build_tree_list (NULL_TREE, p)); |
| else if (ident != error_mark_node) |
| error ("cannot find protocol declaration for %qs", |
| IDENTIFIER_POINTER (ident)); |
| } |
| |
| return return_value; |
| } |
| |
| /* Create a declaration for field NAME of a given TYPE. */ |
| |
| static tree |
| create_field_decl (tree type, const char *name) |
| { |
| return build_decl (FIELD_DECL, get_identifier (name), type); |
| } |
| |
| /* Create a global, static declaration for variable NAME of a given TYPE. The |
| finish_var_decl() routine will need to be called on it afterwards. */ |
| |
| static tree |
| start_var_decl (tree type, const char *name) |
| { |
| tree var = build_decl (VAR_DECL, get_identifier (name), type); |
| |
| TREE_STATIC (var) = 1; |
| DECL_INITIAL (var) = error_mark_node; /* A real initializer is coming... */ |
| DECL_IGNORED_P (var) = 1; |
| DECL_ARTIFICIAL (var) = 1; |
| DECL_CONTEXT (var) = NULL_TREE; |
| #ifdef OBJCPLUS |
| DECL_THIS_STATIC (var) = 1; /* squash redeclaration errors */ |
| #endif |
| |
| return var; |
| } |
| |
| /* Finish off the variable declaration created by start_var_decl(). */ |
| |
| static void |
| finish_var_decl (tree var, tree initializer) |
| { |
| finish_decl (var, initializer, NULL_TREE); |
| /* Ensure that the variable actually gets output. */ |
| mark_decl_referenced (var); |
| /* Mark the decl to avoid "defined but not used" warning. */ |
| TREE_USED (var) = 1; |
| } |
| |
| /* Find the decl for the constant string class reference. This is only |
| used for the NeXT runtime. */ |
| |
| static tree |
| setup_string_decl (void) |
| { |
| char *name; |
| size_t length; |
| |
| /* %s in format will provide room for terminating null */ |
| length = strlen (STRING_OBJECT_GLOBAL_FORMAT) |
| + strlen (constant_string_class_name); |
| name = XNEWVEC (char, length); |
| sprintf (name, STRING_OBJECT_GLOBAL_FORMAT, |
| constant_string_class_name); |
| constant_string_global_id = get_identifier (name); |
| string_class_decl = lookup_name (constant_string_global_id); |
| |
| return string_class_decl; |
| } |
| |
| /* Purpose: "play" parser, creating/installing representations |
| of the declarations that are required by Objective-C. |
| |
| Model: |
| |
| type_spec--------->sc_spec |
| (tree_list) (tree_list) |
| | | |
| | | |
| identifier_node identifier_node */ |
| |
| static void |
| synth_module_prologue (void) |
| { |
| tree type; |
| enum debug_info_type save_write_symbols = write_symbols; |
| const struct gcc_debug_hooks *const save_hooks = debug_hooks; |
| |
| /* Suppress outputting debug symbols, because |
| dbxout_init hasn't been called yet. */ |
| write_symbols = NO_DEBUG; |
| debug_hooks = &do_nothing_debug_hooks; |
| |
| #ifdef OBJCPLUS |
| push_lang_context (lang_name_c); /* extern "C" */ |
| #endif |
| |
| /* The following are also defined in <objc/objc.h> and friends. */ |
| |
| objc_object_id = get_identifier (TAG_OBJECT); |
| objc_class_id = get_identifier (TAG_CLASS); |
| |
| objc_object_reference = xref_tag (RECORD_TYPE, objc_object_id); |
| objc_class_reference = xref_tag (RECORD_TYPE, objc_class_id); |
| |
| objc_object_type = build_pointer_type (objc_object_reference); |
| objc_class_type = build_pointer_type (objc_class_reference); |
| |
| objc_object_name = get_identifier (OBJECT_TYPEDEF_NAME); |
| objc_class_name = get_identifier (CLASS_TYPEDEF_NAME); |
| |
| /* Declare the 'id' and 'Class' typedefs. */ |
| |
| type = lang_hooks.decls.pushdecl (build_decl (TYPE_DECL, |
| objc_object_name, |
| objc_object_type)); |
| TREE_NO_WARNING (type) = 1; |
| type = lang_hooks.decls.pushdecl (build_decl (TYPE_DECL, |
| objc_class_name, |
| objc_class_type)); |
| TREE_NO_WARNING (type) = 1; |
| |
| /* Forward-declare '@interface Protocol'. */ |
| |
| type = get_identifier (PROTOCOL_OBJECT_CLASS_NAME); |
| objc_declare_class (tree_cons (NULL_TREE, type, NULL_TREE)); |
| objc_protocol_type = build_pointer_type (xref_tag (RECORD_TYPE, |
| type)); |
| |
| /* Declare type of selector-objects that represent an operation name. */ |
| |
| if (flag_next_runtime) |
| /* `struct objc_selector *' */ |
| objc_selector_type |
| = build_pointer_type (xref_tag (RECORD_TYPE, |
| get_identifier (TAG_SELECTOR))); |
| else |
| /* `const struct objc_selector *' */ |
| objc_selector_type |
| = build_pointer_type |
| (build_qualified_type (xref_tag (RECORD_TYPE, |
| get_identifier (TAG_SELECTOR)), |
| TYPE_QUAL_CONST)); |
| |
| /* Declare receiver type used for dispatching messages to 'super'. */ |
| |
| /* `struct objc_super *' */ |
| objc_super_type = build_pointer_type (xref_tag (RECORD_TYPE, |
| get_identifier (TAG_SUPER))); |
| |
| /* Declare pointers to method and ivar lists. */ |
| objc_method_list_ptr = build_pointer_type |
| (xref_tag (RECORD_TYPE, |
| get_identifier (UTAG_METHOD_LIST))); |
| objc_method_proto_list_ptr |
| = build_pointer_type (xref_tag (RECORD_TYPE, |
| get_identifier (UTAG_METHOD_PROTOTYPE_LIST))); |
| objc_ivar_list_ptr = build_pointer_type |
| (xref_tag (RECORD_TYPE, |
| get_identifier (UTAG_IVAR_LIST))); |
| |
| /* TREE_NOTHROW is cleared for the message-sending functions, |
| because the function that gets called can throw in Obj-C++, or |
| could itself call something that can throw even in Obj-C. */ |
| |
| if (flag_next_runtime) |
| { |
| /* NB: In order to call one of the ..._stret (struct-returning) |
| functions, the function *MUST* first be cast to a signature that |
| corresponds to the actual ObjC method being invoked. This is |
| what is done by the build_objc_method_call() routine below. */ |
| |
| /* id objc_msgSend (id, SEL, ...); */ |
| /* id objc_msgSendNonNil (id, SEL, ...); */ |
| /* id objc_msgSend_stret (id, SEL, ...); */ |
| /* id objc_msgSendNonNil_stret (id, SEL, ...); */ |
| type |
| = build_function_type (objc_object_type, |
| tree_cons (NULL_TREE, objc_object_type, |
| tree_cons (NULL_TREE, objc_selector_type, |
| NULL_TREE))); |
| umsg_decl = add_builtin_function (TAG_MSGSEND, |
| type, 0, NOT_BUILT_IN, |
| NULL, NULL_TREE); |
| umsg_nonnil_decl = add_builtin_function (TAG_MSGSEND_NONNIL, |
| type, 0, NOT_BUILT_IN, |
| NULL, NULL_TREE); |
| umsg_stret_decl = add_builtin_function (TAG_MSGSEND_STRET, |
| type, 0, NOT_BUILT_IN, |
| NULL, NULL_TREE); |
| umsg_nonnil_stret_decl = add_builtin_function (TAG_MSGSEND_NONNIL_STRET, |
| type, 0, NOT_BUILT_IN, |
| NULL, NULL_TREE); |
| |
| /* These can throw, because the function that gets called can throw |
| in Obj-C++, or could itself call something that can throw even |
| in Obj-C. */ |
| TREE_NOTHROW (umsg_decl) = 0; |
| TREE_NOTHROW (umsg_nonnil_decl) = 0; |
| TREE_NOTHROW (umsg_stret_decl) = 0; |
| TREE_NOTHROW (umsg_nonnil_stret_decl) = 0; |
| |
| /* id objc_msgSend_Fast (id, SEL, ...) |
| __attribute__ ((hard_coded_address (OFFS_MSGSEND_FAST))); */ |
| #ifdef OFFS_MSGSEND_FAST |
| umsg_fast_decl = add_builtin_function (TAG_MSGSEND_FAST, |
| type, 0, NOT_BUILT_IN, |
| NULL, NULL_TREE); |
| TREE_NOTHROW (umsg_fast_decl) = 0; |
| DECL_ATTRIBUTES (umsg_fast_decl) |
| = tree_cons (get_identifier ("hard_coded_address"), |
| build_int_cst (NULL_TREE, OFFS_MSGSEND_FAST), |
| NULL_TREE); |
| #else |
| /* No direct dispatch available. */ |
| umsg_fast_decl = umsg_decl; |
| #endif |
| |
| /* id objc_msgSendSuper (struct objc_super *, SEL, ...); */ |
| /* id objc_msgSendSuper_stret (struct objc_super *, SEL, ...); */ |
| type |
| = build_function_type (objc_object_type, |
| tree_cons (NULL_TREE, objc_super_type, |
| tree_cons (NULL_TREE, objc_selector_type, |
| NULL_TREE))); |
| umsg_super_decl = add_builtin_function (TAG_MSGSENDSUPER, |
| type, 0, NOT_BUILT_IN, |
| NULL, NULL_TREE); |
| umsg_super_stret_decl = add_builtin_function (TAG_MSGSENDSUPER_STRET, |
| type, 0, NOT_BUILT_IN, 0, |
| NULL_TREE); |
| TREE_NOTHROW (umsg_super_decl) = 0; |
| TREE_NOTHROW (umsg_super_stret_decl) = 0; |
| } |
| else |
| { |
| /* GNU runtime messenger entry points. */ |
| |
| /* typedef id (*IMP)(id, SEL, ...); */ |
| tree IMP_type |
| = build_pointer_type |
| (build_function_type (objc_object_type, |
| tree_cons (NULL_TREE, objc_object_type, |
| tree_cons (NULL_TREE, objc_selector_type, |
| NULL_TREE)))); |
| |
| /* IMP objc_msg_lookup (id, SEL); */ |
| type |
| = build_function_type (IMP_type, |
| tree_cons (NULL_TREE, objc_object_type, |
| tree_cons (NULL_TREE, objc_selector_type, |
| OBJC_VOID_AT_END))); |
| umsg_decl = add_builtin_function (TAG_MSGSEND, |
| type, 0, NOT_BUILT_IN, |
| NULL, NULL_TREE); |
| TREE_NOTHROW (umsg_decl) = 0; |
| |
| /* IMP objc_msg_lookup_super (struct objc_super *, SEL); */ |
| type |
| = build_function_type (IMP_type, |
| tree_cons (NULL_TREE, objc_super_type, |
| tree_cons (NULL_TREE, objc_selector_type, |
| OBJC_VOID_AT_END))); |
| umsg_super_decl = add_builtin_function (TAG_MSGSENDSUPER, |
| type, 0, NOT_BUILT_IN, |
| NULL, NULL_TREE); |
| TREE_NOTHROW (umsg_super_decl) = 0; |
| |
| /* The following GNU runtime entry point is called to initialize |
| each module: |
| |
| __objc_exec_class (void *); */ |
| type |
| = build_function_type (void_type_node, |
| tree_cons (NULL_TREE, ptr_type_node, |
| OBJC_VOID_AT_END)); |
| execclass_decl = add_builtin_function (TAG_EXECCLASS, |
| type, 0, NOT_BUILT_IN, |
| NULL, NULL_TREE); |
| } |
| |
| /* id objc_getClass (const char *); */ |
| |
| type = build_function_type (objc_object_type, |
| tree_cons (NULL_TREE, |
| const_string_type_node, |
| OBJC_VOID_AT_END)); |
| |
| objc_get_class_decl |
| = add_builtin_function (TAG_GETCLASS, type, 0, NOT_BUILT_IN, |
| NULL, NULL_TREE); |
| |
| /* id objc_getMetaClass (const char *); */ |
| |
| objc_get_meta_class_decl |
| = add_builtin_function (TAG_GETMETACLASS, type, 0, NOT_BUILT_IN, NULL, NULL_TREE); |
| |
| build_class_template (); |
| build_super_template (); |
| build_protocol_template (); |
| build_category_template (); |
| build_objc_exception_stuff (); |
| |
| if (flag_next_runtime) |
| build_next_objc_exception_stuff (); |
| |
| /* static SEL _OBJC_SELECTOR_TABLE[]; */ |
| |
| if (! flag_next_runtime) |
| build_selector_table_decl (); |
| |
| /* Forward declare constant_string_id and constant_string_type. */ |
| if (!constant_string_class_name) |
| constant_string_class_name = default_constant_string_class_name; |
| |
| constant_string_id = get_identifier (constant_string_class_name); |
| objc_declare_class (tree_cons (NULL_TREE, constant_string_id, NULL_TREE)); |
| |
| /* Pre-build the following entities - for speed/convenience. */ |
| self_id = get_identifier ("self"); |
| ucmd_id = get_identifier ("_cmd"); |
| |
| #ifdef OBJCPLUS |
| pop_lang_context (); |
| #endif |
| |
| write_symbols = save_write_symbols; |
| debug_hooks = save_hooks; |
| } |
| |
| /* Ensure that the ivar list for NSConstantString/NXConstantString |
| (or whatever was specified via `-fconstant-string-class') |
| contains fields at least as large as the following three, so that |
| the runtime can stomp on them with confidence: |
| |
| struct STRING_OBJECT_CLASS_NAME |
| { |
| Object isa; |
| char *cString; |
| unsigned int length; |
| }; */ |
| |
| static int |
| check_string_class_template (void) |
| { |
| tree field_decl = objc_get_class_ivars (constant_string_id); |
| |
| #define AT_LEAST_AS_LARGE_AS(F, T) \ |
| (F && TREE_CODE (F) == FIELD_DECL \ |
| && (TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (F))) \ |
| >= TREE_INT_CST_LOW (TYPE_SIZE (T)))) |
| |
| if (!AT_LEAST_AS_LARGE_AS (field_decl, ptr_type_node)) |
| return 0; |
| |
| field_decl = TREE_CHAIN (field_decl); |
| if (!AT_LEAST_AS_LARGE_AS (field_decl, ptr_type_node)) |
| return 0; |
| |
| field_decl = TREE_CHAIN (field_decl); |
| return AT_LEAST_AS_LARGE_AS (field_decl, unsigned_type_node); |
| |
| #undef AT_LEAST_AS_LARGE_AS |
| } |
| |
| /* Avoid calling `check_string_class_template ()' more than once. */ |
| static GTY(()) int string_layout_checked; |
| |
| /* Construct an internal string layout to be used as a template for |
| creating NSConstantString/NXConstantString instances. */ |
| |
| static tree |
| objc_build_internal_const_str_type (void) |
| { |
| tree type = (*lang_hooks.types.make_type) (RECORD_TYPE); |
| tree fields = build_decl (FIELD_DECL, NULL_TREE, ptr_type_node); |
| tree field = build_decl (FIELD_DECL, NULL_TREE, ptr_type_node); |
| |
| TREE_CHAIN (field) = fields; fields = field; |
| field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node); |
| TREE_CHAIN (field) = fields; fields = field; |
| /* NB: The finish_builtin_struct() routine expects FIELD_DECLs in |
| reverse order! */ |
| finish_builtin_struct (type, "__builtin_ObjCString", |
| fields, NULL_TREE); |
| |
| return type; |
| } |
| |
| /* Custom build_string which sets TREE_TYPE! */ |
| |
| static tree |
| my_build_string (int len, const char *str) |
| { |
| return fix_string_type (build_string (len, str)); |
| } |
| |
| /* Build a string with contents STR and length LEN and convert it to a |
| pointer. */ |
| |
| static tree |
| my_build_string_pointer (int len, const char *str) |
| { |
| tree string = my_build_string (len, str); |
| tree ptrtype = build_pointer_type (TREE_TYPE (TREE_TYPE (string))); |
| return build1 (ADDR_EXPR, ptrtype, string); |
| } |
| |
| static hashval_t |
| string_hash (const void *ptr) |
| { |
| const_tree const str = ((const struct string_descriptor *)ptr)->literal; |
| const unsigned char *p = (const unsigned char *) TREE_STRING_POINTER (str); |
| int i, len = TREE_STRING_LENGTH (str); |
| hashval_t h = len; |
| |
| for (i = 0; i < len; i++) |
| h = ((h * 613) + p[i]); |
| |
| return h; |
| } |
| |
| static int |
| string_eq (const void *ptr1, const void *ptr2) |
| { |
| const_tree const str1 = ((const struct string_descriptor *)ptr1)->literal; |
| const_tree const str2 = ((const struct string_descriptor *)ptr2)->literal; |
| int len1 = TREE_STRING_LENGTH (str1); |
| |
| return (len1 == TREE_STRING_LENGTH (str2) |
| && !memcmp (TREE_STRING_POINTER (str1), TREE_STRING_POINTER (str2), |
| len1)); |
| } |
| |
| /* Given a chain of STRING_CST's, build a static instance of |
| NXConstantString which points at the concatenation of those |
| strings. We place the string object in the __string_objects |
| section of the __OBJC segment. The Objective-C runtime will |
| initialize the isa pointers of the string objects to point at the |
| NXConstantString class object. */ |
| |
| tree |
| objc_build_string_object (tree string) |
| { |
| tree initlist, constructor, constant_string_class; |
| int length; |
| tree fields, addr; |
| struct string_descriptor *desc, key; |
| void **loc; |
| |
| /* Prep the string argument. */ |
| string = fix_string_type (string); |
| TREE_SET_CODE (string, STRING_CST); |
| length = TREE_STRING_LENGTH (string) - 1; |
| |
| /* Check whether the string class being used actually exists and has the |
| correct ivar layout. */ |
| if (!string_layout_checked) |
| { |
| string_layout_checked = -1; |
| constant_string_class = lookup_interface (constant_string_id); |
| internal_const_str_type = objc_build_internal_const_str_type (); |
| |
| if (!constant_string_class |
| || !(constant_string_type |
| = CLASS_STATIC_TEMPLATE (constant_string_class))) |
| error ("cannot find interface declaration for %qs", |
| IDENTIFIER_POINTER (constant_string_id)); |
| /* The NSConstantString/NXConstantString ivar layout is now known. */ |
| else if (!check_string_class_template ()) |
| error ("interface %qs does not have valid constant string layout", |
| IDENTIFIER_POINTER (constant_string_id)); |
| /* For the NeXT runtime, we can generate a literal reference |
| to the string class, don't need to run a constructor. */ |
| else if (flag_next_runtime && !setup_string_decl ()) |
| error ("cannot find reference tag for class %qs", |
| IDENTIFIER_POINTER (constant_string_id)); |
| else |
| { |
| string_layout_checked = 1; /* Success! */ |
| add_class_reference (constant_string_id); |
| } |
| } |
| |
| if (string_layout_checked == -1) |
| return error_mark_node; |
| |
| /* Perhaps we already constructed a constant string just like this one? */ |
| key.literal = string; |
| loc = htab_find_slot (string_htab, &key, INSERT); |
| desc = (struct string_descriptor *) *loc; |
| |
| if (!desc) |
| { |
| tree var; |
| *loc = desc = GGC_NEW (struct string_descriptor); |
| desc->literal = string; |
| |
| /* GNU: (NXConstantString *) & ((__builtin_ObjCString) { NULL, string, length }) */ |
| /* NeXT: (NSConstantString *) & ((__builtin_ObjCString) { isa, string, length }) */ |
| fields = TYPE_FIELDS (internal_const_str_type); |
| initlist |
| = build_tree_list (fields, |
| flag_next_runtime |
| ? build_unary_op (input_location, |
| ADDR_EXPR, string_class_decl, 0) |
| : build_int_cst (NULL_TREE, 0)); |
| fields = TREE_CHAIN (fields); |
| initlist = tree_cons (fields, build_unary_op (input_location, |
| ADDR_EXPR, string, 1), |
| initlist); |
| fields = TREE_CHAIN (fields); |
| initlist = tree_cons (fields, build_int_cst (NULL_TREE, length), |
| initlist); |
| constructor = objc_build_constructor (internal_const_str_type, |
| nreverse (initlist)); |
| |
| if (!flag_next_runtime) |
| constructor |
| = objc_add_static_instance (constructor, constant_string_type); |
| else |
| { |
| var = build_decl (CONST_DECL, NULL, TREE_TYPE (constructor)); |
| DECL_INITIAL (var) = constructor; |
| TREE_STATIC (var) = 1; |
| pushdecl_top_level (var); |
| constructor = var; |
| } |
| desc->constructor = constructor; |
| } |
| |
| addr = convert (build_pointer_type (constant_string_type), |
| build_unary_op (input_location, |
| ADDR_EXPR, desc->constructor, 1)); |
| |
| return addr; |
| } |
| |
| /* Declare a static instance of CLASS_DECL initialized by CONSTRUCTOR. */ |
| |
| static GTY(()) int num_static_inst; |
| |
| static tree |
| objc_add_static_instance (tree constructor, tree class_decl) |
| { |
| tree *chain, decl; |
| char buf[256]; |
| |
| /* Find the list of static instances for the CLASS_DECL. Create one if |
| not found. */ |
| for (chain = &objc_static_instances; |
| *chain && TREE_VALUE (*chain) != class_decl; |
| chain = &TREE_CHAIN (*chain)); |
| if (!*chain) |
| { |
| *chain = tree_cons (NULL_TREE, class_decl, NULL_TREE); |
| add_objc_string (OBJC_TYPE_NAME (class_decl), class_names); |
| } |
| |
| sprintf (buf, "_OBJC_INSTANCE_%d", num_static_inst++); |
| decl = build_decl (VAR_DECL, get_identifier (buf), class_decl); |
| DECL_COMMON (decl) = 1; |
| TREE_STATIC (decl) = 1; |
| DECL_ARTIFICIAL (decl) = 1; |
| TREE_USED (decl) = 1; |
| DECL_INITIAL (decl) = constructor; |
| |
| /* We may be writing something else just now. |
| Postpone till end of input. */ |
| DECL_DEFER_OUTPUT (decl) = 1; |
| pushdecl_top_level (decl); |
| rest_of_decl_compilation (decl, 1, 0); |
| |
| /* Add the DECL to the head of this CLASS' list. */ |
| TREE_PURPOSE (*chain) = tree_cons (NULL_TREE, decl, TREE_PURPOSE (*chain)); |
| |
| return decl; |
| } |
| |
| /* Build a static constant CONSTRUCTOR |
| with type TYPE and elements ELTS. */ |
| |
| static tree |
| objc_build_constructor (tree type, tree elts) |
| { |
| tree constructor = build_constructor_from_list (type, elts); |
| |
| TREE_CONSTANT (constructor) = 1; |
| TREE_STATIC (constructor) = 1; |
| TREE_READONLY (constructor) = 1; |
| |
| #ifdef OBJCPLUS |
| /* Adjust for impedance mismatch. We should figure out how to build |
| CONSTRUCTORs that consistently please both the C and C++ gods. */ |
| if (!TREE_PURPOSE (elts)) |
| TREE_TYPE (constructor) = init_list_type_node; |
| #endif |
| |
| return constructor; |
| } |
| |
| /* Take care of defining and initializing _OBJC_SYMBOLS. */ |
| |
| /* Predefine the following data type: |
| |
| struct _objc_symtab |
| { |
| long sel_ref_cnt; |
| SEL *refs; |
| short cls_def_cnt; |
| short cat_def_cnt; |
| void *defs[cls_def_cnt + cat_def_cnt]; |
| }; */ |
| |
| static void |
| build_objc_symtab_template (void) |
| { |
| tree field_decl, field_decl_chain; |
| |
| objc_symtab_template |
| = start_struct (RECORD_TYPE, get_identifier (UTAG_SYMTAB)); |
| |
| /* long sel_ref_cnt; */ |
| field_decl = create_field_decl (long_integer_type_node, "sel_ref_cnt"); |
| field_decl_chain = field_decl; |
| |
| /* SEL *refs; */ |
| field_decl = create_field_decl (build_pointer_type (objc_selector_type), |
| "refs"); |
| chainon (field_decl_chain, field_decl); |
| |
| /* short cls_def_cnt; */ |
| field_decl = create_field_decl (short_integer_type_node, "cls_def_cnt"); |
| chainon (field_decl_chain, field_decl); |
| |
| /* short cat_def_cnt; */ |
| field_decl = create_field_decl (short_integer_type_node, |
| "cat_def_cnt"); |
| chainon (field_decl_chain, field_decl); |
| |
| if (imp_count || cat_count || !flag_next_runtime) |
| { |
| /* void *defs[imp_count + cat_count (+ 1)]; */ |
| /* NB: The index is one less than the size of the array. */ |
| int index = imp_count + cat_count |
| + (flag_next_runtime? -1: 0); |
| field_decl = create_field_decl |
| (build_array_type |
| (ptr_type_node, |
| build_index_type (build_int_cst (NULL_TREE, index))), |
| "defs"); |
| chainon (field_decl_chain, field_decl); |
| } |
| |
| finish_struct (objc_symtab_template, field_decl_chain, NULL_TREE); |
| } |
| |
| /* Create the initial value for the `defs' field of _objc_symtab. |
| This is a CONSTRUCTOR. */ |
| |
| static tree |
| init_def_list (tree type) |
| { |
| tree expr, initlist = NULL_TREE; |
| struct imp_entry *impent; |
| |
| if (imp_count) |
| for (impent = imp_list; impent; impent = impent->next) |
| { |
| if (TREE_CODE (impent->imp_context) == CLASS_IMPLEMENTATION_TYPE) |
| { |
| expr = build_unary_op (input_location, |
| ADDR_EXPR, impent->class_decl, 0); |
| initlist = tree_cons (NULL_TREE, expr, initlist); |
| } |
| } |
| |
| if (cat_count) |
| for (impent = imp_list; impent; impent = impent->next) |
| { |
| if (TREE_CODE (impent->imp_context) == CATEGORY_IMPLEMENTATION_TYPE) |
| { |
| expr = build_unary_op (input_location, |
| ADDR_EXPR, impent->class_decl, 0); |
| initlist = tree_cons (NULL_TREE, expr, initlist); |
| } |
| } |
| |
| if (!flag_next_runtime) |
| { |
| /* statics = { ..., _OBJC_STATIC_INSTANCES, ... } */ |
| tree expr; |
| |
| if (static_instances_decl) |
| expr = build_unary_op (input_location, |
| ADDR_EXPR, static_instances_decl, 0); |
| else |
| expr = build_int_cst (NULL_TREE, 0); |
| |
| initlist = tree_cons (NULL_TREE, expr, initlist); |
| } |
| |
| return objc_build_constructor (type, nreverse (initlist)); |
| } |
| |
| /* Construct the initial value for all of _objc_symtab. */ |
| |
| static tree |
| init_objc_symtab (tree type) |
| { |
| tree initlist; |
| |
| /* sel_ref_cnt = { ..., 5, ... } */ |
| |
| initlist = build_tree_list (NULL_TREE, |
| build_int_cst (long_integer_type_node, 0)); |
| |
| /* refs = { ..., _OBJC_SELECTOR_TABLE, ... } */ |
| |
| if (flag_next_runtime || ! sel_ref_chain) |
| initlist = tree_cons (NULL_TREE, build_int_cst (NULL_TREE, 0), initlist); |
| else |
| initlist |
| = tree_cons (NULL_TREE, |
| convert (build_pointer_type (objc_selector_type), |
| build_unary_op (input_location, ADDR_EXPR, |
| UOBJC_SELECTOR_TABLE_decl, 1)), |
| initlist); |
| |
| /* cls_def_cnt = { ..., 5, ... } */ |
| |
| initlist = tree_cons (NULL_TREE, build_int_cst (NULL_TREE, imp_count), initlist); |
| |
| /* cat_def_cnt = { ..., 5, ... } */ |
| |
| initlist = tree_cons (NULL_TREE, build_int_cst (NULL_TREE, cat_count), initlist); |
| |
| /* cls_def = { ..., { &Foo, &Bar, ...}, ... } */ |
| |
| if (imp_count || cat_count || !flag_next_runtime) |
| { |
| |
| tree field = TYPE_FIELDS (type); |
| field = TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (field)))); |
| |
| initlist = tree_cons (NULL_TREE, init_def_list (TREE_TYPE (field)), |
| initlist); |
| } |
| |
| return objc_build_constructor (type, nreverse (initlist)); |
| } |
| |
| /* Generate forward declarations for metadata such as |
| 'OBJC_CLASS_...'. */ |
| |
| static tree |
| build_metadata_decl (const char *name, tree type) |
| { |
| tree decl; |
| |
| /* struct TYPE NAME_<name>; */ |
| decl = start_var_decl (type, synth_id_with_class_suffix |
| (name, |
| objc_implementation_context)); |
| |
| return decl; |
| } |
| |
| /* Push forward-declarations of all the categories so that |
| init_def_list can use them in a CONSTRUCTOR. */ |
| |
| static void |
| forward_declare_categories (void) |
| { |
| struct imp_entry *impent; |
| tree sav = objc_implementation_context; |
| |
| for (impent = imp_list; impent; impent = impent->next) |
| { |
| if (TREE_CODE (impent->imp_context) == CATEGORY_IMPLEMENTATION_TYPE) |
| { |
| /* Set an invisible arg to synth_id_with_class_suffix. */ |
| objc_implementation_context = impent->imp_context; |
| /* extern struct objc_category _OBJC_CATEGORY_<name>; */ |
| impent->class_decl = build_metadata_decl ("_OBJC_CATEGORY", |
| objc_category_template); |
| } |
| } |
| objc_implementation_context = sav; |
| } |
| |
| /* Create the declaration of _OBJC_SYMBOLS, with type `struct _objc_symtab' |
| and initialized appropriately. */ |
| |
| static void |
| generate_objc_symtab_decl (void) |
| { |
| /* forward declare categories */ |
| if (cat_count) |
| forward_declare_categories (); |
| |
| build_objc_symtab_template (); |
| UOBJC_SYMBOLS_decl = start_var_decl (objc_symtab_template, "_OBJC_SYMBOLS"); |
| finish_var_decl (UOBJC_SYMBOLS_decl, |
| init_objc_symtab (TREE_TYPE (UOBJC_SYMBOLS_decl))); |
| } |
| |
| static tree |
| init_module_descriptor (tree type) |
| { |
| tree initlist, expr; |
| |
| /* version = { 1, ... } */ |
| |
| expr = build_int_cst (long_integer_type_node, OBJC_VERSION); |
| initlist = build_tree_list (NULL_TREE, expr); |
| |
| /* size = { ..., sizeof (struct _objc_module), ... } */ |
| |
| expr = convert (long_integer_type_node, |
| size_in_bytes (objc_module_template)); |
| initlist = tree_cons (NULL_TREE, expr, initlist); |
| |
| /* Don't provide any file name for security reasons. */ |
| /* name = { ..., "", ... } */ |
| |
| expr = add_objc_string (get_identifier (""), class_names); |
| initlist = tree_cons (NULL_TREE, expr, initlist); |
| |
| /* symtab = { ..., _OBJC_SYMBOLS, ... } */ |
| |
| if (UOBJC_SYMBOLS_decl) |
| expr = build_unary_op (input_location, |
| ADDR_EXPR, UOBJC_SYMBOLS_decl, 0); |
| else |
| expr = build_int_cst (NULL_TREE, 0); |
| initlist = tree_cons (NULL_TREE, expr, initlist); |
| |
| return objc_build_constructor (type, nreverse (initlist)); |
| } |
| |
| /* Write out the data structures to describe Objective C classes defined. |
| |
| struct _objc_module { ... } _OBJC_MODULE = { ... }; */ |
| |
| static void |
| build_module_descriptor (void) |
| { |
| tree field_decl, field_decl_chain; |
| |
| #ifdef OBJCPLUS |
| push_lang_context (lang_name_c); /* extern "C" */ |
| #endif |
| |
| objc_module_template |
| = start_struct (RECORD_TYPE, get_identifier (UTAG_MODULE)); |
| |
| /* long version; */ |
| field_decl = create_field_decl (long_integer_type_node, "version"); |
| field_decl_chain = field_decl; |
| |
| /* long size; */ |
| field_decl = create_field_decl (long_integer_type_node, "size"); |
| chainon (field_decl_chain, field_decl); |
| |
| /* char *name; */ |
| field_decl = create_field_decl (string_type_node, "name"); |
| chainon (field_decl_chain, field_decl); |
| |
| /* struct _objc_symtab *symtab; */ |
| field_decl |
| = create_field_decl (build_pointer_type |
| (xref_tag (RECORD_TYPE, |
| get_identifier (UTAG_SYMTAB))), |
| "symtab"); |
| chainon (field_decl_chain, field_decl); |
| |
| finish_struct (objc_module_template, field_decl_chain, NULL_TREE); |
| |
| /* Create an instance of "_objc_module". */ |
| UOBJC_MODULES_decl = start_var_decl (objc_module_template, "_OBJC_MODULES"); |
| finish_var_decl (UOBJC_MODULES_decl, |
| init_module_descriptor (TREE_TYPE (UOBJC_MODULES_decl))); |
| |
| #ifdef OBJCPLUS |
| pop_lang_context (); |
| #endif |
| } |
| |
| /* The GNU runtime requires us to provide a static initializer function |
| for each module: |
| |
| static void __objc_gnu_init (void) { |
| __objc_exec_class (&L_OBJC_MODULES); |
| } */ |
| |
| static void |
| build_module_initializer_routine (void) |
| { |
| tree body; |
| |
| #ifdef OBJCPLUS |
| push_lang_context (lang_name_c); /* extern "C" */ |
| #endif |
| |
| objc_push_parm (build_decl (PARM_DECL, NULL_TREE, void_type_node)); |
| objc_start_function (get_identifier (TAG_GNUINIT), |
| build_function_type (void_type_node, |
| OBJC_VOID_AT_END), |
| NULL_TREE, objc_get_parm_info (0)); |
| |
| body = c_begin_compound_stmt (true); |
| add_stmt (build_function_call |
| (execclass_decl, |
| build_tree_list |
| (NULL_TREE, |
| build_unary_op (input_location, ADDR_EXPR, |
| UOBJC_MODULES_decl, 0)))); |
| add_stmt (c_end_compound_stmt (body, true)); |
| |
| TREE_PUBLIC (current_function_decl) = 0; |
| |
| #ifndef OBJCPLUS |
| /* For Objective-C++, we will need to call __objc_gnu_init |
| from objc_generate_static_init_call() below. */ |
| DECL_STATIC_CONSTRUCTOR (current_function_decl) = 1; |
| #endif |
| |
| GNU_INIT_decl = current_function_decl; |
| finish_function (); |
| |
| #ifdef OBJCPLUS |
| pop_lang_context (); |
| #endif |
| } |
| |
| #ifdef OBJCPLUS |
| /* Return 1 if the __objc_gnu_init function has been synthesized and needs |
| to be called by the module initializer routine. */ |
| |
| int |
| objc_static_init_needed_p (void) |
| { |
| return (GNU_INIT_decl != NULL_TREE); |
| } |
| |
| /* Generate a call to the __objc_gnu_init initializer function. */ |
| |
| tree |
| objc_generate_static_init_call (tree ctors ATTRIBUTE_UNUSED) |
| { |
| add_stmt (build_stmt (EXPR_STMT, |
| build_function_call (GNU_INIT_decl, NULL_TREE))); |
| |
| return ctors; |
| } |
| #endif /* OBJCPLUS */ |
| |
| /* Return the DECL of the string IDENT in the SECTION. */ |
| |
| static tree |
| get_objc_string_decl (tree ident, enum string_section section) |
| { |
| tree chain; |
| |
| if (section == class_names) |
| chain = class_names_chain; |
| else if (section == meth_var_names) |
| chain = meth_var_names_chain; |
| else if (section == meth_var_types) |
| chain = meth_var_types_chain; |
| else |
| abort (); |
| |
| for (; chain != 0; chain = TREE_CHAIN (chain)) |
| if (TREE_VALUE (chain) == ident) |
| return (TREE_PURPOSE (chain)); |
| |
| abort (); |
| return NULL_TREE; |
| } |
| |
| /* Output references to all statically allocated objects. Return the DECL |
| for the array built. */ |
| |
| static void |
| generate_static_references (void) |
| { |
| tree decls = NULL_TREE, expr = NULL_TREE; |
| tree class_name, klass, decl, initlist; |
| tree cl_chain, in_chain, type |
| = build_array_type (build_pointer_type (void_type_node), NULL_TREE); |
| int num_inst, num_class; |
| char buf[256]; |
| |
| if (flag_next_runtime) |
| abort (); |
| |
| for (cl_chain = objc_static_instances, num_class = 0; |
| cl_chain; cl_chain = TREE_CHAIN (cl_chain), num_class++) |
| { |
| for (num_inst = 0, in_chain = TREE_PURPOSE (cl_chain); |
| in_chain; num_inst++, in_chain = TREE_CHAIN (in_chain)); |
| |
| sprintf (buf, "_OBJC_STATIC_INSTANCES_%d", num_class); |
| decl = start_var_decl (type, buf); |
| |
| /* Output {class_name, ...}. */ |
| klass = TREE_VALUE (cl_chain); |
| class_name = get_objc_string_decl (OBJC_TYPE_NAME (klass), class_names); |
| initlist = build_tree_list (NULL_TREE, |
| build_unary_op (input_location, |
| ADDR_EXPR, class_name, 1)); |
| |
| /* Output {..., instance, ...}. */ |
| for (in_chain = TREE_PURPOSE (cl_chain); |
| in_chain; in_chain = TREE_CHAIN (in_chain)) |
| { |
| expr = build_unary_op (input_location, |
| ADDR_EXPR, TREE_VALUE (in_chain), 1); |
| initlist = tree_cons (NULL_TREE, expr, initlist); |
| } |
| |
| /* Output {..., NULL}. */ |
| initlist = tree_cons (NULL_TREE, build_int_cst (NULL_TREE, 0), initlist); |
| |
| expr = objc_build_constructor (TREE_TYPE (decl), nreverse (initlist)); |
| finish_var_decl (decl, expr); |
| decls |
| = tree_cons (NULL_TREE, build_unary_op (input_location, |
| ADDR_EXPR, decl, 1), decls); |
| } |
| |
| decls = tree_cons (NULL_TREE, build_int_cst (NULL_TREE, 0), decls); |
| expr = objc_build_constructor (type, nreverse (decls)); |
| static_instances_decl = start_var_decl (type, "_OBJC_STATIC_INSTANCES"); |
| finish_var_decl (static_instances_decl, expr); |
| } |
| |
| static GTY(()) int selector_reference_idx; |
| |
| static tree |
| build_selector_reference_decl (void) |
| { |
| tree decl; |
| char buf[256]; |
| |
| sprintf (buf, "_OBJC_SELECTOR_REFERENCES_%d", selector_reference_idx++); |
| decl = start_var_decl (objc_selector_type, buf); |
| |
| return decl; |
| } |
| |
| static void |
| build_selector_table_decl (void) |
| { |
| tree temp; |
| |
| if (flag_typed_selectors) |
| { |
| build_selector_template (); |
| temp = build_array_type (objc_selector_template, NULL_TREE); |
| } |
| else |
| temp = build_array_type (objc_selector_type, NULL_TREE); |
| |
| UOBJC_SELECTOR_TABLE_decl = start_var_decl (temp, "_OBJC_SELECTOR_TABLE"); |
| } |
| |
| /* Just a handy wrapper for add_objc_string. */ |
| |
| static tree |
| build_selector (tree ident) |
| { |
| return convert (objc_selector_type, |
| add_objc_string (ident, meth_var_names)); |
| } |
| |
| static void |
| build_selector_translation_table (void) |
| { |
| tree chain, initlist = NULL_TREE; |
| int offset = 0; |
| tree decl = NULL_TREE; |
| |
| for (chain = sel_ref_chain; chain; chain = TREE_CHAIN (chain)) |
| { |
| tree expr; |
| |
| if (warn_selector && objc_implementation_context) |
| { |
| tree method_chain; |
| bool found = false; |
| for (method_chain = meth_var_names_chain; |
| method_chain; |
| method_chain = TREE_CHAIN (method_chain)) |
| { |
| if (TREE_VALUE (method_chain) == TREE_VALUE (chain)) |
| { |
| found = true; |
| break; |
| } |
| } |
| if (!found) |
| { |
| location_t *loc; |
| if (flag_next_runtime && TREE_PURPOSE (chain)) |
| loc = &DECL_SOURCE_LOCATION (TREE_PURPOSE (chain)); |
| else |
| loc = &input_location; |
| warning (0, "%Hcreating selector for nonexistent method %qE", |
| loc, TREE_VALUE (chain)); |
| } |
| } |
| |
| expr = build_selector (TREE_VALUE (chain)); |
| /* add one for the '\0' character */ |
| offset += IDENTIFIER_LENGTH (TREE_VALUE (chain)) + 1; |
| |
| if (flag_next_runtime) |
| { |
| decl = TREE_PURPOSE (chain); |
| finish_var_decl (decl, expr); |
| } |
| else |
| { |
| if (flag_typed_selectors) |
| { |
| tree eltlist = NULL_TREE; |
| tree encoding = get_proto_encoding (TREE_PURPOSE (chain)); |
| eltlist = tree_cons (NULL_TREE, expr, NULL_TREE); |
| eltlist = tree_cons (NULL_TREE, encoding, eltlist); |
| expr = objc_build_constructor (objc_selector_template, |
| nreverse (eltlist)); |
| } |
| |
| initlist = tree_cons (NULL_TREE, expr, initlist); |
| } |
| } |
| |
| if (! flag_next_runtime) |
| { |
| /* Cause the selector table (previously forward-declared) |
| to be actually output. */ |
| initlist = tree_cons (NULL_TREE, |
| flag_typed_selectors |
| ? objc_build_constructor |
| (objc_selector_template, |
| tree_cons (NULL_TREE, |
| build_int_cst (NULL_TREE, 0), |
| tree_cons (NULL_TREE, |
| build_int_cst (NULL_TREE, 0), |
| NULL_TREE))) |
| : build_int_cst (NULL_TREE, 0), initlist); |
| initlist = objc_build_constructor (TREE_TYPE (UOBJC_SELECTOR_TABLE_decl), |
| nreverse (initlist)); |
| finish_var_decl (UOBJC_SELECTOR_TABLE_decl, initlist); |
| } |
| } |
| |
| static tree |
| get_proto_encoding (tree proto) |
| { |
| tree encoding; |
| if (proto) |
| { |
| if (! METHOD_ENCODING (proto)) |
| { |
| encoding = encode_method_prototype (proto); |
| METHOD_ENCODING (proto) = encoding; |
| } |
| else |
| encoding = METHOD_ENCODING (proto); |
| |
| return add_objc_string (encoding, meth_var_types); |
| } |
| else |
| return build_int_cst (NULL_TREE, 0); |
| } |
| |
| /* sel_ref_chain is a list whose "value" fields will be instances of |
| identifier_node that represent the selector. */ |
| |
| static tree |
| build_typed_selector_reference (tree ident, tree prototype) |
| { |
| tree *chain = &sel_ref_chain; |
| tree expr; |
| int index = 0; |
| |
| while (*chain) |
| { |
| if (TREE_PURPOSE (*chain) == prototype && TREE_VALUE (*chain) == ident) |
| goto return_at_index; |
| |
| index++; |
| chain = &TREE_CHAIN (*chain); |
| } |
| |
| *chain = tree_cons (prototype, ident, NULL_TREE); |
| |
| return_at_index: |
| expr = build_unary_op (input_location, ADDR_EXPR, |
| build_array_ref (UOBJC_SELECTOR_TABLE_decl, |
| build_int_cst (NULL_TREE, index), |
| input_location), |
| 1); |
| return convert (objc_selector_type, expr); |
| } |
| |
| static tree |
| build_selector_reference (tree ident) |
| { |
| tree *chain = &sel_ref_chain; |
| tree expr; |
| int index = 0; |
| |
| while (*chain) |
| { |
| if (TREE_VALUE (*chain) == ident) |
| return (flag_next_runtime |
| ? TREE_PURPOSE (*chain) |
| : build_array_ref (UOBJC_SELECTOR_TABLE_decl, |
| build_int_cst (NULL_TREE, index), |
| input_location)); |
| |
| index++; |
| chain = &TREE_CHAIN (*chain); |
| } |
| |
| expr = (flag_next_runtime ? build_selector_reference_decl (): NULL_TREE); |
| |
| *chain = tree_cons (expr, ident, NULL_TREE); |
| |
| return (flag_next_runtime |
| ? expr |
| : build_array_ref (UOBJC_SELECTOR_TABLE_decl, |
| build_int_cst (NULL_TREE, index), |
| input_location)); |
| } |
| |
| static GTY(()) int class_reference_idx; |
| |
| static tree |
| build_class_reference_decl (void) |
| { |
| tree decl; |
| char buf[256]; |
| |
| sprintf (buf, "_OBJC_CLASS_REFERENCES_%d", class_reference_idx++); |
| decl = start_var_decl (objc_class_type, buf); |
| |
| return decl; |
| } |
| |
| /* Create a class reference, but don't create a variable to reference |
| it. */ |
| |
| static void |
| add_class_reference (tree ident) |
| { |
| tree chain; |
| |
| if ((chain = cls_ref_chain)) |
| { |
| tree tail; |
| do |
| { |
| if (ident == TREE_VALUE (chain)) |
| return; |
| |
| tail = chain; |
| chain = TREE_CHAIN (chain); |
| } |
| while (chain); |
| |
| /* Append to the end of the list */ |
| TREE_CHAIN (tail) = tree_cons (NULL_TREE, ident, NULL_TREE); |
| } |
| else |
| cls_ref_chain = tree_cons (NULL_TREE, ident, NULL_TREE); |
| } |
| |
| /* Get a class reference, creating it if necessary. Also create the |
| reference variable. */ |
| |
| tree |
| objc_get_class_reference (tree ident) |
| { |
| tree orig_ident = (DECL_P (ident) |
| ? DECL_NAME (ident) |
| : TYPE_P (ident) |
| ? OBJC_TYPE_NAME (ident) |
| : ident); |
| bool local_scope = false; |
| |
| #ifdef OBJCPLUS |
| if (processing_template_decl) |
| /* Must wait until template instantiation time. */ |
| return build_min_nt (CLASS_REFERENCE_EXPR, ident); |
| #endif |
| |
| if (TREE_CODE (ident) == TYPE_DECL) |
| ident = (DECL_ORIGINAL_TYPE (ident) |
| ? DECL_ORIGINAL_TYPE (ident) |
| : TREE_TYPE (ident)); |
| |
| #ifdef OBJCPLUS |
| if (TYPE_P (ident) && TYPE_CONTEXT (ident) |
| && TYPE_CONTEXT (ident) != global_namespace) |
| local_scope = true; |
| #endif |
| |
| if (local_scope || !(ident = objc_is_class_name (ident))) |
| { |
| error ("%qs is not an Objective-C class name or alias", |
| IDENTIFIER_POINTER (orig_ident)); |
| return error_mark_node; |
| } |
| |
| if (flag_next_runtime && !flag_zero_link) |
| { |
| tree *chain; |
| tree decl; |
| |
| for (chain = &cls_ref_chain; *chain; chain = &TREE_CHAIN (*chain)) |
| if (TREE_VALUE (*chain) == ident) |
| { |
| if (! TREE_PURPOSE (*chain)) |
| TREE_PURPOSE (*chain) = build_class_reference_decl (); |
| |
| return TREE_PURPOSE (*chain); |
| } |
| |
| decl = build_class_reference_decl (); |
| *chain = tree_cons (decl, ident, NULL_TREE); |
| return decl; |
| } |
| else |
| { |
| tree params; |
| |
| add_class_reference (ident); |
| |
| params = build_tree_list (NULL_TREE, |
| my_build_string_pointer |
| (IDENTIFIER_LENGTH (ident) + 1, |
| IDENTIFIER_POINTER (ident))); |
| |
| assemble_external (objc_get_class_decl); |
| return build_function_call (objc_get_class_decl, params); |
| } |
| } |
| |
| /* For each string section we have a chain which maps identifier nodes |
| to decls for the strings. */ |
| |
| static tree |
| add_objc_string (tree ident, enum string_section section) |
| { |
| tree *chain, decl, type, string_expr; |
| |
| if (section == class_names) |
| chain = &class_names_chain; |
| else if (section == meth_var_names) |
| chain = &meth_var_names_chain; |
| else if (section == meth_var_types) |
| chain = &meth_var_types_chain; |
| else |
| abort (); |
| |
| while (*chain) |
| { |
| if (TREE_VALUE (*chain) == ident) |
| return convert (string_type_node, |
| build_unary_op (input_location, |
| ADDR_EXPR, TREE_PURPOSE (*chain), 1)); |
| |
| chain = &TREE_CHAIN (*chain); |
| } |
| |
| decl = build_objc_string_decl (section); |
| |
| type = build_array_type |
| (char_type_node, |
| build_index_type |
| (build_int_cst (NULL_TREE, |
| IDENTIFIER_LENGTH (ident)))); |
| decl = start_var_decl (type, IDENTIFIER_POINTER (DECL_NAME (decl))); |
| string_expr = my_build_string (IDENTIFIER_LENGTH (ident) + 1, |
| IDENTIFIER_POINTER (ident)); |
| finish_var_decl (decl, string_expr); |
| |
| *chain = tree_cons (decl, ident, NULL_TREE); |
| |
| return convert (string_type_node, build_unary_op (input_location, |
| ADDR_EXPR, decl, 1)); |
| } |
| |
| static GTY(()) int class_names_idx; |
| static GTY(()) int meth_var_names_idx; |
| static GTY(()) int meth_var_types_idx; |
| |
| static tree |
| build_objc_string_decl (enum string_section section) |
| { |
| tree decl, ident; |
| char buf[256]; |
| |
| if (section == class_names) |
| sprintf (buf, "_OBJC_CLASS_NAME_%d", class_names_idx++); |
| else if (section == meth_var_names) |
| sprintf (buf, "_OBJC_METH_VAR_NAME_%d", meth_var_names_idx++); |
| else if (section == meth_var_types) |
| sprintf (buf, "_OBJC_METH_VAR_TYPE_%d", meth_var_types_idx++); |
| |
| ident = get_identifier (buf); |
| |
| decl = build_decl (VAR_DECL, ident, build_array_type (char_type_node, 0)); |
| DECL_EXTERNAL (decl) = 1; |
| TREE_PUBLIC (decl) = 0; |
| TREE_USED (decl) = 1; |
| TREE_CONSTANT (decl) = 1; |
| DECL_CONTEXT (decl) = 0; |
| DECL_ARTIFICIAL (decl) = 1; |
| #ifdef OBJCPLUS |
| DECL_THIS_STATIC (decl) = 1; /* squash redeclaration errors */ |
| #endif |
| |
| make_decl_rtl (decl); |
| pushdecl_top_level (decl); |
| |
| return decl; |
| } |
| |
| |
| void |
| objc_declare_alias (tree alias_ident, tree class_ident) |
| { |
| tree underlying_class; |
| |
| #ifdef OBJCPLUS |
| if (current_namespace != global_namespace) { |
| error ("Objective-C declarations may only appear in global scope"); |
| } |
| #endif /* OBJCPLUS */ |
| |
| if (!(underlying_class = objc_is_class_name (class_ident))) |
| warning (0, "cannot find class %qs", IDENTIFIER_POINTER (class_ident)); |
| else if (objc_is_class_name (alias_ident)) |
| warning (0, "class %qs already exists", IDENTIFIER_POINTER (alias_ident)); |
| else |
| { |
| /* Implement @compatibility_alias as a typedef. */ |
| #ifdef OBJCPLUS |
| push_lang_context (lang_name_c); /* extern "C" */ |
| #endif |
| lang_hooks.decls.pushdecl (build_decl |
| (TYPE_DECL, |
| alias_ident, |
| xref_tag (RECORD_TYPE, underlying_class))); |
| #ifdef OBJCPLUS |
| pop_lang_context (); |
| #endif |
| alias_chain = tree_cons (underlying_class, alias_ident, alias_chain); |
| } |
| } |
| |
| void |
| objc_declare_class (tree ident_list) |
| { |
| tree list; |
| #ifdef OBJCPLUS |
| if (current_namespace != global_namespace) { |
| error ("Objective-C declarations may only appear in global scope"); |
| } |
| #endif /* OBJCPLUS */ |
| |
| for (list = ident_list; list; list = TREE_CHAIN (list)) |
| { |
| tree ident = TREE_VALUE (list); |
| |
| if (! objc_is_class_name (ident)) |
| { |
| tree record = lookup_name (ident), type = record; |
| |
| if (record) |
| { |
| if (TREE_CODE (record) == TYPE_DECL) |
| type = DECL_ORIGINAL_TYPE (record); |
| |
| if (!TYPE_HAS_OBJC_INFO (type) |
| || !TYPE_OBJC_INTERFACE (type)) |
| { |
| error ("%qs redeclared as different kind of symbol", |
| IDENTIFIER_POINTER (ident)); |
| error ("previous declaration of %q+D", |
| record); |
| } |
| } |
| |
| record = xref_tag (RECORD_TYPE, ident); |
| INIT_TYPE_OBJC_INFO (record); |
| TYPE_OBJC_INTERFACE (record) = ident; |
| class_chain = tree_cons (NULL_TREE, ident, class_chain); |
| } |
| } |
| } |
| |
| tree |
| objc_is_class_name (tree ident) |
| { |
| tree chain; |
| |
| if (ident && TREE_CODE (ident) == IDENTIFIER_NODE |
| && identifier_global_value (ident)) |
| ident = identifier_global_value (ident); |
| while (ident && TREE_CODE (ident) == TYPE_DECL && DECL_ORIGINAL_TYPE (ident)) |
| ident = OBJC_TYPE_NAME (DECL_ORIGINAL_TYPE (ident)); |
| |
| if (ident && TREE_CODE (ident) == RECORD_TYPE) |
| ident = OBJC_TYPE_NAME (ident); |
| #ifdef OBJCPLUS |
| if (ident && TREE_CODE (ident) == TYPE_DECL) |
| ident = DECL_NAME (ident); |
| #endif |
| if (!ident || TREE_CODE (ident) != IDENTIFIER_NODE) |
| return NULL_TREE; |
| |
| if (lookup_interface (ident)) |
| return ident; |
| |
| for (chain = class_chain; chain; chain = TREE_CHAIN (chain)) |
| { |
| if (ident == TREE_VALUE (chain)) |
| return ident; |
| } |
| |
| for (chain = alias_chain; chain; chain = TREE_CHAIN (chain)) |
| { |
| if (ident == TREE_VALUE (chain)) |
| return TREE_PURPOSE (chain); |
| } |
| |
| return 0; |
| } |
| |
| /* Check whether TYPE is either 'id' or 'Class'. */ |
| |
| tree |
| objc_is_id (tree type) |
| { |
| if (type && TREE_CODE (type) == IDENTIFIER_NODE |
| && identifier_global_value (type)) |
| type = identifier_global_value (type); |
| |
| if (type && TREE_CODE (type) == TYPE_DECL) |
| type = TREE_TYPE (type); |
| |
| /* NB: This function may be called before the ObjC front-end has |
| been initialized, in which case OBJC_OBJECT_TYPE will (still) be NULL. */ |
| return (objc_object_type && type |
| && (IS_ID (type) || IS_CLASS (type) || IS_SUPER (type)) |
| ? type |
| : NULL_TREE); |
| } |
| |
| /* Check whether TYPE is either 'id', 'Class', or a pointer to an ObjC |
| class instance. This is needed by other parts of the compiler to |
| handle ObjC types gracefully. */ |
| |
| tree |
| objc_is_object_ptr (tree type) |
| { |
| tree ret; |
| |
| type = TYPE_MAIN_VARIANT (type); |
| if (!POINTER_TYPE_P (type)) |
| return 0; |
| |
| ret = objc_is_id (type); |
| if (!ret) |
| ret = objc_is_class_name (TREE_TYPE (type)); |
| |
| return ret; |
| } |
| |
| static int |
| objc_is_gcable_type (tree type, int or_strong_p) |
| { |
| tree name; |
| |
| if (!TYPE_P (type)) |
| return 0; |
| if (objc_is_id (TYPE_MAIN_VARIANT (type))) |
| return 1; |
| if (or_strong_p && lookup_attribute ("objc_gc", TYPE_ATTRIBUTES (type))) |
| return 1; |
| if (TREE_CODE (type) != POINTER_TYPE && TREE_CODE (type) != INDIRECT_REF) |
| return 0; |
| type = TREE_TYPE (type); |
| if (TREE_CODE (type) != RECORD_TYPE) |
| return 0; |
| name = TYPE_NAME (type); |
| return (objc_is_class_name (name) != NULL_TREE); |
| } |
| |
| static tree |
| objc_substitute_decl (tree expr, tree oldexpr, tree newexpr) |
| { |
| if (expr == oldexpr) |
| return newexpr; |
| |
| switch (TREE_CODE (expr)) |
| { |
| case COMPONENT_REF: |
| return objc_build_component_ref |
| (objc_substitute_decl (TREE_OPERAND (expr, 0), |
| oldexpr, |
| newexpr), |
| DECL_NAME (TREE_OPERAND (expr, 1))); |
| case ARRAY_REF: |
| return build_array_ref (objc_substitute_decl (TREE_OPERAND (expr, 0), |
| oldexpr, |
| newexpr), |
| TREE_OPERAND (expr, 1), |
| input_location); |
| case INDIRECT_REF: |
| return build_indirect_ref (input_location, |
| objc_substitute_decl (TREE_OPERAND (expr, 0), |
| oldexpr, |
| newexpr), "->"); |
| default: |
| return expr; |
| } |
| } |
| |
| static tree |
| objc_build_ivar_assignment (tree outervar, tree lhs, tree rhs) |
| { |
| tree func_params; |
| /* The LHS parameter contains the expression 'outervar->memberspec'; |
| we need to transform it into '&((typeof(outervar) *) 0)->memberspec', |
| where memberspec may be arbitrarily complex (e.g., 'g->f.d[2].g[3]'). |
| */ |
| tree offs |
| = objc_substitute_decl |
| (lhs, outervar, convert (TREE_TYPE (outervar), integer_zero_node)); |
| tree func |
| = (flag_objc_direct_dispatch |
| ? objc_assign_ivar_fast_decl |
| : objc_assign_ivar_decl); |
| |
| offs = convert (integer_type_node, build_unary_op (input_location, |
| ADDR_EXPR, offs, 0)); |
| offs = fold (offs); |
| func_params = tree_cons (NULL_TREE, |
| convert (objc_object_type, rhs), |
| tree_cons (NULL_TREE, convert (objc_object_type, outervar), |
| tree_cons (NULL_TREE, offs, |
| NULL_TREE))); |
| |
| assemble_external (func); |
| return build_function_call (func, func_params); |
| } |
| |
| static tree |
| objc_build_global_assignment (tree lhs, tree rhs) |
| { |
| tree func_params = tree_cons (NULL_TREE, |
| convert (objc_object_type, rhs), |
| tree_cons (NULL_TREE, convert (build_pointer_type (objc_object_type), |
| build_unary_op (input_location, ADDR_EXPR, lhs, 0)), |
| NULL_TREE)); |
| |
| assemble_external (objc_assign_global_decl); |
| return build_function_call (objc_assign_global_decl, func_params); |
| } |
| |
| static tree |
| objc_build_strong_cast_assignment (tree lhs, tree rhs) |
| { |
| tree func_params = tree_cons (NULL_TREE, |
| convert (objc_object_type, rhs), |
| tree_cons (NULL_TREE, convert (build_pointer_type (objc_object_type), |
| build_unary_op (input_location, ADDR_EXPR, lhs, 0)), |
| NULL_TREE)); |
| |
| assemble_external (objc_assign_strong_cast_decl); |
| return build_function_call (objc_assign_strong_cast_decl, func_params); |
| } |
| |
| static int |
| objc_is_gcable_p (tree expr) |
| { |
| return (TREE_CODE (expr) == COMPONENT_REF |
| ? objc_is_gcable_p (TREE_OPERAND (expr, 1)) |
| : TREE_CODE (expr) == ARRAY_REF |
| ? (objc_is_gcable_p (TREE_TYPE (expr)) |
| || objc_is_gcable_p (TREE_OPERAND (expr, 0))) |
| : TREE_CODE (expr) == ARRAY_TYPE |
| ? objc_is_gcable_p (TREE_TYPE (expr)) |
| : TYPE_P (expr) |
| ? objc_is_gcable_type (expr, 1) |
| : (objc_is_gcable_p (TREE_TYPE (expr)) |
| || (DECL_P (expr) |
| && lookup_attribute ("objc_gc", DECL_ATTRIBUTES (expr))))); |
| } |
| |
| static int |
| objc_is_ivar_reference_p (tree expr) |
| { |
| return (TREE_CODE (expr) == ARRAY_REF |
| ? objc_is_ivar_reference_p (TREE_OPERAND (expr, 0)) |
| : TREE_CODE (expr) == COMPONENT_REF |
| ? TREE_CODE (TREE_OPERAND (expr, 1)) == FIELD_DECL |
| : 0); |
| } |
| |
| static int |
| objc_is_global_reference_p (tree expr) |
| { |
| return (TREE_CODE (expr) == INDIRECT_REF || TREE_CODE (expr) == PLUS_EXPR |
| ? objc_is_global_reference_p (TREE_OPERAND (expr, 0)) |
| : DECL_P (expr) |
| ? (!DECL_CONTEXT (expr) || TREE_STATIC (expr)) |
| : 0); |
| } |
| |
| tree |
| objc_generate_write_barrier (tree lhs, enum tree_code modifycode, tree rhs) |
| { |
| tree result = NULL_TREE, outer; |
| int strong_cast_p = 0, outer_gc_p = 0, indirect_p = 0; |
| |
| /* See if we have any lhs casts, and strip them out. NB: The lvalue casts |
| will have been transformed to the form '*(type *)&expr'. */ |
| if (TREE_CODE (lhs) == INDIRECT_REF) |
| { |
| outer = TREE_OPERAND (lhs, 0); |
| |
| while (!strong_cast_p |
| && (CONVERT_EXPR_P (outer) |
| || TREE_CODE (outer) == NON_LVALUE_EXPR)) |
| { |
| tree lhstype = TREE_TYPE (outer); |
| |
| /* Descend down the cast chain, and record the first objc_gc |
| attribute found. */ |
| if (POINTER_TYPE_P (lhstype)) |
| { |
| tree attr |
| = lookup_attribute ("objc_gc", |
| TYPE_ATTRIBUTES (TREE_TYPE (lhstype))); |
| |
| if (attr) |
| strong_cast_p = 1; |
| } |
| |
| outer = TREE_OPERAND (outer, 0); |
| } |
| } |
| |
| /* If we have a __strong cast, it trumps all else. */ |
| if (strong_cast_p) |
| { |
| if (modifycode != NOP_EXPR) |
| goto invalid_pointer_arithmetic; |
| |
| if (warn_assign_intercept) |
| warning (0, "strong-cast assignment has been intercepted"); |
| |
| result = objc_build_strong_cast_assignment (lhs, rhs); |
| |
| goto exit_point; |
| } |
| |
| /* the lhs must be of a suitable type, regardless of its underlying |
| structure. */ |
| if (!objc_is_gcable_p (lhs)) |
| goto exit_point; |
| |
| outer = lhs; |
| |
| while (outer |
| && (TREE_CODE (outer) == COMPONENT_REF |
| || TREE_CODE (outer) == ARRAY_REF)) |
| outer = TREE_OPERAND (outer, 0); |
| |
| if (TREE_CODE (outer) == INDIRECT_REF) |
| { |
| outer = TREE_OPERAND (outer, 0); |
| indirect_p = 1; |
| } |
| |
| outer_gc_p = objc_is_gcable_p (outer); |
| |
| /* Handle ivar assignments. */ |
| if (objc_is_ivar_reference_p (lhs)) |
| { |
| /* if the struct to the left of the ivar is not an Objective-C object (__strong |
| doesn't cut it here), the best we can do here is suggest a cast. */ |
| if (!objc_is_gcable_type (TREE_TYPE (outer), 0)) |
| { |
| /* We may still be able to use the global write barrier... */ |
| if (!indirect_p && objc_is_global_reference_p (outer)) |
| goto global_reference; |
| |
| suggest_cast: |
| if (modifycode == NOP_EXPR) |
| { |
| if (warn_assign_intercept) |
| warning (0, "strong-cast may possibly be needed"); |
| } |
| |
| goto exit_point; |
| } |
| |
| if (modifycode != NOP_EXPR) |
| goto invalid_pointer_arithmetic; |
| |
| if (warn_assign_intercept) |
| warning (0, "instance variable assignment has been intercepted"); |
| |
| result = objc_build_ivar_assignment (outer, lhs, rhs); |
| |
| goto exit_point; |
| } |
| |
| /* Likewise, intercept assignment to global/static variables if their type is |
| GC-marked. */ |
| if (objc_is_global_reference_p (outer)) |
| { |
| if (indirect_p) |
| goto suggest_cast; |
| |
| global_reference: |
| if (modifycode != NOP_EXPR) |
| { |
| invalid_pointer_arithmetic: |
| if (outer_gc_p) |
| warning (0, "pointer arithmetic for garbage-collected objects not allowed"); |
| |
| goto exit_point; |
| } |
| |
| if (warn_assign_intercept) |
| warning (0, "global/static variable assignment has been intercepted"); |
| |
| result = objc_build_global_assignment (lhs, rhs); |
| } |
| |
| /* In all other cases, fall back to the normal mechanism. */ |
| exit_point: |
| return result; |
| } |
| |
| struct interface_tuple GTY(()) |
| { |
| tree id; |
| tree class_name; |
| }; |
| |
| static GTY ((param_is (struct interface_tuple))) htab_t interface_htab; |
| |
| static hashval_t |
| hash_interface (const void *p) |
| { |
| const struct interface_tuple *d = (const struct interface_tuple *) p; |
| return IDENTIFIER_HASH_VALUE (d->id); |
| } |
| |
| static int |
| eq_interface (const void *p1, const void *p2) |
| { |
| const struct interface_tuple *d = (const struct interface_tuple *) p1; |
| return d->id == p2; |
| } |
| |
| static tree |
| lookup_interface (tree ident) |
| { |
| #ifdef OBJCPLUS |
| if (ident && TREE_CODE (ident) == TYPE_DECL) |
| ident = DECL_NAME (ident); |
| #endif |
| |
| if (ident == NULL_TREE || TREE_CODE (ident) != IDENTIFIER_NODE) |
| return NULL_TREE; |
| |
| { |
| struct interface_tuple **slot; |
| tree i = NULL_TREE; |
| |
| if (interface_htab) |
| { |
| slot = (struct interface_tuple **) |
| htab_find_slot_with_hash (interface_htab, ident, |
| IDENTIFIER_HASH_VALUE (ident), |
| NO_INSERT); |
| if (slot && *slot) |
| i = (*slot)->class_name; |
|