Finished rewrite of message sending. Deleted lots of legacy stuff.
parent
9dcfe43e69
commit
b1b9baf382
@ -0,0 +1,75 @@
|
||||
#include "lock.h"
|
||||
#include "class.h"
|
||||
#include "sarray2.h"
|
||||
/**
|
||||
* Pointer to the sparse array representing the pretend (uninstalled) dtable.
|
||||
*/
|
||||
extern SparseArray *__objc_uninstalled_dtable;
|
||||
/**
|
||||
* Structure for maintaining a linked list of temporary dtables. When sending
|
||||
* an +initialize message to a class, we create a temporary dtables and store
|
||||
* it in a linked list. This is then used when sending other messages to
|
||||
* instances of classes in the middle of initialisation.
|
||||
*/
|
||||
typedef struct _InitializingDtable
|
||||
{
|
||||
/** The class that owns the dtable. */
|
||||
Class class;
|
||||
/** The dtable for this class. */
|
||||
void *dtable;
|
||||
/** The next uninstalled dtable in the list. */
|
||||
struct _InitializingDtable *next;
|
||||
} InitializingDtable;
|
||||
|
||||
/** Head of the list of temporary dtables. Protected by initialize_lock. */
|
||||
extern InitializingDtable *temporary_dtables;
|
||||
mutex_t initialize_lock;
|
||||
|
||||
static inline int classHasInstalledDtable(struct objc_class *cls)
|
||||
{
|
||||
return ((void*)cls->dtable != __objc_uninstalled_dtable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the dtable for a given class. If we are currently in an +initialize
|
||||
* method then this will block if called from a thread other than the one
|
||||
* running the +initialize method.
|
||||
*/
|
||||
static inline SparseArray *dtable_for_class(Class cls)
|
||||
{
|
||||
if (classHasInstalledDtable(cls))
|
||||
{
|
||||
return (SparseArray*)cls->dtable;
|
||||
}
|
||||
LOCK_UNTIL_RETURN(&initialize_lock);
|
||||
if (classHasInstalledDtable(cls))
|
||||
{
|
||||
return (SparseArray*)cls->dtable;
|
||||
}
|
||||
/* This is a linear search, and so, in theory, could be very slow. It is
|
||||
* O(n) where n is the number of +initialize methods on the stack. In
|
||||
* practice, this is a very small number. Profiling with GNUstep showed that
|
||||
* this peaks at 8. */
|
||||
SparseArray *dtable = __objc_uninstalled_dtable;
|
||||
InitializingDtable *buffer = temporary_dtables;
|
||||
while (NULL != buffer)
|
||||
{
|
||||
if (buffer->class == cls)
|
||||
{
|
||||
dtable = (SparseArray*)buffer->dtable;
|
||||
break;
|
||||
}
|
||||
buffer = buffer->next;
|
||||
}
|
||||
UNLOCK(&initialize_lock);
|
||||
if (dtable == 0)
|
||||
{
|
||||
dtable = __objc_uninstalled_dtable;
|
||||
}
|
||||
return dtable;
|
||||
}
|
||||
|
||||
static inline int classHasDtable(struct objc_class *cls)
|
||||
{
|
||||
return (dtable_for_class(cls) != __objc_uninstalled_dtable);
|
||||
}
|
||||
@ -1,451 +0,0 @@
|
||||
/* Basic data types for Objective C.
|
||||
Copyright (C) 1998, 2002, 2004, 2005, 2006, 2009 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/objc.h"
|
||||
#include "objc/encoding.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if OBJC_WITH_GC
|
||||
|
||||
#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_get_instance_size (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);
|
||||
len = (type - ivar->ivar_type);
|
||||
memcpy (new_type, ivar->ivar_type, len);
|
||||
new_type[len] = 0;
|
||||
strcat (new_type, "!");
|
||||
strcat (new_type, 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 */
|
||||
@ -1,39 +0,0 @@
|
||||
/* Force linking of classes required by Objective C runtime.
|
||||
Copyright (C) 1997, 2009 Free Software Foundation, Inc.
|
||||
Contributed by Ovidiu Predescu (ovidiu@net-community.com).
|
||||
|
||||
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/Object.h>
|
||||
#include <objc/NXConstStr.h>
|
||||
|
||||
/* Generate references to Object and NXConstanstString classes since they are
|
||||
needed by the runtime system to run correctly. */
|
||||
|
||||
|
||||
void __objc_linking (void)
|
||||
{
|
||||
[Object name];
|
||||
[NXConstantString name];
|
||||
}
|
||||
|
||||
@ -1,54 +0,0 @@
|
||||
/* GNU Objective C Runtime nil receiver function
|
||||
Copyright (C) 1993, 1995, 1996, 2002, 2009 Free Software Foundation, Inc.
|
||||
Contributed by Kresten Krab Thorup
|
||||
|
||||
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/>. */
|
||||
|
||||
|
||||
/* This is the nil method, the function that is called when the receiver
|
||||
of a method is nil */
|
||||
|
||||
#include "objc/runtime-legacy.h"
|
||||
|
||||
/* When the receiver of a method invocation is nil, the runtime
|
||||
returns nil_method() as the method implementation. This function
|
||||
will be casted to whatever function was supposed to be executed to
|
||||
execute that method (that function will take an id, followed by a
|
||||
SEL, followed by who knows what arguments, depends on the method),
|
||||
and executed.
|
||||
|
||||
For this reason, nil_method() should be a function which can be
|
||||
called in place of any function taking an 'id' argument followed by
|
||||
a 'SEL' argument, followed by zero, or one, or any number of
|
||||
arguments (both a fixed number, or a variable number !).
|
||||
|
||||
There is no "proper" implementation of such a nil_method function
|
||||
in C, however in all existing implementations it does not matter
|
||||
when extra arguments are present, so we can simply create a function
|
||||
taking a receiver and a selector, and all other arguments will be
|
||||
ignored. :-)
|
||||
*/
|
||||
|
||||
id
|
||||
nil_method (id receiver, SEL op __attribute__ ((__unused__)))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@ -1,499 +0,0 @@
|
||||
/* GNU Objective C Runtime message lookup
|
||||
Copyright (C) 1993, 1995, 1996, 1997, 1998,
|
||||
2001, 2002, 2004, 2009 Free Software Foundation, Inc.
|
||||
Contributed by Kresten Krab Thorup
|
||||
|
||||
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 <stdlib.h>
|
||||
#include "objc/runtime-legacy.h"
|
||||
#include "objc/slot.h"
|
||||
#include "objc/encoding.h"
|
||||
#include "lock.h"
|
||||
#include "slot_pool.h"
|
||||
|
||||
/* Mutex protecting the pre-initialize dtable */
|
||||
static mutex_t initialize_lock;
|
||||
|
||||
void objc_resolve_class(Class);
|
||||
|
||||
/* Two hooks for method forwarding. If either is set, it is invoked
|
||||
* to return a function that performs the real forwarding. If both
|
||||
* are set, the result of __objc_msg_forward2 will be preferred over
|
||||
* that of __objc_msg_forward. If both return NULL or are unset,
|
||||
* the libgcc based functions (__builtin_apply and friends) are
|
||||
* used.
|
||||
*/
|
||||
IMP (*__objc_msg_forward) (SEL) = NULL;
|
||||
IMP (*__objc_msg_forward2) (id, SEL) = NULL;
|
||||
|
||||
/* Send +initialize to class */
|
||||
static void __objc_send_initialize (Class);
|
||||
|
||||
static void __objc_install_dispatch_table_for_class (Class);
|
||||
|
||||
|
||||
#include "dtable.c"
|
||||
|
||||
/* Forward declare some functions */
|
||||
static void __objc_init_install_dtable (id, SEL);
|
||||
|
||||
static Method_t search_for_method_in_hierarchy (Class class, SEL sel);
|
||||
Method_t search_for_method_in_list (MethodList_t list, SEL op);
|
||||
id nil_method (id, SEL);
|
||||
|
||||
/* Given a selector, return the proper forwarding implementation. */
|
||||
inline static
|
||||
IMP
|
||||
__objc_get_forward_imp (id rcv, SEL sel)
|
||||
{
|
||||
/* If a custom forwarding hook was registered, try getting a forwarding
|
||||
function from it. There are two forward routine hooks, one that
|
||||
takes the receiver as an argument and one that does not. */
|
||||
if (__objc_msg_forward2)
|
||||
{
|
||||
IMP result;
|
||||
if ((result = __objc_msg_forward2 (rcv, sel)) != NULL)
|
||||
return result;
|
||||
}
|
||||
if (__objc_msg_forward)
|
||||
{
|
||||
IMP result;
|
||||
if ((result = __objc_msg_forward (sel)) != NULL)
|
||||
return result;
|
||||
}
|
||||
fprintf(stderr, "Object forwarding not available");
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
static inline IMP sarray_get_imp (void *dtable, size_t key)
|
||||
{
|
||||
struct objc_slot *slot = sarray_get_safe (dtable, key);
|
||||
return (NULL != slot) ? slot->method : (IMP)0;
|
||||
}
|
||||
|
||||
|
||||
/* Given a class and selector, return the selector's implementation. */
|
||||
inline
|
||||
IMP
|
||||
get_imp (Class class, SEL sel)
|
||||
{
|
||||
/* In a vanilla implementation we would first check if the dispatch
|
||||
table is installed. Here instead, to get more speed in the
|
||||
standard case (that the dispatch table is installed) we first try
|
||||
to get the imp using brute force. Only if that fails, we do what
|
||||
we should have been doing from the very beginning, that is, check
|
||||
if the dispatch table needs to be installed, install it if it's
|
||||
not installed, and retrieve the imp from the table if it's
|
||||
installed. */
|
||||
IMP res = sarray_get_imp (class->dtable, (size_t) sel->sel_id);
|
||||
if (res == 0)
|
||||
{
|
||||
/* This will block if calling +initialize from another thread. */
|
||||
void *dtable = dtable_for_class(class);
|
||||
/* Not a valid method */
|
||||
if (dtable == __objc_uninstalled_dtable)
|
||||
{
|
||||
/* The dispatch table needs to be installed. */
|
||||
objc_mutex_lock (__objc_runtime_mutex);
|
||||
|
||||
/* Double-checked locking pattern: Check
|
||||
__objc_uninstalled_dtable again in case another thread
|
||||
installed the dtable while we were waiting for the lock
|
||||
to be released. */
|
||||
if (dtable_for_class(class) == __objc_uninstalled_dtable)
|
||||
{
|
||||
__objc_install_dispatch_table_for_class (class);
|
||||
}
|
||||
|
||||
objc_mutex_unlock (__objc_runtime_mutex);
|
||||
/* Call ourselves with the installed dispatch table
|
||||
and get the real method */
|
||||
res = get_imp (class, sel);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The dispatch table has been installed. */
|
||||
|
||||
/* Get the method from the dispatch table (we try to get it
|
||||
again in case another thread has installed the dtable just
|
||||
after we invoked sarray_get_safe, but before we checked
|
||||
class->dtable == __objc_uninstalled_dtable).
|
||||
*/
|
||||
res = sarray_get_imp (dtable, (size_t) sel->sel_id);
|
||||
if (res == 0)
|
||||
{
|
||||
/* The dispatch table has been installed, and the method
|
||||
is not in the dispatch table. So the method just
|
||||
doesn't exist for the class. Return the forwarding
|
||||
implementation. */
|
||||
res = __objc_get_forward_imp ((id)class, sel);
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Query if an object can respond to a selector, returns YES if the
|
||||
object implements the selector otherwise NO. Does not check if the
|
||||
method can be forwarded. */
|
||||
inline
|
||||
BOOL
|
||||
__objc_responds_to (id object, SEL sel)
|
||||
{
|
||||
void *res;
|
||||
|
||||
/* Install dispatch table if need be */
|
||||
if (dtable_for_class(object->class_pointer) == __objc_uninstalled_dtable)
|
||||
{
|
||||
objc_mutex_lock (__objc_runtime_mutex);
|
||||
if (dtable_for_class(object->class_pointer) == __objc_uninstalled_dtable)
|
||||
{
|
||||
__objc_install_dispatch_table_for_class (object->class_pointer);
|
||||
}
|
||||
objc_mutex_unlock (__objc_runtime_mutex);
|
||||
}
|
||||
|
||||
/* Get the method from the dispatch table */
|
||||
res = sarray_get_safe (dtable_for_class(object->class_pointer),
|
||||
(size_t) sel->sel_id);
|
||||
return (res != 0);
|
||||
}
|
||||
|
||||
extern id (*objc_proxy_lookup)(id receiver, SEL op);
|
||||
/* This is the lookup function. All entries in the table are either a
|
||||
valid method *or* zero. If zero then either the dispatch table
|
||||
needs to be installed or it doesn't exist and forwarding is attempted. */
|
||||
inline
|
||||
IMP
|
||||
objc_msg_lookup (id receiver, SEL op)
|
||||
{
|
||||
IMP result;
|
||||
if (receiver)
|
||||
{
|
||||
result = sarray_get_imp (receiver->class_pointer->dtable,
|
||||
PTR_TO_IDX(op->sel_id));
|
||||
if (result == 0)
|
||||
{
|
||||
/** Get the dtable that we should be using for lookup. This will
|
||||
* block if we are in the middle of running +initialize in another
|
||||
* thread. */
|
||||
void *dtable = dtable_for_class(receiver->class_pointer);
|
||||
/* Not a valid method */
|
||||
if (dtable == __objc_uninstalled_dtable)
|
||||
{
|
||||
/* The dispatch table needs to be installed.
|
||||
This happens on the very first method call to the class. */
|
||||
__objc_init_install_dtable (receiver, op);
|
||||
|
||||
/* Get real method for this in newly installed dtable */
|
||||
result = get_imp (receiver->class_pointer, op);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The dispatch table has been installed. Check again
|
||||
if the method exists (just in case the dispatch table
|
||||
has been installed by another thread after we did the
|
||||
previous check that the method exists).
|
||||
*/
|
||||
result = sarray_get_imp (dtable, PTR_TO_IDX(op->sel_id));
|
||||
if (result == 0)
|
||||
{
|
||||
/* Try again after giving the code a chance to install new
|
||||
* methods. This lookup mechanism doesn't support forwarding
|
||||
* to other objects, so only call the lookup recursively if
|
||||
* the receiver is not changed.
|
||||
*/
|
||||
id newReceiver = objc_proxy_lookup (receiver, op);
|
||||
if (newReceiver == receiver)
|
||||
{
|
||||
return objc_msg_lookup(receiver, op);
|
||||
}
|
||||
/* If the method still just doesn't exist for the
|
||||
class, attempt to forward the method. */
|
||||
result = __objc_get_forward_imp (receiver, op);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else
|
||||
return (IMP)nil_method;
|
||||
}
|
||||
|
||||
IMP
|
||||
objc_msg_lookup_super (Super_t super, SEL sel)
|
||||
{
|
||||
if (super->self)
|
||||
return get_imp (super->class, sel);
|
||||
else
|
||||
return (IMP)nil_method;
|
||||
}
|
||||
|
||||
int method_get_sizeof_arguments (Method *);
|
||||
|
||||
/*
|
||||
* TODO: This never worked properly. Delete it after checking no one is
|
||||
* misguided enough to be using it.
|
||||
*/
|
||||
retval_t
|
||||
objc_msg_sendv (id object, SEL op, arglist_t arg_frame)
|
||||
{
|
||||
fprintf(stderr, "objc_msg_sendv() never worked correctly. Don't use it.\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
/* Send +initialize to class if not already done */
|
||||
static void
|
||||
__objc_send_initialize (Class class)
|
||||
{
|
||||
/* This *must* be a class object */
|
||||
assert (CLS_ISCLASS (class));
|
||||
assert (! CLS_ISMETA (class));
|
||||
|
||||
if (! CLS_ISINITIALIZED (class))
|
||||
{
|
||||
/* This is always called with the runtime lock, which guarantees
|
||||
* atomicity, but we also need to make sure that the initialize lock is
|
||||
* held so that we can create the premature dtable. */
|
||||
LOCK(&initialize_lock);
|
||||
if (! CLS_ISRESOLV (class))
|
||||
objc_resolve_class(class);
|
||||
|
||||
|
||||
/* Create the garbage collector type memory description */
|
||||
__objc_generate_gc_type_description (class);
|
||||
|
||||
if (class->super_class)
|
||||
__objc_send_initialize (class->super_class);
|
||||
// Superclass +initialize might possibly send a message to this class, in
|
||||
// which case this method would be called again. See NSObject and
|
||||
// NSAutoreleasePool +initialize interaction in GNUstep.
|
||||
if (CLS_ISINITIALIZED (class))
|
||||
{
|
||||
UNLOCK(&initialize_lock);
|
||||
return;
|
||||
}
|
||||
CLS_SETINITIALIZED (class);
|
||||
CLS_SETINITIALIZED (class->class_pointer);
|
||||
/* Create the dtable, but don't install it on the class quite yet. */
|
||||
|
||||
void *dtable = create_dtable_for_class(class);
|
||||
/* Taking a pointer to an on-stack object and storing it in a global is
|
||||
* usually a silly idea. It is safe here, because we invert this
|
||||
* transform before we return, and we use initialize_lock to make sure no
|
||||
* other threads have access to this pointer. */
|
||||
InitializingDtable buffer = { class, dtable, temporary_dtables };
|
||||
temporary_dtables = &buffer;
|
||||
|
||||
{
|
||||
SEL op = sel_register_name ("initialize");
|
||||
IMP imp = 0;
|
||||
MethodList_t method_list = class->class_pointer->methods;
|
||||
|
||||
while (method_list) {
|
||||
int i;
|
||||
Method_t method;
|
||||
|
||||
for (i = 0; i < method_list->method_count; i++) {
|
||||
method = &(method_list->method_list[i]);
|
||||
if (method->method_name
|
||||
&& method->method_name->sel_id == op->sel_id) {
|
||||
imp = method->method_imp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (imp)
|
||||
break;
|
||||
|
||||
method_list = method_list->method_next;
|
||||
|
||||
}
|
||||
if (imp)
|
||||
(*imp) ((id) class, op);
|
||||
|
||||
}
|
||||
class->dtable = dtable;
|
||||
/* Note: We don't free the cache entry; it was allocated on the stack. */
|
||||
if (temporary_dtables == &buffer)
|
||||
{
|
||||
temporary_dtables = temporary_dtables->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
InitializingDtable *prev = temporary_dtables;
|
||||
while (prev->next->class != class)
|
||||
{
|
||||
prev = prev->next;
|
||||
}
|
||||
prev->next = buffer.next;
|
||||
}
|
||||
UNLOCK(&initialize_lock);
|
||||
}
|
||||
}
|
||||
|
||||
/* This function is called by objc_msg_lookup when the
|
||||
dispatch table needs to be installed; thus it is called once
|
||||
for each class, namely when the very first message is sent to it. */
|
||||
static void
|
||||
__objc_init_install_dtable (id receiver, SEL op __attribute__ ((__unused__)))
|
||||
{
|
||||
objc_mutex_lock (__objc_runtime_mutex);
|
||||
|
||||
/* This may happen, if the programmer has taken the address of a
|
||||
method before the dtable was initialized... too bad for him! */
|
||||
if (dtable_for_class(receiver->class_pointer) != __objc_uninstalled_dtable)
|
||||
{
|
||||
objc_mutex_unlock (__objc_runtime_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (CLS_ISCLASS (receiver->class_pointer))
|
||||
{
|
||||
/* receiver is an ordinary object */
|
||||
assert (CLS_ISCLASS (receiver->class_pointer));
|
||||
|
||||
/* call +initialize -- this will in turn install the factory
|
||||
dispatch table if not already done :-) */
|
||||
__objc_send_initialize (receiver->class_pointer);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* receiver is a class object */
|
||||
assert (CLS_ISCLASS ((Class)receiver));
|
||||
assert (CLS_ISMETA (receiver->class_pointer));
|
||||
__objc_send_initialize ((Class)receiver);
|
||||
}
|
||||
objc_mutex_unlock (__objc_runtime_mutex);
|
||||
}
|
||||
|
||||
/* This function adds a method list to a class. This function is
|
||||
typically called by another function specific to the run-time. As
|
||||
such this function does not worry about thread safe issues.
|
||||
|
||||
This one is only called for categories. Class objects have their
|
||||
methods installed right away, and their selectors are made into
|
||||
SEL's by the function __objc_register_selectors_from_class. */
|
||||
void
|
||||
class_add_method_list (Class class, MethodList_t list)
|
||||
{
|
||||
/* Passing of a linked list is not allowed. Do multiple calls. */
|
||||
assert (! list->method_next);
|
||||
|
||||
__objc_register_selectors_from_list(list);
|
||||
|
||||
/* Add the methods to the class's method list. */
|
||||
list->method_next = class->methods;
|
||||
class->methods = list;
|
||||
|
||||
/* Update the dispatch table of class */
|
||||
__objc_update_dispatch_table_for_class (class);
|
||||
}
|
||||
|
||||
Method_t
|
||||
class_get_instance_method (Class class, SEL op)
|
||||
{
|
||||
return search_for_method_in_hierarchy (class, op);
|
||||
}
|
||||
|
||||
Method_t
|
||||
class_get_class_method (MetaClass class, SEL op)
|
||||
{
|
||||
return search_for_method_in_hierarchy (class, op);
|
||||
}
|
||||
|
||||
|
||||
/* Search for a method starting from the current class up its hierarchy.
|
||||
Return a pointer to the method's method structure if found. NULL
|
||||
otherwise. */
|
||||
|
||||
static Method_t
|
||||
search_for_method_in_hierarchy (Class cls, SEL sel)
|
||||
{
|
||||
Method_t method = NULL;
|
||||
Class class;
|
||||
|
||||
if (! sel_is_mapped (sel))
|
||||
return NULL;
|
||||
|
||||
/* Scan the method list of the class. If the method isn't found in the
|
||||
list then step to its super class. */
|
||||
for (class = cls; ((! method) && class); class = class->super_class)
|
||||
method = search_for_method_in_list (class->methods, sel);
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Given a linked list of method and a method's name. Search for the named
|
||||
method's method structure. Return a pointer to the method's method
|
||||
structure if found. NULL otherwise. */
|
||||
Method_t
|
||||
search_for_method_in_list (MethodList_t list, SEL op)
|
||||
{
|
||||
MethodList_t method_list = list;
|
||||
|
||||
if (! sel_is_mapped (op))
|
||||
return NULL;
|
||||
|
||||
/* If not found then we'll search the list. */
|
||||
while (method_list)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Search the method list. */
|
||||
for (i = 0; i < method_list->method_count; ++i)
|
||||
{
|
||||
Method_t method = &method_list->method_list[i];
|
||||
|
||||
if (method->method_name)
|
||||
if (method->method_name->sel_id == op->sel_id)
|
||||
return method;
|
||||
}
|
||||
|
||||
/* The method wasn't found. Follow the link to the next list of
|
||||
methods. */
|
||||
method_list = method_list->method_next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Returns the uninstalled dispatch table indicator.
|
||||
If a class' dispatch table points to __objc_uninstalled_dtable
|
||||
then that means it needs its dispatch table to be installed. */
|
||||
inline
|
||||
struct sarray *
|
||||
objc_get_uninstalled_dtable ()
|
||||
{
|
||||
return (void*)__objc_uninstalled_dtable;
|
||||
}
|
||||
|
||||
// This is an ugly hack to make sure that the compiler can do inlining into
|
||||
// sendmsg2.c from here. It should be removed and the two compiled separately
|
||||
// once we drop support for compilers that are too primitive to do cross-module
|
||||
// inlining.
|
||||
#include "sendmsg2.c"
|
||||
Loading…
Reference in New Issue