libs-base/Source/NSMethodSignature.m
David Chisnall 7a80adeb9b A huge blob of changes to make -base build with libobjc2, without using the old libobjc2 headers.
Most of these changes involve simply removing direct manipulation of any runtime structures and replacing them with runtime function calls.  For example class->name becomes class_getName(class) and so on.

libobjc2, like the Apple runtime, the NeXT runtime, and every version of the Objective-C spec, calls the class pointer in id isa.  A few files now have #define class_pointer isa at the top.  This line replaces class_pointer in the old GNU libobjc headers with isa so either class_pointer or isa can be used for accessing the class of an object.  Note: object_getClass() should be used in most cases because, in some future version of the runtime, this will skip things like lock classes and other hidden classes (e.g. KVO classes).

All of the old forwarding stuff has been removed.  Most of this stuff followed convoluted code paths that ended with an exception.  A few simply broke in exciting ways.  Hopefully no one has used them for the last ten years or so, but we can bring them back with some #ifndef stuff if they're really needed by anyone.

There is currently a bug in configure, which prevents dladdr() from being detected, so you need to manually tweak config.h to build - I have not fixed the fall-back code in objc-load.m to work with libobjc2, I just added a new version that uses the loader's functionality directly.  

Although -base now builds, it builds with a lot of warnings.  <string.h> is missing from a lot of files, so memcpy() and strlen() generate implicit function declaration warnings.  

Additionally, libobjc2 does still provide the sel_{get,register}_*() functions, but they're wrappers around the newer API ones.  These are deprecated and are not exposed in the headers.  Although they work, we should be replacing them with the libobjc2 versions as soon as possible.

This incorporates a patch by Eric.



git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@31265 72102866-910b-0410-8b05-ffd578937521
2010-09-09 15:06:09 +00:00

587 lines
12 KiB
Objective-C

/** Implementation of NSMethodSignature for GNUStep
Copyright (C) 1994, 1995, 1996, 1998 Free Software Foundation, Inc.
Written by: Andrew Kachites McCallum <mccallum@gnu.ai.mit.edu>
Date: August 1994
Rewritten: Richard Frith-Macdonald <richard@brainstorm.co.uk>
Date: August 1998
This file is part of the GNUstep Base Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02111 USA.
<title>NSMethodSignature class reference</title>
$Date$ $Revision$
*/
#import "common.h"
#include <objc/encoding.h>
#define EXPOSE_NSMethodSignature_IVARS 1
#import "Foundation/NSMethodSignature.h"
#import "Foundation/NSException.h"
#import "Foundation/NSCoder.h"
#import "GSInvocation.h"
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif
#ifdef HAVE_ALLOCA_H
#include <alloca.h>
#endif
/* The objc runtime library objc_skip_offset() is buggy on some compiler
* versions, so we use our own alternative implementation.
*/
static const char *
skip_offset(const char *ptr)
{
if (*ptr == '+' || *ptr == '-') ptr++;
while (isdigit(*ptr)) ptr++;
return ptr;
}
#define ROUND(V, A) \
({ typeof(V) __v=(V); typeof(A) __a=(A); \
__a*((__v+__a-1)/__a); })
/* Step through method encoding information extracting details.
* If outTypes is non-nul then we copy the qualified type into
* the buffer as a nul terminated string and use the values in
* this buffer for the qtype and type in info, rather than pointers
* to positions in typePtr
*/
static const char *
next_arg(const char *typePtr, NSArgumentInfo *info, char *outTypes)
{
NSArgumentInfo local;
BOOL flag;
BOOL negative = NO;
if (info == 0)
{
info = &local;
}
info->qtype = typePtr;
/*
* Skip past any type qualifiers - if the caller wants them, return them.
*/
flag = YES;
info->qual = 0;
while (flag)
{
switch (*typePtr)
{
case _C_CONST: info->qual |= _F_CONST; break;
case _C_IN: info->qual |= _F_IN; break;
case _C_INOUT: info->qual |= _F_INOUT; break;
case _C_OUT: info->qual |= _F_OUT; break;
case _C_BYCOPY: info->qual |= _F_BYCOPY; break;
#ifdef _C_BYREF
case _C_BYREF: info->qual |= _F_BYREF; break;
#endif
case _C_ONEWAY: info->qual |= _F_ONEWAY; break;
#ifdef _C_GCINVISIBLE
case _C_GCINVISIBLE: info->qual |= _F_GCINVISIBLE; break;
#endif
default: flag = NO;
}
if (flag)
{
typePtr++;
}
}
info->type = typePtr;
/*
* Scan for size and alignment information.
*/
switch (*typePtr++)
{
case _C_ID:
info->size = sizeof(id);
info->align = __alignof__(id);
break;
case _C_CLASS:
info->size = sizeof(Class);
info->align = __alignof__(Class);
break;
case _C_SEL:
info->size = sizeof(SEL);
info->align = __alignof__(SEL);
break;
case _C_CHR:
info->size = sizeof(char);
info->align = __alignof__(char);
break;
case _C_UCHR:
info->size = sizeof(unsigned char);
info->align = __alignof__(unsigned char);
break;
case _C_SHT:
info->size = sizeof(short);
info->align = __alignof__(short);
break;
case _C_USHT:
info->size = sizeof(unsigned short);
info->align = __alignof__(unsigned short);
break;
case _C_INT:
info->size = sizeof(int);
info->align = __alignof__(int);
break;
case _C_UINT:
info->size = sizeof(unsigned int);
info->align = __alignof__(unsigned int);
break;
case _C_LNG:
info->size = sizeof(long);
info->align = __alignof__(long);
break;
case _C_ULNG:
info->size = sizeof(unsigned long);
info->align = __alignof__(unsigned long);
break;
case _C_LNG_LNG:
info->size = sizeof(long long);
info->align = __alignof__(long long);
break;
case _C_ULNG_LNG:
info->size = sizeof(unsigned long long);
info->align = __alignof__(unsigned long long);
break;
case _C_FLT:
info->size = sizeof(float);
info->align = __alignof__(float);
break;
case _C_DBL:
info->size = sizeof(double);
info->align = __alignof__(double);
break;
case _C_PTR:
info->size = sizeof(char*);
info->align = __alignof__(char*);
if (*typePtr == '?')
{
typePtr++;
}
else
{
typePtr = objc_skip_typespec(typePtr);
}
break;
case _C_ATOM:
case _C_CHARPTR:
info->size = sizeof(char*);
info->align = __alignof__(char*);
break;
case _C_ARY_B:
{
int length = atoi(typePtr);
while (isdigit(*typePtr))
{
typePtr++;
}
typePtr = next_arg(typePtr, &local, 0);
info->size = length * ROUND(local.size, local.align);
info->align = local.align;
typePtr++; /* Skip end-of-array */
}
break;
case _C_STRUCT_B:
{
unsigned int acc_size = 0;
unsigned int def_align = objc_alignof_type(typePtr-1);
unsigned int acc_align = def_align;
const char *ptr = typePtr;
/*
* Skip "<name>=" stuff.
*/
while (*ptr != _C_STRUCT_E && *ptr != '=') ptr++;
if (*ptr == '=') typePtr = ptr;
typePtr++;
/*
* Base structure alignment on first element.
*/
if (*typePtr != _C_STRUCT_E)
{
typePtr = next_arg(typePtr, &local, 0);
if (typePtr == 0)
{
return 0; /* error */
}
acc_size = ROUND(acc_size, local.align);
acc_size += local.size;
acc_align = MAX(local.align, def_align);
}
/*
* Continue accumulating structure size
* and adjust alignment if necessary
*/
while (*typePtr != _C_STRUCT_E)
{
typePtr = next_arg(typePtr, &local, 0);
if (typePtr == 0)
{
return 0; /* error */
}
acc_size = ROUND(acc_size, local.align);
acc_size += local.size;
acc_align = MAX(local.align, acc_align);
}
/*
* Size must be a multiple of alignment
*/
if (acc_size % acc_align != 0)
{
acc_size += acc_align - acc_size % acc_align;
}
info->size = acc_size;
info->align = acc_align;
typePtr++; /* Skip end-of-struct */
}
break;
case _C_UNION_B:
{
unsigned int max_size = 0;
unsigned int max_align = 0;
/*
* Skip "<name>=" stuff.
*/
while (*typePtr != _C_UNION_E)
{
if (*typePtr++ == '=')
{
break;
}
}
while (*typePtr != _C_UNION_E)
{
typePtr = next_arg(typePtr, &local, 0);
if (typePtr == 0)
{
return 0; /* error */
}
max_size = MAX(max_size, local.size);
max_align = MAX(max_align, local.align);
}
info->size = max_size;
info->align = max_align;
typePtr++; /* Skip end-of-union */
}
break;
case _C_VOID:
info->size = 0;
info->align = __alignof__(char*);
break;
default:
return 0;
}
if (typePtr == 0)
{ /* Error condition. */
return 0;
}
/* Copy the type information into the buffer if provided.
*/
if (outTypes != 0)
{
unsigned len = typePtr - info->qtype;
strncpy(outTypes, info->qtype, len);
outTypes[len] = '\0';
info->qtype = outTypes;
info->type = objc_skip_type_qualifiers (outTypes);
}
/*
* May tell the caller if the item is stored in a register.
*/
if (*typePtr == '+')
{
typePtr++;
info->isReg = YES;
}
else
{
info->isReg = NO;
}
/*
* Cope with negative offsets.
*/
if (*typePtr == '-')
{
typePtr++;
negative = YES;
}
/*
* May tell the caller what the stack/register offset is for
* this argument.
*/
info->offset = 0;
while (isdigit(*typePtr))
{
info->offset = info->offset * 10 + (*typePtr++ - '0');
}
if (negative == YES)
{
info->offset = -info->offset;
}
return typePtr;
}
@implementation NSMethodSignature
- (id) _initWithObjCTypes: (const char*)t
{
const char *p = t;
if (t == 0 || *t == '\0')
{
DESTROY(self);
}
else
{
const char *q;
char *args;
char *ret;
/* In case we have been given a method encoding string without offsets,
* we attempt to generate the frame size and offsets in a new copy of
* the types string.
*/
ret = alloca((strlen(t)+1)*16);
/* Copy the return type (including qualifiers) with ehough room
* after it to store the frame size.
*/
p = t;
p = objc_skip_typespec (p);
strncpy(ret, t, p - t);
ret[p - t] = '\0';
args = ret + (p - t) + 10;
*args = '\0';
/* Skip to the first arg type, taking note of where the qualifiers start.
* Assume that casting _argFrameLength to int will not lose information.
*/
p = skip_offset (p);
q = p;
p = objc_skip_type_qualifiers (p);
while (p && *p)
{
int size;
_numArgs++;
size = objc_promoted_size (p);
p = objc_skip_typespec (p);
strncat(args, q, p - q);
sprintf(args + strlen(args), "%d", (int)_argFrameLength);
_argFrameLength += size;
p = skip_offset (p);
q = p;
p = objc_skip_type_qualifiers (p);
}
sprintf(ret + strlen(ret), "%d", (int)_argFrameLength);
_methodTypes = NSZoneMalloc(NSDefaultMallocZone(),
strlen(args) + strlen(ret) + 1);
strcpy((char*)_methodTypes, ret);
strcat((char*)_methodTypes, args);
}
return self;
}
+ (NSMethodSignature*) signatureWithObjCTypes: (const char*)t
{
return AUTORELEASE([[[self class] alloc] _initWithObjCTypes: t]);
}
- (NSArgumentInfo) argumentInfoAtIndex: (NSUInteger)index
{
if (index >= _numArgs)
{
[NSException raise: NSInvalidArgumentException
format: @"Index too high."];
}
if (_inf == 0)
{
[self methodInfo];
}
return _inf[index+1];
}
- (NSUInteger) frameLength
{
return _argFrameLength;
}
- (const char*) getArgumentTypeAtIndex: (NSUInteger)index
{
if (index >= _numArgs)
{
[NSException raise: NSInvalidArgumentException
format: @"Index too high."];
}
if (_inf == 0)
{
[self methodInfo];
}
return _inf[index+1].qtype;
}
- (BOOL) isOneway
{
if (_inf == 0)
{
[self methodInfo];
}
return (_inf[0].qual & _F_ONEWAY) ? YES : NO;
}
- (NSUInteger) methodReturnLength
{
if (_inf == 0)
{
[self methodInfo];
}
return _inf[0].size;
}
- (const char*) methodReturnType
{
if (_inf == 0)
{
[self methodInfo];
}
return _inf[0].qtype;
}
- (NSUInteger) numberOfArguments
{
return _numArgs;
}
- (void) dealloc
{
if (_methodTypes)
NSZoneFree(NSDefaultMallocZone(), (void*)_methodTypes);
if (_inf)
NSZoneFree(NSDefaultMallocZone(), (void*)_inf);
[super dealloc];
}
- (BOOL) isEqual: (id)other
{
BOOL isEqual = YES;
if (other == nil)
{
return NO;
}
if (((NSMethodSignature *)other)->isa != isa)
{
return NO;
}
isEqual = ([self numberOfArguments] == [other numberOfArguments]
&& [self frameLength] == [other frameLength]
&& *[self methodReturnType] == *[other methodReturnType]
&& [self isOneway] == [other isOneway]);
if (isEqual == NO)
{
return NO;
}
else
{
int i, n;
n = [self numberOfArguments];
for (i = 0; i < n; i++)
{
if ((*[self getArgumentTypeAtIndex:i]
== *[other getArgumentTypeAtIndex:i]) == NO)
{
return NO;
}
}
}
return isEqual;
}
@end
@implementation NSMethodSignature(GNUstep)
- (NSArgumentInfo*) methodInfo
{
if (_inf == 0)
{
const char *types = _methodTypes;
char *outTypes;
unsigned int i;
/* Allocate space enough for an NSArgumentInfo structure for each
* argument (including the return type), and enough space to hold
* the type information for each argument as a nul terminated
* string.
*/
outTypes = NSZoneMalloc(NSDefaultMallocZone(),
sizeof(NSArgumentInfo)*(_numArgs+1) + strlen(types)*2);
_info = (void*)outTypes;
outTypes = outTypes + sizeof(NSArgumentInfo)*(_numArgs+1);
/* Fill in the full argment information for each arg.
*/
for (i = 0; i <= _numArgs; i++)
{
types = next_arg(types, &_inf[i], outTypes);
outTypes += strlen(outTypes) + 1;
}
}
return _inf;
}
- (const char*) methodType
{
return _methodTypes;
}
@end