Simplify the KVC getter code and make it return structs boxed in NSValues

(Apple-compatible).  This is currently slow, because we go via NSInvocation.
We could probably make it faster, but I am not inclined to optimise for
something that I sincerely hope is a very unusual use case.

Richard: Please check I haven't done something silly here...



git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@31411 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
David Chisnall 2010-09-24 19:20:16 +00:00
parent 0364f2e365
commit ec239ebfaf
4 changed files with 102 additions and 373 deletions

View file

@ -39,7 +39,7 @@
#import "Foundation/NSEnumerator.h"
#import "Foundation/NSException.h"
#import "Foundation/NSLock.h"
#import "Foundation/NSMethodSignature.h"
#import "Foundation/NSInvocation.h"
#import "Foundation/NSNull.h"
#import "Foundation/NSSet.h"
#import "Foundation/NSValue.h"
@ -928,6 +928,11 @@ GSObjCAddClassOverride(Class receiver, Class override)
#import "Foundation/NSKeyValueCoding.h"
#endif
/**
* Casts the accessor method IMP for sel to the correct type and calls it.
*/
#define CALL_ACCESSOR(type, self, sel)\
(((type (*)(id, SEL))[self methodForSelector: sel])(self, sel))
/**
* This is used internally by the key-value coding methods, to get a
@ -943,9 +948,13 @@ id
GSObjCGetVal(NSObject *self, const char *key, SEL sel,
const char *type, unsigned size, int offset)
{
NSMethodSignature *sig = nil;
if (sel != 0)
{
NSMethodSignature *sig = [self methodSignatureForSelector: sel];
// FIXME: This can be slow, because it has to iterate over protocols for
// DO calling conventions, which we don't care about here. With
// libobjc2, we can skip it and just do a fast slot lookup.
sig = [self methodSignatureForSelector: sel];
if ([sig numberOfArguments] != 2)
{
@ -956,362 +965,74 @@ GSObjCGetVal(NSObject *self, const char *key, SEL sel,
}
if (type == NULL)
{
// FIXME: This is stupid - we have an NSString in the caller, then we
// extract the buffer, then we create a new NSString.
return [self valueForUndefinedKey: [NSString stringWithUTF8String: key]];
}
else
if (0 == sel)
{
id val = nil;
void *addr = ((char *)self + offset);
switch (type[0])
{
// No boxing required for things that are already objects
case '@': case '#':
return *(id*)addr;
// Types not allowed for KVC:
case '(': case '^': case '[': case '*': case '?':
return [self valueForUndefinedKey: [NSString stringWithUTF8String: key]];
// Anything else, we ask NSValue to box for us
default:
return [NSValue valueWithBytes: addr objCType: type];
}
}
// Box a primitive type in an NSNumber and return it
switch (*type)
{
case _C_ID:
case _C_CLASS:
{
id v;
if (sel == 0)
{
v = *(id *)((char *)self + offset);
}
else
{
id (*imp)(id, SEL) =
(id (*)(id, SEL))[self methodForSelector: sel];
v = (*imp)(self, sel);
}
val = v;
}
break;
case _C_CHR:
{
signed char v;
if (sel == 0)
{
v = *(char *)((char *)self + offset);
}
else
{
signed char (*imp)(id, SEL) =
(signed char (*)(id, SEL))[self methodForSelector: sel];
v = (*imp)(self, sel);
}
val = [NSNumber numberWithChar: v];
}
break;
case _C_UCHR:
{
unsigned char v;
if (sel == 0)
{
v = *(unsigned char *)((char *)self + offset);
}
else
{
unsigned char (*imp)(id, SEL) =
(unsigned char (*)(id, SEL))[self methodForSelector:
sel];
v = (*imp)(self, sel);
}
val = [NSNumber numberWithUnsignedChar: v];
}
break;
case _C_SHT:
{
short v;
if (sel == 0)
{
v = *(short *)((char *)self + offset);
}
else
{
short (*imp)(id, SEL) =
(short (*)(id, SEL))[self methodForSelector: sel];
v = (*imp)(self, sel);
}
val = [NSNumber numberWithShort: v];
}
break;
case _C_USHT:
{
unsigned short v;
if (sel == 0)
{
v = *(unsigned short *)((char *)self + offset);
}
else
{
unsigned short (*imp)(id, SEL) =
(unsigned short (*)(id, SEL))[self methodForSelector:
sel];
v = (*imp)(self, sel);
}
val = [NSNumber numberWithUnsignedShort: v];
}
break;
case _C_INT:
{
int v;
if (sel == 0)
{
v = *(int *)((char *)self + offset);
}
else
{
int (*imp)(id, SEL) =
(int (*)(id, SEL))[self methodForSelector: sel];
v = (*imp)(self, sel);
}
val = [NSNumber numberWithInt: v];
}
break;
case _C_UINT:
{
unsigned int v;
if (sel == 0)
{
v = *(unsigned int *)((char *)self + offset);
}
else
{
unsigned int (*imp)(id, SEL) =
(unsigned int (*)(id, SEL))[self methodForSelector:
sel];
v = (*imp)(self, sel);
}
val = [NSNumber numberWithUnsignedInt: v];
}
break;
case _C_LNG:
{
long v;
if (sel == 0)
{
v = *(long *)((char *)self + offset);
}
else
{
long (*imp)(id, SEL) =
(long (*)(id, SEL))[self methodForSelector: sel];
v = (*imp)(self, sel);
}
val = [NSNumber numberWithLong: v];
}
break;
case _C_ULNG:
{
unsigned long v;
if (sel == 0)
{
v = *(unsigned long *)((char *)self + offset);
}
else
{
unsigned long (*imp)(id, SEL) =
(unsigned long (*)(id, SEL))[self methodForSelector:
sel];
v = (*imp)(self, sel);
}
val = [NSNumber numberWithUnsignedLong: v];
}
break;
#ifdef _C_LNG_LNG
case _C_LNG_LNG:
{
long long v;
if (sel == 0)
{
v = *(long long *)((char *)self + offset);
}
else
{
long long (*imp)(id, SEL) =
(long long (*)(id, SEL))[self methodForSelector: sel];
v = (*imp)(self, sel);
}
val = [NSNumber numberWithLongLong: v];
}
break;
#endif
#ifdef _C_ULNG_LNG
case _C_ULNG_LNG:
{
unsigned long long v;
if (sel == 0)
{
v = *(unsigned long long *)((char *)self + offset);
}
else
{
unsigned long long (*imp)(id, SEL) =
(unsigned long long (*)(id, SEL))[self
methodForSelector: sel];
v = (*imp)(self, sel);
}
val = [NSNumber numberWithUnsignedLongLong: v];
}
break;
#endif
case _C_FLT:
{
float v;
if (sel == 0)
{
v = *(float *)((char *)self + offset);
}
else
{
float (*imp)(id, SEL) =
(float (*)(id, SEL))[self methodForSelector: sel];
v = (*imp)(self, sel);
}
val = [NSNumber numberWithFloat: v];
}
break;
case _C_DBL:
{
double v;
if (sel == 0)
{
v = *(double *)((char *)self + offset);
}
else
{
double (*imp)(id, SEL) =
(double (*)(id, SEL))[self methodForSelector: sel];
v = (*imp)(self, sel);
}
val = [NSNumber numberWithDouble: v];
}
break;
return [self methodForSelector: sel](self, sel);
#define INTEGER_MACRO(encoding, type, name, capitalizedName) \
case encoding: \
return [NSNumber numberWith ## capitalizedName: CALL_ACCESSOR(type, self, sel)];
#define NO_NSNUMBER 1
#include "../GSNumberTypes.h"
case _C_VOID:
{
void (*imp)(id, SEL) =
(void (*)(id, SEL))[self methodForSelector: sel];
(*imp)(self, sel);
}
val = nil;
break;
// Calling an accessor method that doesn't return anything is crazy,
// but it's Apple-compatible crazy.
CALL_ACCESSOR(void, self, sel);
return nil;
case _C_STRUCT_B:
if (strcmp(@encode(NSPoint), type) == 0)
{
NSPoint v;
if (sel == 0)
{
memcpy((char*)&v, ((char *)self + offset), sizeof(v));
}
else
{
NSPoint (*imp)(id, SEL) =
(NSPoint (*)(id, SEL))[self methodForSelector: sel];
v = (*imp)(self, sel);
}
val = [NSValue valueWithPoint: v];
return [NSValue valueWithPoint: CALL_ACCESSOR(NSPoint, self, sel)];
}
else if (strcmp(@encode(NSRange), type) == 0)
{
NSRange v;
if (sel == 0)
{
memcpy((char*)&v, ((char *)self + offset), sizeof(v));
}
else
{
NSRange (*imp)(id, SEL) =
(NSRange (*)(id, SEL))[self methodForSelector: sel];
v = (*imp)(self, sel);
}
val = [NSValue valueWithRange: v];
return [NSValue valueWithRange: CALL_ACCESSOR(NSRange, self, sel)];
}
else if (strcmp(@encode(NSRect), type) == 0)
{
NSRect v;
if (sel == 0)
{
memcpy((char*)&v, ((char *)self + offset), sizeof(v));
}
else
{
NSRect (*imp)(id, SEL) =
(NSRect (*)(id, SEL))[self methodForSelector: sel];
v = (*imp)(self, sel);
}
val = [NSValue valueWithRect: v];
return [NSValue valueWithRect: CALL_ACCESSOR(NSRect, self, sel)];
}
else if (strcmp(@encode(NSSize), type) == 0)
{
NSSize v;
if (sel == 0)
{
memcpy((char*)&v, ((char *)self + offset), sizeof(v));
return [NSValue valueWithSize: CALL_ACCESSOR(NSSize, self, sel)];
}
else
{
NSSize (*imp)(id, SEL) =
(NSSize (*)(id, SEL))[self methodForSelector: sel];
v = (*imp)(self, sel);
}
val = [NSValue valueWithSize: v];
}
else
NSInvocation *inv = [NSInvocation invocationWithMethodSignature: sig];
[inv invokeWithTarget: self];
{
val = [self valueForUndefinedKey:
[NSString stringWithUTF8String: key]];
size_t retSize = [sig methodReturnLength];
char ret[retSize];
[inv getReturnValue: ret];
return [NSValue valueWithBytes: ret objCType: type];
}
}
break;
default:
val = [self valueForUndefinedKey:
[NSString stringWithUTF8String: key]];
}
return val;
return [self valueForUndefinedKey: [NSString stringWithUTF8String: key]];
}
}

View file

@ -7,20 +7,28 @@
*
*/
#ifndef INTEGER_MACRO
#error Define INTEGER_MACRO(type, name, capitalizedName) before including GSNumberTypes.h
#error Define INTEGER_MACRO(encoding, type, name, capitalizedName) before including GSNumberTypes.h
#endif
INTEGER_MACRO(double, double, Double)
INTEGER_MACRO(float, float, Float)
INTEGER_MACRO(signed char, char, Char)
INTEGER_MACRO(int, int, Int)
INTEGER_MACRO(short, short, Short)
INTEGER_MACRO(long, long, Long)
INTEGER_MACRO(NSInteger, integer, Integer)
INTEGER_MACRO(NSUInteger, unsignedInteger, UnsignedInteger)
INTEGER_MACRO(long long, longLong, LongLong)
INTEGER_MACRO(unsigned char, unsignedChar, UnsignedChar)
INTEGER_MACRO(unsigned short, unsignedShort, UnsignedShort)
INTEGER_MACRO(unsigned int, unsignedInt, UnsignedInt)
INTEGER_MACRO(unsigned long, unsignedLong, UnsignedLong)
INTEGER_MACRO(unsigned long long, unsignedLongLong, UnsignedLongLong)
INTEGER_MACRO('d', double, double, Double)
INTEGER_MACRO('f', float, float, Float)
INTEGER_MACRO('c', signed char, char, Char)
INTEGER_MACRO('i', int, int, Int)
INTEGER_MACRO('s', short, short, Short)
INTEGER_MACRO('l', long, long, Long)
#ifndef NO_NSNUMBER
# if SIZEOF_VOIDP == 4
INTEGER_MACRO('i', NSInteger, integer, Integer)
INTEGER_MACRO('I', NSUInteger, unsignedInteger, UnsignedInteger)
# undef NO_NSNUMBER
# else
INTEGER_MACRO('q', NSInteger, integer, Integer)
INTEGER_MACRO('Q', NSUInteger, unsignedInteger, UnsignedInteger)
# endif
#endif
INTEGER_MACRO('q', long long, longLong, LongLong)
INTEGER_MACRO('C', unsigned char, unsignedChar, UnsignedChar)
INTEGER_MACRO('S', unsigned short, unsignedShort, UnsignedShort)
INTEGER_MACRO('I', unsigned int, unsignedInt, UnsignedInt)
INTEGER_MACRO('L', unsigned long, unsignedLong, UnsignedLong)
INTEGER_MACRO('Q', unsigned long long, unsignedLongLong, UnsignedLongLong)
#undef INTEGER_MACRO

View file

@ -411,7 +411,7 @@ static NSBoolNumber *boolN; // Boolean NO (integer 0)
return 0; // Not reached
}
#define INTEGER_MACRO(type, ignored, name) \
#define INTEGER_MACRO(encoding,type, ignored, name) \
- (id) initWith ## name: (type)aValue \
{\
DESTROY(self);\
@ -735,7 +735,7 @@ if (aValue >= -1 && aValue <= 12)\
* exception here matches OS X behaviour, although they throw an invalid
* argument exception.
*/
#define INTEGER_MACRO(type, name, ignored) \
#define INTEGER_MACRO(encoding, type, name, ignored) \
- (type) name ## Value\
{\
[self subclassResponsibility: _cmd];\

View file

@ -1,4 +1,4 @@
#define INTEGER_MACRO(type, name, ignored) \
#define INTEGER_MACRO(encoding, type, name, ignored) \
- (type) name ## Value\
{\
return (type)VALUE;\