From 6c65299b5eb54054e639c3f2523e30542fe25555 Mon Sep 17 00:00:00 2001 From: Richard Stallman Date: Fri, 17 Jan 1992 23:15:38 +0000 Subject: [PATCH] Initial revision From-SVN: r203 --- gcc/objc/objc-act.c | 5217 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 5217 insertions(+) create mode 100644 gcc/objc/objc-act.c diff --git a/gcc/objc/objc-act.c b/gcc/objc/objc-act.c new file mode 100644 index 00000000000..dcc269dc2e4 --- /dev/null +++ b/gcc/objc/objc-act.c @@ -0,0 +1,5217 @@ +/* Implement classes and message passing for Objective C. + Copyright (C) 1992 Free Software Foundation, Inc. + Author: 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, 675 Mass Ave, Cambridge, MA 02139, 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, OBJC_NONUNIQUE_SELECTORS + */ + +#include +#include "config.h" +#include "tree.h" +#include "c-tree.h" +#include "c-lex.h" +#include "flags.h" +#include "objc-actions.h" +#include "input.h" + +/* 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 + +/* for encode_method_def */ +#include "rtl.h" + +#define OBJC_VERSION 2 + +#define NULLT (tree) 0 + +#define OBJC_ENCODE_INLINE_DEFS 0 +#define OBJC_ENCODE_DONT_INLINE_DEFS 1 + +/*** Private Interface (procedures) ***/ + +/* code generation */ + +static void synth_module_prologue (); +static char *build_module_descriptor (); +static tree init_module_descriptor (); +static void build_module_entry (); +static tree build_objc_method_call (); +static void build_message_selector_pool (); +static void build_selector_translation_table (); +static tree build_ivar_chain (); + +static tree build_ivar_template (); +static tree build_method_template (); +static tree build_private_template (); +static void build_class_template (); +static void build_category_template (); +static tree build_super_template (); + +static void synth_forward_declarations (); +static void generate_ivar_lists (); +static void generate_dispatch_tables (); +static void generate_shared_structures (); + +static tree build_msg_pool_reference (); +static tree init_selector (); +static tree build_keword_selector (); +static tree synth_id_with_class_suffix (); + +/* misc. bookkeeping */ + +typedef struct hashedEntry *hash; +typedef struct hashedAttribute *attr; + +struct hashedAttribute { + attr next; + tree value; +}; +struct hashedEntry { + attr list; + hash next; + tree key; +}; +static void hash_init (); +static void hash_enter (); +static hash hash_lookup (); +static void hash_add_attr (); +static tree lookup_method (); +static tree lookup_instance_method_static (); +static tree lookup_class_method_static (); +static tree add_class (); +static int add_selector_reference (); +static void add_class_reference (); +static int add_objc_string (); + +/* type encoding */ + +static void encode_aggregate (); +static void encode_bitfield (); +static void encode_type (); +static void encode_field_decl (); + +static void really_start_method (); +static int comp_method_with_proto (); +static int comp_proto_with_proto (); +static tree get_arg_type_list (); +static tree expr_last (); + +/* utilities for debugging and error diagnostics: */ + +static void warn_with_method (); +static void error_with_method (); +static void error_with_ivar (); +static char *gen_method_decl (); +static char *gen_declaration (); +static char *gen_declarator (); +static int is_complex_decl (); +static void adorn_decl (); +static void dump_interfaces (); + +/*** 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 _TAG_CLASS "_objc_class" +#define _TAG_IVAR "_objc_ivar" +#define _TAG_IVAR_LIST "_objc_ivar_list" +#define _TAG_METHOD "_objc_method" +#define _TAG_METHOD_LIST "_objc_method_list" +#define _TAG_CATEGORY "_objc_category" +#define _TAG_MODULE "_objc_module" +#define _TAG_SYMTAB "_objc_symtab" +#define _TAG_SUPER "_objc_super" + +/* 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, _cmd_id, _msg_id, _msgSuper_id; +static tree objc_getClass_id, objc_getMetaClass_id; + +static tree self_decl, _msg_decl, _msgSuper_decl; +static tree objc_getClass_decl, objc_getMetaClass_decl; + +static tree super_type, _selector_type, id_type, class_type; +static tree instance_type; + +static tree interface_chain = NULLT; + +/* chains to manage selectors that are referenced and defined in the module */ + +static tree cls_ref_chain = NULLT; /* classes referenced */ +static tree sel_ref_chain = NULLT; /* selectors referenced */ +static tree sel_refdef_chain = NULLT; /* selectors references & defined */ +static int max_selector_index; /* total # of selector referenced */ + +/* hash tables to manage the global pool of method prototypes */ + +static hash *nst_method_hash_list = 0; +static hash *cls_method_hash_list = 0; + +/* the following are used when compiling a class implementation. + * + * implementation_template will normally be an anInterface, however if + * none exists this will be equal to implementation_context...it is + * set in start_class. + */ + +/* backend data declarations */ + +static tree _OBJC_SYMBOLS_decl; +static tree _OBJC_INSTANCE_VARIABLES_decl, _OBJC_CLASS_VARIABLES_decl; +static tree _OBJC_INSTANCE_METHODS_decl, _OBJC_CLASS_METHODS_decl; +static tree _OBJC_CLASS_decl, _OBJC_METACLASS_decl; +#ifdef OBJC_NONUNIQUE_SELECTORS +static tree _OBJC_SELECTOR_REFERENCES_decl; +#endif +static tree _OBJC_MODULES_decl; +static tree _OBJC_STRINGS_decl; + +static tree implementation_context = NULLT, + implementation_template = NULLT; + +struct imp_entry { + struct imp_entry *next; + tree imp_context; + tree imp_template; + tree class_decl; /* _OBJC_CLASS_; */ + tree meta_decl; /* _OBJC_METACLASS_; */ +}; +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, _PRIVATE_record; +static tree _clsSuper_ref, __clsSuper_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; +#ifdef OBJC_NONUNIQUE_SELECTORS +static tree _OBJC_SELECTOR_REFERENCES_id; +#endif +static tree _OBJC_SUPER_decl; + +static tree method_context = NULLT; +static int method_slot = 0; /* used by start_method_def */ + +#define BUFSIZE 512 + +static char *errbuf; /* a buffer for error diagnostics */ +static char *utlbuf; /* a buffer for general utility */ + +extern char *strcpy (), *strcat (); + +extern tree groktypename_in_parm_context (); + +extern struct obstack permanent_obstack, *current_obstack, *rtl_obstack; + +/* data imported from toplev.c */ + +extern char *dump_base_name; + +/* 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; + +void +lang_init () +{ + /* 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); + + /* 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 (doing_objc_thang) + init_objc (); +} + +void +objc_finish () +{ + if (doing_objc_thang) + finish_objc (); /* Objective-C finalization */ + + if (gen_declaration_file) + fclose (gen_declaration_file); +} + +void +lang_finish () +{ +} + +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 + return c_decode_option (p); + + return 1; +} + +static tree +define_decl (declarator, declspecs) + tree declarator; + tree declspecs; +{ + tree decl = start_decl (declarator, declspecs, 0); + finish_decl (decl, NULLT, NULLT); + return decl; +} + +/* + * rules for statically typed objects...called from `c-typeck.comptypes'. + * + * an assignment of the form `a' = `b' is permitted if: + * + * - `a' is of type "id". + * - `a' and `b' are the same class type. + * - `a' and `b' are of class types A and B such that B is a descendant + * of A. + */ + +int +maybe_objc_comptypes (lhs, rhs) + tree lhs, rhs; +{ + if (doing_objc_thang) + return objc_comptypes (lhs, rhs); + return 0; +} + +int +objc_comptypes (lhs, rhs) + tree lhs; + tree rhs; +{ + /* `id' = ` *', ` *' = `id' */ + + if ((TYPE_NAME (lhs) == objc_object_id && TYPED_OBJECT (rhs)) + || (TYPED_OBJECT (lhs) && TYPE_NAME (rhs) == objc_object_id)) + 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; + + /* ` *' = ` *' */ + + else if (TYPED_OBJECT (lhs) && TYPED_OBJECT (rhs)) + { + tree lname = TYPE_NAME (lhs), rname = TYPE_NAME (rhs); + + if (lname == rname) + return 1; + else + { + /* if the left hand side is a super class of the right hand side, + allow it... + */ + tree rinter = lookup_interface (rname); + + while (rinter) + { + if (lname == CLASS_SUPER_NAME (rinter)) + return 1; + + rinter = lookup_interface (CLASS_SUPER_NAME (rinter)); + } + + return 0; + } + } + else + return 0; +} + +/* Called from c-decl.c before all calls to rest_of_decl_compilation. */ + +void +maybe_objc_check_decl (decl) + tree decl; +{ + if (doing_objc_thang) + objc_check_decl (decl); +} + +void +objc_check_decl (decl) + tree decl; +{ + tree type = TREE_TYPE (decl); + static int alreadyWarned = 0; + + if (TREE_CODE (type) == RECORD_TYPE && TREE_STATIC_TEMPLATE (type)) + { + if (!alreadyWarned) + { + error ("GNU compiler does not support statically allocated objects"); + alreadyWarned = 1; + } + error_with_decl (decl, "`%s' cannot be statically allocated"); + } +} + +/* implement static typing. at this point, we know we have an interface... */ + +tree +get_static_reference (interface) + tree interface; +{ + return xref_tag (RECORD_TYPE, CLASS_NAME (interface)); +} + +/* + * 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 sc_spec, type_spec, decl_specs, expr_decl, parms, record; + + /* defined in `objc.h' */ + objc_object_id = get_identifier (TAG_OBJECT); + + objc_object_reference = xref_tag (RECORD_TYPE, objc_object_id); + + id_type = groktypename (build_tree_list ( + build_tree_list (NULLT, objc_object_reference), + build1 (INDIRECT_REF, NULLT, NULLT))); + + objc_class_id = get_identifier (TAG_CLASS); + + class_type = groktypename (build_tree_list + (build_tree_list + (NULLT, xref_tag (RECORD_TYPE, objc_class_id)), + build1 (INDIRECT_REF, NULLT, NULLT))); + +/* Declare SEL type before prototypes for objc_msgSend(), or else those + struct tags are considered local to the prototype and won't match the one + in . */ + +#ifdef OBJC_INT_SELECTORS + /* `unsigned int' */ + _selector_type = unsigned_type_node; +#else + /* `struct objc_selector *' */ + _selector_type = groktypename (build_tree_list ( + build_tree_list (NULLT, + xref_tag (RECORD_TYPE, + get_identifier (TAG_SELECTOR))), + build1 (INDIRECT_REF, NULLT, NULLT))); +#endif /* not OBJC_INT_SELECTORS */ + + /* forward declare type...or else the prototype for `super' will bitch */ + groktypename (build_tree_list (build_tree_list (NULLT, + xref_tag (RECORD_TYPE, get_identifier (TAG_SUPER))), + build1 (INDIRECT_REF, NULLT, NULLT))); + + _msg_id = get_identifier ("objc_msgSend"); + _msgSuper_id = get_identifier ("objc_msgSendSuper"); + objc_getClass_id = get_identifier ("objc_getClass"); + objc_getMetaClass_id = get_identifier ("objc_getMetaClass"); + + /* struct objc_object *objc_msgSend (id, SEL, ...); */ + pushlevel (0); + decl_specs = build_tree_list (NULLT, objc_object_reference); + push_parm_decl (build_tree_list (decl_specs, + build1 (INDIRECT_REF, NULLT, NULLT))); + +#ifdef OBJC_INT_SELECTORS + decl_specs = build_tree_list (NULLT, ridpointers[(int) RID_UNSIGNED]); + decl_specs = tree_cons (NULLT, ridpointers[(int) RID_INT], decl_specs); + expr_decl = NULLT; +#else + decl_specs = build_tree_list (NULLT, + xref_tag (RECORD_TYPE, + get_identifier (TAG_SELECTOR))); + expr_decl = build1 (INDIRECT_REF, NULLT, NULLT); +#endif /* not OBJC_INT_SELECTORS */ + + push_parm_decl (build_tree_list (decl_specs, expr_decl)); + parms = get_parm_info (0); + poplevel (0, 0, 0); + + decl_specs = build_tree_list (NULLT, objc_object_reference); + expr_decl = build_nt (CALL_EXPR, _msg_id, parms, NULLT); + expr_decl = build1 (INDIRECT_REF, NULLT, expr_decl); + + _msg_decl = define_decl (expr_decl, decl_specs); + + /* struct objc_object *objc_msgSendSuper (struct objc_super *, SEL, ...); */ + pushlevel (0); + decl_specs = build_tree_list (NULLT, xref_tag (RECORD_TYPE, + get_identifier (TAG_SUPER))); + push_parm_decl (build_tree_list (decl_specs, + build1 (INDIRECT_REF, NULLT, NULLT))); + +#ifdef OBJC_INT_SELECTORS + decl_specs = build_tree_list (NULLT, ridpointers[(int) RID_UNSIGNED]); + decl_specs = tree_cons (NULLT, ridpointers[(int) RID_INT], decl_specs); + expr_decl = NULLT; +#else /* not OBJC_INT_SELECTORS */ + decl_specs = build_tree_list (NULLT, + xref_tag (RECORD_TYPE, + get_identifier (TAG_SELECTOR))); + expr_decl = build1 (INDIRECT_REF, NULLT, NULLT); +#endif /* not OBJC_INT_SELECTORS */ + + push_parm_decl (build_tree_list (decl_specs, expr_decl)); + parms = get_parm_info (0); + poplevel (0, 0, 0); + + decl_specs = build_tree_list (NULLT, objc_object_reference); + expr_decl = build_nt (CALL_EXPR, _msgSuper_id, parms, NULLT); + expr_decl = build1 (INDIRECT_REF, NULLT, expr_decl); + + _msgSuper_decl = define_decl (expr_decl, decl_specs); + + /* id objc_getClass (); */ + parms = build_tree_list (NULLT, NULLT); + expr_decl = build_nt (CALL_EXPR, objc_getClass_id, parms, NULLT); + expr_decl = build1 (INDIRECT_REF, NULLT, expr_decl); + + objc_getClass_decl = define_decl (expr_decl, decl_specs); + + /* id objc_getMetaClass (); */ + parms = build_tree_list (NULLT, NULLT); + expr_decl = build_nt (CALL_EXPR, objc_getMetaClass_id, parms, NULLT); + expr_decl = build1 (INDIRECT_REF, NULLT, expr_decl); + + objc_getMetaClass_decl = define_decl (expr_decl, decl_specs); + +#ifdef OBJC_NONUNIQUE_SELECTORS + _OBJC_SELECTOR_REFERENCES_id = get_identifier ("_OBJC_SELECTOR_REFERENCES"); + + /* extern SEL _OBJC_SELECTOR_REFERENCES[]; */ + sc_spec = tree_cons (NULLT, ridpointers[(int) RID_EXTERN], NULLT); + +#ifdef OBJC_INT_SELECTORS + decl_specs = tree_cons (NULLT, ridpointers[(int) RID_UNSIGNED], sc_spec); + decl_specs = tree_cons (NULLT, ridpointers[(int) RID_INT], decl_specs); + expr_decl = _OBJC_SELECTOR_REFERENCES_id; +#else /* not OBJC_INT_SELECTORS */ + decl_specs = build_tree_list (NULLT, + xref_tag (RECORD_TYPE, + get_identifier (TAG_SELECTOR))); + expr_decl = build1 (INDIRECT_REF, NULLT, _OBJC_SELECTOR_REFERENCES_id); +#endif /* not OBJC_INT_SELECTORS */ + + expr_decl = build_nt (ARRAY_REF, expr_decl, NULLT); + _OBJC_SELECTOR_REFERENCES_decl = define_decl (expr_decl, decl_specs); +#endif +} + +/* + * custom "build_string ()" which sets TREE_TYPE! + */ +static tree +my_build_string (len, str) + int len; + char *str; +{ + int wide_flag = 0; + tree aString = build_string (len, str); + /* + * some code from "combine_strings ()", which is local to c-parse.y. + */ + if (TREE_TYPE (aString) == int_array_type_node) + wide_flag = 1; + + TREE_TYPE (aString) = + build_array_type (wide_flag ? integer_type_node : char_type_node, + build_index_type (build_int_2 (len - 1, 0))); + + TREE_CONSTANT (aString) = 1; /* puts string in the ".text" segment */ + TREE_STATIC (aString) = 1; + + return aString; +} + +/* + * struct objc_symtab { + * long sel_ref_cnt; + * char *refs; + * long cls_def_cnt; + * long cat_def_cnt; + * void *defs[cls_def_cnt + cat_def_cnt]; + * }; + */ +static void +build_objc_symtab_template () +{ + tree decl_specs, field_decl, field_decl_chain; + + objc_symtab_template = start_struct (RECORD_TYPE, get_identifier (_TAG_SYMTAB)); + + /* long sel_ref_cnt; */ + + decl_specs = build_tree_list (NULLT, ridpointers[(int) RID_LONG]); + field_decl = get_identifier ("sel_ref_cnt"); + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + field_decl_chain = field_decl; + +#ifdef OBJC_INT_SELECTORS + /* unsigned int *sel_ref; */ + decl_specs = build_tree_list (NULLT, ridpointers[(int) RID_UNSIGNED]); + decl_specs = tree_cons (NULLT, ridpointers[(int) RID_INT], decl_specs); + field_decl = build1 (INDIRECT_REF, NULLT, get_identifier ("refs")); +#else /* not OBJC_INT_SELECTORS */ + /* struct objc_selector **sel_ref; */ + decl_specs = build_tree_list (NULLT, + xref_tag (RECORD_TYPE, + get_identifier (TAG_SELECTOR))); + field_decl = build1 (INDIRECT_REF, NULLT, get_identifier ("refs")); + field_decl = build1 (INDIRECT_REF, NULLT, field_decl); +#endif /* not OBJC_INT_SELECTORS */ + + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + chainon (field_decl_chain, field_decl); + + /* short cls_def_cnt; */ + + decl_specs = build_tree_list (NULLT, ridpointers[(int) RID_SHORT]); + field_decl = get_identifier ("cls_def_cnt"); + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + chainon (field_decl_chain, field_decl); + + /* short cat_def_cnt; */ + + decl_specs = build_tree_list (NULLT, ridpointers[(int) RID_SHORT]); + field_decl = get_identifier ("cat_def_cnt"); + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + chainon (field_decl_chain, field_decl); + + /* void *defs[cls_def_cnt + cat_def_cnt]; */ + + decl_specs = build_tree_list (NULLT, ridpointers[(int) RID_VOID]); + field_decl = build_nt (ARRAY_REF, get_identifier ("defs"), + build_int_2 (imp_count + cat_count, 0)); + field_decl = build1 (INDIRECT_REF, NULLT, field_decl); + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + chainon (field_decl_chain, field_decl); + + finish_struct (objc_symtab_template, field_decl_chain); +} + +static tree +init_def_list () +{ + tree expr, initlist = NULLT; + struct imp_entry *impent; + + if (imp_count) + for (impent = imp_list; impent; impent = impent->next) + { + if (TREE_CODE (impent->imp_context) == IMPLEMENTATION_TYPE) + { + expr = build_unary_op (ADDR_EXPR, impent->class_decl, 0); + initlist = tree_cons (NULLT, expr, initlist); + } + } + + if (cat_count) + for (impent = imp_list; impent; impent = impent->next) + { + if (TREE_CODE (impent->imp_context) == CATEGORY_TYPE) + { + expr = build_unary_op (ADDR_EXPR, impent->class_decl, 0); + initlist = tree_cons (NULLT, expr, initlist); + } + } + return build_nt (CONSTRUCTOR, NULLT, nreverse (initlist)); +} + +/* + * struct objc_symtab { + * long sel_ref_cnt; + * char *refs; + * long cls_def_cnt; + * long cat_def_cnt; + * void *defs[cls_def_cnt + cat_def_cnt]; + * }; + */ +static tree +init_objc_symtab () +{ + tree initlist; + + /* sel_ref_cnt = { ..., 5, ... } */ + + if (sel_ref_chain) + initlist = build_tree_list (NULLT, build_int_2 (max_selector_index, 0)); + else + initlist = build_tree_list (NULLT, build_int_2 (0, 0)); + + /* refs = { ..., _OBJC_SELECTOR_REFERENCES, ... } */ + +#ifndef OBJC_NONUNIQUE_SELECTORS + initlist = tree_cons (NULLT, build_int_2 (0, 0), initlist); +#else + if (sel_ref_chain) + initlist = tree_cons (NULLT, _OBJC_SELECTOR_REFERENCES_decl, initlist); + else + initlist = tree_cons (NULLT, build_int_2 (0, 0), initlist); +#endif + + /* cls_def_cnt = { ..., 5, ... } */ + + initlist = tree_cons (NULLT, build_int_2 (imp_count, 0), initlist); + + /* cat_def_cnt = { ..., 5, ... } */ + + initlist = tree_cons (NULLT, build_int_2 (cat_count, 0), initlist); + + /* cls_def = { ..., { &Foo, &Bar, ...}, ... } */ + + if (imp_count || cat_count) + initlist = tree_cons (NULLT, init_def_list (), initlist); + + return build_nt (CONSTRUCTOR, NULLT, nreverse (initlist)); +} + +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_TYPE) + { + tree sc_spec, decl_specs, decl; + + sc_spec = build_tree_list (NULLT, ridpointers[(int) RID_EXTERN]); + decl_specs = tree_cons (NULLT, objc_category_template, sc_spec); + + implementation_context = impent->imp_context; + impent->class_decl = define_decl ( + synth_id_with_class_suffix ("_OBJC_CATEGORY"), + decl_specs); + } + } + implementation_context = sav; +} + +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 (NULLT, ridpointers[(int) RID_STATIC]); + + _OBJC_SYMBOLS_decl = start_decl (get_identifier ("_OBJC_SYMBOLS"), + tree_cons (NULLT, objc_symtab_template, sc_spec), 1); + + finish_decl (_OBJC_SYMBOLS_decl, init_objc_symtab (), NULLT); +} + +/* + * tree_node------->tree_node----->... + * | | + * | (value) | (value) + * | | + * expr expr + */ +static tree +init_module_descriptor () +{ + tree initlist, expr; + + /* version = { 1, ... } */ + + expr = build_int_2 (OBJC_VERSION, 0); + initlist = build_tree_list (NULLT, expr); + + /* size = { ..., sizeof (struct objc_module), ... } */ + + expr = build_int_2 (TREE_INT_CST_LOW (TYPE_SIZE (objc_module_template)) / + BITS_PER_UNIT, 0); + initlist = tree_cons (NULLT, expr, initlist); + + /* name = { ..., "foo.m", ... } */ + + expr = build_msg_pool_reference ( + add_objc_string (get_identifier (input_filename))); + initlist = tree_cons (NULLT, expr, initlist); + + /* symtab = { ..., _OBJC_SYMBOLS, ... } */ + + if (_OBJC_SYMBOLS_decl) + expr = build_unary_op (ADDR_EXPR, _OBJC_SYMBOLS_decl, 0); + else + expr = build_int_2 (0, 0); + initlist = tree_cons (NULLT, expr, initlist); + + return build_nt (CONSTRUCTOR, NULLT, 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). */ + +static char * +build_module_descriptor () +{ + tree decl_specs, field_decl, field_decl_chain; + + objc_module_template = start_struct (RECORD_TYPE, get_identifier (_TAG_MODULE)); + + /* long version; */ + + decl_specs = build_tree_list (NULLT, ridpointers[(int) RID_LONG]); + field_decl = get_identifier ("version"); + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + field_decl_chain = field_decl; + + /* long size; */ + + decl_specs = build_tree_list (NULLT, ridpointers[(int) RID_LONG]); + field_decl = get_identifier ("size"); + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + chainon (field_decl_chain, field_decl); + + /* char *name; */ + + decl_specs = build_tree_list (NULLT, ridpointers[(int) RID_CHAR]); + field_decl = build1 (INDIRECT_REF, NULLT, get_identifier ("name")); + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + chainon (field_decl_chain, field_decl); + + /* struct objc_symtab *symtab; */ + + decl_specs = get_identifier (_TAG_SYMTAB); + decl_specs = build_tree_list (NULLT, xref_tag (RECORD_TYPE, decl_specs)); + field_decl = build1 (INDIRECT_REF, NULLT, get_identifier ("symtab")); + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + chainon (field_decl_chain, field_decl); + + finish_struct (objc_module_template, field_decl_chain); + + /* create an instance of "objc_module" */ + + decl_specs = tree_cons (NULLT, objc_module_template, + build_tree_list (NULLT, ridpointers[(int) RID_STATIC])); + + _OBJC_MODULES_decl = start_decl (get_identifier ("_OBJC_MODULES"), + decl_specs, 1); + + finish_decl (_OBJC_MODULES_decl, init_module_descriptor (), NULLT); + + /* Mark the decl as used to avoid "defined but not used" warning. */ + TREE_USED (_OBJC_MODULES_decl) = 1; + + /* Generate a constructor call for the module descriptor. + This code was generated by reading the grammar rules + of c-parse.y; Therefore, it may not be the most efficient + way of generating the requisite code. */ +#ifndef NEXT_OBJC_RUNTIME + { + tree parms, function_decl, decelerator, void_list_node; + tree function_type; + char *buf; + char *global_object_name = 0; + tree t; + + /* Use a global object (which is already required to be unique over + the program) rather than the file name (which imposes extra + constraints). -- Raeburn@MIT.EDU, 10 Jan 1990. */ + + /* Find the name of some global object defined in this file. */ + for (t = getdecls (); t; t = TREE_CHAIN (t)) + if (TREE_PUBLIC (t) && !TREE_EXTERNAL (t) && DECL_INITIAL (t) != 0) + { + global_object_name = IDENTIFIER_POINTER (DECL_NAME (t)); + break; + } + + /* If none, use the name of the file. */ + if (!global_object_name) + { + char *p, *q; + global_object_name + = (char *) alloca (strlen (main_input_filename) + 1); + + p = main_input_filename; + q = global_object_name; + + /* Replace any weird characters in the file name. */ + for (; *p; p++) + if (! ((*p >= '0' && *p <= '9') + || (*p >= 'A' && *p <= 'Z') + || (*p >= 'a' && *p <= 'z'))) + *q++ = '_'; + else + *q++ = *p; + *q = 0; + } + + /* Make the constructor name from the name we have found. */ + buf = (char *) xmalloc (sizeof (CONSTRUCTOR_NAME_FORMAT) + + strlen (global_object_name)); + sprintf (buf, CONSTRUCTOR_NAME_FORMAT, global_object_name); + + /* 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 ("__objc_execClass"), + function_type); + TREE_EXTERNAL (function_decl) = 1; + TREE_PUBLIC (function_decl) = 1; + pushdecl (function_decl); + rest_of_decl_compilation (function_decl, 0, 0, 0); + + parms + = build_tree_list (NULLT, + build_unary_op (ADDR_EXPR, _OBJC_MODULES_decl, 0)); + decelerator = build_function_call (function_decl, parms); + + /* void __objc_file_init () {objc_execClass(&L_OBJC_MODULES);} */ + + start_function (void_list_node, + build_parse_node (CALL_EXPR, get_identifier (buf), + /* This has the format of the output + of get_parm_info. */ + tree_cons (NULL_TREE, NULL_TREE, + void_list_node), + NULL_TREE), + 0, 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); + + finish_function (0); + + /* Return the name of the constructor function. */ + return buf; + } +#else /* NEXT_OBJC_RUNTIME */ + return "__objcInit"; +#endif /* NEXT_OBJC_RUNTIME */ +} + +/* extern const char _OBJC_STRINGS[]; */ + +static void +generate_forward_declaration_to_string_table () +{ + tree sc_spec, decl_specs, expr_decl; + + sc_spec = tree_cons (NULLT, ridpointers[(int) RID_EXTERN], NULLT); + decl_specs = tree_cons (NULLT, ridpointers[(int) RID_CHAR], sc_spec); + + expr_decl = build_nt (ARRAY_REF, get_identifier ("_OBJC_STRINGS"), NULLT); + + _OBJC_STRINGS_decl = define_decl (expr_decl, decl_specs); +} + +/* static char _OBJC_STRINGS[] = "..."; */ + +static void +build_message_selector_pool () +{ + tree sc_spec, decl_specs, expr_decl; + tree chain, string_expr; + int goolengthtmp = 0, msg_pool_size = 0; + char *string_goo; + + sc_spec = tree_cons (NULLT, ridpointers[(int) RID_STATIC], NULLT); + decl_specs = tree_cons (NULLT, ridpointers[(int) RID_CHAR], sc_spec); + + expr_decl = build_nt (ARRAY_REF, get_identifier ("_OBJC_STRINGS"), NULLT); + + _OBJC_STRINGS_decl = start_decl (expr_decl, decl_specs, 1); + + for (chain = sel_refdef_chain; chain; chain = TREE_CHAIN (chain)) + msg_pool_size += IDENTIFIER_LENGTH (TREE_VALUE (chain)) + 1; + + msg_pool_size++; + + string_goo = (char *)xmalloc (msg_pool_size); + bzero (string_goo, msg_pool_size); + + for (chain = sel_refdef_chain; chain; chain = TREE_CHAIN (chain)) + { + strcpy (string_goo + goolengthtmp, IDENTIFIER_POINTER (TREE_VALUE (chain))); + goolengthtmp += IDENTIFIER_LENGTH (TREE_VALUE (chain)) + 1; + } + + string_expr = my_build_string (msg_pool_size, string_goo); + + finish_decl (_OBJC_STRINGS_decl, string_expr, NULLT); +} + +/* + * synthesize the following expr: (char *)&_OBJC_STRINGS[] + * + * 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 (_OBJC_STRINGS_decl, expr); + expr = build_unary_op (ADDR_EXPR, expr, 0); + + cast = build_tree_list (build_tree_list (NULLT, ridpointers[(int) RID_CHAR]), + build1 (INDIRECT_REF, NULLT, NULLT)); + TREE_TYPE (expr) = groktypename (cast); + return expr; +} + +#ifndef OBJC_NONUNIQUE_SELECTORS +static tree +build_selector_reference (idx) + int idx; +{ + tree ref, decl, name, ident; + char buf[256]; + struct obstack *save_current_obstack = current_obstack; + struct obstack *save_rtl_obstack = rtl_obstack; + + sprintf (buf, "_OBJC_SELECTOR_REFERENCES_%d", idx); + + /* new stuff */ + rtl_obstack = current_obstack = &permanent_obstack; + ident = get_identifier (buf); + + if (IDENTIFIER_GLOBAL_VALUE (ident)) + decl = IDENTIFIER_GLOBAL_VALUE (ident); /* set by pushdecl() */ + else + { + decl = build_decl (VAR_DECL, ident, _selector_type); + TREE_EXTERNAL (decl) = 1; + TREE_PUBLIC (decl) = 1; + TREE_USED (decl) = 1; + + make_decl_rtl (decl, 0, 1); /* usually called from `rest_of_decl_compilation' */ + pushdecl_top_level (decl); /* our `extended/custom' pushdecl in c-decl.c */ + } + current_obstack = save_current_obstack; + rtl_obstack = save_rtl_obstack; + + return decl; +} +#endif + +static tree +init_selector (offset) + int offset; +{ + tree expr = build_msg_pool_reference (offset); + TREE_TYPE (expr) = _selector_type; /* cast */ + return expr; +} + +static void +build_selector_translation_table () +{ + tree sc_spec, decl_specs, expr_decl; + tree chain, initlist = NULLT; + int offset = 0; +#ifndef OBJC_NONUNIQUE_SELECTORS + tree decl, var_decl; + int idx = 0; + char buf[256]; +#else/ + + sc_spec = tree_cons (NULLT, ridpointers[(int) RID_STATIC], NULLT); + +#ifdef OBJC_INT_SELECTORS + /* static unsigned int _OBJC_SELECTOR_REFERENCES[] = { 1, 2, ... }; */ + decl_specs = tree_cons (NULLT, ridpointers[(int) RID_UNSIGNED], sc_spec); + decl_specs = tree_cons (NULLT, ridpointers[(int) RID_INT], decl_specs); + expr_decl = _OBJC_SELECTOR_REFERENCES_id; +#else /* not OBJC_INT_SELECTORS */ + /* static struct objc_selector *_OBJC_SELECTOR_REFERENCES[] = { 1, 2, .}; */ + decl_specs = build_tree_list (NULLT, + xref_tag (RECORD_TYPE, + get_identifier (TAG_SELECTOR))); + expr_decl = build1 (INDIRECT_REF, NULLT, _OBJC_SELECTOR_REFERENCES_id); +#endif /* not OBJC_INT_SELECTORS */ + + expr_decl = build_nt (ARRAY_REF, expr_decl, NULLT); + _OBJC_SELECTOR_REFERENCES_decl = start_decl (expr_decl, decl_specs, 1); +#endif + + for (chain = sel_ref_chain; chain; chain = TREE_CHAIN (chain)) + { + tree expr; + +#ifndef OBJC_NONUNIQUE_SELECTORS + sprintf (buf, "_OBJC_SELECTOR_REFERENCES_%d", idx); + sc_spec = build_tree_list (NULLT, ridpointers[RID_STATIC]); + +#ifdef OBJC_INT_SELECTORS + /* static unsigned int _OBJC_SELECTOR_REFERENCES_n = ...; */ + decl_specs = tree_cons (NULLT, ridpointers[(int) RID_UNSIGNED], sc_spec); + decl_specs = tree_cons (NULLT, ridpointers[(int) RID_INT], decl_specs); + var_decl = get_identifier (buf); +#else /* not OBJC_INT_SELECTORS */ + /* static struct objc_selector *_OBJC_SELECTOR_REFERENCES_n = ...; */ + decl_specs = tree_cons (NULLT, + xref_tag (RECORD_TYPE, + get_identifier (TAG_SELECTOR)), + sc_spec); + var_decl = build1 (INDIRECT_REF, NULLT, get_identifier (buf)); +#endif /* not OBJC_INT_SELECTORS */ + + /* 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); +#endif + + expr = init_selector (offset); + + /* add one for the '\0' character */ + offset += IDENTIFIER_LENGTH (TREE_VALUE (chain)) + 1; + +#ifndef OBJC_NONUNIQUE_SELECTORS + finish_decl (decl, expr, NULLT); + idx++; +#else + initlist = tree_cons (NULLT, expr, initlist); +#endif + } + +#ifdef OBJC_NONUNIQUE_SELECTORS + initlist = build_nt (CONSTRUCTOR, NULLT, nreverse (initlist)); + finish_decl (_OBJC_SELECTOR_REFERENCES_decl, initlist, NULLT); +#endif +} + +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 (NULLT, ident, NULLT); + } + else + cls_ref_chain = perm_tree_cons (NULLT, ident, NULLT); +} + +/* + * sel_ref_chain is a list whose "value" fields will be instances of + * identifier_node that represent the selector. + */ +static int +add_selector_reference (ident) + tree ident; +{ + tree chain; + int index = 0; + + /* this adds it to sel_refdef_chain, the global pool of selectors */ + add_objc_string (ident); + + if (chain = sel_ref_chain) + { + tree tail; + do + { + if (ident == TREE_VALUE (chain)) + return index; + + index++; + tail = chain; + chain = TREE_CHAIN (chain); + } + while (chain); + + /* append to the end of the list */ + TREE_CHAIN (tail) = perm_tree_cons (NULLT, ident, NULLT); + } + else + sel_ref_chain = perm_tree_cons (NULLT, ident, NULLT); + + max_selector_index++; + return index; +} + +/* + * 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. + */ +static int +add_objc_string (ident) + tree ident; +{ + tree chain; + int offset = 0; + + if (chain = sel_refdef_chain) + { + tree tail; + do + { + if (ident == TREE_VALUE (chain)) + return offset; + + /* add one for the '\0' character */ + offset += IDENTIFIER_LENGTH (TREE_VALUE (chain)) + 1; + tail = chain; + chain = TREE_CHAIN (chain); + } + while (chain); + + /* append to the end of the list */ + TREE_CHAIN (tail) = perm_tree_cons (NULLT, ident, NULLT); + } + else + sel_refdef_chain = perm_tree_cons (NULLT, ident, NULLT); + + return offset; +} + +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 NULLT; +} + +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_FRAME_SIZE (tail), 0); + + newlist = chainon (newlist, tail); + list = TREE_CHAIN (list); + } + *head = newlist; + return tail; +} + +/* used by: + * build_private_template (), get_class_ivars (), and get_static_reference (). + */ +static tree +build_ivar_chain (interface) + tree interface; +{ + tree my_name, super_name, ivar_chain; + + my_name = CLASS_NAME (interface); + super_name = CLASS_SUPER_NAME (interface); + + /* "leaf" ivars never get copied...there is no reason to. */ + 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 (34); + } + 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 { + * struct objc_class *isa; + * ... + * }; + */ +static tree +build_private_template (class) + tree class; +{ + tree ivar_context; + + if (CLASS_STATIC_TEMPLATE (class)) + { + _PRIVATE_record = CLASS_STATIC_TEMPLATE (class); + ivar_context = TYPE_FIELDS (CLASS_STATIC_TEMPLATE (class)); + } + else + { + _PRIVATE_record = start_struct (RECORD_TYPE, CLASS_NAME (class)); + + ivar_context = build_ivar_chain (class); + + finish_struct (_PRIVATE_record, ivar_context); + + CLASS_STATIC_TEMPLATE (class) = _PRIVATE_record; + + /* mark this record as class template - for class type checking */ + TREE_STATIC_TEMPLATE (_PRIVATE_record) = 1; + } + instance_type = groktypename ( + build_tree_list (build_tree_list (NULLT, _PRIVATE_record), + build1 (INDIRECT_REF, NULLT, NULLT))); + return ivar_context; +} + +/* + * struct objc_category { + * char *category_name; + * char *class_name; + * struct objc_method_list *instance_methods; + * struct objc_method_list *class_methods; + * }; + */ +static void +build_category_template () +{ + tree decl_specs, field_decl, field_decl_chain; + + objc_category_template = start_struct (RECORD_TYPE, + get_identifier (_TAG_CATEGORY)); + /* char *category_name; */ + + decl_specs = build_tree_list (NULLT, ridpointers[(int) RID_CHAR]); + field_decl = build1 (INDIRECT_REF, NULLT, get_identifier ("category_name")); + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + field_decl_chain = field_decl; + + /* char *class_name; */ + + decl_specs = build_tree_list (NULLT, ridpointers[(int) RID_CHAR]); + field_decl = build1 (INDIRECT_REF, NULLT, get_identifier ("class_name")); + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + chainon (field_decl_chain, field_decl); + + /* struct objc_method_list *instance_methods; */ + + decl_specs = build_tree_list (NULLT, xref_tag (RECORD_TYPE, + get_identifier (_TAG_METHOD_LIST))); + field_decl = build1 (INDIRECT_REF, NULLT, get_identifier ("instance_methods")); + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + chainon (field_decl_chain, field_decl); + + /* struct objc_method_list *class_methods; */ + + decl_specs = build_tree_list (NULLT, xref_tag (RECORD_TYPE, + get_identifier (_TAG_METHOD_LIST))); + field_decl = build1 (INDIRECT_REF, NULLT, get_identifier ("class_methods")); + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + chainon (field_decl_chain, field_decl); + + finish_struct (objc_category_template, field_decl_chain); +} + +/* + * 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; + * struct objc_cache *cache; + * }; + */ +static void +build_class_template () +{ + tree decl_specs, field_decl, field_decl_chain; + + objc_class_template = start_struct (RECORD_TYPE, get_identifier (_TAG_CLASS)); + + /* struct objc_class *isa; */ + + decl_specs = build_tree_list (NULLT, objc_class_template); + field_decl = build1 (INDIRECT_REF, NULLT, get_identifier ("isa")); + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + field_decl_chain = field_decl; + + /* struct objc_class *super_class; */ + + decl_specs = build_tree_list (NULLT, objc_class_template); + field_decl = build1 (INDIRECT_REF, NULLT, get_identifier ("super_class")); + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + chainon (field_decl_chain, field_decl); + + /* char *name; */ + + decl_specs = build_tree_list (NULLT, ridpointers[(int) RID_CHAR]); + field_decl = build1 (INDIRECT_REF, NULLT, get_identifier ("name")); + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + chainon (field_decl_chain, field_decl); + + /* long version; */ + + decl_specs = build_tree_list (NULLT, ridpointers[(int) RID_LONG]); + field_decl = get_identifier ("version"); + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + chainon (field_decl_chain, field_decl); + + /* long info; */ + + decl_specs = build_tree_list (NULLT, ridpointers[(int) RID_LONG]); + field_decl = get_identifier ("info"); + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + chainon (field_decl_chain, field_decl); + + /* long instance_size; */ + + decl_specs = build_tree_list (NULLT, ridpointers[(int) RID_LONG]); + field_decl = get_identifier ("instance_size"); + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + chainon (field_decl_chain, field_decl); + + /* struct objc_ivar_list *ivars; */ + + decl_specs = build_tree_list (NULLT, xref_tag (RECORD_TYPE, + get_identifier (_TAG_IVAR_LIST))); + field_decl = build1 (INDIRECT_REF, NULLT, get_identifier ("ivars")); + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + chainon (field_decl_chain, field_decl); + + /* struct objc_method_list *methods; */ + + decl_specs = build_tree_list (NULLT, xref_tag (RECORD_TYPE, + get_identifier (_TAG_METHOD_LIST))); + field_decl = build1 (INDIRECT_REF, NULLT, get_identifier ("methods")); + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + chainon (field_decl_chain, field_decl); + + /* struct objc_cache *cache; */ + + decl_specs = build_tree_list (NULLT, xref_tag (RECORD_TYPE, + get_identifier ("objc_cache"))); + field_decl = build1 (INDIRECT_REF, NULLT, get_identifier ("cache")); + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + chainon (field_decl_chain, field_decl); + + finish_struct (objc_class_template, field_decl_chain); +} + +/* + * generate appropriate forward declarations for an implementation + */ +static void +synth_forward_declarations () +{ + tree sc_spec, decl_specs, factory_id, anId; + + /* extern struct objc_class _OBJC_CLASS_; */ + + anId = synth_id_with_class_suffix ("_OBJC_CLASS"); + + sc_spec = build_tree_list (NULLT, ridpointers[(int) RID_EXTERN]); + decl_specs = tree_cons (NULLT, objc_class_template, sc_spec); + _OBJC_CLASS_decl = define_decl (anId, decl_specs); + + /* extern struct objc_class _OBJC_METACLASS_; */ + + anId = synth_id_with_class_suffix ("_OBJC_METACLASS"); + + _OBJC_METACLASS_decl = define_decl (anId, decl_specs); + + /* pre-build the following entities - for speed/convenience. */ + + anId = get_identifier ("super_class"); + _clsSuper_ref = build_component_ref (_OBJC_CLASS_decl, anId); + __clsSuper_ref = build_component_ref (_OBJC_METACLASS_decl, anId); +} + +static void +error_with_ivar (message, decl, rawdecl) + char *message; + tree decl; + tree rawdecl; +{ + count_error (0); + fprintf (stderr, "%s:%d: ", + DECL_SOURCE_FILE (decl), DECL_SOURCE_LINE (decl)); + bzero (errbuf, BUFSIZE); + fprintf (stderr, "%s `%s'\n", message, gen_declaration (rawdecl, errbuf)); +} + +#define USERTYPE(t) (TREE_CODE (t) == RECORD_TYPE || \ + TREE_CODE (t) == UNION_TYPE || \ + TREE_CODE (t) == ENUMERAL_TYPE) + +static void +check_ivars (inter, imp) + tree inter; + tree imp; +{ + tree intdecls = CLASS_IVARS (inter); + tree impdecls = CLASS_IVARS (imp); + tree rawintdecls = CLASS_RAW_IVARS (inter); + tree rawimpdecls = CLASS_RAW_IVARS (imp); + + while (1) + { + tree t1, t2; + + if (intdecls == 0 && impdecls == 0) + break; + if (intdecls == 0 || impdecls == 0) + { + error ("inconsistent instance variable specification"); + break; + } + t1 = TREE_TYPE (intdecls); t2 = TREE_TYPE (impdecls); + + if (!comptypes (t1, t2)) + { + if (DECL_NAME (intdecls) == DECL_NAME (impdecls)) + { + error_with_ivar ("conflicting instance variable type", + impdecls, rawimpdecls); + error_with_ivar ("previous declaration of", + intdecls, rawintdecls); + } + else /* both the type and the name don't match */ + { + error ("inconsistent instance variable specification"); + break; + } + } + else if (DECL_NAME (intdecls) != DECL_NAME (impdecls)) + { + error_with_ivar ("conflicting instance variable name", + impdecls, rawimpdecls); + error_with_ivar ("previous declaration of", + intdecls, rawintdecls); + } + intdecls = TREE_CHAIN (intdecls); + impdecls = TREE_CHAIN (impdecls); + rawintdecls = TREE_CHAIN (rawintdecls); + rawimpdecls = TREE_CHAIN (rawimpdecls); + } +} + +/* + * struct objc_super { + * id self; + * struct objc_class *class; + * }; + */ +static tree +build_super_template () +{ + tree record, decl_specs, field_decl, field_decl_chain; + + record = start_struct (RECORD_TYPE, get_identifier (_TAG_SUPER)); + + /* struct objc_object *self; */ + + decl_specs = build_tree_list (NULLT, objc_object_reference); + field_decl = get_identifier ("self"); + field_decl = build1 (INDIRECT_REF, NULLT, field_decl); + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + field_decl_chain = field_decl; + + /* struct objc_class *class; */ + + decl_specs = get_identifier (_TAG_CLASS); + decl_specs = build_tree_list (NULLT, xref_tag (RECORD_TYPE, decl_specs)); + field_decl = build1 (INDIRECT_REF, NULLT, get_identifier ("class")); + + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + chainon (field_decl_chain, field_decl); + + finish_struct (record, field_decl_chain); + + /* `struct objc_super *' */ + super_type = groktypename (build_tree_list (build_tree_list (NULLT, record), + build1 (INDIRECT_REF, NULLT, NULLT))); + return record; +} + +/* + * struct objc_ivar { + * char *ivar_name; + * char *ivar_type; + * int ivar_offset; + * }; + */ +static tree +build_ivar_template () +{ + tree objc_ivar_id, objc_ivar_record; + tree decl_specs, field_decl, field_decl_chain; + + objc_ivar_id = get_identifier (_TAG_IVAR); + objc_ivar_record = start_struct (RECORD_TYPE, objc_ivar_id); + + /* char *ivar_name; */ + + decl_specs = build_tree_list (NULLT, ridpointers[(int) RID_CHAR]); + field_decl = build1 (INDIRECT_REF, NULLT, get_identifier ("ivar_name")); + + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + field_decl_chain = field_decl; + + /* char *ivar_type; */ + + decl_specs = build_tree_list (NULLT, ridpointers[(int) RID_CHAR]); + field_decl = build1 (INDIRECT_REF, NULLT, get_identifier ("ivar_type")); + + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + chainon (field_decl_chain, field_decl); + + /* int ivar_offset; */ + + decl_specs = build_tree_list (NULLT, ridpointers[(int) RID_INT]); + field_decl = get_identifier ("ivar_offset"); + + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + chainon (field_decl_chain, field_decl); + + finish_struct (objc_ivar_record, field_decl_chain); + + return objc_ivar_record; +} + +/* + * struct { + * int ivar_count; + * struct objc_ivar ivar_list[ivar_count]; + * }; + */ +static tree +build_ivar_list_template (list_type, size) + tree list_type; + int size; +{ + tree objc_ivar_list_id, objc_ivar_list_record; + tree decl_specs, field_decl, field_decl_chain; + + objc_ivar_list_record = start_struct (RECORD_TYPE, NULLT); + + /* int ivar_count; */ + + decl_specs = build_tree_list (NULLT, ridpointers[(int) RID_INT]); + field_decl = get_identifier ("ivar_count"); + + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + field_decl_chain = field_decl; + + /* struct objc_ivar ivar_list[]; */ + + decl_specs = build_tree_list (NULLT, list_type); + field_decl = build_nt (ARRAY_REF, get_identifier ("ivar_list"), + build_int_2 (size, 0)); + + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + chainon (field_decl_chain, field_decl); + + finish_struct (objc_ivar_list_record, field_decl_chain); + + return objc_ivar_list_record; +} + +/* + * struct { + * int method_next; + * int method_count; + * struct objc_method method_list[method_count]; + * }; + */ +static tree +build_method_list_template (list_type, size) + tree list_type; + int size; +{ + tree objc_ivar_list_id, objc_ivar_list_record; + tree decl_specs, field_decl, field_decl_chain; + + objc_ivar_list_record = start_struct (RECORD_TYPE, NULLT); + + /* int method_next; */ + + decl_specs = build_tree_list (NULLT, ridpointers[(int) RID_INT]); + field_decl = get_identifier ("method_next"); + + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + field_decl_chain = field_decl; + + /* int method_count; */ + + decl_specs = build_tree_list (NULLT, ridpointers[(int) RID_INT]); + field_decl = get_identifier ("method_count"); + + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + chainon (field_decl_chain, field_decl); + + /* struct objc_method method_list[]; */ + + decl_specs = build_tree_list (NULLT, 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, NULLT); + chainon (field_decl_chain, field_decl); + + finish_struct (objc_ivar_list_record, field_decl_chain); + + return objc_ivar_list_record; +} + +static tree +build_ivar_list_initializer (field_decl, size) + tree field_decl; + int *size; +{ + tree initlist = NULLT; + + do + { + int offset; + + /* set name */ + if (DECL_NAME (field_decl)) + { + offset = add_objc_string (DECL_NAME (field_decl)); + initlist = tree_cons (NULLT, build_msg_pool_reference (offset), initlist); + } + else + { + /* unnamed bit-field ivar (yuck). */ + initlist = tree_cons (NULLT, build_int_2 (0, 0), initlist); + } + + /* set type */ + bzero (utlbuf, BUFSIZE); + encode_field_decl (field_decl, utlbuf, OBJC_ENCODE_DONT_INLINE_DEFS); + + offset = add_objc_string (get_identifier (utlbuf)); + initlist = tree_cons (NULLT, build_msg_pool_reference (offset), initlist); + + /* set offset */ + initlist = tree_cons (NULLT, + build_int_2 (TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field_decl)) / BITS_PER_UNIT, 0), + + initlist); + (*size)++; + field_decl = TREE_CHAIN (field_decl); + } + while (field_decl); + + return build_nt (CONSTRUCTOR, NULLT, nreverse (initlist)); +} + +static tree +generate_ivars_list (type, name, size, list) + tree type; + char *name; + int size; + tree list; +{ + tree sc_spec, decl_specs, decl, initlist; + + sc_spec = tree_cons (NULLT, ridpointers[(int) RID_STATIC], NULLT); + decl_specs = tree_cons (NULLT, type, sc_spec); + + decl = start_decl (synth_id_with_class_suffix (name), decl_specs, 1); + + initlist = build_tree_list (NULLT, build_int_2 (size, 0)); + initlist = tree_cons (NULLT, list, initlist); + + finish_decl (decl, build_nt (CONSTRUCTOR, NULLT, nreverse (initlist)), NULLT); + + return decl; +} + +static void +generate_ivar_lists () +{ + tree initlist, ivar_list_template, chain; + tree cast, variable_length_type; + int size; + + if (!objc_ivar_template) + objc_ivar_template = build_ivar_template (); + + cast = build_tree_list (build_tree_list (NULLT, xref_tag (RECORD_TYPE, + get_identifier (_TAG_IVAR_LIST))), NULLT); + variable_length_type = groktypename (cast); + + /* only generate class variables for the root of the inheritance + hierarchy since these will be the same for every class */ + + if (CLASS_SUPER_NAME (implementation_template) == NULLT + && (chain = TYPE_FIELDS (objc_class_template))) + { + size = 0; + initlist = build_ivar_list_initializer (chain, &size); + + ivar_list_template = build_ivar_list_template (objc_ivar_template, size); + + _OBJC_CLASS_VARIABLES_decl = + generate_ivars_list (ivar_list_template, "_OBJC_CLASS_VARIABLES", + size, initlist); + /* cast! */ + TREE_TYPE (_OBJC_CLASS_VARIABLES_decl) = variable_length_type; + } + else + _OBJC_CLASS_VARIABLES_decl = 0; + + chain = CLASS_IVARS (implementation_template); + if (chain) + { + size = 0; + initlist = build_ivar_list_initializer (chain, &size); + + ivar_list_template = build_ivar_list_template (objc_ivar_template, size); + + _OBJC_INSTANCE_VARIABLES_decl = + generate_ivars_list (ivar_list_template, "_OBJC_INSTANCE_VARIABLES", + size, initlist); + /* cast! */ + TREE_TYPE (_OBJC_INSTANCE_VARIABLES_decl) = variable_length_type; + } + else + _OBJC_INSTANCE_VARIABLES_decl = 0; +} + +static tree +build_dispatch_table_initializer (entries, size) + tree entries; + int *size; +{ + tree initlist = NULLT; + + do + { + int offset = add_objc_string (METHOD_SEL_NAME (entries)); + + initlist = tree_cons (NULLT, init_selector (offset), initlist); + + offset = add_objc_string (METHOD_ENCODING (entries)); + initlist = tree_cons (NULLT, build_msg_pool_reference (offset), initlist); + + initlist = tree_cons (NULLT, METHOD_DEFINITION (entries), initlist); + + (*size)++; + entries = TREE_CHAIN (entries); + } + while (entries); + + return build_nt (CONSTRUCTOR, NULLT, nreverse (initlist)); +} + +/* + * To accomplish method prototyping without generating all kinds of + * inane warnings, the definition of the dispatch table entries were + * changed from: + * + * struct objc_method { SEL _cmd; id (*_imp)(); }; + * to: + * struct objc_method { SEL _cmd; void *_imp; }; + */ +static tree +build_method_template () +{ + tree _SLT_record; + tree decl_specs, field_decl, field_decl_chain, parms; + + _SLT_record = start_struct (RECORD_TYPE, get_identifier (_TAG_METHOD)); + +#ifdef OBJC_INT_SELECTORS + /* unsigned int _cmd; */ + decl_specs = tree_cons (NULLT, ridpointers[(int) RID_UNSIGNED], NULLT); + decl_specs = tree_cons (NULLT, ridpointers[(int) RID_INT], decl_specs); + field_decl = get_identifier ("_cmd"); +#else /* not OBJC_INT_SELECTORS */ + /* struct objc_selector *_cmd; */ + decl_specs = tree_cons (NULLT, + xref_tag (RECORD_TYPE, + get_identifier (TAG_SELECTOR)), + NULLT); + field_decl = build1 (INDIRECT_REF, NULLT, get_identifier ("_cmd")); +#endif /* not OBJC_INT_SELECTORS */ + + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + field_decl_chain = field_decl; + + decl_specs = tree_cons (NULLT, ridpointers[(int) RID_CHAR], NULLT); + field_decl = build1 (INDIRECT_REF, NULLT, get_identifier ("method_types")); + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + chainon (field_decl_chain, field_decl); + + /* void *_imp; */ + + decl_specs = tree_cons (NULLT, ridpointers[(int) RID_VOID], NULLT); + field_decl = build1 (INDIRECT_REF, NULLT, get_identifier ("_imp")); + field_decl = grokfield (input_filename, lineno, field_decl, decl_specs, NULLT); + chainon (field_decl_chain, field_decl); + + finish_struct (_SLT_record, field_decl_chain); + + return _SLT_record; +} + + +static tree +generate_dispatch_table (type, name, size, list) + tree type; + char *name; + int size; + tree list; +{ + tree sc_spec, decl_specs, decl, initlist; + + sc_spec = tree_cons (NULLT, ridpointers[(int) RID_STATIC], NULLT); + decl_specs = tree_cons (NULLT, type, sc_spec); + + decl = start_decl (synth_id_with_class_suffix (name), decl_specs, 1); + + initlist = build_tree_list (NULLT, build_int_2 (0, 0)); + initlist = tree_cons (NULLT, build_int_2 (size, 0), initlist); + initlist = tree_cons (NULLT, list, initlist); + + finish_decl (decl, build_nt (CONSTRUCTOR, NULLT, nreverse (initlist)), NULLT); + + return decl; +} + +static void +generate_dispatch_tables () +{ + tree initlist, chain, method_list_template; + tree cast, variable_length_type; + int size; + + if (!objc_method_template) + objc_method_template = build_method_template (); + + cast = build_tree_list (build_tree_list (NULLT, xref_tag (RECORD_TYPE, + get_identifier (_TAG_METHOD_LIST))), NULLT); + variable_length_type = groktypename (cast); + + chain = CLASS_CLS_METHODS (implementation_context); + if (chain) + { + size = 0; + initlist = build_dispatch_table_initializer (chain, &size); + + method_list_template = build_method_list_template (objc_method_template, + size); + if (TREE_CODE (implementation_context) == IMPLEMENTATION_TYPE) + _OBJC_CLASS_METHODS_decl = + generate_dispatch_table (method_list_template, + "_OBJC_CLASS_METHODS", + size, initlist); + else + /* we have a category */ + _OBJC_CLASS_METHODS_decl = + generate_dispatch_table (method_list_template, + "_OBJC_CATEGORY_CLASS_METHODS", + size, initlist); + /* cast! */ + TREE_TYPE (_OBJC_CLASS_METHODS_decl) = variable_length_type; + } + else + _OBJC_CLASS_METHODS_decl = 0; + + chain = CLASS_NST_METHODS (implementation_context); + if (chain) + { + size = 0; + initlist = build_dispatch_table_initializer (chain, &size); + + method_list_template = build_method_list_template (objc_method_template, + size); + if (TREE_CODE (implementation_context) == IMPLEMENTATION_TYPE) + _OBJC_INSTANCE_METHODS_decl = + generate_dispatch_table (method_list_template, + "_OBJC_INSTANCE_METHODS", + size, initlist); + else + /* we have a category */ + _OBJC_INSTANCE_METHODS_decl = + generate_dispatch_table (method_list_template, + "_OBJC_CATEGORY_INSTANCE_METHODS", + size, initlist); + /* cast! */ + TREE_TYPE (_OBJC_INSTANCE_METHODS_decl) = variable_length_type; + } + else + _OBJC_INSTANCE_METHODS_decl = 0; +} + +static tree +build_category_initializer (cat_name, class_name, + instance_methods, class_methods) + tree cat_name; + tree class_name; + tree instance_methods; + tree class_methods; +{ + tree initlist = NULLT, expr; + + initlist = tree_cons (NULLT, cat_name, initlist); + initlist = tree_cons (NULLT, class_name, initlist); + + if (!instance_methods) + initlist = tree_cons (NULLT, build_int_2 (0, 0), initlist); + else + { + expr = build_unary_op (ADDR_EXPR, instance_methods, 0); + initlist = tree_cons (NULLT, expr, initlist); + } + if (!class_methods) + initlist = tree_cons (NULLT, build_int_2 (0, 0), initlist); + else + { + expr = build_unary_op (ADDR_EXPR, class_methods, 0); + initlist = tree_cons (NULLT, expr, initlist); + } + return build_nt (CONSTRUCTOR, NULLT, nreverse (initlist)); +} + +/* + * 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; + * struct objc_cache *cache; + * }; + */ +static tree +build_shared_structure_initializer (isa, super, name, size, status, + dispatch_table, ivar_list) + tree isa; + tree super; + tree name; + tree size; + int status; + tree dispatch_table; + tree ivar_list; +{ + tree initlist = NULLT, expr; + + /* isa = */ + initlist = tree_cons (NULLT, isa, initlist); + + /* super_class = */ + initlist = tree_cons (NULLT, super, initlist); + + /* name = */ + initlist = tree_cons (NULLT, name, initlist); + + /* version = */ + initlist = tree_cons (NULLT, build_int_2 (0, 0), initlist); + + /* info = */ + initlist = tree_cons (NULLT, build_int_2 (status), initlist); + + /* instance_size = */ + initlist = tree_cons (NULLT, size, initlist); + + /* objc_ivar_list = */ + if (!ivar_list) + initlist = tree_cons (NULLT, build_int_2 (0, 0), initlist); + else + { + expr = build_unary_op (ADDR_EXPR, ivar_list, 0); + initlist = tree_cons (NULLT, expr, initlist); + } + + /* objc_method_list = */ + if (!dispatch_table) + initlist = tree_cons (NULLT, build_int_2 (0, 0), initlist); + else + { + expr = build_unary_op (ADDR_EXPR, dispatch_table, 0); + initlist = tree_cons (NULLT, expr, initlist); + } + + /* method_cache = */ + initlist = tree_cons (NULLT, build_int_2 (0, 0), initlist); + + return build_nt (CONSTRUCTOR, NULLT, nreverse (initlist)); +} + +/* + * static struct objc_category _OBJC_CATEGORY_ = { ... }; + */ +static void +generate_category (cat) + tree cat; +{ + tree sc_spec, decl_specs, decl; + tree initlist, cat_name_expr, class_name_expr; + int offset; + + sc_spec = tree_cons (NULLT, ridpointers[(int) RID_STATIC], NULLT); + decl_specs = tree_cons (NULLT, objc_category_template, sc_spec); + + decl = start_decl (synth_id_with_class_suffix ("_OBJC_CATEGORY"), + decl_specs, 1); + + offset = add_objc_string (CLASS_SUPER_NAME (cat)); + cat_name_expr = build_msg_pool_reference (offset); + + offset = add_objc_string (CLASS_NAME (cat)); + class_name_expr = build_msg_pool_reference (offset); + + initlist = build_category_initializer ( + cat_name_expr, class_name_expr, + _OBJC_INSTANCE_METHODS_decl, _OBJC_CLASS_METHODS_decl); + + finish_decl (decl, initlist, NULLT); +} + +/* + * static struct objc_class _OBJC_METACLASS_Foo={ ... }; + * static struct objc_class _OBJC_CLASS_Foo={ ... }; + */ +static void +generate_shared_structures () +{ + tree sc_spec, decl_specs, expr_decl, decl; + tree name_expr, super_expr, root_expr; + tree my_root_id = NULLT, my_super_id = NULLT; + tree cast_type, initlist; + int offset; + + my_super_id = CLASS_SUPER_NAME (implementation_template); + if (my_super_id) + { + add_class_reference (my_super_id); + + /* compute "my_root_id" - this is required for code generation. + * the "isa" for all meta class structures points to the root of + * the inheritance hierarchy (e.g. "__Object")... + */ + my_root_id = my_super_id; + do + { + tree my_root_int = lookup_interface (my_root_id); + + if (my_root_int && CLASS_SUPER_NAME (my_root_int)) + my_root_id = CLASS_SUPER_NAME (my_root_int); + else + break; + } + while (1); + } + else /* no super class */ + { + my_root_id = CLASS_NAME (implementation_template); + } + + cast_type = groktypename (build_tree_list (build_tree_list (NULLT, + objc_class_template), build1 (INDIRECT_REF, NULLT, NULLT))); + + offset = add_objc_string (CLASS_NAME (implementation_template)); + name_expr = build_msg_pool_reference (offset); + + /* install class `isa' and `super' pointers at runtime */ + if (my_super_id) + { + offset = add_objc_string (my_super_id); + super_expr = build_msg_pool_reference (offset); + TREE_TYPE (super_expr) = cast_type; /* cast! */ + } + else + super_expr = build_int_2 (0, 0); + + offset = add_objc_string (my_root_id); + root_expr = build_msg_pool_reference (offset); + TREE_TYPE (root_expr) = cast_type; /* cast! */ + + /* static struct objc_class _OBJC_METACLASS_Foo = { ... }; */ + + sc_spec = build_tree_list (NULLT, ridpointers[(int) RID_STATIC]); + decl_specs = tree_cons (NULLT, objc_class_template, sc_spec); + + decl = start_decl (DECL_NAME (_OBJC_METACLASS_decl), decl_specs, 1); + + initlist = build_shared_structure_initializer ( + root_expr, super_expr, name_expr, + build_int_2 (TREE_INT_CST_LOW (TYPE_SIZE (objc_class_template)) / BITS_PER_UNIT, 0), + 2 /*CLS_META*/, + _OBJC_CLASS_METHODS_decl, _OBJC_CLASS_VARIABLES_decl); + + finish_decl (decl, initlist, NULLT); + + /* static struct objc_class _OBJC_CLASS_Foo={ ... }; */ + + decl = start_decl (DECL_NAME (_OBJC_CLASS_decl), decl_specs, 1); + + initlist = build_shared_structure_initializer ( + build_unary_op (ADDR_EXPR, _OBJC_METACLASS_decl, 0), + super_expr, name_expr, + build_int_2 (TREE_INT_CST_LOW (TYPE_SIZE (CLASS_STATIC_TEMPLATE (implementation_template))) / BITS_PER_UNIT, 0), + 1 /*CLS_FACTORY*/, + _OBJC_INSTANCE_METHODS_decl, _OBJC_INSTANCE_VARIABLES_decl); + + finish_decl (decl, initlist, NULLT); +} + +static tree +synth_id_with_class_suffix (preamble) + char *preamble; +{ + if (TREE_CODE (implementation_context) == IMPLEMENTATION_TYPE) + sprintf (utlbuf, "%s_%s", preamble, + IDENTIFIER_POINTER (CLASS_NAME (implementation_context))); + else + /* we have a category */ + sprintf (utlbuf, "%s_%s_%s", preamble, + IDENTIFIER_POINTER (CLASS_NAME (implementation_context)), + IDENTIFIER_POINTER (CLASS_SUPER_NAME (implementation_context))); + return get_identifier (utlbuf); +} + +/* + * usage: + * keyworddecl: + * selector ':' '(' typename ')' identifier + * + * purpose: + * transform an Objective-C keyword argument into + * the C equivalent parameter declarator. + * + * in: key_name, an "identifier_node" (optional). + * arg_type, a "tree_list" (optional). + * arg_name, an "identifier_node". + * + * note: it would be really nice to strongly type the preceding + * arguments in the function prototype; however, then i + * could not use the "accessor" macros defined in "tree.h". + * + * out: an instance of "keyword_decl". + * + */ + +tree +build_keyword_decl (key_name, arg_type, arg_name) + tree key_name; + tree arg_type; + tree arg_name; +{ + tree keyword_decl; + + /* if no type is specified, default to "id" */ + if (arg_type == NULLT) + arg_type = build_tree_list (build_tree_list (NULLT, objc_object_reference), + build1 (INDIRECT_REF, NULLT, NULLT)); + + keyword_decl = make_node (KEYWORD_DECL); + + TREE_TYPE (keyword_decl) = arg_type; + KEYWORD_ARG_NAME (keyword_decl) = arg_name; + KEYWORD_KEY_NAME (keyword_decl) = key_name; + + return keyword_decl; +} + +/* + * given a chain of keyword_decl's, synthesize the full keyword selector. + */ +static tree +build_keyword_selector (selector) + tree selector; +{ + int len = 0; + tree key_chain, key_name; + char *buf; + + for (key_chain = selector; key_chain; key_chain = TREE_CHAIN (key_chain)) + { + if (TREE_CODE (selector) == KEYWORD_DECL) + key_name = KEYWORD_KEY_NAME (key_chain); + else if (TREE_CODE (selector) == TREE_LIST) + key_name = TREE_PURPOSE (key_chain); + + if (key_name) + len += IDENTIFIER_LENGTH (key_name) + 1; + else /* just a ':' arg */ + len++; + } + buf = (char *)alloca (len + 1); + bzero (buf, len + 1); + + for (key_chain = selector; key_chain; key_chain = TREE_CHAIN (key_chain)) + { + if (TREE_CODE (selector) == KEYWORD_DECL) + key_name = KEYWORD_KEY_NAME (key_chain); + else if (TREE_CODE (selector) == TREE_LIST) + key_name = TREE_PURPOSE (key_chain); + + if (key_name) + strcat (buf, IDENTIFIER_POINTER (key_name)); + strcat (buf, ":"); + } + return get_identifier (buf); +} + +/* used for declarations and definitions */ + +tree +build_method_decl (code, ret_type, selector, add_args) + enum tree_code code; + tree ret_type; + tree selector; + tree add_args; +{ + tree method_decl; + + /* if no type is specified, default to "id" */ + if (ret_type == NULLT) + ret_type = build_tree_list (build_tree_list (NULLT, objc_object_reference), + build1 (INDIRECT_REF, NULLT, NULLT)); + + method_decl = make_node (code); + TREE_TYPE (method_decl) = ret_type; + + /* + * if we have a keyword selector, create an identifier_node that + * represents the full selector name (`:' included)... + */ + if (TREE_CODE (selector) == KEYWORD_DECL) + { + METHOD_SEL_NAME (method_decl) = build_keyword_selector (selector); + METHOD_SEL_ARGS (method_decl) = selector; + METHOD_ADD_ARGS (method_decl) = add_args; + } + else + { + METHOD_SEL_NAME (method_decl) = selector; + METHOD_SEL_ARGS (method_decl) = NULLT; + METHOD_ADD_ARGS (method_decl) = NULLT; + } + + return method_decl; +} + +#define METHOD_DEF 0 +#define METHOD_REF 1 +/* + * used by `build_message_expr' and `comp_method_types'. + * + * add_args is a tree_list node the following info on a parameter list: + * + * The TREE_PURPOSE is a chain of decls of those parms. + * The TREE_VALUE is a list of structure, union and enum tags defined. + * The TREE_CHAIN is a list of argument types to go in the FUNCTION_TYPE. + * This tree_list node is later fed to `grokparms'. + * + * VOID_AT_END nonzero means append `void' to the end of the type-list. + * Zero means the parmlist ended with an ellipsis so don't append `void'. + */ +static tree +get_arg_type_list (meth, context, superflag) + tree meth; + int context; + int superflag; +{ + tree arglist, akey; + + /* receiver type */ + if (superflag) + arglist = build_tree_list (NULLT, super_type); + else + { + if (context == METHOD_DEF) + arglist = build_tree_list (NULLT, TREE_TYPE (self_decl)); + else + arglist = build_tree_list (NULLT, id_type); + } + + /* selector type - will eventually change to `int' */ + chainon (arglist, build_tree_list (NULLT, _selector_type)); + + /* build a list of argument types */ + for (akey = METHOD_SEL_ARGS (meth); akey; akey = TREE_CHAIN (akey)) + { + tree arg_decl = groktypename_in_parm_context (TREE_TYPE (akey)); + chainon (arglist, build_tree_list (NULLT, TREE_TYPE (arg_decl))); + } + + if (METHOD_ADD_ARGS (meth) == (tree)1) + /* + * we have a `, ...' immediately following the selector, + * finalize the arglist...simulate get_parm_info (0) + */ + ; + else if (METHOD_ADD_ARGS (meth)) + { + /* we have a variable length selector */ + tree add_arg_list = TREE_CHAIN (METHOD_ADD_ARGS (meth)); + chainon (arglist, add_arg_list); + } + else /* finalize the arglist...simulate get_parm_info (1) */ + chainon (arglist, build_tree_list (NULLT, void_type_node)); + + return arglist; +} + +static tree +check_duplicates (hsh) + hash hsh; +{ + tree meth = NULLT; + + if (hsh) + { + meth = hsh->key; + + if (hsh->list) + { + /* we have two methods with the same name and different types */ + attr loop; + char type; + + type = (TREE_CODE (meth) == INSTANCE_METHOD_DECL) ? '-' : '+'; + + warning ("multiple declarations for method `%s'", + IDENTIFIER_POINTER (METHOD_SEL_NAME (meth))); + + warn_with_method ("using", type, meth); + for (loop = hsh->list; loop; loop = loop->next) + warn_with_method ("also found", type, loop->value); + } + } + return meth; +} + +static tree +receiver_is_class_object (receiver) + tree receiver; +{ + /* the receiver is a function call that returns an id... + * ...check if it is a call to objc_getClass, if so, give it + * special treatment. + */ + tree exp = 0; + + if ((exp = TREE_OPERAND (receiver, 0)) && (TREE_CODE (exp) == ADDR_EXPR)) + { + if ((exp = TREE_OPERAND (exp, 0)) && + (TREE_CODE (exp) == FUNCTION_DECL) && exp == objc_getClass_decl) + { + /* we have a call to objc_getClass! */ + tree arg = 0; + + if ((arg = TREE_OPERAND (receiver, 1)) && + (TREE_CODE (arg) == TREE_LIST) && + (arg = TREE_VALUE (arg)) && + (TREE_CODE (arg) == NOP_EXPR) && + (arg = TREE_OPERAND (arg, 0)) && + (TREE_CODE (arg) == ADDR_EXPR) && + (arg = TREE_OPERAND (arg, 0)) && + (TREE_CODE (arg) == STRING_CST)) + /* finally, we have the class name */ + return get_identifier (TREE_STRING_POINTER (arg)); + } + } + return 0; +} + +/* If we are currently building a message expr, this holds + the identifier of the selector of the message. This is + used when printing warnings about argument mismatches. */ + +static tree building_objc_message_expr = 0; + +tree +maybe_building_objc_message_expr () +{ + return building_objc_message_expr; +} + +/* Construct an expression for sending a message. + MESS has the object to send to in TREE_PURPOSE + and the argument list (including selector) in TREE_VALUE. */ + +tree +build_message_expr (mess) + tree mess; +{ + tree receiver = TREE_PURPOSE (mess); + tree selector, self_object; + tree rtype, sel_name; + tree args = TREE_VALUE (mess); + tree method_params = NULLT; + tree method_prototype = NULLT; + int selTransTbl_index; + tree retval; + int statically_typed = 0, statically_allocated = 0; + tree class_ident = 0; + + /* 1 if this is sending to the superclass. */ + int super; + + if (!doing_objc_thang) + fatal ("Objective-C text in C source file"); + + if (TREE_CODE (receiver) == ERROR_MARK) + return error_mark_node; + + /* determine receiver type */ + rtype = TREE_TYPE (receiver); + super = (TREE_TYPE (receiver) == super_type); + + if (! super) + { + if (TREE_STATIC_TEMPLATE (rtype)) + statically_allocated = 1; + else if (TREE_CODE (rtype) == POINTER_TYPE + && TREE_STATIC_TEMPLATE (TREE_TYPE (rtype))) + statically_typed = 1; + /* classfix -smn */ + else if (TREE_CODE (receiver) == CALL_EXPR && rtype == id_type + && (class_ident = receiver_is_class_object (receiver))) + ; + else if (rtype != id_type && rtype != class_type) + { + bzero (errbuf, BUFSIZE); + warning ("invalid receiver type `%s'", gen_declaration (rtype, errbuf)); + } + if (statically_allocated) + receiver = build_unary_op (ADDR_EXPR, receiver, 0); + + self_object = receiver; + } + else + /* If sending to `super', use current self as the object. */ + self_object = self_decl; + + /* Obtain the full selector name. */ + + if (TREE_CODE (args) == IDENTIFIER_NODE) + /* a unary selector */ + sel_name = args; + else if (TREE_CODE (args) == TREE_LIST) + sel_name = build_keyword_selector (args); + + selTransTbl_index = add_selector_reference (sel_name); + + /* Build the parameters list for looking up the method. + These are the object itself and the selector. */ + +#ifndef OBJC_NONUNIQUE_SELECTORS + selector = build_selector_reference (selTransTbl_index); +#else + selector = build_array_ref (_OBJC_SELECTOR_REFERENCES_decl, + build_int_2 (selTransTbl_index, 0)); +#endif + + /* Build the parameter list to give to the method. */ + + method_params = NULLT; + if (TREE_CODE (args) == TREE_LIST) + { + tree chain = args, prev = NULLT; + + /* We have a keyword selector--check for comma expressions. */ + while (chain) + { + tree element = TREE_VALUE (chain); + + /* We have a comma expression, must collapse... */ + if (TREE_CODE (element) == TREE_LIST) + { + if (prev) + TREE_CHAIN (prev) = element; + else + args = element; + } + prev = chain; + chain = TREE_CHAIN (chain); + } + method_params = args; + } + + /* Determine operation return type. */ + + if (rtype == super_type) + { + tree iface; + + if (CLASS_SUPER_NAME (implementation_template)) + { + iface = lookup_interface (CLASS_SUPER_NAME (implementation_template)); + + if (TREE_CODE (method_context) == INSTANCE_METHOD_DECL) + method_prototype = lookup_instance_method_static (iface, sel_name); + else + method_prototype = lookup_class_method_static (iface, sel_name); + + if (iface && !method_prototype) + warning ("`%s' does not respond to `%s'", + IDENTIFIER_POINTER (CLASS_SUPER_NAME (implementation_template)), + IDENTIFIER_POINTER (sel_name)); + } + else + { + error ("no super class declared in interface for `%s'", + IDENTIFIER_POINTER (CLASS_NAME (implementation_template))); + return error_mark_node; + } + + } + else if (statically_allocated) + { + tree iface = lookup_interface (TYPE_NAME (rtype)); + + if (iface && !(method_prototype = lookup_instance_method_static (iface, sel_name))) + warning ("`%s' does not respond to `%s'", + IDENTIFIER_POINTER (TYPE_NAME (rtype)), + IDENTIFIER_POINTER (sel_name)); + } + else if (statically_typed) + { + tree ctype = TREE_TYPE (rtype); + + /* `self' is now statically_typed...all methods should be visible + * within the context of the implementation... + */ + if (implementation_context + && CLASS_NAME (implementation_context) == TYPE_NAME (ctype)) + { + method_prototype = lookup_instance_method_static (implementation_template, sel_name); + + if (!method_prototype && implementation_template != implementation_context) + /* the method is not published in the interface...check locally */ + method_prototype = lookup_method (CLASS_NST_METHODS (implementation_context), + sel_name); + } + else + { + tree iface; + + if (iface = lookup_interface (TYPE_NAME (ctype))) + method_prototype = lookup_instance_method_static (iface, sel_name); + } + + if (!method_prototype) + warning ("`%s' does not respond to `%s'", + IDENTIFIER_POINTER (TYPE_NAME (ctype)), + IDENTIFIER_POINTER (sel_name)); + } + else if (class_ident) + { + if (implementation_context + && CLASS_NAME (implementation_context) == class_ident) + { + method_prototype + = lookup_class_method_static (implementation_template, sel_name); + + if (!method_prototype + && implementation_template != implementation_context) + /* the method is not published in the interface...check locally */ + method_prototype + = lookup_method (CLASS_CLS_METHODS (implementation_context), + sel_name); + } + else + { + tree iface; + + if (iface = lookup_interface (class_ident)) + method_prototype = lookup_class_method_static (iface, sel_name); + } + + if (!method_prototype) + { + warning ("cannot find class (factory) method."); + warning ("return type for `%s' defaults to id", + IDENTIFIER_POINTER (sel_name)); + } + } + else + { + hash hsh; + + /* we think we have an instance...loophole: extern id Object; */ + hsh = hash_lookup (nst_method_hash_list, sel_name); + if (!hsh) + /* for various loopholes...like sending messages to self in a + factory context... */ + hsh = hash_lookup (cls_method_hash_list, sel_name); + + method_prototype = check_duplicates (hsh); + if (!method_prototype) + { + warning ("cannot find method."); + warning ("return type for `%s' defaults to id", + IDENTIFIER_POINTER (sel_name)); + } + } + + /* Save the selector name for printing error messages. */ + building_objc_message_expr = sel_name; + + retval = build_objc_method_call (super, method_prototype, + receiver, self_object, + selector, method_params); + + building_objc_message_expr = 0; + + return retval; +} + +/* Build a tree expression to send OBJECT the operation SELECTOR, + looking up the method on object LOOKUP_OBJECT (often same as OBJECT), + assuming the method has prototype METHOD_PROTOTYPE. + (That is an INSTANCE_METHOD_DECL or CLASS_METHOD_DECL.) + Use METHOD_PARAMS as list of args to pass to the method. + If SUPER_FLAG is nonzero, we look up the superclass's method. */ + +static tree +build_objc_method_call (super_flag, method_prototype, lookup_object, object, + selector, method_params) + int super_flag; + tree method_prototype, lookup_object, object, selector, method_params; +{ + tree sender = (super_flag ? _msgSuper_decl : _msg_decl); + +#ifdef NEXT_OBJC_RUNTIME + if (!method_prototype) + { + method_params = tree_cons (NULLT, lookup_object, + tree_cons (NULLT, selector, method_params)); + return build_function_call (sender, method_params); + } + else + { + /* This is a real kludge, but it is used only for the Next. + Clobber the data type of SENDER temporarily to accept + all the arguments for this operation, and to return + whatever this operation returns. */ + tree arglist = NULLT; + tree retval; + + /* Save the proper contents of SENDER's data type. */ + tree savarg = TYPE_ARG_TYPES (TREE_TYPE (sender)); + tree savret = TREE_TYPE (TREE_TYPE (sender)); + + /* Install this method's argument types. */ + arglist = get_arg_type_list (method_prototype, METHOD_REF, super_flag); + TYPE_ARG_TYPES (TREE_TYPE (sender)) = arglist; + + /* Install this method's return type. */ + TREE_TYPE (TREE_TYPE (sender)) + = groktypename (TREE_TYPE (method_prototype)); + + /* Call SENDER with all the parameters. + This will do type checking using the arg types for this method. */ + method_params = tree_cons (NULLT, lookup_object, + tree_cons (NULLT, selector, method_params)); + retval = build_function_call (sender, method_params); + + /* Restore SENDER's return/argument types. */ + TYPE_ARG_TYPES (TREE_TYPE (sender)) = savarg; + TREE_TYPE (TREE_TYPE (sender)) = savret; + return retval; + } +#else /* not NEXT_OBJC_RUNTIME */ + /* This is the portable way. + First call the lookup function to get a pointer to the method, + then cast the pointer, then call it with the method arguments. */ + tree method; + + /* Avoid trouble since we may evaluate each of these twice. */ + object = save_expr (object); + selector = save_expr (selector); + + method + = build_function_call (sender, + tree_cons (NULLT, lookup_object, + tree_cons (NULLT, selector, NULLT))); + + /* If we have a method prototype, construct the data type this method needs, + and cast what we got from SENDER into a pointer to that type. */ + if (method_prototype) + { + tree arglist = get_arg_type_list (method_prototype, METHOD_REF, super_flag); + tree valtype = groktypename (TREE_TYPE (method_prototype)); + tree fake_function_type = build_function_type (valtype, arglist); + TREE_TYPE (method) = build_pointer_type (fake_function_type); + } + else + { + TREE_TYPE (method) + = build_pointer_type (build_function_type (ptr_type_node, NULLT)); + } + /* Pass the object to the method. */ + return build_function_call (method, + tree_cons (NULLT, object, + tree_cons (NULLT, selector, + method_params))); +#endif /* not NEXT_OBJC_RUNTIME */ +} + +tree +build_selector_expr (selnamelist) + tree selnamelist; +{ + tree selname; + int selTransTbl_index; + + if (!doing_objc_thang) + fatal ("Objective-C text in C source file"); + + /* obtain the full selector name */ + if (TREE_CODE (selnamelist) == IDENTIFIER_NODE) + /* a unary selector */ + selname = selnamelist; + else if (TREE_CODE (selnamelist) == TREE_LIST) + selname = build_keyword_selector (selnamelist); + + selTransTbl_index = add_selector_reference (selname); + +#ifndef OBJC_NONUNIQUE_SELECTORS + return build_selector_reference (selTransTbl_index); +#else + /* synthesize a reference into the selector translation table */ + return build_array_ref (_OBJC_SELECTOR_REFERENCES_decl, + build_int_2 (selTransTbl_index, 0)); +#endif +} + +tree +build_encode_expr (type) + tree type; +{ + if (!doing_objc_thang) + fatal ("Objective-C text in C source file"); + + if (!utlbuf) + utlbuf = (char *)xmalloc (BUFSIZE); + bzero (utlbuf, BUFSIZE); + + encode_type (type, utlbuf, OBJC_ENCODE_INLINE_DEFS); + + /* synthesize a string that represents the encoded struct/union */ + return my_build_string (strlen (utlbuf) + 1, utlbuf); +} + +tree +build_ivar_reference (id) + tree id; +{ + if (TREE_CODE (method_context) == CLASS_METHOD_DECL) + TREE_TYPE (self_decl) = instance_type; /* cast */ + + return build_component_ref (build_indirect_ref (self_decl, "->"), id); +} + +#define HASH_ALLOC_LIST_SIZE 170 +#define ATTR_ALLOC_LIST_SIZE 170 +#define SIZEHASHTABLE 257 +#define HASHFUNCTION(key) ((int)key >> 2) /* divide by 4 */ + +static void +hash_init () +{ + nst_method_hash_list = (hash *)xmalloc (SIZEHASHTABLE * sizeof (hash)); + cls_method_hash_list = (hash *)xmalloc (SIZEHASHTABLE * sizeof (hash)); + + if (!nst_method_hash_list || !cls_method_hash_list) + perror ("unable to allocate space in objc-tree.c"); + else + { + int i; + + for (i = 0; i < SIZEHASHTABLE; i++) + { + nst_method_hash_list[i] = 0; + cls_method_hash_list[i] = 0; + } + } +} + +static void +hash_enter (hashlist, method) + hash *hashlist; + tree method; +{ + static hash hash_alloc_list = 0; + static int hash_alloc_index = 0; + hash obj; + int slot = HASHFUNCTION (METHOD_SEL_NAME (method)) % SIZEHASHTABLE; + + if (!hash_alloc_list || hash_alloc_index >= HASH_ALLOC_LIST_SIZE) + { + hash_alloc_index = 0; + hash_alloc_list = (hash)xmalloc (sizeof (struct hashedEntry) * + HASH_ALLOC_LIST_SIZE); + if (!hash_alloc_list) + perror ("unable to allocate in objc-tree.c"); + } + obj = &hash_alloc_list[hash_alloc_index++]; + obj->list = 0; + obj->next = hashlist[slot]; + obj->key = method; + + hashlist[slot] = obj; /* append to front */ +} + +static hash +hash_lookup (hashlist, sel_name) + hash *hashlist; + tree sel_name; +{ + hash target; + + target = hashlist[HASHFUNCTION (sel_name) % SIZEHASHTABLE]; + + while (target) + { + if (sel_name == METHOD_SEL_NAME (target->key)) + return target; + + target = target->next; + } + return 0; +} + +static void +hash_add_attr (entry, value) + hash entry; + tree value; +{ + static attr attr_alloc_list = 0; + static int attr_alloc_index = 0; + attr obj; + + if (!attr_alloc_list || attr_alloc_index >= ATTR_ALLOC_LIST_SIZE) + { + attr_alloc_index = 0; + attr_alloc_list = (attr)xmalloc (sizeof (struct hashedAttribute) * + ATTR_ALLOC_LIST_SIZE); + if (!attr_alloc_list) + perror ("unable to allocate in objc-tree.c"); + } + obj = &attr_alloc_list[attr_alloc_index++]; + obj->next = entry->list; + obj->value = value; + + entry->list = obj; /* append to front */ +} + +static tree +lookup_method (mchain, method) + tree mchain; + tree method; +{ + tree key; + + if (TREE_CODE (method) == IDENTIFIER_NODE) + key = method; + else + key = METHOD_SEL_NAME (method); + + while (mchain) + { + if (METHOD_SEL_NAME (mchain) == key) + return mchain; + mchain = TREE_CHAIN (mchain); + } + return NULLT; +} + +static tree +lookup_instance_method_static (interface, ident) + tree interface; + tree ident; +{ + tree inter = interface; + tree chain = CLASS_NST_METHODS (inter); + tree meth = NULLT; + + do + { + if (meth = lookup_method (chain, ident)) + return meth; + + if (CLASS_CATEGORY_LIST (inter)) + { + tree category = CLASS_CATEGORY_LIST (inter); + chain = CLASS_NST_METHODS (category); + + do + { + if (meth = lookup_method (chain, ident)) + return meth; + + if (category = CLASS_CATEGORY_LIST (category)) + chain = CLASS_NST_METHODS (category); + } + while (category); + } + + if (inter = lookup_interface (CLASS_SUPER_NAME (inter))) + chain = CLASS_NST_METHODS (inter); + } + while (inter); + + return meth; +} + +static tree +lookup_class_method_static (interface, ident) + tree interface; + tree ident; +{ + tree inter = interface; + tree chain = CLASS_CLS_METHODS (inter); + tree meth = NULLT; + + do + { + if (meth = lookup_method (chain, ident)) + return meth; + + if (CLASS_CATEGORY_LIST (inter)) + { + tree category = CLASS_CATEGORY_LIST (inter); + chain = CLASS_CLS_METHODS (category); + + do + { + if (meth = lookup_method (chain, ident)) + return meth; + + if (category = CLASS_CATEGORY_LIST (category)) + chain = CLASS_CLS_METHODS (category); + } + while (category); + } + + if (inter = lookup_interface (CLASS_SUPER_NAME (inter))) + chain = CLASS_CLS_METHODS (inter); + } + while (inter); + + return meth; +} + +tree +add_class_method (class, method) + tree class; + tree method; +{ + tree mth; + hash hsh; + + if (!(mth = lookup_method (CLASS_CLS_METHODS (class), method))) + { + /* put method on list in reverse order */ + TREE_CHAIN (method) = CLASS_CLS_METHODS (class); + CLASS_CLS_METHODS (class) = method; + } + else + { + if (TREE_CODE (class) == IMPLEMENTATION_TYPE) + error ("duplicate definition of class method `%s'.", + IDENTIFIER_POINTER (METHOD_SEL_NAME (mth))); + else + { + /* check types, if different complain */ + if (!comp_proto_with_proto (method, mth)) + error ("duplicate declaration of class method `%s'.", + IDENTIFIER_POINTER (METHOD_SEL_NAME (mth))); + } + } + + if (!(hsh = hash_lookup (cls_method_hash_list, METHOD_SEL_NAME (method)))) + { + /* install on a global chain */ + hash_enter (cls_method_hash_list, method); + } + else + { + /* check types, if different add to a list */ + if (!comp_proto_with_proto (method, hsh->key)) + hash_add_attr (hsh, method); + } + return method; +} + +tree +add_instance_method (class, method) + tree class; + tree method; +{ + tree mth; + hash hsh; + + if (!(mth = lookup_method (CLASS_NST_METHODS (class), method))) + { + /* put method on list in reverse order */ + TREE_CHAIN (method) = CLASS_NST_METHODS (class); + CLASS_NST_METHODS (class) = method; + } + else + { + if (TREE_CODE (class) == IMPLEMENTATION_TYPE) + error ("duplicate definition of instance method `%s'.", + IDENTIFIER_POINTER (METHOD_SEL_NAME (mth))); + else + { + /* check types, if different complain */ + if (!comp_proto_with_proto (method, mth)) + error ("duplicate declaration of instance method `%s'.", + IDENTIFIER_POINTER (METHOD_SEL_NAME (mth))); + } + } + + if (!(hsh = hash_lookup (nst_method_hash_list, METHOD_SEL_NAME (method)))) + { + /* install on a global chain */ + hash_enter (nst_method_hash_list, method); + } + else + { + /* check types, if different add to a list */ + if (!comp_proto_with_proto (method, hsh->key)) + hash_add_attr (hsh, method); + } + return method; +} + +static tree +add_class (class) + tree class; +{ + /* put interfaces on list in reverse order */ + TREE_CHAIN (class) = interface_chain; + interface_chain = class; + return interface_chain; +} + +static void +add_category (class, category) + tree class; + tree category; +{ + /* put categories on list in reverse order */ + CLASS_CATEGORY_LIST (category) = CLASS_CATEGORY_LIST (class); + CLASS_CATEGORY_LIST (class) = category; +} + +/* called after parsing each instance variable declaration. Necessary to + * preserve typedefs and implement public/private... + */ +tree +add_instance_variable (class, public, declarator, declspecs, width) + tree class; + int public; + tree declarator; + tree declspecs; + tree width; +{ + tree field_decl, raw_decl; + + raw_decl = build_tree_list (declspecs /*purpose*/, declarator/*value*/); + + if (CLASS_RAW_IVARS (class)) + chainon (CLASS_RAW_IVARS (class), raw_decl); + else + CLASS_RAW_IVARS (class) = raw_decl; + + field_decl = grokfield (input_filename, lineno, + declarator, declspecs, width); + + /* overload the public attribute, it is not used for FIELD_DECL's */ + if (public) + TREE_PUBLIC (field_decl) = 1; + + if (CLASS_IVARS (class)) + chainon (CLASS_IVARS (class), field_decl); + else + CLASS_IVARS (class) = field_decl; + + return class; +} + +tree +is_ivar (decl_chain, ident) + tree decl_chain; + tree ident; +{ + for ( ; decl_chain; decl_chain = TREE_CHAIN (decl_chain)) + if (DECL_NAME (decl_chain) == ident) + return decl_chain; + return NULL_TREE; +} + +/* we have an instance variable reference, check to see if it is public...*/ + +int +is_public (expr, identifier) + tree expr; + tree identifier; +{ + tree basetype = TREE_TYPE (expr); + enum tree_code code = TREE_CODE (basetype); + tree decl; + + if (code == RECORD_TYPE) + { + if (TREE_STATIC_TEMPLATE (basetype)) + { + if (decl = is_ivar (TYPE_FIELDS (basetype), identifier)) + { + /* important diffence between the Stepstone translator: + + all instance variables should be public within the context + of the implementation... + */ + if (implementation_context) + { + if ((TREE_CODE (implementation_context) == IMPLEMENTATION_TYPE + && CLASS_NAME (implementation_context) == TYPE_NAME (basetype)) + || (TREE_CODE (implementation_context) == CATEGORY_TYPE + && CLASS_NAME (implementation_context) == TYPE_NAME (basetype))) + return 1; + } + + if (TREE_PUBLIC (decl)) + return 1; + + error ("instance variable `%s' is declared private", + IDENTIFIER_POINTER (identifier)); + return 0; + } + } + else if (implementation_context && (basetype == objc_object_reference)) + { + TREE_TYPE (expr) = _PRIVATE_record; + if (extra_warnings) + { + warning ("static access to object of type `id'"); + warning ("please change to type `%s *'", + IDENTIFIER_POINTER (CLASS_NAME (implementation_context))); + } + } + } + return 1; +} + +/* implement @defs () within struct bodies. */ + +tree +get_class_ivars (interface) + tree interface; +{ + if (!doing_objc_thang) + fatal ("Objective-C text in C source file"); + + return build_ivar_chain (interface); +} + +tree +get_class_reference (interface) + tree interface; +{ + tree params; + + add_class_reference (CLASS_NAME (interface)); + + params = build_tree_list (NULLT, + my_build_string (IDENTIFIER_LENGTH (CLASS_NAME (interface)) + 1, + IDENTIFIER_POINTER (CLASS_NAME (interface)))); + + return build_function_call (objc_getClass_decl, params); +} + +/* make sure all entries in "chain" are also in "list" */ + +static void +check_methods (chain, list, mtype) + tree chain; + tree list; + int mtype; +{ + int first = 1; + + while (chain) + { + if (!lookup_method (list, chain)) + { + if (first) + { + if (TREE_CODE (implementation_context) == IMPLEMENTATION_TYPE) + warning ("incomplete implementation of class `%s'", + IDENTIFIER_POINTER (CLASS_NAME (implementation_context))); + else if (TREE_CODE (implementation_context) == CATEGORY_TYPE) + warning ("incomplete implementation of category `%s'", + IDENTIFIER_POINTER (CLASS_SUPER_NAME (implementation_context))); + first = 0; + } + warning ("method definition for `%c%s' not found", + mtype, IDENTIFIER_POINTER (METHOD_SEL_NAME (chain))); + } + chain = TREE_CHAIN (chain); + } +} + +tree +start_class (code, class_name, super_name) + enum tree_code code; + tree class_name; + tree super_name; +{ + tree class; + + if (!doing_objc_thang) + fatal ("Objective-C text in C source file"); + + class = make_node (code); + + CLASS_NAME (class) = class_name; + CLASS_SUPER_NAME (class) = super_name; + CLASS_CLS_METHODS (class) = NULL_TREE; + + if (code == IMPLEMENTATION_TYPE) + { + /* pre-build the following entities - for speed/convenience. */ + if (!self_id) + self_id = get_identifier ("self"); + if (!_cmd_id) + _cmd_id = get_identifier ("_cmd"); + + if (!objc_super_template) + objc_super_template = build_super_template (); + + method_slot = 0; /* reset for multiple classes per file */ + + implementation_context = class; + + /* lookup the interface for this implementation. */ + + if (!(implementation_template = lookup_interface (class_name))) + { + warning ("Cannot find interface declaration for `%s'", + IDENTIFIER_POINTER (class_name)); + add_class (implementation_template = implementation_context); + } + + /* if a super class has been specified in the implementation, + insure it conforms to the one specified in the interface */ + + if (super_name + && (super_name != CLASS_SUPER_NAME (implementation_template))) + { + error ("conflicting super class name `%s'", + IDENTIFIER_POINTER (super_name)); + error ("previous declaration of `%s'", + IDENTIFIER_POINTER (CLASS_SUPER_NAME (implementation_template))); + } + } + else if (code == INTERFACE_TYPE) + { + if (lookup_interface (class_name)) + warning ("duplicate interface declaration for class `%s'", + IDENTIFIER_POINTER (class_name)); + else + add_class (class); + } + else if (code == PROTOCOL_TYPE) + { + tree class_category_is_assoc_with; + + /* for a category, class_name is really the name of the class that + the following set of methods will be associated with...we must + find the interface so that can derive the objects template */ + + if (!(class_category_is_assoc_with = lookup_interface (class_name))) + { + error ("Cannot find interface declaration for `%s'", + IDENTIFIER_POINTER (class_name)); + exit (1); + } + else + add_category (class_category_is_assoc_with, class); + } + else if (code == CATEGORY_TYPE) + { + /* pre-build the following entities - for speed/convenience. */ + if (!self_id) + self_id = get_identifier ("self"); + if (!_cmd_id) + _cmd_id = get_identifier ("_cmd"); + + if (!objc_super_template) + objc_super_template = build_super_template (); + + method_slot = 0; /* reset for multiple classes per file */ + + implementation_context = class; + + /* for a category, class_name is really the name of the class that + the following set of methods will be associated with...we must + find the interface so that can derive the objects template */ + + if (!(implementation_template = lookup_interface (class_name))) + { + error ("Cannot find interface declaration for `%s'", + IDENTIFIER_POINTER (class_name)); + exit (1); + } + } + return class; +} + +tree +continue_class (class) + tree class; +{ + if (TREE_CODE (class) == IMPLEMENTATION_TYPE + || TREE_CODE (class) == CATEGORY_TYPE) + { + struct imp_entry *impEntry; + tree ivar_context; + + /* check consistency of the instance variables. */ + + if (CLASS_IVARS (class)) + check_ivars (implementation_template, class); + + /* code generation */ + + ivar_context = build_private_template (implementation_template); + + if (!objc_class_template) + build_class_template (); + + if (!(impEntry = (struct imp_entry *)xmalloc (sizeof (struct imp_entry)))) + perror ("unable to allocate in objc-tree.c"); + + impEntry->next = imp_list; + impEntry->imp_context = class; + impEntry->imp_template = implementation_template; + + synth_forward_declarations (); + impEntry->class_decl = _OBJC_CLASS_decl; + impEntry->meta_decl = _OBJC_METACLASS_decl; + + /* append to front and increment count */ + imp_list = impEntry; + if (TREE_CODE (class) == IMPLEMENTATION_TYPE) + imp_count++; + else + cat_count++; + + return ivar_context; + } + else if (TREE_CODE (class) == INTERFACE_TYPE) + { + tree record = xref_tag (RECORD_TYPE, CLASS_NAME (class)); + + if (!TYPE_FIELDS (record)) + { + finish_struct (record, build_ivar_chain (class)); + CLASS_STATIC_TEMPLATE (class) = record; + + /* mark this record as a class template - for static typing */ + TREE_STATIC_TEMPLATE (record) = 1; + } + return NULLT; + } + else + return error_mark_node; +} + +/* + * this is called once we see the "@end" in an interface/implementation. + */ +void +finish_class (class) + tree class; +{ + if (TREE_CODE (class) == IMPLEMENTATION_TYPE) + { + /* all code generation is done in finish_objc */ + + if (implementation_template != implementation_context) + { + /* ensure that all method listed in the interface contain bodies! */ + check_methods (CLASS_CLS_METHODS (implementation_template), + CLASS_CLS_METHODS (implementation_context), '+'); + check_methods (CLASS_NST_METHODS (implementation_template), + CLASS_NST_METHODS (implementation_context), '-'); + } + } + else if (TREE_CODE (class) == CATEGORY_TYPE) + { + tree category = CLASS_CATEGORY_LIST (implementation_template); + + /* find the category interface from the class it is associated with */ + while (category) + { + if (CLASS_SUPER_NAME (class) == CLASS_SUPER_NAME (category)) + break; + category = CLASS_CATEGORY_LIST (category); + } + + if (category) + { + /* ensure that all method listed in the interface contain bodies! */ + check_methods (CLASS_CLS_METHODS (category), + CLASS_CLS_METHODS (implementation_context), '+'); + check_methods (CLASS_NST_METHODS (category), + CLASS_NST_METHODS (implementation_context), '-'); + } + } + else if (TREE_CODE (class) == INTERFACE_TYPE) + { + tree decl_specs; + + /* extern struct objc_object *_; */ + + sprintf (utlbuf, "_%s", IDENTIFIER_POINTER (CLASS_NAME (class))); + + decl_specs = build_tree_list (NULLT, ridpointers[(int) RID_EXTERN]); + decl_specs = tree_cons (NULLT, objc_object_reference, decl_specs); + define_decl (build1 (INDIRECT_REF, NULLT, get_identifier (utlbuf)), decl_specs); + } +} + +static void +encode_pointer (type, str, format) + tree type; + char *str; + int format; +{ + tree pointer_to = TREE_TYPE (type); + + if (TREE_CODE (pointer_to) == RECORD_TYPE) + { + if (TYPE_NAME (pointer_to) + && TREE_CODE (TYPE_NAME (pointer_to)) == IDENTIFIER_NODE) + { + char *name = IDENTIFIER_POINTER (TYPE_NAME (pointer_to)); + + if ((strcmp (name, TAG_OBJECT) == 0) || /* '@' */ + (TREE_STATIC_TEMPLATE (pointer_to))) + { + strcat (str, "@"); + return; + } + else if (strcmp (name, TAG_CLASS) == 0) /* '#' */ + { + strcat (str, "#"); + return; + } +#ifndef OBJC_INT_SELECTORS + else if (strcmp (name, TAG_SELECTOR) == 0) /* ':' */ + { + strcat (str, ":"); + return; + } +#endif /* OBJC_INT_SELECTORS */ + } + } + else if (TREE_CODE (pointer_to) == INTEGER_TYPE + && TYPE_MODE (pointer_to) == QImode) + { + strcat (str, "*"); + return; + } + + /* we have a type that does not get special treatment... */ + + /* NeXT extension */ + strcat (str, "^"); + encode_type (pointer_to, str, format); +} + +static void +encode_array (type, str, format) + tree type; + char *str; + int format; +{ + tree anIntCst = TYPE_SIZE (type); + tree array_of = TREE_TYPE (type); + + /* An incomplete array is treated like a pointer. */ + if (anIntCst == NULL) + { + /* split for obvious reasons. North-Keys 30 Mar 1991 */ + encode_pointer (type, str, format); + return; + } + + sprintf (str + strlen (str), "[%d", + TREE_INT_CST_LOW (anIntCst) + / TREE_INT_CST_LOW (TYPE_SIZE (array_of))); + encode_type (array_of, str, format); + strcat (str, "]"); + return; +} + +static void +encode_aggregate (type, str, format) + tree type; + char *str; + int format; +{ + enum tree_code code = TREE_CODE (type); + + switch (code) + { + case RECORD_TYPE: + { + if (str[strlen (str)-1] == '^') + { + /* we have a reference - this is a NeXT extension */ + if (TYPE_NAME (type) + && (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE)) + sprintf (str + strlen (str), "{%s}", + IDENTIFIER_POINTER (TYPE_NAME (type))); + else /* we have an untagged structure or a typedef */ + sprintf (str + strlen (str), "{?}"); + } + else + { + tree fields = TYPE_FIELDS (type); + + if (format == OBJC_ENCODE_INLINE_DEFS) + { + strcat (str, "{"); + for ( ; fields; fields = TREE_CHAIN (fields)) + encode_field_decl (fields, str, format); + strcat (str, "}"); + } + else + { + if (TYPE_NAME (type) + && (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE)) + sprintf (str + strlen (str), "{%s}", + IDENTIFIER_POINTER (TYPE_NAME (type))); + else /* we have an untagged structure or a typedef */ + sprintf (str + strlen (str), "{?}"); + } + } + break; + } + case UNION_TYPE: + { + if (str[strlen (str)-1] == '^') + { + if (TYPE_NAME (type) + && (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE)) + /* we have a reference - this is a NeXT extension */ + sprintf (str + strlen (str), "(%s)", + IDENTIFIER_POINTER (TYPE_NAME (type))); + else /* we have an untagged structure */ + sprintf (str + strlen (str), "(?)"); + } + else + { + tree fields = TYPE_FIELDS (type); + + if (format == OBJC_ENCODE_INLINE_DEFS) + { + strcat (str, "("); + for ( ; fields; fields = TREE_CHAIN (fields)) + encode_field_decl (fields, str, format); + strcat (str, ")"); + } + else + { + if (TYPE_NAME (type) && + (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE)) + /* we have a reference - this is a NeXT extension */ + sprintf (str + strlen (str), "(%s)", + IDENTIFIER_POINTER (TYPE_NAME (type))); + else /* we have an untagged structure */ + sprintf (str + strlen (str), "(?)"); + } + } + break; + } + case ENUMERAL_TYPE: + strcat (str, "i"); + break; + } +} + +/* + * support bitfields, the current version of Objective-C does not support + * them. the string will consist of one or more "b:n"'s where n is an + * integer describing the width of the bitfield. Currently, classes in + * the kit implement a method "-(char *)describeBitfieldStruct:" that + * simulates this...if they do not implement this method, the archiver + * assumes the bitfield is 16 bits wide (padded if necessary) and packed + * according to the GNU compiler. After looking at the "kit", it appears + * that all classes currently rely on this default behavior, rather than + * hand generating this string (which is tedious). + */ +static void +encode_bitfield (width, str, format) + int width; + char *str; + int format; +{ + sprintf (str + strlen (str), "b%d", width); +} + +/* + * format will be: + * + * OBJC_ENCODE_INLINE_DEFS or OBJC_ENCODE_DONT_INLINE_DEFS + */ +static void +encode_type (type, str, format) + tree type; + char *str; + int format; +{ + enum tree_code code = TREE_CODE (type); + + if (code == INTEGER_TYPE) + { + if (TREE_INT_CST_LOW (TYPE_MIN_VALUE (type)) == 0) + { + /* unsigned integer types */ + + if (TYPE_MODE (type) == QImode) /* 'C' */ + strcat (str, "C"); + else if (TYPE_MODE (type) == HImode) /* 'S' */ + strcat (str, "S"); + else if (TYPE_MODE (type) == SImode) + { + if (type == long_unsigned_type_node) + strcat (str, "L"); /* 'L' */ + else + strcat (str, "I"); /* 'I' */ + } + } + else /* signed integer types */ + { + if (TYPE_MODE (type) == QImode) /* 'c' */ + strcat (str, "c"); + else if (TYPE_MODE (type) == HImode) /* 's' */ + strcat (str, "s"); + else if (TYPE_MODE (type) == SImode) /* 'i' */ + { + if (type == long_integer_type_node) + strcat (str, "l"); /* 'l' */ + else + strcat (str, "i"); /* 'i' */ + } + } + } + else if (code == REAL_TYPE) + { + /* floating point types */ + + if (TYPE_MODE (type) == SFmode) /* 'f' */ + strcat (str, "f"); + else if (TYPE_MODE (type) == DFmode + || TYPE_MODE (type) == TFmode) /* 'd' */ + strcat (str, "d"); + } + + else if (code == VOID_TYPE) /* 'v' */ + strcat (str, "v"); + + else if (code == ARRAY_TYPE) + encode_array (type, str, format); + + else if (code == POINTER_TYPE) + encode_pointer (type, str, format); + + else if (code == RECORD_TYPE || code == UNION_TYPE || code == ENUMERAL_TYPE) + encode_aggregate (type, str, format); + + else if (code == FUNCTION_TYPE) /* '?' */ + strcat (str, "?"); +} + +static void +encode_field_decl (field_decl, str, format) + tree field_decl; + char *str; + int format; +{ + if (DECL_BIT_FIELD (field_decl)) + encode_bitfield (DECL_FRAME_SIZE (field_decl), str, format); + else + encode_type (TREE_TYPE (field_decl), str, format); +} + +static tree +expr_last (complex_expr) + tree complex_expr; +{ + tree next; + + if (complex_expr) + while (next = TREE_OPERAND (complex_expr, 0)) + complex_expr = next; + return complex_expr; +} + +/* The selector of the current method, + or NULL if we aren't compiling a method. */ + +tree +maybe_objc_method_name (decl) + tree decl; +{ + if (method_context) + return METHOD_SEL_NAME (method_context); + else + return 0; +} + +/* + * Transform a method definition into a function definition as follows: + * + * - synthesize the first two arguments, "self" and "_cmd". + */ + +void +start_method_def (method) + tree method; +{ + tree decl_specs; + + /* required to implement _msgSuper () */ + method_context = method; + _OBJC_SUPER_decl = NULLT; + + pushlevel (0); /* must be called BEFORE "start_function ()" */ + + /* generate prototype declarations for arguments..."new-style" */ + + if (TREE_CODE (method_context) == INSTANCE_METHOD_DECL) + decl_specs = build_tree_list (NULLT, _PRIVATE_record); + else + /* really a `struct objc_class *'...however we allow people to + assign to self...which changes its type midstream. + */ + decl_specs = build_tree_list (NULLT, objc_object_reference); + + push_parm_decl (build_tree_list (decl_specs, + build1 (INDIRECT_REF, NULLT, self_id))); + +#ifdef OBJC_INT_SELECTORS + decl_specs = build_tree_list (NULLT, ridpointers[(int) RID_UNSIGNED]); + decl_specs = tree_cons (NULLT, ridpointers[(int) RID_INT], decl_specs); + push_parm_decl (build_tree_list (decl_specs, _cmd_id)); +#else /* not OBJC_INT_SELECTORS */ + decl_specs = build_tree_list (NULLT, + xref_tag (RECORD_TYPE, + get_identifier (TAG_SELECTOR))); + push_parm_decl (build_tree_list (decl_specs, + build1 (INDIRECT_REF, NULLT, _cmd_id))); +#endif /* not OBJC_INT_SELECTORS */ + + /* generate argument delclarations if a keyword_decl */ + if (METHOD_SEL_ARGS (method)) + { + tree arglist = METHOD_SEL_ARGS (method); + do + { + tree arg_spec = TREE_PURPOSE (TREE_TYPE (arglist)); + tree arg_decl = TREE_VALUE (TREE_TYPE (arglist)); + + if (arg_decl) + { + tree last_expr = expr_last (arg_decl); + + /* unite the abstract decl with its name */ + TREE_OPERAND (last_expr, 0) = KEYWORD_ARG_NAME (arglist); + push_parm_decl (build_tree_list (arg_spec, arg_decl)); + /* unhook...restore the abstract declarator */ + TREE_OPERAND (last_expr, 0) = NULLT; + } + else + push_parm_decl (build_tree_list (arg_spec, KEYWORD_ARG_NAME (arglist))); + + arglist = TREE_CHAIN (arglist); + } + while (arglist); + } + + if (METHOD_ADD_ARGS (method) > (tree)1) + { + /* we have a variable length selector - in "prototype" format */ + tree akey = TREE_PURPOSE (METHOD_ADD_ARGS (method)); + while (akey) + { + /* this must be done prior to calling pushdecl (). pushdecl () is + * going to change our chain on us... + */ + tree nextkey = TREE_CHAIN (akey); + pushdecl (akey); + akey = nextkey; + } + } +} + +static void +error_with_method (message, mtype, method) + char *message; + char mtype; + tree method; +{ + count_error (0); + fprintf (stderr, "%s:%d: ", + DECL_SOURCE_FILE (method), DECL_SOURCE_LINE (method)); + bzero (errbuf, BUFSIZE); + fprintf (stderr, "%s `%c%s'\n", message, mtype, gen_method_decl (method, errbuf)); +} + +static void +warn_with_method (message, mtype, method) + char *message; + char mtype; + tree method; +{ + count_error (1); + fprintf (stderr, "%s:%d: ", + DECL_SOURCE_FILE (method), DECL_SOURCE_LINE (method)); + bzero (errbuf, BUFSIZE); + fprintf (stderr, "%s `%c%s'\n", message, mtype, gen_method_decl (method, errbuf)); +} + +/* return 1 if `method' is consistent with `proto' */ + +static int +comp_method_with_proto (method, proto) + tree method, proto; +{ + static tree function_type = 0; + + /* create a function_type node once */ + if (!function_type) + { + struct obstack *ambient_obstack = current_obstack; + + current_obstack = &permanent_obstack; + function_type = make_node (FUNCTION_TYPE); + current_obstack = ambient_obstack; + } + + /* install argument types - normally set by "build_function_type ()". */ + TYPE_ARG_TYPES (function_type) = get_arg_type_list (proto, METHOD_DEF, 0); + + /* install return type */ + TREE_TYPE (function_type) = groktypename (TREE_TYPE (proto)); + + return comptypes (TREE_TYPE (METHOD_DEFINITION (method)), function_type); +} + +/* return 1 if `proto1' is consistent with `proto2' */ + +static int +comp_proto_with_proto (proto1, proto2) + tree proto1, proto2; +{ + static tree function_type1 = 0, function_type2 = 0; + + /* create a couple function_type node's once */ + if (!function_type1) + { + struct obstack *ambient_obstack = current_obstack; + + current_obstack = &permanent_obstack; + function_type1 = make_node (FUNCTION_TYPE); + function_type2 = make_node (FUNCTION_TYPE); + current_obstack = ambient_obstack; + } + + /* install argument types - normally set by "build_function_type ()". */ + TYPE_ARG_TYPES (function_type1) = get_arg_type_list (proto1, METHOD_REF, 0); + TYPE_ARG_TYPES (function_type2) = get_arg_type_list (proto2, METHOD_REF, 0); + + /* install return type */ + TREE_TYPE (function_type1) = groktypename (TREE_TYPE (proto1)); + TREE_TYPE (function_type2) = groktypename (TREE_TYPE (proto2)); + + return comptypes (function_type1, function_type2); +} + +/* + * - generate an identifier for the function. the format is "_n_cls", + * where 1 <= n <= nMethods, and cls is the name the implementation we + * are processing. + * - install the return type from the method declaration. + * - if we have a prototype, check for type consistency. + */ +static void +really_start_method (method, parmlist) + tree method, parmlist; +{ + tree sc_spec, ret_spec, ret_decl, decl_specs; + tree method_decl, method_id; + char buf[256]; + + /* synth the storage class & assemble the return type */ + sc_spec = tree_cons (NULLT, ridpointers[(int) RID_STATIC], NULLT); + ret_spec = TREE_PURPOSE (TREE_TYPE (method)); + decl_specs = chainon (sc_spec, ret_spec); + + if (TREE_CODE (implementation_context) == IMPLEMENTATION_TYPE) +#ifdef OBJC_GEN_METHOD_LABEL + OBJC_GEN_METHOD_LABEL (buf, + TREE_CODE (method) == INSTANCE_METHOD_DECL, + IDENTIFIER_POINTER (CLASS_NAME (implementation_context)), + NULL, + IDENTIFIER_POINTER (METHOD_SEL_NAME (method))); +#else + sprintf (buf, "_%d_%s", ++method_slot, + IDENTIFIER_POINTER (CLASS_NAME (implementation_context))); +#endif + else /* we have a category */ +#ifdef OBJC_GEN_METHOD_LABEL + OBJC_GEN_METHOD_LABEL (buf, + TREE_CODE (method) == INSTANCE_METHOD_DECL, + IDENTIFIER_POINTER (CLASS_NAME (implementation_context)), + IDENTIFIER_POINTER (CLASS_SUPER_NAME (implementation_context)), + IDENTIFIER_POINTER (METHOD_SEL_NAME (method))); +#else + sprintf (buf, "_%d_%s_%s", ++method_slot, + IDENTIFIER_POINTER (CLASS_NAME (implementation_context)), + IDENTIFIER_POINTER (CLASS_SUPER_NAME (implementation_context))); +#endif + + method_id = get_identifier (buf); + + method_decl = build_nt (CALL_EXPR, method_id, parmlist, NULLT); + + /* check the delclarator portion of the return type for the method */ + if (ret_decl = TREE_VALUE (TREE_TYPE (method))) + { + /* + * unite the complex decl (specified in the abstract decl) with the + * function decl just synthesized...(int *), (int (*)()), (int (*)[]). + */ + tree save_expr = expr_last (ret_decl); + + TREE_OPERAND (save_expr, 0) = method_decl; + method_decl = ret_decl; + /* fool the parser into thinking it is starting a function */ + start_function (decl_specs, method_decl, 0); + /* unhook...this has the effect of restoring the abstract declarator */ + TREE_OPERAND (save_expr, 0) = NULLT; + } + else + { + TREE_VALUE (TREE_TYPE (method)) = method_decl; + /* fool the parser into thinking it is starting a function */ + start_function (decl_specs, method_decl, 0); + /* unhook...this has the effect of restoring the abstract declarator */ + TREE_VALUE (TREE_TYPE (method)) = NULLT; + } + + METHOD_DEFINITION (method) = current_function_decl; + + /* check consistency...start_function (), pushdecl (), duplicate_decls (). */ + + if (implementation_template != implementation_context) + { + tree chain, proto; + + if (TREE_CODE (method) == INSTANCE_METHOD_DECL) + chain = CLASS_NST_METHODS (implementation_template); + else + chain = CLASS_CLS_METHODS (implementation_template); + + if (proto = lookup_method (chain, METHOD_SEL_NAME (method))) + { + if (!comp_method_with_proto (method, proto)) + { + fprintf (stderr, "%s: In method `%s'\n", input_filename, + IDENTIFIER_POINTER (METHOD_SEL_NAME (method))); + if (TREE_CODE (method) == INSTANCE_METHOD_DECL) + { + error_with_method ("conflicting types for", '-', method); + error_with_method ("previous declaration of", '-', proto); + } + else + { + error_with_method ("conflicting types for", '+', method); + error_with_method ("previous declaration of", '+', proto); + } + } + } + } +} + +/* + * the following routine is always called...this "architecture" is to + * accommodate "old-style" variable length selectors. + * + * - a:a b:b // prototype ; id c; id d; // old-style + */ +void +continue_method_def () +{ + tree parmlist; + + if (METHOD_ADD_ARGS (method_context) == (tree)1) + /* + * we have a `, ...' immediately following the selector. + */ + parmlist = get_parm_info (0); + else + parmlist = get_parm_info (1); /* place a `void_at_end' */ + + /* set self_decl from the first argument...this global is used by + * build_ivar_reference ().build_indirect_ref (). + */ + self_decl = TREE_PURPOSE (parmlist); + + poplevel (0, 0, 0); /* must be called BEFORE "start_function ()" */ + + really_start_method (method_context, parmlist); + + store_parm_decls (); /* must be called AFTER "start_function ()" */ +} + +void +add_objc_decls () +{ + if (!_OBJC_SUPER_decl) + _OBJC_SUPER_decl = start_decl (get_identifier (_TAG_SUPER), + build_tree_list (NULLT, objc_super_template), 0); + + /* this prevents `unused variable' warnings when compiling with `-Wall' */ + TREE_USED (_OBJC_SUPER_decl) = 1; +} + +/* + * _n_Method (id self, SEL sel, ...) + * { + * struct objc_super _S; + * + * _msgSuper ((_S.self = self, _S.class = _cls, &_S), ...); + * } + */ +tree +get_super_receiver () +{ + if (method_context) + { + tree super_expr, super_expr_list; + + /* set receiver to self */ + super_expr = build_component_ref (_OBJC_SUPER_decl, self_id); + super_expr = build_modify_expr (super_expr, NOP_EXPR, self_decl); + super_expr_list = build_tree_list (NULLT, super_expr); + + /* set class to begin searching */ + super_expr = build_component_ref (_OBJC_SUPER_decl, get_identifier ("class")); + + if (TREE_CODE (implementation_context) == IMPLEMENTATION_TYPE) + { + /* [_cls, __cls]Super are "pre-built" in synth_foward_declarations () */ + + if (TREE_CODE (method_context) == INSTANCE_METHOD_DECL) + super_expr = build_modify_expr (super_expr, NOP_EXPR, _clsSuper_ref); + else + super_expr = build_modify_expr (super_expr, NOP_EXPR, __clsSuper_ref); + } + else /* we have a category... */ + { + tree params, super_name = CLASS_SUPER_NAME (implementation_template); + tree funcCall; + + if (!super_name) /* Barf if super used in a category of Object. */ + { + error("no super class declared in interface for `%s'", + IDENTIFIER_POINTER (CLASS_NAME (implementation_template))); + return error_mark_node; + } + + add_class_reference (super_name); + + params = build_tree_list (NULLT, + my_build_string (IDENTIFIER_LENGTH (super_name) + 1, + IDENTIFIER_POINTER (super_name))); + + if (TREE_CODE (method_context) == INSTANCE_METHOD_DECL) + funcCall = build_function_call (objc_getClass_decl, params); + else + funcCall = build_function_call (objc_getMetaClass_decl, params); + + /* cast! */ + TREE_TYPE (funcCall) = TREE_TYPE (_clsSuper_ref); + super_expr = build_modify_expr (super_expr, NOP_EXPR, funcCall); + } + chainon (super_expr_list, build_tree_list (NULL_TREE, super_expr)); + + super_expr = build_unary_op (ADDR_EXPR, _OBJC_SUPER_decl, 0); + chainon (super_expr_list, build_tree_list (NULL_TREE, super_expr)); + + return build_compound_expr (super_expr_list); + } + else + { + error ("[super ...] must appear in a method context"); + return error_mark_node; + } +} + +static tree +encode_method_def (func_decl) + tree func_decl; +{ + tree parms; + int stack_size = 0; + + bzero (utlbuf, BUFSIZE); + + /* return type */ + encode_type (TREE_TYPE (TREE_TYPE (func_decl)), utlbuf, + OBJC_ENCODE_DONT_INLINE_DEFS); + /* stack size */ + for (parms = DECL_ARGUMENTS (func_decl); parms; + parms = TREE_CHAIN (parms)) + stack_size += TREE_INT_CST_LOW (TYPE_SIZE (DECL_ARG_TYPE (parms))) + / BITS_PER_UNIT; + + sprintf (&utlbuf[strlen (utlbuf)], "%d", stack_size); + + /* argument types */ + for (parms = DECL_ARGUMENTS (func_decl); parms; + parms = TREE_CHAIN (parms)) + { + int offset_in_bytes; + + /* type */ + encode_type (TREE_TYPE (parms), utlbuf, OBJC_ENCODE_DONT_INLINE_DEFS); + + /* compute offset */ + if (GET_CODE (DECL_INCOMING_RTL (parms)) == MEM) + { + rtx addr = XEXP (DECL_INCOMING_RTL (parms), 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; + + /* 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 + if (TREE_TYPE (parms) != DECL_ARG_TYPE (parms)) + offset_in_bytes += (GET_MODE_SIZE (TYPE_MODE (DECL_ARG_TYPE (parms))) + - GET_MODE_SIZE (GET_MODE (DECL_RTL (parms)))); +#endif + } + else + offset_in_bytes = 0; + + /* The "+ 4" is a total hack to account for the return pc and + saved fp on the 68k. We should redefine this format! */ + sprintf (&utlbuf[strlen (utlbuf)], "%d", offset_in_bytes + 8); + } + + return get_identifier (utlbuf); +} + +void +finish_method_def () +{ + METHOD_ENCODING (method_context) = + encode_method_def (current_function_decl); + + finish_function (0); + + /* this must be done AFTER finish_function, since the optimizer may + find "may be used before set" errors. */ + method_context = NULLT; /* required to implement _msgSuper () */ +} + +int +lang_report_error_function (decl) + tree decl; +{ + if (method_context) + { + fprintf (stderr, "In method `%s'\n", + IDENTIFIER_POINTER (METHOD_SEL_NAME (method_context))); + return 1; + } + else + return 0; +} + +static int +is_complex_decl (type) + tree type; +{ + return (TREE_CODE (type) == ARRAY_TYPE + || TREE_CODE (type) == FUNCTION_TYPE + || TREE_CODE (type) == POINTER_TYPE); +} + + +/* Code to convert a decl node into text for a declaration in C. */ + +static char tmpbuf[256]; + +static void +adorn_decl (decl, str) + tree decl; + char *str; +{ + enum tree_code code = TREE_CODE (decl); + + if (code == ARRAY_REF) + { + tree anIntCst = TREE_OPERAND (decl, 1); + + sprintf (str + strlen (str), "[%d]", TREE_INT_CST_LOW (anIntCst)); + } + else if (code == ARRAY_TYPE) + { + tree anIntCst = TYPE_SIZE (decl); + tree array_of = TREE_TYPE (decl); + + sprintf (str + strlen (str), "[%d]", + TREE_INT_CST_LOW (anIntCst)/TREE_INT_CST_LOW (TYPE_SIZE (array_of))); + } + else if (code == CALL_EXPR) + strcat (str, "()"); + else if (code == FUNCTION_TYPE) + { + tree chain = TYPE_ARG_TYPES (decl); /* a list of types */ + strcat (str, "("); + while (chain && TREE_VALUE (chain) != void_type_node) + { + gen_declaration (TREE_VALUE (chain), str); + chain = TREE_CHAIN (chain); + if (chain && TREE_VALUE (chain) != void_type_node) + strcat (str, ","); + } + strcat (str, ")"); + } + else + { + strcpy (tmpbuf, "*"); strcat (tmpbuf, str); + strcpy (str, tmpbuf); + } +} + +static char * +gen_declarator (decl, buf, name) + tree decl; + char *buf; + char *name; +{ + if (decl) + { + enum tree_code code = TREE_CODE (decl); + char *str; + tree op; + int wrap = 0; + + switch (code) + { + case ARRAY_REF: case INDIRECT_REF: case CALL_EXPR: + { + op = TREE_OPERAND (decl, 0); + + /* we have a pointer to a function or array...(*)(), (*)[] */ + if ((code == ARRAY_REF || code == CALL_EXPR) && + (op && TREE_CODE (op) == INDIRECT_REF)) + wrap = 1; + + str = gen_declarator (op, buf, name); + + if (wrap) + { + strcpy (tmpbuf, "("); strcat (tmpbuf, str); strcat (tmpbuf, ")"); + strcpy (str, tmpbuf); + } + + adorn_decl (decl, str); + break; + } + case ARRAY_TYPE: case FUNCTION_TYPE: case POINTER_TYPE: + { + str = strcpy (buf, name); + + /* this clause is done iteratively...rather than recursively */ + do + { + op = is_complex_decl (TREE_TYPE (decl)) + ? TREE_TYPE (decl) + : NULLT; + + adorn_decl (decl, str); + + /* we have a pointer to a function or array...(*)(), (*)[] */ + if ((code == POINTER_TYPE) && + (op && (TREE_CODE (op) == FUNCTION_TYPE + || TREE_CODE (op) == ARRAY_TYPE))) + { + strcpy (tmpbuf, "("); strcat (tmpbuf, str); strcat (tmpbuf, ")"); + strcpy (str, tmpbuf); + } + + decl = is_complex_decl (TREE_TYPE (decl)) + ? TREE_TYPE (decl) + : NULLT; + } + while (decl && (code = TREE_CODE (decl))); + + break; + } + case IDENTIFIER_NODE: + /* will only happen if we are processing a "raw" expr-decl. */ + return strcpy (buf, IDENTIFIER_POINTER (decl)); + } + + return str; + } + else /* we have an abstract declarator or a _DECL node */ + { + return strcpy (buf, name); + } +} + +static void +gen_declspecs (declspecs, buf, raw) + tree declspecs; + char *buf; + int raw; +{ + if (raw) + { + tree chain; + + for (chain = declspecs; chain; chain = TREE_CHAIN (chain)) + { + tree aspec = TREE_VALUE (chain); + + if (TREE_CODE (aspec) == IDENTIFIER_NODE) + strcat (buf, IDENTIFIER_POINTER (aspec)); + else if (TREE_CODE (aspec) == RECORD_TYPE) + { + if (TYPE_NAME (aspec)) + { + if (!TREE_STATIC_TEMPLATE (aspec)) + strcat (buf, "struct "); + strcat (buf, IDENTIFIER_POINTER (TYPE_NAME (aspec))); + } + else + strcat (buf, "untagged struct"); + } + else if (TREE_CODE (aspec) == UNION_TYPE) + { + if (TYPE_NAME (aspec)) + { + if (!TREE_STATIC_TEMPLATE (aspec)) + strcat (buf, "union "); + strcat (buf, IDENTIFIER_POINTER (TYPE_NAME (aspec))); + } + else + strcat (buf, "untagged union"); + } + else if (TREE_CODE (aspec) == ENUMERAL_TYPE) + { + if (TYPE_NAME (aspec)) + { + if (!TREE_STATIC_TEMPLATE (aspec)) + strcat (buf, "enum "); + strcat (buf, IDENTIFIER_POINTER (TYPE_NAME (aspec))); + } + else + strcat (buf, "untagged enum"); + } + strcat (buf, " "); + } + } + else + switch (TREE_CODE (declspecs)) + { + /* type specifiers */ + + case INTEGER_TYPE: /* signed integer types */ + + if (declspecs == short_integer_type_node) /* 's' */ + strcat (buf, "short int "); + else if (declspecs == integer_type_node) /* 'i' */ + strcat (buf, "int "); + else if (declspecs == long_integer_type_node) /* 'l' */ + strcat (buf, "long int "); + else if (declspecs == signed_char_type_node || /* 'c' */ + declspecs == char_type_node) + strcat (buf, "char "); + + /* unsigned integer types */ + + else if (declspecs == short_unsigned_type_node) /* 'S' */ + strcat (buf, "unsigned short "); + else if (declspecs == unsigned_type_node) /* 'I' */ + strcat (buf, "unsigned int "); + else if (declspecs == long_unsigned_type_node) /* 'L' */ + strcat (buf, "unsigned long "); + else if (declspecs == unsigned_char_type_node) /* 'C' */ + strcat (buf, "unsigned char "); + break; + + case REAL_TYPE: /* floating point types */ + + if (declspecs == float_type_node) /* 'f' */ + strcat (buf, "float "); + else if (declspecs == double_type_node) /* 'd' */ + strcat (buf, "double "); + else if (declspecs == long_double_type_node) /* 'd' */ + strcat (buf, "long double "); + break; + + case RECORD_TYPE: + if (!TREE_STATIC_TEMPLATE (declspecs)) + strcat (buf, "struct "); + if (TYPE_NAME (declspecs) && + (TREE_CODE (TYPE_NAME (declspecs)) == IDENTIFIER_NODE)) + { + strcat (buf, IDENTIFIER_POINTER (TYPE_NAME (declspecs))); + strcat (buf, " "); + } + break; + case UNION_TYPE: + strcat (buf, "union "); + if (TYPE_NAME (declspecs) && + (TREE_CODE (TYPE_NAME (declspecs)) == IDENTIFIER_NODE)) + { + strcat (buf, IDENTIFIER_POINTER (TYPE_NAME (declspecs))); + strcat (buf, " "); + } + break; + case ENUMERAL_TYPE: + strcat (buf, "enum "); + if (TYPE_NAME (declspecs) && + (TREE_CODE (TYPE_NAME (declspecs)) == IDENTIFIER_NODE)) + { + strcat (buf, IDENTIFIER_POINTER (TYPE_NAME (declspecs))); + strcat (buf, " "); + } + break; + case VOID_TYPE: + strcat (buf, "void "); + } +} + +static char * +gen_declaration (atype_or_adecl, buf) + tree atype_or_adecl; + char *buf; +{ + char declbuf[256]; + + if (TREE_CODE (atype_or_adecl) == TREE_LIST) + { + tree declspecs; /* "identifier_node", "record_type" */ + tree declarator; /* "array_ref", "indirect_ref", "call_expr"... */ + + /* we have a "raw", abstract delclarator (typename) */ + declarator = TREE_VALUE (atype_or_adecl); + declspecs = TREE_PURPOSE (atype_or_adecl); + + gen_declspecs (declspecs, buf, 1); + strcat (buf, gen_declarator (declarator, declbuf, "")); + } + else + { + tree atype; + tree declspecs; /* "integer_type", "real_type", "record_type"... */ + tree declarator; /* "array_type", "function_type", "pointer_type". */ + + if (TREE_CODE (atype_or_adecl) == FIELD_DECL + || TREE_CODE (atype_or_adecl) == PARM_DECL + || TREE_CODE (atype_or_adecl) == FUNCTION_DECL) + atype = TREE_TYPE (atype_or_adecl); + else + atype = atype_or_adecl; /* assume we have a *_type node */ + + if (is_complex_decl (atype)) + { + tree chain; + + /* get the declaration specifier...it is at the end of the list */ + declarator = chain = atype; + do + chain = TREE_TYPE (chain); /* not TREE_CHAIN (chain); */ + while (is_complex_decl (chain)); + declspecs = chain; + } + else + { + declspecs = atype; + declarator = NULLT; + } + + gen_declspecs (declspecs, buf, 0); + + if (TREE_CODE (atype_or_adecl) == FIELD_DECL + || TREE_CODE (atype_or_adecl) == PARM_DECL + || TREE_CODE (atype_or_adecl) == FUNCTION_DECL) + { + if (declarator) + { + strcat (buf, gen_declarator (declarator, declbuf, + IDENTIFIER_POINTER (DECL_NAME (atype_or_adecl)))); + } + else + strcat (buf, IDENTIFIER_POINTER (DECL_NAME (atype_or_adecl))); + } + else + { + strcat (buf, gen_declarator (declarator, declbuf, "")); + } + } + return buf; +} + +#define RAW_TYPESPEC(meth) (TREE_VALUE (TREE_PURPOSE (TREE_TYPE (meth)))) + +static char * +gen_method_decl (method, buf) + tree method; + char *buf; +{ + tree chain; + + if (RAW_TYPESPEC (method) != objc_object_reference) + { + strcpy (buf, "("); + gen_declaration (TREE_TYPE (method), buf); + strcat (buf, ")"); + } + + chain = METHOD_SEL_ARGS (method); + if (chain) + { /* we have a chain of keyword_decls */ + do + { + if (KEYWORD_KEY_NAME (chain)) + strcat (buf, IDENTIFIER_POINTER (KEYWORD_KEY_NAME (chain))); + + strcat (buf, ":"); + if (RAW_TYPESPEC (chain) != objc_object_reference) + { + strcat (buf, "("); + gen_declaration (TREE_TYPE (chain), buf); + strcat (buf, ")"); + } + strcat (buf, IDENTIFIER_POINTER (KEYWORD_ARG_NAME (chain))); + if (chain = TREE_CHAIN (chain)) + strcat (buf, " "); + } + while (chain); + + if (METHOD_ADD_ARGS (method) == (tree)1) + strcat (buf, ", ..."); + else if (METHOD_ADD_ARGS (method)) + { /* we have a tree list node as generate by `get_parm_info ()' */ + chain = TREE_PURPOSE (METHOD_ADD_ARGS (method)); + /* know we have a chain of parm_decls */ + while (chain) + { + strcat (buf, ", "); + gen_declaration (chain, buf); + chain = TREE_CHAIN (chain); + } + } + } + else /* we have a unary selector */ + { + strcat (buf, IDENTIFIER_POINTER (METHOD_SEL_NAME (method))); + } + + return buf; +} + +void +gen_prototype (fp, decl) + FILE *fp; + tree decl; +{ + /* we have a function definition - generate prototype */ + bzero (errbuf, BUFSIZE); + gen_declaration (decl, errbuf); + fprintf (fp, "%s;\n", errbuf); +} +/* + * debug info... + */ +static void +dump_interface (fp, chain) + FILE *fp; + tree chain; +{ + char *buf = (char *)xmalloc (256); + char *my_name = IDENTIFIER_POINTER (CLASS_NAME (chain)); + tree ivar_decls = CLASS_RAW_IVARS (chain); + tree nst_methods = CLASS_NST_METHODS (chain); + tree cls_methods = CLASS_CLS_METHODS (chain); + + fprintf (fp, "\n@interface %s", my_name); + + if (CLASS_SUPER_NAME (chain)) + { + char *super_name = IDENTIFIER_POINTER (CLASS_SUPER_NAME (chain)); + fprintf (fp, " : %s\n", super_name); + } + else + fprintf (fp, "\n"); + + if (ivar_decls) + { + fprintf (fp, "{\n"); + do + { + bzero (buf, 256); + fprintf (fp, "\t%s;\n", gen_declaration (ivar_decls, buf)); + ivar_decls = TREE_CHAIN (ivar_decls); + } + while (ivar_decls); + fprintf (fp, "}\n"); + } + + while (nst_methods) + { + bzero (buf, 256); + fprintf (fp, "- %s;\n", gen_method_decl (nst_methods, buf)); + nst_methods = TREE_CHAIN (nst_methods); + } + + while (cls_methods) + { + bzero (buf, 256); + fprintf (fp, "+ %s;\n", gen_method_decl (cls_methods, buf)); + cls_methods = TREE_CHAIN (cls_methods); + } + fprintf (fp, "\n@end"); +} + +void +init_objc () +{ + /* Add the special tree codes of Objective C to the tables. */ + + tree_code_type + = (char **) realloc (tree_code_type, + sizeof (char *) * LAST_OBJC_TREE_CODE); + tree_code_length + = (int *) realloc (tree_code_length, + sizeof (int) * LAST_OBJC_TREE_CODE); + tree_code_name + = (char **) realloc (tree_code_name, + sizeof (char *) * LAST_OBJC_TREE_CODE); + bcopy (objc_tree_code_type, + tree_code_type + (int) LAST_AND_UNUSED_TREE_CODE, + (((int) LAST_OBJC_TREE_CODE - (int) LAST_AND_UNUSED_TREE_CODE) + * sizeof (char *))); + bcopy (objc_tree_code_length, + tree_code_length + (int) LAST_AND_UNUSED_TREE_CODE, + (((int) LAST_OBJC_TREE_CODE - (int) LAST_AND_UNUSED_TREE_CODE) + * sizeof (int))); + bcopy (objc_tree_code_name, + tree_code_name + (int) LAST_AND_UNUSED_TREE_CODE, + (((int) LAST_OBJC_TREE_CODE - (int) LAST_AND_UNUSED_TREE_CODE) + * sizeof (char *))); + + errbuf = (char *)xmalloc (BUFSIZE); + utlbuf = (char *)xmalloc (BUFSIZE); + hash_init (); + synth_module_prologue (); +} + +void +finish_objc () +{ + struct imp_entry *impent; + tree chain; + + generate_forward_declaration_to_string_table (); + +#ifdef OBJC_PROLOGUE + OBJC_PROLOGUE; +#endif + + if (implementation_context || sel_refdef_chain) + generate_objc_symtab_decl (); + + for (impent = imp_list; impent; impent = impent->next) + { + implementation_context = impent->imp_context; + implementation_template = impent->imp_template; + + _OBJC_CLASS_decl = impent->class_decl; + _OBJC_METACLASS_decl = impent->meta_decl; + + if (TREE_CODE (implementation_context) == IMPLEMENTATION_TYPE) + { + /* all of the following reference the string pool... */ + generate_ivar_lists (); + generate_dispatch_tables (); + generate_shared_structures (); + } + else + { + generate_dispatch_tables (); + generate_category (implementation_context); + } + } + + if (sel_ref_chain) + build_selector_translation_table (); + + if (implementation_context || sel_refdef_chain) + { + /* Arrange for Objc data structures to be initialized at run time. */ + + char *init_name = build_module_descriptor (); + assemble_constructor (init_name); + } + + /* dump the string table last */ + + if (sel_refdef_chain) + { + build_message_selector_pool (); + } + + /* dump the class references...this forces the appropriate classes + to be linked into the executable image, preserving unix archive + semantics...this can be removed when we move to a more dynamically + linked environment + */ + for (chain = cls_ref_chain; chain; chain = TREE_CHAIN (chain)) + { + tree decl; +#if 0 /* Grossly unportable. */ + sprintf (utlbuf, ".reference .objc_class_name_%s", + IDENTIFIER_POINTER (TREE_VALUE (chain))); + assemble_asm (my_build_string (strlen (utlbuf) + 1, utlbuf)); +#endif + sprintf (utlbuf, ".objc_class_name_%s", + IDENTIFIER_POINTER (TREE_VALUE (chain))); + assemble_global (utlbuf); + /* Make a decl for this name, so we can use its address in a tree. */ + decl = build_decl (VAR_DECL, get_identifier (utlbuf), char_type_node); + TREE_EXTERNAL (decl) = 1; + TREE_PUBLIC (decl) = 1; + + pushdecl (decl); + rest_of_decl_compilation (decl, 0, 0, 0); + + /* Output a constant to reference this address. */ + output_constant (build1 (ADDR_EXPR, string_type_node, decl), + int_size_in_bytes (string_type_node)); + } + + for (impent = imp_list; impent; impent = impent->next) + { + implementation_context = impent->imp_context; + implementation_template = impent->imp_template; + + if (TREE_CODE (impent->imp_context) == IMPLEMENTATION_TYPE) + { +#if 0 /* Grossly unportable. People should know better that to + assume such things about assembler syntax! */ + sprintf (utlbuf, ".objc_class_name_%s=0", + IDENTIFIER_POINTER (CLASS_NAME (impent->imp_context))); + assemble_asm (my_build_string (strlen (utlbuf) + 1, utlbuf)); +#endif + sprintf (utlbuf, ".objc_class_name_%s", + IDENTIFIER_POINTER (CLASS_NAME (impent->imp_context))); + assemble_global (utlbuf); + assemble_label (utlbuf); + } + else if (TREE_CODE (impent->imp_context) == CATEGORY_TYPE) + { + /* Do the same for categories. Even though no references to these + symbols are generated automatically by the compiler, it gives + you a handle to pull them into an archive by hand. */ +#if 0 /* Grossly unportable. */ + sprintf (utlbuf, ".objc_category_name_%s_%s=0", + IDENTIFIER_POINTER (CLASS_NAME (impent->imp_context)), + IDENTIFIER_POINTER (CLASS_SUPER_NAME (impent->imp_context))); + assemble_asm (my_build_string (strlen (utlbuf) + 1, utlbuf)); +#endif + sprintf (utlbuf, ".objc_category_name_%s_%s", + IDENTIFIER_POINTER (CLASS_NAME (impent->imp_context)), + IDENTIFIER_POINTER (CLASS_SUPER_NAME (impent->imp_context))); + assemble_global (utlbuf); + assemble_label (utlbuf); + } + } +#if 0 /* If GAS has such a bug, let's fix it. */ + /*** this fixes a gross bug in the assembler...it `expects' #APP to have + *** a matching #NO_APP, or it crashes (sometimes). app_disable () will + *** insure this is the case. 5/19/89, s.naroff. + ***/ + if (cls_ref_chain || imp_list) + app_disable (); +#endif + + if (flag_gen_declaration) + { + add_class (implementation_context); + dump_interface (gen_declaration_file, implementation_context); + } + if (warn_selector) + { + int slot; + + /* Run through the selector hash tables and print a warning for any + selector which has multiple methods. */ + + for (slot = 0; slot < SIZEHASHTABLE; slot++) + { + hash hsh; + + for (hsh = cls_method_hash_list[slot]; hsh; hsh = hsh->next) + { + if (hsh->list) + { + tree meth = hsh->key; + char type = (TREE_CODE (meth) == INSTANCE_METHOD_DECL) + ? '-' : '+'; + attr loop; + + warning ("potential selector conflict for method `%s'", + IDENTIFIER_POINTER (METHOD_SEL_NAME (meth))); + warn_with_method ("found", type, meth); + for (loop = hsh->list; loop; loop = loop->next) + warn_with_method ("found", type, loop->value); + } + } + } + + for (slot = 0; slot < SIZEHASHTABLE; slot++) + { + hash hsh; + + for (hsh = nst_method_hash_list[slot]; hsh; hsh = hsh->next) + { + if (hsh->list) + { + tree meth = hsh->key; + char type = (TREE_CODE (meth) == INSTANCE_METHOD_DECL) + ? '-' : '+'; + attr loop; + + warning ("potential selector conflict for method `%s'", + IDENTIFIER_POINTER (METHOD_SEL_NAME (meth))); + warn_with_method ("found", type, meth); + for (loop = hsh->list; loop; loop = loop->next) + warn_with_method ("found", type, loop->value); + } + } + } + } +} + +#ifdef DEBUG + +static void +objc_debug (fp) + FILE *fp; +{ + char *buf = (char *)xmalloc (256); + + { /* dump function prototypes */ + tree loop = _OBJC_MODULES_decl; + + fprintf (fp, "\n\nfunction prototypes:\n"); + while (loop) + { + if (TREE_CODE (loop) == FUNCTION_DECL && DECL_INITIAL (loop)) + { + /* we have a function definition - generate prototype */ + bzero (errbuf, BUFSIZE); + gen_declaration (loop, errbuf); + fprintf (fp, "%s;\n", errbuf); + } + loop = TREE_CHAIN (loop); + } + } + { /* dump global chains */ + tree loop; + int i, index = 0, offset = 0; + hash hashlist; + + for (i = 0; i < SIZEHASHTABLE; i++) + { + if (hashlist = nst_method_hash_list[i]) + { + fprintf (fp, "\n\nnst_method_hash_list[%d]:\n", i); + do + { + bzero (buf, 256); + fprintf (fp, "-%s;\n", gen_method_decl (hashlist->key, buf)); + hashlist = hashlist->next; + } + while (hashlist); + } + } + for (i = 0; i < SIZEHASHTABLE; i++) + { + if (hashlist = cls_method_hash_list[i]) + { + fprintf (fp, "\n\ncls_method_hash_list[%d]:\n", i); + do + { + bzero (buf, 256); + fprintf (fp, "-%s;\n", gen_method_decl (hashlist->key, buf)); + hashlist = hashlist->next; + } + while (hashlist); + } + } + fprintf (fp, "\nsel_refdef_chain:\n"); + for (loop = sel_refdef_chain; loop; loop = TREE_CHAIN (loop)) + { + fprintf (fp, "(index: %4d offset: %4d) %s\n", index, offset, + IDENTIFIER_POINTER (TREE_VALUE (loop))); + index++; + /* add one for the '\0' character */ + offset += IDENTIFIER_LENGTH (TREE_VALUE (loop)) + 1; + } + fprintf (fp, "\n (max_selector_index: %4d.\n", max_selector_index); + } +} +#endif + +void +print_lang_statistics () +{ +} -- 2.30.2