/* Implement classes and message passing for Objective C. Copyright (C) 1992, 1993, 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 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 2, 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 COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* 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 "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 /* APPLE LOCAL ObjC super dealloc */ 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 */ /* APPLE LOCAL begin new tree dump */ #ifdef ENABLE_DMP_TREE #include "dmp-tree.h" extern int c_dump_tree_p PARAMS ((FILE *, const char *, tree, int)); extern int objc_dump_tree_p PARAMS ((FILE *, const char *, tree, int)); extern lang_dump_tree_p_t objc_prev_lang_dump_tree_p; #endif /* APPLE LOCAL end new tree dump */ /* 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. */ /* APPLE LOCAL ObjC GC */ #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 void synth_module_prologue (void); static tree objc_build_constructor (tree, tree); static void build_module_descriptor (void); static void build_module_initializer_routine (void); static tree init_module_descriptor (tree); static tree build_objc_method_call (int, tree, tree, tree, tree); static void generate_strings (void); static tree get_proto_encoding (tree); static void build_selector_translation_table (void); 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); 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 int is_private (tree); static tree get_super_receiver (void); static void build_objc_exception_stuff (void); static void build_next_objc_exception_stuff (void); /* APPLE LOCAL begin ObjC GC */ static int objc_is_gcable_type (tree, int); static tree objc_substitute_decl (tree, tree, tree); static tree objc_build_ivar_assignment (tree, tree, tree); static tree objc_build_global_assignment (tree, tree); static tree objc_build_strong_cast_assignment (tree, tree); static int objc_is_gcable_p (tree); static int objc_is_ivar_reference_p (tree); static int objc_is_global_reference_p (tree); /* APPLE LOCAL end ObjC GC */ static tree build_ivar_template (void); static tree build_method_template (void); static void build_private_template (tree); static void build_class_template (void); static void build_selector_template (void); static void build_category_template (void); static tree lookup_method_in_hash_lists (tree, int); static void build_super_template (void); static tree build_category_initializer (tree, tree, tree, tree, tree, tree); static tree build_protocol_initializer (tree, tree, tree, tree, tree); static void synth_forward_declarations (void); static int ivar_list_length (tree); static tree get_class_ivars (tree); static void generate_ivar_lists (void); static void generate_dispatch_tables (void); /* APPLE LOCAL ObjC C++ ivars */ static void generate_shared_structures (int); static tree generate_protocol_list (tree); static void build_protocol_reference (tree); /* APPLE LOCAL begin ObjC C++ ivars */ #ifdef OBJCPLUS static void objc_generate_cxx_ctor_or_dtor (bool); static void objc_generate_cxx_cdtors (void); #endif /* APPLE LOCAL end ObjC C++ ivars */ static tree build_keyword_selector (tree); static const char *synth_id_with_class_suffix (const char *, tree); static void generate_static_references (void); static int check_methods_accessible (tree, tree, int); static void encode_aggregate_within (tree, int, int, int, int); static const char *objc_demangle (const char *); /* Hash tables to manage the global pool of method prototypes. */ hash *nst_method_hash_list = 0; hash *cls_method_hash_list = 0; static size_t hash_func (tree); static void hash_init (void); static void hash_enter (hash *, tree); static hash hash_lookup (hash *, tree); static void hash_add_attr (hash, tree); static tree lookup_method (tree, tree); static tree lookup_method_static (tree, tree, int); static void add_method_to_hash_list (hash *, tree); /* APPLE LOCAL objc speedup --dpatel */ static tree add_class (tree, tree); static void add_category (tree, tree); static inline tree lookup_category (tree, tree); 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 get_objc_string_decl (tree, enum string_section); static tree build_objc_string_decl (enum string_section); static tree build_selector_reference_decl (void); static void build_selector_table_decl (void); /* Protocol additions. */ static tree add_protocol (tree); static tree lookup_protocol (tree); static void check_protocol_recursively (tree, tree); static tree lookup_and_install_protocols (tree); /* Type encoding. */ static void encode_type_qualifiers (tree); static void encode_pointer (tree, int, int); static void encode_array (tree, int, int); static void encode_aggregate (tree, int, int); static void encode_next_bitfield (int); static void encode_gnu_bitfield (int, tree, int); 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 objc_types_are_equivalent (tree, tree); /* APPLE LOCAL begin Objective-C */ static int objc_types_share_size_and_alignment (tree, tree); static int comp_proto_with_proto (tree, tree, int); /* APPLE LOCAL end Objective-C */ static tree get_arg_type_list (tree, int, 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 static void synth_self_and_ucmd_args (void); /* Utilities for debugging and error diagnostics. */ static void warn_with_method (const char *, int, tree); static void error_with_ivar (const char *, 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); static void dump_interface (FILE *, tree); /* Everything else. */ static tree lookup_method_in_protocol_list (tree, tree, int); static tree lookup_protocol_in_reflist (tree, tree); static tree start_var_decl (tree, const char *); static void finish_var_decl (tree, tree); static tree create_field_decl (tree, const char *); static tree setup_string_decl (void); static int check_string_class_template (void); static tree my_build_string (int, const char *); static void build_objc_symtab_template (void); static tree init_def_list (tree); static tree init_objc_symtab (tree); static tree build_metadata_decl (const char *, tree); static void forward_declare_categories (void); static void generate_objc_symtab_decl (void); static tree build_selector (tree); static tree build_typed_selector_reference (tree, tree); static tree build_selector_reference (tree); static tree build_class_reference_decl (void); static void add_class_reference (tree); static void build_protocol_template (void); static tree build_descriptor_table_initializer (tree, tree); static tree build_method_prototype_list_template (tree, int); static tree build_method_prototype_template (void); static tree objc_method_parm_type (tree); static int objc_encoded_type_size (tree); static tree encode_method_prototype (tree); static tree generate_descriptor_table (tree, const char *, int, tree, tree); static void generate_method_descriptors (tree); static void generate_protocol_references (tree); static void generate_protocols (void); static void check_ivars (tree, tree); static tree build_ivar_list_template (tree, int); static tree build_method_list_template (tree, int); static tree build_ivar_list_initializer (tree, tree); static tree generate_ivars_list (tree, const char *, int, tree); static tree build_dispatch_table_initializer (tree, tree); static tree generate_dispatch_table (tree, const char *, int, tree); static tree build_shared_structure_initializer (tree, tree, tree, tree, tree, int, tree, tree, tree); static void generate_category (tree); static tree adjust_type_for_id_default (tree); static tree check_duplicates (hash, int, int); static tree receiver_is_class_object (tree, int, int); static int check_methods (tree, tree, int); static int conforms_to_protocol (tree, tree); static void check_protocol (tree, const char *, const char *); static void check_protocols (tree, const char *, const char *); 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 /* APPLE LOCAL ObjC C++ ivars */ #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" /* APPLE LOCAL begin ObjC GC */ #define TAG_ASSIGNIVAR "objc_assign_ivar" #define TAG_ASSIGNGLOBAL "objc_assign_global" #define TAG_ASSIGNSTRONGCAST "objc_assign_strongCast" /* APPLE LOCAL end ObjC GC */ /* APPLE LOCAL begin ObjC direct dispatch */ /* 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 OFFS_MSGSEND_FAST 0xFFFEFF00 #define OFFS_ASSIGNIVAR_FAST 0xFFFEFEC0 /* APPLE LOCAL end ObjC direct dispatch */ /* APPLE LOCAL begin ObjC C++ ivars */ #define TAG_CXX_CONSTRUCT ".cxx_construct" #define TAG_CXX_DESTRUCT ".cxx_destruct" /* APPLE LOCAL end ObjC C++ ivars */ /* GNU-specific tags. */ #define TAG_EXECCLASS "__objc_exec_class" #define TAG_GNUINIT "__objc_gnu_init" /* APPLE LOCAL begin Objective-C */ /* Flags for lookup_method_static(). */ #define OBJC_LOOKUP_CLASS 1 /* Look for class methods. */ #define OBJC_LOOKUP_NO_SUPER 2 /* Do not examine superclasses. */ /* APPLE LOCAL end Objective-C */ /* 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; static hashval_t string_hash (const void *); static int string_eq (const void *, const void *); 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; #ifndef USE_MAPPED_LOCATION /* Force the line number back to 0; check_newline will have raised it to 1, which will make the builtin functions appear not to be built in. */ input_line = 0; #endif /* APPLE LOCAL begin new tree dump */ #ifdef ENABLE_DMP_TREE if (!objc_prev_lang_dump_tree_p) objc_prev_lang_dump_tree_p = set_dump_tree_p (objc_dump_tree_p); /* At this point, objc_prev_lang_dump_tree_p should point at the C tree dump routine (which, in the case of Objective-C++, points at the C++ tree dump routine in turn). */ if (objc_prev_lang_dump_tree_p != &c_dump_tree_p) abort (); SET_MAX_DMP_TREE_CODE (LAST_OBJC_TREE_CODE); #endif /* APPLE LOCAL end new tree dump */ /* 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 /* APPLE LOCAL begin Objective-C */ /* 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 (); /* APPLE LOCAL end Objective-C */ if (gen_declaration_file) fclose (gen_declaration_file); #ifdef OBJCPLUS cp_finish_file (); #endif } /* 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 class, tree super_class, tree protos) { objc_interface_context = objc_ivar_context = start_class (CLASS_INTERFACE_TYPE, class, super_class, protos); objc_public_flag = 0; } void objc_start_category_interface (tree class, tree categ, tree protos) { objc_interface_context = start_class (CATEGORY_INTERFACE_TYPE, class, 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 class, tree super_class) { objc_implementation_context = objc_ivar_context = start_class (CLASS_IMPLEMENTATION_TYPE, class, super_class, NULL_TREE); objc_public_flag = 0; } void objc_start_category_implementation (tree class, tree categ) { objc_implementation_context = start_class (CATEGORY_IMPLEMENTATION_TYPE, class, 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) { /* APPLE LOCAL begin ObjC C++ ivars */ #ifdef OBJCPLUS if (flag_objc_call_cxx_cdtors) objc_generate_cxx_cdtors (); #endif /* APPLE LOCAL end ObjC C++ ivars */ if (objc_implementation_context) { finish_class (objc_implementation_context); objc_ivar_chain = NULL_TREE; objc_implementation_context = NULL_TREE; } else warning ("`@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) { return build_method_decl (objc_inherit_code, rettype, selector, optparms); } 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) #ifdef OBJCPLUS || code == RID_CLASS || code == RID_PUBLIC || code == RID_PROTECTED || code == RID_PRIVATE || code == RID_TRY || code == RID_THROW || code == RID_CATCH #endif ); } /* 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; } int objc_types_compatible_p (tree type1, tree type2) { if (objc_is_object_ptr (type1) || objc_is_object_ptr (type2) || objc_is_class_name (type1) || objc_is_class_name (type2)) { return lhd_types_compatible_p (type1, type2); } else { #ifdef OBJCPLUS return cxx_types_compatible_p (type1, type2); #else return c_types_compatible_p (type1, type2); #endif } } /* Return 1 if LHS and RHS are compatible types for assignment or various other operations. Return 0 if they are incompatible, and return -1 if we choose to not decide (because the types are really just C types, not ObjC specific ones). When the operation is REFLEXIVE (typically comparisons), check for compatibility in either direction; when it's not (typically assignments), don't. This function is called in two cases: when both lhs and rhs are pointers to records (in which case we check protocols too), and when both lhs and rhs are records (in which case we check class inheritance only). Warnings about classes/protocols not implementing a protocol are emitted here (multiple of those warnings might be emitted for a single line!); generic warnings about incompatible assignments and lacks of casts in comparisons are/must be emitted by the caller if we return 0. */ int objc_comptypes (tree lhs, tree rhs, int reflexive) { /* New clause for protocols. */ /* Here we manage the case of a POINTER_TYPE = POINTER_TYPE. We only manage the ObjC ones, and leave the rest to the C code. */ if (TREE_CODE (lhs) == POINTER_TYPE && TREE_CODE (TREE_TYPE (lhs)) == RECORD_TYPE && TREE_CODE (rhs) == POINTER_TYPE && TREE_CODE (TREE_TYPE (rhs)) == RECORD_TYPE) { int lhs_is_proto = IS_PROTOCOL_QUALIFIED_UNTYPED (lhs); int rhs_is_proto = IS_PROTOCOL_QUALIFIED_UNTYPED (rhs); if (lhs_is_proto) { tree lproto, lproto_list = TYPE_PROTOCOL_LIST (lhs); tree rproto, rproto_list; tree p; /* = */ if (rhs_is_proto) { /* Class != id ; id != Class */ if (IS_ID (lhs) != IS_ID (rhs)) return 0; rproto_list = TYPE_PROTOCOL_LIST (rhs); if (!reflexive) { /* An assignment between objects of type 'id '; make sure the protocol on the lhs is supported by the object on the rhs. */ for (lproto = lproto_list; lproto; lproto = TREE_CHAIN (lproto)) { p = TREE_VALUE (lproto); rproto = lookup_protocol_in_reflist (rproto_list, p); if (!rproto) warning ("object does not conform to the `%s' protocol", IDENTIFIER_POINTER (PROTOCOL_NAME (p))); } return 1; } else { /* Obscure case - a comparison between two objects of type 'id '. Check that either the protocol on the lhs is supported by the object on the rhs, or viceversa. */ /* Check if the protocol on the lhs is supported by the object on the rhs. */ for (lproto = lproto_list; lproto; lproto = TREE_CHAIN (lproto)) { p = TREE_VALUE (lproto); rproto = lookup_protocol_in_reflist (rproto_list, p); if (!rproto) { /* Check failed - check if the protocol on the rhs is supported by the object on the lhs. */ for (rproto = rproto_list; rproto; rproto = TREE_CHAIN (rproto)) { p = TREE_VALUE (rproto); lproto = lookup_protocol_in_reflist (lproto_list, p); if (!lproto) { /* This check failed too: incompatible */ return 0; } } return 1; } } return 1; } } /* = * */ else if (TYPED_OBJECT (TREE_TYPE (rhs))) { tree rname = OBJC_TYPE_NAME (TREE_TYPE (rhs)); tree rinter; /* Class != * */ if (IS_CLASS (lhs)) return 0; /* Make sure the protocol is supported by the object on the rhs. */ for (lproto = lproto_list; lproto; lproto = TREE_CHAIN (lproto)) { p = TREE_VALUE (lproto); rproto = 0; rinter = lookup_interface (rname); while (rinter && !rproto) { tree cat; rproto_list = CLASS_PROTOCOL_LIST (rinter); rproto = lookup_protocol_in_reflist (rproto_list, p); /* If the underlying ObjC class does not have the protocol we're looking for, check for "one-off" protocols (e.g., `NSObject *foo;') attached to the rhs. */ if (!rproto) { rproto_list = TYPE_PROTOCOL_LIST (TREE_TYPE (rhs)); rproto = lookup_protocol_in_reflist (rproto_list, p); } /* Check for protocols adopted by categories. */ cat = CLASS_CATEGORY_LIST (rinter); while (cat && !rproto) { rproto_list = CLASS_PROTOCOL_LIST (cat); rproto = lookup_protocol_in_reflist (rproto_list, p); cat = CLASS_CATEGORY_LIST (cat); } rinter = lookup_interface (CLASS_SUPER_NAME (rinter)); } if (!rproto) warning ("class `%s' does not implement the `%s' protocol", IDENTIFIER_POINTER (OBJC_TYPE_NAME (TREE_TYPE (rhs))), IDENTIFIER_POINTER (PROTOCOL_NAME (p))); } return 1; } /* id = id; Class = id */ else if (objc_is_object_id (TREE_TYPE (rhs))) { return 1; } /* id != Class; Class = Class */ else if (objc_is_class_id (TREE_TYPE (rhs))) { return IS_CLASS (lhs); } /* = ?? : let comptypes decide. */ return -1; } else if (rhs_is_proto) { /* * = */ if (TYPED_OBJECT (TREE_TYPE (lhs))) { /* * != Class */ if (IS_CLASS (rhs)) return 0; if (reflexive) { tree rname = OBJC_TYPE_NAME (TREE_TYPE (lhs)); tree rinter; tree rproto, rproto_list = TYPE_PROTOCOL_LIST (rhs); /* Make sure the protocol is supported by the object on the lhs. */ for (rproto = rproto_list; rproto; rproto = TREE_CHAIN (rproto)) { tree p = TREE_VALUE (rproto); tree lproto = 0; rinter = lookup_interface (rname); while (rinter && !lproto) { tree cat; tree lproto_list = CLASS_PROTOCOL_LIST (rinter); lproto = lookup_protocol_in_reflist (lproto_list, p); /* If the underlying ObjC class does not have the protocol we're looking for, check for "one-off" protocols (e.g., `NSObject *foo;') attached to the lhs. */ if (!lproto) { lproto_list = TYPE_PROTOCOL_LIST (TREE_TYPE (lhs)); lproto = lookup_protocol_in_reflist (lproto_list, p); } /* Check for protocols adopted by categories. */ cat = CLASS_CATEGORY_LIST (rinter); while (cat && !lproto) { lproto_list = CLASS_PROTOCOL_LIST (cat); lproto = lookup_protocol_in_reflist (lproto_list, p); cat = CLASS_CATEGORY_LIST (cat); } rinter = lookup_interface (CLASS_SUPER_NAME (rinter)); } if (!lproto) warning ("class `%s' does not implement the `%s' protocol", IDENTIFIER_POINTER (OBJC_TYPE_NAME (TREE_TYPE (lhs))), IDENTIFIER_POINTER (PROTOCOL_NAME (p))); } return 1; } else return 0; } /* id = id ; id = Class */ else if (objc_is_object_id (TREE_TYPE (lhs))) { return 1; } /* Class != id ; Class = Class */ else if (objc_is_class_id (TREE_TYPE (lhs))) { return IS_CLASS (rhs); } /* ??? = : let comptypes decide */ else { return -1; } } else { /* Attention: we shouldn't defer to comptypes here. One bad side effect would be that we might loose the REFLEXIVE information. */ lhs = TREE_TYPE (lhs); rhs = TREE_TYPE (rhs); } } if (TREE_CODE (lhs) != RECORD_TYPE || TREE_CODE (rhs) != RECORD_TYPE) { /* Nothing to do with ObjC - let immediately comptypes take responsibility for checking. */ return -1; } /* `id' = ` *' ` *' = `id': always allow it. Please note that 'Object *o = [[Object alloc] init]; falls in the case * = `id'. */ if ((objc_is_object_id (lhs) && TYPED_OBJECT (rhs)) || (objc_is_object_id (rhs) && TYPED_OBJECT (lhs))) return 1; /* `id' = `Class', `Class' = `id' */ else if ((objc_is_object_id (lhs) && objc_is_class_id (rhs)) || (objc_is_class_id (lhs) && objc_is_object_id (rhs))) return 1; /* `Class' != ` *' && ` *' != `Class'! */ else if ((OBJC_TYPE_NAME (lhs) == objc_class_id && TYPED_OBJECT (rhs)) || (OBJC_TYPE_NAME (rhs) == objc_class_id && TYPED_OBJECT (lhs))) return 0; /* ` *' = ` *' */ else if (TYPED_OBJECT (lhs) && TYPED_OBJECT (rhs)) { tree lname = OBJC_TYPE_NAME (lhs); tree rname = OBJC_TYPE_NAME (rhs); tree inter; if (lname == rname) return 1; /* If the left hand side is a super class of the right hand side, allow it. */ for (inter = lookup_interface (rname); inter; inter = lookup_interface (CLASS_SUPER_NAME (inter))) if (lname == CLASS_SUPER_NAME (inter)) return 1; /* Allow the reverse when reflexive. */ if (reflexive) for (inter = lookup_interface (lname); inter; inter = lookup_interface (CLASS_SUPER_NAME (inter))) if (rname == CLASS_SUPER_NAME (inter)) return 1; return 0; } else /* Not an ObjC type - let comptypes do the check. */ return -1; } /* 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 `%s'", 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) { tree type; if (!interface) type = objc_object_type; else if (!(type = objc_is_id (interface))) { 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); /* Look up protocols and install in lang specific list. Note that the protocol list can have a different lifetime than T! */ SET_TYPE_PROTOCOL_LIST (type, lookup_and_install_protocols (protocols)); /* Establish the ObjC-ness of this record. */ if (TREE_CODE (type) == RECORD_TYPE) TREE_STATIC_TEMPLATE (type) = 1; } 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 `%s' 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) error ("cannot find protocol declaration for `%s'", IDENTIFIER_POINTER (ident)); else return_value = chainon (return_value, build_tree_list (NULL_TREE, p)); } 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 = xmalloc (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'r 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 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)); DECL_IN_SYSTEM_HEADER (type) = 1; type = lang_hooks.decls.pushdecl (build_decl (TYPE_DECL, objc_class_name, objc_class_type)); DECL_IN_SYSTEM_HEADER (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))); 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 = builtin_function (TAG_MSGSEND, type, 0, NOT_BUILT_IN, NULL, NULL_TREE); umsg_nonnil_decl = builtin_function (TAG_MSGSEND_NONNIL, type, 0, NOT_BUILT_IN, NULL, NULL_TREE); umsg_stret_decl = builtin_function (TAG_MSGSEND_STRET, type, 0, NOT_BUILT_IN, NULL, NULL_TREE); umsg_nonnil_stret_decl = builtin_function (TAG_MSGSEND_NONNIL_STRET, type, 0, NOT_BUILT_IN, NULL, NULL_TREE); /* APPLE LOCAL begin ObjC direct dispatch */ /* id objc_msgSend_Fast (id, SEL, ...) __attribute__ ((hard_coded_address (OFFS_MSGSEND_FAST))); */ #ifdef TARGET_POWERPC umsg_fast_decl = builtin_function (TAG_MSGSEND_FAST, type, 0, NOT_BUILT_IN, NULL, NULL_TREE); DECL_ATTRIBUTES (umsg_fast_decl) = tree_cons (get_identifier ("hard_coded_address"), build_int_cst (NULL_TREE, OFFS_MSGSEND_FAST), NULL_TREE); #else /* Not needed on x86 (at least for now). */ umsg_fast_decl = umsg_decl; #endif /* APPLE LOCAL end ObjC direct dispatch */ /* 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 = builtin_function (TAG_MSGSENDSUPER, type, 0, NOT_BUILT_IN, NULL, NULL_TREE); umsg_super_stret_decl = builtin_function (TAG_MSGSENDSUPER_STRET, type, 0, NOT_BUILT_IN, 0, NULL_TREE); } 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 = builtin_function (TAG_MSGSEND, type, 0, NOT_BUILT_IN, NULL, NULL_TREE); /* 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 = builtin_function (TAG_MSGSENDSUPER, type, 0, NOT_BUILT_IN, NULL, NULL_TREE); /* 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 = 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 = builtin_function (TAG_GETCLASS, type, 0, NOT_BUILT_IN, NULL, NULL_TREE); /* id objc_getMetaClass (const char *); */ objc_get_meta_class_decl = 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"); #ifndef OBJCPLUS /* The C++ front-end does not appear to grok __attribute__((__unused__)). */ unused_list = build_tree_list (get_identifier ("__unused__"), NULL_TREE); #endif #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 = TYPE_FIELDS (constant_string_type); #define AT_LEAST_AS_LARGE_AS(F, T) \ (F && TREE_CODE (F) == FIELD_DECL \ && (TREE_INT_CST_LOW (DECL_SIZE (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; /* 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)); } static hashval_t string_hash (const void *ptr) { tree str = ((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) { tree str1 = ((struct string_descriptor *)ptr1)->literal; tree str2 = ((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; /* APPLE LOCAL begin constant cfstrings */ /* The target may have different ideas on how to construct an ObjC string literal. On Darwin (Mac OS X), for example, we may wish to obtain a constant CFString reference instead. */ constructor = (*targetm.construct_objc_string) (string); if (constructor) return build_c_cast (objc_object_type, constructor); /* APPLE LOCAL end constant cfstrings */ /* 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); if (!constant_string_class || !(constant_string_type = CLASS_STATIC_TEMPLATE (constant_string_class))) error ("cannot find interface declaration for `%s'", IDENTIFIER_POINTER (constant_string_id)); /* The NSConstantString/NXConstantString ivar layout is now known. */ else if (!check_string_class_template ()) error ("interface `%s' 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 `%s'", 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 = *loc; if (!desc) { tree var; *loc = desc = ggc_alloc (sizeof (*desc)); desc->literal = string; /* GNU: & ((NXConstantString) { NULL, string, length }) */ /* NeXT: & ((NSConstantString) { isa, string, length }) */ fields = TYPE_FIELDS (constant_string_type); initlist = build_tree_list (fields, flag_next_runtime ? build_unary_op (ADDR_EXPR, string_class_decl, 0) : build_int_cst (NULL_TREE, 0)); fields = TREE_CHAIN (fields); initlist = tree_cons (fields, build_unary_op (ADDR_EXPR, string, 1), initlist); fields = TREE_CHAIN (fields); initlist = tree_cons (fields, build_int_cst (NULL_TREE, length), initlist); constructor = objc_build_constructor (constant_string_type, nreverse (initlist)); TREE_INVARIANT (constructor) = true; 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 = build_unary_op (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; 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 (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) = NULL_TREE; TREE_HAS_CONSTRUCTOR (constructor) = 1; #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 (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 (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 (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 (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_; */ 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_; */ 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); /* name = { ..., "foo.m", ... } */ expr = add_objc_string (get_identifier (input_filename), class_names); initlist = tree_cons (NULL_TREE, expr, initlist); /* symtab = { ..., _OBJC_SYMBOLS, ... } */ if (UOBJC_SYMBOLS_decl) expr = build_unary_op (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 (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, class, 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, ...}. */ class = TREE_VALUE (cl_chain); class_name = get_objc_string_decl (OBJC_TYPE_NAME (class), class_names); initlist = build_tree_list (NULL_TREE, build_unary_op (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 (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 (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); } /* Output all strings. */ static void generate_strings (void) { tree chain, string_expr; tree string, decl, type; for (chain = class_names_chain; chain; chain = TREE_CHAIN (chain)) { string = TREE_VALUE (chain); decl = TREE_PURPOSE (chain); type = build_array_type (char_type_node, build_index_type (build_int_cst (NULL_TREE, IDENTIFIER_LENGTH (string)))); decl = start_var_decl (type, IDENTIFIER_POINTER (DECL_NAME (decl))); string_expr = my_build_string (IDENTIFIER_LENGTH (string) + 1, IDENTIFIER_POINTER (string)); finish_var_decl (decl, string_expr); } for (chain = meth_var_names_chain; chain; chain = TREE_CHAIN (chain)) { string = TREE_VALUE (chain); decl = TREE_PURPOSE (chain); type = build_array_type (char_type_node, build_index_type (build_int_cst (NULL_TREE, IDENTIFIER_LENGTH (string)))); decl = start_var_decl (type, IDENTIFIER_POINTER (DECL_NAME (decl))); string_expr = my_build_string (IDENTIFIER_LENGTH (string) + 1, IDENTIFIER_POINTER (string)); finish_var_decl (decl, string_expr); } for (chain = meth_var_types_chain; chain; chain = TREE_CHAIN (chain)) { string = TREE_VALUE (chain); decl = TREE_PURPOSE (chain); type = build_array_type (char_type_node, build_index_type (build_int_cst (NULL_TREE, IDENTIFIER_LENGTH (string)))); decl = start_var_decl (type, IDENTIFIER_POINTER (DECL_NAME (decl))); string_expr = my_build_string (IDENTIFIER_LENGTH (string) + 1, IDENTIFIER_POINTER (string)); finish_var_decl (decl, string_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) warning ("%Jcreating selector for nonexistent method %qE", TREE_PURPOSE (chain), 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 (ADDR_EXPR, build_array_ref (UOBJC_SELECTOR_TABLE_decl, build_int_cst (NULL_TREE, index)), 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))); 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))); } 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; /* APPLE LOCAL Objective-C++ */ bool local_scope = false; #ifdef OBJCPLUS if (processing_template_decl) /* Must wait until template instantiation time. */ return build_min_nt (CLASS_REFERENCE_EXPR, ident); /* APPLE LOCAL begin Objective-C++ */ if (TREE_CODE (ident) == TYPE_DECL) { /* The type must exist in the global namespace. */ if (DECL_CONTEXT (ident) && DECL_CONTEXT (ident) != global_namespace) local_scope = true; ident = DECL_NAME (ident); } /* APPLE LOCAL end Objective-C++ */ #endif orig_ident = ident; /* APPLE LOCAL Objective-C++ */ if (local_scope || !(ident = objc_is_class_name (ident))) { error ("`%s' 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 (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; 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 (ADDR_EXPR, TREE_PURPOSE (*chain), 1)); chain = &TREE_CHAIN (*chain); } decl = build_objc_string_decl (section); *chain = tree_cons (decl, ident, NULL_TREE); return convert (string_type_node, build_unary_op (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 ("cannot find class `%s'", IDENTIFIER_POINTER (class_ident)); else if (objc_is_class_name (alias_ident)) warning ("class `%s' already exists", IDENTIFIER_POINTER (alias_ident)); else 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); if (record && ! TREE_STATIC_TEMPLATE (record)) { error ("`%s' redeclared as different kind of symbol", IDENTIFIER_POINTER (ident)); error ("%Jprevious declaration of '%D'", record, record); } record = xref_tag (RECORD_TYPE, ident); TREE_STATIC_TEMPLATE (record) = 1; 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; } /* APPLE LOCAL begin ObjC GC */ 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 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)); case INDIRECT_REF: return build_indirect_ref (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 (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 (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 (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 && (TREE_CODE (outer) == CONVERT_EXPR || TREE_CODE (outer) == NOP_EXPR || 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 ("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 ("strong-cast may possibly be needed"); } goto exit_point; } if (modifycode != NOP_EXPR) goto invalid_pointer_arithmetic; if (warn_assign_intercept) warning ("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 ("pointer arithmetic for garbage-collected objects not allowed"); goto exit_point; } if (warn_assign_intercept) warning ("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; } /* APPLE LOCAL end ObjC GC */ static tree lookup_interface (tree ident) { /* APPLE LOCAL objc speedup --dpatel */ /* tree chain; */ #ifdef OBJCPLUS if (ident && TREE_CODE (ident) == TYPE_DECL) ident = DECL_NAME (ident); #endif /* APPLE LOCAL begin objc speedup --dpatel */ return (ident && TREE_CODE (ident) == IDENTIFIER_NODE ? IDENTIFIER_INTERFACE_VALUE (ident) : NULL_TREE); /* APPLE LOCAL end objc speedup --dpatel */ } /* Implement @defs () within struct bodies. */ tree objc_get_class_ivars (tree class_name) { tree interface = lookup_interface (class_name); if (interface) return get_class_ivars (interface); error ("cannot find interface declaration for `%s'", IDENTIFIER_POINTER (class_name)); return error_mark_node; } /* Used by: build_private_template, continue_class, and for @defs constructs. */ static tree get_class_ivars (tree interface) { tree ivar_chain = copy_list (CLASS_RAW_IVARS (interface)); /* Both CLASS_RAW_IVARS and CLASS_IVARS contain a list of ivars declared by the current class (i.e., they do not include super-class ivars). However, the CLASS_IVARS list will be side-effected by a call to finish_struct(), which will fill in field offsets. */ if (!CLASS_IVARS (interface)) CLASS_IVARS (interface) = ivar_chain; while (CLASS_SUPER_NAME (interface)) { /* Prepend super-class ivars. */ interface = lookup_interface (CLASS_SUPER_NAME (interface)); ivar_chain = chainon (copy_list (CLASS_RAW_IVARS (interface)), ivar_chain); } return ivar_chain; } static tree objc_create_temporary_var (tree type) { tree decl; decl = build_decl (VAR_DECL, NULL_TREE, type); TREE_USED (decl) = 1; DECL_ARTIFICIAL (decl) = 1; DECL_IGNORED_P (decl) = 1; DECL_CONTEXT (decl) = current_function_decl; return decl; } /* Exception handling constructs. We begin by having the parser do most of the work and passing us blocks. What we do next depends on whether we're doing "native" exception handling or legacy Darwin setjmp exceptions. We abstract all of this in a handful of appropriately named routines. */ /* Stack of open try blocks. */ struct objc_try_context { struct objc_try_context *outer; /* Statements (or statement lists) as processed by the parser. */ tree try_body; tree finally_body; /* Some file position locations. */ location_t try_locus; location_t end_try_locus; location_t end_catch_locus; location_t finally_locus; location_t end_finally_locus; /* A STATEMENT_LIST of CATCH_EXPRs, appropriate for sticking into op1 of a TRY_CATCH_EXPR. Even when doing Darwin setjmp. */ tree catch_list; /* The CATCH_EXPR of an open @catch clause. */ tree current_catch; /* The VAR_DECL holding the Darwin equivalent of EXC_PTR_EXPR. */ tree caught_decl; tree stack_decl; tree rethrow_decl; }; static struct objc_try_context *cur_try_context; /* This hook, called via lang_eh_runtime_type, generates a runtime object that represents TYPE. For Objective-C, this is just the class name. */ /* ??? Isn't there a class object or some such? Is it easy to get? */ #ifndef OBJCPLUS static tree objc_eh_runtime_type (tree type) { return add_objc_string (OBJC_TYPE_NAME (TREE_TYPE (type)), class_names); } #endif /* Initialize exception handling. */ static void objc_init_exceptions (void) { static bool done = false; if (done) return; done = true; if (flag_objc_sjlj_exceptions) { /* On Darwin, ObjC exceptions require a sufficiently recent version of the runtime, so the user must ask for them explicitly. */ if (!flag_objc_exceptions) warning ("use %<-fobjc-exceptions%> to enable Objective-C " "exception syntax"); } #ifndef OBJCPLUS else { c_eh_initialized_p = true; eh_personality_libfunc = init_one_libfunc (USING_SJLJ_EXCEPTIONS ? "__gnu_objc_personality_sj0" : "__gnu_objc_personality_v0"); using_eh_for_cleanups (); lang_eh_runtime_type = objc_eh_runtime_type; } #endif } /* Build an EXC_PTR_EXPR, or the moral equivalent. In the case of Darwin, we'll arrange for it to be initialized (and associated with a binding) later. */ static tree objc_build_exc_ptr (void) { if (flag_objc_sjlj_exceptions) { tree var = cur_try_context->caught_decl; if (!var) { var = objc_create_temporary_var (objc_object_type); cur_try_context->caught_decl = var; } return var; } else return build (EXC_PTR_EXPR, objc_object_type); } /* Build "objc_exception_try_exit(&_stack)". */ static tree next_sjlj_build_try_exit (void) { tree t; t = build_fold_addr_expr (cur_try_context->stack_decl); t = tree_cons (NULL, t, NULL); t = build_function_call (objc_exception_try_exit_decl, t); return t; } /* Build objc_exception_try_enter (&_stack); if (_setjmp(&_stack.buf)) ; else ; Return the COND_EXPR. Note that the THEN and ELSE fields are left empty, ready for the caller to fill them in. */ static tree next_sjlj_build_enter_and_setjmp (void) { tree t, enter, sj, cond; t = build_fold_addr_expr (cur_try_context->stack_decl); t = tree_cons (NULL, t, NULL); enter = build_function_call (objc_exception_try_enter_decl, t); t = build_component_ref (cur_try_context->stack_decl, get_identifier ("buf")); t = build_fold_addr_expr (t); /* APPLE LOCAL begin Objective-C++ */ #ifdef OBJCPLUS /* Convert _setjmp argument to type that is expected. */ if (TYPE_ARG_TYPES (TREE_TYPE (objc_setjmp_decl))) t = convert (TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (objc_setjmp_decl))), t); else #endif t = convert (ptr_type_node, t); /* APPLE LOCAL end Objective-C++ */ t = tree_cons (NULL, t, NULL); sj = build_function_call (objc_setjmp_decl, t); cond = build (COMPOUND_EXPR, TREE_TYPE (sj), enter, sj); cond = lang_hooks.truthvalue_conversion (cond); return build (COND_EXPR, void_type_node, cond, NULL, NULL); } /* Build DECL = objc_exception_extract(&_stack); */ static tree next_sjlj_build_exc_extract (tree decl) { tree t; t = build_fold_addr_expr (cur_try_context->stack_decl); t = tree_cons (NULL, t, NULL); t = build_function_call (objc_exception_extract_decl, t); t = convert (TREE_TYPE (decl), t); t = build (MODIFY_EXPR, void_type_node, decl, t); return t; } /* Build if (objc_exception_match(obj_get_class(TYPE), _caught) BODY else if (...) ... else { _rethrow = _caught; objc_exception_try_exit(&_stack); } from the sequence of CATCH_EXPRs in the current try context. */ static tree next_sjlj_build_catch_list (void) { tree_stmt_iterator i = tsi_start (cur_try_context->catch_list); tree catch_seq, t; tree *last = &catch_seq; bool saw_id = false; for (; !tsi_end_p (i); tsi_next (&i)) { tree stmt = tsi_stmt (i); tree type = CATCH_TYPES (stmt); tree body = CATCH_BODY (stmt); if (type == NULL) { *last = body; saw_id = true; break; } else { tree args, cond; if (type == error_mark_node) cond = error_mark_node; else { args = tree_cons (NULL, cur_try_context->caught_decl, NULL); t = objc_get_class_reference (OBJC_TYPE_NAME (TREE_TYPE (type))); args = tree_cons (NULL, t, args); t = build_function_call (objc_exception_match_decl, args); cond = lang_hooks.truthvalue_conversion (t); } t = build (COND_EXPR, void_type_node, cond, body, NULL); SET_EXPR_LOCUS (t, EXPR_LOCUS (stmt)); *last = t; last = &COND_EXPR_ELSE (t); } } if (!saw_id) { t = build (MODIFY_EXPR, void_type_node, cur_try_context->rethrow_decl, cur_try_context->caught_decl); SET_EXPR_LOCATION (t, cur_try_context->end_catch_locus); append_to_statement_list (t, last); t = next_sjlj_build_try_exit (); SET_EXPR_LOCATION (t, cur_try_context->end_catch_locus); append_to_statement_list (t, last); } return catch_seq; } /* Build a complete @try-@catch-@finally block for legacy Darwin setjmp exception handling. We aim to build: { struct _objc_exception_data _stack; id volatile _rethrow = 0; try { objc_exception_try_enter (&_stack); if (_setjmp(&_stack.buf)) { id _caught = objc_exception_extract(&_stack); objc_exception_try_enter (&_stack); if (_setjmp(&_stack.buf)) _rethrow = objc_exception_extract(&_stack); else CATCH-LIST } else TRY-BLOCK } finally { if (!_rethrow) objc_exception_try_exit(&_stack); FINALLY-BLOCK if (_rethrow) objc_exception_throw(_rethrow); } } If CATCH-LIST is empty, we can omit all of the block containing "_caught" except for the setting of _rethrow. Note the use of a real TRY_FINALLY_EXPR here, which is not involved in EH per-se, but handles goto and other exits from the block. */ static tree next_sjlj_build_try_catch_finally (void) { tree rethrow_decl, stack_decl, t; tree catch_seq, try_fin, bind; /* Create the declarations involved. */ t = xref_tag (RECORD_TYPE, get_identifier (UTAG_EXCDATA)); stack_decl = objc_create_temporary_var (t); cur_try_context->stack_decl = stack_decl; rethrow_decl = objc_create_temporary_var (objc_object_type); cur_try_context->rethrow_decl = rethrow_decl; TREE_THIS_VOLATILE (rethrow_decl) = 1; TREE_CHAIN (rethrow_decl) = stack_decl; /* Build the outermost variable binding level. */ bind = build (BIND_EXPR, void_type_node, rethrow_decl, NULL, NULL); SET_EXPR_LOCATION (bind, cur_try_context->try_locus); TREE_SIDE_EFFECTS (bind) = 1; /* Initialize rethrow_decl. */ t = build (MODIFY_EXPR, void_type_node, rethrow_decl, convert (objc_object_type, null_pointer_node)); SET_EXPR_LOCATION (t, cur_try_context->try_locus); append_to_statement_list (t, &BIND_EXPR_BODY (bind)); /* Build the outermost TRY_FINALLY_EXPR. */ try_fin = build (TRY_FINALLY_EXPR, void_type_node, NULL, NULL); SET_EXPR_LOCATION (try_fin, cur_try_context->try_locus); TREE_SIDE_EFFECTS (try_fin) = 1; append_to_statement_list (try_fin, &BIND_EXPR_BODY (bind)); /* Create the complete catch sequence. */ if (cur_try_context->catch_list) { tree caught_decl = objc_build_exc_ptr (); catch_seq = build_stmt (BIND_EXPR, caught_decl, NULL, NULL); t = next_sjlj_build_exc_extract (caught_decl); append_to_statement_list (t, &BIND_EXPR_BODY (catch_seq)); t = next_sjlj_build_enter_and_setjmp (); COND_EXPR_THEN (t) = next_sjlj_build_exc_extract (rethrow_decl); COND_EXPR_ELSE (t) = next_sjlj_build_catch_list (); append_to_statement_list (t, &BIND_EXPR_BODY (catch_seq)); } else catch_seq = next_sjlj_build_exc_extract (rethrow_decl); SET_EXPR_LOCATION (catch_seq, cur_try_context->end_try_locus); /* Build the main register-and-try if statement. */ t = next_sjlj_build_enter_and_setjmp (); SET_EXPR_LOCATION (t, cur_try_context->try_locus); COND_EXPR_THEN (t) = catch_seq; COND_EXPR_ELSE (t) = cur_try_context->try_body; TREE_OPERAND (try_fin, 0) = t; /* Build the complete FINALLY statement list. */ t = next_sjlj_build_try_exit (); t = build_stmt (COND_EXPR, lang_hooks.truthvalue_conversion (rethrow_decl), NULL, t); SET_EXPR_LOCATION (t, cur_try_context->finally_locus); append_to_statement_list (t, &TREE_OPERAND (try_fin, 1)); append_to_statement_list (cur_try_context->finally_body, &TREE_OPERAND (try_fin, 1)); t = tree_cons (NULL, rethrow_decl, NULL); t = build_function_call (objc_exception_throw_decl, t); t = build_stmt (COND_EXPR, lang_hooks.truthvalue_conversion (rethrow_decl), t, NULL); SET_EXPR_LOCATION (t, cur_try_context->end_finally_locus); append_to_statement_list (t, &TREE_OPERAND (try_fin, 1)); return bind; } /* Called just after parsing the @try and its associated BODY. We now must prepare for the tricky bits -- handling the catches and finally. */ void objc_begin_try_stmt (location_t try_locus, tree body) { struct objc_try_context *c = xcalloc (1, sizeof (*c)); c->outer = cur_try_context; c->try_body = body; c->try_locus = try_locus; c->end_try_locus = input_location; cur_try_context = c; objc_init_exceptions (); /* APPLE LOCAL begin Objective-C */ if (flag_objc_sjlj_exceptions) objc_mark_locals_volatile (NULL); /* APPLE LOCAL end Objective-C */ } /* Called just after parsing "@catch (parm)". Open a binding level, enter DECL into the binding level, and initialize it. Leave the binding level open while the body of the compound statement is parsed. */ void objc_begin_catch_clause (tree decl) { tree compound, type, t; /* Begin a new scope that the entire catch clause will live in. */ compound = c_begin_compound_stmt (true); /* The parser passed in a PARM_DECL, but what we really want is a VAR_DECL. */ decl = build_decl (VAR_DECL, DECL_NAME (decl), TREE_TYPE (decl)); lang_hooks.decls.pushdecl (decl); /* Since a decl is required here by syntax, don't warn if its unused. */ /* ??? As opposed to __attribute__((unused))? Anyway, this appears to be what the previous objc implementation did. */ TREE_USED (decl) = 1; /* Verify that the type of the catch is valid. It must be a pointer to an Objective-C class, or "id" (which is catch-all). */ type = TREE_TYPE (decl); if (POINTER_TYPE_P (type) && objc_is_object_id (TREE_TYPE (type))) type = NULL; else if (!POINTER_TYPE_P (type) || !TYPED_OBJECT (TREE_TYPE (type))) { error ("@catch parameter is not a known Objective-C class type"); type = error_mark_node; } else if (cur_try_context->catch_list) { /* Examine previous @catch clauses and see if we've already caught the type in question. */ tree_stmt_iterator i = tsi_start (cur_try_context->catch_list); for (; !tsi_end_p (i); tsi_next (&i)) { tree stmt = tsi_stmt (i); t = CATCH_TYPES (stmt); if (t == error_mark_node) continue; if (!t || objc_comptypes (TREE_TYPE (t), TREE_TYPE (type), 0) == 1) { warning ("exception of type %<%T%> will be caught", TREE_TYPE (type)); warning ("%H by earlier handler for %<%T%>", EXPR_LOCUS (stmt), TREE_TYPE (t ? t : objc_object_type)); break; } } } /* Record the data for the catch in the try context so that we can finalize it later. */ t = build_stmt (CATCH_EXPR, type, compound); cur_try_context->current_catch = t; /* Initialize the decl from the EXC_PTR_EXPR we get from the runtime. */ t = objc_build_exc_ptr (); t = convert (TREE_TYPE (decl), t); t = build (MODIFY_EXPR, void_type_node, decl, t); add_stmt (t); } /* Called just after parsing the closing brace of a @catch clause. Close the open binding level, and record a CATCH_EXPR for it. */ void objc_finish_catch_clause (void) { tree c = cur_try_context->current_catch; cur_try_context->current_catch = NULL; cur_try_context->end_catch_locus = input_location; CATCH_BODY (c) = c_end_compound_stmt (CATCH_BODY (c), 1); append_to_statement_list (c, &cur_try_context->catch_list); } /* Called after parsing a @finally clause and its associated BODY. Record the body for later placement. */ void objc_build_finally_clause (location_t finally_locus, tree body) { cur_try_context->finally_body = body; cur_try_context->finally_locus = finally_locus; cur_try_context->end_finally_locus = input_location; } /* Called to finalize a @try construct. */ /* APPLE LOCAL Objective-C++ */ tree objc_finish_try_stmt (void) { struct objc_try_context *c = cur_try_context; tree stmt; if (c->catch_list == NULL && c->finally_body == NULL) error ("`@try' without `@catch' or `@finally'"); /* If we're doing Darwin setjmp exceptions, build the big nasty. */ if (flag_objc_sjlj_exceptions) { if (!cur_try_context->finally_body) { cur_try_context->finally_locus = input_location; cur_try_context->end_finally_locus = input_location; } stmt = next_sjlj_build_try_catch_finally (); } else { /* Otherwise, nest the CATCH inside a FINALLY. */ stmt = c->try_body; if (c->catch_list) { stmt = build_stmt (TRY_CATCH_EXPR, stmt, c->catch_list); SET_EXPR_LOCATION (stmt, cur_try_context->try_locus); } if (c->finally_body) { stmt = build_stmt (TRY_FINALLY_EXPR, stmt, c->finally_body); SET_EXPR_LOCATION (stmt, cur_try_context->try_locus); } } add_stmt (stmt); cur_try_context = c->outer; free (c); /* APPLE LOCAL Objective-C++ */ return stmt; } tree objc_build_throw_stmt (tree throw_expr) { tree args; objc_init_exceptions (); if (throw_expr == NULL) { /* If we're not inside a @catch block, there is no "current exception" to be rethrown. */ if (cur_try_context == NULL || cur_try_context->current_catch == NULL) { error ("%<@throw%> (rethrow) used outside of a @catch block"); return NULL_TREE; } /* Otherwise the object is still sitting in the EXC_PTR_EXPR value that we get from the runtime. */ throw_expr = objc_build_exc_ptr (); } /* A throw is just a call to the runtime throw function with the object as a parameter. */ args = tree_cons (NULL, throw_expr, NULL); return add_stmt (build_function_call (objc_exception_throw_decl, args)); } /* APPLE LOCAL Objective-C++ */ tree objc_build_synchronized (location_t start_locus, tree mutex, tree body) { tree args, call; /* First lock the mutex. */ mutex = save_expr (mutex); args = tree_cons (NULL, mutex, NULL); call = build_function_call (objc_sync_enter_decl, args); SET_EXPR_LOCATION (call, start_locus); add_stmt (call); /* Build the mutex unlock. */ args = tree_cons (NULL, mutex, NULL); call = build_function_call (objc_sync_exit_decl, args); SET_EXPR_LOCATION (call, input_location); /* Put the that and the body in a TRY_FINALLY. */ objc_begin_try_stmt (start_locus, body); objc_build_finally_clause (input_location, call); /* APPLE LOCAL Objective-C++ */ return objc_finish_try_stmt (); } /* Predefine the following data type: struct _objc_exception_data { int buf[_JBLEN]; void *pointers[4]; }; */ /* The following yuckiness should prevent users from having to #include in their code... */ #ifdef TARGET_POWERPC /* snarfed from /usr/include/ppc/setjmp.h */ #define _JBLEN (26 + 36 + 129 + 1) #else /* snarfed from /usr/include/i386/{setjmp,signal}.h */ #define _JBLEN 18 #endif static void build_next_objc_exception_stuff (void) { tree field_decl, field_decl_chain, index, temp_type; objc_exception_data_template = start_struct (RECORD_TYPE, get_identifier (UTAG_EXCDATA)); /* int buf[_JBLEN]; */ index = build_index_type (build_int_cst (NULL_TREE, _JBLEN - 1)); field_decl = create_field_decl (build_array_type (integer_type_node, index), "buf"); field_decl_chain = field_decl; /* void *pointers[4]; */ index = build_index_type (build_int_cst (NULL_TREE, 4 - 1)); field_decl = create_field_decl (build_array_type (ptr_type_node, index), "pointers"); chainon (field_decl_chain, field_decl); finish_struct (objc_exception_data_template, field_decl_chain, NULL_TREE); /* int _setjmp(...); */ /* If the user includes , this shall be superseded by 'int _setjmp(jmp_buf);' */ temp_type = build_function_type (integer_type_node, NULL_TREE); objc_setjmp_decl = builtin_function (TAG_SETJMP, temp_type, 0, NOT_BUILT_IN, NULL, NULL_TREE); /* id objc_exception_extract(struct _objc_exception_data *); */ temp_type = build_function_type (objc_object_type, tree_cons (NULL_TREE, build_pointer_type (objc_exception_data_template), OBJC_VOID_AT_END)); objc_exception_extract_decl = builtin_function (TAG_EXCEPTIONEXTRACT, temp_type, 0, NOT_BUILT_IN, NULL, NULL_TREE); /* void objc_exception_try_enter(struct _objc_exception_data *); */ /* void objc_exception_try_exit(struct _objc_exception_data *); */ temp_type = build_function_type (void_type_node, tree_cons (NULL_TREE, build_pointer_type (objc_exception_data_template), OBJC_VOID_AT_END)); objc_exception_try_enter_decl = builtin_function (TAG_EXCEPTIONTRYENTER, temp_type, 0, NOT_BUILT_IN, NULL, NULL_TREE); objc_exception_try_exit_decl = builtin_function (TAG_EXCEPTIONTRYEXIT, temp_type, 0, NOT_BUILT_IN, NULL, NULL_TREE); /* int objc_exception_match(id, id); */ temp_type = build_function_type (integer_type_node, tree_cons (NULL_TREE, objc_object_type, tree_cons (NULL_TREE, objc_object_type, OBJC_VOID_AT_END))); objc_exception_match_decl = builtin_function (TAG_EXCEPTIONMATCH, temp_type, 0, NOT_BUILT_IN, NULL, NULL_TREE); /* APPLE LOCAL begin ObjC GC */ /* id objc_assign_ivar (id, id, unsigned int); */ /* id objc_assign_ivar_Fast (id, id, unsigned int) __attribute__ ((hard_coded_address (OFFS_ASSIGNIVAR_FAST))); */ temp_type = build_function_type (objc_object_type, tree_cons (NULL_TREE, objc_object_type, tree_cons (NULL_TREE, objc_object_type, tree_cons (NULL_TREE, unsigned_type_node, OBJC_VOID_AT_END)))); objc_assign_ivar_decl = builtin_function (TAG_ASSIGNIVAR, temp_type, 0, NOT_BUILT_IN, NULL, NULL_TREE); #ifdef TARGET_POWERPC objc_assign_ivar_fast_decl = builtin_function (TAG_ASSIGNIVAR_FAST, temp_type, 0, NOT_BUILT_IN, NULL, NULL_TREE); DECL_ATTRIBUTES (objc_assign_ivar_fast_decl) = tree_cons (get_identifier ("hard_coded_address"), build_int_cst (NULL_TREE, OFFS_ASSIGNIVAR_FAST), NULL_TREE); #else /* Not needed on x86 (at least for now). */ objc_assign_ivar_fast_decl = objc_assign_ivar_decl; #endif /* id objc_assign_global (id, id *); */ /* id objc_assign_strongCast (id, id *); */ temp_type = build_function_type (objc_object_type, tree_cons (NULL_TREE, objc_object_type, tree_cons (NULL_TREE, build_pointer_type (objc_object_type), OBJC_VOID_AT_END))); objc_assign_global_decl = builtin_function (TAG_ASSIGNGLOBAL, temp_type, 0, NOT_BUILT_IN, NULL, NULL_TREE); objc_assign_strong_cast_decl = builtin_function (TAG_ASSIGNSTRONGCAST, temp_type, 0, NOT_BUILT_IN, NULL, NULL_TREE); /* APPLE LOCAL end ObjC GC */ } static void build_objc_exception_stuff (void) { tree noreturn_list, nothrow_list, temp_type; noreturn_list = tree_cons (get_identifier ("noreturn"), NULL, NULL); nothrow_list = tree_cons (get_identifier ("nothrow"), NULL, NULL); /* void objc_exception_throw(id) __attribute__((noreturn)); */ /* void objc_sync_enter(id); */ /* void objc_sync_exit(id); */ temp_type = build_function_type (void_type_node, tree_cons (NULL_TREE, objc_object_type, OBJC_VOID_AT_END)); objc_exception_throw_decl = builtin_function (TAG_EXCEPTIONTHROW, temp_type, 0, NOT_BUILT_IN, NULL, noreturn_list); objc_sync_enter_decl = builtin_function (TAG_SYNCENTER, temp_type, 0, NOT_BUILT_IN, NULL, nothrow_list); objc_sync_exit_decl = builtin_function (TAG_SYNCEXIT, temp_type, 0, NOT_BUILT_IN, NULL, nothrow_list); } /* struct { struct _objc_class *isa; ... }; */ /* APPLE LOCAL Objective-C++ */ static void build_private_template (tree class) { /* APPLE LOCAL Objective-C++ */ if (CLASS_STATIC_TEMPLATE (class)) /* APPLE LOCAL Objective-C++ */ uprivate_record = CLASS_STATIC_TEMPLATE (class); else { uprivate_record = start_struct (RECORD_TYPE, CLASS_NAME (class)); /* APPLE LOCAL begin NSFoundation classes not included in -gused (radar #3261135) */ /* FSF Candidate */ /* Set the TREE_USED bit for this struct, so that stab generator can emit stabs for this struct type. */ if (flag_debug_only_used_symbols && TYPE_STUB_DECL (uprivate_record)) TREE_USED (TYPE_STUB_DECL (uprivate_record)) = 1; /* APPLE LOCAL end NSFoundation classes not included in -gused (radar #3261135) */ /* APPLE LOCAL Objective-C++ */ finish_struct (uprivate_record, get_class_ivars (class), NULL_TREE); CLASS_STATIC_TEMPLATE (class) = uprivate_record; /* mark this record as class template - for class type checking */ TREE_STATIC_TEMPLATE (uprivate_record) = 1; } objc_instance_type = build_pointer_type (uprivate_record); /* APPLE LOCAL Objective-C++ */ } /* Begin code generation for protocols... */ /* struct _objc_protocol { struct _objc_class *isa; char *protocol_name; struct _objc_protocol **protocol_list; struct _objc__method_prototype_list *instance_methods; struct _objc__method_prototype_list *class_methods; }; */ static void build_protocol_template (void) { tree field_decl, field_decl_chain; objc_protocol_template = start_struct (RECORD_TYPE, get_identifier (UTAG_PROTOCOL)); /* struct _objc_class *isa; */ field_decl = create_field_decl (build_pointer_type (xref_tag (RECORD_TYPE, get_identifier (UTAG_CLASS))), "isa"); field_decl_chain = field_decl; /* char *protocol_name; */ field_decl = create_field_decl (string_type_node, "protocol_name"); chainon (field_decl_chain, field_decl); /* struct _objc_protocol **protocol_list; */ field_decl = create_field_decl (build_pointer_type (build_pointer_type (objc_protocol_template)), "protocol_list"); chainon (field_decl_chain, field_decl); /* struct objc_method_list *instance_methods; */ field_decl = create_field_decl (build_pointer_type (xref_tag (RECORD_TYPE, get_identifier (UTAG_METHOD_PROTOTYPE_LIST))), "instance_methods"); chainon (field_decl_chain, field_decl); /* struct objc_method_list *class_methods; */ field_decl = create_field_decl (build_pointer_type (xref_tag (RECORD_TYPE, get_identifier (UTAG_METHOD_PROTOTYPE_LIST))), "class_methods"); chainon (field_decl_chain, field_decl); finish_struct (objc_protocol_template, field_decl_chain, NULL_TREE); } static tree build_descriptor_table_initializer (tree type, tree entries) { tree initlist = NULL_TREE; do { tree eltlist = NULL_TREE; eltlist = tree_cons (NULL_TREE, build_selector (METHOD_SEL_NAME (entries)), NULL_TREE); eltlist = tree_cons (NULL_TREE, add_objc_string (METHOD_ENCODING (entries), meth_var_types), eltlist); initlist = tree_cons (NULL_TREE, objc_build_constructor (type, nreverse (eltlist)), initlist); entries = TREE_CHAIN (entries); } while (entries); return objc_build_constructor (build_array_type (type, 0), nreverse (initlist)); } /* struct objc_method_prototype_list { int count; struct objc_method_prototype { SEL name; char *types; } list[1]; }; */ static tree build_method_prototype_list_template (tree list_type, int size) { tree objc_ivar_list_record; tree field_decl, field_decl_chain; /* Generate an unnamed struct definition. */ objc_ivar_list_record = start_struct (RECORD_TYPE, NULL_TREE); /* int method_count; */ field_decl = create_field_decl (integer_type_node, "method_count"); field_decl_chain = field_decl; /* struct objc_method method_list[]; */ field_decl = create_field_decl (build_array_type (list_type, build_index_type (build_int_cst (NULL_TREE, size - 1))), "method_list"); chainon (field_decl_chain, field_decl); finish_struct (objc_ivar_list_record, field_decl_chain, NULL_TREE); return objc_ivar_list_record; } static tree build_method_prototype_template (void) { tree proto_record; tree field_decl, field_decl_chain; proto_record = start_struct (RECORD_TYPE, get_identifier (UTAG_METHOD_PROTOTYPE)); /* SEL _cmd; */ field_decl = create_field_decl (objc_selector_type, "_cmd"); field_decl_chain = field_decl; /* char *method_types; */ field_decl = create_field_decl (string_type_node, "method_types"); chainon (field_decl_chain, field_decl); finish_struct (proto_record, field_decl_chain, NULL_TREE); return proto_record; } static tree objc_method_parm_type (tree type) { type = TREE_VALUE (TREE_TYPE (type)); if (TREE_CODE (type) == TYPE_DECL) type = TREE_TYPE (type); /* APPLE LOCAL Objective-C */ return type; } static int objc_encoded_type_size (tree type) { int sz = int_size_in_bytes (type); /* Make all integer and enum types at least as large as an int. */ if (sz > 0 && INTEGRAL_TYPE_P (type)) sz = MAX (sz, int_size_in_bytes (integer_type_node)); /* Treat arrays as pointers, since that's how they're passed in. */ else if (TREE_CODE (type) == ARRAY_TYPE) sz = int_size_in_bytes (ptr_type_node); return sz; } static tree encode_method_prototype (tree method_decl) { tree parms; int parm_offset, i; char buf[40]; tree result; /* ONEWAY and BYCOPY, for remote object are the only method qualifiers. */ encode_type_qualifiers (TREE_PURPOSE (TREE_TYPE (method_decl))); /* Encode return type. */ encode_type (objc_method_parm_type (method_decl), obstack_object_size (&util_obstack), OBJC_ENCODE_INLINE_DEFS); /* Stack size. */ /* The first two arguments (self and _cmd) are pointers; account for their size. */ i = int_size_in_bytes (ptr_type_node); parm_offset = 2 * i; for (parms = METHOD_SEL_ARGS (method_decl); parms; parms = TREE_CHAIN (parms)) { tree type = objc_method_parm_type (parms); int sz = objc_encoded_type_size (type); /* If a type size is not known, bail out. */ if (sz < 0) { error ("%Jtype '%D' does not have a known size", type, type); /* Pretend that the encoding succeeded; the compilation will fail nevertheless. */ goto finish_encoding; } parm_offset += sz; } sprintf (buf, "%d@0:%d", parm_offset, i); obstack_grow (&util_obstack, buf, strlen (buf)); /* Argument types. */ parm_offset = 2 * i; for (parms = METHOD_SEL_ARGS (method_decl); parms; parms = TREE_CHAIN (parms)) { tree type = objc_method_parm_type (parms); /* Process argument qualifiers for user supplied arguments. */ encode_type_qualifiers (TREE_PURPOSE (TREE_TYPE (parms))); /* Type. */ encode_type (type, obstack_object_size (&util_obstack), OBJC_ENCODE_INLINE_DEFS); /* Compute offset. */ sprintf (buf, "%d", parm_offset); parm_offset += objc_encoded_type_size (type); obstack_grow (&util_obstack, buf, strlen (buf)); } finish_encoding: obstack_1grow (&util_obstack, '\0'); result = get_identifier (obstack_finish (&util_obstack)); obstack_free (&util_obstack, util_firstobj); return result; } static tree generate_descriptor_table (tree type, const char *name, int size, tree list, tree proto) { tree decl, initlist; decl = start_var_decl (type, synth_id_with_class_suffix (name, proto)); initlist = build_tree_list (NULL_TREE, build_int_cst (NULL_TREE, size)); initlist = tree_cons (NULL_TREE, list, initlist); finish_var_decl (decl, objc_build_constructor (type, nreverse (initlist))); return decl; } static void generate_method_descriptors (tree protocol) { tree initlist, chain, method_list_template; tree variable_length_type = xref_tag (RECORD_TYPE, get_identifier (UTAG_METHOD_PROTOTYPE_LIST)); int size; if (!objc_method_prototype_template) objc_method_prototype_template = build_method_prototype_template (); chain = PROTOCOL_CLS_METHODS (protocol); if (chain) { size = list_length (chain); method_list_template = build_method_prototype_list_template (objc_method_prototype_template, size); initlist = build_descriptor_table_initializer (objc_method_prototype_template, chain); UOBJC_CLASS_METHODS_decl = generate_descriptor_table (method_list_template, "_OBJC_PROTOCOL_CLASS_METHODS", size, initlist, protocol); TREE_TYPE (UOBJC_CLASS_METHODS_decl) = variable_length_type; } else UOBJC_CLASS_METHODS_decl = 0; chain = PROTOCOL_NST_METHODS (protocol); if (chain) { size = list_length (chain); method_list_template = build_method_prototype_list_template (objc_method_prototype_template, size); initlist = build_descriptor_table_initializer (objc_method_prototype_template, chain); UOBJC_INSTANCE_METHODS_decl = generate_descriptor_table (method_list_template, "_OBJC_PROTOCOL_INSTANCE_METHODS", size, initlist, protocol); TREE_TYPE (UOBJC_INSTANCE_METHODS_decl) = variable_length_type; } else UOBJC_INSTANCE_METHODS_decl = 0; } static void generate_protocol_references (tree plist) { tree lproto; /* Forward declare protocols referenced. */ for (lproto = plist; lproto; lproto = TREE_CHAIN (lproto)) { tree proto = TREE_VALUE (lproto); if (TREE_CODE (proto) == PROTOCOL_INTERFACE_TYPE && PROTOCOL_NAME (proto)) { if (! PROTOCOL_FORWARD_DECL (proto)) build_protocol_reference (proto); if (PROTOCOL_LIST (proto)) generate_protocol_references (PROTOCOL_LIST (proto)); } } } /* APPLE LOCAL begin ObjC C++ ivars */ /* Generate either '- .cxx_construct' or '- .cxx_destruct' for the current class. */ #ifdef OBJCPLUS static void objc_generate_cxx_ctor_or_dtor (bool dtor) { tree fn, body, compound_stmt, ivar; /* - (id) .cxx_construct { ... return self; } */ /* - (void) .cxx_construct { ... } */ objc_set_method_type (MINUS_EXPR); objc_start_method_definition (objc_build_method_signature (build_tree_list (NULL_TREE, dtor ? void_type_node : objc_object_type), get_identifier (dtor ? TAG_CXX_DESTRUCT : TAG_CXX_CONSTRUCT), make_node (TREE_LIST))); body = begin_function_body (); compound_stmt = begin_compound_stmt (0); ivar = CLASS_IVARS (implementation_template); /* Destroy ivars in reverse order. */ if (dtor) ivar = nreverse (copy_list (ivar)); for (; ivar; ivar = TREE_CHAIN (ivar)) { if (TREE_CODE (ivar) == FIELD_DECL) { tree type = TREE_TYPE (ivar); /* Call the ivar's default constructor or destructor. Do not call the destructor unless a corresponding constructor call has also been made (or is not needed). */ if (IS_AGGR_TYPE (type) && (dtor ? (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type) && (!TYPE_NEEDS_CONSTRUCTING (type) || TYPE_HAS_DEFAULT_CONSTRUCTOR (type))) : (TYPE_NEEDS_CONSTRUCTING (type) && TYPE_HAS_DEFAULT_CONSTRUCTOR (type)))) finish_expr_stmt (build_special_member_call (build_ivar_reference (DECL_NAME (ivar)), dtor ? complete_dtor_identifier : complete_ctor_identifier, NULL_TREE, type, LOOKUP_NORMAL)); } } /* The constructor returns 'self'. */ if (!dtor) finish_return_stmt (self_decl); finish_compound_stmt (compound_stmt); finish_function_body (body); fn = current_function_decl; finish_function (); objc_finish_method_definition (fn); } /* The following routine will examine the current @interface for any non-POD C++ ivars requiring non-trivial construction and/or destruction, and then synthesize special '- .cxx_construct' and/or '- .cxx_destruct' methods which will run the appropriate construction or destruction code. Note that ivars inherited from super-classes are _not_ considered. */ static void objc_generate_cxx_cdtors (void) { bool need_ctor = false, need_dtor = false; tree ivar; /* We do not want to do this for categories, since they do not have their own ivars. */ if (TREE_CODE (objc_implementation_context) != CLASS_IMPLEMENTATION_TYPE) return; /* First, determine if we even need a constructor and/or destructor. */ for (ivar = CLASS_IVARS (implementation_template); ivar; ivar = TREE_CHAIN (ivar)) { if (TREE_CODE (ivar) == FIELD_DECL) { tree type = TREE_TYPE (ivar); if (IS_AGGR_TYPE (type)) { if (TYPE_NEEDS_CONSTRUCTING (type) && TYPE_HAS_DEFAULT_CONSTRUCTOR (type)) /* NB: If a default constructor is not available, we will not be able to initialize this ivar; the add_instance_variable() routine will already have warned about this. */ need_ctor = true; if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type) && (!TYPE_NEEDS_CONSTRUCTING (type) || TYPE_HAS_DEFAULT_CONSTRUCTOR (type))) /* NB: If a default constructor is not available, we will not call the destructor either, for symmetry. */ need_dtor = true; } } } /* Generate '- .cxx_construct' if needed. */ if (need_ctor) objc_generate_cxx_ctor_or_dtor (false); /* Generate '- .cxx_destruct' if needed. */ if (need_dtor) objc_generate_cxx_ctor_or_dtor (true); /* The 'imp_list' variable points at an imp_entry record for the current @implementation. Record the existence of '- .cxx_construct' and/or '- .cxx_destruct' methods therein; it will be included in the metadata for the class. */ if (flag_next_runtime) imp_list->has_cxx_cdtors = (need_ctor || need_dtor); } #endif /* APPLE LOCAL end ObjC C++ ivars */ /* For each protocol which was referenced either from a @protocol() expression, or because a class/category implements it (then a pointer to the protocol is stored in the struct describing the class/category), we create a statically allocated instance of the Protocol class. The code is written in such a way as to generate as few Protocol objects as possible; we generate a unique Protocol instance for each protocol, and we don't generate a Protocol instance if the protocol is never referenced (either from a @protocol() or from a class/category implementation). These statically allocated objects can be referred to via the static (that is, private to this module) symbols _OBJC_PROTOCOL_n. The statically allocated Protocol objects that we generate here need to be fixed up at runtime in order to be used: the 'isa' pointer of the objects need to be set up to point to the 'Protocol' class, as known at runtime. The NeXT runtime fixes up all protocols at program startup time, before main() is entered. It uses a low-level trick to look up all those symbols, then loops on them and fixes them up. The GNU runtime as well fixes up all protocols before user code from the module is executed; it requires pointers to those symbols to be put in the objc_symtab (which is then passed as argument to the function __objc_exec_class() which the compiler sets up to be executed automatically when the module is loaded); setup of those Protocol objects happen in two ways in the GNU runtime: all Protocol objects referred to by a class or category implementation are fixed up when the class/category is loaded; all Protocol objects referred to by a @protocol() expression are added by the compiler to the list of statically allocated instances to fixup (the same list holding the statically allocated constant string objects). Because, as explained above, the compiler generates as few Protocol objects as possible, some Protocol object might end up being referenced multiple times when compiled with the GNU runtime, and end up being fixed up multiple times at runtime initialization. But that doesn't hurt, it's just a little inefficient. */ static void generate_protocols (void) { tree p, encoding; tree decl; tree initlist, protocol_name_expr, refs_decl, refs_expr; /* If a protocol was directly referenced, pull in indirect references. */ for (p = protocol_chain; p; p = TREE_CHAIN (p)) if (PROTOCOL_FORWARD_DECL (p) && PROTOCOL_LIST (p)) generate_protocol_references (PROTOCOL_LIST (p)); for (p = protocol_chain; p; p = TREE_CHAIN (p)) { tree nst_methods = PROTOCOL_NST_METHODS (p); tree cls_methods = PROTOCOL_CLS_METHODS (p); /* If protocol wasn't referenced, don't generate any code. */ decl = PROTOCOL_FORWARD_DECL (p); if (!decl) continue; /* Make sure we link in the Protocol class. */ add_class_reference (get_identifier (PROTOCOL_OBJECT_CLASS_NAME)); while (nst_methods) { if (! METHOD_ENCODING (nst_methods)) {