| /* Implement classes and message passing for Objective C. |
| Copyright (C) 1992, 1993, 1994, 1995, 1997, 1998, 1999, 2000, |
| 2001, 2002, 2003 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" |
| #include "c-tree.h" |
| #include "c-common.h" |
| #include "flags.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" |
| |
| #define OBJC_VOID_AT_END build_tree_list (NULL_TREE, void_type_node) |
| |
| /* This is the default way of generating a method name. */ |
| /* I am not sure it is really correct. |
| Perhaps there's a danger that it will make name conflicts |
| if method names contain underscores. -- rms. */ |
| #ifndef OBJC_GEN_METHOD_LABEL |
| #define OBJC_GEN_METHOD_LABEL(BUF, IS_INST, CLASS_NAME, CAT_NAME, SEL_NAME, NUM) \ |
| do { \ |
| char *temp; \ |
| sprintf ((BUF), "_%s_%s_%s_%s", \ |
| ((IS_INST) ? "i" : "c"), \ |
| (CLASS_NAME), \ |
| ((CAT_NAME)? (CAT_NAME) : ""), \ |
| (SEL_NAME)); \ |
| for (temp = (BUF); *temp; temp++) \ |
| if (*temp == ':') *temp = '_'; \ |
| } while (0) |
| #endif |
| |
| /* These need specifying. */ |
| #ifndef OBJC_FORWARDING_STACK_OFFSET |
| #define OBJC_FORWARDING_STACK_OFFSET 0 |
| #endif |
| |
| #ifndef OBJC_FORWARDING_MIN_OFFSET |
| #define OBJC_FORWARDING_MIN_OFFSET 0 |
| #endif |
| |
| /* Set up for use of obstacks. */ |
| |
| #include "obstack.h" |
| |
| /* This obstack is used to accumulate the encoding of a data type. */ |
| static struct obstack util_obstack; |
| |
| /* This points to the beginning of obstack contents, so we can free |
| the whole contents. */ |
| char *util_firstobj; |
| |
| /* The version identifies which language generation and runtime |
| the module (file) was compiled for, and is recorded in the |
| module descriptor. */ |
| |
| #define OBJC_VERSION (flag_next_runtime ? 5 : 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 rtx build_module_descriptor (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 objc_add_static_instance (tree, tree); |
| |
| static void build_objc_exception_stuff (void); |
| static tree objc_declare_variable (enum rid, tree, tree, tree); |
| static tree objc_enter_block (void); |
| static tree objc_exit_block (void); |
| static void objc_build_try_enter_fragment (void); |
| static void objc_build_try_exit_fragment (void); |
| static void objc_build_extract_fragment (void); |
| static tree objc_build_extract_expr (void); |
| |
| static tree build_ivar_template (void); |
| static tree build_method_template (void); |
| static tree 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, int); |
| static void generate_ivar_lists (void); |
| static void generate_dispatch_tables (void); |
| static void generate_shared_structures (void); |
| static tree generate_protocol_list (tree); |
| static void generate_forward_declaration_to_string_table (void); |
| static void build_protocol_reference (tree); |
| |
| static tree build_keyword_selector (tree); |
| static tree 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 *); |
| static void objc_expand_function_end (void); |
| |
| /* 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); |
| static tree add_class (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); |
| |
| /* 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); |
| |
| static void really_start_method (tree, tree); |
| static int comp_method_with_proto (tree, tree); |
| static int objc_types_are_equivalent (tree, tree); |
| static int comp_proto_with_proto (tree, tree); |
| static tree get_arg_type_list (tree, int, int); |
| static tree objc_expr_last (tree); |
| 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, tree); |
| static char *gen_method_decl (tree, char *); |
| static char *gen_declaration (tree, char *); |
| static void gen_declaration_1 (tree, char *); |
| static char *gen_declarator (tree, char *, const char *); |
| static int is_complex_decl (tree); |
| static void adorn_decl (tree, char *); |
| static void dump_interface (FILE *, tree); |
| |
| /* Everything else. */ |
| |
| static tree define_decl (tree, tree); |
| static tree lookup_method_in_protocol_list (tree, tree, int); |
| static tree lookup_protocol_in_reflist (tree, tree); |
| static tree create_builtin_decl (enum tree_code, tree, const char *); |
| static void 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 tree 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 int is_objc_type_qualifier (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 gen_declspecs (tree, char *, int); |
| 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 TYPE_ID "id" |
| #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 *TAG_EXECCLASS; |
| static const char *default_constant_string_class_name; |
| |
| /* Runtime metadata flags. */ |
| #define CLS_FACTORY 0x0001L |
| #define CLS_META 0x0002L |
| |
| #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 |
| |
| #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 TAG_RETURN_STRUCT "objc_return_struct" |
| |
| #define UTAG_EXCDATA "_objc_exception_data" |
| #define UTAG_EXCDATA_VAR "_stackExceptionData" |
| #define UTAG_CAUGHTEXC_VAR "_caughtException" |
| #define UTAG_RETHROWEXC_VAR "_rethrowException" |
| #define UTAG_EVALONCE_VAR "_eval_once" |
| |
| struct val_stack { |
| long val; |
| struct val_stack *next; |
| }; |
| static struct val_stack *catch_count_stack, *exc_binding_stack; |
| |
| /* useful for debugging */ |
| static int if_nesting_count; |
| static int blk_nesting_count; |
| |
| static void val_stack_push (struct val_stack **, long); |
| static void val_stack_pop (struct val_stack **); |
| |
| /* 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' */ |
| |
| /* 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; |
| |
| 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_builtin_decl (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_builtin_decl (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) |
| { |
| if (c_objc_common_init () == false) |
| return false; |
| |
| /* 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; |
| |
| /* 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"; |
| TAG_EXECCLASS = "__objc_execClass"; |
| 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. */ |
| TAG_EXECCLASS = "__objc_exec_class"; |
| default_constant_string_class_name = "NXConstantString"; |
| flag_typed_selectors = 1; |
| } |
| |
| objc_ellipsis_node = make_node (ERROR_MARK); |
| |
| init_objc (); |
| |
| if (print_struct_values) |
| generate_struct_by_value_array (); |
| |
| return true; |
| } |
| |
| void |
| finish_file (void) |
| { |
| mark_referenced_methods (); |
| c_objc_common_finish_file (); |
| |
| /* Finalize Objective-C runtime data. No need to generate tables |
| and code if only checking syntax. */ |
| if (!flag_syntax_only) |
| finish_objc (); |
| |
| if (gen_declaration_file) |
| fclose (gen_declaration_file); |
| } |
| |
| static tree |
| define_decl (tree declarator, tree declspecs) |
| { |
| tree decl = start_decl (declarator, declspecs, 0, NULL_TREE); |
| finish_decl (decl, NULL_TREE, NULL_TREE); |
| return decl; |
| } |
| |
| static tree |
| lookup_method_in_protocol_list (tree rproto_list, tree sel_name, |
| int class_meth) |
| { |
| 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 (class_meth |
| ? 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, class_meth); |
| } |
| 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; |
| } |
| |
| /* 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_ID (lhs); |
| int rhs_is_proto = IS_PROTOCOL_QUALIFIED_ID (rhs); |
| |
| if (lhs_is_proto) |
| { |
| tree lproto, lproto_list = TYPE_PROTOCOL_LIST (lhs); |
| tree rproto, rproto_list; |
| tree p; |
| |
| /* <Protocol> = <Protocol> */ |
| if (rhs_is_proto) |
| { |
| rproto_list = TYPE_PROTOCOL_LIST (rhs); |
| |
| if (!reflexive) |
| { |
| /* An assignment between objects of type 'id |
| <Protocol>'; 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 <Protocol>'. 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; |
| } |
| } |
| /* <Protocol> = <class> * */ |
| else if (TYPED_OBJECT (TREE_TYPE (rhs))) |
| { |
| tree rname = OBJC_TYPE_NAME (TREE_TYPE (rhs)); |
| tree rinter; |
| |
| /* 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<MyProt> *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; |
| } |
| /* <Protocol> = id */ |
| else if (OBJC_TYPE_NAME (TREE_TYPE (rhs)) == objc_object_id) |
| { |
| return 1; |
| } |
| /* <Protocol> = Class */ |
| else if (OBJC_TYPE_NAME (TREE_TYPE (rhs)) == objc_class_id) |
| { |
| return 0; |
| } |
| /* <Protocol> = ?? : let comptypes decide. */ |
| return -1; |
| } |
| else if (rhs_is_proto) |
| { |
| /* <class> * = <Protocol> */ |
| if (TYPED_OBJECT (TREE_TYPE (lhs))) |
| { |
| 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<MyProt> *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 = <Protocol> */ |
| else if (OBJC_TYPE_NAME (TREE_TYPE (lhs)) == objc_object_id) |
| { |
| return 1; |
| } |
| /* Class = <Protocol> */ |
| else if (OBJC_TYPE_NAME (TREE_TYPE (lhs)) == objc_class_id) |
| { |
| return 0; |
| } |
| /* ??? = <Protocol> : 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' = `<class> *' `<class> *' = `id': always allow it. |
| Please note that |
| 'Object *o = [[Object alloc] init]; falls |
| in the case <class> * = `id'. |
| */ |
| if ((OBJC_TYPE_NAME (lhs) == objc_object_id && TYPED_OBJECT (rhs)) |
| || (OBJC_TYPE_NAME (rhs) == objc_object_id && TYPED_OBJECT (lhs))) |
| return 1; |
| |
| /* `id' = `Class', `Class' = `id' */ |
| |
| else if ((OBJC_TYPE_NAME (lhs) == objc_object_id |
| && OBJC_TYPE_NAME (rhs) == objc_class_id) |
| || (OBJC_TYPE_NAME (lhs) == objc_class_id |
| && OBJC_TYPE_NAME (rhs) == objc_object_id)) |
| return 1; |
| |
| /* `<class> *' = `<class> *' */ |
| |
| 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 (TYPE_NAME (type) && (type = is_class_name (TYPE_NAME (type)))) |
| error ("statically allocated instance of Objective-C class `%s'", |
| IDENTIFIER_POINTER (type)); |
| } |
| |
| /* Implement static typing. At this point, we know we have an interface. */ |
| |
| tree |
| get_static_reference (tree interface, tree protocols) |
| { |
| tree type = xref_tag (RECORD_TYPE, interface); |
| |
| if (protocols) |
| { |
| tree t, m = TYPE_MAIN_VARIANT (type); |
| |
| t = copy_node (type); |
| |
| /* Add this type to the chain of variants of TYPE. */ |
| TYPE_NEXT_VARIANT (t) = TYPE_NEXT_VARIANT (m); |
| TYPE_NEXT_VARIANT (m) = t; |
| |
| /* 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 (t, lookup_and_install_protocols (protocols)); |
| |
| /* This forces a new pointer type to be created later |
| (in build_pointer_type)...so that the new template |
| we just created will actually be used...what a hack! */ |
| if (TYPE_POINTER_TO (t)) |
| TYPE_POINTER_TO (t) = NULL_TREE; |
| |
| type = t; |
| } |
| |
| return type; |
| } |
| |
| tree |
| get_object_reference (tree protocols) |
| { |
| tree type_decl = lookup_name (objc_id_id); |
| tree type; |
| |
| if (type_decl && TREE_CODE (type_decl) == TYPE_DECL) |
| { |
| type = TREE_TYPE (type_decl); |
| if (TYPE_MAIN_VARIANT (type) != id_type) |
| warning ("unexpected type for `id' (%s)", |
| gen_declaration (type, errbuf)); |
| } |
| else |
| { |
| error ("undefined type `id', please import <objc/objc.h>"); |
| return error_mark_node; |
| } |
| |
| /* This clause creates a new pointer type that is qualified with |
| the protocol specification...this info is used later to do more |
| elaborate type checking. */ |
| |
| if (protocols) |
| { |
| tree t, m = TYPE_MAIN_VARIANT (type); |
| |
| t = copy_node (type); |
| |
| /* Add this type to the chain of variants of TYPE. */ |
| TYPE_NEXT_VARIANT (t) = TYPE_NEXT_VARIANT (m); |
| TYPE_NEXT_VARIANT (m) = t; |
| |
| /* Look up protocols...and install in lang specific list */ |
| SET_TYPE_PROTOCOL_LIST (t, lookup_and_install_protocols (protocols)); |
| |
| /* This forces a new pointer type to be created later |
| (in build_pointer_type)...so that the new template |
| we just created will actually be used...what a hack! */ |
| if (TYPE_POINTER_TO (t)) |
| TYPE_POINTER_TO (t) = NULL_TREE; |
| |
| type = t; |
| } |
| 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 and push a decl for a built-in external variable or field NAME. |
| CODE says which. |
| TYPE is its data type. */ |
| |
| static tree |
| create_builtin_decl (enum tree_code code, tree type, const char *name) |
| { |
| tree decl = build_decl (code, get_identifier (name), type); |
| |
| if (code == VAR_DECL) |
| { |
| TREE_STATIC (decl) = 1; |
| make_decl_rtl (decl, 0); |
| pushdecl (decl); |
| DECL_ARTIFICIAL (decl) = 1; |
| } |
| |
| return decl; |
| } |
| |
| /* Find the decl for the constant string class. */ |
| |
| static void |
| setup_string_decl (void) |
| { |
| if (!string_class_decl) |
| { |
| if (!constant_string_global_id) |
| { |
| 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); |
| } |
| } |
| |
| /* 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 temp_type; |
| |
| /* Defined in `objc.h' */ |
| objc_object_id = get_identifier (TAG_OBJECT); |
| |
| objc_object_reference = xref_tag (RECORD_TYPE, objc_object_id); |
| |
| id_type = build_pointer_type (objc_object_reference); |
| |
| objc_id_id = get_identifier (TYPE_ID); |
| objc_class_id = get_identifier (TAG_CLASS); |
| |
| objc_class_type = build_pointer_type (xref_tag (RECORD_TYPE, objc_class_id)); |
| temp_type = get_identifier (PROTOCOL_OBJECT_CLASS_NAME); |
| objc_declare_class (tree_cons (NULL_TREE, temp_type, NULL_TREE)); |
| protocol_type = build_pointer_type (xref_tag (RECORD_TYPE, |
| temp_type)); |
| |
| /* Declare type of selector-objects that represent an operation name. */ |
| |
| /* `struct objc_selector *' */ |
| selector_type |
| = build_pointer_type (xref_tag (RECORD_TYPE, |
| get_identifier (TAG_SELECTOR))); |
| |
| /* Forward declare type, or else the prototype for msgSendSuper will |
| complain. */ |
| |
| /* `struct objc_super *' */ |
| super_type = build_pointer_type (xref_tag (RECORD_TYPE, |
| get_identifier (TAG_SUPER))); |
| |
| |
| /* id objc_msgSend (id, SEL, ...); */ |
| |
| temp_type |
| = build_function_type (id_type, |
| tree_cons (NULL_TREE, id_type, |
| tree_cons (NULL_TREE, selector_type, |
| NULL_TREE))); |
| |
| if (! flag_next_runtime) |
| { |
| umsg_decl = build_decl (FUNCTION_DECL, |
| get_identifier (TAG_MSGSEND), temp_type); |
| DECL_EXTERNAL (umsg_decl) = 1; |
| TREE_PUBLIC (umsg_decl) = 1; |
| DECL_INLINE (umsg_decl) = 1; |
| DECL_ARTIFICIAL (umsg_decl) = 1; |
| |
| make_decl_rtl (umsg_decl, NULL); |
| pushdecl (umsg_decl); |
| } |
| else |
| { |
| umsg_decl = builtin_function (TAG_MSGSEND, |
| temp_type, 0, NOT_BUILT_IN, |
| NULL, NULL_TREE); |
| /* id objc_msgSendNonNil (id, SEL, ...); */ |
| umsg_nonnil_decl = builtin_function (TAG_MSGSEND_NONNIL, |
| temp_type, 0, NOT_BUILT_IN, |
| NULL, NULL_TREE); |
| } |
| |
| /* id objc_msgSendSuper (struct objc_super *, SEL, ...); */ |
| |
| temp_type |
| = build_function_type (id_type, |
| tree_cons (NULL_TREE, super_type, |
| tree_cons (NULL_TREE, selector_type, |
| NULL_TREE))); |
| |
| umsg_super_decl = builtin_function (TAG_MSGSENDSUPER, |
| temp_type, 0, NOT_BUILT_IN, |
| NULL, NULL_TREE); |
| |
| /* The NeXT runtime defines the following additional entry points, |
| used for dispatching calls to methods returning structs: |
| |
| #if defined(__cplusplus) |
| id objc_msgSend_stret(id self, SEL op, ...); |
| id objc_msgSendSuper_stret(struct objc_super *super, SEL op, ...); |
| #else |
| void objc_msgSend_stret(void * stretAddr, id self, SEL op, ...); |
| void objc_msgSendSuper_stret(void * stretAddr, struct objc_super *super, |
| SEL op, ...); |
| #endif |
| |
| struct objc_return_struct objc_msgSendNonNil_stret(id self, SEL op, ...); |
| |
| These prototypes appear in <objc/objc-runtime.h>; however, they |
| CANNOT BE USED DIRECTLY. In order to call one of the ..._stret |
| 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. */ |
| |
| if (flag_next_runtime) |
| { |
| tree objc_return_struct_type |
| = xref_tag (RECORD_TYPE, |
| get_identifier (TAG_RETURN_STRUCT)); |
| |
| tree stret_temp_type |
| = build_function_type (id_type, |
| tree_cons (NULL_TREE, id_type, |
| tree_cons (NULL_TREE, selector_type, |
| NULL_TREE))); |
| |
| umsg_stret_decl = builtin_function (TAG_MSGSEND_STRET, |
| stret_temp_type, 0, NOT_BUILT_IN, |
| NULL, NULL_TREE); |
| stret_temp_type |
| = build_function_type (objc_return_struct_type, |
| tree_cons (NULL_TREE, id_type, |
| tree_cons (NULL_TREE, selector_type, |
| NULL_TREE))); |
| |
| umsg_nonnil_stret_decl = builtin_function (TAG_MSGSEND_NONNIL_STRET, |
| stret_temp_type, 0, NOT_BUILT_IN, |
| NULL, NULL_TREE); |
| |
| stret_temp_type |
| = build_function_type (id_type, |
| tree_cons (NULL_TREE, super_type, |
| tree_cons (NULL_TREE, selector_type, |
| NULL_TREE))); |
| |
| umsg_super_stret_decl = builtin_function (TAG_MSGSENDSUPER_STRET, |
| stret_temp_type, 0, NOT_BUILT_IN, 0, |
| NULL_TREE); |
| } |
| |
| /* id objc_getClass (const char *); */ |
| |
| temp_type = build_function_type (id_type, |
| tree_cons (NULL_TREE, |
| const_string_type_node, |
| OBJC_VOID_AT_END)); |
| |
| objc_get_class_decl |
| = builtin_function (TAG_GETCLASS, temp_type, 0, NOT_BUILT_IN, |
| NULL, NULL_TREE); |
| |
| /* id objc_getMetaClass (const char *); */ |
| |
| objc_get_meta_class_decl |
| = builtin_function (TAG_GETMETACLASS, temp_type, 0, NOT_BUILT_IN, NULL, NULL_TREE); |
| |
| build_super_template (); |
| if (flag_next_runtime) |
| build_objc_exception_stuff (); |
| |
| /* static SEL _OBJC_SELECTOR_TABLE[]; */ |
| |
| if (! flag_next_runtime) |
| { |
| if (flag_typed_selectors) |
| { |
| /* Suppress outputting debug symbols, because |
| dbxout_init hasn'r been called yet. */ |
| enum debug_info_type save_write_symbols = write_symbols; |
| const struct gcc_debug_hooks *const save_hooks = debug_hooks; |
| write_symbols = NO_DEBUG; |
| debug_hooks = &do_nothing_debug_hooks; |
| |
| build_selector_template (); |
| temp_type = build_array_type (objc_selector_template, NULL_TREE); |
| |
| write_symbols = save_write_symbols; |
| debug_hooks = save_hooks; |
| } |
| else |
| temp_type = build_array_type (selector_type, NULL_TREE); |
| |
| layout_type (temp_type); |
| UOBJC_SELECTOR_TABLE_decl |
| = create_builtin_decl (VAR_DECL, temp_type, |
| "_OBJC_SELECTOR_TABLE"); |
| |
| /* Avoid warning when not sending messages. */ |
| TREE_USED (UOBJC_SELECTOR_TABLE_decl) = 1; |
| } |
| |
| generate_forward_declaration_to_string_table (); |
| |
| /* 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 |
| } |
| |
| /* 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)); |
| } |
| |
| /* 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 |
| build_objc_string_object (tree string) |
| { |
| tree initlist, constructor, constant_string_class; |
| int length; |
| |
| string = fix_string_type (string); |
| |
| 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)); |
| return error_mark_node; |
| } |
| |
| /* Call to 'combine_strings' has been moved above. */ |
| TREE_SET_CODE (string, STRING_CST); |
| length = TREE_STRING_LENGTH (string) - 1; |
| |
| if (!string_layout_checked) |
| { |
| /* The NSConstantString/NXConstantString ivar layout is now |
| known. */ |
| if (!check_string_class_template ()) |
| { |
| error ("interface `%s' does not have valid constant string layout", |
| IDENTIFIER_POINTER (constant_string_id)); |
| return error_mark_node; |
| } |
| add_class_reference (constant_string_id); |
| } |
| |
| /* & ((NXConstantString) { NULL, string, length }) */ |
| |
| if (flag_next_runtime) |
| { |
| /* For the NeXT runtime, we can generate a literal reference |
| to the string class, don't need to run a constructor. */ |
| setup_string_decl (); |
| if (string_class_decl == NULL_TREE) |
| { |
| error ("cannot find reference tag for class `%s'", |
| IDENTIFIER_POINTER (constant_string_id)); |
| return error_mark_node; |
| } |
| initlist = build_tree_list |
| (NULL_TREE, |
| copy_node (build_unary_op (ADDR_EXPR, string_class_decl, 0))); |
| } |
| else |
| { |
| initlist = build_tree_list (NULL_TREE, build_int_2 (0, 0)); |
| } |
| |
| initlist |
| = tree_cons (NULL_TREE, copy_node (build_unary_op (ADDR_EXPR, string, 1)), |
| initlist); |
| initlist = tree_cons (NULL_TREE, build_int_2 (length, 0), initlist); |
| constructor = objc_build_constructor (constant_string_type, |
| nreverse (initlist)); |
| |
| if (!flag_next_runtime) |
| { |
| constructor |
| = objc_add_static_instance (constructor, constant_string_type); |
| } |
| |
| return (build_unary_op (ADDR_EXPR, constructor, 1)); |
| } |
| |
| /* 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, 0, 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, f, e; |
| |
| /* ??? Most of the places that we build constructors, we don't fill in |
| the type of integers properly. Convert them all en masse. */ |
| if (TREE_CODE (type) == ARRAY_TYPE) |
| { |
| f = TREE_TYPE (type); |
| if (TREE_CODE (f) == POINTER_TYPE || TREE_CODE (f) == INTEGER_TYPE) |
| for (e = elts; e ; e = TREE_CHAIN (e)) |
| TREE_VALUE (e) = convert (f, TREE_VALUE (e)); |
| } |
| else |
| { |
| f = TYPE_FIELDS (type); |
| for (e = elts; e && f; e = TREE_CHAIN (e), f = TREE_CHAIN (f)) |
| if (TREE_CODE (TREE_TYPE (f)) == POINTER_TYPE |
| || TREE_CODE (TREE_TYPE (f)) == INTEGER_TYPE) |
| TREE_VALUE (e) = convert (TREE_TYPE (f), TREE_VALUE (e)); |
| } |
| |
| constructor = build_constructor (type, elts); |
| TREE_CONSTANT (constructor) = 1; |
| TREE_STATIC (constructor) = 1; |
| TREE_READONLY (constructor) = 1; |
| |
| #ifdef OBJCPLUS |
| /* zlaski 2001-Apr-02: mark this as a call to a constructor, as required by |
| build_unary_op (wasn't true in 2.7.2.1 days) */ |
| 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_builtin_decl (FIELD_DECL, |
| long_integer_type_node, |
| "sel_ref_cnt"); |
| field_decl_chain = field_decl; |
| |
| /* SEL *refs; */ |
| |
| field_decl = create_builtin_decl (FIELD_DECL, |
| build_pointer_type (selector_type), |
| "refs"); |
| chainon (field_decl_chain, field_decl); |
| |
| /* short cls_def_cnt; */ |
| |
| field_decl = create_builtin_decl (FIELD_DECL, |
| short_integer_type_node, |
| "cls_def_cnt"); |
| chainon (field_decl_chain, field_decl); |
| |
| /* short cat_def_cnt; */ |
| |
| field_decl = create_builtin_decl (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_builtin_decl |
| (FIELD_DECL, |
| build_array_type |
| (ptr_type_node, |
| build_index_type (build_int_2 (index, 0))), |
| "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_2 (0, 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_2 (0, 0)); |
| |
| /* refs = { ..., _OBJC_SELECTOR_TABLE, ... } */ |
| |
| if (flag_next_runtime || ! sel_ref_chain) |
| initlist = tree_cons (NULL_TREE, build_int_2 (0, 0), initlist); |
| else |
| initlist = tree_cons (NULL_TREE, |
| build_unary_op (ADDR_EXPR, |
| UOBJC_SELECTOR_TABLE_decl, 1), |
| initlist); |
| |
| /* cls_def_cnt = { ..., 5, ... } */ |
| |
| initlist = tree_cons (NULL_TREE, build_int_2 (imp_count, 0), initlist); |
| |
| /* cat_def_cnt = { ..., 5, ... } */ |
| |
| initlist = tree_cons (NULL_TREE, build_int_2 (cat_count, 0), 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, decl_specs; |
| /* extern struct TYPE NAME_<name>; */ |
| decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_EXTERN]); |
| decl_specs = tree_cons (NULL_TREE, type, decl_specs); |
| decl = define_decl (synth_id_with_class_suffix |
| (name, |
| objc_implementation_context), |
| decl_specs); |
| TREE_USED (decl) = 1; |
| DECL_ARTIFICIAL (decl) = 1; |
| TREE_PUBLIC (decl) = 0; |
| return decl; |
| } |
| |
| /* Push forward-declarations of all the categories so that |
| init_def_list can use them in a CONSTRUCTOR. */ |
| |
| static void |
| forward_declare_categories (void) |
| { |
| struct imp_entry *impent; |
| tree sav = objc_implementation_context; |
| |
| for (impent = imp_list; impent; impent = impent->next) |
| { |
| if (TREE_CODE (impent->imp_context) == CATEGORY_IMPLEMENTATION_TYPE) |
| { |
| /* Set an invisible arg to synth_id_with_class_suffix. */ |
| objc_implementation_context = impent->imp_context; |
| /* extern struct objc_category _OBJC_CATEGORY_<name>; */ |
| impent->class_decl = build_metadata_decl ("_OBJC_CATEGORY", |
| objc_category_template); |
| } |
| } |
| objc_implementation_context = sav; |
| } |
| |
| /* Create the declaration of _OBJC_SYMBOLS, with type `struct _objc_symtab' |
| and initialized appropriately. */ |
| |
| static void |
| generate_objc_symtab_decl (void) |
| { |
| tree sc_spec; |
| |
| if (!objc_category_template) |
| build_category_template (); |
| |
| /* forward declare categories */ |
| if (cat_count) |
| forward_declare_categories (); |
| |
| if (!objc_symtab_template) |
| build_objc_symtab_template (); |
| |
| sc_spec = build_tree_list (NULL_TREE, ridpointers[(int) RID_STATIC]); |
| |
| UOBJC_SYMBOLS_decl = start_decl (get_identifier ("_OBJC_SYMBOLS"), |
| tree_cons (NULL_TREE, |
| objc_symtab_template, sc_spec), |
| 1, |
| NULL_TREE); |
| |
| TREE_USED (UOBJC_SYMBOLS_decl) = 1; |
| DECL_IGNORED_P (UOBJC_SYMBOLS_decl) = 1; |
| DECL_ARTIFICIAL (UOBJC_SYMBOLS_decl) = 1; |
| finish_decl (UOBJC_SYMBOLS_decl, |
| init_objc_symtab (TREE_TYPE (UOBJC_SYMBOLS_decl)), |
| NULL_TREE); |
| } |
| |
| static tree |
| init_module_descriptor (tree type) |
| { |
| tree initlist, expr; |
| |
| /* version = { 1, ... } */ |
| |
| expr = build_int_2 (OBJC_VERSION, 0); |
| initlist = build_tree_list (NULL_TREE, expr); |
| |
| /* size = { ..., sizeof (struct objc_module), ... } */ |
| |
| expr = 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_2 (0, 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. |
| If appropriate, compile and output a setup function to initialize them. |
| Return a symbol_ref to the function to call to initialize the Objective C |
| data structures for this file (and perhaps for other files also). |
| |
| struct objc_module { ... } _OBJC_MODULE = { ... }; */ |
| |
| static rtx |
| build_module_descriptor (void) |
| { |
| tree decl_specs, field_decl, field_decl_chain; |
| |
| objc_module_template |
| = start_struct (RECORD_TYPE, get_identifier (UTAG_MODULE)); |
| |
| /* Long version; */ |
| |
| decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_LONG]); |
| field_decl = get_identifier ("version"); |
| field_decl = grokfield (field_decl, decl_specs, NULL_TREE); |
| field_decl_chain = field_decl; |
| |
| /* long size; */ |
| |
| decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_LONG]); |
| field_decl = get_identifier ("size"); |
| field_decl = grokfield (field_decl, decl_specs, NULL_TREE); |
| chainon (field_decl_chain, field_decl); |
| |
| /* char *name; */ |
| |
| decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_CHAR]); |
| field_decl = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("name")); |
| field_decl = grokfield (field_decl, decl_specs, NULL_TREE); |
| chainon (field_decl_chain, field_decl); |
| |
| /* struct objc_symtab *symtab; */ |
| |
| decl_specs = get_identifier (UTAG_SYMTAB); |
| decl_specs = build_tree_list (NULL_TREE, xref_tag (RECORD_TYPE, decl_specs)); |
| field_decl = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("symtab")); |
| field_decl = grokfield (field_decl, decl_specs, NULL_TREE); |
| chainon (field_decl_chain, field_decl); |
| |
| finish_struct (objc_module_template, field_decl_chain, NULL_TREE); |
| |
| /* Create an instance of "objc_module". */ |
| |
| decl_specs = tree_cons (NULL_TREE, objc_module_template, |
| build_tree_list (NULL_TREE, |
| ridpointers[(int) RID_STATIC])); |
| |
| UOBJC_MODULES_decl = start_decl (get_identifier ("_OBJC_MODULES"), |
| decl_specs, 1, NULL_TREE); |
| |
| DECL_ARTIFICIAL (UOBJC_MODULES_decl) = 1; |
| DECL_IGNORED_P (UOBJC_MODULES_decl) = 1; |
| DECL_CONTEXT (UOBJC_MODULES_decl) = NULL_TREE; |
| |
| finish_decl (UOBJC_MODULES_decl, |
| init_module_descriptor (TREE_TYPE (UOBJC_MODULES_decl)), |
| NULL_TREE); |
| |
| /* Mark the decl to avoid "defined but not used" warning. */ |
| DECL_IN_SYSTEM_HEADER (UOBJC_MODULES_decl) = 1; |
| |
| /* Generate a constructor call for the module descriptor. |
| This code was generated by reading the grammar rules |
| of c-parse.in; Therefore, it may not be the most efficient |
| way of generating the requisite code. */ |
| |
| if (flag_next_runtime) |
| return NULL_RTX; |
| |
| { |
| tree parms, execclass_decl, decelerator, void_list_node_1; |
| tree init_function_name, init_function_decl; |
| |
| /* Declare void __objc_execClass (void *); */ |
| |
| void_list_node_1 = build_tree_list (NULL_TREE, void_type_node); |
| execclass_decl = build_decl (FUNCTION_DECL, |
| get_identifier (TAG_EXECCLASS), |
| build_function_type (void_type_node, |
| tree_cons (NULL_TREE, ptr_type_node, |
| OBJC_VOID_AT_END))); |
| |
| DECL_EXTERNAL (execclass_decl) = 1; |
| DECL_ARTIFICIAL (execclass_decl) = 1; |
| TREE_PUBLIC (execclass_decl) = 1; |
| pushdecl (execclass_decl); |
| rest_of_decl_compilation (execclass_decl, 0, 0, 0); |
| assemble_external (execclass_decl); |
| |
| /* void _GLOBAL_$I$<gnyf> () {objc_execClass (&L_OBJC_MODULES);} */ |
| |
| init_function_name = get_file_function_name ('I'); |
| start_function (void_list_node_1, |
| build_nt (CALL_EXPR, init_function_name, |
| tree_cons (NULL_TREE, NULL_TREE, |
| OBJC_VOID_AT_END), |
| NULL_TREE), |
| NULL_TREE); |
| store_parm_decls (); |
| |
| init_function_decl = current_function_decl; |
| TREE_PUBLIC (init_function_decl) = ! targetm.have_ctors_dtors; |
| TREE_USED (init_function_decl) = 1; |
| /* Don't let this one be deferred. */ |
| DECL_INLINE (init_function_decl) = 0; |
| DECL_UNINLINABLE (init_function_decl) = 1; |
| current_function_cannot_inline |
| = "static constructors and destructors cannot be inlined"; |
| |
| parms |
| = build_tree_list (NULL_TREE, |
| build_unary_op (ADDR_EXPR, UOBJC_MODULES_decl, 0)); |
| decelerator = build_function_call (execclass_decl, parms); |
| |
| c_expand_expr_stmt (decelerator); |
| |
| finish_function (); |
| |
| return XEXP (DECL_RTL (init_function_decl), 0); |
| } |
| } |
| |
| /* extern const char _OBJC_STRINGS[]; */ |
| |
| static void |
| generate_forward_declaration_to_string_table (void) |
| { |
| tree sc_spec, decl_specs, expr_decl; |
| |
| sc_spec = tree_cons (NULL_TREE, ridpointers[(int) RID_EXTERN], NULL_TREE); |
| decl_specs = tree_cons (NULL_TREE, ridpointers[(int) RID_CHAR], sc_spec); |
| |
| expr_decl |
| = build_nt (ARRAY_REF, get_identifier ("_OBJC_STRINGS"), NULL_TREE); |
| |
| UOBJC_STRINGS_decl = define_decl (expr_decl, decl_specs); |
| } |
| |
| /* 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, ident, decl_spec, expr_decl, expr = NULL_TREE; |
| tree class_name, class, decl, initlist; |
| tree cl_chain, in_chain, type; |
| 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); |
| ident = get_identifier (buf); |
| |
| expr_decl = build_nt (ARRAY_REF, ident, NULL_TREE); |
| decl_spec = tree_cons (NULL_TREE, build_pointer_type (void_type_node), |
| build_tree_list (NULL_TREE, |
| ridpointers[(int) RID_STATIC])); |
| decl = start_decl (expr_decl, decl_spec, 1, NULL_TREE); |
| DECL_CONTEXT (decl) = 0; |
| DECL_ARTIFICIAL (decl) = 1; |
| |
| /* 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_2 (0, 0), initlist); |
| |
| expr = objc_build_constructor (TREE_TYPE (decl), nreverse (initlist)); |
| finish_decl (decl, expr, NULL_TREE); |
| TREE_USED (decl) = 1; |
| |
| type = build_array_type (build_pointer_type (void_type_node), 0); |
| decl = build_decl (VAR_DECL, ident, type); |
| TREE_USED (decl) = 1; |
| TREE_STATIC (decl) = 1; |
| decls |
| = tree_cons (NULL_TREE, build_unary_op (ADDR_EXPR, decl, 1), decls); |
| } |
| |
| decls = tree_cons (NULL_TREE, build_int_2 (0, 0), decls); |
| ident = get_identifier ("_OBJC_STATIC_INSTANCES"); |
| expr_decl = build_nt (ARRAY_REF, ident, NULL_TREE); |
| decl_spec = tree_cons (NULL_TREE, build_pointer_type (void_type_node), |
| build_tree_list (NULL_TREE, |
| ridpointers[(int) RID_STATIC])); |
| static_instances_decl |
| = start_decl (expr_decl, decl_spec, 1, NULL_TREE); |
| TREE_USED (static_instances_decl) = 1; |
| DECL_CONTEXT (static_instances_decl) = 0; |
| DECL_ARTIFICIAL (static_instances_decl) = 1; |
| expr = objc_build_constructor (TREE_TYPE (static_instances_decl), |
| nreverse (decls)); |
| finish_decl (static_instances_decl, expr, NULL_TREE); |
| } |
| |
| /* Output all strings. */ |
| |
| static void |
| generate_strings (void) |
| { |
| tree sc_spec, decl_specs, expr_decl; |
| tree chain, string_expr; |
| tree string, decl; |
| |
| for (chain = class_names_chain; chain; chain = TREE_CHAIN (chain)) |
| { |
| string = TREE_VALUE (chain); |
| decl = TREE_PURPOSE (chain); |
| sc_spec |
| = tree_cons (NULL_TREE, ridpointers[(int) RID_STATIC], NULL_TREE); |
| decl_specs = tree_cons (NULL_TREE, ridpointers[(int) RID_CHAR], sc_spec); |
| expr_decl = build_nt (ARRAY_REF, DECL_NAME (decl), NULL_TREE); |
| decl = start_decl (expr_decl, decl_specs, 1, NULL_TREE); |
| DECL_CONTEXT (decl) = NULL_TREE; |
| string_expr = my_build_string (IDENTIFIER_LENGTH (string) + 1, |
| IDENTIFIER_POINTER (string)); |
| finish_decl (decl, string_expr, NULL_TREE); |
| } |
| |
| for (chain = meth_var_names_chain; chain; chain = TREE_CHAIN (chain)) |
| { |
| string = TREE_VALUE (chain); |
| decl = TREE_PURPOSE (chain); |
| sc_spec |
| = tree_cons (NULL_TREE, ridpointers[(int) RID_STATIC], NULL_TREE); |
| decl_specs = tree_cons (NULL_TREE, ridpointers[(int) RID_CHAR], sc_spec); |
| expr_decl = build_nt (ARRAY_REF, DECL_NAME (decl), NULL_TREE); |
| decl = start_decl (expr_decl, decl_specs, 1, NULL_TREE); |
| DECL_CONTEXT (decl) = NULL_TREE; |
| string_expr = my_build_string (IDENTIFIER_LENGTH (string) + 1, |
| IDENTIFIER_POINTER (string)); |
| finish_decl (decl, string_expr, NULL_TREE); |
| } |
| |
| for (chain = meth_var_types_chain; chain; chain = TREE_CHAIN (chain)) |
| { |
| string = TREE_VALUE (chain); |
| decl = TREE_PURPOSE (chain); |
| sc_spec |
| = tree_cons (NULL_TREE, ridpointers[(int) RID_STATIC], NULL_TREE); |
| decl_specs = tree_cons (NULL_TREE, ridpointers[(int) RID_CHAR], sc_spec); |
| expr_decl = build_nt (ARRAY_REF, DECL_NAME (decl), NULL_TREE); |
| decl = start_decl (expr_decl, decl_specs, 1, NULL_TREE); |
| DECL_CONTEXT (decl) = NULL_TREE; |
| string_expr = my_build_string (IDENTIFIER_LENGTH (string) + 1, |
| IDENTIFIER_POINTER (string)); |
| finish_decl (decl, string_expr, NULL_TREE); |
| } |
| } |
| |
| static GTY(()) int selector_reference_idx; |
| |
| static tree |
| build_selector_reference_decl (void) |
| { |
| tree decl, ident; |
| char buf[256]; |
| |
| sprintf (buf, "_OBJC_SELECTOR_REFERENCES_%d", selector_reference_idx++); |
| |
| ident = get_identifier (buf); |
| |
| decl = build_decl (VAR_DECL, ident, selector_type); |
| DECL_EXTERNAL (decl) = 1; |
| TREE_PUBLIC (decl) = 0; |
| TREE_USED (decl) = 1; |
| DECL_ARTIFICIAL (decl) = 1; |
| DECL_CONTEXT (decl) = 0; |
| |
| make_decl_rtl (decl, 0); |
| pushdecl_top_level (decl); |
| |
| return decl; |
| } |
| |
| /* Just a handy wrapper for add_objc_string. */ |
| |
| static tree |
| build_selector (tree ident) |
| { |
| tree expr = add_objc_string (ident, meth_var_names); |
| if (flag_typed_selectors) |
| return expr; |
| else |
| return build_c_cast (selector_type, expr); /* cast! */ |
| } |
| |
| static void |
| build_selector_translation_table (void) |
| { |
| tree sc_spec, decl_specs; |
| tree chain, initlist = NULL_TREE; |
| int offset = 0; |
| tree decl = NULL_TREE, var_decl, name; |
| |
| 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) |
| { |
| /* Adjust line number for warning message. */ |
| int save_lineno = input_line; |
| if (flag_next_runtime && TREE_PURPOSE (chain)) |
| input_line = DECL_SOURCE_LINE (TREE_PURPOSE (chain)); |
| warning ("creating selector for non existant method %s", |
| IDENTIFIER_POINTER (TREE_VALUE (chain))); |
| input_line = save_lineno; |
| } |
| } |
| |
| expr = build_selector (TREE_VALUE (chain)); |
| |
| if (flag_next_runtime) |
| { |
| name = DECL_NAME (TREE_PURPOSE (chain)); |
| |
| sc_spec = build_tree_list (NULL_TREE, ridpointers[(int) RID_STATIC]); |
| |
| /* static SEL _OBJC_SELECTOR_REFERENCES_n = ...; */ |
| decl_specs = tree_cons (NULL_TREE, selector_type, sc_spec); |
| |
| var_decl = name; |
| |
| /* The `decl' that is returned from start_decl is the one that we |
| forward declared in `build_selector_reference' */ |
| decl = start_decl (var_decl, decl_specs, 1, NULL_TREE ); |
| } |
| |
| /* add one for the '\0' character */ |
| offset += IDENTIFIER_LENGTH (TREE_VALUE (chain)) + 1; |
| |
| if (flag_next_runtime) |
| finish_decl (decl, expr, NULL_TREE); |
| 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 variable and its initial value to be actually output. */ |
| DECL_EXTERNAL (UOBJC_SELECTOR_TABLE_decl) = 0; |
| TREE_STATIC (UOBJC_SELECTOR_TABLE_decl) = 1; |
| /* NULL terminate the list and fix the decl for output. */ |
| initlist = tree_cons (NULL_TREE, build_int_2 (0, 0), initlist); |
| DECL_INITIAL (UOBJC_SELECTOR_TABLE_decl) = objc_ellipsis_node; |
| initlist = objc_build_constructor (TREE_TYPE (UOBJC_SELECTOR_TABLE_decl), |
| nreverse (initlist)); |
| finish_decl (UOBJC_SELECTOR_TABLE_decl, initlist, NULL_TREE); |
| current_function_decl = NULL_TREE; |
| } |
| } |
| |
| 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_2 (0, 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_2 (index, 0)), |
| 1); |
| return build_c_cast (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_2 (index, 0))); |
| |
| index++; |
| chain = &TREE_CHAIN (*chain); |
| } |
| |
| expr = build_selector_reference_decl (); |
| |
| *chain = tree_cons (expr, ident, NULL_TREE); |
| |
| return (flag_next_runtime |
| ? expr |
| : build_array_ref (UOBJC_SELECTOR_TABLE_decl, |
| build_int_2 (index, 0))); |
| } |
| |
| static GTY(()) int class_reference_idx; |
| |
| static tree |
| build_class_reference_decl (void) |
| { |
| tree decl, ident; |
| char buf[256]; |
| |
| sprintf (buf, "_OBJC_CLASS_REFERENCES_%d", class_reference_idx++); |
| |
| ident = get_identifier (buf); |
| |
| decl = build_decl (VAR_DECL, ident, objc_class_type); |
| DECL_EXTERNAL (decl) = 1; |
| TREE_PUBLIC (decl) = 0; |
| TREE_USED (decl) = 1; |
| DECL_CONTEXT (decl) = 0; |
| DECL_ARTIFICIAL (decl) = 1; |
| |
| make_decl_rtl (decl, 0); |
| pushdecl_top_level (decl); |
| |
| 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 |
| get_class_reference (tree ident) |
| { |
| tree orig_ident; |
| |
| #ifdef OBJCPLUS |
| if (processing_template_decl) |
| /* Must wait until template instantiation time. */ |
| return build_min_nt (CLASS_REFERENCE_EXPR, ident); |
| if (TREE_CODE (ident) == TYPE_DECL) |
| ident = DECL_NAME (ident); |
| #endif |
| orig_ident = ident; |
| |
| if (!(ident = 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 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 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; |
| |
| make_decl_rtl (decl, 0); |
| 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 = is_class_name (class_ident))) |
| warning ("cannot find class `%s'", IDENTIFIER_POINTER (class_ident)); |
| else if (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 (! 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 |
| 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 = TYPE_NAME (DECL_ORIGINAL_TYPE (ident)); |
| |
| #ifdef OBJCPLUS |
| if (ident && TREE_CODE (ident) == RECORD_TYPE) |
| ident = TYPE_NAME (ident); |
| 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', '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) |
| { |
| type = TYPE_MAIN_VARIANT (type); |
| if (!type || TREE_CODE (type) != POINTER_TYPE) |
| return 0; |
| /* NB: This function may be called before the ObjC front-end has |
| been initialized, in which case ID_TYPE will be NULL. */ |
| if (id_type && type && TYPE_P (type) |
| && (IS_ID (type) |
| || TREE_TYPE (type) == TREE_TYPE (objc_class_type))) |
| return type; |
| return is_class_name (OBJC_TYPE_NAME (TREE_TYPE (type))); |
| } |
| |
| tree |
| lookup_interface (tree ident) |
| { |
| tree chain; |
| |
| #ifdef OBJCPLUS |
| if (ident && TREE_CODE (ident) == TYPE_DECL) |
| ident = DECL_NAME (ident); |
| #endif |
| for (chain = interface_chain; chain; chain = TREE_CHAIN (chain)) |
| { |
| if (ident == CLASS_NAME (chain)) |
| return chain; |
| } |
| return NULL_TREE; |
| } |
| |
| /* Implement @defs (<classname>) within struct bodies. */ |
| |
| tree |
| get_class_ivars_from_name (tree class_name) |
| { |
| tree interface = lookup_interface (class_name); |
| tree field, fields = NULL_TREE; |
| |
| if (interface) |
| { |
| tree raw_ivar = get_class_ivars (interface, 1); |
| |
| /* Regenerate the FIELD_DECLs for the enclosing struct. */ |
| for (; raw_ivar; raw_ivar = TREE_CHAIN (raw_ivar)) |
| { |
| field = grokfield (TREE_PURPOSE (TREE_VALUE (raw_ivar)), |
| TREE_PURPOSE (raw_ivar), |
| TREE_VALUE (TREE_VALUE (raw_ivar))); |
| #ifdef OBJCPLUS |
| finish_member_declaration (field); |
| #else |
| fields = chainon (fields, field); |
| #endif |
| } |
| } |
| else |
| error ("cannot find interface declaration for `%s'", |
| IDENTIFIER_POINTER (class_name)); |
| |
| return fields; |
| } |
| |
| /* Used by: build_private_template, continue_class, |
| and for @defs constructs. */ |
| |
| static tree |
| get_class_ivars (tree interface, int raw) |
| { |
| tree my_name, super_name, ivar_chain; |
| |
| my_name = CLASS_NAME (interface); |
| super_name = CLASS_SUPER_NAME (interface); |
| if (raw) |
| ivar_chain = CLASS_RAW_IVARS (interface); |
| else |
| { |
| ivar_chain = CLASS_IVARS (interface); |
| /* Save off a pristine copy of the leaf ivars (i.e, those not |
| inherited from a super class). */ |
| if (!CLASS_OWN_IVARS (interface)) |
| CLASS_OWN_IVARS (interface) = copy_list (ivar_chain); |
| } |
| |
| while (super_name) |
| { |
| tree op1; |
| tree super_interface = lookup_interface (super_name); |
| |
| if (!super_interface) |
| { |
| /* fatal did not work with 2 args...should fix */ |
| error ("cannot find interface declaration for `%s', superclass of `%s'", |
| IDENTIFIER_POINTER (super_name), |
| IDENTIFIER_POINTER (my_name)); |
| exit (FATAL_EXIT_CODE); |
| } |
| |
| if (super_interface == interface) |
| fatal_error ("circular inheritance in interface declaration for `%s'", |
| IDENTIFIER_POINTER (super_name)); |
| |
| interface = super_interface; |
| my_name = CLASS_NAME (interface); |
| super_name = CLASS_SUPER_NAME (interface); |
| |
| op1 = (raw ? CLASS_RAW_IVARS (interface) : CLASS_OWN_IVARS (interface)); |
| if (op1) |
| { |
| tree head = copy_list (op1); |
| |
| /* Prepend super class ivars...make a copy of the list, we |
| do not want to alter the original. */ |
| chainon (head, ivar_chain); |
| ivar_chain = head; |
| } |
| } |
| |
| return ivar_chain; |
| } |
| |
| static tree |
| objc_enter_block (void) |
| { |
| tree block; |
| |
| #ifdef OBJCPLUS |
| block = begin_compound_stmt (0); |
| #else |
| block = c_begin_compound_stmt (); |
| pushlevel (0); |
| clear_last_expr (); |
| add_scope_stmt (/*begin_p=*/1, /*partial_p=*/0); |
| #endif |
| |
| objc_exception_block_stack = tree_cons (NULL_TREE, block, |
| objc_exception_block_stack); |
| |
| blk_nesting_count++; |
| return block; |
| } |
| |
| static tree |
| objc_exit_block (void) |
| { |
| tree block = TREE_VALUE (objc_exception_block_stack); |
| #ifndef OBJCPLUS |
| tree scope_stmt, inner; |
| #endif |
| |
| objc_clear_super_receiver (); |
| #ifdef OBJCPLUS |
| finish_compound_stmt (0, block); |
| #else |
| scope_stmt = add_scope_stmt (/*begin_p=*/0, /*partial_p=*/0); |
| inner = poplevel (KEEP_MAYBE, 1, 0); |
| |
| SCOPE_STMT_BLOCK (TREE_PURPOSE (scope_stmt)) |
| = SCOPE_STMT_BLOCK (TREE_VALUE (scope_stmt)) |
| = inner; |
| RECHAIN_STMTS (block, COMPOUND_BODY (block)); |
| #endif |
| last_expr_type = NULL_TREE; |
| objc_exception_block_stack = TREE_CHAIN (objc_exception_block_stack); |
| |
| blk_nesting_count--; |
| return block; |
| } |
| |
| static tree |
| objc_declare_variable (enum rid scspec, tree name, tree type, tree init) |
| { |
| tree decl; |
| |
| type = tree_cons (NULL_TREE, type, |
| tree_cons (NULL_TREE, ridpointers[(int) scspec], |
| NULL_TREE)); |
| TREE_STATIC (type) = 1; |
| decl = start_decl (name, type, (init != NULL_TREE), NULL_TREE); |
| finish_decl (decl, init, NULL_TREE); |
| /* This prevents `unused variable' warnings when compiling with -Wall. */ |
| TREE_USED (decl) = 1; |
| DECL_ARTIFICIAL (decl) = 1; |
| return decl; |
| } |
| |
| tree |
| objc_build_throw_stmt (tree throw_expr) |
| { |
| tree func_params; |
| |
| if (!flag_objc_exceptions) |
| fatal_error ("Use `-fobjc-exceptions' to enable Objective-C exception syntax"); |
| |
| if (!throw_expr && objc_caught_exception) |
| throw_expr = TREE_VALUE (objc_caught_exception); |
| |
| if (!throw_expr) |
| { |
| error ("`@throw;' (rethrow) used outside of a `@catch' block"); |
| return error_mark_node; |
| } |
| |
| func_params = tree_cons (NULL_TREE, throw_expr, NULL_TREE); |
| |
| assemble_external (objc_exception_throw_decl); |
| return c_expand_expr_stmt (build_function_call (objc_exception_throw_decl, |
| func_params)); |
| } |
| |
| static void |
| val_stack_push (struct val_stack **nc, long val) |
| { |
| struct val_stack *new_elem = xmalloc (sizeof (struct val_stack)); |
| new_elem->val = val; |
| new_elem->next = *nc; |
| *nc = new_elem; |
| } |
| |
| static void |
| val_stack_pop (struct val_stack **nc) |
| { |
| struct val_stack *old_elem = *nc; |
| *nc = old_elem->next; |
| free (old_elem); |
| } |
| |
| static void |
| objc_build_try_enter_fragment (void) |
| { |
| /* objc_exception_try_enter(&_stackExceptionData); |
| if (!_setjmp(&_stackExceptionData.buf)) { */ |
| |
| tree func_params, if_stmt, cond; |
| |
| func_params |
| = tree_cons (NULL_TREE, |
| build_unary_op (ADDR_EXPR, |
| TREE_VALUE (objc_stack_exception_data), |
| 0), |
| NULL_TREE); |
| |
| assemble_external (objc_exception_try_enter_decl); |
| c_expand_expr_stmt (build_function_call |
| (objc_exception_try_enter_decl, func_params)); |
| |
| if_stmt = c_begin_if_stmt (); |
| if_nesting_count++; |
| /* If <setjmp.h> has been included, the _setjmp prototype has |
| acquired a real, breathing type for its parameter. Cast our |
| argument to that type. */ |
| func_params |
| = tree_cons (NULL_TREE, |
| build_c_cast (TYPE_ARG_TYPES (TREE_TYPE (objc_setjmp_decl)) |
| ? TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (objc_setjmp_decl))) |
| : ptr_type_node, |
| build_unary_op |
| (ADDR_EXPR, |
| build_component_ref (TREE_VALUE (objc_stack_exception_data), |
| get_identifier ("buf")), 0)), |
| NULL_TREE); |
| assemble_external (objc_setjmp_decl); |
| cond = build_unary_op (TRUTH_NOT_EXPR, |
| build_function_call (objc_setjmp_decl, func_params), |
| 0); |
| c_expand_start_cond (c_common_truthvalue_conversion (cond), |
| 0, if_stmt); |
| objc_enter_block (); |
| } |
| |
| static tree |
| objc_build_extract_expr (void) |
| { |
| /* ... = objc_exception_extract(&_stackExceptionData); */ |
| |
| tree func_params |
| = tree_cons (NULL_TREE, |
| build_unary_op (ADDR_EXPR, |
| TREE_VALUE (objc_stack_exception_data), 0), |
| NULL_TREE); |
| |
| assemble_external (objc_exception_extract_decl); |
| return build_function_call (objc_exception_extract_decl, func_params); |
| } |
| |
| static void |
| objc_build_try_exit_fragment (void) |
| { |
| /* objc_exception_try_exit(&_stackExceptionData); */ |
| |
| tree func_params |
| = tree_cons (NULL_TREE, |
| build_unary_op (ADDR_EXPR, |
| TREE_VALUE (objc_stack_exception_data), 0), |
| NULL_TREE); |
| |
| assemble_external (objc_exception_try_exit_decl); |
| c_expand_expr_stmt (build_function_call (objc_exception_try_exit_decl, |
| func_params)); |
| } |
| |
| static void |
| objc_build_extract_fragment (void) |
| { |
| /* } else { |
| _rethrowException = objc_exception_extract(&_stackExceptionData); |
| } */ |
| |
| objc_exit_block (); |
| c_finish_then (); |
| |
| c_expand_start_else (); |
| objc_enter_block (); |
| c_expand_expr_stmt (build_modify_expr |
| (TREE_VALUE (objc_rethrow_exception), |
| NOP_EXPR, |
| objc_build_extract_expr ())); |
| objc_exit_block (); |
| c_finish_else (); |
| c_expand_end_cond (); |
| if_nesting_count--; |
| } |
| |
| tree |
| objc_build_try_prologue (void) |
| { |
| /* { // new scope |
| struct _objc_exception_data _stackExceptionData; |
| volatile id _rethrowException = nil; |
| { // begin TRY-CATCH scope |
| objc_exception_try_enter(&_stackExceptionData); |
| if (!_setjmp(&_stackExceptionData.buf)) { */ |
| |
| tree try_catch_block; |
| |
| if (!flag_objc_exceptions) |
| fatal_error ("Use `-fobjc-exceptions' to enable Objective-C exception syntax"); |
| |
| objc_mark_locals_volatile ((void *)(exc_binding_stack |
| ? exc_binding_stack->val |
| : 0)); |
| objc_enter_block (); |
| objc_stack_exception_data |
| = tree_cons (NULL_TREE, |
| objc_declare_variable (RID_AUTO, |
| get_identifier (UTAG_EXCDATA_VAR), |
| xref_tag (RECORD_TYPE, |
| get_identifier (UTAG_EXCDATA)), |
| NULL_TREE), |
| objc_stack_exception_data); |
| objc_rethrow_exception = tree_cons (NULL_TREE, |
| objc_declare_variable (RID_VOLATILE, |
| get_identifier (UTAG_RETHROWEXC_VAR), |
| id_type, |
| build_int_2 (0, 0)), |
| objc_rethrow_exception); |
| |
| try_catch_block = objc_enter_block (); |
| val_stack_push (&exc_binding_stack, (long) get_current_scope ()); |
| objc_build_try_enter_fragment (); |
| |
| return try_catch_block; |
| } |
| |
| void |
| objc_build_try_epilogue (int also_catch_prologue) |
| { |
| if (also_catch_prologue) |
| { |
| /* } else { |
| register id _caughtException = objc_exception_extract( &_stackExceptionData); |
| objc_exception_try_enter(&_stackExceptionData); |
| if(!_setjmp(&_stackExceptionData.buf)) { |
| if (0) { */ |
| |
| tree if_stmt; |
| |
| objc_exit_block (); |
| c_finish_then (); |
| |
| c_expand_start_else (); |
| objc_enter_block (); |
| objc_caught_exception |
| = tree_cons (NULL_TREE, |
| objc_declare_variable (RID_REGISTER, |
| get_identifier (UTAG_CAUGHTEXC_VAR), |
| id_type, |
| objc_build_extract_expr ()), |
| objc_caught_exception); |
| objc_build_try_enter_fragment (); |
| val_stack_push (&catch_count_stack, 1); |
| if_stmt = c_begin_if_stmt (); |
| if_nesting_count++; |
| c_expand_start_cond (c_common_truthvalue_conversion (boolean_false_node), |
| 0, if_stmt); |
| objc_enter_block (); |
| |
| /* Start a new chain of @catch statements for this @try. */ |
| objc_catch_type = tree_cons (objc_catch_type, NULL_TREE, NULL_TREE); |
| } |
| else |
| { /* !also_catch_prologue */ |
| |
| /* } else { |
| _rethrowException = objc_exception_extract( &_stackExceptionData); |
| } |
| } */ |
| objc_build_extract_fragment (); |
| objc_exit_block (); |
| } |
| } |
| |
| void |
| objc_build_catch_stmt (tree catch_expr) |
| { |
| /* } else if (objc_exception_match(objc_get_class("SomeClass"), _caughtException)) { |
| register SomeClass *e = _caughtException; */ |
| |
| tree if_stmt, cond, func_params, prev_catch, var_name, var_type; |
| int catch_id; |
| |
| #ifndef OBJCPLUS |
| /* Yet another C/C++ impedance mismatch. */ |
| catch_expr = TREE_PURPOSE (catch_expr); |
| #endif |
| |
| var_name = TREE_VALUE (catch_expr); |
| var_type = TREE_VALUE (TREE_PURPOSE (catch_expr)); |
| if (TREE_CODE (var_name) == INDIRECT_REF) |
| var_name = TREE_OPERAND (var_name, 0); |
| if (TREE_CODE (var_type) == TYPE_DECL |
| || TREE_CODE (var_type) == POINTER_TYPE) |
| var_type = TREE_TYPE (var_type); |
| catch_id = (var_type == TREE_TYPE (id_type)); |
| |
| if (!flag_objc_exceptions) |
| fatal_error ("Use `-fobjc-exceptions' to enable Objective-C exception syntax"); |
| |
| if (!(catch_id || TYPED_OBJECT (var_type))) |
| fatal_error ("`@catch' parameter is not a known Objective-C class type"); |
| |
| /* Examine previous @catch clauses for the current @try block for |
| superclasses of the 'var_type' class. */ |
| for (prev_catch = objc_catch_type; TREE_VALUE (prev_catch); |
| prev_catch = TREE_CHAIN (prev_catch)) |
| { |
| if (TREE_VALUE (prev_catch) == TREE_TYPE (id_type)) |
| { |
| warning ("Exception already handled by preceding `@catch(id)'"); |
| break; |
| } |
| else if (!catch_id |
| && objc_comptypes (TREE_VALUE (prev_catch), var_type, 0) == 1) |
| warning ("Exception of type `%s *' already handled by `@catch (%s *)'", |
| IDENTIFIER_POINTER (OBJC_TYPE_NAME (var_type)), |
| IDENTIFIER_POINTER (OBJC_TYPE_NAME (TREE_VALUE (prev_catch)))); |
| } |
| |
| objc_catch_type = tree_cons (NULL_TREE, var_type, objc_catch_type); |
| |
| objc_exit_block (); |
| c_finish_then (); |
| |
| c_expand_start_else (); |
| catch_count_stack->val++; |
| if_stmt = c_begin_if_stmt (); |
| if_nesting_count++; |
| |
| if (catch_id) |
| cond = integer_one_node; |
| else |
| { |
| cond = get_class_reference (OBJC_TYPE_NAME (var_type)); |
| |
| func_params |
| = tree_cons (NULL_TREE, cond, |
| tree_cons (NULL_TREE, |
| TREE_VALUE (objc_caught_exception), |
| NULL_TREE)); |
| assemble_external (objc_exception_match_decl); |
| cond = build_function_call (objc_exception_match_decl, func_params); |
| } |
| |
| c_expand_start_cond (c_common_truthvalue_conversion (cond), |
| 0, if_stmt); |
| objc_enter_block (); |
| objc_declare_variable (RID_REGISTER, var_name, |
| build_pointer_type (var_type), |
| TREE_VALUE (objc_caught_exception)); |
| } |
| |
| void |
| objc_build_catch_epilogue (void) |
| { |
| /* } else { |
| _rethrowException = _caughtException; |
| objc_exception_try_exit(&_stackExceptionData); |
| } |
| } else { |
| _rethrowException = objc_exception_extract(&_stackExceptionData); |
| } |
| } |
| } // end TRY-CATCH scope |
| */ |
| |
| objc_exit_block (); |
| c_finish_then (); |
| |
| c_expand_start_else (); |
| objc_enter_block (); |
| c_expand_expr_stmt |
| (build_modify_expr |
| (TREE_VALUE (objc_rethrow_exception), |
| NOP_EXPR, |
| TREE_VALUE (objc_caught_exception))); |
| objc_build_try_exit_fragment (); |
| objc_exit_block (); |
| while (catch_count_stack->val--) |
| { |
| c_finish_else (); /* close off all the nested ifs ! */ |
| c_expand_end_cond (); |
| if_nesting_count--; |
| } |
| val_stack_pop (&catch_count_stack); |
| objc_caught_exception = TREE_CHAIN (objc_caught_exception); |
| |
| objc_build_extract_fragment (); |
| |
| objc_exit_block (); |
| c_finish_else (); |
| c_expand_end_cond (); |
| if_nesting_count--; |
| objc_exit_block (); |
| |
| /* Return to enclosing chain of @catch statements (if any). */ |
| while (TREE_VALUE (objc_catch_type)) |
| objc_catch_type = TREE_CHAIN (objc_catch_type); |
| objc_catch_type = TREE_PURPOSE (objc_catch_type); |
| } |
| |
| tree |
| objc_build_finally_prologue (void) |
| { |
| /* { // begin FINALLY scope |
| if (!_rethrowException) { |
| objc_exception_try_exit(&_stackExceptionData); |
| } */ |
| |
| tree blk = objc_enter_block (); |
| |
| tree if_stmt = c_begin_if_stmt (); |
| if_nesting_count++; |
| |
| c_expand_start_cond (c_common_truthvalue_conversion |
| (build_unary_op |
| (TRUTH_NOT_EXPR, |
| TREE_VALUE (objc_rethrow_exception), 0)), |
| 0, if_stmt); |
| objc_enter_block (); |
| objc_build_try_exit_fragment (); |
| objc_exit_block (); |
| c_finish_then (); |
| c_expand_end_cond (); |
| if_nesting_count--; |
| |
| return blk; |
| } |
| |
| tree |
| objc_build_finally_epilogue (void) |
| { |
| /* if (_rethrowException) { |
| objc_exception_throw(_rethrowException); |
| } |
| } // end FINALLY scope |
| } */ |
| |
| tree if_stmt = c_begin_if_stmt (); |
| if_nesting_count++; |
| |
| c_expand_start_cond |
| (c_common_truthvalue_conversion (TREE_VALUE (objc_rethrow_exception)), |
| 0, if_stmt); |
| objc_enter_block (); |
| objc_build_throw_stmt (TREE_VALUE (objc_rethrow_exception)); |
| objc_exit_block (); |
| c_finish_then (); |
| c_expand_end_cond (); |
| if_nesting_count--; |
| |
| objc_exit_block (); |
| objc_rethrow_exception = TREE_CHAIN (objc_rethrow_exception); |
| objc_stack_exception_data = TREE_CHAIN (objc_stack_exception_data); |
| |
| val_stack_pop (&exc_binding_stack); |
| return objc_exit_block (); |
| } |
| |
| tree |
| objc_build_try_catch_finally_stmt (int has_catch, int has_finally) |
| { |
| /* NB: The operative assumption here is that TRY_FINALLY_EXPR will |
| deal with all exits from 'try_catch_blk' and route them through |
| 'finally_blk'. */ |
| tree outer_blk = objc_build_finally_epilogue (); |
| tree prec_stmt = TREE_CHAIN (TREE_CHAIN (COMPOUND_BODY (outer_blk))); |
| tree try_catch_blk = TREE_CHAIN (prec_stmt), try_catch_expr; |
| tree finally_blk = TREE_CHAIN (try_catch_blk), finally_expr; |
| tree succ_stmt = TREE_CHAIN (finally_blk); |
| tree try_finally_stmt, try_finally_expr; |
| |
| if (!flag_objc_exceptions) |
| fatal_error ("Use `-fobjc-exceptions' to enable Objective-C exception syntax"); |
| |
| /* It is an error to have a @try block without a @catch and/or @finally |
| (even though sensible code can be generated nonetheless). */ |
| |
| if (!has_catch && !has_finally) |
| error ("`@try' without `@catch' or `@finally'"); |
| |
| /* We shall now do something truly disgusting. We shall remove the |
| 'try_catch_blk' and 'finally_blk' from the 'outer_blk' statement |
| chain, and replace them with a TRY_FINALLY_EXPR statement! If |
| this doesn't work, we will have to learn (from Per/gcj) how to |
| construct the 'outer_blk' lazily. */ |
| |
| TREE_CHAIN (try_catch_blk) = TREE_CHAIN (finally_blk) = NULL_TREE; |
| try_catch_expr = build1 (STMT_EXPR, void_type_node, try_catch_blk); |
| TREE_SIDE_EFFECTS (try_catch_expr) = 1; |
| finally_expr = build1 (STMT_EXPR, void_type_node, finally_blk); |
| TREE_SIDE_EFFECTS (finally_expr) = 1; |
| try_finally_expr = build (TRY_FINALLY_EXPR, void_type_node, try_catch_expr, |
| finally_expr); |
| TREE_SIDE_EFFECTS (try_finally_expr) = 1; |
| try_finally_stmt = build_stmt (EXPR_STMT, try_finally_expr); |
| TREE_CHAIN (prec_stmt) = try_finally_stmt; |
| TREE_CHAIN (try_finally_stmt) = succ_stmt; |
| |
| return outer_blk; /* the whole enchilada */ |
| } |
| |
| void |
| objc_build_synchronized_prologue (tree sync_expr) |
| { |
| /* { |
| id _eval_once = <sync_expr>; |
| @try { |
| objc_sync_enter( _eval_once ); */ |
| |
| tree func_params; |
| |
| if (!flag_objc_exceptions) |
| fatal_error ("Use `-fobjc-exceptions' to enable Objective-C exception syntax"); |
| |
| objc_enter_block (); |
| objc_eval_once |
| = tree_cons (NULL_TREE, |
| objc_declare_variable (RID_AUTO, |
| get_identifier (UTAG_EVALONCE_VAR), |
| id_type, |
| sync_expr), |
| objc_eval_once); |
| objc_build_try_prologue (); |
| objc_enter_block (); |
| func_params = tree_cons (NULL_TREE, |
| TREE_VALUE (objc_eval_once), |
| NULL_TREE); |
| |
| assemble_external (objc_sync_enter_decl); |
| c_expand_expr_stmt (build_function_call |
| (objc_sync_enter_decl, func_params)); |
| } |
| |
| tree |
| objc_build_synchronized_epilogue (void) |
| { |
| /* } |
| @finally { |
| objc_sync_exit( _eval_once ); |
| } |
| } */ |
| |
| tree func_params; |
| |
| objc_exit_block (); |
| objc_build_try_epilogue (0); |
| objc_build_finally_prologue (); |
| func_params = tree_cons (NULL_TREE, TREE_VALUE (objc_eval_once), |
| NULL_TREE); |
| |
| assemble_external (objc_sync_exit_decl); |
| c_expand_expr_stmt (build_function_call (objc_sync_exit_decl, |
| func_params)); |
| objc_build_try_catch_finally_stmt (0, 1); |
| |
| return objc_exit_block (); |
| } |
| |
| /* 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 |
| <setjmp.h> 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_objc_exception_stuff (void) |
| { |
| tree field_decl, field_decl_chain, index, temp_type; |
| |
| /* Suppress outputting debug symbols, because |
| dbxout_init hasn't been called yet. */ |
| enum debug_info_type save_write_symbols = write_symbols; |
| const struct gcc_debug_hooks *save_hooks = debug_hooks; |
| |
| write_symbols = NO_DEBUG; |
| debug_hooks = &do_nothing_debug_hooks; |
| objc_exception_data_template |
| = start_struct (RECORD_TYPE, get_identifier (UTAG_EXCDATA)); |
| |
| /* int buf[_JBLEN]; */ |
|