/* Basic data types for Objective C. Copyright (C) 1998-2015 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.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_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 */