| /* Implement classes and message passing for Objective C. |
| Copyright (C) 1992, 1993, 1994, 1995, 1997 Free Software Foundation, Inc. |
| Contributed by Steve Naroff. |
| |
| This file is part of GNU CC. |
| |
| GNU CC is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU CC is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU CC; see the file COPYING. If not, write to |
| the Free Software Foundation, 59 Temple Place - Suite 330, |
| Boston, MA 02111-1307, USA. */ |
| |
| /* 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': |
| |
| - OBJC_INT_SELECTORS */ |
| |
| #include <stdio.h> |
| #include "config.h" |
| #include "tree.h" |
| #include "c-tree.h" |
| #include "c-lex.h" |
| #include "flags.h" |
| #include "objc-act.h" |
| #include "input.h" |
| #include "except.h" |
| #include "function.h" |
| |
| |
| /* 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 |
| |
| /* Define the special tree codes that we use. */ |
| |
| /* Table indexed by tree code giving a string containing a character |
| classifying the tree code. Possibilities are |
| t, d, s, c, r, <, 1 and 2. See objc-tree.def for details. */ |
| |
| #define DEFTREECODE(SYM, NAME, TYPE, LENGTH) TYPE, |
| |
| char *objc_tree_code_type[] = { |
| "x", |
| #include "objc-tree.def" |
| }; |
| #undef DEFTREECODE |
| |
| /* Table indexed by tree code giving number of expression |
| operands beyond the fixed part of the node structure. |
| Not used for types or decls. */ |
| |
| #define DEFTREECODE(SYM, NAME, TYPE, LENGTH) LENGTH, |
| |
| int objc_tree_code_length[] = { |
| 0, |
| #include "objc-tree.def" |
| }; |
| #undef DEFTREECODE |
| |
| /* Names of tree components. |
| Used for printing out the tree and error messages. */ |
| #define DEFTREECODE(SYM, NAME, TYPE, LEN) NAME, |
| |
| char *objc_tree_code_name[] = { |
| "@@dummy", |
| #include "objc-tree.def" |
| }; |
| #undef DEFTREECODE |
| |
| /* Set up for use of obstacks. */ |
| |
| #include "obstack.h" |
| |
| #define obstack_chunk_alloc xmalloc |
| #define obstack_chunk_free free |
| |
| /* 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; |
| |
| /* List of classes with list of their static instances. */ |
| static tree objc_static_instances = NULL_TREE; |
| |
| /* The declaration of the array administrating the static instances. */ |
| static tree static_instances_decl = NULL_TREE; |
| |
| /* for encode_method_def */ |
| #include "rtl.h" |
| #include "c-parse.h" |
| |
| #define OBJC_VERSION (flag_next_runtime ? 5 : 8) |
| #define PROTOCOL_VERSION 2 |
| |
| #define OBJC_ENCODE_INLINE_DEFS 0 |
| #define OBJC_ENCODE_DONT_INLINE_DEFS 1 |
| |
| /*** Private Interface (procedures) ***/ |
| |
| /* Used by compile_file. */ |
| |
| static void init_objc PROTO((void)); |
| static void finish_objc PROTO((void)); |
| |
| /* Code generation. */ |
| |
| static void synth_module_prologue PROTO((void)); |
| static tree build_constructor PROTO((tree, tree)); |
| static char *build_module_descriptor PROTO((void)); |
| static tree init_module_descriptor PROTO((tree)); |
| static tree build_objc_method_call PROTO((int, tree, tree, |
| tree, tree, tree)); |
| static void generate_strings PROTO((void)); |
| static tree get_proto_encoding PROTO((tree)); |
| static void build_selector_translation_table PROTO((void)); |
| static tree build_ivar_chain PROTO((tree, int)); |
| |
| static tree objc_add_static_instance PROTO((tree, tree)); |
| |
| static tree build_ivar_template PROTO((void)); |
| static tree build_method_template PROTO((void)); |
| static tree build_private_template PROTO((tree)); |
| static void build_class_template PROTO((void)); |
| static void build_selector_template PROTO((void)); |
| static void build_category_template PROTO((void)); |
| static tree build_super_template PROTO((void)); |
| static tree build_category_initializer PROTO((tree, tree, tree, |
| tree, tree, tree)); |
| static tree build_protocol_initializer PROTO((tree, tree, tree, |
| tree, tree)); |
| |
| static void synth_forward_declarations PROTO((void)); |
| static void generate_ivar_lists PROTO((void)); |
| static void generate_dispatch_tables PROTO((void)); |
| static void generate_shared_structures PROTO((void)); |
| static tree generate_protocol_list PROTO((tree)); |
| static void generate_forward_declaration_to_string_table PROTO((void)); |
| static void build_protocol_reference PROTO((tree)); |
| |
| static tree init_selector PROTO((int)); |
| static tree build_keyword_selector PROTO((tree)); |
| static tree synth_id_with_class_suffix PROTO((char *, tree)); |
| |
| /* From expr.c */ |
| extern int apply_args_register_offset PROTO((int)); |
| |
| /* Misc. bookkeeping */ |
| |
| typedef struct hashed_entry *hash; |
| typedef struct hashed_attribute *attr; |
| |
| struct hashed_attribute |
| { |
| attr next; |
| tree value; |
| }; |
| struct hashed_entry |
| { |
| attr list; |
| hash next; |
| tree key; |
| }; |
| |
| static void hash_init PROTO((void)); |
| static void hash_enter PROTO((hash *, tree)); |
| static hash hash_lookup PROTO((hash *, tree)); |
| static void hash_add_attr PROTO((hash, tree)); |
| static tree lookup_method PROTO((tree, tree)); |
| static tree lookup_instance_method_static PROTO((tree, tree)); |
| static tree lookup_class_method_static PROTO((tree, tree)); |
| static tree add_class PROTO((tree)); |
| static void add_category PROTO((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 PROTO((tree, |
| enum string_section)); |
| static tree build_objc_string_decl PROTO((tree, |
| enum string_section)); |
| static tree build_selector_reference_decl PROTO((tree)); |
| |
| /* Protocol additions. */ |
| |
| static tree add_protocol PROTO((tree)); |
| static tree lookup_protocol PROTO((tree)); |
| static tree lookup_and_install_protocols PROTO((tree)); |
| |
| /* Type encoding. */ |
| |
| static void encode_type_qualifiers PROTO((tree)); |
| static void encode_pointer PROTO((tree, int, int)); |
| static void encode_array PROTO((tree, int, int)); |
| static void encode_aggregate PROTO((tree, int, int)); |
| static void encode_bitfield PROTO((int, int)); |
| static void encode_type PROTO((tree, int, int)); |
| static void encode_field_decl PROTO((tree, int, int)); |
| |
| static void really_start_method PROTO((tree, tree)); |
| static int comp_method_with_proto PROTO((tree, tree)); |
| static int comp_proto_with_proto PROTO((tree, tree)); |
| static tree get_arg_type_list PROTO((tree, int, int)); |
| static tree expr_last PROTO((tree)); |
| |
| /* Utilities for debugging and error diagnostics. */ |
| |
| static void warn_with_method PROTO((char *, int, tree)); |
| static void error_with_ivar PROTO((char *, tree, tree)); |
| static char *gen_method_decl PROTO((tree, char *)); |
| static char *gen_declaration PROTO((tree, char *)); |
| static char *gen_declarator PROTO((tree, char *, char *)); |
| static int is_complex_decl PROTO((tree)); |
| static void adorn_decl PROTO((tree, char *)); |
| static void dump_interface PROTO((FILE *, tree)); |
| |
| /* Everything else. */ |
| |
| static void objc_fatal PROTO((void)); |
| static tree define_decl PROTO((tree, tree)); |
| static tree lookup_method_in_protocol_list PROTO((tree, tree, int)); |
| static tree lookup_protocol_in_reflist PROTO((tree, tree)); |
| static tree create_builtin_decl PROTO((enum tree_code, |
| tree, char *)); |
| static tree my_build_string PROTO((int, char *)); |
| static void build_objc_symtab_template PROTO((void)); |
| static tree init_def_list PROTO((tree)); |
| static tree init_objc_symtab PROTO((tree)); |
| static void forward_declare_categories PROTO((void)); |
| static void generate_objc_symtab_decl PROTO((void)); |
| static tree build_selector PROTO((tree)); |
| static tree build_msg_pool_reference PROTO((int)); |
| static tree build_typed_selector_reference PROTO((tree, tree)); |
| static tree build_selector_reference PROTO((tree)); |
| static tree build_class_reference_decl PROTO((tree)); |
| static void add_class_reference PROTO((tree)); |
| static tree objc_copy_list PROTO((tree, tree *)); |
| static tree build_protocol_template PROTO((void)); |
| static tree build_descriptor_table_initializer PROTO((tree, tree)); |
| static tree build_method_prototype_list_template PROTO((tree, int)); |
| static tree build_method_prototype_template PROTO((void)); |
| static int forwarding_offset PROTO((tree)); |
| static tree encode_method_prototype PROTO((tree, tree)); |
| static tree generate_descriptor_table PROTO((tree, char *, int, tree, tree)); |
| static void generate_method_descriptors PROTO((tree)); |
| static tree build_tmp_function_decl PROTO((void)); |
| static void hack_method_prototype PROTO((tree, tree)); |
| static void generate_protocol_references PROTO((tree)); |
| static void generate_protocols PROTO((void)); |
| static void check_ivars PROTO((tree, tree)); |
| static tree build_ivar_list_template PROTO((tree, int)); |
| static tree build_method_list_template PROTO((tree, int)); |
| static tree build_ivar_list_initializer PROTO((tree, tree)); |
| static tree generate_ivars_list PROTO((tree, char *, |
| int, tree)); |
| static tree build_dispatch_table_initializer PROTO((tree, tree)); |
| static tree generate_dispatch_table PROTO((tree, char *, |
| int, tree)); |
| static tree build_shared_structure_initializer PROTO((tree, tree, tree, tree, |
| tree, int, tree, tree, |
| tree)); |
| static void generate_category PROTO((tree)); |
| static int is_objc_type_qualifier PROTO((tree)); |
| static tree adjust_type_for_id_default PROTO((tree)); |
| static tree check_duplicates PROTO((hash)); |
| static tree receiver_is_class_object PROTO((tree)); |
| static int check_methods PROTO((tree, tree, int)); |
| static int conforms_to_protocol PROTO((tree, tree)); |
| static void check_protocols PROTO((tree, char *, char *)); |
| static tree encode_method_def PROTO((tree)); |
| static void gen_declspecs PROTO((tree, char *, int)); |
| static void generate_classref_translation_entry PROTO((tree)); |
| static void handle_class_ref PROTO((tree)); |
| |
| /*** 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_STATICS "_objc_statics" |
| #define UTAG_SYMTAB "_objc_symtab" |
| #define UTAG_SUPER "_objc_super" |
| #define UTAG_SELECTOR "_objc_selector" |
| |
| #define UTAG_PROTOCOL "_objc_protocol" |
| #define UTAG_PROTOCOL_LIST "_objc_protocol_list" |
| #define UTAG_METHOD_PROTOTYPE "_objc_method_prototype" |
| #define UTAG_METHOD_PROTOTYPE_LIST "_objc__method_prototype_list" |
| |
| #define STRING_OBJECT_CLASS_NAME "NXConstantString" |
| #define PROTOCOL_OBJECT_CLASS_NAME "Protocol" |
| |
| static char *TAG_GETCLASS; |
| static char *TAG_GETMETACLASS; |
| static char *TAG_MSGSEND; |
| static char *TAG_MSGSENDSUPER; |
| static char *TAG_EXECCLASS; |
| |
| /* Set by `continue_class' and checked by `is_public'. */ |
| |
| #define TREE_STATIC_TEMPLATE(record_type) (TREE_PUBLIC (record_type)) |
| #define TYPED_OBJECT(type) \ |
| (TREE_CODE (type) == RECORD_TYPE && TREE_STATIC_TEMPLATE (type)) |
| |
| /* Some commonly used instances of "identifier_node". */ |
| |
| static tree self_id, ucmd_id; |
| static tree unused_list; |
| |
| static tree self_decl, umsg_decl, umsg_super_decl; |
| static tree objc_get_class_decl, objc_get_meta_class_decl; |
| |
| static tree super_type, selector_type, id_type, objc_class_type; |
| static tree instance_type, protocol_type; |
| |
| /* Type checking macros. */ |
| |
| #define IS_ID(TYPE) \ |
| (TYPE_MAIN_VARIANT (TYPE) == TYPE_MAIN_VARIANT (id_type)) |
| #define IS_PROTOCOL_QUALIFIED_ID(TYPE) \ |
| (IS_ID (TYPE) && TYPE_PROTOCOL_LIST (TYPE)) |
| #define IS_SUPER(TYPE) \ |
| (super_type && TYPE_MAIN_VARIANT (TYPE) == TYPE_MAIN_VARIANT (super_type)) |
| |
| static tree class_chain = NULL_TREE; |
| static tree alias_chain = NULL_TREE; |
| static tree interface_chain = NULL_TREE; |
| static tree protocol_chain = NULL_TREE; |
| |
| /* Chains to manage selectors that are referenced and defined in the |
| module. */ |
| |
| static tree cls_ref_chain = NULL_TREE; /* Classes referenced. */ |
| static tree sel_ref_chain = NULL_TREE; /* Selectors referenced. */ |
| |
| /* Chains to manage uniquing of strings. */ |
| |
| static tree class_names_chain = NULL_TREE; |
| static tree meth_var_names_chain = NULL_TREE; |
| static tree meth_var_types_chain = NULL_TREE; |
| |
| /* Hash tables to manage the global pool of method prototypes. */ |
| |
| static hash *nst_method_hash_list = 0; |
| static hash *cls_method_hash_list = 0; |
| |
| /* Backend data declarations. */ |
| |
| static tree UOBJC_SYMBOLS_decl; |
| static tree UOBJC_INSTANCE_VARIABLES_decl, UOBJC_CLASS_VARIABLES_decl; |
| static tree UOBJC_INSTANCE_METHODS_decl, UOBJC_CLASS_METHODS_decl; |
| static tree UOBJC_CLASS_decl, UOBJC_METACLASS_decl; |
| static tree UOBJC_SELECTOR_TABLE_decl; |
| static tree UOBJC_MODULES_decl; |
| static tree UOBJC_STRINGS_decl; |
| |
| /* The following are used when compiling a class implementation. |
| implementation_template will normally be an interface, however if |
| none exists this will be equal to implementation_context...it is |
| set in start_class. */ |
| |
| static tree implementation_context = NULL_TREE; |
| static tree implementation_template = NULL_TREE; |
| |
| struct imp_entry |
| { |
| struct imp_entry *next; |
| tree imp_context; |
| tree imp_template; |
| tree class_decl; /* _OBJC_CLASS_<my_name>; */ |
| tree meta_decl; /* _OBJC_METACLASS_<my_name>; */ |
| }; |
| |
| static void handle_impent PROTO((struct imp_entry *)); |
| |
| static struct imp_entry *imp_list = 0; |
| static int imp_count = 0; /* `@implementation' */ |
| static int cat_count = 0; /* `@category' */ |
| |
| static tree objc_class_template, objc_category_template, uprivate_record; |
| static tree objc_protocol_template, objc_selector_template; |
| static tree ucls_super_ref, uucls_super_ref; |
| |
| static tree objc_method_template, objc_ivar_template; |
| static tree objc_symtab_template, objc_module_template; |
| static tree objc_super_template, objc_object_reference; |
| |
| static tree objc_object_id, objc_class_id, objc_id_id; |
| static tree constant_string_id; |
| static tree constant_string_type; |
| static tree UOBJC_SUPER_decl; |
| |
| static tree method_context = NULL_TREE; |
| static int method_slot = 0; /* Used by start_method_def, */ |
| |
| #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 char *dump_base_name; |
| |
| /* Generate code for GNU or NeXT runtime environment. */ |
| |
| #ifdef NEXT_OBJC_RUNTIME |
| int flag_next_runtime = 1; |
| #else |
| int flag_next_runtime = 0; |
| #endif |
| |
| int flag_typed_selectors; |
| |
| /* Open and close the file for outputting class declarations, if requested. */ |
| |
| int flag_gen_declaration = 0; |
| |
| FILE *gen_declaration_file; |
| |
| /* Warn if multiple methods are seen for the same selector, but with |
| different argument types. */ |
| |
| int warn_selector = 0; |
| |
| /* Warn if methods required by a protocol are not implemented in the |
| class adopting it. When turned off, methods inherited to that |
| class are also considered implemented */ |
| |
| int flag_warn_protocol = 1; |
| |
| /* 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). This |
| was added to support features being planned for dbkit2. */ |
| |
| static int generating_instance_variables = 0; |
| |
| void |
| lang_init () |
| { |
| #if !USE_CPPLIB |
| /* The beginning of the file is a new line; check for #. |
| With luck, we discover the real source file's name from that |
| and put it in input_filename. */ |
| ungetc (check_newline (), finput); |
| #endif |
| |
| /* The line number can be -1 if we had -g3 and the input file |
| had a directive specifying line 0. But we want predefined |
| functions to have a line number of 0, not -1. */ |
| if (lineno == -1) |
| lineno = 0; |
| |
| /* If gen_declaration desired, open the output file. */ |
| if (flag_gen_declaration) |
| { |
| int dump_base_name_length = strlen (dump_base_name); |
| register char *dumpname = (char *) xmalloc (dump_base_name_length + 7); |
| strcpy (dumpname, dump_base_name); |
| strcat (dumpname, ".decl"); |
| gen_declaration_file = fopen (dumpname, "w"); |
| if (gen_declaration_file == 0) |
| pfatal_with_name (dumpname); |
| } |
| |
| if (flag_next_runtime) |
| { |
| TAG_GETCLASS = "objc_getClass"; |
| TAG_GETMETACLASS = "objc_getMetaClass"; |
| TAG_MSGSEND = "objc_msgSend"; |
| TAG_MSGSENDSUPER = "objc_msgSendSuper"; |
| TAG_EXECCLASS = "__objc_execClass"; |
| } |
| else |
| { |
| TAG_GETCLASS = "objc_get_class"; |
| TAG_GETMETACLASS = "objc_get_meta_class"; |
| TAG_MSGSEND = "objc_msg_lookup"; |
| TAG_MSGSENDSUPER = "objc_msg_lookup_super"; |
| TAG_EXECCLASS = "__objc_exec_class"; |
| flag_typed_selectors = 1; |
| } |
| |
| if (doing_objc_thang) |
| init_objc (); |
| } |
| |
| static void |
| objc_fatal () |
| { |
| fatal ("Objective-C text in C source file"); |
| } |
| |
| void |
| finish_file () |
| { |
| if (doing_objc_thang) |
| finish_objc (); /* Objective-C finalization */ |
| |
| if (gen_declaration_file) |
| fclose (gen_declaration_file); |
| } |
| |
| void |
| lang_finish () |
| { |
| } |
| |
| char * |
| lang_identify () |
| { |
| return "objc"; |
| } |
| |
| int |
| lang_decode_option (p) |
| char *p; |
| { |
| if (!strcmp (p, "-lang-objc")) |
| doing_objc_thang = 1; |
| else if (!strcmp (p, "-gen-decls")) |
| flag_gen_declaration = 1; |
| else if (!strcmp (p, "-Wselector")) |
| warn_selector = 1; |
| else if (!strcmp (p, "-Wno-selector")) |
| warn_selector = 0; |
| else if (!strcmp (p, "-Wprotocol")) |
| flag_warn_protocol = 1; |
| else if (!strcmp (p, "-Wno-protocol")) |
| flag_warn_protocol = 0; |
| else if (!strcmp (p, "-fgnu-runtime")) |
| flag_next_runtime = 0; |
| else if (!strcmp (p, "-fno-next-runtime")) |
| flag_next_runtime = 0; |
| else if (!strcmp (p, "-fno-gnu-runtime")) |
| flag_next_runtime = 1; |
| else if (!strcmp (p, "-fnext-runtime")) |
| flag_next_runtime = 1; |
| else |
| return c_decode_option (p); |
| |
| return 1; |
| } |
| |
| static tree |
| define_decl (declarator, declspecs) |
| tree declarator; |
| tree declspecs; |
| { |
| tree decl = start_decl (declarator, declspecs, 0, NULL_TREE, NULL_TREE); |
| finish_decl (decl, NULL_TREE, NULL_TREE); |
| return decl; |
| } |
| |
| /* 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. When the operation is |
| REFLEXIVE, check for compatibility in either direction. |
| |
| For statically typed objects, an assignment of the form `a' = `b' |
| is permitted if: |
| |
| `a' is of type "id", |
| `a' and `b' are the same class type, or |
| `a' and `b' are of class types A and B such that B is a descendant of A. */ |
| |
| int |
| maybe_objc_comptypes (lhs, rhs, reflexive) |
| tree lhs, rhs; |
| int reflexive; |
| { |
| if (doing_objc_thang) |
| return objc_comptypes (lhs, rhs, reflexive); |
| return -1; |
| } |
| |
| static tree |
| lookup_method_in_protocol_list (rproto_list, sel_name, class_meth) |
| 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 (rproto_list, lproto) |
| tree rproto_list; |
| tree lproto; |
| { |
| tree rproto, p; |
| |
| /* Make sure the protocol is support 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. When the operation |
| is REFLEXIVE, check for compatibility in either direction. */ |
| |
| int |
| objc_comptypes (lhs, rhs, reflexive) |
| tree lhs; |
| tree rhs; |
| int reflexive; |
| { |
| /* New clause for protocols. */ |
| |
| 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; |
| |
| if (rhs_is_proto) |
| { |
| rproto_list = TYPE_PROTOCOL_LIST (rhs); |
| |
| /* 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 = lookup_protocol_in_reflist (rproto_list, p); |
| |
| if (!rproto) |
| warning ("object does not conform to the `%s' protocol", |
| IDENTIFIER_POINTER (PROTOCOL_NAME (p))); |
| } |
| } |
| else if (TYPED_OBJECT (TREE_TYPE (rhs))) |
| { |
| tree rname = 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); |
| |
| /* 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 (TYPE_NAME (TREE_TYPE (rhs))), |
| IDENTIFIER_POINTER (PROTOCOL_NAME (p))); |
| } |
| } |
| |
| /* May change...based on whether there was any mismatch */ |
| return 1; |
| } |
| else if (rhs_is_proto) |
| /* Lhs is not a protocol...warn if it is statically typed */ |
| return (TYPED_OBJECT (TREE_TYPE (lhs)) != 0); |
| |
| else |
| /* Defer to comptypes .*/ |
| return -1; |
| } |
| |
| else if (TREE_CODE (lhs) == RECORD_TYPE && TREE_CODE (rhs) == RECORD_TYPE) |
| ; /* Fall thru. This is the case we have been handling all along */ |
| else |
| /* Defer to comptypes. */ |
| return -1; |
| |
| /* `id' = `<class> *', `<class> *' = `id' */ |
| |
| if ((TYPE_NAME (lhs) == objc_object_id && TYPED_OBJECT (rhs)) |
| || (TYPE_NAME (rhs) == objc_object_id && TYPED_OBJECT (lhs))) |
| return 1; |
| |
| /* `id' = `Class', `Class' = `id' */ |
| |
| else if ((TYPE_NAME (lhs) == objc_object_id |
| && TYPE_NAME (rhs) == objc_class_id) |
| || (TYPE_NAME (lhs) == objc_class_id |
| && TYPE_NAME (rhs) == objc_object_id)) |
| return 1; |
| |
| /* `<class> *' = `<class> *' */ |
| |
| else if (TYPED_OBJECT (lhs) && TYPED_OBJECT (rhs)) |
| { |
| tree lname = TYPE_NAME (lhs); |
| tree rname = 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 |
| /* Defer to comptypes. */ |
| return -1; |
| } |
| |
| /* Called from c-decl.c before all calls to rest_of_decl_compilation. */ |
| |
| void |
| objc_check_decl (decl) |
| tree decl; |
| { |
| tree type = TREE_TYPE (decl); |
| |
| if (TREE_CODE (type) == RECORD_TYPE |
| && TREE_STATIC_TEMPLATE (type) |
| && type != constant_string_type) |
| { |
| error_with_decl (decl, "`%s' cannot be statically allocated"); |
| fatal ("statically allocated objects not supported"); |
| } |
| } |
| |
| void |
| maybe_objc_check_decl (decl) |
| tree decl; |
| { |
| if (doing_objc_thang) |
| objc_check_decl (decl); |
| } |
| |
| /* Implement static typing. At this point, we know we have an interface. */ |
| |
| tree |
| get_static_reference (interface, protocols) |
| tree interface; |
| tree protocols; |
| { |
| tree type = xref_tag (RECORD_TYPE, interface); |
| |
| if (protocols) |
| { |
| tree t, m = TYPE_MAIN_VARIANT (type); |
| |
| push_obstacks_nochange (); |
| end_temporary_allocation (); |
| t = copy_node (type); |
| TYPE_BINFO (t) = make_tree_vec (2); |
| pop_obstacks (); |
| |
| /* 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. */ |
| 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) = 0; |
| |
| type = t; |
| } |
| |
| return type; |
| } |
| |
| tree |
| get_object_reference (protocols) |
| 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 |
| fatal ("Undefined type `id', please import <objc/objc.h>"); |
| |
| /* 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); |
| |
| push_obstacks_nochange (); |
| end_temporary_allocation (); |
| t = copy_node (type); |
| TYPE_BINFO (t) = make_tree_vec (2); |
| pop_obstacks (); |
| |
| /* 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 */ |
| 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; |
| |
| type = t; |
| } |
| return type; |
| } |
| |
| static tree |
| lookup_and_install_protocols (protocols) |
| tree protocols; |
| { |
| tree proto; |
| tree prev = NULL; |
| tree return_value = protocols; |
| |
| 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)); |
| if (prev) |
| TREE_CHAIN (prev) = TREE_CHAIN (proto); |
| else |
| return_value = TREE_CHAIN (proto); |
| } |
| else |
| { |
| /* Replace identifier with actual protocol node. */ |
| TREE_VALUE (proto) = p; |
| prev = proto; |
| } |
| } |
| |
| 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 (code, type, name) |
| enum tree_code code; |
| tree type; |
| char *name; |
| { |
| tree decl = build_decl (code, get_identifier (name), type); |
| |
| if (code == VAR_DECL) |
| { |
| TREE_STATIC (decl) = 1; |
| make_decl_rtl (decl, 0, 1); |
| pushdecl (decl); |
| } |
| |
| DECL_ARTIFICIAL (decl) = 1; |
| return decl; |
| } |
| |
| /* Purpose: "play" parser, creating/installing representations |
| of the declarations that are required by Objective-C. |
| |
| Model: |
| |
| type_spec--------->sc_spec |
| (tree_list) (tree_list) |
| | | |
| | | |
| identifier_node identifier_node */ |
| |
| static void |
| synth_module_prologue () |
| { |
| tree temp_type; |
| tree super_p; |
| |
| /* 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)); |
| protocol_type = build_pointer_type (xref_tag (RECORD_TYPE, |
| get_identifier (PROTOCOL_OBJECT_CLASS_NAME))); |
| |
| /* Declare type of selector-objects that represent an operation name. */ |
| |
| #ifdef OBJC_INT_SELECTORS |
| /* `unsigned int' */ |
| selector_type = unsigned_type_node; |
| #else |
| /* `struct objc_selector *' */ |
| selector_type |
| = build_pointer_type (xref_tag (RECORD_TYPE, |
| get_identifier (TAG_SELECTOR))); |
| #endif /* not OBJC_INT_SELECTORS */ |
| |
| /* Forward declare type, or else the prototype for msgSendSuper will |
| complain. */ |
| |
| super_p = 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; |
| |
| if (flag_traditional && TAG_MSGSEND[0] != '_') |
| DECL_BUILT_IN_NONANSI (umsg_decl) = 1; |
| |
| make_decl_rtl (umsg_decl, NULL_PTR, 1); |
| pushdecl (umsg_decl); |
| } |
| else |
| umsg_decl = builtin_function (TAG_MSGSEND, temp_type, NOT_BUILT_IN, 0); |
| |
| /* id objc_msgSendSuper (struct objc_super *, SEL, ...); */ |
| |
| temp_type |
| = build_function_type (id_type, |
| tree_cons (NULL_TREE, super_p, |
| tree_cons (NULL_TREE, selector_type, |
| NULL_TREE))); |
| |
| umsg_super_decl = builtin_function (TAG_MSGSENDSUPER, |
| temp_type, NOT_BUILT_IN, 0); |
| |
| /* id objc_getClass (const char *); */ |
| |
| temp_type = build_function_type (id_type, |
| tree_cons (NULL_TREE, |
| const_string_type_node, |
| tree_cons (NULL_TREE, void_type_node, |
| NULL_TREE))); |
| |
| objc_get_class_decl |
| = builtin_function (TAG_GETCLASS, temp_type, NOT_BUILT_IN, 0); |
| |
| /* id objc_getMetaClass (const char *); */ |
| |
| objc_get_meta_class_decl |
| = builtin_function (TAG_GETMETACLASS, temp_type, NOT_BUILT_IN, 0); |
| |
| /* 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; |
| write_symbols = NO_DEBUG; |
| |
| build_selector_template (); |
| temp_type = build_array_type (objc_selector_template, NULL_TREE); |
| |
| write_symbols = save_write_symbols; |
| } |
| 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. */ |
| constant_string_id = get_identifier (STRING_OBJECT_CLASS_NAME); |
| constant_string_type = xref_tag (RECORD_TYPE, constant_string_id); |
| } |
| |
| /* Custom build_string which sets TREE_TYPE! */ |
| |
| static tree |
| my_build_string (len, str) |
| int len; |
| char *str; |
| { |
| int wide_flag = 0; |
| tree a_string = build_string (len, str); |
| |
| /* Some code from combine_strings, which is local to c-parse.y. */ |
| if (TREE_TYPE (a_string) == int_array_type_node) |
| wide_flag = 1; |
| |
| TREE_TYPE (a_string) |
| = build_array_type (wide_flag ? integer_type_node : char_type_node, |
| build_index_type (build_int_2 (len - 1, 0))); |
| |
| TREE_CONSTANT (a_string) = 1; /* Puts string in the readonly segment */ |
| TREE_STATIC (a_string) = 1; |
| |
| return a_string; |
| } |
| |
| /* Return a newly constructed OBJC_STRING_CST node whose value is |
| the LEN characters at STR. |
| The TREE_TYPE is not initialized. */ |
| |
| tree |
| build_objc_string (len, str) |
| int len; |
| char *str; |
| { |
| tree s = build_string (len, str); |
| |
| TREE_SET_CODE (s, OBJC_STRING_CST); |
| return s; |
| } |
| |
| /* Given a chain of OBJC_STRING_CST's, build a static instance of |
| NXConstanString 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 NXConstandString class |
| object. */ |
| |
| tree |
| build_objc_string_object (strings) |
| tree strings; |
| { |
| tree string, initlist, constructor; |
| int length; |
| |
| if (!doing_objc_thang) |
| objc_fatal (); |
| |
| if (lookup_interface (constant_string_id) == NULL_TREE) |
| { |
| error ("Cannot find interface declaration for `%s'", |
| IDENTIFIER_POINTER (constant_string_id)); |
| return error_mark_node; |
| } |
| |
| add_class_reference (constant_string_id); |
| |
| /* Combine_strings will work for OBJC_STRING_CST's too. */ |
| string = combine_strings (strings); |
| TREE_SET_CODE (string, STRING_CST); |
| length = TREE_STRING_LENGTH (string) - 1; |
| |
| if (! flag_next_runtime) |
| { |
| push_obstacks_nochange (); |
| end_temporary_allocation (); |
| if (! TREE_PERMANENT (strings)) |
| string = my_build_string (length + 1, |
| TREE_STRING_POINTER (string)); |
| } |
| |
| /* & ((NXConstantString) {0, string, length}) */ |
| |
| 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 = build_constructor (constant_string_type, nreverse (initlist)); |
| |
| if (!flag_next_runtime) |
| { |
| constructor |
| = objc_add_static_instance (constructor, constant_string_type); |
| pop_obstacks (); |
| } |
| |
| return (build_unary_op (ADDR_EXPR, constructor, 1)); |
| } |
| |
| /* Declare a static instance of CLASS_DECL initialized by CONSTRUCTOR. */ |
| |
| static tree |
| objc_add_static_instance (constructor, class_decl) |
| tree constructor, class_decl; |
| { |
| static int num_static_inst; |
| tree *chain, decl, decl_spec, decl_expr; |
| char buf[256]; |
| |
| push_obstacks_nochange (); |
| end_temporary_allocation (); |
| |
| /* 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 (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; |
| pushdecl_top_level (decl); |
| rest_of_decl_compilation (decl, 0, 1, 0); |
| |
| /* Do this here so it gets output later instead of possibly |
| inside something else we are writing. */ |
| DECL_INITIAL (decl) = constructor; |
| |
| /* Add the DECL to the head of this CLASS' list. */ |
| TREE_PURPOSE (*chain) = tree_cons (NULL_TREE, decl, TREE_PURPOSE (*chain)); |
| |
| pop_obstacks (); |
| return decl; |
| } |
| |
| /* Build a static constant CONSTRUCTOR |
| with type TYPE and elements ELTS. */ |
| |
| static tree |
| build_constructor (type, elts) |
| tree type, elts; |
| { |
| tree constructor = build (CONSTRUCTOR, type, NULL_TREE, elts); |
| |
| TREE_CONSTANT (constructor) = 1; |
| TREE_STATIC (constructor) = 1; |
| TREE_READONLY (constructor) = 1; |
| |
| 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 () |
| { |
| tree field_decl, field_decl_chain, index; |
| |
| 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); |
| |
| /* void *defs[cls_def_cnt + cat_def_cnt]; */ |
| |
| if (!flag_next_runtime) |
| index = build_index_type (build_int_2 (imp_count + cat_count, 0)); |
| else |
| index = build_index_type (build_int_2 (imp_count + cat_count - 1, |
| imp_count == 0 && cat_count == 0 |
| ? -1 : 0)); |
| field_decl = create_builtin_decl (FIELD_DECL, |
| build_array_type (ptr_type_node, index), |
| "defs"); |
| chainon (field_decl_chain, field_decl); |
| |
| finish_struct (objc_symtab_template, field_decl_chain, NULL_TREE); |
| } |
| |
| /* Create the initial value for the `defs' field of _objc_symtab. |
| This is a CONSTRUCTOR. */ |
| |
| static tree |
| init_def_list (type) |
| 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 build_constructor (type, nreverse (initlist)); |
| } |
| |
| /* Construct the initial value for all of _objc_symtab. */ |
| |
| static tree |
| init_objc_symtab (type) |
| 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 || static_instances_decl) |
| { |
| |
| 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 build_constructor (type, nreverse (initlist)); |
| } |
| |
| /* Push forward-declarations of all the categories |
| so that init_def_list can use them in a CONSTRUCTOR. */ |
| |
| static void |
| forward_declare_categories () |
| { |
| struct imp_entry *impent; |
| tree sav = 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. */ |
| implementation_context = impent->imp_context; |
| impent->class_decl |
| = create_builtin_decl (VAR_DECL, objc_category_template, |
| IDENTIFIER_POINTER (synth_id_with_class_suffix ("_OBJC_CATEGORY", implementation_context))); |
| } |
| } |
| implementation_context = sav; |
| } |
| |
| /* Create the declaration of _OBJC_SYMBOLS, with type `strict _objc_symtab' |
| and initialized appropriately. */ |
| |
| static void |
| generate_objc_symtab_decl () |
| { |
| 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, 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 (type) |
| 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 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 string which is the name of a 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 char * |
| build_module_descriptor () |
| { |
| 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 (input_filename, lineno, 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 (input_filename, lineno, 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 (input_filename, lineno, 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 (input_filename, lineno, 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, NULL_TREE); |
| |
| DECL_ARTIFICIAL (UOBJC_MODULES_decl) = 1; |
| DECL_IGNORED_P (UOBJC_MODULES_decl) = 1; |
| 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 0; |
| |
| { |
| tree parms, function_decl, decelerator, void_list_node; |
| tree function_type; |
| extern tree get_file_function_name (); |
| tree init_function_name = get_file_function_name ('I'); |
| |
| /* Declare void __objc_execClass (void *); */ |
| |
| void_list_node = build_tree_list (NULL_TREE, void_type_node); |
| function_type |
| = build_function_type (void_type_node, |
| tree_cons (NULL_TREE, ptr_type_node, |
| void_list_node)); |
| function_decl = build_decl (FUNCTION_DECL, |
| get_identifier (TAG_EXECCLASS), |
| function_type); |
| DECL_EXTERNAL (function_decl) = 1; |
| DECL_ARTIFICIAL (function_decl) = 1; |
| TREE_PUBLIC (function_decl) = 1; |
| |
| pushdecl (function_decl); |
| rest_of_decl_compilation (function_decl, 0, 0, 0); |
| |
| parms |
| = build_tree_list (NULL_TREE, |
| build_unary_op (ADDR_EXPR, UOBJC_MODULES_decl, 0)); |
| decelerator = build_function_call (function_decl, parms); |
| |
| /* void _GLOBAL_$I$<gnyf> () {objc_execClass (&L_OBJC_MODULES);} */ |
| |
| start_function (void_list_node, |
| build_parse_node (CALL_EXPR, init_function_name, |
| /* This has the format of the output |
| of get_parm_info. */ |
| tree_cons (NULL_TREE, NULL_TREE, |
| void_list_node), |
| NULL_TREE), |
| NULL_TREE, NULL_TREE, 0); |
| #if 0 /* This should be turned back on later |
| for the systems where collect is not needed. */ |
| /* Make these functions nonglobal |
| so each file can use the same name. */ |
| TREE_PUBLIC (current_function_decl) = 0; |
| #endif |
| TREE_USED (current_function_decl) = 1; |
| store_parm_decls (); |
| |
| assemble_external (function_decl); |
| c_expand_expr_stmt (decelerator); |
| |
| TREE_PUBLIC (current_function_decl) = 1; |
| |
| function_decl = current_function_decl; |
| finish_function (0); |
| |
| /* Return the name of the constructor function. */ |
| return XSTR (XEXP (DECL_RTL (function_decl), 0), 0); |
| } |
| } |
| |
| /* extern const char _OBJC_STRINGS[]; */ |
| |
| static void |
| generate_forward_declaration_to_string_table () |
| { |
| 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 (ident, section) |
| 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; |
| |
| for (; chain != 0; chain = TREE_VALUE (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 tree |
| generate_static_references () |
| { |
| tree decls = NULL_TREE, ident, decl_spec, expr_decl, expr = NULL_TREE; |
| tree class_name, class, decl, instance, idecl, 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, NULL_TREE); |
| DECL_CONTEXT (decl) = 0; |
| DECL_ARTIFICIAL (decl) = 1; |
| |
| /* Output {class_name, ...}. */ |
| class = TREE_VALUE (cl_chain); |
| class_name = get_objc_string_decl (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 = 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); |
| make_decl_rtl (decl, 0, 1); |
| TREE_USED (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, NULL_TREE); |
| TREE_USED (static_instances_decl) = 1; |
| DECL_CONTEXT (static_instances_decl) = 0; |
| DECL_ARTIFICIAL (static_instances_decl) = 1; |
| end_temporary_allocation (); |
| expr = build_constructor (TREE_TYPE (static_instances_decl), |
| nreverse (decls)); |
| finish_decl (static_instances_decl, expr, NULL_TREE); |
| } |
| |
| /* Output all strings. */ |
| |
| static void |
| generate_strings () |
| { |
| 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, NULL_TREE); |
| end_temporary_allocation (); |
| 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, 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, NULL_TREE); |
| string_expr = my_build_string (IDENTIFIER_LENGTH (string) + 1, |
| IDENTIFIER_POINTER (string)); |
| finish_decl (decl, string_expr, NULL_TREE); |
| } |
| } |
| |
| static tree |
| build_selector_reference_decl (name) |
| tree name; |
| { |
| tree decl, ident; |
| char buf[256]; |
| static int idx = 0; |
| |
| sprintf (buf, "_OBJC_SELECTOR_REFERENCES_%d", idx++); |
| |
| push_obstacks_nochange (); |
| end_temporary_allocation (); |
| |
| ident = get_identifier (buf); |
| |
| decl = build_decl (VAR_DECL, ident, selector_type); |
| DECL_EXTERNAL (decl) = 1; |
| TREE_PUBLIC (decl) = 1; |
| TREE_USED (decl) = 1; |
| TREE_READONLY (decl) = 1; |
| DECL_ARTIFICIAL (decl) = 1; |
| DECL_CONTEXT (decl) = 0; |
| |
| make_decl_rtl (decl, 0, 1); |
| pushdecl_top_level (decl); |
| |
| pop_obstacks (); |
| |
| return decl; |
| } |
| |
| /* Just a handy wrapper for add_objc_string. */ |
| |
| static tree |
| build_selector (ident) |
| 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! */ |
| } |
| |
| /* Synthesize the following expr: (char *)&_OBJC_STRINGS[<offset>] |
| The cast stops the compiler from issuing the following message: |
| grok.m: warning: initialization of non-const * pointer from const * |
| grok.m: warning: initialization between incompatible pointer types. */ |
| |
| static tree |
| build_msg_pool_reference (offset) |
| int offset; |
| { |
| tree expr = build_int_2 (offset, 0); |
| tree cast; |
| |
| expr = build_array_ref (UOBJC_STRINGS_decl, expr); |
| expr = build_unary_op (ADDR_EXPR, expr, 0); |
| |
| cast = build_tree_list (build_tree_list (NULL_TREE, |
| ridpointers[(int) RID_CHAR]), |
| build1 (INDIRECT_REF, NULL_TREE, NULL_TREE)); |
| TREE_TYPE (expr) = groktypename (cast); |
| return expr; |
| } |
| |
| static tree |
| init_selector (offset) |
| int offset; |
| { |
| tree expr = build_msg_pool_reference (offset); |
| TREE_TYPE (expr) = selector_type; |
| return expr; |
| } |
| |
| static void |
| build_selector_translation_table () |
| { |
| tree sc_spec, decl_specs; |
| tree chain, initlist = NULL_TREE; |
| int offset = 0; |
| tree decl, var_decl, name; |
| |
| /* The corresponding pop_obstacks is in finish_decl, |
| called at the end of this function. */ |
| if (! flag_next_runtime) |
| push_obstacks_nochange (); |
| |
| for (chain = sel_ref_chain; chain; chain = TREE_CHAIN (chain)) |
| { |
| tree expr; |
| |
| 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, 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 = 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) = (tree) 1; |
| initlist = 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 (proto) |
| tree proto; |
| { |
| tree encoding; |
| if (proto) |
| { |
| tree tmp_decl; |
| |
| if (! METHOD_ENCODING (proto)) |
| { |
| tmp_decl = build_tmp_function_decl (); |
| hack_method_prototype (proto, tmp_decl); |
| encoding = encode_method_prototype (proto, tmp_decl); |
| 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 (ident, proto) |
| tree ident, proto; |
| { |
| tree *chain = &sel_ref_chain; |
| tree expr; |
| int index = 0; |
| |
| while (*chain) |
| { |
| if (TREE_PURPOSE (*chain) == ident && TREE_VALUE (*chain) == proto) |
| goto return_at_index; |
| |
| index++; |
| chain = &TREE_CHAIN (*chain); |
| } |
| |
| *chain = perm_tree_cons (proto, 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 (ident) |
| 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 (ident); |
| |
| *chain = perm_tree_cons (expr, ident, NULL_TREE); |
| |
| return (flag_next_runtime |
| ? expr |
| : build_array_ref (UOBJC_SELECTOR_TABLE_decl, |
| build_int_2 (index, 0))); |
| } |
| |
| static tree |
| build_class_reference_decl (name) |
| tree name; |
| { |
| tree decl, ident; |
| char buf[256]; |
| static int idx = 0; |
| |
| sprintf (buf, "_OBJC_CLASS_REFERENCES_%d", idx++); |
| |
| push_obstacks_nochange (); |
| end_temporary_allocation (); |
| |
| ident = get_identifier (buf); |
| |
| decl = build_decl (VAR_DECL, ident, objc_class_type); |
| DECL_EXTERNAL (decl) = 1; |
| TREE_PUBLIC (decl) = 1; |
| TREE_USED (decl) = 1; |
| TREE_READONLY (decl) = 1; |
| DECL_CONTEXT (decl) = 0; |
| DECL_ARTIFICIAL (decl) = 1; |
| |
| make_decl_rtl (decl, 0, 1); |
| pushdecl_top_level (decl); |
| |
| pop_obstacks (); |
| |
| return decl; |
| } |
| |
| /* Create a class reference, but don't create a variable to reference |
| it. */ |
| |
| static void |
| add_class_reference (ident) |
| 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) = perm_tree_cons (NULL_TREE, ident, NULL_TREE); |
| } |
| else |
| cls_ref_chain = perm_tree_cons (NULL_TREE, ident, NULL_TREE); |
| } |
| |
| /* Get a class reference, creating it if necessary. Also create the |
| reference variable. */ |
| |
| tree |
| get_class_reference (ident) |
| tree ident; |
| { |
| if (flag_next_runtime) |
| { |
| 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 (ident); |
| |
| return TREE_PURPOSE (*chain); |
| } |
| |
| decl = build_class_reference_decl (ident); |
| *chain = perm_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); |
| } |
| } |
| |
| /* SEL_REFDEF_CHAIN is a list whose "value" fields will be instances |
| of identifier_node that represent the selector. It returns the |
| offset of the selector from the beginning of the _OBJC_STRINGS |
| pool. This offset is typically used by init_selector during code |
| generation. |
| |
| For each string section we have a chain which maps identifier nodes |
| to decls for the strings. */ |
| |
| static tree |
| add_objc_string (ident, section) |
| 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; |
| |
| 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 (ident, section); |
| |
| *chain = perm_tree_cons (decl, ident, NULL_TREE); |
| |
| return build_unary_op (ADDR_EXPR, decl, 1); |
| } |
| |
| static tree |
| build_objc_string_decl (name, section) |
| tree name; |
| enum string_section section; |
| { |
| tree decl, ident; |
| char buf[256]; |
| static int class_names_idx = 0; |
| static int meth_var_names_idx = 0; |
| static int meth_var_types_idx = 0; |
| |
| 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++); |
| |
| push_obstacks_nochange (); |
| end_temporary_allocation (); |
| ident = get_identifier (buf); |
| |
| decl = build_decl (VAR_DECL, ident, build_array_type (char_type_node, 0)); |
| DECL_EXTERNAL (decl) = 1; |
| TREE_PUBLIC (decl) = 1; |
| TREE_USED (decl) = 1; |
| TREE_READONLY (decl) = 1; |
| TREE_CONSTANT (decl) = 1; |
| DECL_CONTEXT (decl) = 0; |
| DECL_ARTIFICIAL (decl) = 1; |
| |
| make_decl_rtl (decl, 0, 1); |
| pushdecl_top_level (decl); |
| |
| pop_obstacks (); |
| |
| return decl; |
| } |
| |
| |
| void |
| objc_declare_alias (alias_ident, class_ident) |
| tree alias_ident; |
| tree class_ident; |
| { |
| if (!doing_objc_thang) |
| objc_fatal (); |
| |
| if (is_class_name (class_ident) != 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 (class_ident, alias_ident, alias_chain); |
| } |
| |
| void |
| objc_declare_class (ident_list) |
| tree ident_list; |
| { |
| tree list; |
| |
| if (!doing_objc_thang) |
| objc_fatal (); |
| |
| for (list = ident_list; list; list = TREE_CHAIN (list)) |
| { |
| tree ident = TREE_VALUE (list); |
| tree decl; |
| |
| if ((decl = lookup_name (ident))) |
| { |
| error ("`%s' redeclared as different kind of symbol", |
| IDENTIFIER_POINTER (ident)); |
| error_with_decl (decl, "previous declaration of `%s'"); |
| } |
| |
| if (! is_class_name (ident)) |
| { |
| tree record = xref_tag (RECORD_TYPE, ident); |
| TREE_STATIC_TEMPLATE (record) = 1; |
| class_chain = tree_cons (NULL_TREE, ident, class_chain); |
| } |
| } |
| } |
| |
| tree |
| is_class_name (ident) |
| tree ident; |
| { |
| tree chain; |
| |
| 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; |
| } |
| |
| tree |
| lookup_interface (ident) |
| tree ident; |
| { |
| tree chain; |
| |
| for (chain = interface_chain; chain; chain = TREE_CHAIN (chain)) |
| { |
| if (ident == CLASS_NAME (chain)) |
| return chain; |
| } |
| return NULL_TREE; |
| } |
| |
| static tree |
| objc_copy_list (list, head) |
| tree list; |
| tree *head; |
| { |
| tree newlist = NULL_TREE, tail = NULL_TREE; |
| |
| while (list) |
| { |
| tail = copy_node (list); |
| |
| /* The following statement fixes a bug when inheriting instance |
| variables that are declared to be bitfields. finish_struct |
| expects to find the width of the bitfield in DECL_INITIAL, |
| which it nulls out after processing the decl of the super |
| class...rather than change the way finish_struct works (which |
| is risky), I create the situation it expects...s.naroff |
| (7/23/89). */ |
| |
| if (DECL_BIT_FIELD (tail) && DECL_INITIAL (tail) == 0) |
| DECL_INITIAL (tail) = build_int_2 (DECL_FIELD_SIZE (tail), 0); |
| |
| newlist = chainon (newlist, tail); |
| list = TREE_CHAIN (list); |
| } |
| |
| *head = newlist; |
| return tail; |
| } |
| |
| /* Used by: build_private_template, get_class_ivars, and |
| continue_class. COPY is 1 when called from @defs. In this case |
| copy all fields. Otherwise don't copy leaf ivars since we rely on |
| them being side-effected exactly once by finish_struct. */ |
| |
| static tree |
| build_ivar_chain (interface, copy) |
| tree interface; |
| int copy; |
| { |
| tree my_name, super_name, ivar_chain; |
| |
| my_name = CLASS_NAME (interface); |
| super_name = CLASS_SUPER_NAME (interface); |
| |
| /* Possibly copy leaf ivars. */ |
| if (copy) |
| objc_copy_list (CLASS_IVARS (interface), &ivar_chain); |
| else |
| ivar_chain = CLASS_IVARS (interface); |
| |
| 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 ("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 = CLASS_IVARS (interface); |
| if (op1) |
| { |
| tree head, tail = objc_copy_list (op1, &head); |
| |
| /* Prepend super class ivars...make a copy of the list, we |
| do not want to alter the original. */ |
| TREE_CHAIN (tail) = ivar_chain; |
| ivar_chain = head; |
| } |
| } |
| return ivar_chain; |
| } |
| |
| /* struct <classname> { |
| struct objc_class *isa; |
| ... |
| }; */ |
| |
| static tree |
| build_private_template (class) |
| tree class; |
| { |
| tree ivar_context; |
| |
| if (CLASS_STATIC_TEMPLATE (class)) |
| { |
| uprivate_record = CLASS_STATIC_TEMPLATE (class); |
| ivar_context = TYPE_FIELDS (CLASS_STATIC_TEMPLATE (class)); |
| } |
| else |
| { |
| uprivate_record = start_struct (RECORD_TYPE, CLASS_NAME (class)); |
| |
| ivar_context = build_ivar_chain (class, 0); |
| |
| finish_struct (uprivate_record, ivar_context, NULL_TREE); |
| |
| CLASS_STATIC_TEMPLATE (class) = uprivate_record; |
| |
| /* mark this record as class template - for class type checking */ |
| TREE_STATIC_TEMPLATE (uprivate_record) = 1; |
| } |
| |
| instance_type |
| = groktypename (build_tree_list (build_tree_list (NULL_TREE, |
| uprivate_record), |
| build1 (INDIRECT_REF, NULL_TREE, |
| NULL_TREE))); |
| |
| return ivar_context; |
| } |
| |
| /* Begin code generation for protocols... */ |
| |
| /* struct objc_protocol { |
| char *protocol_name; |
| struct objc_protocol **protocol_list; |
| struct objc_method_desc *instance_methods; |
| struct objc_method_desc *class_methods; |
| }; */ |
| |
| static tree |
| build_protocol_template () |
| { |
| tree decl_specs, field_decl, field_decl_chain; |
| tree template; |
| |
| template = start_struct (RECORD_TYPE, get_identifier (UTAG_PROTOCOL)); |
| |
| /* struct objc_class *isa; */ |
| |
| decl_specs = build_tree_list (NULL_TREE, xref_tag (RECORD_TYPE, |
| get_identifier (UTAG_CLASS))); |
| field_decl = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("isa")); |
| field_decl |
| = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); |
| field_decl_chain = field_decl; |
| |
| /* char *protocol_name; */ |
| |
| decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_CHAR]); |
| field_decl |
| = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("protocol_name")); |
| field_decl |
| = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); |
| chainon (field_decl_chain, field_decl); |
| |
| /* struct objc_protocol **protocol_list; */ |
| |
| decl_specs = build_tree_list (NULL_TREE, template); |
| field_decl |
| = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("protocol_list")); |
| field_decl = build1 (INDIRECT_REF, NULL_TREE, field_decl); |
| field_decl |
| = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); |
| chainon (field_decl_chain, field_decl); |
| |
| /* struct objc_method_list *instance_methods; */ |
| |
| decl_specs |
| = build_tree_list (NULL_TREE, |
| xref_tag (RECORD_TYPE, |
| get_identifier (UTAG_METHOD_PROTOTYPE_LIST))); |
| field_decl |
| = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("instance_methods")); |
| field_decl |
| = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); |
| chainon (field_decl_chain, field_decl); |
| |
| /* struct objc_method_list *class_methods; */ |
| |
| decl_specs |
| = build_tree_list (NULL_TREE, |
| xref_tag (RECORD_TYPE, |
| get_identifier (UTAG_METHOD_PROTOTYPE_LIST))); |
| field_decl |
| = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("class_methods")); |
| field_decl |
| = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); |
| chainon (field_decl_chain, field_decl); |
| |
| return finish_struct (template, field_decl_chain, NULL_TREE); |
| } |
| |
| static tree |
| build_descriptor_table_initializer (type, entries) |
| tree type; |
| tree entries; |
| { |
| tree initlist = NULL_TREE; |
| |
| do |
| { |
| tree eltlist = NULL_TREE; |
| |
| eltlist |
| = tree_cons (NULL_TREE, |
| build_selector (METHOD_SEL_NAME (entries)), NULL_TREE); |
| eltlist |
| = tree_cons (NULL_TREE, |
| add_objc_string (METHOD_ENCODING (entries), |
| meth_var_types), |
| eltlist); |
| |
| initlist |
| = tree_cons (NULL_TREE, |
| build_constructor (type, nreverse (eltlist)), initlist); |
| |
| entries = TREE_CHAIN (entries); |
| } |
| while (entries); |
| |
| return build_constructor (build_array_type (type, 0), nreverse (initlist)); |
| } |
| |
| /* struct objc_method_prototype_list { |
| int count; |
| struct objc_method_prototype { |
| SEL name; |
| char *types; |
| } list[1]; |
| }; */ |
| |
| static tree |
| build_method_prototype_list_template (list_type, size) |
| tree list_type; |
| int size; |
| { |
| tree objc_ivar_list_record; |
| tree decl_specs, field_decl, field_decl_chain; |
| |
| /* Generate an unnamed struct definition. */ |
| |
| objc_ivar_list_record = start_struct (RECORD_TYPE, NULL_TREE); |
| |
| /* int method_count; */ |
| |
| decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_INT]); |
| field_decl = get_identifier ("method_count"); |
| |
| field_decl |
| = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); |
| field_decl_chain = field_decl; |
| |
| /* struct objc_method method_list[]; */ |
| |
| decl_specs = build_tree_list (NULL_TREE, list_type); |
| field_decl = build_nt (ARRAY_REF, get_identifier ("method_list"), |
| build_int_2 (size, 0)); |
| |
| field_decl |
| = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); |
| chainon (field_decl_chain, field_decl); |
| |
| finish_struct (objc_ivar_list_record, field_decl_chain, NULL_TREE); |
| |
| return objc_ivar_list_record; |
| } |
| |
| static tree |
| build_method_prototype_template () |
| { |
| tree proto_record; |
| tree decl_specs, field_decl, field_decl_chain; |
| |
| proto_record |
| = start_struct (RECORD_TYPE, get_identifier (UTAG_METHOD_PROTOTYPE)); |
| |
| #ifdef OBJC_INT_SELECTORS |
| /* unsigned int _cmd; */ |
| decl_specs |
| = tree_cons (NULL_TREE, ridpointers[(int) RID_UNSIGNED], NULL_TREE); |
| decl_specs = tree_cons (NULL_TREE, ridpointers[(int) RID_INT], decl_specs); |
| field_decl = get_identifier ("_cmd"); |
| #else /* OBJC_INT_SELECTORS */ |
| /* struct objc_selector *_cmd; */ |
| decl_specs = tree_cons (NULL_TREE, xref_tag (RECORD_TYPE, |
| get_identifier (TAG_SELECTOR)), NULL_TREE); |
| field_decl = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("_cmd")); |
| #endif /* OBJC_INT_SELECTORS */ |
| |
| field_decl |
| = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); |
| field_decl_chain = field_decl; |
| |
| decl_specs = tree_cons (NULL_TREE, ridpointers[(int) RID_CHAR], NULL_TREE); |
| field_decl |
| = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("method_types")); |
| field_decl |
| = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); |
| chainon (field_decl_chain, field_decl); |
| |
| finish_struct (proto_record, field_decl_chain, NULL_TREE); |
| |
| return proto_record; |
| } |
| |
| /* True if last call to forwarding_offset yielded a register offset. */ |
| static int offset_is_register; |
| |
| static int |
| forwarding_offset (parm) |
| tree parm; |
| { |
| int offset_in_bytes; |
| |
| if (GET_CODE (DECL_INCOMING_RTL (parm)) == MEM) |
| { |
| rtx addr = XEXP (DECL_INCOMING_RTL (parm), 0); |
| |
| /* ??? Here we assume that the parm address is indexed |
| off the frame pointer or arg pointer. |
| If that is not true, we produce meaningless results, |
| but do not crash. */ |
| if (GET_CODE (addr) == PLUS |
| && GET_CODE (XEXP (addr, 1)) == CONST_INT) |
| offset_in_bytes = INTVAL (XEXP (addr, 1)); |
| else |
| offset_in_bytes = 0; |
| |
| offset_in_bytes += OBJC_FORWARDING_STACK_OFFSET; |
| offset_is_register = 0; |
| } |
| else if (GET_CODE (DECL_INCOMING_RTL (parm)) == REG) |
| { |
| int regno = REGNO (DECL_INCOMING_RTL (parm)); |
| offset_in_bytes = apply_args_register_offset (regno); |
| offset_is_register = 1; |
| } |
| else |
| return 0; |
| |
| /* This is the case where the parm is passed as an int or double |
| and it is converted to a char, short or float and stored back |
| in the parmlist. In this case, describe the parm |
| with the variable's declared type, and adjust the address |
| if the least significant bytes (which we are using) are not |
| the first ones. */ |
| if (BYTES_BIG_ENDIAN && TREE_TYPE (parm) != DECL_ARG_TYPE (parm)) |
| offset_in_bytes += (GET_MODE_SIZE (TYPE_MODE (DECL_ARG_TYPE (parm))) |
| - GET_MODE_SIZE (GET_MODE (DECL_RTL (parm)))); |
| |
| return offset_in_bytes; |
| } |
| |
| static tree |
| encode_method_prototype (method_decl, func_decl) |
| tree method_decl; |
| tree func_decl; |
| { |
| tree parms; |
| int stack_size, i; |
| tree user_args; |
| int max_parm_end = 0; |
| char buf[40]; |
| tree result; |
| |
| /* ONEWAY and BYCOPY, for remote object are the only method qualifiers. */ |
| encode_type_qualifiers (TREE_PURPOSE (TREE_TYPE (method_decl))); |
| |
| /* C type. */ |
| encode_type (TREE_TYPE (TREE_TYPE (func_decl)), |
| obstack_object_size (&util_obstack), |
| OBJC_ENCODE_INLINE_DEFS); |
| |
| /* Stack size. */ |
| for (parms = DECL_ARGUMENTS (func_decl); parms; |
| parms = TREE_CHAIN (parms)) |
| { |
| int parm_end = (forwarding_offset (parms) |
| + (TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (parms))) |
| / BITS_PER_UNIT)); |
| |
| if (!offset_is_register && max_parm_end < parm_end) |
| max_parm_end = parm_end; |
| } |
| |
| stack_size = max_parm_end - OBJC_FORWARDING_MIN_OFFSET; |
| |
| sprintf (buf, "%d", stack_size); |
| obstack_grow (&util_obstack, buf, strlen (buf)); |
| |
| user_args = METHOD_SEL_ARGS (method_decl); |
| |
| /* Argument types. */ |
| for (parms = DECL_ARGUMENTS (func_decl), i = 0; parms; |
| parms = TREE_CHAIN (parms), i++) |
| { |
| /* Process argument qualifiers for user supplied arguments. */ |
| if (i > 1) |
| { |
| encode_type_qualifiers (TREE_PURPOSE (TREE_TYPE (user_args))); |
| user_args = TREE_CHAIN (user_args); |
| } |
| |
| /* Type. */ |
| encode_type (TREE_TYPE (parms), |
| obstack_object_size (&util_obstack), |
| OBJC_ENCODE_INLINE_DEFS); |
| |
| /* Compute offset. */ |
| sprintf (buf, "%d", forwarding_offset (parms)); |
| |
| /* Indicate register. */ |
| if (offset_is_register) |
| obstack_1grow (&util_obstack, '+'); |
| |
| obstack_grow (&util_obstack, buf, strlen (buf)); |
| } |
| |
| obstack_1grow (&util_obstack, '\0'); |
| result = get_identifier (obstack_finish (&util_obstack)); |
| obstack_free (&util_obstack, util_firstobj); |
| return result; |
| } |
| |
| static tree |
| generate_descriptor_table (type, name, size, list, proto) |
| tree type; |
| char *name; |
| int size; |
| tree list; |
| tree proto; |
| { |
| tree sc_spec, decl_specs, decl, initlist; |
| |
| sc_spec = tree_cons (NULL_TREE, ridpointers[(int) RID_STATIC], NULL_TREE); |
| decl_specs = tree_cons (NULL_TREE, type, sc_spec); |
| |
| decl = start_decl (synth_id_with_class_suffix (name, proto), |
| decl_specs, 1, NULL_TREE, NULL_TREE); |
| |
| initlist = build_tree_list (NULL_TREE, build_int_2 (size, 0)); |
| initlist = tree_cons (NULL_TREE, list, initlist); |
| |
| finish_decl (decl, build_constructor (type, nreverse (initlist)), |
| NULL_TREE); |
| |
| return decl; |
| } |
| |
| static void |
| generate_method_descriptors (protocol) /* generate_dispatch_tables */ |
| tree protocol; |
| { |
| static tree objc_method_prototype_template; |
| tree initlist, chain, method_list_template; |
| tree cast, variable_length_type; |
| int size; |
| |
| if (!objc_method_prototype_template) |
| objc_method_prototype_template = build_method_prototype_template (); |
| |
| cast = build_tree_list (build_tree_list (NULL_TREE, xref_tag (RECORD_TYPE, |
| get_identifier (UTAG_METHOD_PROTOTYPE_LIST))), |
| NULL_TREE); |
| variable_length_type = groktypename (cast); |
| |
| chain = PROTOCOL_CLS_METHODS (protocol); |
| if (chain) |
| { |
| size = list_length (chain); |
| |
| method_list_template |
| = build_method_prototype_list_template (objc_method_prototype_template, |
| size); |
| |
| initlist |
| = build_descriptor_table_initializer (objc_method_prototype_template, |
| chain); |
| |
| UOBJC_CLASS_METHODS_decl |
| = generate_descriptor_table (method_list_template, |
| "_OBJC_PROTOCOL_CLASS_METHODS", |
| size, initlist, protocol); |
| TREE_TYPE (UOBJC_CLASS_METHODS_decl) = variable_length_type; |
| } |
| else |
| UOBJC_CLASS_METHODS_decl = 0; |
| |
| chain = PROTOCOL_NST_METHODS (protocol); |
| if (chain) |
| { |
| size = list_length (chain); |
| |
| method_list_template |
| = build_method_prototype_list_template (objc_method_prototype_template, |
| size); |
| initlist |
| = build_descriptor_table_initializer (objc_method_prototype_template, |
| chain); |
| |
| UOBJC_INSTANCE_METHODS_decl |
| = generate_descriptor_table (method_list_template, |
| "_OBJC_PROTOCOL_INSTANCE_METHODS", |
| size, initlist, protocol); |
| TREE_TYPE (UOBJC_INSTANCE_METHODS_decl) = variable_length_type; |
| } |
| else |
| UOBJC_INSTANCE_METHODS_decl = 0; |
| } |
| |
| static tree |
| build_tmp_function_decl () |
| { |
| tree decl_specs, expr_decl, parms; |
| static int xxx = 0; |
| char buffer[80]; |
| |
| /* struct objc_object *objc_xxx (id, SEL, ...); */ |
| pushlevel (0); |
| decl_specs = build_tree_list (NULL_TREE, objc_object_reference); |
| push_parm_decl (build_tree_list |
| (build_tree_list (decl_specs, |
| build1 (INDIRECT_REF, NULL_TREE, |
| NULL_TREE)), |
| build_tree_list (NULL_TREE, NULL_TREE))); |
| |
| decl_specs = build_tree_list (NULL_TREE, xref_tag (RECORD_TYPE, |
| get_identifier (TAG_SELECTOR))); |
| expr_decl = build1 (INDIRECT_REF, NULL_TREE, NULL_TREE); |
| |
| push_parm_decl (build_tree_list (build_tree_list (decl_specs, expr_decl), |
| build_tree_list (NULL_TREE, NULL_TREE))); |
| parms = get_parm_info (0); |
| poplevel (0, 0, 0); |
| |
| decl_specs = build_tree_list (NULL_TREE, objc_object_reference); |
| sprintf (buffer, "__objc_tmp_%x", xxx++); |
| expr_decl = build_nt (CALL_EXPR, get_identifier (buffer), parms, NULL_TREE); |
| expr_decl = build1 (INDIRECT_REF, NULL_TREE, expr_decl); |
| |
| return define_decl (expr_decl, decl_specs); |
| } |
| |
| static void |
| hack_method_prototype (nst_methods, tmp_decl) |
| tree nst_methods; |
| tree tmp_decl; |
| { |
| tree parms; |
| tree parm; |
| |
| /* Hack to avoid problem with static typing of self arg. */ |
| TREE_SET_CODE (nst_methods, CLASS_METHOD_DECL); |
| start_method_def (nst_methods); |
| TREE_SET_CODE (nst_methods, INSTANCE_METHOD_DECL); |
| |
| if (METHOD_ADD_ARGS (nst_methods) == (tree) 1) |
| parms = get_parm_info (0); /* we have a `, ...' */ |
| else |
| parms = get_parm_info (1); /* place a `void_at_end' */ |
| |
| poplevel (0, 0, 0); /* Must be called BEFORE start_function. */ |
| |
| /* Usually called from store_parm_decls -> init_function_start. */ |
| |
| DECL_ARGUMENTS (tmp_decl) = TREE_PURPOSE (parms); |
| current_function_decl = tmp_decl; |
| |
| { |
| /* Code taken from start_function. */ |
| tree restype = TREE_TYPE (TREE_TYPE (tmp_decl)); |
| /* Promote the value to int before returning it. */ |
| if (TREE_CODE (restype) == INTEGER_TYPE |
| && TYPE_PRECISION (restype) < TYPE_PRECISION (integer_type_node)) |
| restype = integer_type_node; |
| DECL_RESULT (tmp_decl) = build_decl (RESULT_DECL, 0, restype); |
| } |
| |
| for (parm = DECL_ARGUMENTS (tmp_decl); parm; parm = TREE_CHAIN (parm)) |
| DECL_CONTEXT (parm) = tmp_decl; |
| |
| init_function_start (tmp_decl, "objc-act", 0); |
| |
| /* Typically called from expand_function_start for function definitions. */ |
| assign_parms (tmp_decl, 0); |
| |
| /* install return type */ |
| TREE_TYPE (TREE_TYPE (tmp_decl)) = groktypename (TREE_TYPE (nst_methods)); |
| |
| } |
| |
| static void |
| generate_protocol_references (plist) |
| tree plist; |
| { |
| tree lproto; |
| |
| /* Forward declare protocols referenced. */ |
| for (lproto = plist; lproto; lproto = TREE_CHAIN (lproto)) |
| { |
| tree proto = TREE_VALUE (lproto); |
| |
| if (TREE_CODE (proto) == PROTOCOL_INTERFACE_TYPE |
| && PROTOCOL_NAME (proto)) |
| { |
| if (! PROTOCOL_FORWARD_DECL (proto)) |
| build_protocol_reference (proto); |
| |
| if (PROTOCOL_LIST (proto)) |
| generate_protocol_references (PROTOCOL_LIST (proto)); |
| } |
| } |
| } |
| |
| static void |
| generate_protocols () |
| { |
| tree p, tmp_decl, encoding; |
| tree sc_spec, decl_specs, decl; |
| tree initlist, protocol_name_expr, refs_decl, refs_expr; |
| tree cast_type2 = 0; |
| |
| tmp_decl = build_tmp_function_decl (); |
| |
| if (! objc_protocol_template) |
| objc_protocol_template = build_protocol_template (); |
| |
| /* If a protocol was directly referenced, pull in indirect references. */ |
| for (p = protocol_chain; p; p = TREE_CHAIN (p)) |
| if (PROTOCOL_FORWARD_DECL (p) && PROTOCOL_LIST (p)) |
| generate_protocol_references (PROTOCOL_LIST (p)); |
| |
| for (p = protocol_chain; p; p = TREE_CHAIN (p)) |
| { |
| tree nst_methods = PROTOCOL_NST_METHODS (p); |
| tree cls_methods = PROTOCOL_CLS_METHODS (p); |
| |
| /* If protocol wasn't referenced, don't generate any code. */ |
| if (! PROTOCOL_FORWARD_DECL (p)) |
| continue; |
| |
| /* Make sure we link in the Protocol class. */ |
| add_class_reference (get_identifier (PROTOCOL_OBJECT_CLASS_NAME)); |
| |
| while (nst_methods) |
| { |
| if (! METHOD_ENCODING (nst_methods)) |
| { |
| hack_method_prototype (nst_methods, tmp_decl); |
| encoding = encode_method_prototype (nst_methods, tmp_decl); |
| METHOD_ENCODING (nst_methods) = encoding; |
| } |
| nst_methods = TREE_CHAIN (nst_methods); |
| } |
| |
| while (cls_methods) |
| { |
| if (! METHOD_ENCODING (cls_methods)) |
| { |
| hack_method_prototype (cls_methods, tmp_decl); |
| encoding = encode_method_prototype (cls_methods, tmp_decl); |
| METHOD_ENCODING (cls_methods) = encoding; |
| } |
| |
| cls_methods = TREE_CHAIN (cls_methods); |
| } |
| generate_method_descriptors (p); |
| |
| if (PROTOCOL_LIST (p)) |
| refs_decl = generate_protocol_list (p); |
| else |
| refs_decl = 0; |
| |
| /* static struct objc_protocol _OBJC_PROTOCOL_<mumble>; */ |
| |
| sc_spec = tree_cons (NULL_TREE, ridpointers[(int) RID_STATIC], |
| NULL_TREE); |
| decl_specs = tree_cons (NULL_TREE, objc_protocol_template, sc_spec); |
| |
| decl = start_decl (synth_id_with_class_suffix ("_OBJC_PROTOCOL", p), |
| decl_specs, 1, NULL_TREE, NULL_TREE); |
| |
| protocol_name_expr = add_objc_string (PROTOCOL_NAME (p), class_names); |
| |
| if (refs_decl) |
| { |
| if (!cast_type2) |
| cast_type2 |
| = groktypename |
| (build_tree_list (build_tree_list (NULL_TREE, |
| objc_protocol_template), |
| build1 (INDIRECT_REF, NULL_TREE, |
| build1 (INDIRECT_REF, NULL_TREE, |
| NULL_TREE)))); |
| |
| refs_expr = build_unary_op (ADDR_EXPR, refs_decl, 0); |
| TREE_TYPE (refs_expr) = cast_type2; |
| } |
| else |
| refs_expr = build_int_2 (0, 0); |
| |
| /* UOBJC_INSTANCE_METHODS_decl/UOBJC_CLASS_METHODS_decl are set |
| by generate_method_descriptors, which is called above. */ |
| initlist = build_protocol_initializer (TREE_TYPE (decl), |
| protocol_name_expr, refs_expr, |
| UOBJC_INSTANCE_METHODS_decl, |
| UOBJC_CLASS_METHODS_decl); |
| finish_decl (decl, initlist, NULL_TREE); |
| |
| /* Mark the decl as used to avoid "defined but not used" warning. */ |
| TREE_USED (decl) = 1; |
| } |
| } |
| |
| static tree |
| build_protocol_initializer (type, protocol_name, protocol_list, |
| instance_methods, class_methods) |
| tree type; |
| tree protocol_name; |
| tree protocol_list; |
| tree instance_methods; |
| tree class_methods; |
| { |
| tree initlist = NULL_TREE, expr; |
| static tree cast_type = 0; |
| |
| if (!cast_type) |
| cast_type |
| = groktypename |
| (build_tree_list |
| (build_tree_list (NULL_TREE, |
| xref_tag (RECORD_TYPE, |
| get_identifier (UTAG_CLASS))), |
| build1 (INDIRECT_REF, NULL_TREE, NULL_TREE))); |
| |
| /* Filling the "isa" in with one allows the runtime system to |
| detect that the version change...should remove before final release. */ |
| |
| expr = build_int_2 (PROTOCOL_VERSION, 0); |
| TREE_TYPE (expr) = cast_type; |
| initlist = tree_cons (NULL_TREE, expr, initlist); |
| initlist = tree_cons (NULL_TREE, protocol_name, initlist); |
| initlist = tree_cons (NULL_TREE, protocol_list, initlist); |
| |
| if (!instance_methods) |
| initlist = tree_cons (NULL_TREE, build_int_2 (0, 0), initlist); |
| else |
| { |
| expr = build_unary_op (ADDR_EXPR, instance_methods, 0); |
| initlist = tree_cons (NULL_TREE, expr, initlist); |
| } |
| |
| if (!class_methods) |
| initlist = tree_cons (NULL_TREE, build_int_2 (0, 0), initlist); |
| else |
| { |
| expr = build_unary_op (ADDR_EXPR, class_methods, 0); |
| initlist = tree_cons (NULL_TREE, expr, initlist); |
| } |
| |
| return build_constructor (type, nreverse (initlist)); |
| } |
| |
| /* struct objc_category { |
| char *category_name; |
| char *class_name; |
| struct objc_method_list *instance_methods; |
| struct objc_method_list *class_methods; |
| struct objc_protocol_list *protocols; |
| }; */ |
| |
| static void |
| build_category_template () |
| { |
| tree decl_specs, field_decl, field_decl_chain; |
| |
| objc_category_template = start_struct (RECORD_TYPE, |
| get_identifier (UTAG_CATEGORY)); |
| /* char *category_name; */ |
| |
| decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_CHAR]); |
| field_decl |
| = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("category_name")); |
| field_decl |
| = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); |
| field_decl_chain = field_decl; |
| |
| /* char *class_name; */ |
| |
| decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_CHAR]); |
| field_decl = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("class_name")); |
| field_decl |
| = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); |
| chainon (field_decl_chain, field_decl); |
| |
| /* struct objc_method_list *instance_methods; */ |
| |
| decl_specs = build_tree_list (NULL_TREE, |
| xref_tag (RECORD_TYPE, |
| get_identifier (UTAG_METHOD_LIST))); |
| field_decl |
| = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("instance_methods")); |
| field_decl |
| = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); |
| chainon (field_decl_chain, field_decl); |
| |
| /* struct objc_method_list *class_methods; */ |
| |
| decl_specs = build_tree_list (NULL_TREE, |
| xref_tag (RECORD_TYPE, |
| get_identifier (UTAG_METHOD_LIST))); |
| field_decl |
| = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("class_methods")); |
| field_decl |
| = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); |
| chainon (field_decl_chain, field_decl); |
| |
| /* struct objc_protocol **protocol_list; */ |
| |
| decl_specs = build_tree_list (NULL_TREE, |
| xref_tag (RECORD_TYPE, |
| get_identifier (UTAG_PROTOCOL))); |
| field_decl |
| = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("protocol_list")); |
| field_decl = build1 (INDIRECT_REF, NULL_TREE, field_decl); |
| field_decl |
| = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); |
| chainon (field_decl_chain, field_decl); |
| |
| finish_struct (objc_category_template, field_decl_chain, NULL_TREE); |
| } |
| |
| /* struct objc_selector { |
| void *sel_id; |
| char *sel_type; |
| }; */ |
| |
| static void |
| build_selector_template () |
| { |
| |
| tree decl_specs, field_decl, field_decl_chain; |
| |
| objc_selector_template |
| = start_struct (RECORD_TYPE, get_identifier (UTAG_SELECTOR)); |
| |
| /* void *sel_id; */ |
| |
| decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_VOID]); |
| field_decl = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("sel_id")); |
| field_decl |
| = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); |
| field_decl_chain = field_decl; |
| |
| /* char *sel_type; */ |
| |
| decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_CHAR]); |
| field_decl = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("sel_type")); |
| field_decl |
| = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); |
| chainon (field_decl_chain, field_decl); |
| |
| finish_struct (objc_selector_template, field_decl_chain, NULL_TREE); |
| } |
| |
| /* struct objc_class { |
| struct objc_class *isa; |
| struct objc_class *super_class; |
| char *name; |
| long version; |
| long info; |
| long instance_size; |
| struct objc_ivar_list *ivars; |
| struct objc_method_list *methods; |
| if (flag_next_runtime) |
| struct objc_cache *cache; |
| else { |
| struct sarray *dtable; |
| struct objc_class *subclass_list; |
| struct objc_class *sibling_class; |
| } |
| struct objc_protocol_list *protocols; |
| }; */ |
| |
| static void |
| build_class_template () |
| { |
| tree decl_specs, field_decl, field_decl_chain; |
| |
| objc_class_template |
| = start_struct (RECORD_TYPE, get_identifier (UTAG_CLASS)); |
| |
| /* struct objc_class *isa; */ |
| |
| decl_specs = build_tree_list (NULL_TREE, objc_class_template); |
| field_decl = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("isa")); |
| field_decl |
| = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); |
| field_decl_chain = field_decl; |
| |
| /* struct objc_class *super_class; */ |
| |
| decl_specs = build_tree_list (NULL_TREE, objc_class_template); |
| field_decl |
| = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("super_class")); |
| field_decl |
| = grokfield (input_filename, lineno, 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 (input_filename, lineno, field_decl, decl_specs, NULL_TREE); |
| chainon (field_decl_chain, field_decl); |
| |
| /* long version; */ |
| |
| decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_LONG]); |
| field_decl = get_identifier ("version"); |
| field_decl |
| = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); |
| chainon (field_decl_chain, field_decl); |
| |
| |