gcc/libobjc/gc.c
2023-01-16 11:52:17 +01:00

460 lines
12 KiB
C

/* Basic data types for Objective C.
Copyright (C) 1998-2023 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, &current);
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 */