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