2012-03-27 23:13:14 +00:00
|
|
|
/* Basic data types for Objective C.
|
2015-08-28 15:33:40 +00:00
|
|
|
Copyright (C) 1998-2015 Free Software Foundation, Inc.
|
2012-03-27 23:13:14 +00:00
|
|
|
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 */
|