| /* Next Runtime (ABI-2) private. |
| Copyright (C) 2011-2022 Free Software Foundation, Inc. |
| |
| Contributed by Iain Sandoe and based, in part, on an implementation in |
| 'branches/apple/trunk' contributed by Apple Computer Inc. |
| |
| 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 3, 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 COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| /* The NeXT ABI2 is used for m64 implementations on Darwin/OSX machines. |
| |
| This version is intended to match (logically) the output of Apple's |
| 4.2.1 compiler. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "tree.h" |
| #include "stringpool.h" |
| #include "attribs.h" |
| |
| #ifdef OBJCPLUS |
| #include "cp/cp-tree.h" |
| #else |
| #include "c/c-tree.h" |
| #include "c/c-lang.h" |
| #endif |
| #include "langhooks.h" |
| #include "c-family/c-objc.h" |
| #include "objc-act.h" |
| |
| /* When building Objective-C++, we are not linking against the C front-end |
| and so need to replicate the C tree-construction functions in some way. */ |
| #ifdef OBJCPLUS |
| #define OBJCP_REMAP_FUNCTIONS |
| #include "objcp-decl.h" |
| #endif /* OBJCPLUS */ |
| |
| #include "target.h" |
| #include "tree-iterator.h" |
| #include "opts.h" |
| |
| #include "objc-runtime-hooks.h" |
| #include "objc-runtime-shared-support.h" |
| #include "objc-next-metadata-tags.h" |
| #include "objc-encoding.h" |
| |
| /* ABI 2 Private definitions. */ |
| #define DEF_CONSTANT_STRING_CLASS_NAME "NSConstantString" |
| |
| #define TAG_GETCLASS "objc_getClass" |
| #define TAG_GETMETACLASS "objc_getMetaClass" |
| |
| #define TAG_MSGSEND "objc_msgSend" |
| #define TAG_MSGSENDID "objc_msgSendId" |
| #define TAG_MSGSENDSUPER "objc_msgSendSuper2" |
| #define TAG_MSGSEND_STRET "objc_msgSend_stret" |
| #define TAG_MSGSENDID_STRET "objc_msgSendId_stret" |
| #define TAG_MSGSENDSUPER_STRET "objc_msgSendSuper2_stret" |
| |
| #define USE_FIXUP_BEFORE 100600 |
| #define TAG_FIXUP "_fixup" |
| |
| |
| #define TAG_NEXT_EHVTABLE_NAME "objc_ehtype_vtable" |
| #define TAG_V2_EH_TYPE "objc_ehtype_t" |
| |
| #define UTAG_V2_CLASS "_class_t" |
| #define UTAG_V2_CLASS_RO "_class_ro_t" |
| #define UTAG_V2_PROTOCOL "_protocol_t" |
| #define UTAG_V2_PROTOCOL_LIST "_protocol_list_t" |
| |
| #define UTAG_V2_EH_TYPE "_objc_ehtype_t" |
| |
| #define OBJC2_CLS_HAS_CXX_STRUCTORS 0x0004L |
| |
| enum objc_v2_tree_index |
| { |
| /* Templates. */ |
| OCTI_V2_CLS_TEMPL, |
| OCTI_V2_CAT_TEMPL, |
| OCTI_V2_CLS_RO_TEMPL, |
| OCTI_V2_PROTO_TEMPL, |
| OCTI_V2_IVAR_TEMPL, |
| OCTI_V2_IVAR_LIST_TEMPL, |
| OCTI_V2_MESSAGE_REF_TEMPL, |
| OCTI_V2_SUPER_MESSAGE_REF_TEMPL, |
| |
| OCTI_V2_MESSAGE_SELECTOR_TYPE, |
| OCTI_V2_SUPER_MESSAGE_SELECTOR_TYPE, |
| OCTI_V2_IMP_TYPE, |
| OCTI_V2_SUPER_IMP_TYPE, |
| |
| OCTI_V2_CACHE_DECL, |
| OCTI_V2_VTABLE_DECL, |
| |
| OCTI_V2_PROPERTY_TEMPL, |
| |
| /* V2 messaging. */ |
| OCTI_V2_UMSG_FIXUP_DECL, |
| OCTI_V2_UMSG_STRET_FIXUP_DECL, |
| OCTI_V2_UMSG_ID_FIXUP_DECL, |
| OCTI_V2_UMSG_ID_STRET_FIXUP_DECL, |
| OCTI_V2_UMSG_SUPER2_FIXUP_DECL, |
| OCTI_V2_UMSG_SUPER2_STRET_FIXUP_DECL, |
| |
| /* Exceptions - related. */ |
| OCTI_V2_BEGIN_CATCH_DECL, |
| OCTI_V2_END_CATCH_DECL, |
| OCTI_V2_RETHROW_DECL, |
| |
| OCTI_V2_MAX |
| }; |
| |
| #define objc_v2_class_template objc_v2_global_trees[OCTI_V2_CLS_TEMPL] |
| #define objc_v2_class_ro_template \ |
| objc_v2_global_trees[OCTI_V2_CLS_RO_TEMPL] |
| #define objc_v2_category_template \ |
| objc_v2_global_trees[OCTI_V2_CAT_TEMPL] |
| #define objc_v2_protocol_template \ |
| objc_v2_global_trees[OCTI_V2_PROTO_TEMPL] |
| |
| /* struct message_ref_t */ |
| #define objc_v2_message_ref_template \ |
| objc_v2_global_trees[OCTI_V2_MESSAGE_REF_TEMPL] |
| |
| #define objc_v2_ivar_list_ptr objc_v2_global_trees[OCTI_V2_IVAR_LIST_TEMPL] |
| |
| /* struct super_message_ref_t */ |
| #define objc_v2_super_message_ref_template \ |
| objc_v2_global_trees[OCTI_V2_SUPER_MESSAGE_REF_TEMPL] |
| |
| /* struct message_ref_t* */ |
| #define objc_v2_selector_type objc_v2_global_trees[OCTI_V2_MESSAGE_SELECTOR_TYPE] |
| /* struct super_super_message_ref_t */ |
| #define objc_v2_super_selector_type \ |
| objc_v2_global_trees[OCTI_V2_SUPER_MESSAGE_SELECTOR_TYPE] |
| #define objc_v2_imp_type objc_v2_global_trees[OCTI_V2_IMP_TYPE] |
| #define objc_v2_super_imp_type objc_v2_global_trees[OCTI_V2_SUPER_IMP_TYPE] |
| |
| #define UOBJC_V2_CACHE_decl objc_v2_global_trees[OCTI_V2_CACHE_DECL] |
| #define UOBJC_V2_VTABLE_decl objc_v2_global_trees[OCTI_V2_VTABLE_DECL] |
| |
| #define objc_v2_ivar_template objc_v2_global_trees[OCTI_V2_IVAR_TEMPL] |
| #define objc_v2_property_template \ |
| objc_v2_global_trees[OCTI_V2_PROPERTY_TEMPL] |
| |
| /* V2 Messaging */ |
| |
| /* objc_msgSend_fixup_rtp */ |
| #define umsg_fixup_decl objc_v2_global_trees[OCTI_V2_UMSG_FIXUP_DECL] |
| /* objc_msgSend_stret_fixup_rtp */ |
| #define umsg_stret_fixup_decl objc_v2_global_trees[OCTI_V2_UMSG_STRET_FIXUP_DECL] |
| /* objc_msgSendId_fixup_rtp */ |
| #define umsg_id_fixup_decl objc_v2_global_trees[OCTI_V2_UMSG_ID_FIXUP_DECL] |
| /* objc_msgSendId_stret_fixup_rtp */ |
| #define umsg_id_stret_fixup_decl \ |
| objc_v2_global_trees[OCTI_V2_UMSG_ID_STRET_FIXUP_DECL] |
| /* objc_msgSendSuper2_fixup_rtp */ |
| #define umsg_id_super2_fixup_decl \ |
| objc_v2_global_trees[OCTI_V2_UMSG_SUPER2_FIXUP_DECL] |
| /* objc_msgSendSuper2_stret_fixup_rtp */ |
| #define umsg_id_super2_stret_fixup_decl \ |
| objc_v2_global_trees[OCTI_V2_UMSG_SUPER2_STRET_FIXUP_DECL] |
| |
| #define objc2_begin_catch_decl objc_v2_global_trees[OCTI_V2_BEGIN_CATCH_DECL] |
| #define objc2_end_catch_decl objc_v2_global_trees[OCTI_V2_END_CATCH_DECL] |
| #define objc_rethrow_exception_decl \ |
| objc_v2_global_trees[OCTI_V2_RETHROW_DECL] |
| |
| /* The OCTI_V2_... enumeration itself is in above. */ |
| static GTY(()) tree objc_v2_global_trees[OCTI_V2_MAX]; |
| |
| static void next_runtime_02_initialize (void); |
| |
| static void build_v2_message_ref_templates (void); |
| static void build_v2_class_templates (void); |
| static void build_v2_super_template (void); |
| static void build_v2_category_template (void); |
| static void build_v2_protocol_template (void); |
| |
| static tree next_runtime_abi_02_super_superclassfield_id (void); |
| |
| static tree next_runtime_abi_02_class_decl (tree); |
| static tree next_runtime_abi_02_metaclass_decl (tree); |
| static tree next_runtime_abi_02_category_decl (tree); |
| static tree next_runtime_abi_02_protocol_decl (tree); |
| static tree next_runtime_abi_02_string_decl (tree, const char *, string_section); |
| |
| static tree next_runtime_abi_02_get_class_reference (tree); |
| static tree next_runtime_abi_02_build_selector_reference (location_t, tree, tree); |
| static tree next_runtime_abi_02_get_protocol_reference (location_t, tree); |
| static tree next_runtime_abi_02_build_ivar_ref (location_t, tree, tree); |
| static tree next_runtime_abi_02_get_class_super_ref (location_t, struct imp_entry *, bool); |
| static tree next_runtime_abi_02_get_category_super_ref (location_t, struct imp_entry *, bool); |
| |
| static tree next_runtime_abi_02_receiver_is_class_object (tree); |
| static void next_runtime_abi_02_get_arg_type_list_base (vec<tree, va_gc> **, |
| tree, int, int); |
| static tree next_runtime_abi_02_build_objc_method_call (location_t, tree, tree, |
| tree, tree, tree, int); |
| static bool next_runtime_abi_02_setup_const_string_class_decl (void); |
| static tree next_runtime_abi_02_build_const_string_constructor (location_t, tree, int); |
| |
| static tree create_extern_decl (tree, const char *); |
| |
| static void objc_generate_v2_next_metadata (void); |
| static bool objc2_objc_exception_attr (tree); |
| |
| /* void build_v2_protocol_reference (tree);*/ |
| static void build_v2_ehtype_template (void); |
| static void build_v2_eh_catch_objects (void); |
| static tree next_runtime_02_eh_type (tree); |
| static tree objc_eh_personality (void); |
| static tree build_throw_stmt (location_t, tree, bool); |
| static tree objc_build_exc_ptr (struct objc_try_context **); |
| static tree begin_catch (struct objc_try_context **, tree, tree, tree, bool); |
| static void finish_catch (struct objc_try_context **, tree); |
| static tree finish_try_stmt (struct objc_try_context **); |
| |
| /* TODO: Use an objc-map. */ |
| static GTY ((length ("SIZEHASHTABLE"))) hash *extern_names; |
| |
| bool |
| objc_next_runtime_abi_02_init (objc_runtime_hooks *rthooks) |
| { |
| extern_names = ggc_cleared_vec_alloc<hash> (SIZEHASHTABLE); |
| |
| if (flag_objc_sjlj_exceptions) |
| { |
| inform (UNKNOWN_LOCATION, |
| "%<-fobjc-sjlj-exceptions%> is ignored for " |
| "%<-fnext-runtime%> when %<-fobjc-abi-version%> " |
| "greater than 1"); |
| flag_objc_sjlj_exceptions = 0; |
| } |
| |
| /* NeXT ABI 2 is intended to default to checking for nil receivers. */ |
| if (! OPTION_SET_P (flag_objc_nilcheck)) |
| flag_objc_nilcheck = 1; |
| |
| rthooks->initialize = next_runtime_02_initialize; |
| rthooks->default_constant_string_class_name = DEF_CONSTANT_STRING_CLASS_NAME; |
| rthooks->tag_getclass = TAG_GETCLASS; |
| rthooks->super_superclassfield_ident = next_runtime_abi_02_super_superclassfield_id; |
| |
| rthooks->class_decl = next_runtime_abi_02_class_decl; |
| rthooks->metaclass_decl = next_runtime_abi_02_metaclass_decl; |
| rthooks->category_decl = next_runtime_abi_02_category_decl; |
| rthooks->protocol_decl = next_runtime_abi_02_protocol_decl; |
| rthooks->string_decl = next_runtime_abi_02_string_decl; |
| |
| rthooks->get_class_reference = next_runtime_abi_02_get_class_reference; |
| rthooks->build_selector_reference = next_runtime_abi_02_build_selector_reference; |
| rthooks->get_protocol_reference = next_runtime_abi_02_get_protocol_reference; |
| rthooks->build_ivar_reference = next_runtime_abi_02_build_ivar_ref; |
| rthooks->get_class_super_ref = next_runtime_abi_02_get_class_super_ref; |
| rthooks->get_category_super_ref = next_runtime_abi_02_get_category_super_ref; |
| |
| rthooks->receiver_is_class_object = next_runtime_abi_02_receiver_is_class_object; |
| rthooks->get_arg_type_list_base = next_runtime_abi_02_get_arg_type_list_base; |
| rthooks->build_objc_method_call = next_runtime_abi_02_build_objc_method_call; |
| |
| rthooks->setup_const_string_class_decl = |
| next_runtime_abi_02_setup_const_string_class_decl; |
| rthooks->build_const_string_constructor = |
| next_runtime_abi_02_build_const_string_constructor; |
| |
| rthooks->build_throw_stmt = build_throw_stmt; |
| rthooks->build_exc_ptr = objc_build_exc_ptr; |
| rthooks->begin_catch = begin_catch; |
| rthooks->finish_catch = finish_catch; |
| rthooks->finish_try_stmt = finish_try_stmt; |
| |
| rthooks->generate_metadata = objc_generate_v2_next_metadata; |
| return true; |
| } |
| |
| /* We need a way to convey what kind of meta-data are represented by a given |
| variable, since each type is expected (by the runtime) to be found in a |
| specific named section. The solution must be usable with LTO. |
| |
| The scheme used for NeXT ABI 0/1 (partial matching of variable names) is not |
| satisfactory when LTO is used with ABI-2. We now tag ObjC meta-data with |
| identification attributes in the front end. The back-end may choose to act |
| on these as it requires. */ |
| |
| static void |
| next_runtime_abi_02_init_metadata_attributes (void) |
| { |
| if (!objc_meta) |
| objc_meta = get_identifier ("OBJC2META"); |
| |
| if (!meta_base) |
| meta_base = get_identifier ("V2_BASE"); |
| |
| meta_class = get_identifier ("G2_CLAS"); |
| meta_metaclass = get_identifier ("G2_META"); |
| meta_category = meta_base; |
| meta_protocol = get_identifier ("V2_PCOL"); |
| |
| meta_clac_vars = |
| meta_clai_vars = meta_base; |
| |
| meta_clac_meth = |
| meta_clai_meth = |
| meta_catc_meth = |
| meta_cati_meth = |
| meta_proto_cls_meth = |
| meta_proto_nst_meth = meta_base; |
| |
| meta_clas_prot = |
| meta_catg_prot = meta_base; |
| |
| meta_sel_refs = get_identifier ("V2_SRFS"); |
| |
| meta_class_name = get_identifier ("V2_CNAM"); |
| meta_meth_name = get_identifier ("V2_MNAM"); |
| |
| meta_meth_type = get_identifier ("V2_MTYP"); |
| meta_prop_name_attr = get_identifier ("V2_STRG"); |
| |
| meta_mref = get_identifier ("V2_MREF"); |
| meta_class_ref = get_identifier ("V2_CLRF"); |
| meta_superclass_ref = get_identifier ("V2_SURF"); |
| |
| meta_label_classlist = get_identifier ("V2_CLAB"); |
| meta_label_nonlazy_classlist = get_identifier ("V2_NLCL"); |
| meta_label_categorylist = get_identifier ("V2_CALA"); |
| meta_label_nonlazy_categorylist = get_identifier ("V2_NLCA"); |
| |
| meta_label_protocollist = get_identifier ("V2_PLST"); |
| meta_proto_ref = get_identifier ("V2_PRFS"); |
| |
| meta_info = get_identifier ("V2_INFO"); |
| |
| meta_ehtype = get_identifier ("V2_EHTY"); |
| |
| meta_const_str = get_identifier ("V2_CSTR"); |
| |
| meta_ivar_ref = get_identifier ("V2_IVRF"); |
| } |
| |
| static void next_runtime_02_initialize (void) |
| { |
| tree type; |
| #ifdef OBJCPLUS |
| /* For all NeXT objc ABIs -fobjc-call-cxx-cdtors is on by |
| default. */ |
| if (!OPTION_SET_P (flag_objc_call_cxx_cdtors)) |
| global_options.x_flag_objc_call_cxx_cdtors = 1; |
| #endif |
| |
| /* Set up attributes to be attached to the meta-data so that they |
| will be placed in the correct sections. */ |
| next_runtime_abi_02_init_metadata_attributes (); |
| |
| /* `struct objc_selector *' */ |
| objc_selector_type = build_pointer_type (xref_tag (RECORD_TYPE, |
| get_identifier (TAG_SELECTOR))); |
| |
| /* SEL typedef. */ |
| type = lang_hooks.decls.pushdecl (build_decl (input_location, |
| TYPE_DECL, |
| objc_selector_name, |
| objc_selector_type)); |
| suppress_warning (type); |
| |
| /* IMP : id (*) (id, _message_ref_t*, ...) |
| SUPER_IMP : id (*) ( super_t*, _super_message_ref_t*, ...) |
| objc_v2_selector_type. */ |
| build_v2_message_ref_templates (); |
| |
| objc_v2_ivar_list_ptr = |
| build_pointer_type (xref_tag (RECORD_TYPE, |
| get_identifier ("_ivar_list_t"))); |
| |
| objc_prop_list_ptr = |
| build_pointer_type (xref_tag (RECORD_TYPE, |
| get_identifier ("_prop_list_t"))); |
| |
| build_v2_class_templates (); |
| build_v2_super_template (); |
| build_v2_protocol_template (); |
| build_v2_category_template (); |
| |
| bool fixup_p = flag_next_runtime < USE_FIXUP_BEFORE; |
| if (fixup_p) |
| { |
| /* id objc_msgSend_fixup_rtp (id, struct message_ref_t*, ...); */ |
| type = build_varargs_function_type_list (objc_object_type, |
| objc_object_type, |
| objc_v2_selector_type, |
| NULL_TREE); |
| } |
| else |
| { |
| /* id objc_msgSendXXXX (id, SEL, ...); */ |
| type = build_varargs_function_type_list (objc_object_type, |
| objc_object_type, |
| objc_selector_type, |
| NULL_TREE); |
| } |
| const char *fnam = fixup_p ? TAG_MSGSEND TAG_FIXUP : TAG_MSGSEND; |
| umsg_fixup_decl = add_builtin_function (fnam, type, 0, NOT_BUILT_IN, |
| NULL, NULL_TREE); |
| TREE_NOTHROW (umsg_fixup_decl) = 0; |
| |
| /* id objc_msgSend_stret_fixup_rtp (id, struct message_ref_t*, ...); */ |
| fnam = fixup_p ? TAG_MSGSEND_STRET TAG_FIXUP : TAG_MSGSEND_STRET; |
| umsg_stret_fixup_decl = add_builtin_function (fnam, type, 0, NOT_BUILT_IN, |
| NULL, NULL_TREE); |
| TREE_NOTHROW (umsg_stret_fixup_decl) = 0; |
| |
| /* id objc_msgSendId_fixup_rtp (id, struct message_ref_t*, ...); */ |
| fnam = fixup_p ? TAG_MSGSENDID TAG_FIXUP : TAG_MSGSENDID; |
| umsg_id_fixup_decl = add_builtin_function (fnam, type, 0, NOT_BUILT_IN, |
| NULL, NULL_TREE); |
| TREE_NOTHROW (umsg_id_fixup_decl) = 0; |
| |
| /* id objc_msgSendId_stret_fixup_rtp (id, struct message_ref_t*, ...); */ |
| fnam = fixup_p ? TAG_MSGSENDID_STRET TAG_FIXUP : TAG_MSGSENDID_STRET; |
| umsg_id_stret_fixup_decl = add_builtin_function (fnam, type, 0, NOT_BUILT_IN, |
| NULL, NULL_TREE); |
| TREE_NOTHROW (umsg_id_stret_fixup_decl) = 0; |
| |
| /* id objc_msgSendSuper2_fixup_rtp |
| (struct objc_super *, struct message_ref_t*, ...); */ |
| type = build_varargs_function_type_list (objc_object_type, |
| objc_super_type, |
| objc_v2_super_selector_type, |
| NULL_TREE); |
| fnam = fixup_p ? TAG_MSGSENDSUPER TAG_FIXUP : TAG_MSGSENDSUPER; |
| umsg_id_super2_fixup_decl = add_builtin_function (fnam, type, 0, NOT_BUILT_IN, |
| NULL, NULL_TREE); |
| TREE_NOTHROW (umsg_id_super2_fixup_decl) = 0; |
| |
| /* id objc_msgSendSuper2_stret_fixup_rtp |
| (struct objc_super *, struct message_ref_t*, ...); */ |
| fnam = fixup_p ? TAG_MSGSENDSUPER_STRET TAG_FIXUP : TAG_MSGSENDSUPER_STRET; |
| umsg_id_super2_stret_fixup_decl = add_builtin_function (fnam, type, 0, |
| NOT_BUILT_IN, NULL, |
| NULL_TREE); |
| TREE_NOTHROW (umsg_id_super2_stret_fixup_decl) = 0; |
| |
| /* Present in the library, but unused by the FE. */ |
| /* Protocol *objc_getProtocol (const char *) |
| type = build_function_type_list (objc_protocol_type, |
| const_string_type_node, |
| NULL_TREE); |
| objc_v2_getprotocol_decl = add_builtin_function ("objc_getProtocol", |
| type, 0, NOT_BUILT_IN, |
| NULL, NULL_TREE); |
| TREE_NOTHROW (objc_v2_getprotocol_decl) = 0;*/ |
| |
| UOBJC_V2_CACHE_decl = create_extern_decl (ptr_type_node, |
| "_objc_empty_cache"); |
| |
| UOBJC_V2_VTABLE_decl = create_extern_decl (objc_v2_imp_type, |
| "_objc_empty_vtable"); |
| |
| /* id objc_getClass (const char *); */ |
| type = build_function_type_list (objc_object_type, |
| const_string_type_node, |
| NULL_TREE); |
| objc_get_class_decl = add_builtin_function (TAG_GETCLASS, |
| type, 0, NOT_BUILT_IN, |
| NULL, NULL_TREE); |
| |
| /* id objc_getMetaClass (const char *); */ |
| objc_get_meta_class_decl = add_builtin_function (TAG_GETMETACLASS, |
| type, 0, NOT_BUILT_IN, |
| NULL, NULL_TREE); |
| |
| /* This is the type of all of the following functions |
| objc_copyStruct(). */ |
| type = build_function_type_list (void_type_node, |
| ptr_type_node, |
| const_ptr_type_node, |
| ptrdiff_type_node, |
| boolean_type_node, |
| boolean_type_node, |
| NULL_TREE); |
| /* Declare the following function: |
| void |
| objc_copyStruct (void *destination, const void *source, |
| ptrdiff_t size, BOOL is_atomic, BOOL has_strong); */ |
| objc_copyStruct_decl = add_builtin_function ("objc_copyStruct", |
| type, 0, NOT_BUILT_IN, |
| NULL, NULL_TREE); |
| TREE_NOTHROW (objc_copyStruct_decl) = 0; |
| objc_getPropertyStruct_decl = NULL_TREE; |
| objc_setPropertyStruct_decl = NULL_TREE; |
| |
| gcc_checking_assert (!flag_objc_sjlj_exceptions); |
| |
| /* Although we warn that fobjc-exceptions is required for exceptions |
| code, we carry on and create it anyway. */ |
| |
| /* This can be required, even when exceptions code is not present, |
| when an __attribute__((objc_exception)) is applied to a |
| class. */ |
| build_v2_ehtype_template (); |
| |
| /* void * objc_begin_catch (void *) */ |
| type = build_function_type_list (ptr_type_node, |
| ptr_type_node, NULL_TREE); |
| |
| objc2_begin_catch_decl = add_builtin_function ("objc_begin_catch", |
| type, 0, NOT_BUILT_IN, |
| NULL, NULL_TREE); |
| TREE_NOTHROW (objc2_begin_catch_decl) = 0; |
| |
| /* void objc_end_catch () */ |
| type = build_function_type_list (void_type_node, NULL_TREE); |
| objc2_end_catch_decl = add_builtin_function ("objc_end_catch", |
| type, 0, NOT_BUILT_IN, |
| NULL, NULL_TREE); |
| TREE_NOTHROW (objc2_end_catch_decl) = 0; |
| |
| /* void objc_exception_rethrow (void) */ |
| objc_rethrow_exception_decl = |
| add_builtin_function ("objc_exception_rethrow", |
| type, 0, NOT_BUILT_IN, |
| NULL, NULL_TREE); |
| TREE_NOTHROW (objc_rethrow_exception_decl) = 0; |
| using_eh_for_cleanups (); |
| lang_hooks.eh_runtime_type = next_runtime_02_eh_type; |
| lang_hooks.eh_personality = objc_eh_personality; |
| } |
| |
| /* NOTE --- templates --- */ |
| |
| /* Set 'objc_v2_message_ref_template' to the data type node for |
| 'struct _message_ref_t'. This needs to be done just once per |
| compilation. Also Set 'objc_v2_super_message_ref_template' to data |
| type node for 'struct _super_message_ref_t'. */ |
| |
| /* struct _message_ref_t |
| { |
| IMP messenger; |
| SEL name; |
| }; |
| where IMP is: id (*) (id, _message_ref_t*, ...) |
| */ |
| |
| /* struct _super_message_ref_t |
| { |
| SUPER_IMP messenger; |
| SEL name; |
| }; |
| where SUPER_IMP is: id (*) ( super_t*, _super_message_ref_t*, ...) |
| */ |
| |
| static void |
| build_v2_message_ref_templates (void) |
| { |
| tree ptr_message_ref_t; |
| tree decls, *chain = NULL; |
| |
| /* struct _message_ref_t {...} */ |
| objc_v2_message_ref_template = |
| objc_start_struct (get_identifier ("_message_ref_t")); |
| |
| /* IMP messenger; */ |
| ptr_message_ref_t = |
| build_pointer_type (xref_tag (RECORD_TYPE, |
| get_identifier ("_message_ref_t"))); |
| |
| objc_v2_imp_type = |
| build_pointer_type (build_function_type_list |
| (objc_object_type, |
| objc_object_type, |
| ptr_message_ref_t, |
| NULL_TREE)); |
| |
| decls = add_field_decl (objc_v2_imp_type, "messenger", &chain); |
| |
| /* SEL name; */ |
| add_field_decl (objc_selector_type, "name", &chain); |
| |
| objc_finish_struct (objc_v2_message_ref_template, decls); |
| |
| objc_v2_selector_type = build_pointer_type (objc_v2_message_ref_template); |
| |
| chain = NULL; |
| /* struct _super_message_ref_t {...} */ |
| objc_v2_super_message_ref_template = |
| objc_start_struct (get_identifier ("_super_message_ref_t")); |
| |
| /* SUPER_IMP messenger; */ |
| ptr_message_ref_t = build_pointer_type |
| (xref_tag (RECORD_TYPE, |
| get_identifier ("_super_message_ref_t"))); |
| |
| objc_v2_super_imp_type = |
| build_pointer_type (build_function_type_list |
| (objc_object_type, |
| objc_super_type, |
| ptr_message_ref_t, |
| NULL_TREE)); |
| |
| add_field_decl (objc_v2_super_imp_type, "messenger", &chain); |
| |
| /* SEL name; */ |
| add_field_decl (objc_selector_type, "name", &chain); |
| |
| objc_finish_struct (objc_v2_super_message_ref_template, decls); |
| objc_v2_super_selector_type = |
| build_pointer_type (objc_v2_super_message_ref_template); |
| } |
| |
| /* Build following types which represent each class implementation. |
| |
| struct class_ro_t |
| { |
| uint32_t const flags; |
| uint32_t const instanceStart; |
| uint32_t const instanceSize; |
| #ifdef __LP64__ |
| uint32_t const reserved; |
| #endif |
| const uint8_t * const ivarLayout; |
| const char *const name; |
| const struct method_list_t * const baseMethods; |
| const struct objc_protocol_list *const baseProtocols; |
| const struct ivar_list_t *const ivars; |
| const uint8_t * const weakIvarLayout; |
| const struct _prop_list_t * const properties; |
| }; |
| |
| struct class_t |
| { |
| struct class_t *isa; |
| struct class_t *superclass; |
| void *cache; |
| IMP *vtable; |
| |
| ...When this is active - it will point to a rw version, but |
| when we build the meta-data we point it to the ro... |
| struct class_ro_t *data; |
| }; |
| |
| */ |
| |
| static void |
| build_v2_class_templates (void) |
| { |
| tree cnst_strg_type; |
| tree decls, *chain = NULL; |
| |
| /* struct class_ro_t {...} */ |
| objc_v2_class_ro_template = |
| objc_start_struct (get_identifier (UTAG_V2_CLASS_RO)); |
| |
| /* uint32_t const flags; */ |
| decls = add_field_decl (integer_type_node, "flags", &chain); |
| |
| /* uint32_t const instanceStart; */ |
| add_field_decl (integer_type_node, "instanceStart", &chain); |
| |
| /* uint32_t const instanceSize; */ |
| add_field_decl (integer_type_node, "instanceSize", &chain); |
| |
| /* This ABI is currently only used on m64 NeXT. We always |
| explicitly declare the alignment padding. */ |
| /* uint32_t const reserved; */ |
| add_field_decl (integer_type_node, "reserved", &chain); |
| |
| /* const uint8_t * const ivarLayout; */ |
| cnst_strg_type = build_pointer_type (unsigned_char_type_node); |
| add_field_decl (cnst_strg_type, "ivarLayout", &chain); |
| |
| /* const char *const name; */ |
| add_field_decl (string_type_node, "name", &chain); |
| |
| /* const struct method_list_t * const baseMethods; */ |
| add_field_decl (objc_method_list_ptr, "baseMethods", &chain); |
| |
| /* const struct objc_protocol_list *const baseProtocols; */ |
| add_field_decl (build_pointer_type |
| (xref_tag (RECORD_TYPE, |
| get_identifier (UTAG_V2_PROTOCOL_LIST))), |
| "baseProtocols", &chain); |
| |
| /* const struct ivar_list_t *const ivars; */ |
| add_field_decl (objc_v2_ivar_list_ptr, "ivars", &chain); |
| |
| /* const uint8_t * const weakIvarLayout; */ |
| add_field_decl (cnst_strg_type, "weakIvarLayout", &chain); |
| |
| /* struct _prop_list_t * baseProperties; */ |
| add_field_decl (objc_prop_list_ptr, "baseProperties", &chain); |
| |
| objc_finish_struct (objc_v2_class_ro_template, decls); |
| |
| chain = NULL; |
| /* struct class_t {...} */ |
| objc_v2_class_template = |
| objc_start_struct (get_identifier (UTAG_V2_CLASS)); |
| |
| /* struct class_t *isa; */ |
| decls = add_field_decl (build_pointer_type (objc_v2_class_template), |
| "isa", &chain); |
| |
| /* struct class_t * const superclass; */ |
| add_field_decl (build_pointer_type (objc_v2_class_template), |
| "superclass", &chain); |
| |
| /* void *cache; */ |
| add_field_decl (build_pointer_type (void_type_node), "cache", &chain); |
| |
| /* IMP *vtable; */ |
| add_field_decl (build_pointer_type (objc_v2_imp_type), "vtable", &chain); |
| |
| /* struct class_ro_t *ro; */ |
| add_field_decl (build_pointer_type (objc_v2_class_ro_template), "ro", &chain); |
| |
| objc_finish_struct (objc_v2_class_template, decls); |
| } |
| |
| /* struct _objc_super |
| { |
| struct _objc_object *self; |
| Class cls; |
| }; */ |
| void |
| build_v2_super_template (void) |
| { |
| tree decls, *chain = NULL; |
| |
| objc_super_template = objc_start_struct (get_identifier (UTAG_SUPER)); |
| |
| /* struct _objc_object *self; */ |
| decls = add_field_decl (objc_object_type, "self", &chain); |
| |
| /* Class cls; */ |
| add_field_decl (objc_class_type, "cls", &chain); |
| |
| objc_finish_struct (objc_super_template, decls); |
| } |
| |
| /* struct protocol_t |
| { |
| Class isa; |
| const char * const protocol_name; |
| const struct protocol_list_t * const protocol_list; |
| const struct method_list_t * const instance_methods; |
| const struct method_list_t * const class_methods; |
| const struct method_list_t * optionalInstanceMethods; |
| const struct method_list_t * optionalClassMethod |
| const struct _prop_list_t * const properties; |
| const uint32_t size; |
| const uint32_t flags; |
| const char ** extended_method_types; |
| const char * demangled_name; |
| const struct _prop_list_t * class_properties; |
| } |
| */ |
| static void |
| build_v2_protocol_template (void) |
| { |
| tree decls, *chain = NULL; |
| |
| objc_v2_protocol_template = |
| objc_start_struct (get_identifier (UTAG_V2_PROTOCOL)); |
| |
| /* Class isa; */ |
| decls = add_field_decl (objc_object_type, "isa", &chain); |
| |
| /* char *protocol_name; */ |
| add_field_decl (string_type_node, "protocol_name", &chain); |
| |
| /* const struct protocol_list_t * const protocol_list; */ |
| add_field_decl (build_pointer_type (objc_v2_protocol_template), |
| "protocol_list", &chain); |
| |
| /* const struct method_list_t * const instance_methods; */ |
| add_field_decl (objc_method_proto_list_ptr, "instance_methods", &chain); |
| |
| /* const struct method_list_t * const class_methods; */ |
| add_field_decl (objc_method_proto_list_ptr, "class_methods", &chain); |
| |
| /* const struct method_list_t * optionalInstanceMethods; */ |
| add_field_decl (objc_method_proto_list_ptr, "optionalInstanceMethods", &chain); |
| |
| /* const struct method_list_t * optionalClassMethods; */ |
| add_field_decl (objc_method_proto_list_ptr, "optionalClassMethods", &chain); |
| |
| /* struct _prop_list_t * properties; */ |
| add_field_decl (objc_prop_list_ptr, "properties", &chain); |
| |
| /* const uint32_t size; */ |
| add_field_decl (integer_type_node, "size", &chain); |
| |
| /* const uint32_t flags; */ |
| add_field_decl (integer_type_node, "flags", &chain); |
| |
| /* const char **extendedMethodTypes; */ |
| tree ptr_to_ptr_to_char = build_pointer_type (string_type_node); |
| add_field_decl (ptr_to_ptr_to_char, "extended_method_types", &chain); |
| |
| /* const char *demangledName; */ |
| add_field_decl (string_type_node, "demangled_name", &chain); |
| |
| /* const struct _prop_list_t *class_properties; */ |
| add_field_decl (objc_prop_list_ptr, "class_properties", &chain); |
| |
| objc_finish_struct (objc_v2_protocol_template, decls); |
| } |
| |
| /* Build type for a category: |
| struct category_t |
| { |
| const char * const name; |
| struct class_t *const cls; |
| const struct method_list_t * const instance_methods; |
| const struct method_list_t * const class_methods; |
| const struct protocol_list_t * const protocols; |
| const struct _prop_list_t * const properties; |
| } |
| */ |
| |
| static void |
| build_v2_category_template (void) |
| { |
| tree decls, *chain = NULL; |
| |
| objc_v2_category_template = |
| objc_start_struct (get_identifier ("_category_t")); |
| |
| /* char *name; */ |
| decls = add_field_decl (string_type_node, "name", &chain); |
| |
| /* struct class_t *const cls; */ |
| add_field_decl (build_pointer_type (objc_v2_class_template), "cls", &chain); |
| |
| /* struct method_list_t *instance_methods; */ |
| add_field_decl (objc_method_list_ptr, "instance_methods", &chain); |
| |
| /* struct method_list_t *class_methods; */ |
| add_field_decl (objc_method_list_ptr, "class_methods", &chain); |
| |
| /* struct protocol_list_t *protocol_list; */ |
| add_field_decl (build_pointer_type (objc_v2_protocol_template), |
| "protocol_list", &chain ); |
| |
| /* struct _prop_list_t * properties; */ |
| add_field_decl (objc_prop_list_ptr, "properties", &chain); |
| |
| objc_finish_struct (objc_v2_category_template, decls); |
| } |
| |
| /* NOTE --- Decls, Identifiers, Names etc. --- */ |
| |
| /* This routine is given a name and returns a matching extern variable |
| if one is found. */ |
| |
| static tree |
| hash_name_lookup (hash *hashlist, tree name) |
| { |
| hash target; |
| |
| target = hashlist[IDENTIFIER_HASH_VALUE (name) % SIZEHASHTABLE]; |
| |
| while (target) |
| { |
| if (name == DECL_NAME (target->key)) |
| return target->key; |
| |
| target = target->next; |
| } |
| return 0; |
| } |
| |
| /* This routine is given an extern variable and enters it in its hash |
| table. Note that hashing is done on its inner IDENTIFIER_NODE |
| node. */ |
| |
| static void |
| hash_name_enter (hash *hashlist, tree id) |
| { |
| hash obj; |
| int slot = IDENTIFIER_HASH_VALUE (DECL_NAME (id)) % SIZEHASHTABLE; |
| |
| obj = ggc_alloc<hashed_entry> (); |
| obj->list = 0; |
| obj->next = hashlist[slot]; |
| obj->key = id; |
| |
| hashlist[slot] = obj; /* append to front */ |
| } |
| |
| /* Create a declaration "extern <type> <name>;" |
| The var will need to be finalized (e.g. by calling finish_var_decl()). */ |
| |
| static tree |
| create_extern_decl (tree type, const char *name) |
| { |
| tree id = get_identifier (name); |
| tree var = hash_name_lookup (extern_names, id); |
| if (var) |
| return var; |
| /* New name. */ |
| var = start_var_decl (type, name); |
| TREE_STATIC (var) = 0; |
| DECL_EXTERNAL (var) = 1; |
| TREE_PUBLIC (var) = 1; |
| hash_name_enter (extern_names, var); |
| return var; |
| } |
| |
| /* Create a globally visible definition for variable NAME of a given TYPE. The |
| finish_var_decl() routine will need to be called on it afterwards. */ |
| static tree |
| create_global_decl (tree type, const char *name, bool is_def = false); |
| |
| static tree |
| create_global_decl (tree type, const char *name, bool is_def) |
| { |
| tree id = get_identifier (name); |
| tree var = hash_name_lookup (extern_names, id); |
| if (var) |
| is_def = true; |
| else |
| { |
| var = start_var_decl (type, name); |
| hash_name_enter (extern_names, var); |
| } |
| if (is_def) |
| { |
| DECL_EXTERNAL (var) = 0; |
| TREE_STATIC (var) = 1; |
| } |
| TREE_PUBLIC (var) = 1; |
| return var; |
| } |
| |
| /* Create a symbol with __attribute__ ((visibility ("hidden"))) |
| attribute (private extern). */ |
| static tree |
| create_hidden_decl (tree type, const char *name, bool is_def = false); |
| |
| static tree |
| create_hidden_decl (tree type, const char *name, bool is_def) |
| { |
| tree decl = create_global_decl (type, name, is_def); |
| DECL_VISIBILITY (decl) = VISIBILITY_HIDDEN; |
| DECL_VISIBILITY_SPECIFIED (decl) = 1; |
| return decl; |
| } |
| |
| /* Irritatingly, we have a different superclass field name for ABI=2. */ |
| /* PS/TODO: The field name does not matter, it is only used internally |
| by the compiler. We can rename it to whatever we want. ;-) */ |
| |
| static tree |
| next_runtime_abi_02_super_superclassfield_id (void) |
| { |
| /* TODO: Simplify. Just always return get_identifier ("cls"), or at |
| most look it once at startup then always return it. */ |
| if (!super_superclassfield_id) |
| super_superclassfield_id = get_identifier ("cls"); |
| return super_superclassfield_id; |
| } |
| |
| static tree |
| next_runtime_abi_02_class_decl (tree klass) |
| { |
| tree decl; |
| char buf[BUFSIZE]; |
| snprintf (buf, BUFSIZE, "OBJC_CLASS_$_%s", |
| IDENTIFIER_POINTER (CLASS_NAME (klass))); |
| /* ObjC2 classes are extern visible. */ |
| decl = create_global_decl (objc_v2_class_template, buf); |
| OBJCMETA (decl, objc_meta, meta_class); |
| return decl; |
| } |
| |
| static tree |
| next_runtime_abi_02_metaclass_decl (tree klass) |
| { |
| tree decl; |
| char buf[BUFSIZE]; |
| snprintf (buf, BUFSIZE, "OBJC_METACLASS_$_%s", |
| IDENTIFIER_POINTER (CLASS_NAME (klass))); |
| /* ObjC2 classes are extern visible. */ |
| decl = create_global_decl (objc_v2_class_template, buf); |
| OBJCMETA (decl, objc_meta, meta_metaclass); |
| return decl; |
| } |
| |
| static tree |
| next_runtime_abi_02_category_decl (tree klass) |
| { |
| tree decl; |
| char buf[BUFSIZE]; |
| snprintf (buf, BUFSIZE, "_OBJC_Category_%s_%s", |
| IDENTIFIER_POINTER (CLASS_NAME (klass)), |
| IDENTIFIER_POINTER (CLASS_SUPER_NAME (klass))); |
| decl = start_var_decl (objc_v2_category_template, buf); |
| OBJCMETA (decl, objc_meta, meta_category); |
| return decl; |
| } |
| |
| static tree |
| next_runtime_abi_02_protocol_decl (tree p) |
| { |
| tree decl; |
| char buf[BUFSIZE]; |
| |
| /* static struct _objc_protocol _OBJC_Protocol_<mumble>; */ |
| snprintf (buf, BUFSIZE, "_OBJC_Protocol_%s", |
| IDENTIFIER_POINTER (PROTOCOL_NAME (p))); |
| if (flag_next_runtime >= USE_FIXUP_BEFORE) |
| { |
| decl = create_hidden_decl (objc_v2_protocol_template, buf); |
| DECL_WEAK (decl) = true; |
| } |
| else |
| decl = start_var_decl (objc_v2_protocol_template, buf); |
| OBJCMETA (decl, objc_meta, meta_protocol); |
| DECL_PRESERVE_P (decl) = 1; |
| return decl; |
| } |
| |
| static tree |
| next_runtime_abi_02_string_decl (tree type, const char *name, string_section where) |
| { |
| tree var = start_var_decl (type, name); |
| switch (where) |
| { |
| case class_names: |
| OBJCMETA (var, objc_meta, meta_class_name); |
| break; |
| case meth_var_names: |
| OBJCMETA (var, objc_meta, meta_meth_name); |
| break; |
| case meth_var_types: |
| OBJCMETA (var, objc_meta, meta_meth_type); |
| break; |
| case prop_names_attr: |
| OBJCMETA (var, objc_meta, meta_prop_name_attr); |
| break; |
| default: |
| OBJCMETA (var, objc_meta, meta_base); |
| break; |
| } |
| return var; |
| } |
| |
| /* NOTE --- entry --- */ |
| |
| struct GTY(()) ident_data_tuple { |
| tree ident; |
| tree data; |
| }; |
| |
| /* This routine creates a file scope static variable of type 'Class' |
| to hold the address of a class. */ |
| |
| static tree |
| build_v2_class_reference_decl (tree ident) |
| { |
| tree decl; |
| char buf[BUFSIZE]; |
| |
| snprintf (buf, BUFSIZE, "_OBJC_ClassRef_%s", IDENTIFIER_POINTER (ident)); |
| decl = start_var_decl (objc_class_type, buf); |
| OBJCMETA (decl, objc_meta, meta_class_ref); |
| return decl; |
| } |
| |
| /* This routine builds a class refs entry for each class name used. |
| Initially, a (static-ref, IDENT) tuple is added to the list. The |
| ident is replaced with address of the class metadata (of type |
| 'Class') in the output routine. */ |
| |
| static GTY (()) vec<ident_data_tuple, va_gc> *classrefs; |
| |
| static tree |
| objc_v2_get_class_reference (tree ident) |
| { |
| tree decl; |
| ident_data_tuple e; |
| if (classrefs) |
| { |
| int count; |
| ident_data_tuple *ref; |
| FOR_EACH_VEC_ELT (*classrefs, count, ref) |
| { |
| if (ref->ident == ident) |
| { |
| if (!ref->data) |
| ref->data = build_v2_class_reference_decl (ident); |
| return ref->data; |
| } |
| } |
| } |
| else |
| /* Somewhat arbitrary initial provision. */ |
| vec_alloc (classrefs, 16); |
| |
| /* We come here if we don't find the entry - or if the table was yet |
| to be created. */ |
| decl = build_v2_class_reference_decl (ident); |
| e.ident = ident; |
| e.data = decl; |
| vec_safe_push (classrefs, e); |
| return decl; |
| } |
| |
| static tree |
| next_runtime_abi_02_get_class_reference (tree ident) |
| { |
| if (!flag_zero_link) |
| return objc_v2_get_class_reference (ident); |
| else |
| { |
| /* We fall back to using objc_getClass (). */ |
| vec<tree, va_gc> *v; |
| vec_alloc (v, 1); |
| tree t; |
| /* ??? add_class_reference (ident); - is pointless, since the |
| system lib does not export the equivalent symbols. Maybe we |
| need to build a class ref anyway. */ |
| t = my_build_string_pointer (IDENTIFIER_LENGTH (ident) + 1, |
| IDENTIFIER_POINTER (ident)); |
| v->quick_push (t); |
| t = build_function_call_vec (input_location, vNULL, objc_get_class_decl, |
| v, 0); |
| vec_free (v); |
| return t; |
| } |
| } |
| |
| /* Used by build_function_type_for_method. Append the types for |
| receiver & _cmd at the start of a method argument list to ARGTYPES. |
| CONTEXT is either METHOD_DEF or METHOD_REF, saying whether we are |
| trying to define a method or call one. SUPERFLAG says this is for a |
| send to super. METH may be NULL, in the case that there is no |
| prototype. */ |
| |
| static void |
| next_runtime_abi_02_get_arg_type_list_base (vec<tree, va_gc> **argtypes, |
| tree meth, int context, |
| int superflag) |
| { |
| tree receiver_type; |
| |
| if (superflag) |
| receiver_type = objc_super_type; |
| else if (context == METHOD_DEF && TREE_CODE (meth) == INSTANCE_METHOD_DECL) |
| receiver_type = objc_instance_type; |
| else |
| receiver_type = objc_object_type; |
| |
| vec_safe_push (*argtypes, receiver_type); |
| if (flag_next_runtime < USE_FIXUP_BEFORE) |
| /* Selector type - will eventually change to `int'. */ |
| vec_safe_push (*argtypes, superflag ? objc_v2_super_selector_type |
| : objc_v2_selector_type); |
| else |
| vec_safe_push (*argtypes, objc_selector_type); |
| } |
| |
| /* TODO: Merge this with the message refs. */ |
| static tree |
| build_selector_reference_decl (tree ident) |
| { |
| tree decl; |
| char *t, buf[BUFSIZE]; |
| |
| snprintf (buf, BUFSIZE, "_OBJC_SelRef_%s", IDENTIFIER_POINTER (ident)); |
| t = buf; |
| while (*t) |
| { |
| if (*t==':') |
| *t = '$'; /* Underscore would clash between foo:bar and foo_bar. */ |
| t++; |
| } |
| decl = start_var_decl (objc_selector_type, buf); |
| OBJCMETA (decl, objc_meta, meta_sel_refs); |
| return decl; |
| } |
| |
| static tree |
| next_runtime_abi_02_build_selector_reference (location_t loc ATTRIBUTE_UNUSED, |
| tree ident, |
| tree proto ATTRIBUTE_UNUSED) |
| { |
| tree *chain = &sel_ref_chain; |
| tree expr; |
| |
| while (*chain) |
| { |
| if (TREE_VALUE (*chain) == ident) |
| return TREE_PURPOSE (*chain); |
| |
| chain = &TREE_CHAIN (*chain); |
| } |
| |
| expr = build_selector_reference_decl (ident); |
| *chain = tree_cons (expr, ident, NULL_TREE); |
| |
| return expr; |
| } |
| |
| /* Declare a variable of type 'struct message_ref_t'. */ |
| /* This will be finished in build_v2_message_ref_translation_table (). |
| We take an idea from LLVM in making the names a bit more connected |
| and thus the asm more readable. */ |
| |
| static tree |
| build_v2_message_reference_decl (tree sel_name, tree message_func_ident) |
| { |
| tree decl; |
| char buf[BUFSIZE], *t; |
| int offset = 12; |
| |
| /* Skip past the objc_msgSend it's the same for all... */ |
| if (IDENTIFIER_POINTER (message_func_ident)[offset] == '_') |
| offset++; |
| |
| snprintf (buf, BUFSIZE, "_OBJC_MsgRef_%s_%s", |
| &(IDENTIFIER_POINTER (message_func_ident)[offset]), |
| IDENTIFIER_POINTER (sel_name)); |
| t = buf; |
| while (*t) |
| { |
| if (*t==':') |
| *t = '$'; /* Underscore would clash between foo:bar and foo_bar. */ |
| t++; |
| } |
| decl = start_var_decl (objc_v2_message_ref_template, buf); |
| OBJCMETA (decl, objc_meta, meta_mref); |
| return decl; |
| } |
| |
| struct GTY(()) msgref_entry { |
| tree func; |
| tree selname; |
| tree refdecl; |
| }; |
| |
| static GTY (()) vec<msgref_entry, va_gc> *msgrefs; |
| |
| /* Build the list of (objc_msgSend_fixup_xxx, selector name), used |
| later on to initialize the table of 'struct message_ref_t' |
| elements. */ |
| |
| static tree |
| build_v2_selector_messenger_reference (tree sel_name, tree message_func_decl) |
| { |
| tree decl; |
| msgref_entry e; |
| if (msgrefs) |
| { |
| int count; |
| msgref_entry *ref; |
| FOR_EACH_VEC_ELT (*msgrefs, count, ref) |
| if (ref->func == message_func_decl && ref->selname == sel_name) |
| return ref->refdecl; |
| } |
| else |
| /* Somewhat arbitrary initial provision. */ |
| vec_alloc (msgrefs, 32); |
| |
| /* We come here if we don't find a match or at the start. */ |
| decl = build_v2_message_reference_decl (sel_name, |
| DECL_NAME (message_func_decl)); |
| e.func = message_func_decl; |
| e.selname = sel_name; |
| e.refdecl = decl; |
| vec_safe_push (msgrefs, e); |
| return decl; |
| } |
| |
| static tree |
| build_v2_protocollist_ref_decl (tree protocol) |
| { |
| tree decl; |
| tree protocol_ident = PROTOCOL_NAME (protocol); |
| char buf[BUFSIZE]; |
| |
| snprintf (buf, BUFSIZE, "_OBJC_ProtocolRef_%s", |
| IDENTIFIER_POINTER (protocol_ident)); |
| /* TODO: other compiler versions make these hidden & weak. */ |
| decl = create_global_decl (objc_protocol_type, buf); |
| /* Let optimizer know that this decl is not removable. */ |
| DECL_PRESERVE_P (decl) = 1; |
| OBJCMETA (decl, objc_meta, meta_proto_ref); |
| return decl; |
| } |
| |
| struct GTY(()) prot_list_entry { |
| tree id; |
| tree refdecl; |
| }; |
| static GTY (()) vec<prot_list_entry, va_gc> *protrefs; |
| |
| static tree |
| objc_v2_get_protocol_reference (tree ident) |
| { |
| tree decl; |
| prot_list_entry e; |
| if (protrefs) |
| { |
| int count; |
| prot_list_entry *ref; |
| FOR_EACH_VEC_ELT (*protrefs, count, ref) |
| { |
| if (ref->id == ident) |
| { |
| if (!ref->refdecl) |
| ref->refdecl = build_v2_protocollist_ref_decl (ident); |
| return ref->refdecl; |
| } |
| } |
| } |
| else |
| /* Somewhat arbitrary initial provision. */ |
| vec_alloc (protrefs, 32); |
| |
| /* We come here if we don't find the entry - or if the table was yet |
| to be created. */ |
| decl = build_v2_protocollist_ref_decl (ident); |
| e.id = ident; |
| e.refdecl = decl; |
| vec_safe_push (protrefs, e); |
| return decl; |
| } |
| |
| static tree |
| next_runtime_abi_02_get_protocol_reference (location_t loc ATTRIBUTE_UNUSED, |
| tree p) |
| { |
| if (!PROTOCOL_FORWARD_DECL (p)) |
| PROTOCOL_FORWARD_DECL (p) = next_runtime_abi_02_protocol_decl (p); |
| |
| return objc_v2_get_protocol_reference (p); |
| } |
| |
| /* This routine returns the ivar declaration, if component is a valid |
| ivar field; NULL_TREE otherwise. On finding an ivar, it also |
| returns the class name in CLASS. */ |
| |
| static tree |
| objc_is_ivar (tree expr, tree component, tree *klass) |
| { |
| tree field = NULL_TREE; |
| tree basetype = TYPE_MAIN_VARIANT (TREE_TYPE (expr)); |
| |
| if (TREE_CODE (basetype) == RECORD_TYPE |
| && TYPE_HAS_OBJC_INFO (basetype) && TYPE_OBJC_INTERFACE (basetype)) |
| { |
| *klass = lookup_interface (OBJC_TYPE_NAME (basetype)); |
| if (*klass) |
| { |
| do |
| { |
| tree ivar_chain = CLASS_RAW_IVARS (*klass); |
| if (ivar_chain) |
| { |
| field = is_ivar (ivar_chain, component); |
| if (field != NULL_TREE) |
| break; |
| } |
| *klass = lookup_interface (CLASS_SUPER_NAME (*klass)); |
| } |
| while (*klass); |
| } |
| } |
| return field; |
| } |
| |
| static void |
| create_ivar_offset_name (char *buf, tree class_name, tree field_decl) |
| { |
| tree fname = DECL_NAME (field_decl); |
| |
| sprintf (buf, "OBJC_IVAR_$_%s.%s", IDENTIFIER_POINTER (class_name), |
| IDENTIFIER_POINTER (fname)); |
| return; |
| } |
| |
| /* This routine generates new abi's ivar reference tree. It amounts |
| to generating *(TYPE*)((char*)pObj + OFFSET_IVAR) when we normally |
| generate pObj->IVAR. OFFSET_IVAR is an 'extern' variable holding |
| the offset for 'IVAR' field. TYPE is type of IVAR field. */ |
| |
| static tree |
| objc_v2_build_ivar_ref (tree datum, tree component) |
| { |
| tree field, ref, class_name, offset, ftype, expr; |
| char var_offset_name[512]; |
| |
| field = objc_is_ivar (datum, component, &class_name); |
| if (!field) |
| return NULL_TREE; |
| |
| /* This routine only handles non-bitfield fields */ |
| if (DECL_C_BIT_FIELD (field)) |
| return NULL_TREE; |
| |
| create_ivar_offset_name (var_offset_name, CLASS_NAME (class_name), field); |
| |
| offset = create_extern_decl (TREE_TYPE (size_zero_node), var_offset_name); |
| |
| ftype = TREE_TYPE (field); |
| |
| /* (char*)datum */ |
| expr = build_c_cast (input_location, |
| string_type_node, build_fold_addr_expr (datum)); |
| |
| /* (char*)datum + offset */ |
| expr = fold_build_pointer_plus_loc (input_location, expr, offset); |
| |
| /* (ftype*)((char*)datum + offset) */ |
| expr = build_c_cast (input_location, build_pointer_type (ftype), expr); |
| |
| /* Finally: *(ftype*)((char*)datum + offset) */ |
| ref = build_indirect_ref (input_location, expr, RO_UNARY_STAR); |
| |
| /* We must set type of the resulting expression to be the same as |
| the field type. This is because, build_indirect_ref (...) |
| rebuilds the type which may result in lost information; as in the |
| case of protocol-qualified types (id <protocol> ). */ |
| TREE_TYPE (ref) = ftype; |
| |
| if (TREE_READONLY (datum) || TREE_READONLY (field)) |
| TREE_READONLY (ref) = 1; |
| |
| if (TREE_THIS_VOLATILE (datum) || TREE_THIS_VOLATILE (field)) |
| TREE_THIS_VOLATILE (ref) = 1; |
| |
| if (TREE_DEPRECATED (field)) |
| warn_deprecated_use (field, NULL_TREE); |
| |
| return ref; |
| } |
| |
| /* IVAR refs are made via an externally referenceable offset and built |
| on the fly. That is, unless they refer to (private) fields in the |
| class structure. */ |
| static tree |
| next_runtime_abi_02_build_ivar_ref (location_t loc ATTRIBUTE_UNUSED, |
| tree base, tree id) |
| { |
| tree ivar; |
| if ((ivar = objc_v2_build_ivar_ref (base, id))) |
| return ivar; |
| return objc_build_component_ref (base, id); |
| } |
| |
| /* [super ...] references are listed here (and built into a table at |
| meta -data emit time). */ |
| static tree |
| build_v2_superclass_ref_decl (tree ident, bool inst) |
| { |
| tree decl; |
| char buf[BUFSIZE]; |
| |
| snprintf (buf, BUFSIZE, "_OBJC_%sSuperRef_%s", (inst?"":"Meta"), |
| IDENTIFIER_POINTER (ident)); |
| decl = start_var_decl (objc_class_type, buf); |
| OBJCMETA (decl, objc_meta, meta_superclass_ref); |
| return decl; |
| } |
| |
| static GTY (()) vec<ident_data_tuple, va_gc> *class_super_refs; |
| static GTY (()) vec<ident_data_tuple, va_gc> *metaclass_super_refs; |
| |
| /* Find or build a superclass reference decl for class NAME. */ |
| |
| static tree |
| objc_get_superclass_ref_decl (tree name, bool inst_meth) |
| { |
| tree decl; |
| vec<ident_data_tuple, va_gc> *list = inst_meth ? class_super_refs |
| : metaclass_super_refs; |
| |
| if (list) |
| { |
| int count; |
| ident_data_tuple *ref; |
| FOR_EACH_VEC_ELT (*list, count, ref) |
| { |
| if (ref->ident == name) |
| { |
| if (!ref->data) |
| ref->data = build_v2_superclass_ref_decl (name, inst_meth); |
| return ref->data; |
| } |
| } |
| } |
| else |
| { |
| /* Somewhat arbitrary initial provision. */ |
| if (inst_meth) |
| { |
| vec_alloc (class_super_refs, 16); |
| list = class_super_refs; |
| } |
| else |
| { |
| vec_alloc (metaclass_super_refs, 16); |
| list = metaclass_super_refs; |
| } |
| } |
| /* We come here if we don't find the entry - or if the table was yet |
| to be created. */ |
| decl = build_v2_superclass_ref_decl (name, inst_meth); |
| ident_data_tuple e; |
| e.ident = name; |
| e.data = decl; |
| vec_safe_push (list, e); |
| return decl; |
| } |
| |
| /* Get a reference to the superclass for IMP. */ |
| |
| static tree |
| next_runtime_abi_02_get_class_super_ref (location_t loc ATTRIBUTE_UNUSED, |
| struct imp_entry *imp, bool inst_meth) |
| { |
| tree name = CLASS_NAME (imp->imp_context); |
| return objc_get_superclass_ref_decl (name, inst_meth); |
| } |
| |
| /* Get a reference to the superclass for category IMP. */ |
| |
| static tree |
| next_runtime_abi_02_get_category_super_ref (location_t loc ATTRIBUTE_UNUSED, |
| struct imp_entry *imp, |
| bool inst_meth) |
| { |
| if (flag_zero_link) |
| { |
| /* Do it the slow way. */ |
| tree get_cl_fn = inst_meth ? objc_get_class_decl |
| : objc_get_meta_class_decl; |
| tree super_name = CLASS_SUPER_NAME (imp->imp_template); |
| super_name = my_build_string_pointer (IDENTIFIER_LENGTH (super_name) + 1, |
| IDENTIFIER_POINTER (super_name)); |
| /* super_class = objc_get{Meta}Class("CLASS_SUPER_NAME"); */ |
| return build_function_call (input_location, get_cl_fn, |
| build_tree_list (NULL_TREE, super_name)); |
| } |
| |
| /* This is the 'usual' path. */ |
| tree cls_name = CLASS_NAME (imp->imp_template); |
| if (!inst_meth) |
| return objc_get_superclass_ref_decl (cls_name, inst_meth); |
| return objc_get_class_reference (cls_name); |
| } |
| |
| static tree |
| next_runtime_abi_02_receiver_is_class_object (tree receiver) |
| { |
| if (TREE_CODE (receiver) == VAR_DECL |
| && IS_CLASS (TREE_TYPE (receiver)) |
| && vec_safe_length (classrefs)) |
| { |
| int count; |
| ident_data_tuple *ref; |
| /* The receiver is a variable created by build_class_reference_decl. */ |
| FOR_EACH_VEC_ELT (*classrefs, count, ref) |
| if (ref->data == receiver) |
| return ref->ident; |
| } |
| return NULL_TREE; |
| } |
| |
| /* Assign all arguments in VALUES which have side-effect to a temporary |
| and replaced that argument in VALUES list with the temporary. The |
| arguments will be passed to a function with FNTYPE. */ |
| |
| static tree |
| objc_copy_to_temp_side_effect_params (tree fntype, tree values) |
| { |
| tree valtail; |
| function_args_iterator iter; |
| |
| /* Skip over receiver and the &_msf_ref types. */ |
| function_args_iter_init (&iter, fntype); |
| function_args_iter_next (&iter); |
| function_args_iter_next (&iter); |
| |
| for (valtail = values; valtail; |
| valtail = TREE_CHAIN (valtail), function_args_iter_next (&iter)) |
| { |
| tree value = TREE_VALUE (valtail); |
| tree type = function_args_iter_cond (&iter); |
| if (type == NULL_TREE) |
| break; |
| if (!TREE_SIDE_EFFECTS (value)) |
| continue; |
| /* To prevent re-evaluation. */ |
| value = save_expr (value); |
| add_stmt (value); |
| TREE_VALUE (valtail) = value; |
| } |
| return values; |
| } |
| |
| /* Build the new abi's messaging library call. It looks like: |
| (*_msg.messenger) (receiver, &_msg, ...) */ |
| |
| static tree |
| build_v2_objc_method_fixup_call (int super_flag, tree method_prototype, |
| tree lookup_object, tree selector, |
| tree method_params, bool check_for_nil) |
| { |
| tree ret_val; |
| tree sender, rcv_p, t; |
| tree ret_type |
| = (method_prototype |
| ? TREE_VALUE (TREE_TYPE (method_prototype)) |
| : objc_object_type); |
| tree ftype = build_function_type_for_method (ret_type, method_prototype, |
| METHOD_REF, super_flag); |
| tree sender_cast; |
| |
| if (method_prototype && METHOD_TYPE_ATTRIBUTES (method_prototype)) |
| ftype = build_type_attribute_variant ( |
| ftype, METHOD_TYPE_ATTRIBUTES (method_prototype)); |
| |
| sender_cast = build_pointer_type (ftype); |
| |
| if (check_for_nil) |
| method_params = objc_copy_to_temp_side_effect_params (ftype, |
| method_params); |
| |
| /* Get &message_ref_t.messenger. */ |
| sender = build_c_cast (input_location, |
| build_pointer_type (super_flag |
| ? objc_v2_super_imp_type |
| : objc_v2_imp_type), |
| selector); |
| |
| sender = build_indirect_ref (input_location, sender, RO_UNARY_STAR); |
| |
| rcv_p = (super_flag ? objc_super_type : objc_object_type); |
| |
| lookup_object = build_c_cast (input_location, rcv_p, lookup_object); |
| |
| /* Use SAVE_EXPR to avoid evaluating the receiver twice. */ |
| lookup_object = save_expr (lookup_object); |
| |
| method_params = tree_cons (NULL_TREE, lookup_object, |
| tree_cons (NULL_TREE, selector, |
| method_params)); |
| t = build3 (OBJ_TYPE_REF, sender_cast, sender, lookup_object, |
| build_int_cst (TREE_TYPE (lookup_object), 0)); |
| ret_val = build_function_call (input_location, t, method_params); |
| if (check_for_nil) |
| { |
| /* receiver != nil ? ret_val : 0 */ |
| tree ftree; |
| tree ifexp; |
| |
| if (TREE_CODE (ret_type) == RECORD_TYPE |
| || TREE_CODE (ret_type) == UNION_TYPE) |
| /* An empty constructor is zero-filled by the middle end. */ |
| ftree = objc_build_constructor (ret_type, NULL); |
| else |
| ftree = fold_convert (ret_type, integer_zero_node); |
| |
| ifexp = build_binary_op (input_location, NE_EXPR, |
| lookup_object, |
| fold_convert (rcv_p, integer_zero_node), 1); |
| |
| #ifdef OBJCPLUS |
| ret_val = build_conditional_expr (input_location, |
| ifexp, ret_val, ftree, |
| tf_warning_or_error); |
| #else |
| ret_val = build_conditional_expr (input_location, |
| ifexp, 0, |
| ret_val, NULL_TREE, input_location, |
| ftree, NULL_TREE, input_location); |
| ret_val = fold_convert (ret_type, ret_val); |
| #endif |
| } |
| return ret_val; |
| } |
| |
| static tree |
| build_v2_build_objc_method_call (int super, tree method_prototype, |
| tree lookup_object, tree selector, |
| tree method_params, location_t loc, |
| bool check_for_nil, bool rx_is_id) |
| { |
| tree sender, sender_cast, method, t; |
| tree rcv_p = (super ? objc_super_type : objc_object_type); |
| vec<tree, va_gc> *parms; |
| unsigned nparm = (method_params ? list_length (method_params) : 0); |
| |
| /* If a prototype for the method to be called exists, then cast |
| the sender's return type and arguments to match that of the method. |
| Otherwise, leave sender as is. */ |
| tree ret_type |
| = (method_prototype |
| ? TREE_VALUE (TREE_TYPE (method_prototype)) |
| : objc_object_type); |
| tree ftype = build_function_type_for_method (ret_type, method_prototype, |
| METHOD_REF, super); |
| |
| if (method_prototype && METHOD_TYPE_ATTRIBUTES (method_prototype)) |
| ftype = build_type_attribute_variant (ftype, |
| METHOD_TYPE_ATTRIBUTES |
| (method_prototype)); |
| |
| sender_cast = build_pointer_type (ftype); |
| |
| lookup_object = build_c_cast (loc, rcv_p, lookup_object); |
| |
| /* Use SAVE_EXPR to avoid evaluating the receiver twice. */ |
| lookup_object = save_expr (lookup_object); |
| |
| /* Param list + 2 slots for object and selector. */ |
| vec_alloc (parms, nparm + 2); |
| |
| /* If we are returning an item that must be returned in memory, and the |
| target ABI does this by an invisible pointer provided as the first arg, |
| we need to adjust the message signature to include this. The second |
| part of this excludes targets that provide some alternate scheme for |
| structure returns. */ |
| if (ret_type && !VOID_TYPE_P (ret_type) |
| && targetm.calls.return_in_memory (ret_type, 0) |
| && !(targetm.calls.struct_value_rtx (0, 0) |
| && (TREE_CODE (ret_type) == RECORD_TYPE |
| || TREE_CODE (ret_type) == UNION_TYPE))) |
| { |
| if (super) |
| sender = umsg_id_super2_stret_fixup_decl; |
| else |
| sender = rx_is_id ? umsg_id_stret_fixup_decl |
| : umsg_stret_fixup_decl; |
| } |
| else |
| { |
| if (super) |
| sender = umsg_id_super2_fixup_decl; |
| else |
| sender = rx_is_id ? umsg_id_fixup_decl |
| : umsg_fixup_decl; |
| } |
| |
| method = build_fold_addr_expr_loc (loc, sender); |
| |
| /* Pass the object to the method. */ |
| parms->quick_push (lookup_object); |
| /* Pass the selector to the method. */ |
| parms->quick_push (selector); |
| /* Now append the remainder of the parms. */ |
| if (nparm) |
| for (; method_params; method_params = TREE_CHAIN (method_params)) |
| parms->quick_push (TREE_VALUE (method_params)); |
| |
| /* Build an obj_type_ref, with the correct cast for the method call. */ |
| t = build3 (OBJ_TYPE_REF, sender_cast, method, |
| lookup_object, build_int_cst (TREE_TYPE (lookup_object), 0)); |
| tree ret_val = build_function_call_vec (loc, vNULL, t, parms, NULL); |
| vec_free (parms); |
| if (check_for_nil) |
| { |
| /* receiver != nil ? ret_val : 0 */ |
| tree ftree; |
| tree ifexp; |
| |
| if (TREE_CODE (ret_type) == RECORD_TYPE |
| || TREE_CODE (ret_type) == UNION_TYPE) |
| { |
| /* An empty constructor is zero-filled by the middle end. */ |
| ftree = objc_build_constructor (ret_type, NULL); |
| } |
| else |
| ftree = fold_convert (ret_type, integer_zero_node); |
| |
| ifexp = build_binary_op (loc, NE_EXPR, |
| lookup_object, |
| fold_convert (rcv_p, integer_zero_node), 1); |
| |
| #ifdef OBJCPLUS |
| ret_val = build_conditional_expr (loc, ifexp, ret_val, ftree, |
| tf_warning_or_error); |
| #else |
| ret_val = build_conditional_expr (loc, ifexp, 1, |
| ret_val, NULL_TREE, loc, |
| ftree, NULL_TREE, loc); |
| ret_val = fold_convert (ret_type, ret_val); |
| #endif |
| } |
| return ret_val; |
| } |
| |
| static tree |
| next_runtime_abi_02_build_objc_method_call (location_t loc, |
| tree method_prototype, |
| tree receiver, |
| tree rtype, |
| tree sel_name, |
| tree method_params, |
| int super) |
| { |
| /* Do we need to check for nil receivers ? */ |
| /* For now, message sent to classes need no nil check. In the |
| future, class declaration marked as weak_import must be nil |
| checked. */ |
| bool check_for_nil = flag_objc_nilcheck; |
| if (super |
| || (TREE_CODE (receiver) == VAR_DECL |
| && TREE_TYPE (receiver) == objc_class_type)) |
| check_for_nil = false; |
| |
| if (flag_next_runtime >= USE_FIXUP_BEFORE) |
| { |
| tree selector |
| = next_runtime_abi_02_build_selector_reference (loc, sel_name, |
| method_prototype); |
| return build_v2_build_objc_method_call (super, method_prototype, |
| receiver, selector, |
| method_params, loc, |
| check_for_nil, |
| objc_is_id (rtype)); |
| } |
| |
| /* else we have to build a pair of the function and selector. */ |
| tree message_func_decl; |
| tree ret_type = method_prototype |
| ? TREE_VALUE (TREE_TYPE (method_prototype)) |
| : objc_object_type; |
| |
| /* See comment for the fixup version above. */ |
| if (ret_type && !VOID_TYPE_P (ret_type) |
| && targetm.calls.return_in_memory (ret_type, 0) |
| && !(targetm.calls.struct_value_rtx (0, 0) |
| && (TREE_CODE (ret_type) == RECORD_TYPE |
| || TREE_CODE (ret_type) == UNION_TYPE))) |
| { |
| if (super) |
| message_func_decl = umsg_id_super2_stret_fixup_decl; |
| else |
| message_func_decl = objc_is_id (rtype) |
| ? umsg_id_stret_fixup_decl |
| : umsg_stret_fixup_decl; |
| } |
| else |
| { |
| if (super) |
| message_func_decl = umsg_id_super2_fixup_decl; |
| else |
| message_func_decl = objc_is_id (rtype) |
| ? umsg_id_fixup_decl |
| : umsg_fixup_decl; |
| } |
| |
| tree selector = build_v2_selector_messenger_reference (sel_name, |
| message_func_decl); |
| |
| /* selector = &_msg; */ |
| selector = build_unary_op (loc, ADDR_EXPR, selector, 0); |
| |
| selector = build_c_cast (loc, (super ? objc_v2_super_selector_type |
| : objc_v2_selector_type), |
| selector); |
| |
| /* (*_msg.messenger) (receiver, &_msg, ...); */ |
| return build_v2_objc_method_fixup_call (super, method_prototype, receiver, |
| selector, method_params, |
| check_for_nil); |
| } |
| |
| /* NOTE --- Constant String Class Stuff --- */ |
| |
| static bool |
| next_runtime_abi_02_setup_const_string_class_decl (void) |
| { |
| if (!constant_string_global_id) |
| { |
| /* Hopefully, this should not represent a serious limitation. */ |
| char buf[BUFSIZE]; |
| snprintf (buf, BUFSIZE, "OBJC_CLASS_$_%s", constant_string_class_name); |
| constant_string_global_id = get_identifier (buf); |
| } |
| |
| string_class_decl = lookup_name (constant_string_global_id); |
| |
| /* In OBJC2 abi, constant string class reference refers to class |
| name for NSConstantString class. This declaration may not be |
| available yet (in fact it is not in most cases). So, declare an |
| extern OBJC_CLASS_$_NSConstantString in its place. */ |
| if (!string_class_decl) |
| string_class_decl = |
| create_extern_decl (objc_v2_class_template, |
| IDENTIFIER_POINTER (constant_string_global_id)); |
| |
| return (string_class_decl != NULL_TREE); |
| } |
| |
| static tree |
| next_runtime_abi_02_build_const_string_constructor (location_t loc, tree string, |
| int length) |
| { |
| tree constructor, fields, var; |
| vec<constructor_elt, va_gc> *v = NULL; |
| |
| /* NeXT: (NSConstantString *) & ((__builtin_ObjCString) { isa, string, length }) */ |
| fields = TYPE_FIELDS (internal_const_str_type); |
| CONSTRUCTOR_APPEND_ELT (v, fields, |
| build_unary_op (loc, ADDR_EXPR, string_class_decl, 0)); |
| |
| fields = DECL_CHAIN (fields); |
| CONSTRUCTOR_APPEND_ELT (v, fields, |
| build_unary_op (loc, ADDR_EXPR, string, 1)); |
| |
| /* ??? check if this should be long. */ |
| fields = DECL_CHAIN (fields); |
| CONSTRUCTOR_APPEND_ELT (v, fields, build_int_cst (NULL_TREE, length)); |
| constructor = objc_build_constructor (internal_const_str_type, v); |
| |
| var = build_decl (input_location, CONST_DECL, NULL, TREE_TYPE (constructor)); |
| DECL_INITIAL (var) = constructor; |
| TREE_STATIC (var) = 1; |
| DECL_CONTEXT (var) = NULL; |
| OBJCMETA (var, objc_meta, meta_const_str); |
| return var; |
| } |
| |
| /* NOTE --- NeXT V2 Metadata templates --- */ |
| |
| /* This routine builds the following type: |
| struct _prop_t |
| { |
| const char * const name; // property name |
| const char * const attributes; // comma-delimited, encoded, |
| // property attributes |
| }; |
| */ |
| |
| static tree |
| build_v2_property_template (void) |
| { |
| tree prop_record; |
| tree decls, *chain = NULL; |
| |
| prop_record = objc_start_struct (get_identifier ("_prop_t")); |
| /* const char * name */ |
| decls = add_field_decl (string_type_node, "name", &chain); |
| |
| /* const char * attribute */ |
| add_field_decl (string_type_node, "attribute", &chain); |
| |
| objc_finish_struct (prop_record, decls); |
| return prop_record; |
| } |
| |
| /* struct ivar_t |
| { |
| unsigned long int *offset; |
| char *name; |
| char *type; |
| uint32_t alignment; |
| uint32_t size; |
| }; |
| */ |
| |
| static tree |
| build_v2_ivar_t_template (void) |
| { |
| tree objc_ivar_id, objc_ivar_record; |
| tree decls, *chain = NULL; |
| |
| objc_ivar_id = get_identifier ("_ivar_t"); |
| objc_ivar_record = objc_start_struct (objc_ivar_id); |
| |
| /* unsigned long int *offset; */ |
| decls = add_field_decl (build_pointer_type |
| (TREE_TYPE (size_zero_node)), "offset", &chain); |
| |
| /* char *name; */ |
| add_field_decl (string_type_node, "name", &chain); |
| |
| /* char *type; */ |
| add_field_decl (string_type_node, "type", &chain); |
| |
| /* uint32_t alignment; */ |
| add_field_decl (integer_type_node, "alignment", &chain); |
| |
| /* uint32_t size; */ |
| add_field_decl (integer_type_node, "size", &chain); |
| |
| objc_finish_struct (objc_ivar_record, decls); |
| return objc_ivar_record; |
| } |
| |
| static void |
| build_metadata_templates (void) |
| { |
| |
| if (!objc_method_template) |
| objc_method_template = build_method_template (); |
| |
| if (!objc_v2_property_template) |
| objc_v2_property_template = build_v2_property_template (); |
| |
| if (!objc_v2_ivar_template) |
| objc_v2_ivar_template = build_v2_ivar_t_template (); |
| |
| } |
| |
| /* NOTE --- Output NeXT V2 Metadata --- */ |
| |
| /* Routine builds name of Interface's main meta-data of type class_t. */ |
| |
| static char * |
| objc_build_internal_classname (tree ident, bool metaclass) |
| { |
| static char string[512]; |
| snprintf (string, 512, "%s_%s", metaclass ? "OBJC_METACLASS_$" |
| : "OBJC_CLASS_$", |
| IDENTIFIER_POINTER (ident)); |
| return string; |
| } |
| |
| /* Build the name for object of type struct class_ro_t */ |
| |
| static const char * |
| newabi_append_ro (const char *name) |
| { |
| const char *dollar; |
| char *p; |
| static char string[BUFSIZE]; |
| dollar = strchr (name, '$'); |
| gcc_assert (dollar); |
| p = string; |
| *p = '_'; p++; |
| strncpy (p, name, (int)(dollar - name)); |
| p += (int)(dollar - name); |
| sprintf (p, "RO_%s", dollar); |
| return string; |
| } |
| |
| /* Build the struct message_ref_t msg = |
| {objc_msgSend_fixup_xxx, @selector(func)} |
| table. */ |
| |
| static |
| void build_v2_message_ref_translation_table (void) |
| { |
| int count; |
| msgref_entry *ref; |
| |
| if (!vec_safe_length (msgrefs)) |
| return; |
| |
| FOR_EACH_VEC_ELT (*msgrefs, count, ref) |
| { |
| vec<constructor_elt, va_gc> *initializer; |
| tree expr, constructor; |
| tree struct_type = TREE_TYPE (ref->refdecl); |
| location_t loc = DECL_SOURCE_LOCATION (ref->refdecl); |
| |
| initializer = NULL; |
| /* First 'IMP messenger' field... */ |
| expr = build_unary_op (loc, ADDR_EXPR, ref->func, 0); |
| expr = convert (objc_v2_imp_type, expr); |
| CONSTRUCTOR_APPEND_ELT (initializer, NULL_TREE, expr); |
| |
| /* ... then 'SEL name' field. */ |
| expr = build_selector (ref->selname); |
| CONSTRUCTOR_APPEND_ELT (initializer, NULL_TREE, expr); |
| constructor = objc_build_constructor (struct_type, initializer); |
| finish_var_decl (ref->refdecl, constructor); |
| } |
| } |
| |
| /* Build decl = initializer; for each externally visible class |
| reference. */ |
| |
| static void |
| build_v2_classrefs_table (void) |
| { |
| int count; |
| ident_data_tuple *ref; |
| |
| if (!vec_safe_length (classrefs)) |
| return; |
| |
| FOR_EACH_VEC_ELT (*classrefs, count, ref) |
| { |
| tree expr = ref->ident; |
| tree decl = ref->data; |
| /* Interface with no implementation and yet one of its messages |
| has been used. Need to generate a full address-of tree for it |
| here. */ |
| if (TREE_CODE (expr) == IDENTIFIER_NODE) |
| { |
| const char *name = objc_build_internal_classname (expr, false); |
| expr = create_extern_decl (objc_v2_class_template, name); |
| expr = convert (objc_class_type, build_fold_addr_expr (expr)); |
| } |
| /* The runtime wants this, even if it appears unused, so we must force the |
| output. */ |
| DECL_PRESERVE_P (decl) = 1; |
| finish_var_decl (decl, expr); |
| } |
| } |
| |
| /* Build decl = initializer; for each externally visible super class |
| reference. */ |
| |
| static void |
| build_v2_super_classrefs_table (bool metaclass) |
| { |
| int count; |
| ident_data_tuple *ref; |
| vec<ident_data_tuple, va_gc> *list = metaclass ? metaclass_super_refs |
| : class_super_refs; |
| |
| if (!vec_safe_length (list)) |
| return; |
| |
| FOR_EACH_VEC_ELT (*list, count, ref) |
| { |
| tree expr = ref->ident; |
| tree decl = ref->data; |
| /* Interface with no implementation and yet one of its messages |
| has been used. Need to generate a full address-of tree for it |
| here. */ |
| if (TREE_CODE (expr) == IDENTIFIER_NODE) |
| { |
| const char * name = objc_build_internal_classname (expr, metaclass); |
| expr = create_extern_decl (objc_v2_class_template, name); |
| expr = convert (objc_class_type, build_fold_addr_expr (expr)); |
| } |
| finish_var_decl (decl, expr); |
| } |
| } |
| |
| /* Add the global class meta-data declaration to the list which later |
| on ends up in the __class_list section. */ |
| |
| static GTY(()) vec<tree, va_gc> *class_list; |
| |
| static void |
| objc_v2_add_to_class_list (tree global_class_decl) |
| { |
| vec_safe_push (class_list, global_class_decl); |
| } |
| |
| static GTY(()) vec<tree, va_gc> *nonlazy_class_list; |
| |
| /* Add the global class meta-data declaration to the list which later |
| on ends up in the __nonlazy_class section. */ |
| |
| static void |
| objc_v2_add_to_nonlazy_class_list (tree global_class_decl) |
| { |
| vec_safe_push (nonlazy_class_list, global_class_decl); |
| } |
| |
| static GTY(()) vec<tree, va_gc> *category_list; |
| |
| /* Add the category meta-data declaration to the list which later on |
| ends up in the __nonlazy_category section. */ |
| |
| static void |
| objc_v2_add_to_category_list (tree decl) |
| { |
| vec_safe_push (category_list, decl); |
| } |
| |
| static GTY(()) vec<tree, va_gc> *nonlazy_category_list; |
| |
| /* Add the category meta-data declaration to the list which later on |
| ends up in the __category_list section. */ |
| |
| static void |
| objc_v2_add_to_nonlazy_category_list (tree decl) |
| { |
| vec_safe_push (nonlazy_category_list, decl); |
| } |
| |
| static bool |
| has_load_impl (tree clsmeth) |
| { |
| while (clsmeth) |
| { |
| tree id = METHOD_SEL_NAME (clsmeth); |
| if (IDENTIFIER_LENGTH (id) == 4 |
| && startswith (IDENTIFIER_POINTER (id), "load")) |
| return true; |
| clsmeth = DECL_CHAIN (clsmeth); |
| } |
| |
| return false; |
| } |
| |
| /* Build a __{class,category}_list section table containing address of |
| all @implemented {class,category} meta-data. */ |
| |
| static void |
| build_v2_address_table (vec<tree, va_gc> *src, const char *nam, tree attr) |
| { |
| int count=0; |
| tree type, decl, expr; |
| vec<constructor_elt, va_gc> *initlist = NULL; |
| |
| if (!vec_safe_length (src)) |
| return; |
| |
| FOR_EACH_VEC_ELT (*src, count, decl) |
| { |
| #ifndef OBJCPLUS |
| tree purpose = build_int_cst (NULL_TREE, count); |
| #else |
| tree purpose = NULL_TREE; |
| #endif |
| expr = convert (objc_class_type, build_fold_addr_expr (decl)); |
| CONSTRUCTOR_APPEND_ELT (initlist, purpose, expr); |
| } |
| gcc_assert (count > 0); |
| type = build_array_type (objc_class_type, |
| build_index_type (build_int_cst (NULL_TREE, count - 1))); |
| decl = start_var_decl (type, nam); |
| /* The runtime wants this, even if it appears unused, so we must |
| force the output. */ |
| DECL_PRESERVE_P (decl) = 1; |
| expr = objc_build_constructor (type, initlist); |
| OBJCMETA (decl, objc_meta, attr); |
| finish_var_decl (decl, expr); |
| } |
| |
| /* Build decl = initializer; for each protocol referenced in |
| @protocol(MyProt) expression. Refs as built in the entry section |
| above. */ |
| |
| static void |
| build_v2_protocol_list_translation_table (void) |
| { |
| int count; |
| prot_list_entry *ref; |
| |
| if (!protrefs) |
| return; |
| |
| FOR_EACH_VEC_ELT (*protrefs, count, ref) |
| { |
| char buf[BUFSIZE]; |
| tree expr; |
| gcc_assert (TREE_CODE (ref->id) == PROTOCOL_INTERFACE_TYPE); |
| snprintf (buf, BUFSIZE, "_OBJC_Protocol_%s", |
| IDENTIFIER_POINTER (PROTOCOL_NAME (ref->id))); |
| expr = start_var_decl (objc_v2_protocol_template, buf); |
| expr = convert (objc_protocol_type, build_fold_addr_expr (expr)); |
| finish_var_decl (ref->refdecl, expr); |
| } |
| /* TODO: Maybe we could explicitly delete the vec. now? */ |
| } |
| |
| static GTY (()) vec<prot_list_entry, va_gc> *protlist; |
| |
| /* Add the local protocol meta-data declaration to the list which |
| later on ends up in the __protocol_list section. */ |
| |
| static void |
| objc_add_to_protocol_list (tree protocol_interface_decl, tree protocol_decl) |
| { |
| prot_list_entry e; |
| if (!protlist) |
| /* Arbitrary init count. */ |
| vec_alloc (protlist, 32); |
| e.id = protocol_interface_decl; |
| e.refdecl = protocol_decl; |
| vec_safe_push (protlist, e); |
| } |
| |
| /* Build the __protocol_list section table containing address of all |
| generate protocol_t meta-data. */ |
| |
| static void |
| build_v2_protocol_list_address_table (void) |
| { |
| int count; |
| prot_list_entry *ref; |
| if (!vec_safe_length (protlist)) |
| return; |
| |
| FOR_EACH_VEC_ELT (*protlist, count, ref) |
| { |
| tree decl, expr; |
| char buf[BUFSIZE]; |
| gcc_assert (ref->id && TREE_CODE (ref->id) == PROTOCOL_INTERFACE_TYPE); |
| snprintf (buf, BUFSIZE, "_OBJC_LabelProtocol_%s", |
| IDENTIFIER_POINTER (PROTOCOL_NAME (ref->id))); |
| if (flag_next_runtime >= USE_FIXUP_BEFORE) |
| { |
| decl = create_hidden_decl (objc_protocol_type, buf, /*is def=*/true); |
| DECL_WEAK (decl) = true; |
| } |
| else |
| decl = create_global_decl (objc_protocol_type, buf, /*is def=*/true); |
| expr = convert (objc_protocol_type, build_fold_addr_expr (ref->refdecl)); |
| OBJCMETA (decl, objc_meta, meta_label_protocollist); |
| finish_var_decl (decl, expr); |
| DECL_PRESERVE_P (decl) = 1; |
| } |
| |
| /* TODO: delete the vec. */ |
| /* TODO: upgrade to the clang/llvm hidden version. */ |
| } |
| |
| /* This routine declares a variable to hold meta data for 'struct |
| protocol_list_t'. */ |
| |
| static tree |
| generate_v2_protocol_list (tree i_or_p, tree klass_ctxt) |
| { |
| tree refs_decl, lproto, e, plist, ptempl_p_t; |
| int size = 0; |
| vec<constructor_elt, va_gc> *initlist = NULL; |
| char buf[BUFSIZE]; |
| |
| if (TREE_CODE (i_or_p) == CLASS_INTERFACE_TYPE |
| || TREE_CODE (i_or_p) == CATEGORY_INTERFACE_TYPE) |
| plist = CLASS_PROTOCOL_LIST (i_or_p); |
| else if (TREE_CODE (i_or_p) == PROTOCOL_INTERFACE_TYPE) |
| plist = PROTOCOL_LIST (i_or_p); |
| else |
| gcc_unreachable (); |
| |
| /* Compute size. */ |
| for (lproto = plist; lproto; lproto = TREE_CHAIN (lproto)) |
| if (TREE_CODE (TREE_VALUE (lproto)) == PROTOCOL_INTERFACE_TYPE |
| && PROTOCOL_FORWARD_DECL (TREE_VALUE (lproto))) |
| size++; |
| |
| /* Build initializer. */ |
| |
| ptempl_p_t = build_pointer_type (objc_v2_protocol_template); |
| e = build_int_cst (ptempl_p_t, size); |
| CONSTRUCTOR_APPEND_ELT (initlist, NULL_TREE, e); |
| |
| for (lproto = plist; lproto; lproto = TREE_CHAIN (lproto)) |
| { |
| tree pval = TREE_VALUE (lproto); |
| |
| if (TREE_CODE (pval) == PROTOCOL_INTERFACE_TYPE |
| && PROTOCOL_FORWARD_DECL (pval)) |
| { |
| tree fwref = PROTOCOL_FORWARD_DECL (pval); |
| location_t loc = DECL_SOURCE_LOCATION (fwref) ; |
| e = build_unary_op (loc, ADDR_EXPR, fwref, 0); |
| CONSTRUCTOR_APPEND_ELT (initlist, NULL_TREE, e); |
| } |
| } |
| |
| /* static struct protocol_list_t *list[size]; */ |
| |
| switch (TREE_CODE (i_or_p)) |
| { |
| case PROTOCOL_INTERFACE_TYPE: |
| snprintf (buf, BUFSIZE, "_OBJC_ProtocolRefs_%s", |
| IDENTIFIER_POINTER (PROTOCOL_NAME (i_or_p))); |
| break; |
| case CLASS_INTERFACE_TYPE: |
| snprintf (buf, BUFSIZE, "_OBJC_ClassProtocols_%s", |
| IDENTIFIER_POINTER (CLASS_NAME (i_or_p))); |
| break; |
| case CATEGORY_INTERFACE_TYPE: |
| snprintf (buf, BUFSIZE, "_OBJC_CategoryProtocols_%s_%s", |
| IDENTIFIER_POINTER (CLASS_NAME (klass_ctxt)), |
| IDENTIFIER_POINTER (CLASS_SUPER_NAME (klass_ctxt))); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| refs_decl = start_var_decl (build_sized_array_type (ptempl_p_t, size+1), |
| buf); |
| /* ObjC2 puts all these in the base section. */ |
| OBJCMETA (refs_decl, objc_meta, meta_base); |
| DECL_PRESERVE_P (refs_decl) = 1; |
| finish_var_decl (refs_decl, |
| objc_build_constructor (TREE_TYPE (refs_decl),initlist)); |
| return refs_decl; |
| } |
| |
| /* This routine builds one 'struct method_t' initializer list. Note |
| that the old ABI is supposed to build 'struct objc_method' which |
| has 3 fields, but it does not build the initialization expression |
| for 'method_imp' which for protocols is NULL any way. To be |
| consistent with declaration of 'struct method_t', in the new ABI we |
| set the method_t.imp to NULL. */ |
| |
| static tree |
| build_v2_descriptor_table_initializer (tree type, tree entries) |
| { |
| vec<constructor_elt, va_gc> *initlist = NULL; |
| do |
| { |
| vec<constructor_elt, va_gc> *eltlist = NULL; |
| CONSTRUCTOR_APPEND_ELT (eltlist, NULL_TREE, |
| build_selector (METHOD_SEL_NAME (entries))); |
| CONSTRUCTOR_APPEND_ELT (eltlist, NULL_TREE, |
| add_objc_string (METHOD_ENCODING (entries), |
| meth_var_types)); |
| CONSTRUCTOR_APPEND_ELT (eltlist, NULL_TREE, null_pointer_node); |
| |
| CONSTRUCTOR_APPEND_ELT (initlist, NULL_TREE, |
| objc_build_constructor (type, eltlist)); |
| entries = TREE_CHAIN (entries); |
| } |
| while (entries); |
| |
| return objc_build_constructor (build_array_type (type, 0), initlist); |
| } |
| |
| /* struct method_list_t |
| { |
| uint32_t entsize; |
| uint32_t method_count; |
| struct objc_method method_list[method_count]; |
| }; */ |
| |
| static tree |
| build_v2_method_list_template (tree list_type, int size) |
| { |
| tree method_list_t_record; |
| tree array_type, decls, *chain = NULL; |
| |
| method_list_t_record = objc_start_struct (NULL_TREE); |
| |
| /* uint32_t const entsize; */ |
| decls = add_field_decl (integer_type_node, "entsize", &chain); |
| |
| /* int method_count; */ |
| add_field_decl (integer_type_node, "method_count", &chain); |
| |
| /* struct objc_method method_list[]; */ |
| array_type = build_sized_array_type (list_type, size); |
| add_field_decl (array_type, "method_list", &chain); |
| |
| objc_finish_struct (method_list_t_record, decls); |
| return method_list_t_record; |
| } |
| |
| /* Note, as above that we are building to the objc_method_template |
| which has the *imp field. ABI0/1 build with |
| objc_method_prototype_template which is missing this field. */ |
| static tree |
| generate_v2_meth_descriptor_table (tree chain, tree protocol, |
| const char *prefix, tree attr, |
| vec<tree>& all_meths) |
| { |
| tree method_list_template, initlist, decl; |
| int size, entsize; |
| vec<constructor_elt, va_gc> *v = NULL; |
| char buf[BUFSIZE]; |
| |
| if (!chain || !prefix) |
| return NULL_TREE; |
| |
| tree method = chain; |
| size = 0; |
| while (method) |
| { |
| if (! METHOD_ENCODING (method)) |
| METHOD_ENCODING (method) = encode_method_prototype (method); |
| all_meths.safe_push (method); |
| method = TREE_CHAIN (method); |
| size++; |
| } |
| |
| gcc_assert (size); |
| method_list_template = build_v2_method_list_template (objc_method_template, |
| size); |
| snprintf (buf, BUFSIZE, "%s_%s", prefix, |
| IDENTIFIER_POINTER (PROTOCOL_NAME (protocol))); |
| |
| decl = start_var_decl (method_list_template, buf); |
| |
| entsize = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (objc_method_template)); |
| CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, build_int_cst (NULL_TREE, entsize)); |
| CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, build_int_cst (NULL_TREE, size)); |
| initlist = |
| build_v2_descriptor_table_initializer (objc_method_template, |
| chain); |
| CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, initlist); |
| /* Get into the right section. */ |
| OBJCMETA (decl, objc_meta, attr); |
| finish_var_decl (decl, objc_build_constructor (method_list_template, v)); |
| return decl; |
| } |
| |
| static tree |
| generate_v2_meth_type_list (vec<tree>& all_meths, tree protocol, |
| const char *prefix) |
| { |
| if (all_meths.is_empty () || !prefix) |
| return NULL_TREE; |
| |
| unsigned size = all_meths.length (); |
| tree list_type = build_sized_array_type (string_type_node, size); |
| char *nam; |
| asprintf (&nam, "%s_%s", prefix, |
| IDENTIFIER_POINTER (PROTOCOL_NAME (protocol))); |
| tree decl = start_var_decl (list_type, nam); |
| free (nam); |
| OBJCMETA (decl, objc_meta, meta_base); |
| vec<constructor_elt, va_gc> *v = NULL; |
| |
| for (unsigned i = 0; i < size; ++i) |
| CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, |
| add_objc_string (METHOD_ENCODING (all_meths[i]), |
| meth_var_types)); |
| finish_var_decl (decl, objc_build_constructor (list_type, v)); |
| return decl; |
| } |
| |
| /* This routine builds the initializer list to initialize the 'struct |
| _prop_t prop_list[]' field of 'struct _prop_list_t' meta-data. */ |
| |
| static tree |
| build_v2_property_table_initializer (tree type, tree context) |
| { |
| tree x; |
| vec<constructor_elt, va_gc> *inits = NULL; |
| if (TREE_CODE (context) == PROTOCOL_INTERFACE_TYPE) |
| x = CLASS_PROPERTY_DECL (context); |
| else |
| x = IMPL_PROPERTY_DECL (context); |
| |
| for (; x; x = TREE_CHAIN (x)) |
| { |
| vec<constructor_elt, va_gc> *elemlist = NULL; |
| /* NOTE! sections where property name/attribute go MUST change |
| later. */ |
| tree attribute, name_ident = PROPERTY_NAME (x); |
| |
| CONSTRUCTOR_APPEND_ELT (elemlist, NULL_TREE, |
| add_objc_string (name_ident, prop_names_attr)); |
| |
| attribute = objc_v2_encode_prop_attr (x); |
| CONSTRUCTOR_APPEND_ELT (elemlist, NULL_TREE, |
| add_objc_string (attribute, prop_names_attr)); |
| |
| CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, |
| objc_build_constructor (type, elemlist)); |
| } |
| |
| return objc_build_constructor (build_array_type (type, 0),inits); |
| } |
| |
| /* This routine builds the following type: |
| struct _prop_list_t |
| { |
| uint32_t entsize; // sizeof (struct _prop_t) |
| uint32_t prop_count; |
| struct _prop_t prop_list [prop_count]; |
| } |
| */ |
| |
| static tree |
| build_v2_property_list_template (tree list_type, int size) |
| { |
| tree property_list_t_record; |
| tree array_type, decls, *chain = NULL; |
| |
| /* anonymous. */ |
| property_list_t_record = objc_start_struct (NULL_TREE); |
| |
| /* uint32_t const entsize; */ |
| decls = add_field_decl (integer_type_node, "entsize", &chain); |
| |
| /* int prop_count; */ |
| add_field_decl (integer_type_node, "prop_count", &chain); |
| |
| /* struct _prop_t prop_list[]; */ |
| array_type = build_sized_array_type (list_type, size); |
| add_field_decl (array_type, "prop_list", &chain); |
| |
| objc_finish_struct (property_list_t_record, decls); |
| return property_list_t_record; |
| } |
| |
| /* Top-level routine to generate property tables for each |
| implementation. */ |
| |
| static tree |
| generate_v2_property_table (tree context, tree klass_ctxt) |
| { |
| tree x, decl, initlist, property_list_template; |
| bool is_proto = false; |
| vec<constructor_elt, va_gc> *inits = NULL; |
| int init_val, size = 0; |
| char buf[BUFSIZE]; |
| |
| if (context) |
| { |
| gcc_assert (TREE_CODE (context) == PROTOCOL_INTERFACE_TYPE); |
| x = CLASS_PROPERTY_DECL (context); |
| is_proto = true; |
| } |
| else |
| x = IMPL_PROPERTY_DECL (klass_ctxt); |
| |
| for (; x; x = TREE_CHAIN (x)) |
| size++; |
| |
| if (size == 0) |
| return NULL_TREE; |
| |
| property_list_template = |
| build_v2_property_list_template (objc_v2_property_template, |
| size); |
| |
| initlist = build_v2_property_table_initializer (objc_v2_property_template, |
| is_proto ? context |
| : klass_ctxt); |
| |
| init_val = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (objc_v2_property_template)); |
| if (is_proto) |
| snprintf (buf, BUFSIZE, "_OBJC_ProtocolPropList_%s", |
| IDENTIFIER_POINTER (PROTOCOL_NAME (context))); |
| else |
| snprintf (buf, BUFSIZE, "_OBJC_ClassPropList_%s", |
| IDENTIFIER_POINTER (CLASS_NAME (klass_ctxt))); |
| |
| decl = start_var_decl (property_list_template, buf); |
| |
| CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, |
| build_int_cst (NULL_TREE, init_val)); |
| CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, |
| build_int_cst (NULL_TREE, size)); |
| CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, initlist); |
| |
| OBJCMETA (decl, objc_meta, meta_base); |
| finish_var_decl (decl, objc_build_constructor (TREE_TYPE (decl), inits)); |
| return decl; |
| } |
| |
| static tree |
| build_v2_protocol_initializer (tree type, tree protocol_name, tree protocol_list, |
| tree inst_methods, tree class_methods, |
| tree opt_ins_meth, tree opt_cls_meth, |
| tree property_list, tree ext_meth_types, |
| tree demangled_name, tree class_prop_list) |
| { |
| tree expr, ttyp; |
| location_t loc; |
| vec<constructor_elt, va_gc> *inits = NULL; |
| |
| /* TODO: find a better representation of location from the inputs. */ |
| loc = UNKNOWN_LOCATION; |
| |
| /* This is NULL for the new ABI. */ |
| CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, |
| convert (objc_object_type, null_pointer_node)); |
| |
| CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, protocol_name); |
| CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, protocol_list); |
| |
| ttyp = objc_method_proto_list_ptr; |
| if (inst_methods) |
| expr = convert (ttyp, build_unary_op (loc, ADDR_EXPR, inst_methods, 0)); |
| else |
| expr = convert (ttyp, null_pointer_node); |
| CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, expr); |
| |
| if (class_methods) |
| expr = convert (ttyp, build_unary_op (loc, ADDR_EXPR, class_methods, 0)); |
| else |
| expr = convert (ttyp, null_pointer_node); |
| CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, expr); |
| |
| if (opt_ins_meth) |
| expr = convert (ttyp, build_unary_op (loc, ADDR_EXPR, opt_ins_meth, 0)); |
| else |
| expr = convert (ttyp, null_pointer_node); |
| CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, expr); |
| |
| if (opt_cls_meth) |
| expr = convert (ttyp, build_unary_op (loc, ADDR_EXPR, opt_cls_meth, 0)); |
| else |
| expr = convert (ttyp, null_pointer_node); |
| CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, expr); |
| |
| ttyp = objc_prop_list_ptr; |
| if (property_list) |
| expr = convert (ttyp, build_unary_op (loc, ADDR_EXPR, property_list, 0)); |
| else |
| expr = convert (ttyp, null_pointer_node); |
| CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, expr); |
| |
| /* const uint32_t size; = sizeof(struct protocol_t) */ |
| expr = build_int_cst (integer_type_node, |
| TREE_INT_CST_LOW (TYPE_SIZE_UNIT (objc_v2_protocol_template))); |
| CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, expr); |
| /* const uint32_t flags; = 0 */ |
| CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, integer_zero_node); |
| |
| ttyp = build_pointer_type (string_type_node); |
| if (ext_meth_types) |
| expr = convert (ttyp, build_unary_op (loc, ADDR_EXPR, ext_meth_types, 0)); |
| else |
| expr = convert (ttyp, null_pointer_node); |
| CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, expr); |
| |
| ttyp = string_type_node; |
| if (demangled_name) |
| expr = convert (ttyp, build_unary_op (loc, ADDR_EXPR, demangled_name, 0)); |
| else |
| expr = convert (ttyp, null_pointer_node); |
| CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, expr); |
| |
| ttyp = objc_prop_list_ptr; |
| if (class_prop_list) |
| expr = convert (ttyp, build_unary_op (loc, ADDR_EXPR, class_prop_list, 0)); |
| else |
| expr = convert (ttyp, null_pointer_node); |
| CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, expr); |
| |
| return objc_build_constructor (type, inits); |
| } |
| |
| /* Main routine to build all meta data for all protocols used in a |
| translation unit. */ |
| |
| static void |
| generate_v2_protocols (void) |
| { |
| tree p ; |
| bool some = false; |
| |
| if (!protocol_chain) |
| return ; |
| |
| /* If a protocol was directly referenced, pull in indirect |
| references. */ |
| for (p = protocol_chain; p; p = TREE_CHAIN (p)) |
| if (PROTOCOL_FORWARD_DECL (p) && PROTOCOL_LIST (p)) |
| generate_protocol_references (PROTOCOL_LIST (p)); |
| |
| for (p = protocol_chain; p; p = TREE_CHAIN (p)) |
| { |
| location_t loc; |
| tree inst_meth, class_meth, opt_inst_meth, opt_class_meth, props; |
| tree decl, initlist, protocol_name_expr, refs_decl, refs_expr; |
| |
| /* If protocol wasn't referenced, don't generate any code. */ |
| decl = PROTOCOL_FORWARD_DECL (p); |
| |
| if (!decl) |
| continue; |
| |
| loc = DECL_SOURCE_LOCATION (decl); |
| some = true; |
| |
| vec<tree> all_meths = vNULL; |
| inst_meth = |
| generate_v2_meth_descriptor_table (PROTOCOL_NST_METHODS (p), p, |
| "_OBJC_ProtocolInstanceMethods", |
| meta_proto_nst_meth, all_meths); |
| |
| class_meth = |
| generate_v2_meth_descriptor_table (PROTOCOL_CLS_METHODS (p), p, |
| "_OBJC_ProtocolClassMethods", |
| meta_proto_cls_meth, all_meths); |
| |
| opt_inst_meth = |
| generate_v2_meth_descriptor_table (PROTOCOL_OPTIONAL_NST_METHODS (p), p, |
| "_OBJC_ProtocolOptInstMethods", |
| meta_proto_nst_meth, all_meths); |
| |
| opt_class_meth = |
| generate_v2_meth_descriptor_table (PROTOCOL_OPTIONAL_CLS_METHODS (p), p, |
| "_OBJC_ProtocolOptClassMethods", |
| meta_proto_cls_meth, all_meths); |
| |
| if (PROTOCOL_LIST (p)) |
| refs_decl = generate_v2_protocol_list (p, NULL_TREE); |
| else |
| refs_decl = 0; |
| |
| /* static struct objc_protocol _OBJC_Protocol_<mumble>; */ |
| protocol_name_expr = add_objc_string (PROTOCOL_NAME (p), class_names); |
| |
| if (refs_decl) |
| refs_expr = convert (build_pointer_type (objc_v2_protocol_template), |
| build_unary_op (loc, ADDR_EXPR, refs_decl, 0)); |
| else |
| refs_expr = build_int_cst (NULL_TREE, 0); |
| |
| props = generate_v2_property_table (p, NULL_TREE); |
| |
| tree ext_meth_types |
| = generate_v2_meth_type_list (all_meths, p, |
| "_OBJC_ProtocolMethodTypes"); |
| tree demangled_name = NULL_TREE; |
| tree class_prop_list = NULL_TREE; |
| |
| initlist = build_v2_protocol_initializer (TREE_TYPE (decl), |
| protocol_name_expr, refs_expr, |
| inst_meth, class_meth, |
| opt_inst_meth, opt_class_meth, |
| props, ext_meth_types, |
| demangled_name,class_prop_list); |
| finish_var_decl (decl, initlist); |
| objc_add_to_protocol_list (p, decl); |
| all_meths.truncate (0); |
| } |
| |
| if (some) |
| { |
| /* Make sure we get the Protocol class linked in - reference |
| it... */ |
| p = objc_v2_get_class_reference (get_identifier (PROTOCOL_OBJECT_CLASS_NAME)); |
| /* ... but since we don't specifically use the reference... we |
| need to force it. */ |
| DECL_PRESERVE_P (p) = 1; |
| } |
| } |
| |
| static tree |
| generate_v2_dispatch_table (tree chain, const char *name, tree attr) |
| { |
| tree decl, method_list_template, initlist; |
| vec<constructor_elt, va_gc> *v = NULL; |
| int size, init_val; |
| |
| if (!chain || !name || !(size = list_length (chain))) |
| return NULL_TREE; |
| |
| method_list_template |
| = build_v2_method_list_template (objc_method_template, size); |
| initlist |
| = build_dispatch_table_initializer (objc_method_template, chain); |
| |
| decl = start_var_decl (method_list_template, name); |
| |
| init_val = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (objc_method_template)); |
| CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, |
| build_int_cst (integer_type_node, init_val)); |
| CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, |
| build_int_cst (integer_type_node, size)); |
| CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, initlist); |
| |
| OBJCMETA (decl, objc_meta, attr); |
| finish_var_decl (decl, |
| objc_build_constructor (TREE_TYPE (decl), v)); |
| return decl; |
| } |
| |
| /* Init a category. */ |
| static tree |
| build_v2_category_initializer (tree type, tree cat_name, tree class_name, |
| tree inst_methods, tree class_methods, |
| tree protocol_list, tree property_list, |
| location_t loc) |
| { |
| tree expr, ltyp; |
| vec<constructor_elt, va_gc> *v = NULL; |
| |
| CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, cat_name); |
| CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, class_name); |
| |
| ltyp = objc_method_list_ptr; |
| if (inst_methods) |
| expr = convert (ltyp, build_unary_op (loc, ADDR_EXPR, inst_methods, 0)); |
| else |
| expr = convert (ltyp, null_pointer_node); |
| CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, expr); |
| |
| if (class_methods) |
| expr = convert (ltyp, build_unary_op (loc, ADDR_EXPR, class_methods, 0)); |
| else |
| expr = convert (ltyp, null_pointer_node); |
| CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, expr); |
| |
| /* protocol_list = */ |
| ltyp = build_pointer_type (objc_v2_protocol_template); |
| if (protocol_list) |
| expr = convert (ltyp, build_unary_op (loc, ADDR_EXPR, protocol_list, 0)); |
| else |
| expr = convert (ltyp, null_pointer_node); |
| CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, expr); |
| |
| ltyp = objc_prop_list_ptr; |
| if (property_list) |
| expr = convert (ltyp, build_unary_op (loc, ADDR_EXPR, property_list, 0)); |
| else |
| expr = convert (ltyp, null_pointer_node); |
| CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, expr); |
| |
| return objc_build_constructor (type, v); |
| } |
| |
| /* static struct category_t _OBJC_CATEGORY_$_<name> = { ... }; */ |
| |
| static void |
| generate_v2_category (struct imp_entry *impent) |
| { |
| tree initlist, cat_name_expr, class_name_expr; |
| tree protocol_decl, category, props, t; |
| tree inst_methods = NULL_TREE, class_methods = NULL_TREE; |
| tree cat = impent->imp_context; |
| tree cat_decl = impent->class_decl; |
| location_t loc; |
| char buf[BUFSIZE]; |
| |
| loc = DECL_SOURCE_LOCATION (cat_decl); |
| |
| /* ??? not sure this is really necessary, the following references should |
| force appropriate linkage linkage... |
| -- but ... ensure a reference to the class... */ |
| t = objc_v2_get_class_reference (CLASS_NAME (cat)); |
| /* ... which we ignore so force it out.. */ |
| DECL_PRESERVE_P (t) = 1; |
| |
| snprintf (buf, BUFSIZE, "OBJC_CLASS_$_%s", IDENTIFIER_POINTER (CLASS_NAME (cat))); |
| class_name_expr = create_extern_decl (objc_v2_class_template, buf); |
| class_name_expr = build_fold_addr_expr (class_name_expr); |
| |
| cat_name_expr = add_objc_string (CLASS_SUPER_NAME (cat), class_names); |
| category = lookup_category (impent->imp_template, CLASS_SUPER_NAME (cat)); |
| |
| if (category && CLASS_PROTOCOL_LIST (category)) |
| { |
| generate_protocol_references (CLASS_PROTOCOL_LIST (category)); |
| protocol_decl = generate_v2_protocol_list (category, cat); |
| } |
| else |
| protocol_decl = NULL_TREE; |
| |
| /* decl = update_var_decl(impent->class_decl); */ |
| |
| props = generate_v2_property_table (NULL_TREE, cat); |
| |
| if (CLASS_NST_METHODS (cat)) |
| { |
| snprintf (buf, BUFSIZE, "_OBJC_CategoryInstanceMethods_%s_%s", |
| IDENTIFIER_POINTER (CLASS_NAME (cat)), |
| IDENTIFIER_POINTER (CLASS_SUPER_NAME (cat))); |
| inst_methods = generate_v2_dispatch_table (CLASS_NST_METHODS (cat), buf, |
| meta_cati_meth); |
| } |
| |
| if (CLASS_CLS_METHODS (cat)) |
| { |
| snprintf (buf, BUFSIZE, "_OBJC_CategoryClassMethods_%s_%s", |
| IDENTIFIER_POINTER (CLASS_NAME (cat)), |
| IDENTIFIER_POINTER (CLASS_SUPER_NAME (cat))); |
| class_methods = generate_v2_dispatch_table (CLASS_CLS_METHODS (cat), buf, |
| meta_catc_meth); |
| } |
| |
| initlist = build_v2_category_initializer (TREE_TYPE (cat_decl), |
| cat_name_expr, class_name_expr, |
| inst_methods, class_methods, |
| protocol_decl, props, loc); |
| |
| finish_var_decl (cat_decl, initlist); |
| impent->class_decl = cat_decl; |
| |
| /* Add to list of pointers in __category_list section. */ |
| objc_v2_add_to_category_list (cat_decl); |
| if (has_load_impl (CLASS_CLS_METHODS (impent->imp_context))) |
| objc_v2_add_to_nonlazy_category_list (cat_decl); |
| } |
| |
| /* This routine declares a variable to hold the offset for ivar |
| FIELD_DECL. Variable name is .objc_ivar.ClassName.IvarName. */ |
| |
| struct GTY(()) ivarref_entry |
| { |
| tree decl; |
| tree offset; |
| }; |
| |
| static GTY (()) vec<ivarref_entry, va_gc> *ivar_offset_refs; |
| |
| static tree |
| ivar_offset_ref (tree class_name, tree field_decl) |
| { |
| tree decl, field_decl_id; |
| ivarref_entry e; |
| bool global_var; |
| char buf[512]; |
| |
| create_ivar_offset_name (buf, class_name, field_decl); |
| field_decl_id = get_identifier (buf); |
| |
| if (ivar_offset_refs) |
| { |
| int count; |
| ivarref_entry *ref; |
| FOR_EACH_VEC_ELT (*ivar_offset_refs, count, ref) |
| if (DECL_NAME (ref->decl) == field_decl_id) |
| return ref->decl; |
| } |
| else |
| /* Somewhat arbitrary initial provision. */ |
| vec_alloc (ivar_offset_refs, 32); |
| |
| /* We come here if we don't find a match or at the start. */ |
| global_var = (TREE_PUBLIC (field_decl) || TREE_PROTECTED (field_decl)); |
| if (global_var) |
| decl = create_global_decl (TREE_TYPE (size_zero_node), buf); |
| else |
| decl = create_hidden_decl (TREE_TYPE (size_zero_node), buf); |
| |
| /* Identify so that we can indirect these where the ABI requires. */ |
| OBJCMETA (decl, objc_meta, meta_ivar_ref); |
| |
| e.decl = decl; |
| e.offset = byte_position (field_decl); |
| vec_safe_push (ivar_offset_refs, e); |
| return decl; |
| } |
| |
| /* This routine builds initializer-list needed to initialize 'struct |
| ivar_t list[count] of 'struct ivar_list_t' meta data. TYPE is |
| 'struct ivar_t' and FIELD_DECL is list of ivars for the target |
| class. */ |
| |
| static tree |
| build_v2_ivar_list_initializer (tree class_name, tree type, tree field_decl) |
| { |
| vec<constructor_elt, va_gc> *inits = NULL; |
| |
| do |
| { |
| vec<constructor_elt, va_gc> *ivar = NULL; |
| int val; |
| tree id; |
| |
| /* Unnamed bitfields are ignored. */ |
| if (!DECL_NAME (field_decl)) |
| { |
| field_decl = DECL_CHAIN (field_decl); |
| continue; |
| } |
| |
| /* Set offset. */ |
| CONSTRUCTOR_APPEND_ELT (ivar, NULL_TREE, |
| build_unary_op (input_location, |
| ADDR_EXPR, |
| ivar_offset_ref (class_name, |
| field_decl), 0)); |
| |
| /* Set name. */ |
| CONSTRUCTOR_APPEND_ELT (ivar, NULL_TREE, |
| add_objc_string (DECL_NAME (field_decl), |
| meth_var_names)); |
| |
| /* Set type. */ |
| id = add_objc_string (encode_field_decl (field_decl), |
| meth_var_types); |
| CONSTRUCTOR_APPEND_ELT (ivar, NULL_TREE, id); |
| |
| /* Set alignment. */ |
| val = DECL_ALIGN_UNIT (field_decl); |
| val = exact_log2 (val); |
| CONSTRUCTOR_APPEND_ELT (ivar, NULL_TREE, |
| build_int_cst (integer_type_node, val)); |
| |
| /* Set size. */ |
| val = TREE_INT_CST_LOW (DECL_SIZE_UNIT (field_decl)); |
| CONSTRUCTOR_APPEND_ELT (ivar, NULL_TREE, |
| build_int_cst (integer_type_node, val)); |
| |
| CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, |
| objc_build_constructor (type, ivar)); |
| |
| do |
| field_decl = DECL_CHAIN (field_decl); |
| while (field_decl && TREE_CODE (field_decl) != FIELD_DECL); |
| } |
| while (field_decl); |
| |
| return objc_build_constructor (build_array_type (type, 0), inits); |
| } |
| |
| /* |
| struct ivar_list_t |
| { |
| uint32 entsize; |
| uint32 count; |
| struct iver_t list[count]; |
| }; |
| */ |
| |
| static tree |
| build_v2_ivar_list_t_template (tree list_type, int size) |
| { |
| tree objc_ivar_list_record; |
| tree decls, *chain = NULL; |
| |
| /* Anonymous. */ |
| objc_ivar_list_record = objc_start_struct (NULL_TREE); |
| |
| /* uint32 entsize; */ |
| decls = add_field_decl (integer_type_node, "entsize", &chain); |
| |
| /* uint32 count; */ |
| add_field_decl (integer_type_node, "count", &chain); |
| |
| /* struct objc_ivar ivar_list[]; */ |
| add_field_decl (build_sized_array_type (list_type, size), |
| "list", &chain); |
| |
| objc_finish_struct (objc_ivar_list_record, decls); |
| return objc_ivar_list_record; |
| } |
| |
| /* This routine declares a static variable of type 'struct |
| ivar_list_t' and initializes it. chain is the source of the data, |
| name is the name for the var. attr is the meta-data section tag |
| attribute. templ is the implementation template for the class. */ |
| |
| static tree |
| generate_v2_ivars_list (tree chain, const char *name, tree attr, tree templ) |
| { |
| tree decl, initlist, ivar_list_template; |
| vec<constructor_elt, va_gc> *inits = NULL; |
| int size, ivar_t_size; |
| |
| if (!chain || !name || !(size = ivar_list_length (chain))) |
| return NULL_TREE; |
| |
| generating_instance_variables = 1; |
| ivar_list_template = build_v2_ivar_list_t_template (objc_v2_ivar_template, |
| size); |
| |
| initlist = build_v2_ivar_list_initializer (CLASS_NAME (templ), |
| objc_v2_ivar_template, chain); |
| ivar_t_size = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (objc_v2_ivar_template)); |
| |
| decl = start_var_decl (ivar_list_template, name); |
| CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, |
| build_int_cst (integer_type_node, ivar_t_size)); |
| CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, |
| build_int_cst (integer_type_node, size)); |
| CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, initlist); |
| OBJCMETA (decl, objc_meta, attr); |
| finish_var_decl (decl, objc_build_constructor (TREE_TYPE (decl), inits)); |
| generating_instance_variables = 0; |
| return decl; |
| } |
| |
| /* Routine to build initializer list to initialize objects of type |
| struct class_t; */ |
| |
| static tree |
| build_v2_class_t_initializer (tree type, tree isa, tree superclass, |
| tree ro, tree cache, tree vtable) |
| { |
| vec<constructor_elt, va_gc> *initlist = NULL; |
| |
| /* isa */ |
| CONSTRUCTOR_APPEND_ELT (initlist, NULL_TREE, isa); |
| |
| /* superclass */ |
| CONSTRUCTOR_APPEND_ELT (initlist, NULL_TREE, superclass); |
| |
| /* cache */ |
| if (cache) |
| CONSTRUCTOR_APPEND_ELT (initlist, NULL_TREE, cache); |
| else |
| CONSTRUCTOR_APPEND_ELT (initlist, NULL_TREE, null_pointer_node); |
| |
| /* vtable */ |
| if (vtable) |
| CONSTRUCTOR_APPEND_ELT (initlist, NULL_TREE, vtable); |
| else |
| CONSTRUCTOR_APPEND_ELT (initlist, NULL_TREE, null_pointer_node); |
| |
| /* ro */ |
| CONSTRUCTOR_APPEND_ELT (initlist, NULL_TREE, ro); |
| |
| return objc_build_constructor (type, initlist); |
| } |
| |
| /* Routine to build object of struct class_ro_t { ... }; */ |
| |
| static tree |
| build_v2_class_ro_t_initializer (tree type, tree name, |
| unsigned int flags, unsigned int instanceStart, |
| unsigned int instanceSize, |
| tree ivarLayout, |
| tree baseMethods, tree baseProtocols, |
| tree ivars, tree property_list) |
| { |
| tree expr, unsigned_char_star, ltyp; |
| location_t loc; |
| vec<constructor_elt, va_gc> *initlist = NULL; |
| |
| /* TODO: fish out the real location from somewhere. */ |
| loc = UNKNOWN_LOCATION; |
| |
| /* flags */ |
| CONSTRUCTOR_APPEND_ELT (initlist, NULL_TREE, |
| build_int_cst (integer_type_node, flags)); |
| |
| /* instanceStart */ |
| CONSTRUCTOR_APPEND_ELT (initlist, NULL_TREE, |
| build_int_cst (integer_type_node, instanceStart)); |
| |
| /* instanceSize */ |
| CONSTRUCTOR_APPEND_ELT (initlist, NULL_TREE, |
| build_int_cst (integer_type_node, instanceSize)); |
| |
| /* This ABI is currently only used on m64 NeXT. We always |
| explicitly declare the alignment padding. */ |
| /* reserved, pads alignment. */ |
| CONSTRUCTOR_APPEND_ELT (initlist, NULL_TREE, |
| build_int_cst (integer_type_node, 0)); |
| |
| /* ivarLayout */ |
| unsigned_char_star = build_pointer_type (unsigned_char_type_node); |
| if (ivarLayout) |
| expr = ivarLayout; |
| else |
| expr = convert (unsigned_char_star, null_pointer_node); |
| CONSTRUCTOR_APPEND_ELT (initlist, NULL_TREE, expr); |
| |
| /* name */ |
| CONSTRUCTOR_APPEND_ELT (initlist, NULL_TREE, default_conversion (name)); |
| |
| /* baseMethods */ |
| ltyp = objc_method_list_ptr; |
| if (baseMethods) |
| expr = convert (ltyp, build_unary_op (loc, ADDR_EXPR, baseMethods, 0)); |
| else |
| expr = convert (ltyp, null_pointer_node); |
| CONSTRUCTOR_APPEND_ELT (initlist, NULL_TREE, expr); |
| |
| /* baseProtocols */ |
| ltyp = build_pointer_type (xref_tag (RECORD_TYPE, |
| get_identifier (UTAG_V2_PROTOCOL_LIST))); |
| if (baseProtocols) |
| expr = convert (ltyp, build_unary_op (loc, ADDR_EXPR, baseProtocols, 0)); |
| else |
| expr = convert (ltyp, null_pointer_node); |
| CONSTRUCTOR_APPEND_ELT (initlist, NULL_TREE, expr); |
| |
| /* ivars */ |
| ltyp = objc_v2_ivar_list_ptr; |
| if (ivars) |
| expr = convert (ltyp, build_unary_op (loc, ADDR_EXPR, ivars, 0)); |
| else |
| expr = convert (ltyp, null_pointer_node); |
| CONSTRUCTOR_APPEND_ELT (initlist, NULL_TREE, expr); |
| |
| /* TODO: We don't yet have the weak/strong stuff... */ |
| /* weakIvarLayout */ |
| CONSTRUCTOR_APPEND_ELT (initlist, NULL_TREE, |
| convert (unsigned_char_star, null_pointer_node)); |
| |
| /* property list */ |
| ltyp = objc_prop_list_ptr; |
| if (property_list) |
| expr = convert (ltyp, build_unary_op (loc, ADDR_EXPR, property_list, 0)); |
| else |
| expr = convert (ltyp, null_pointer_node); |
| CONSTRUCTOR_APPEND_ELT (initlist, NULL_TREE, expr); |
| return objc_build_constructor (type, initlist); |
| } |
| |
| static GTY (()) vec<ident_data_tuple, va_gc> *ehtype_list; |
| |
| /* Record a name as needing a catcher. */ |
| static void |
| objc_v2_add_to_ehtype_list (tree name) |
| { |
| ident_data_tuple e; |
| if (ehtype_list) |
| { |
| int count = 0; |
| ident_data_tuple *ref; |
| |
| FOR_EACH_VEC_ELT (*ehtype_list, count, ref) |
| if (ref->ident == name) |
| return; /* Already entered. */ |
| } |
| else |
| /* Arbitrary initial count. */ |
| vec_alloc (ehtype_list, 8); |
| |
| /* Not found, or new list. */ |
| e.ident = name; |
| e.data = NULL_TREE; |
| vec_safe_push (
|