blob: b1762cfcce142a37877f7c09d34cedbc4cc2079d [file] [log] [blame]
/* Implement classes and message passing for Objective C.
Copyright (C) 1992, 1993, 1994, 1995, 1997, 1998, 1999, 2000,
2001, 2002, 2003 Free Software Foundation, Inc.
Contributed by Steve Naroff.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
/* Purpose: This module implements the Objective-C 4.0 language.
compatibility issues (with the Stepstone translator):
- does not recognize the following 3.3 constructs.
@requires, @classes, @messages, = (...)
- methods with variable arguments must conform to ANSI standard.
- tagged structure definitions that appear in BOTH the interface
and implementation are not allowed.
- public/private: all instance variables are public within the
context of the implementation...I consider this to be a bug in
the translator.
- statically allocated objects are not supported. the user will
receive an error if this service is requested.
code generation `options':
*/
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
#include "rtl.h"
#include "tm_p.h"
#include "expr.h"
#include "c-tree.h"
#include "c-common.h"
#include "flags.h"
#include "objc-act.h"
#include "input.h"
#include "except.h"
#include "function.h"
#include "output.h"
#include "toplev.h"
#include "ggc.h"
#include "varray.h"
#include "debug.h"
#include "target.h"
#include "diagnostic.h"
#include "cgraph.h"
#define OBJC_VOID_AT_END build_tree_list (NULL_TREE, void_type_node)
/* This is the default way of generating a method name. */
/* I am not sure it is really correct.
Perhaps there's a danger that it will make name conflicts
if method names contain underscores. -- rms. */
#ifndef OBJC_GEN_METHOD_LABEL
#define OBJC_GEN_METHOD_LABEL(BUF, IS_INST, CLASS_NAME, CAT_NAME, SEL_NAME, NUM) \
do { \
char *temp; \
sprintf ((BUF), "_%s_%s_%s_%s", \
((IS_INST) ? "i" : "c"), \
(CLASS_NAME), \
((CAT_NAME)? (CAT_NAME) : ""), \
(SEL_NAME)); \
for (temp = (BUF); *temp; temp++) \
if (*temp == ':') *temp = '_'; \
} while (0)
#endif
/* These need specifying. */
#ifndef OBJC_FORWARDING_STACK_OFFSET
#define OBJC_FORWARDING_STACK_OFFSET 0
#endif
#ifndef OBJC_FORWARDING_MIN_OFFSET
#define OBJC_FORWARDING_MIN_OFFSET 0
#endif
/* Set up for use of obstacks. */
#include "obstack.h"
/* This obstack is used to accumulate the encoding of a data type. */
static struct obstack util_obstack;
/* This points to the beginning of obstack contents, so we can free
the whole contents. */
char *util_firstobj;
/* The version identifies which language generation and runtime
the module (file) was compiled for, and is recorded in the
module descriptor. */
#define OBJC_VERSION (flag_next_runtime ? 5 : 8)
#define PROTOCOL_VERSION 2
/* (Decide if these can ever be validly changed.) */
#define OBJC_ENCODE_INLINE_DEFS 0
#define OBJC_ENCODE_DONT_INLINE_DEFS 1
/*** Private Interface (procedures) ***/
/* Used by compile_file. */
static void init_objc (void);
static void finish_objc (void);
/* Code generation. */
static void synth_module_prologue (void);
static tree objc_build_constructor (tree, tree);
static rtx build_module_descriptor (void);
static tree init_module_descriptor (tree);
static tree build_objc_method_call (int, tree, tree, tree, tree);
static void generate_strings (void);
static tree get_proto_encoding (tree);
static void build_selector_translation_table (void);
static tree objc_add_static_instance (tree, tree);
static void build_objc_exception_stuff (void);
static tree objc_declare_variable (enum rid, tree, tree, tree);
static tree objc_enter_block (void);
static tree objc_exit_block (void);
static void objc_build_try_enter_fragment (void);
static void objc_build_try_exit_fragment (void);
static void objc_build_extract_fragment (void);
static tree objc_build_extract_expr (void);
static tree build_ivar_template (void);
static tree build_method_template (void);
static tree build_private_template (tree);
static void build_class_template (void);
static void build_selector_template (void);
static void build_category_template (void);
static tree lookup_method_in_hash_lists (tree, int);
static void build_super_template (void);
static tree build_category_initializer (tree, tree, tree, tree, tree, tree);
static tree build_protocol_initializer (tree, tree, tree, tree, tree);
static void synth_forward_declarations (void);
static int ivar_list_length (tree);
static tree get_class_ivars (tree, int);
static void generate_ivar_lists (void);
static void generate_dispatch_tables (void);
static void generate_shared_structures (void);
static tree generate_protocol_list (tree);
static void generate_forward_declaration_to_string_table (void);
static void build_protocol_reference (tree);
static tree build_keyword_selector (tree);
static tree synth_id_with_class_suffix (const char *, tree);
static void generate_static_references (void);
static int check_methods_accessible (tree, tree, int);
static void encode_aggregate_within (tree, int, int, int, int);
static const char *objc_demangle (const char *);
static void objc_expand_function_end (void);
/* Hash tables to manage the global pool of method prototypes. */
hash *nst_method_hash_list = 0;
hash *cls_method_hash_list = 0;
static size_t hash_func (tree);
static void hash_init (void);
static void hash_enter (hash *, tree);
static hash hash_lookup (hash *, tree);
static void hash_add_attr (hash, tree);
static tree lookup_method (tree, tree);
static tree lookup_method_static (tree, tree, int);
static void add_method_to_hash_list (hash *, tree);
static tree add_class (tree);
static void add_category (tree, tree);
static inline tree lookup_category (tree, tree);
enum string_section
{
class_names, /* class, category, protocol, module names */
meth_var_names, /* method and variable names */
meth_var_types /* method and variable type descriptors */
};
static tree add_objc_string (tree, enum string_section);
static tree get_objc_string_decl (tree, enum string_section);
static tree build_objc_string_decl (enum string_section);
static tree build_selector_reference_decl (void);
/* Protocol additions. */
static tree add_protocol (tree);
static tree lookup_protocol (tree);
static void check_protocol_recursively (tree, tree);
static tree lookup_and_install_protocols (tree);
/* Type encoding. */
static void encode_type_qualifiers (tree);
static void encode_pointer (tree, int, int);
static void encode_array (tree, int, int);
static void encode_aggregate (tree, int, int);
static void encode_next_bitfield (int);
static void encode_gnu_bitfield (int, tree, int);
static void encode_type (tree, int, int);
static void encode_field_decl (tree, int, int);
static void really_start_method (tree, tree);
static int comp_method_with_proto (tree, tree);
static int objc_types_are_equivalent (tree, tree);
static int comp_proto_with_proto (tree, tree);
static tree get_arg_type_list (tree, int, int);
static tree objc_expr_last (tree);
static void synth_self_and_ucmd_args (void);
/* Utilities for debugging and error diagnostics. */
static void warn_with_method (const char *, int, tree);
static void error_with_ivar (const char *, tree, tree);
static char *gen_method_decl (tree, char *);
static char *gen_declaration (tree, char *);
static void gen_declaration_1 (tree, char *);
static char *gen_declarator (tree, char *, const char *);
static int is_complex_decl (tree);
static void adorn_decl (tree, char *);
static void dump_interface (FILE *, tree);
/* Everything else. */
static tree define_decl (tree, tree);
static tree lookup_method_in_protocol_list (tree, tree, int);
static tree lookup_protocol_in_reflist (tree, tree);
static tree create_builtin_decl (enum tree_code, tree, const char *);
static void setup_string_decl (void);
static int check_string_class_template (void);
static tree my_build_string (int, const char *);
static void build_objc_symtab_template (void);
static tree init_def_list (tree);
static tree init_objc_symtab (tree);
static tree build_metadata_decl (const char *, tree);
static void forward_declare_categories (void);
static void generate_objc_symtab_decl (void);
static tree build_selector (tree);
static tree build_typed_selector_reference (tree, tree);
static tree build_selector_reference (tree);
static tree build_class_reference_decl (void);
static void add_class_reference (tree);
static tree build_protocol_template (void);
static tree build_descriptor_table_initializer (tree, tree);
static tree build_method_prototype_list_template (tree, int);
static tree build_method_prototype_template (void);
static tree objc_method_parm_type (tree);
static int objc_encoded_type_size (tree);
static tree encode_method_prototype (tree);
static tree generate_descriptor_table (tree, const char *, int, tree, tree);
static void generate_method_descriptors (tree);
static void generate_protocol_references (tree);
static void generate_protocols (void);
static void check_ivars (tree, tree);
static tree build_ivar_list_template (tree, int);
static tree build_method_list_template (tree, int);
static tree build_ivar_list_initializer (tree, tree);
static tree generate_ivars_list (tree, const char *, int, tree);
static tree build_dispatch_table_initializer (tree, tree);
static tree generate_dispatch_table (tree, const char *, int, tree);
static tree build_shared_structure_initializer (tree, tree, tree, tree,
tree, int, tree, tree, tree);
static void generate_category (tree);
static int is_objc_type_qualifier (tree);
static tree adjust_type_for_id_default (tree);
static tree check_duplicates (hash, int, int);
static tree receiver_is_class_object (tree, int, int);
static int check_methods (tree, tree, int);
static int conforms_to_protocol (tree, tree);
static void check_protocol (tree, const char *, const char *);
static void check_protocols (tree, const char *, const char *);
static void gen_declspecs (tree, char *, int);
static void generate_classref_translation_entry (tree);
static void handle_class_ref (tree);
static void generate_struct_by_value_array (void)
ATTRIBUTE_NORETURN;
static void mark_referenced_methods (void);
static void generate_objc_image_info (void);
/*** Private Interface (data) ***/
/* Reserved tag definitions. */
#define TYPE_ID "id"
#define TAG_OBJECT "objc_object"
#define TAG_CLASS "objc_class"
#define TAG_SUPER "objc_super"
#define TAG_SELECTOR "objc_selector"
#define UTAG_CLASS "_objc_class"
#define UTAG_IVAR "_objc_ivar"
#define UTAG_IVAR_LIST "_objc_ivar_list"
#define UTAG_METHOD "_objc_method"
#define UTAG_METHOD_LIST "_objc_method_list"
#define UTAG_CATEGORY "_objc_category"
#define UTAG_MODULE "_objc_module"
#define UTAG_SYMTAB "_objc_symtab"
#define UTAG_SUPER "_objc_super"
#define UTAG_SELECTOR "_objc_selector"
#define UTAG_PROTOCOL "_objc_protocol"
#define UTAG_METHOD_PROTOTYPE "_objc_method_prototype"
#define UTAG_METHOD_PROTOTYPE_LIST "_objc__method_prototype_list"
/* Note that the string object global name is only needed for the
NeXT runtime. */
#define STRING_OBJECT_GLOBAL_FORMAT "_%sClassReference"
#define PROTOCOL_OBJECT_CLASS_NAME "Protocol"
static const char *TAG_GETCLASS;
static const char *TAG_GETMETACLASS;
static const char *TAG_MSGSEND;
static const char *TAG_MSGSENDSUPER;
/* The NeXT Objective-C messenger may have two extra entry points, for use
when returning a structure. */
static const char *TAG_MSGSEND_STRET;
static const char *TAG_MSGSENDSUPER_STRET;
static const char *TAG_EXECCLASS;
static const char *default_constant_string_class_name;
/* Runtime metadata flags. */
#define CLS_FACTORY 0x0001L
#define CLS_META 0x0002L
#define OBJC_MODIFIER_STATIC 0x00000001
#define OBJC_MODIFIER_FINAL 0x00000002
#define OBJC_MODIFIER_PUBLIC 0x00000004
#define OBJC_MODIFIER_PRIVATE 0x00000008
#define OBJC_MODIFIER_PROTECTED 0x00000010
#define OBJC_MODIFIER_NATIVE 0x00000020
#define OBJC_MODIFIER_SYNCHRONIZED 0x00000040
#define OBJC_MODIFIER_ABSTRACT 0x00000080
#define OBJC_MODIFIER_VOLATILE 0x00000100
#define OBJC_MODIFIER_TRANSIENT 0x00000200
#define OBJC_MODIFIER_NONE_SPECIFIED 0x80000000
#define TAG_MSGSEND_NONNIL "objc_msgSendNonNil"
#define TAG_MSGSEND_NONNIL_STRET "objc_msgSendNonNil_stret"
#define TAG_EXCEPTIONEXTRACT "objc_exception_extract"
#define TAG_EXCEPTIONTRYENTER "objc_exception_try_enter"
#define TAG_EXCEPTIONTRYEXIT "objc_exception_try_exit"
#define TAG_EXCEPTIONMATCH "objc_exception_match"
#define TAG_EXCEPTIONTHROW "objc_exception_throw"
#define TAG_SYNCENTER "objc_sync_enter"
#define TAG_SYNCEXIT "objc_sync_exit"
#define TAG_SETJMP "_setjmp"
#define TAG_RETURN_STRUCT "objc_return_struct"
#define UTAG_EXCDATA "_objc_exception_data"
#define UTAG_EXCDATA_VAR "_stackExceptionData"
#define UTAG_CAUGHTEXC_VAR "_caughtException"
#define UTAG_RETHROWEXC_VAR "_rethrowException"
#define UTAG_EVALONCE_VAR "_eval_once"
struct val_stack {
long val;
struct val_stack *next;
};
static struct val_stack *catch_count_stack, *exc_binding_stack;
/* useful for debugging */
static int if_nesting_count;
static int blk_nesting_count;
static void val_stack_push (struct val_stack **, long);
static void val_stack_pop (struct val_stack **);
/* The OCTI_... enumeration itself is in objc/objc-act.h. */
tree objc_global_trees[OCTI_MAX];
static void handle_impent (struct imp_entry *);
struct imp_entry *imp_list = 0;
int imp_count = 0; /* `@implementation' */
int cat_count = 0; /* `@category' */
/* Use to generate method labels. */
static int method_slot = 0;
#define BUFSIZE 1024
static char *errbuf; /* Buffer for error diagnostics */
/* Data imported from tree.c. */
extern enum debug_info_type write_symbols;
/* Data imported from toplev.c. */
extern const char *dump_base_name;
static int flag_typed_selectors;
FILE *gen_declaration_file;
/* Tells "encode_pointer/encode_aggregate" whether we are generating
type descriptors for instance variables (as opposed to methods).
Type descriptors for instance variables contain more information
than methods (for static typing and embedded structures). */
static int generating_instance_variables = 0;
/* Some platforms pass small structures through registers versus
through an invisible pointer. Determine at what size structure is
the transition point between the two possibilities. */
static void
generate_struct_by_value_array (void)
{
tree type;
tree field_decl, field_decl_chain;
int i, j;
int aggregate_in_mem[32];
int found = 0;
/* Presumably no platform passes 32 byte structures in a register. */
for (i = 1; i < 32; i++)
{
char buffer[5];
/* Create an unnamed struct that has `i' character components */
type = start_struct (RECORD_TYPE, NULL_TREE);
strcpy (buffer, "c1");
field_decl = create_builtin_decl (FIELD_DECL,
char_type_node,
buffer);
field_decl_chain = field_decl;
for (j = 1; j < i; j++)
{
sprintf (buffer, "c%d", j + 1);
field_decl = create_builtin_decl (FIELD_DECL,
char_type_node,
buffer);
chainon (field_decl_chain, field_decl);
}
finish_struct (type, field_decl_chain, NULL_TREE);
aggregate_in_mem[i] = aggregate_value_p (type, 0);
if (!aggregate_in_mem[i])
found = 1;
}
/* We found some structures that are returned in registers instead of memory
so output the necessary data. */
if (found)
{
for (i = 31; i >= 0; i--)
if (!aggregate_in_mem[i])
break;
printf ("#define OBJC_MAX_STRUCT_BY_VALUE %d\n\n", i);
/* The first member of the structure is always 0 because we don't handle
structures with 0 members */
printf ("static int struct_forward_array[] = {\n 0");
for (j = 1; j <= i; j++)
printf (", %d", aggregate_in_mem[j]);
printf ("\n};\n");
}
exit (0);
}
bool
objc_init (void)
{
if (c_objc_common_init () == false)
return false;
/* Force the line number back to 0; check_newline will have
raised it to 1, which will make the builtin functions appear
not to be built in. */
input_line = 0;
/* If gen_declaration desired, open the output file. */
if (flag_gen_declaration)
{
register char * const dumpname = concat (dump_base_name, ".decl", NULL);
gen_declaration_file = fopen (dumpname, "w");
if (gen_declaration_file == 0)
fatal_error ("can't open %s: %m", dumpname);
free (dumpname);
}
if (flag_next_runtime)
{
TAG_GETCLASS = "objc_getClass";
TAG_GETMETACLASS = "objc_getMetaClass";
TAG_MSGSEND = "objc_msgSend";
TAG_MSGSENDSUPER = "objc_msgSendSuper";
TAG_MSGSEND_STRET = "objc_msgSend_stret";
TAG_MSGSENDSUPER_STRET = "objc_msgSendSuper_stret";
TAG_EXECCLASS = "__objc_execClass";
default_constant_string_class_name = "NSConstantString";
}
else
{
TAG_GETCLASS = "objc_get_class";
TAG_GETMETACLASS = "objc_get_meta_class";
TAG_MSGSEND = "objc_msg_lookup";
TAG_MSGSENDSUPER = "objc_msg_lookup_super";
/* GNU runtime does not provide special functions to support
structure-returning methods. */
TAG_EXECCLASS = "__objc_exec_class";
default_constant_string_class_name = "NXConstantString";
flag_typed_selectors = 1;
}
objc_ellipsis_node = make_node (ERROR_MARK);
init_objc ();
if (print_struct_values)
generate_struct_by_value_array ();
return true;
}
void
finish_file (void)
{
mark_referenced_methods ();
c_objc_common_finish_file ();
/* Finalize Objective-C runtime data. No need to generate tables
and code if only checking syntax. */
if (!flag_syntax_only)
finish_objc ();
if (gen_declaration_file)
fclose (gen_declaration_file);
}
static tree
define_decl (tree declarator, tree declspecs)
{
tree decl = start_decl (declarator, declspecs, 0, NULL_TREE);
finish_decl (decl, NULL_TREE, NULL_TREE);
return decl;
}
static tree
lookup_method_in_protocol_list (tree rproto_list, tree sel_name,
int class_meth)
{
tree rproto, p;
tree fnd = 0;
for (rproto = rproto_list; rproto; rproto = TREE_CHAIN (rproto))
{
p = TREE_VALUE (rproto);
if (TREE_CODE (p) == PROTOCOL_INTERFACE_TYPE)
{
if ((fnd = lookup_method (class_meth
? PROTOCOL_CLS_METHODS (p)
: PROTOCOL_NST_METHODS (p), sel_name)))
;
else if (PROTOCOL_LIST (p))
fnd = lookup_method_in_protocol_list (PROTOCOL_LIST (p),
sel_name, class_meth);
}
else
{
; /* An identifier...if we could not find a protocol. */
}
if (fnd)
return fnd;
}
return 0;
}
static tree
lookup_protocol_in_reflist (tree rproto_list, tree lproto)
{
tree rproto, p;
/* Make sure the protocol is supported by the object on the rhs. */
if (TREE_CODE (lproto) == PROTOCOL_INTERFACE_TYPE)
{
tree fnd = 0;
for (rproto = rproto_list; rproto; rproto = TREE_CHAIN (rproto))
{
p = TREE_VALUE (rproto);
if (TREE_CODE (p) == PROTOCOL_INTERFACE_TYPE)
{
if (lproto == p)
fnd = lproto;
else if (PROTOCOL_LIST (p))
fnd = lookup_protocol_in_reflist (PROTOCOL_LIST (p), lproto);
}
if (fnd)
return fnd;
}
}
else
{
; /* An identifier...if we could not find a protocol. */
}
return 0;
}
/* Return 1 if LHS and RHS are compatible types for assignment or
various other operations. Return 0 if they are incompatible, and
return -1 if we choose to not decide (because the types are really
just C types, not ObjC specific ones). When the operation is
REFLEXIVE (typically comparisons), check for compatibility in
either direction; when it's not (typically assignments), don't.
This function is called in two cases: when both lhs and rhs are
pointers to records (in which case we check protocols too), and
when both lhs and rhs are records (in which case we check class
inheritance only).
Warnings about classes/protocols not implementing a protocol are
emitted here (multiple of those warnings might be emitted for a
single line!); generic warnings about incompatible assignments and
lacks of casts in comparisons are/must be emitted by the caller if
we return 0.
*/
int
objc_comptypes (tree lhs, tree rhs, int reflexive)
{
/* New clause for protocols. */
/* Here we manage the case of a POINTER_TYPE = POINTER_TYPE. We only
manage the ObjC ones, and leave the rest to the C code. */
if (TREE_CODE (lhs) == POINTER_TYPE
&& TREE_CODE (TREE_TYPE (lhs)) == RECORD_TYPE
&& TREE_CODE (rhs) == POINTER_TYPE
&& TREE_CODE (TREE_TYPE (rhs)) == RECORD_TYPE)
{
int lhs_is_proto = IS_PROTOCOL_QUALIFIED_ID (lhs);
int rhs_is_proto = IS_PROTOCOL_QUALIFIED_ID (rhs);
if (lhs_is_proto)
{
tree lproto, lproto_list = TYPE_PROTOCOL_LIST (lhs);
tree rproto, rproto_list;
tree p;
/* <Protocol> = <Protocol> */
if (rhs_is_proto)
{
rproto_list = TYPE_PROTOCOL_LIST (rhs);
if (!reflexive)
{
/* An assignment between objects of type 'id
<Protocol>'; make sure the protocol on the lhs is
supported by the object on the rhs. */
for (lproto = lproto_list; lproto;
lproto = TREE_CHAIN (lproto))
{
p = TREE_VALUE (lproto);
rproto = lookup_protocol_in_reflist (rproto_list, p);
if (!rproto)
warning
("object does not conform to the `%s' protocol",
IDENTIFIER_POINTER (PROTOCOL_NAME (p)));
}
return 1;
}
else
{
/* Obscure case - a comparison between two objects
of type 'id <Protocol>'. Check that either the
protocol on the lhs is supported by the object on
the rhs, or viceversa. */
/* Check if the protocol on the lhs is supported by the
object on the rhs. */
for (lproto = lproto_list; lproto;
lproto = TREE_CHAIN (lproto))
{
p = TREE_VALUE (lproto);
rproto = lookup_protocol_in_reflist (rproto_list, p);
if (!rproto)
{
/* Check failed - check if the protocol on the rhs
is supported by the object on the lhs. */
for (rproto = rproto_list; rproto;
rproto = TREE_CHAIN (rproto))
{
p = TREE_VALUE (rproto);
lproto = lookup_protocol_in_reflist (lproto_list,
p);
if (!lproto)
{
/* This check failed too: incompatible */
return 0;
}
}
return 1;
}
}
return 1;
}
}
/* <Protocol> = <class> * */
else if (TYPED_OBJECT (TREE_TYPE (rhs)))
{
tree rname = OBJC_TYPE_NAME (TREE_TYPE (rhs));
tree rinter;
/* Make sure the protocol is supported by the object on
the rhs. */
for (lproto = lproto_list; lproto; lproto = TREE_CHAIN (lproto))
{
p = TREE_VALUE (lproto);
rproto = 0;
rinter = lookup_interface (rname);
while (rinter && !rproto)
{
tree cat;
rproto_list = CLASS_PROTOCOL_LIST (rinter);
rproto = lookup_protocol_in_reflist (rproto_list, p);
/* If the underlying ObjC class does not have
the protocol we're looking for, check for "one-off"
protocols (e.g., `NSObject<MyProt> *foo;') attached
to the rhs. */
if (!rproto)
{
rproto_list = TYPE_PROTOCOL_LIST (TREE_TYPE (rhs));
rproto = lookup_protocol_in_reflist (rproto_list, p);
}
/* Check for protocols adopted by categories. */
cat = CLASS_CATEGORY_LIST (rinter);
while (cat && !rproto)
{
rproto_list = CLASS_PROTOCOL_LIST (cat);
rproto = lookup_protocol_in_reflist (rproto_list, p);
cat = CLASS_CATEGORY_LIST (cat);
}
rinter = lookup_interface (CLASS_SUPER_NAME (rinter));
}
if (!rproto)
warning ("class `%s' does not implement the `%s' protocol",
IDENTIFIER_POINTER (OBJC_TYPE_NAME (TREE_TYPE (rhs))),
IDENTIFIER_POINTER (PROTOCOL_NAME (p)));
}
return 1;
}
/* <Protocol> = id */
else if (OBJC_TYPE_NAME (TREE_TYPE (rhs)) == objc_object_id)
{
return 1;
}
/* <Protocol> = Class */
else if (OBJC_TYPE_NAME (TREE_TYPE (rhs)) == objc_class_id)
{
return 0;
}
/* <Protocol> = ?? : let comptypes decide. */
return -1;
}
else if (rhs_is_proto)
{
/* <class> * = <Protocol> */
if (TYPED_OBJECT (TREE_TYPE (lhs)))
{
if (reflexive)
{
tree rname = OBJC_TYPE_NAME (TREE_TYPE (lhs));
tree rinter;
tree rproto, rproto_list = TYPE_PROTOCOL_LIST (rhs);
/* Make sure the protocol is supported by the object on
the lhs. */
for (rproto = rproto_list; rproto;
rproto = TREE_CHAIN (rproto))
{
tree p = TREE_VALUE (rproto);
tree lproto = 0;
rinter = lookup_interface (rname);
while (rinter && !lproto)
{
tree cat;
tree lproto_list = CLASS_PROTOCOL_LIST (rinter);
lproto = lookup_protocol_in_reflist (lproto_list, p);
/* If the underlying ObjC class does not
have the protocol we're looking for,
check for "one-off" protocols (e.g.,
`NSObject<MyProt> *foo;') attached to the
lhs. */
if (!lproto)
{
lproto_list = TYPE_PROTOCOL_LIST
(TREE_TYPE (lhs));
lproto = lookup_protocol_in_reflist
(lproto_list, p);
}
/* Check for protocols adopted by categories. */
cat = CLASS_CATEGORY_LIST (rinter);
while (cat && !lproto)
{
lproto_list = CLASS_PROTOCOL_LIST (cat);
lproto = lookup_protocol_in_reflist (lproto_list,
p);
cat = CLASS_CATEGORY_LIST (cat);
}
rinter = lookup_interface (CLASS_SUPER_NAME
(rinter));
}
if (!lproto)
warning ("class `%s' does not implement the `%s' protocol",
IDENTIFIER_POINTER (OBJC_TYPE_NAME
(TREE_TYPE (lhs))),
IDENTIFIER_POINTER (PROTOCOL_NAME (p)));
}
return 1;
}
else
return 0;
}
/* id = <Protocol> */
else if (OBJC_TYPE_NAME (TREE_TYPE (lhs)) == objc_object_id)
{
return 1;
}
/* Class = <Protocol> */
else if (OBJC_TYPE_NAME (TREE_TYPE (lhs)) == objc_class_id)
{
return 0;
}
/* ??? = <Protocol> : let comptypes decide */
else
{
return -1;
}
}
else
{
/* Attention: we shouldn't defer to comptypes here. One bad
side effect would be that we might loose the REFLEXIVE
information.
*/
lhs = TREE_TYPE (lhs);
rhs = TREE_TYPE (rhs);
}
}
if (TREE_CODE (lhs) != RECORD_TYPE || TREE_CODE (rhs) != RECORD_TYPE)
{
/* Nothing to do with ObjC - let immediately comptypes take
responsibility for checking. */
return -1;
}
/* `id' = `<class> *' `<class> *' = `id': always allow it.
Please note that
'Object *o = [[Object alloc] init]; falls
in the case <class> * = `id'.
*/
if ((OBJC_TYPE_NAME (lhs) == objc_object_id && TYPED_OBJECT (rhs))
|| (OBJC_TYPE_NAME (rhs) == objc_object_id && TYPED_OBJECT (lhs)))
return 1;
/* `id' = `Class', `Class' = `id' */
else if ((OBJC_TYPE_NAME (lhs) == objc_object_id
&& OBJC_TYPE_NAME (rhs) == objc_class_id)
|| (OBJC_TYPE_NAME (lhs) == objc_class_id
&& OBJC_TYPE_NAME (rhs) == objc_object_id))
return 1;
/* `<class> *' = `<class> *' */
else if (TYPED_OBJECT (lhs) && TYPED_OBJECT (rhs))
{
tree lname = OBJC_TYPE_NAME (lhs);
tree rname = OBJC_TYPE_NAME (rhs);
tree inter;
if (lname == rname)
return 1;
/* If the left hand side is a super class of the right hand side,
allow it. */
for (inter = lookup_interface (rname); inter;
inter = lookup_interface (CLASS_SUPER_NAME (inter)))
if (lname == CLASS_SUPER_NAME (inter))
return 1;
/* Allow the reverse when reflexive. */
if (reflexive)
for (inter = lookup_interface (lname); inter;
inter = lookup_interface (CLASS_SUPER_NAME (inter)))
if (rname == CLASS_SUPER_NAME (inter))
return 1;
return 0;
}
else
/* Not an ObjC type - let comptypes do the check. */
return -1;
}
/* Called from finish_decl. */
void
objc_check_decl (tree decl)
{
tree type = TREE_TYPE (decl);
if (TREE_CODE (type) != RECORD_TYPE)
return;
if (TYPE_NAME (type) && (type = is_class_name (TYPE_NAME (type))))
error ("statically allocated instance of Objective-C class `%s'",
IDENTIFIER_POINTER (type));
}
/* Implement static typing. At this point, we know we have an interface. */
tree
get_static_reference (tree interface, tree protocols)
{
tree type = xref_tag (RECORD_TYPE, interface);
if (protocols)
{
tree t, m = TYPE_MAIN_VARIANT (type);
t = copy_node (type);
/* Add this type to the chain of variants of TYPE. */
TYPE_NEXT_VARIANT (t) = TYPE_NEXT_VARIANT (m);
TYPE_NEXT_VARIANT (m) = t;
/* Look up protocols and install in lang specific list. Note
that the protocol list can have a different lifetime than T! */
SET_TYPE_PROTOCOL_LIST (t, lookup_and_install_protocols (protocols));
/* This forces a new pointer type to be created later
(in build_pointer_type)...so that the new template
we just created will actually be used...what a hack! */
if (TYPE_POINTER_TO (t))
TYPE_POINTER_TO (t) = NULL_TREE;
type = t;
}
return type;
}
tree
get_object_reference (tree protocols)
{
tree type_decl = lookup_name (objc_id_id);
tree type;
if (type_decl && TREE_CODE (type_decl) == TYPE_DECL)
{
type = TREE_TYPE (type_decl);
if (TYPE_MAIN_VARIANT (type) != id_type)
warning ("unexpected type for `id' (%s)",
gen_declaration (type, errbuf));
}
else
{
error ("undefined type `id', please import <objc/objc.h>");
return error_mark_node;
}
/* This clause creates a new pointer type that is qualified with
the protocol specification...this info is used later to do more
elaborate type checking. */
if (protocols)
{
tree t, m = TYPE_MAIN_VARIANT (type);
t = copy_node (type);
/* Add this type to the chain of variants of TYPE. */
TYPE_NEXT_VARIANT (t) = TYPE_NEXT_VARIANT (m);
TYPE_NEXT_VARIANT (m) = t;
/* Look up protocols...and install in lang specific list */
SET_TYPE_PROTOCOL_LIST (t, lookup_and_install_protocols (protocols));
/* This forces a new pointer type to be created later
(in build_pointer_type)...so that the new template
we just created will actually be used...what a hack! */
if (TYPE_POINTER_TO (t))
TYPE_POINTER_TO (t) = NULL_TREE;
type = t;
}
return type;
}
/* Check for circular dependencies in protocols. The arguments are
PROTO, the protocol to check, and LIST, a list of protocol it
conforms to. */
static void
check_protocol_recursively (tree proto, tree list)
{
tree p;
for (p = list; p; p = TREE_CHAIN (p))
{
tree pp = TREE_VALUE (p);
if (TREE_CODE (pp) == IDENTIFIER_NODE)
pp = lookup_protocol (pp);
if (pp == proto)
fatal_error ("protocol `%s' has circular dependency",
IDENTIFIER_POINTER (PROTOCOL_NAME (pp)));
if (pp)
check_protocol_recursively (proto, PROTOCOL_LIST (pp));
}
}
/* Look up PROTOCOLS, and return a list of those that are found.
If none are found, return NULL. */
static tree
lookup_and_install_protocols (tree protocols)
{
tree proto;
tree return_value = NULL_TREE;
for (proto = protocols; proto; proto = TREE_CHAIN (proto))
{
tree ident = TREE_VALUE (proto);
tree p = lookup_protocol (ident);
if (!p)
error ("cannot find protocol declaration for `%s'",
IDENTIFIER_POINTER (ident));
else
return_value = chainon (return_value,
build_tree_list (NULL_TREE, p));
}
return return_value;
}
/* Create and push a decl for a built-in external variable or field NAME.
CODE says which.
TYPE is its data type. */
static tree
create_builtin_decl (enum tree_code code, tree type, const char *name)
{
tree decl = build_decl (code, get_identifier (name), type);
if (code == VAR_DECL)
{
TREE_STATIC (decl) = 1;
make_decl_rtl (decl, 0);
pushdecl (decl);
DECL_ARTIFICIAL (decl) = 1;
}
return decl;
}
/* Find the decl for the constant string class. */
static void
setup_string_decl (void)
{
if (!string_class_decl)
{
if (!constant_string_global_id)
{
char *name;
size_t length;
/* %s in format will provide room for terminating null */
length = strlen (STRING_OBJECT_GLOBAL_FORMAT)
+ strlen (constant_string_class_name);
name = xmalloc (length);
sprintf (name, STRING_OBJECT_GLOBAL_FORMAT,
constant_string_class_name);
constant_string_global_id = get_identifier (name);
}
string_class_decl = lookup_name (constant_string_global_id);
}
}
/* Purpose: "play" parser, creating/installing representations
of the declarations that are required by Objective-C.
Model:
type_spec--------->sc_spec
(tree_list) (tree_list)
| |
| |
identifier_node identifier_node */
static void
synth_module_prologue (void)
{
tree temp_type;
/* Defined in `objc.h' */
objc_object_id = get_identifier (TAG_OBJECT);
objc_object_reference = xref_tag (RECORD_TYPE, objc_object_id);
id_type = build_pointer_type (objc_object_reference);
objc_id_id = get_identifier (TYPE_ID);
objc_class_id = get_identifier (TAG_CLASS);
objc_class_type = build_pointer_type (xref_tag (RECORD_TYPE, objc_class_id));
temp_type = get_identifier (PROTOCOL_OBJECT_CLASS_NAME);
objc_declare_class (tree_cons (NULL_TREE, temp_type, NULL_TREE));
protocol_type = build_pointer_type (xref_tag (RECORD_TYPE,
temp_type));
/* Declare type of selector-objects that represent an operation name. */
/* `struct objc_selector *' */
selector_type
= build_pointer_type (xref_tag (RECORD_TYPE,
get_identifier (TAG_SELECTOR)));
/* Forward declare type, or else the prototype for msgSendSuper will
complain. */
/* `struct objc_super *' */
super_type = build_pointer_type (xref_tag (RECORD_TYPE,
get_identifier (TAG_SUPER)));
/* id objc_msgSend (id, SEL, ...); */
temp_type
= build_function_type (id_type,
tree_cons (NULL_TREE, id_type,
tree_cons (NULL_TREE, selector_type,
NULL_TREE)));
if (! flag_next_runtime)
{
umsg_decl = build_decl (FUNCTION_DECL,
get_identifier (TAG_MSGSEND), temp_type);
DECL_EXTERNAL (umsg_decl) = 1;
TREE_PUBLIC (umsg_decl) = 1;
DECL_INLINE (umsg_decl) = 1;
DECL_ARTIFICIAL (umsg_decl) = 1;
make_decl_rtl (umsg_decl, NULL);
pushdecl (umsg_decl);
}
else
{
umsg_decl = builtin_function (TAG_MSGSEND,
temp_type, 0, NOT_BUILT_IN,
NULL, NULL_TREE);
/* id objc_msgSendNonNil (id, SEL, ...); */
umsg_nonnil_decl = builtin_function (TAG_MSGSEND_NONNIL,
temp_type, 0, NOT_BUILT_IN,
NULL, NULL_TREE);
}
/* id objc_msgSendSuper (struct objc_super *, SEL, ...); */
temp_type
= build_function_type (id_type,
tree_cons (NULL_TREE, super_type,
tree_cons (NULL_TREE, selector_type,
NULL_TREE)));
umsg_super_decl = builtin_function (TAG_MSGSENDSUPER,
temp_type, 0, NOT_BUILT_IN,
NULL, NULL_TREE);
/* The NeXT runtime defines the following additional entry points,
used for dispatching calls to methods returning structs:
#if defined(__cplusplus)
id objc_msgSend_stret(id self, SEL op, ...);
id objc_msgSendSuper_stret(struct objc_super *super, SEL op, ...);
#else
void objc_msgSend_stret(void * stretAddr, id self, SEL op, ...);
void objc_msgSendSuper_stret(void * stretAddr, struct objc_super *super,
SEL op, ...);
#endif
struct objc_return_struct objc_msgSendNonNil_stret(id self, SEL op, ...);
These prototypes appear in <objc/objc-runtime.h>; however, they
CANNOT BE USED DIRECTLY. In order to call one of the ..._stret
functions, the function must first be cast to a signature that
corresponds to the actual ObjC method being invoked. This is
what is done by the build_objc_method_call() routine below. */
if (flag_next_runtime)
{
tree objc_return_struct_type
= xref_tag (RECORD_TYPE,
get_identifier (TAG_RETURN_STRUCT));
tree stret_temp_type
= build_function_type (id_type,
tree_cons (NULL_TREE, id_type,
tree_cons (NULL_TREE, selector_type,
NULL_TREE)));
umsg_stret_decl = builtin_function (TAG_MSGSEND_STRET,
stret_temp_type, 0, NOT_BUILT_IN,
NULL, NULL_TREE);
stret_temp_type
= build_function_type (objc_return_struct_type,
tree_cons (NULL_TREE, id_type,
tree_cons (NULL_TREE, selector_type,
NULL_TREE)));
umsg_nonnil_stret_decl = builtin_function (TAG_MSGSEND_NONNIL_STRET,
stret_temp_type, 0, NOT_BUILT_IN,
NULL, NULL_TREE);
stret_temp_type
= build_function_type (id_type,
tree_cons (NULL_TREE, super_type,
tree_cons (NULL_TREE, selector_type,
NULL_TREE)));
umsg_super_stret_decl = builtin_function (TAG_MSGSENDSUPER_STRET,
stret_temp_type, 0, NOT_BUILT_IN, 0,
NULL_TREE);
}
/* id objc_getClass (const char *); */
temp_type = build_function_type (id_type,
tree_cons (NULL_TREE,
const_string_type_node,
OBJC_VOID_AT_END));
objc_get_class_decl
= builtin_function (TAG_GETCLASS, temp_type, 0, NOT_BUILT_IN,
NULL, NULL_TREE);
/* id objc_getMetaClass (const char *); */
objc_get_meta_class_decl
= builtin_function (TAG_GETMETACLASS, temp_type, 0, NOT_BUILT_IN, NULL, NULL_TREE);
build_super_template ();
if (flag_next_runtime)
build_objc_exception_stuff ();
/* static SEL _OBJC_SELECTOR_TABLE[]; */
if (! flag_next_runtime)
{
if (flag_typed_selectors)
{
/* Suppress outputting debug symbols, because
dbxout_init hasn'r been called yet. */
enum debug_info_type save_write_symbols = write_symbols;
const struct gcc_debug_hooks *const save_hooks = debug_hooks;
write_symbols = NO_DEBUG;
debug_hooks = &do_nothing_debug_hooks;
build_selector_template ();
temp_type = build_array_type (objc_selector_template, NULL_TREE);
write_symbols = save_write_symbols;
debug_hooks = save_hooks;
}
else
temp_type = build_array_type (selector_type, NULL_TREE);
layout_type (temp_type);
UOBJC_SELECTOR_TABLE_decl
= create_builtin_decl (VAR_DECL, temp_type,
"_OBJC_SELECTOR_TABLE");
/* Avoid warning when not sending messages. */
TREE_USED (UOBJC_SELECTOR_TABLE_decl) = 1;
}
generate_forward_declaration_to_string_table ();
/* Forward declare constant_string_id and constant_string_type. */
if (!constant_string_class_name)
constant_string_class_name = default_constant_string_class_name;
constant_string_id = get_identifier (constant_string_class_name);
objc_declare_class (tree_cons (NULL_TREE, constant_string_id, NULL_TREE));
/* Pre-build the following entities - for speed/convenience. */
self_id = get_identifier ("self");
ucmd_id = get_identifier ("_cmd");
#ifndef OBJCPLUS
/* The C++ front-end does not appear to grok __attribute__((__unused__)). */
unused_list = build_tree_list (get_identifier ("__unused__"), NULL_TREE);
#endif
}
/* Ensure that the ivar list for NSConstantString/NXConstantString
(or whatever was specified via `-fconstant-string-class')
contains fields at least as large as the following three, so that
the runtime can stomp on them with confidence:
struct STRING_OBJECT_CLASS_NAME
{
Object isa;
char *cString;
unsigned int length;
}; */
static int
check_string_class_template (void)
{
tree field_decl = TYPE_FIELDS (constant_string_type);
#define AT_LEAST_AS_LARGE_AS(F, T) \
(F && TREE_CODE (F) == FIELD_DECL \
&& (TREE_INT_CST_LOW (DECL_SIZE (F)) \
>= TREE_INT_CST_LOW (TYPE_SIZE (T))))
if (!AT_LEAST_AS_LARGE_AS (field_decl, ptr_type_node))
return 0;
field_decl = TREE_CHAIN (field_decl);
if (!AT_LEAST_AS_LARGE_AS (field_decl, ptr_type_node))
return 0;
field_decl = TREE_CHAIN (field_decl);
return AT_LEAST_AS_LARGE_AS (field_decl, unsigned_type_node);
#undef AT_LEAST_AS_LARGE_AS
}
/* Avoid calling `check_string_class_template ()' more than once. */
static GTY(()) int string_layout_checked;
/* Custom build_string which sets TREE_TYPE! */
static tree
my_build_string (int len, const char *str)
{
return fix_string_type (build_string (len, str));
}
/* Given a chain of STRING_CST's, build a static instance of
NXConstantString which points at the concatenation of those
strings. We place the string object in the __string_objects
section of the __OBJC segment. The Objective-C runtime will
initialize the isa pointers of the string objects to point at the
NXConstantString class object. */
tree
build_objc_string_object (tree string)
{
tree initlist, constructor, constant_string_class;
int length;
string = fix_string_type (string);
constant_string_class = lookup_interface (constant_string_id);
if (!constant_string_class
|| !(constant_string_type
= CLASS_STATIC_TEMPLATE (constant_string_class)))
{
error ("cannot find interface declaration for `%s'",
IDENTIFIER_POINTER (constant_string_id));
return error_mark_node;
}
/* Call to 'combine_strings' has been moved above. */
TREE_SET_CODE (string, STRING_CST);
length = TREE_STRING_LENGTH (string) - 1;
if (!string_layout_checked)
{
/* The NSConstantString/NXConstantString ivar layout is now
known. */
if (!check_string_class_template ())
{
error ("interface `%s' does not have valid constant string layout",
IDENTIFIER_POINTER (constant_string_id));
return error_mark_node;
}
add_class_reference (constant_string_id);
}
/* & ((NXConstantString) { NULL, string, length }) */
if (flag_next_runtime)
{
/* For the NeXT runtime, we can generate a literal reference
to the string class, don't need to run a constructor. */
setup_string_decl ();
if (string_class_decl == NULL_TREE)
{
error ("cannot find reference tag for class `%s'",
IDENTIFIER_POINTER (constant_string_id));
return error_mark_node;
}
initlist = build_tree_list
(NULL_TREE,
copy_node (build_unary_op (ADDR_EXPR, string_class_decl, 0)));
}
else
{
initlist = build_tree_list (NULL_TREE, build_int_2 (0, 0));
}
initlist
= tree_cons (NULL_TREE, copy_node (build_unary_op (ADDR_EXPR, string, 1)),
initlist);
initlist = tree_cons (NULL_TREE, build_int_2 (length, 0), initlist);
constructor = objc_build_constructor (constant_string_type,
nreverse (initlist));
if (!flag_next_runtime)
{
constructor
= objc_add_static_instance (constructor, constant_string_type);
}
return (build_unary_op (ADDR_EXPR, constructor, 1));
}
/* Declare a static instance of CLASS_DECL initialized by CONSTRUCTOR. */
static GTY(()) int num_static_inst;
static tree
objc_add_static_instance (tree constructor, tree class_decl)
{
tree *chain, decl;
char buf[256];
/* Find the list of static instances for the CLASS_DECL. Create one if
not found. */
for (chain = &objc_static_instances;
*chain && TREE_VALUE (*chain) != class_decl;
chain = &TREE_CHAIN (*chain));
if (!*chain)
{
*chain = tree_cons (NULL_TREE, class_decl, NULL_TREE);
add_objc_string (OBJC_TYPE_NAME (class_decl), class_names);
}
sprintf (buf, "_OBJC_INSTANCE_%d", num_static_inst++);
decl = build_decl (VAR_DECL, get_identifier (buf), class_decl);
DECL_COMMON (decl) = 1;
TREE_STATIC (decl) = 1;
DECL_ARTIFICIAL (decl) = 1;
DECL_INITIAL (decl) = constructor;
/* We may be writing something else just now.
Postpone till end of input. */
DECL_DEFER_OUTPUT (decl) = 1;
pushdecl_top_level (decl);
rest_of_decl_compilation (decl, 0, 1, 0);
/* Add the DECL to the head of this CLASS' list. */
TREE_PURPOSE (*chain) = tree_cons (NULL_TREE, decl, TREE_PURPOSE (*chain));
return decl;
}
/* Build a static constant CONSTRUCTOR
with type TYPE and elements ELTS. */
static tree
objc_build_constructor (tree type, tree elts)
{
tree constructor, f, e;
/* ??? Most of the places that we build constructors, we don't fill in
the type of integers properly. Convert them all en masse. */
if (TREE_CODE (type) == ARRAY_TYPE)
{
f = TREE_TYPE (type);
if (TREE_CODE (f) == POINTER_TYPE || TREE_CODE (f) == INTEGER_TYPE)
for (e = elts; e ; e = TREE_CHAIN (e))
TREE_VALUE (e) = convert (f, TREE_VALUE (e));
}
else
{
f = TYPE_FIELDS (type);
for (e = elts; e && f; e = TREE_CHAIN (e), f = TREE_CHAIN (f))
if (TREE_CODE (TREE_TYPE (f)) == POINTER_TYPE
|| TREE_CODE (TREE_TYPE (f)) == INTEGER_TYPE)
TREE_VALUE (e) = convert (TREE_TYPE (f), TREE_VALUE (e));
}
constructor = build_constructor (type, elts);
TREE_CONSTANT (constructor) = 1;
TREE_STATIC (constructor) = 1;
TREE_READONLY (constructor) = 1;
#ifdef OBJCPLUS
/* zlaski 2001-Apr-02: mark this as a call to a constructor, as required by
build_unary_op (wasn't true in 2.7.2.1 days) */
TREE_HAS_CONSTRUCTOR (constructor) = 1;
#endif
return constructor;
}
/* Take care of defining and initializing _OBJC_SYMBOLS. */
/* Predefine the following data type:
struct _objc_symtab
{
long sel_ref_cnt;
SEL *refs;
short cls_def_cnt;
short cat_def_cnt;
void *defs[cls_def_cnt + cat_def_cnt];
}; */
static void
build_objc_symtab_template (void)
{
tree field_decl, field_decl_chain;
objc_symtab_template
= start_struct (RECORD_TYPE, get_identifier (UTAG_SYMTAB));
/* long sel_ref_cnt; */
field_decl = create_builtin_decl (FIELD_DECL,
long_integer_type_node,
"sel_ref_cnt");
field_decl_chain = field_decl;
/* SEL *refs; */
field_decl = create_builtin_decl (FIELD_DECL,
build_pointer_type (selector_type),
"refs");
chainon (field_decl_chain, field_decl);
/* short cls_def_cnt; */
field_decl = create_builtin_decl (FIELD_DECL,
short_integer_type_node,
"cls_def_cnt");
chainon (field_decl_chain, field_decl);
/* short cat_def_cnt; */
field_decl = create_builtin_decl (FIELD_DECL,
short_integer_type_node,
"cat_def_cnt");
chainon (field_decl_chain, field_decl);
if (imp_count || cat_count || !flag_next_runtime)
{
/* void *defs[imp_count + cat_count (+ 1)]; */
/* NB: The index is one less than the size of the array. */
int index = imp_count + cat_count
+ (flag_next_runtime? -1: 0);
field_decl = create_builtin_decl
(FIELD_DECL,
build_array_type
(ptr_type_node,
build_index_type (build_int_2 (index, 0))),
"defs");
chainon (field_decl_chain, field_decl);
}
finish_struct (objc_symtab_template, field_decl_chain, NULL_TREE);
}
/* Create the initial value for the `defs' field of _objc_symtab.
This is a CONSTRUCTOR. */
static tree
init_def_list (tree type)
{
tree expr, initlist = NULL_TREE;
struct imp_entry *impent;
if (imp_count)
for (impent = imp_list; impent; impent = impent->next)
{
if (TREE_CODE (impent->imp_context) == CLASS_IMPLEMENTATION_TYPE)
{
expr = build_unary_op (ADDR_EXPR, impent->class_decl, 0);
initlist = tree_cons (NULL_TREE, expr, initlist);
}
}
if (cat_count)
for (impent = imp_list; impent; impent = impent->next)
{
if (TREE_CODE (impent->imp_context) == CATEGORY_IMPLEMENTATION_TYPE)
{
expr = build_unary_op (ADDR_EXPR, impent->class_decl, 0);
initlist = tree_cons (NULL_TREE, expr, initlist);
}
}
if (!flag_next_runtime)
{
/* statics = { ..., _OBJC_STATIC_INSTANCES, ... } */
tree expr;
if (static_instances_decl)
expr = build_unary_op (ADDR_EXPR, static_instances_decl, 0);
else
expr = build_int_2 (0, 0);
initlist = tree_cons (NULL_TREE, expr, initlist);
}
return objc_build_constructor (type, nreverse (initlist));
}
/* Construct the initial value for all of _objc_symtab. */
static tree
init_objc_symtab (tree type)
{
tree initlist;
/* sel_ref_cnt = { ..., 5, ... } */
initlist = build_tree_list (NULL_TREE, build_int_2 (0, 0));
/* refs = { ..., _OBJC_SELECTOR_TABLE, ... } */
if (flag_next_runtime || ! sel_ref_chain)
initlist = tree_cons (NULL_TREE, build_int_2 (0, 0), initlist);
else
initlist = tree_cons (NULL_TREE,
build_unary_op (ADDR_EXPR,
UOBJC_SELECTOR_TABLE_decl, 1),
initlist);
/* cls_def_cnt = { ..., 5, ... } */
initlist = tree_cons (NULL_TREE, build_int_2 (imp_count, 0), initlist);
/* cat_def_cnt = { ..., 5, ... } */
initlist = tree_cons (NULL_TREE, build_int_2 (cat_count, 0), initlist);
/* cls_def = { ..., { &Foo, &Bar, ...}, ... } */
if (imp_count || cat_count || !flag_next_runtime)
{
tree field = TYPE_FIELDS (type);
field = TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (field))));
initlist = tree_cons (NULL_TREE, init_def_list (TREE_TYPE (field)),
initlist);
}
return objc_build_constructor (type, nreverse (initlist));
}
/* Generate forward declarations for metadata such as
'OBJC_CLASS_...'. */
static tree
build_metadata_decl (const char *name, tree type)
{
tree decl, decl_specs;
/* extern struct TYPE NAME_<name>; */
decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_EXTERN]);
decl_specs = tree_cons (NULL_TREE, type, decl_specs);
decl = define_decl (synth_id_with_class_suffix
(name,
objc_implementation_context),
decl_specs);
TREE_USED (decl) = 1;
DECL_ARTIFICIAL (decl) = 1;
TREE_PUBLIC (decl) = 0;
return decl;
}
/* Push forward-declarations of all the categories so that
init_def_list can use them in a CONSTRUCTOR. */
static void
forward_declare_categories (void)
{
struct imp_entry *impent;
tree sav = objc_implementation_context;
for (impent = imp_list; impent; impent = impent->next)
{
if (TREE_CODE (impent->imp_context) == CATEGORY_IMPLEMENTATION_TYPE)
{
/* Set an invisible arg to synth_id_with_class_suffix. */
objc_implementation_context = impent->imp_context;
/* extern struct objc_category _OBJC_CATEGORY_<name>; */
impent->class_decl = build_metadata_decl ("_OBJC_CATEGORY",
objc_category_template);
}
}
objc_implementation_context = sav;
}
/* Create the declaration of _OBJC_SYMBOLS, with type `struct _objc_symtab'
and initialized appropriately. */
static void
generate_objc_symtab_decl (void)
{
tree sc_spec;
if (!objc_category_template)
build_category_template ();
/* forward declare categories */
if (cat_count)
forward_declare_categories ();
if (!objc_symtab_template)
build_objc_symtab_template ();
sc_spec = build_tree_list (NULL_TREE, ridpointers[(int) RID_STATIC]);
UOBJC_SYMBOLS_decl = start_decl (get_identifier ("_OBJC_SYMBOLS"),
tree_cons (NULL_TREE,
objc_symtab_template, sc_spec),
1,
NULL_TREE);
TREE_USED (UOBJC_SYMBOLS_decl) = 1;
DECL_IGNORED_P (UOBJC_SYMBOLS_decl) = 1;
DECL_ARTIFICIAL (UOBJC_SYMBOLS_decl) = 1;
finish_decl (UOBJC_SYMBOLS_decl,
init_objc_symtab (TREE_TYPE (UOBJC_SYMBOLS_decl)),
NULL_TREE);
}
static tree
init_module_descriptor (tree type)
{
tree initlist, expr;
/* version = { 1, ... } */
expr = build_int_2 (OBJC_VERSION, 0);
initlist = build_tree_list (NULL_TREE, expr);
/* size = { ..., sizeof (struct objc_module), ... } */
expr = size_in_bytes (objc_module_template);
initlist = tree_cons (NULL_TREE, expr, initlist);
/* name = { ..., "foo.m", ... } */
expr = add_objc_string (get_identifier (input_filename), class_names);
initlist = tree_cons (NULL_TREE, expr, initlist);
/* symtab = { ..., _OBJC_SYMBOLS, ... } */
if (UOBJC_SYMBOLS_decl)
expr = build_unary_op (ADDR_EXPR, UOBJC_SYMBOLS_decl, 0);
else
expr = build_int_2 (0, 0);
initlist = tree_cons (NULL_TREE, expr, initlist);
return objc_build_constructor (type, nreverse (initlist));
}
/* Write out the data structures to describe Objective C classes defined.
If appropriate, compile and output a setup function to initialize them.
Return a symbol_ref to the function to call to initialize the Objective C
data structures for this file (and perhaps for other files also).
struct objc_module { ... } _OBJC_MODULE = { ... }; */
static rtx
build_module_descriptor (void)
{
tree decl_specs, field_decl, field_decl_chain;
objc_module_template
= start_struct (RECORD_TYPE, get_identifier (UTAG_MODULE));
/* Long version; */
decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_LONG]);
field_decl = get_identifier ("version");
field_decl = grokfield (field_decl, decl_specs, NULL_TREE);
field_decl_chain = field_decl;
/* long size; */
decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_LONG]);
field_decl = get_identifier ("size");
field_decl = grokfield (field_decl, decl_specs, NULL_TREE);
chainon (field_decl_chain, field_decl);
/* char *name; */
decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_CHAR]);
field_decl = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("name"));
field_decl = grokfield (field_decl, decl_specs, NULL_TREE);
chainon (field_decl_chain, field_decl);
/* struct objc_symtab *symtab; */
decl_specs = get_identifier (UTAG_SYMTAB);
decl_specs = build_tree_list (NULL_TREE, xref_tag (RECORD_TYPE, decl_specs));
field_decl = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("symtab"));
field_decl = grokfield (field_decl, decl_specs, NULL_TREE);
chainon (field_decl_chain, field_decl);
finish_struct (objc_module_template, field_decl_chain, NULL_TREE);
/* Create an instance of "objc_module". */
decl_specs = tree_cons (NULL_TREE, objc_module_template,
build_tree_list (NULL_TREE,
ridpointers[(int) RID_STATIC]));
UOBJC_MODULES_decl = start_decl (get_identifier ("_OBJC_MODULES"),
decl_specs, 1, NULL_TREE);
DECL_ARTIFICIAL (UOBJC_MODULES_decl) = 1;
DECL_IGNORED_P (UOBJC_MODULES_decl) = 1;
DECL_CONTEXT (UOBJC_MODULES_decl) = NULL_TREE;
finish_decl (UOBJC_MODULES_decl,
init_module_descriptor (TREE_TYPE (UOBJC_MODULES_decl)),
NULL_TREE);
/* Mark the decl to avoid "defined but not used" warning. */
DECL_IN_SYSTEM_HEADER (UOBJC_MODULES_decl) = 1;
/* Generate a constructor call for the module descriptor.
This code was generated by reading the grammar rules
of c-parse.in; Therefore, it may not be the most efficient
way of generating the requisite code. */
if (flag_next_runtime)
return NULL_RTX;
{
tree parms, execclass_decl, decelerator, void_list_node_1;
tree init_function_name, init_function_decl;
/* Declare void __objc_execClass (void *); */
void_list_node_1 = build_tree_list (NULL_TREE, void_type_node);
execclass_decl = build_decl (FUNCTION_DECL,
get_identifier (TAG_EXECCLASS),
build_function_type (void_type_node,
tree_cons (NULL_TREE, ptr_type_node,
OBJC_VOID_AT_END)));
DECL_EXTERNAL (execclass_decl) = 1;
DECL_ARTIFICIAL (execclass_decl) = 1;
TREE_PUBLIC (execclass_decl) = 1;
pushdecl (execclass_decl);
rest_of_decl_compilation (execclass_decl, 0, 0, 0);
assemble_external (execclass_decl);
/* void _GLOBAL_$I$<gnyf> () {objc_execClass (&L_OBJC_MODULES);} */
init_function_name = get_file_function_name ('I');
start_function (void_list_node_1,
build_nt (CALL_EXPR, init_function_name,
tree_cons (NULL_TREE, NULL_TREE,
OBJC_VOID_AT_END),
NULL_TREE),
NULL_TREE);
store_parm_decls ();
init_function_decl = current_function_decl;
TREE_PUBLIC (init_function_decl) = ! targetm.have_ctors_dtors;
TREE_USED (init_function_decl) = 1;
/* Don't let this one be deferred. */
DECL_INLINE (init_function_decl) = 0;
DECL_UNINLINABLE (init_function_decl) = 1;
current_function_cannot_inline
= "static constructors and destructors cannot be inlined";
parms
= build_tree_list (NULL_TREE,
build_unary_op (ADDR_EXPR, UOBJC_MODULES_decl, 0));
decelerator = build_function_call (execclass_decl, parms);
c_expand_expr_stmt (decelerator);
finish_function ();
return XEXP (DECL_RTL (init_function_decl), 0);
}
}
/* extern const char _OBJC_STRINGS[]; */
static void
generate_forward_declaration_to_string_table (void)
{
tree sc_spec, decl_specs, expr_decl;
sc_spec = tree_cons (NULL_TREE, ridpointers[(int) RID_EXTERN], NULL_TREE);
decl_specs = tree_cons (NULL_TREE, ridpointers[(int) RID_CHAR], sc_spec);
expr_decl
= build_nt (ARRAY_REF, get_identifier ("_OBJC_STRINGS"), NULL_TREE);
UOBJC_STRINGS_decl = define_decl (expr_decl, decl_specs);
}
/* Return the DECL of the string IDENT in the SECTION. */
static tree
get_objc_string_decl (tree ident, enum string_section section)
{
tree chain;
if (section == class_names)
chain = class_names_chain;
else if (section == meth_var_names)
chain = meth_var_names_chain;
else if (section == meth_var_types)
chain = meth_var_types_chain;
else
abort ();
for (; chain != 0; chain = TREE_CHAIN (chain))
if (TREE_VALUE (chain) == ident)
return (TREE_PURPOSE (chain));
abort ();
return NULL_TREE;
}
/* Output references to all statically allocated objects. Return the DECL
for the array built. */
static void
generate_static_references (void)
{
tree decls = NULL_TREE, ident, decl_spec, expr_decl, expr = NULL_TREE;
tree class_name, class, decl, initlist;
tree cl_chain, in_chain, type;
int num_inst, num_class;
char buf[256];
if (flag_next_runtime)
abort ();
for (cl_chain = objc_static_instances, num_class = 0;
cl_chain; cl_chain = TREE_CHAIN (cl_chain), num_class++)
{
for (num_inst = 0, in_chain = TREE_PURPOSE (cl_chain);
in_chain; num_inst++, in_chain = TREE_CHAIN (in_chain));
sprintf (buf, "_OBJC_STATIC_INSTANCES_%d", num_class);
ident = get_identifier (buf);
expr_decl = build_nt (ARRAY_REF, ident, NULL_TREE);
decl_spec = tree_cons (NULL_TREE, build_pointer_type (void_type_node),
build_tree_list (NULL_TREE,
ridpointers[(int) RID_STATIC]));
decl = start_decl (expr_decl, decl_spec, 1, NULL_TREE);
DECL_CONTEXT (decl) = 0;
DECL_ARTIFICIAL (decl) = 1;
/* Output {class_name, ...}. */
class = TREE_VALUE (cl_chain);
class_name = get_objc_string_decl (OBJC_TYPE_NAME (class), class_names);
initlist = build_tree_list (NULL_TREE,
build_unary_op (ADDR_EXPR, class_name, 1));
/* Output {..., instance, ...}. */
for (in_chain = TREE_PURPOSE (cl_chain);
in_chain; in_chain = TREE_CHAIN (in_chain))
{
expr = build_unary_op (ADDR_EXPR, TREE_VALUE (in_chain), 1);
initlist = tree_cons (NULL_TREE, expr, initlist);
}
/* Output {..., NULL}. */
initlist = tree_cons (NULL_TREE, build_int_2 (0, 0), initlist);
expr = objc_build_constructor (TREE_TYPE (decl), nreverse (initlist));
finish_decl (decl, expr, NULL_TREE);
TREE_USED (decl) = 1;
type = build_array_type (build_pointer_type (void_type_node), 0);
decl = build_decl (VAR_DECL, ident, type);
TREE_USED (decl) = 1;
TREE_STATIC (decl) = 1;
decls
= tree_cons (NULL_TREE, build_unary_op (ADDR_EXPR, decl, 1), decls);
}
decls = tree_cons (NULL_TREE, build_int_2 (0, 0), decls);
ident = get_identifier ("_OBJC_STATIC_INSTANCES");
expr_decl = build_nt (ARRAY_REF, ident, NULL_TREE);
decl_spec = tree_cons (NULL_TREE, build_pointer_type (void_type_node),
build_tree_list (NULL_TREE,
ridpointers[(int) RID_STATIC]));
static_instances_decl
= start_decl (expr_decl, decl_spec, 1, NULL_TREE);
TREE_USED (static_instances_decl) = 1;
DECL_CONTEXT (static_instances_decl) = 0;
DECL_ARTIFICIAL (static_instances_decl) = 1;
expr = objc_build_constructor (TREE_TYPE (static_instances_decl),
nreverse (decls));
finish_decl (static_instances_decl, expr, NULL_TREE);
}
/* Output all strings. */
static void
generate_strings (void)
{
tree sc_spec, decl_specs, expr_decl;
tree chain, string_expr;
tree string, decl;
for (chain = class_names_chain; chain; chain = TREE_CHAIN (chain))
{
string = TREE_VALUE (chain);
decl = TREE_PURPOSE (chain);
sc_spec
= tree_cons (NULL_TREE, ridpointers[(int) RID_STATIC], NULL_TREE);
decl_specs = tree_cons (NULL_TREE, ridpointers[(int) RID_CHAR], sc_spec);
expr_decl = build_nt (ARRAY_REF, DECL_NAME (decl), NULL_TREE);
decl = start_decl (expr_decl, decl_specs, 1, NULL_TREE);
DECL_CONTEXT (decl) = NULL_TREE;
string_expr = my_build_string (IDENTIFIER_LENGTH (string) + 1,
IDENTIFIER_POINTER (string));
finish_decl (decl, string_expr, NULL_TREE);
}
for (chain = meth_var_names_chain; chain; chain = TREE_CHAIN (chain))
{
string = TREE_VALUE (chain);
decl = TREE_PURPOSE (chain);
sc_spec
= tree_cons (NULL_TREE, ridpointers[(int) RID_STATIC], NULL_TREE);
decl_specs = tree_cons (NULL_TREE, ridpointers[(int) RID_CHAR], sc_spec);
expr_decl = build_nt (ARRAY_REF, DECL_NAME (decl), NULL_TREE);
decl = start_decl (expr_decl, decl_specs, 1, NULL_TREE);
DECL_CONTEXT (decl) = NULL_TREE;
string_expr = my_build_string (IDENTIFIER_LENGTH (string) + 1,
IDENTIFIER_POINTER (string));
finish_decl (decl, string_expr, NULL_TREE);
}
for (chain = meth_var_types_chain; chain; chain = TREE_CHAIN (chain))
{
string = TREE_VALUE (chain);
decl = TREE_PURPOSE (chain);
sc_spec
= tree_cons (NULL_TREE, ridpointers[(int) RID_STATIC], NULL_TREE);
decl_specs = tree_cons (NULL_TREE, ridpointers[(int) RID_CHAR], sc_spec);
expr_decl = build_nt (ARRAY_REF, DECL_NAME (decl), NULL_TREE);
decl = start_decl (expr_decl, decl_specs, 1, NULL_TREE);
DECL_CONTEXT (decl) = NULL_TREE;
string_expr = my_build_string (IDENTIFIER_LENGTH (string) + 1,
IDENTIFIER_POINTER (string));
finish_decl (decl, string_expr, NULL_TREE);
}
}
static GTY(()) int selector_reference_idx;
static tree
build_selector_reference_decl (void)
{
tree decl, ident;
char buf[256];
sprintf (buf, "_OBJC_SELECTOR_REFERENCES_%d", selector_reference_idx++);
ident = get_identifier (buf);
decl = build_decl (VAR_DECL, ident, selector_type);
DECL_EXTERNAL (decl) = 1;
TREE_PUBLIC (decl) = 0;
TREE_USED (decl) = 1;
DECL_ARTIFICIAL (decl) = 1;
DECL_CONTEXT (decl) = 0;
make_decl_rtl (decl, 0);
pushdecl_top_level (decl);
return decl;
}
/* Just a handy wrapper for add_objc_string. */
static tree
build_selector (tree ident)
{
tree expr = add_objc_string (ident, meth_var_names);
if (flag_typed_selectors)
return expr;
else
return build_c_cast (selector_type, expr); /* cast! */
}
static void
build_selector_translation_table (void)
{
tree sc_spec, decl_specs;
tree chain, initlist = NULL_TREE;
int offset = 0;
tree decl = NULL_TREE, var_decl, name;
for (chain = sel_ref_chain; chain; chain = TREE_CHAIN (chain))
{
tree expr;
if (warn_selector && objc_implementation_context)
{
tree method_chain;
bool found = false;
for (method_chain = meth_var_names_chain;
method_chain;
method_chain = TREE_CHAIN (method_chain))
{
if (TREE_VALUE (method_chain) == TREE_VALUE (chain))
{
found = true;
break;
}
}
if (!found)
{
/* Adjust line number for warning message. */
int save_lineno = input_line;
if (flag_next_runtime && TREE_PURPOSE (chain))
input_line = DECL_SOURCE_LINE (TREE_PURPOSE (chain));
warning ("creating selector for non existant method %s",
IDENTIFIER_POINTER (TREE_VALUE (chain)));
input_line = save_lineno;
}
}
expr = build_selector (TREE_VALUE (chain));
if (flag_next_runtime)
{
name = DECL_NAME (TREE_PURPOSE (chain));
sc_spec = build_tree_list (NULL_TREE, ridpointers[(int) RID_STATIC]);
/* static SEL _OBJC_SELECTOR_REFERENCES_n = ...; */
decl_specs = tree_cons (NULL_TREE, selector_type, sc_spec);
var_decl = name;
/* The `decl' that is returned from start_decl is the one that we
forward declared in `build_selector_reference' */
decl = start_decl (var_decl, decl_specs, 1, NULL_TREE );
}
/* add one for the '\0' character */
offset += IDENTIFIER_LENGTH (TREE_VALUE (chain)) + 1;
if (flag_next_runtime)
finish_decl (decl, expr, NULL_TREE);
else
{
if (flag_typed_selectors)
{
tree eltlist = NULL_TREE;
tree encoding = get_proto_encoding (TREE_PURPOSE (chain));
eltlist = tree_cons (NULL_TREE, expr, NULL_TREE);
eltlist = tree_cons (NULL_TREE, encoding, eltlist);
expr = objc_build_constructor (objc_selector_template,
nreverse (eltlist));
}
initlist = tree_cons (NULL_TREE, expr, initlist);
}
}
if (! flag_next_runtime)
{
/* Cause the variable and its initial value to be actually output. */
DECL_EXTERNAL (UOBJC_SELECTOR_TABLE_decl) = 0;
TREE_STATIC (UOBJC_SELECTOR_TABLE_decl) = 1;
/* NULL terminate the list and fix the decl for output. */
initlist = tree_cons (NULL_TREE, build_int_2 (0, 0), initlist);
DECL_INITIAL (UOBJC_SELECTOR_TABLE_decl) = objc_ellipsis_node;
initlist = objc_build_constructor (TREE_TYPE (UOBJC_SELECTOR_TABLE_decl),
nreverse (initlist));
finish_decl (UOBJC_SELECTOR_TABLE_decl, initlist, NULL_TREE);
current_function_decl = NULL_TREE;
}
}
static tree
get_proto_encoding (tree proto)
{
tree encoding;
if (proto)
{
if (! METHOD_ENCODING (proto))
{
encoding = encode_method_prototype (proto);
METHOD_ENCODING (proto) = encoding;
}
else
encoding = METHOD_ENCODING (proto);
return add_objc_string (encoding, meth_var_types);
}
else
return build_int_2 (0, 0);
}
/* sel_ref_chain is a list whose "value" fields will be instances of
identifier_node that represent the selector. */
static tree
build_typed_selector_reference (tree ident, tree prototype)
{
tree *chain = &sel_ref_chain;
tree expr;
int index = 0;
while (*chain)
{
if (TREE_PURPOSE (*chain) == prototype && TREE_VALUE (*chain) == ident)
goto return_at_index;
index++;
chain = &TREE_CHAIN (*chain);
}
*chain = tree_cons (prototype, ident, NULL_TREE);
return_at_index:
expr = build_unary_op (ADDR_EXPR,
build_array_ref (UOBJC_SELECTOR_TABLE_decl,
build_int_2 (index, 0)),
1);
return build_c_cast (selector_type, expr);
}
static tree
build_selector_reference (tree ident)
{
tree *chain = &sel_ref_chain;
tree expr;
int index = 0;
while (*chain)
{
if (TREE_VALUE (*chain) == ident)
return (flag_next_runtime
? TREE_PURPOSE (*chain)
: build_array_ref (UOBJC_SELECTOR_TABLE_decl,
build_int_2 (index, 0)));
index++;
chain = &TREE_CHAIN (*chain);
}
expr = build_selector_reference_decl ();
*chain = tree_cons (expr, ident, NULL_TREE);
return (flag_next_runtime
? expr
: build_array_ref (UOBJC_SELECTOR_TABLE_decl,
build_int_2 (index, 0)));
}
static GTY(()) int class_reference_idx;
static tree
build_class_reference_decl (void)
{
tree decl, ident;
char buf[256];
sprintf (buf, "_OBJC_CLASS_REFERENCES_%d", class_reference_idx++);
ident = get_identifier (buf);
decl = build_decl (VAR_DECL, ident, objc_class_type);
DECL_EXTERNAL (decl) = 1;
TREE_PUBLIC (decl) = 0;
TREE_USED (decl) = 1;
DECL_CONTEXT (decl) = 0;
DECL_ARTIFICIAL (decl) = 1;
make_decl_rtl (decl, 0);
pushdecl_top_level (decl);
return decl;
}
/* Create a class reference, but don't create a variable to reference
it. */
static void
add_class_reference (tree ident)
{
tree chain;
if ((chain = cls_ref_chain))
{
tree tail;
do
{
if (ident == TREE_VALUE (chain))
return;
tail = chain;
chain = TREE_CHAIN (chain);
}
while (chain);
/* Append to the end of the list */
TREE_CHAIN (tail) = tree_cons (NULL_TREE, ident, NULL_TREE);
}
else
cls_ref_chain = tree_cons (NULL_TREE, ident, NULL_TREE);
}
/* Get a class reference, creating it if necessary. Also create the
reference variable. */
tree
get_class_reference (tree ident)
{
tree orig_ident;
#ifdef OBJCPLUS
if (processing_template_decl)
/* Must wait until template instantiation time. */
return build_min_nt (CLASS_REFERENCE_EXPR, ident);
if (TREE_CODE (ident) == TYPE_DECL)
ident = DECL_NAME (ident);
#endif
orig_ident = ident;
if (!(ident = is_class_name (ident)))
{
error ("`%s' is not an Objective-C class name or alias",
IDENTIFIER_POINTER (orig_ident));
return error_mark_node;
}
if (flag_next_runtime && !flag_zero_link)
{
tree *chain;
tree decl;
for (chain = &cls_ref_chain; *chain; chain = &TREE_CHAIN (*chain))
if (TREE_VALUE (*chain) == ident)
{
if (! TREE_PURPOSE (*chain))
TREE_PURPOSE (*chain) = build_class_reference_decl ();
return TREE_PURPOSE (*chain);
}
decl = build_class_reference_decl ();
*chain = tree_cons (decl, ident, NULL_TREE);
return decl;
}
else
{
tree params;
add_class_reference (ident);
params = build_tree_list (NULL_TREE,
my_build_string (IDENTIFIER_LENGTH (ident) + 1,
IDENTIFIER_POINTER (ident)));
assemble_external (objc_get_class_decl);
return build_function_call (objc_get_class_decl, params);
}
}
/* For each string section we have a chain which maps identifier nodes
to decls for the strings. */
static tree
add_objc_string (tree ident, enum string_section section)
{
tree *chain, decl;
if (section == class_names)
chain = &class_names_chain;
else if (section == meth_var_names)
chain = &meth_var_names_chain;
else if (section == meth_var_types)
chain = &meth_var_types_chain;
else
abort ();
while (*chain)
{
if (TREE_VALUE (*chain) == ident)
return build_unary_op (ADDR_EXPR, TREE_PURPOSE (*chain), 1);
chain = &TREE_CHAIN (*chain);
}
decl = build_objc_string_decl (section);
*chain = tree_cons (decl, ident, NULL_TREE);
return build_unary_op (ADDR_EXPR, decl, 1);
}
static GTY(()) int class_names_idx;
static GTY(()) int meth_var_names_idx;
static GTY(()) int meth_var_types_idx;
static tree
build_objc_string_decl (enum string_section section)
{
tree decl, ident;
char buf[256];
if (section == class_names)
sprintf (buf, "_OBJC_CLASS_NAME_%d", class_names_idx++);
else if (section == meth_var_names)
sprintf (buf, "_OBJC_METH_VAR_NAME_%d", meth_var_names_idx++);
else if (section == meth_var_types)
sprintf (buf, "_OBJC_METH_VAR_TYPE_%d", meth_var_types_idx++);
ident = get_identifier (buf);
decl = build_decl (VAR_DECL, ident, build_array_type (char_type_node, 0));
DECL_EXTERNAL (decl) = 1;
TREE_PUBLIC (decl) = 0;
TREE_USED (decl) = 1;
TREE_CONSTANT (decl) = 1;
DECL_CONTEXT (decl) = 0;
DECL_ARTIFICIAL (decl) = 1;
make_decl_rtl (decl, 0);
pushdecl_top_level (decl);
return decl;
}
void
objc_declare_alias (tree alias_ident, tree class_ident)
{
tree underlying_class;
#ifdef OBJCPLUS
if (current_namespace != global_namespace) {
error ("Objective-C declarations may only appear in global scope");
}
#endif /* OBJCPLUS */
if (!(underlying_class = is_class_name (class_ident)))
warning ("cannot find class `%s'", IDENTIFIER_POINTER (class_ident));
else if (is_class_name (alias_ident))
warning ("class `%s' already exists", IDENTIFIER_POINTER (alias_ident));
else
alias_chain = tree_cons (underlying_class, alias_ident, alias_chain);
}
void
objc_declare_class (tree ident_list)
{
tree list;
#ifdef OBJCPLUS
if (current_namespace != global_namespace) {
error ("Objective-C declarations may only appear in global scope");
}
#endif /* OBJCPLUS */
for (list = ident_list; list; list = TREE_CHAIN (list))
{
tree ident = TREE_VALUE (list);
if (! is_class_name (ident))
{
tree record = lookup_name (ident);
if (record && ! TREE_STATIC_TEMPLATE (record))
{
error ("`%s' redeclared as different kind of symbol",
IDENTIFIER_POINTER (ident));
error ("%Jprevious declaration of '%D'",
record, record);
}
record = xref_tag (RECORD_TYPE, ident);
TREE_STATIC_TEMPLATE (record) = 1;
class_chain = tree_cons (NULL_TREE, ident, class_chain);
}
}
}
tree
is_class_name (tree ident)
{
tree chain;
if (ident && TREE_CODE (ident) == IDENTIFIER_NODE
&& identifier_global_value (ident))
ident = identifier_global_value (ident);
while (ident && TREE_CODE (ident) == TYPE_DECL && DECL_ORIGINAL_TYPE (ident))
ident = TYPE_NAME (DECL_ORIGINAL_TYPE (ident));
#ifdef OBJCPLUS
if (ident && TREE_CODE (ident) == RECORD_TYPE)
ident = TYPE_NAME (ident);
if (ident && TREE_CODE (ident) == TYPE_DECL)
ident = DECL_NAME (ident);
#endif
if (!ident || TREE_CODE (ident) != IDENTIFIER_NODE)
return NULL_TREE;
if (lookup_interface (ident))
return ident;
for (chain = class_chain; chain; chain = TREE_CHAIN (chain))
{
if (ident == TREE_VALUE (chain))
return ident;
}
for (chain = alias_chain; chain; chain = TREE_CHAIN (chain))
{
if (ident == TREE_VALUE (chain))
return TREE_PURPOSE (chain);
}
return 0;
}
/* Check whether TYPE is either 'id', 'Class', or a pointer to an ObjC
class instance. This is needed by other parts of the compiler to
handle ObjC types gracefully. */
tree
objc_is_object_ptr (tree type)
{
type = TYPE_MAIN_VARIANT (type);
if (!type || TREE_CODE (type) != POINTER_TYPE)
return 0;
/* NB: This function may be called before the ObjC front-end has
been initialized, in which case ID_TYPE will be NULL. */
if (id_type && type && TYPE_P (type)
&& (IS_ID (type)
|| TREE_TYPE (type) == TREE_TYPE (objc_class_type)))
return type;
return is_class_name (OBJC_TYPE_NAME (TREE_TYPE (type)));
}
tree
lookup_interface (tree ident)
{
tree chain;
#ifdef OBJCPLUS
if (ident && TREE_CODE (ident) == TYPE_DECL)
ident = DECL_NAME (ident);
#endif
for (chain = interface_chain; chain; chain = TREE_CHAIN (chain))
{
if (ident == CLASS_NAME (chain))
return chain;
}
return NULL_TREE;
}
/* Implement @defs (<classname>) within struct bodies. */
tree
get_class_ivars_from_name (tree class_name)
{
tree interface = lookup_interface (class_name);
tree field, fields = NULL_TREE;
if (interface)
{
tree raw_ivar = get_class_ivars (interface, 1);
/* Regenerate the FIELD_DECLs for the enclosing struct. */
for (; raw_ivar; raw_ivar = TREE_CHAIN (raw_ivar))
{
field = grokfield (TREE_PURPOSE (TREE_VALUE (raw_ivar)),
TREE_PURPOSE (raw_ivar),
TREE_VALUE (TREE_VALUE (raw_ivar)));
#ifdef OBJCPLUS
finish_member_declaration (field);
#else
fields = chainon (fields, field);
#endif
}
}
else
error ("cannot find interface declaration for `%s'",
IDENTIFIER_POINTER (class_name));
return fields;
}
/* Used by: build_private_template, continue_class,
and for @defs constructs. */
static tree
get_class_ivars (tree interface, int raw)
{
tree my_name, super_name, ivar_chain;
my_name = CLASS_NAME (interface);
super_name = CLASS_SUPER_NAME (interface);
if (raw)
ivar_chain = CLASS_RAW_IVARS (interface);
else
{
ivar_chain = CLASS_IVARS (interface);
/* Save off a pristine copy of the leaf ivars (i.e, those not
inherited from a super class). */
if (!CLASS_OWN_IVARS (interface))
CLASS_OWN_IVARS (interface) = copy_list (ivar_chain);
}
while (super_name)
{
tree op1;
tree super_interface = lookup_interface (super_name);
if (!super_interface)
{
/* fatal did not work with 2 args...should fix */
error ("cannot find interface declaration for `%s', superclass of `%s'",
IDENTIFIER_POINTER (super_name),
IDENTIFIER_POINTER (my_name));
exit (FATAL_EXIT_CODE);
}
if (super_interface == interface)
fatal_error ("circular inheritance in interface declaration for `%s'",
IDENTIFIER_POINTER (super_name));
interface = super_interface;
my_name = CLASS_NAME (interface);
super_name = CLASS_SUPER_NAME (interface);
op1 = (raw ? CLASS_RAW_IVARS (interface) : CLASS_OWN_IVARS (interface));
if (op1)
{
tree head = copy_list (op1);
/* Prepend super class ivars...make a copy of the list, we
do not want to alter the original. */
chainon (head, ivar_chain);
ivar_chain = head;
}
}
return ivar_chain;
}
static tree
objc_enter_block (void)
{
tree block;
#ifdef OBJCPLUS
block = begin_compound_stmt (0);
#else
block = c_begin_compound_stmt ();
pushlevel (0);
clear_last_expr ();
add_scope_stmt (/*begin_p=*/1, /*partial_p=*/0);
#endif
objc_exception_block_stack = tree_cons (NULL_TREE, block,
objc_exception_block_stack);
blk_nesting_count++;
return block;
}
static tree
objc_exit_block (void)
{
tree block = TREE_VALUE (objc_exception_block_stack);
#ifndef OBJCPLUS
tree scope_stmt, inner;
#endif
objc_clear_super_receiver ();
#ifdef OBJCPLUS
finish_compound_stmt (0, block);
#else
scope_stmt = add_scope_stmt (/*begin_p=*/0, /*partial_p=*/0);
inner = poplevel (KEEP_MAYBE, 1, 0);
SCOPE_STMT_BLOCK (TREE_PURPOSE (scope_stmt))
= SCOPE_STMT_BLOCK (TREE_VALUE (scope_stmt))
= inner;
RECHAIN_STMTS (block, COMPOUND_BODY (block));
#endif
last_expr_type = NULL_TREE;
objc_exception_block_stack = TREE_CHAIN (objc_exception_block_stack);
blk_nesting_count--;
return block;
}
static tree
objc_declare_variable (enum rid scspec, tree name, tree type, tree init)
{
tree decl;
type = tree_cons (NULL_TREE, type,
tree_cons (NULL_TREE, ridpointers[(int) scspec],
NULL_TREE));
TREE_STATIC (type) = 1;
decl = start_decl (name, type, (init != NULL_TREE), NULL_TREE);
finish_decl (decl, init, NULL_TREE);
/* This prevents `unused variable' warnings when compiling with -Wall. */
TREE_USED (decl) = 1;
DECL_ARTIFICIAL (decl) = 1;
return decl;
}
tree
objc_build_throw_stmt (tree throw_expr)
{
tree func_params;
if (!flag_objc_exceptions)
fatal_error ("Use `-fobjc-exceptions' to enable Objective-C exception syntax");
if (!throw_expr && objc_caught_exception)
throw_expr = TREE_VALUE (objc_caught_exception);
if (!throw_expr)
{
error ("`@throw;' (rethrow) used outside of a `@catch' block");
return error_mark_node;
}
func_params = tree_cons (NULL_TREE, throw_expr, NULL_TREE);
assemble_external (objc_exception_throw_decl);
return c_expand_expr_stmt (build_function_call (objc_exception_throw_decl,
func_params));
}
static void
val_stack_push (struct val_stack **nc, long val)
{
struct val_stack *new_elem = xmalloc (sizeof (struct val_stack));
new_elem->val = val;
new_elem->next = *nc;
*nc = new_elem;
}
static void
val_stack_pop (struct val_stack **nc)
{
struct val_stack *old_elem = *nc;
*nc = old_elem->next;
free (old_elem);
}
static void
objc_build_try_enter_fragment (void)
{
/* objc_exception_try_enter(&_stackExceptionData);
if (!_setjmp(&_stackExceptionData.buf)) { */
tree func_params, if_stmt, cond;
func_params
= tree_cons (NULL_TREE,
build_unary_op (ADDR_EXPR,
TREE_VALUE (objc_stack_exception_data),
0),
NULL_TREE);
assemble_external (objc_exception_try_enter_decl);
c_expand_expr_stmt (build_function_call
(objc_exception_try_enter_decl, func_params));
if_stmt = c_begin_if_stmt ();
if_nesting_count++;
/* If <setjmp.h> has been included, the _setjmp prototype has
acquired a real, breathing type for its parameter. Cast our
argument to that type. */
func_params
= tree_cons (NULL_TREE,
build_c_cast (TYPE_ARG_TYPES (TREE_TYPE (objc_setjmp_decl))
? TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (objc_setjmp_decl)))
: ptr_type_node,
build_unary_op
(ADDR_EXPR,
build_component_ref (TREE_VALUE (objc_stack_exception_data),
get_identifier ("buf")), 0)),
NULL_TREE);
assemble_external (objc_setjmp_decl);
cond = build_unary_op (TRUTH_NOT_EXPR,
build_function_call (objc_setjmp_decl, func_params),
0);
c_expand_start_cond (c_common_truthvalue_conversion (cond),
0, if_stmt);
objc_enter_block ();
}
static tree
objc_build_extract_expr (void)
{
/* ... = objc_exception_extract(&_stackExceptionData); */
tree func_params
= tree_cons (NULL_TREE,
build_unary_op (ADDR_EXPR,
TREE_VALUE (objc_stack_exception_data), 0),
NULL_TREE);
assemble_external (objc_exception_extract_decl);
return build_function_call (objc_exception_extract_decl, func_params);
}
static void
objc_build_try_exit_fragment (void)
{
/* objc_exception_try_exit(&_stackExceptionData); */
tree func_params
= tree_cons (NULL_TREE,
build_unary_op (ADDR_EXPR,
TREE_VALUE (objc_stack_exception_data), 0),
NULL_TREE);
assemble_external (objc_exception_try_exit_decl);
c_expand_expr_stmt (build_function_call (objc_exception_try_exit_decl,
func_params));
}
static void
objc_build_extract_fragment (void)
{
/* } else {
_rethrowException = objc_exception_extract(&_stackExceptionData);
} */
objc_exit_block ();
c_finish_then ();
c_expand_start_else ();
objc_enter_block ();
c_expand_expr_stmt (build_modify_expr
(TREE_VALUE (objc_rethrow_exception),
NOP_EXPR,
objc_build_extract_expr ()));
objc_exit_block ();
c_finish_else ();
c_expand_end_cond ();
if_nesting_count--;
}
tree
objc_build_try_prologue (void)
{
/* { // new scope
struct _objc_exception_data _stackExceptionData;
volatile id _rethrowException = nil;
{ // begin TRY-CATCH scope
objc_exception_try_enter(&_stackExceptionData);
if (!_setjmp(&_stackExceptionData.buf)) { */
tree try_catch_block;
if (!flag_objc_exceptions)
fatal_error ("Use `-fobjc-exceptions' to enable Objective-C exception syntax");
objc_mark_locals_volatile ((void *)(exc_binding_stack
? exc_binding_stack->val
: 0));
objc_enter_block ();
objc_stack_exception_data
= tree_cons (NULL_TREE,
objc_declare_variable (RID_AUTO,
get_identifier (UTAG_EXCDATA_VAR),
xref_tag (RECORD_TYPE,
get_identifier (UTAG_EXCDATA)),
NULL_TREE),
objc_stack_exception_data);
objc_rethrow_exception = tree_cons (NULL_TREE,
objc_declare_variable (RID_VOLATILE,
get_identifier (UTAG_RETHROWEXC_VAR),
id_type,
build_int_2 (0, 0)),
objc_rethrow_exception);
try_catch_block = objc_enter_block ();
val_stack_push (&exc_binding_stack, (long) get_current_scope ());
objc_build_try_enter_fragment ();
return try_catch_block;
}
void
objc_build_try_epilogue (int also_catch_prologue)
{
if (also_catch_prologue)
{
/* } else {
register id _caughtException = objc_exception_extract( &_stackExceptionData);
objc_exception_try_enter(&_stackExceptionData);
if(!_setjmp(&_stackExceptionData.buf)) {
if (0) { */
tree if_stmt;
objc_exit_block ();
c_finish_then ();
c_expand_start_else ();
objc_enter_block ();
objc_caught_exception
= tree_cons (NULL_TREE,
objc_declare_variable (RID_REGISTER,
get_identifier (UTAG_CAUGHTEXC_VAR),
id_type,
objc_build_extract_expr ()),
objc_caught_exception);
objc_build_try_enter_fragment ();
val_stack_push (&catch_count_stack, 1);
if_stmt = c_begin_if_stmt ();
if_nesting_count++;
c_expand_start_cond (c_common_truthvalue_conversion (boolean_false_node),
0, if_stmt);
objc_enter_block ();
/* Start a new chain of @catch statements for this @try. */
objc_catch_type = tree_cons (objc_catch_type, NULL_TREE, NULL_TREE);
}
else
{ /* !also_catch_prologue */
/* } else {
_rethrowException = objc_exception_extract( &_stackExceptionData);
}
} */
objc_build_extract_fragment ();
objc_exit_block ();
}
}
void
objc_build_catch_stmt (tree catch_expr)
{
/* } else if (objc_exception_match(objc_get_class("SomeClass"), _caughtException)) {
register SomeClass *e = _caughtException; */
tree if_stmt, cond, func_params, prev_catch, var_name, var_type;
int catch_id;
#ifndef OBJCPLUS
/* Yet another C/C++ impedance mismatch. */
catch_expr = TREE_PURPOSE (catch_expr);
#endif
var_name = TREE_VALUE (catch_expr);
var_type = TREE_VALUE (TREE_PURPOSE (catch_expr));
if (TREE_CODE (var_name) == INDIRECT_REF)
var_name = TREE_OPERAND (var_name, 0);
if (TREE_CODE (var_type) == TYPE_DECL
|| TREE_CODE (var_type) == POINTER_TYPE)
var_type = TREE_TYPE (var_type);
catch_id = (var_type == TREE_TYPE (id_type));
if (!flag_objc_exceptions)
fatal_error ("Use `-fobjc-exceptions' to enable Objective-C exception syntax");
if (!(catch_id || TYPED_OBJECT (var_type)))
fatal_error ("`@catch' parameter is not a known Objective-C class type");
/* Examine previous @catch clauses for the current @try block for
superclasses of the 'var_type' class. */
for (prev_catch = objc_catch_type; TREE_VALUE (prev_catch);
prev_catch = TREE_CHAIN (prev_catch))
{
if (TREE_VALUE (prev_catch) == TREE_TYPE (id_type))
{
warning ("Exception already handled by preceding `@catch(id)'");
break;
}
else if (!catch_id
&& objc_comptypes (TREE_VALUE (prev_catch), var_type, 0) == 1)
warning ("Exception of type `%s *' already handled by `@catch (%s *)'",
IDENTIFIER_POINTER (OBJC_TYPE_NAME (var_type)),
IDENTIFIER_POINTER (OBJC_TYPE_NAME (TREE_VALUE (prev_catch))));
}
objc_catch_type = tree_cons (NULL_TREE, var_type, objc_catch_type);
objc_exit_block ();
c_finish_then ();
c_expand_start_else ();
catch_count_stack->val++;
if_stmt = c_begin_if_stmt ();
if_nesting_count++;
if (catch_id)
cond = integer_one_node;
else
{
cond = get_class_reference (OBJC_TYPE_NAME (var_type));
func_params
= tree_cons (NULL_TREE, cond,
tree_cons (NULL_TREE,
TREE_VALUE (objc_caught_exception),
NULL_TREE));
assemble_external (objc_exception_match_decl);
cond = build_function_call (objc_exception_match_decl, func_params);
}
c_expand_start_cond (c_common_truthvalue_conversion (cond),
0, if_stmt);
objc_enter_block ();
objc_declare_variable (RID_REGISTER, var_name,
build_pointer_type (var_type),
TREE_VALUE (objc_caught_exception));
}
void
objc_build_catch_epilogue (void)
{
/* } else {
_rethrowException = _caughtException;
objc_exception_try_exit(&_stackExceptionData);
}
} else {
_rethrowException = objc_exception_extract(&_stackExceptionData);
}
}
} // end TRY-CATCH scope
*/
objc_exit_block ();
c_finish_then ();
c_expand_start_else ();
objc_enter_block ();
c_expand_expr_stmt
(build_modify_expr
(TREE_VALUE (objc_rethrow_exception),
NOP_EXPR,
TREE_VALUE (objc_caught_exception)));
objc_build_try_exit_fragment ();
objc_exit_block ();
while (catch_count_stack->val--)
{
c_finish_else (); /* close off all the nested ifs ! */
c_expand_end_cond ();
if_nesting_count--;
}
val_stack_pop (&catch_count_stack);
objc_caught_exception = TREE_CHAIN (objc_caught_exception);
objc_build_extract_fragment ();
objc_exit_block ();
c_finish_else ();
c_expand_end_cond ();
if_nesting_count--;
objc_exit_block ();
/* Return to enclosing chain of @catch statements (if any). */
while (TREE_VALUE (objc_catch_type))
objc_catch_type = TREE_CHAIN (objc_catch_type);
objc_catch_type = TREE_PURPOSE (objc_catch_type);
}
tree
objc_build_finally_prologue (void)
{
/* { // begin FINALLY scope
if (!_rethrowException) {
objc_exception_try_exit(&_stackExceptionData);
} */
tree blk = objc_enter_block ();
tree if_stmt = c_begin_if_stmt ();
if_nesting_count++;
c_expand_start_cond (c_common_truthvalue_conversion
(build_unary_op
(TRUTH_NOT_EXPR,
TREE_VALUE (objc_rethrow_exception), 0)),
0, if_stmt);
objc_enter_block ();
objc_build_try_exit_fragment ();
objc_exit_block ();
c_finish_then ();
c_expand_end_cond ();
if_nesting_count--;
return blk;
}
tree
objc_build_finally_epilogue (void)
{
/* if (_rethrowException) {
objc_exception_throw(_rethrowException);
}
} // end FINALLY scope
} */
tree if_stmt = c_begin_if_stmt ();
if_nesting_count++;
c_expand_start_cond
(c_common_truthvalue_conversion (TREE_VALUE (objc_rethrow_exception)),
0, if_stmt);
objc_enter_block ();
objc_build_throw_stmt (TREE_VALUE (objc_rethrow_exception));
objc_exit_block ();
c_finish_then ();
c_expand_end_cond ();
if_nesting_count--;
objc_exit_block ();
objc_rethrow_exception = TREE_CHAIN (objc_rethrow_exception);
objc_stack_exception_data = TREE_CHAIN (objc_stack_exception_data);
val_stack_pop (&exc_binding_stack);
return objc_exit_block ();
}
tree
objc_build_try_catch_finally_stmt (int has_catch, int has_finally)
{
/* NB: The operative assumption here is that TRY_FINALLY_EXPR will
deal with all exits from 'try_catch_blk' and route them through
'finally_blk'. */
tree outer_blk = objc_build_finally_epilogue ();
tree prec_stmt = TREE_CHAIN (TREE_CHAIN (COMPOUND_BODY (outer_blk)));
tree try_catch_blk = TREE_CHAIN (prec_stmt), try_catch_expr;
tree finally_blk = TREE_CHAIN (try_catch_blk), finally_expr;
tree succ_stmt = TREE_CHAIN (finally_blk);
tree try_finally_stmt, try_finally_expr;
if (!flag_objc_exceptions)
fatal_error ("Use `-fobjc-exceptions' to enable Objective-C exception syntax");
/* It is an error to have a @try block without a @catch and/or @finally
(even though sensible code can be generated nonetheless). */
if (!has_catch && !has_finally)
error ("`@try' without `@catch' or `@finally'");
/* We shall now do something truly disgusting. We shall remove the
'try_catch_blk' and 'finally_blk' from the 'outer_blk' statement
chain, and replace them with a TRY_FINALLY_EXPR statement! If
this doesn't work, we will have to learn (from Per/gcj) how to
construct the 'outer_blk' lazily. */
TREE_CHAIN (try_catch_blk) = TREE_CHAIN (finally_blk) = NULL_TREE;
try_catch_expr = build1 (STMT_EXPR, void_type_node, try_catch_blk);
TREE_SIDE_EFFECTS (try_catch_expr) = 1;
finally_expr = build1 (STMT_EXPR, void_type_node, finally_blk);
TREE_SIDE_EFFECTS (finally_expr) = 1;
try_finally_expr = build (TRY_FINALLY_EXPR, void_type_node, try_catch_expr,
finally_expr);
TREE_SIDE_EFFECTS (try_finally_expr) = 1;
try_finally_stmt = build_stmt (EXPR_STMT, try_finally_expr);
TREE_CHAIN (prec_stmt) = try_finally_stmt;
TREE_CHAIN (try_finally_stmt) = succ_stmt;
return outer_blk; /* the whole enchilada */
}
void
objc_build_synchronized_prologue (tree sync_expr)
{
/* {
id _eval_once = <sync_expr>;
@try {
objc_sync_enter( _eval_once ); */
tree func_params;
if (!flag_objc_exceptions)
fatal_error ("Use `-fobjc-exceptions' to enable Objective-C exception syntax");
objc_enter_block ();
objc_eval_once
= tree_cons (NULL_TREE,
objc_declare_variable (RID_AUTO,
get_identifier (UTAG_EVALONCE_VAR),
id_type,
sync_expr),
objc_eval_once);
objc_build_try_prologue ();
objc_enter_block ();
func_params = tree_cons (NULL_TREE,
TREE_VALUE (objc_eval_once),
NULL_TREE);
assemble_external (objc_sync_enter_decl);
c_expand_expr_stmt (build_function_call
(objc_sync_enter_decl, func_params));
}
tree
objc_build_synchronized_epilogue (void)
{
/* }
@finally {
objc_sync_exit( _eval_once );
}
} */
tree func_params;
objc_exit_block ();
objc_build_try_epilogue (0);
objc_build_finally_prologue ();
func_params = tree_cons (NULL_TREE, TREE_VALUE (objc_eval_once),
NULL_TREE);
assemble_external (objc_sync_exit_decl);
c_expand_expr_stmt (build_function_call (objc_sync_exit_decl,
func_params));
objc_build_try_catch_finally_stmt (0, 1);
return objc_exit_block ();
}
/* Predefine the following data type:
struct _objc_exception_data
{
int buf[_JBLEN];
void *pointers[4];
}; */
/* The following yuckiness should prevent users from having to #include
<setjmp.h> in their code... */
#ifdef TARGET_POWERPC
/* snarfed from /usr/include/ppc/setjmp.h */
#define _JBLEN (26 + 36 + 129 + 1)
#else
/* snarfed from /usr/include/i386/{setjmp,signal}.h */
#define _JBLEN 18
#endif
static void
build_objc_exception_stuff (void)
{
tree field_decl, field_decl_chain, index, temp_type;
/* Suppress outputting debug symbols, because
dbxout_init hasn't been called yet. */
enum debug_info_type save_write_symbols = write_symbols;
const struct gcc_debug_hooks *save_hooks = debug_hooks;
write_symbols = NO_DEBUG;
debug_hooks = &do_nothing_debug_hooks;
objc_exception_data_template
= start_struct (RECORD_TYPE, get_identifier (UTAG_EXCDATA));
/* int buf[_JBLEN]; */