| /* Basic data types for Objective C. |
| Copyright (C) 1998-2022 Free Software Foundation, Inc. |
| Contributed by Ovidiu Predescu. |
| |
| 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. |
| |
| Under Section 7 of GPL version 3, you are granted additional |
| permissions described in the GCC Runtime Library Exception, version |
| 3.1, as published by the Free Software Foundation. |
| |
| You should have received a copy of the GNU General Public License and |
| a copy of the GCC Runtime Library Exception along with this program; |
| see the files COPYING3 and COPYING.RUNTIME respectively. If not, see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "objc-private/common.h" |
| #include "objc/objc.h" |
| |
| #if OBJC_WITH_GC |
| |
| #include "tconfig.h" |
| #include <assert.h> |
| #include <ctype.h> /* For isdigit. */ |
| #include <string.h> |
| #include <stdlib.h> |
| #include "objc/runtime.h" |
| #include "objc-private/module-abi-8.h" |
| |
| #include <gc/gc.h> |
| #include <limits.h> |
| |
| /* gc_typed.h uses the following but doesn't declare them */ |
| typedef GC_word word; |
| typedef GC_signed_word signed_word; |
| #define BITS_PER_WORD (CHAR_BIT * sizeof (word)) |
| |
| #include <gc/gc_typed.h> |
| |
| /* The following functions set up in `mask` the corresponding pointers. |
| The offset is incremented with the size of the type. */ |
| |
| #define ROUND(V, A) \ |
| ({ typeof (V) __v = (V); typeof (A) __a = (A); \ |
| __a * ((__v+__a - 1)/__a); }) |
| |
| #define SET_BIT_FOR_OFFSET(mask, offset) \ |
| GC_set_bit (mask, offset / sizeof (void *)) |
| |
| /* Some prototypes */ |
| static void |
| __objc_gc_setup_struct (GC_bitmap mask, const char *type, int offset); |
| static void |
| __objc_gc_setup_union (GC_bitmap mask, const char *type, int offset); |
| |
| |
| static void |
| __objc_gc_setup_array (GC_bitmap mask, const char *type, int offset) |
| { |
| int i, len = atoi (type + 1); |
| |
| while (isdigit (*++type)) |
| /* do nothing */; /* skip the size of the array */ |
| |
| switch (*type) { |
| case _C_ARY_B: |
| for (i = 0; i < len; i++) |
| __objc_gc_setup_array (mask, type, offset); |
| break; |
| |
| case _C_STRUCT_B: |
| for (i = 0; i < len; i++) |
| __objc_gc_setup_struct (mask, type, offset); |
| break; |
| |
| case _C_UNION_B: |
| for (i = 0; i < len; i++) |
| __objc_gc_setup_union (mask, type, offset); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| static void |
| __objc_gc_setup_struct (GC_bitmap mask, const char *type, int offset) |
| { |
| struct objc_struct_layout layout; |
| unsigned int position; |
| const char *mtype; |
| |
| objc_layout_structure (type, &layout); |
| |
| while (objc_layout_structure_next_member (&layout)) |
| { |
| BOOL gc_invisible = NO; |
| |
| objc_layout_structure_get_info (&layout, &position, NULL, &mtype); |
| |
| /* Skip the variable name */ |
| if (*mtype == '"') |
| { |
| for (mtype++; *mtype++ != '"';) |
| /* do nothing */; |
| } |
| |
| if (*mtype == _C_GCINVISIBLE) |
| { |
| gc_invisible = YES; |
| mtype++; |
| } |
| |
| /* Add to position the offset of this structure */ |
| position += offset; |
| |
| switch (*mtype) { |
| case _C_ID: |
| case _C_CLASS: |
| case _C_SEL: |
| case _C_PTR: |
| case _C_CHARPTR: |
| case _C_ATOM: |
| if (! gc_invisible) |
| SET_BIT_FOR_OFFSET (mask, position); |
| break; |
| |
| case _C_ARY_B: |
| __objc_gc_setup_array (mask, mtype, position); |
| break; |
| |
| case _C_STRUCT_B: |
| __objc_gc_setup_struct (mask, mtype, position); |
| break; |
| |
| case _C_UNION_B: |
| __objc_gc_setup_union (mask, mtype, position); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| } |
| |
| static void |
| __objc_gc_setup_union (GC_bitmap mask, const char *type, int offset) |
| { |
| /* Sub-optimal, quick implementation: assume the union is made of |
| pointers, set up the mask accordingly. */ |
| |
| int i, size, align; |
| |
| /* Skip the variable name */ |
| if (*type == '"') |
| { |
| for (type++; *type++ != '"';) |
| /* do nothing */; |
| } |
| |
| size = objc_sizeof_type (type); |
| align = objc_alignof_type (type); |
| |
| offset = ROUND (offset, align); |
| for (i = 0; i < size; i += sizeof (void *)) |
| { |
| SET_BIT_FOR_OFFSET (mask, offset); |
| offset += sizeof (void *); |
| } |
| } |
| |
| |
| /* Iterates over the types in the structure that represents the class |
| encoding and sets the bits in mask according to each ivar type. */ |
| static void |
| __objc_gc_type_description_from_type (GC_bitmap mask, const char *type) |
| { |
| struct objc_struct_layout layout; |
| unsigned int offset, align; |
| const char *ivar_type; |
| |
| objc_layout_structure (type, &layout); |
| |
| while (objc_layout_structure_next_member (&layout)) |
| { |
| BOOL gc_invisible = NO; |
| |
| objc_layout_structure_get_info (&layout, &offset, &align, &ivar_type); |
| |
| /* Skip the variable name */ |
| if (*ivar_type == '"') |
| { |
| for (ivar_type++; *ivar_type++ != '"';) |
| /* do nothing */; |
| } |
| |
| if (*ivar_type == _C_GCINVISIBLE) |
| { |
| gc_invisible = YES; |
| ivar_type++; |
| } |
| |
| switch (*ivar_type) { |
| case _C_ID: |
| case _C_CLASS: |
| case _C_SEL: |
| case _C_PTR: |
| case _C_CHARPTR: |
| if (! gc_invisible) |
| SET_BIT_FOR_OFFSET (mask, offset); |
| break; |
| |
| case _C_ARY_B: |
| __objc_gc_setup_array (mask, ivar_type, offset); |
| break; |
| |
| case _C_STRUCT_B: |
| __objc_gc_setup_struct (mask, ivar_type, offset); |
| break; |
| |
| case _C_UNION_B: |
| __objc_gc_setup_union (mask, ivar_type, offset); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| } |
| |
| /* Computes in *type the full type encoding of this class including |
| its super classes. '*size' gives the total number of bytes allocated |
| into *type, '*current' the number of bytes used so far by the |
| encoding. */ |
| static void |
| __objc_class_structure_encoding (Class class, char **type, int *size, |
| int *current) |
| { |
| int i, ivar_count; |
| struct objc_ivar_list *ivars; |
| |
| if (! class) |
| { |
| strcat (*type, "{"); |
| (*current)++; |
| return; |
| } |
| |
| /* Add the type encodings of the super classes */ |
| __objc_class_structure_encoding (class->super_class, type, size, current); |
| |
| ivars = class->ivars; |
| if (! ivars) |
| return; |
| |
| ivar_count = ivars->ivar_count; |
| |
| for (i = 0; i < ivar_count; i++) |
| { |
| struct objc_ivar *ivar = &(ivars->ivar_list[i]); |
| const char *ivar_type = ivar->ivar_type; |
| int len = strlen (ivar_type); |
| |
| if (*current + len + 1 >= *size) |
| { |
| /* Increase the size of the encoding string so that it |
| contains this ivar's type. */ |
| *size = ROUND (*current + len + 1, 10); |
| *type = objc_realloc (*type, *size); |
| } |
| strcat (*type + *current, ivar_type); |
| *current += len; |
| } |
| } |
| |
| |
| /* Allocates the memory that will hold the type description for class |
| and calls the __objc_class_structure_encoding that generates this |
| value. */ |
| void |
| __objc_generate_gc_type_description (Class class) |
| { |
| GC_bitmap mask; |
| int bits_no, size; |
| int type_size = 10, current; |
| char *class_structure_type; |
| |
| if (! CLS_ISCLASS (class)) |
| return; |
| |
| /* We have to create a mask in which each bit counts for a pointer member. |
| We take into consideration all the non-pointer instance variables and we |
| round them up to the alignment. */ |
| |
| /* The number of bits in the mask is the size of an instance in bytes divided |
| by the size of a pointer. */ |
| bits_no = (ROUND (class_getInstanceSize (class), sizeof (void *)) |
| / sizeof (void *)); |
| size = ROUND (bits_no, BITS_PER_WORD) / BITS_PER_WORD; |
| mask = objc_atomic_malloc (size * sizeof (int)); |
| memset (mask, 0, size * sizeof (int)); |
| |
| class_structure_type = objc_atomic_malloc (type_size); |
| *class_structure_type = current = 0; |
| __objc_class_structure_encoding (class, &class_structure_type, |
| &type_size, ¤t); |
| if (current + 1 == type_size) |
| class_structure_type = objc_realloc (class_structure_type, ++type_size); |
| strcat (class_structure_type + current, "}"); |
| #ifdef DEBUG |
| printf ("type description for '%s' is %s\n", class->name, class_structure_type); |
| #endif |
| |
| __objc_gc_type_description_from_type (mask, class_structure_type); |
| objc_free (class_structure_type); |
| |
| #ifdef DEBUG |
| printf (" mask for '%s', type '%s' (bits %d, mask size %d) is:", |
| class_structure_type, class->name, bits_no, size); |
| { |
| int i; |
| for (i = 0; i < size; i++) |
| printf (" %lx", mask[i]); |
| } |
| puts (""); |
| #endif |
| |
| class->gc_object_type = (void *) GC_make_descriptor (mask, bits_no); |
| } |
| |
| |
| /* Returns YES if type denotes a pointer type, NO otherwise */ |
| static inline BOOL |
| __objc_ivar_pointer (const char *type) |
| { |
| type = objc_skip_type_qualifiers (type); |
| |
| return (*type == _C_ID |
| || *type == _C_CLASS |
| || *type == _C_SEL |
| || *type == _C_PTR |
| || *type == _C_CHARPTR |
| || *type == _C_ATOM); |
| } |
| |
| |
| /* Mark the instance variable whose name is given by ivarname as a |
| weak pointer (a pointer hidden to the garbage collector) if |
| gc_invisible is true. If gc_invisible is false it unmarks the |
| instance variable and makes it a normal pointer, visible to the |
| garbage collector. |
| |
| This operation only makes sense on instance variables that are |
| pointers. */ |
| void |
| class_ivar_set_gcinvisible (Class class, const char *ivarname, |
| BOOL gc_invisible) |
| { |
| int i, ivar_count; |
| struct objc_ivar_list *ivars; |
| |
| if (! class || ! ivarname) |
| return; |
| |
| ivars = class->ivars; |
| if (! ivars) |
| return; |
| |
| ivar_count = ivars->ivar_count; |
| |
| for (i = 0; i < ivar_count; i++) |
| { |
| struct objc_ivar *ivar = &(ivars->ivar_list[i]); |
| const char *type; |
| |
| if (! ivar->ivar_name || strcmp (ivar->ivar_name, ivarname)) |
| continue; |
| |
| assert (ivar->ivar_type); |
| type = ivar->ivar_type; |
| |
| /* Skip the variable name */ |
| if (*type == '"') |
| { |
| for (type++; *type++ != '"';) |
| /* do nothing */; |
| } |
| |
| if (*type == _C_GCINVISIBLE) |
| { |
| char *new_type; |
| size_t len; |
| |
| if (gc_invisible || ! __objc_ivar_pointer (type)) |
| return; /* The type of the variable already matches the |
| requested gc_invisible type */ |
| |
| /* The variable is gc_invisible so we make it gc visible. */ |
| new_type = objc_atomic_malloc (strlen(ivar->ivar_type)); |
| len = (type - ivar->ivar_type); |
| memcpy (new_type, ivar->ivar_type, len); |
| new_type[len] = 0; |
| strcat (new_type, type + 1); |
| ivar->ivar_type = new_type; |
| } |
| else |
| { |
| char *new_type; |
| size_t len; |
| |
| if (! gc_invisible || ! __objc_ivar_pointer (type)) |
| return; /* The type of the variable already matches the |
| requested gc_invisible type */ |
| |
| /* The variable is gc visible so we make it gc_invisible. */ |
| new_type = objc_malloc (strlen(ivar->ivar_type) + 2); |
| |
| /* Copy the variable name. */ |
| len = (type - ivar->ivar_type); |
| memcpy (new_type, ivar->ivar_type, len); |
| /* Add '!'. */ |
| new_type[len++] = _C_GCINVISIBLE; |
| /* Copy the original types. */ |
| strcpy (new_type + len, type); |
| |
| ivar->ivar_type = new_type; |
| } |
| |
| __objc_generate_gc_type_description (class); |
| return; |
| } |
| |
| /* Search the instance variable in the superclasses */ |
| class_ivar_set_gcinvisible (class->super_class, ivarname, gc_invisible); |
| } |
| |
| #else /* !OBJC_WITH_GC */ |
| |
| void |
| __objc_generate_gc_type_description (Class class __attribute__ ((__unused__))) |
| { |
| } |
| |
| void class_ivar_set_gcinvisible (Class class __attribute__ ((__unused__)), |
| const char *ivarname __attribute__ ((__unused__)), |
| BOOL gc_invisible __attribute__ ((__unused__))) |
| { |
| } |
| |
| #endif /* OBJC_WITH_GC */ |