From ec239ebfaf40b91c7dbd05ca45015f960edb41e4 Mon Sep 17 00:00:00 2001 From: David Chisnall Date: Fri, 24 Sep 2010 19:20:16 +0000 Subject: [PATCH] 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 --- Source/Additions/GSObjCRuntime.m | 431 ++++++------------------------- Source/GSNumberTypes.h | 38 +-- Source/NSNumber.m | 4 +- Source/NSNumberMethods.h | 2 +- 4 files changed, 102 insertions(+), 373 deletions(-) diff --git a/Source/Additions/GSObjCRuntime.m b/Source/Additions/GSObjCRuntime.m index aa715abdf..6023ba080 100644 --- a/Source/Additions/GSObjCRuntime.m +++ b/Source/Additions/GSObjCRuntime.m @@ -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,363 +965,75 @@ 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; - - 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; - - case _C_VOID: - { - void (*imp)(id, SEL) = - (void (*)(id, SEL))[self methodForSelector: sel]; - - (*imp)(self, sel); - } - val = nil; - break; - - 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]; - } - 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]; - } - 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]; - } - else if (strcmp(@encode(NSSize), type) == 0) - { - NSSize v; - - if (sel == 0) - { - memcpy((char*)&v, ((char *)self + offset), sizeof(v)); - } - else - { - NSSize (*imp)(id, SEL) = - (NSSize (*)(id, SEL))[self methodForSelector: sel]; - - v = (*imp)(self, sel); - } - val = [NSValue valueWithSize: v]; - } - else - { - val = [self valueForUndefinedKey: - [NSString stringWithUTF8String: key]]; - } - break; - - default: - val = [self valueForUndefinedKey: - [NSString stringWithUTF8String: key]]; - } - return val; + 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: + 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: + // 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) + { + return [NSValue valueWithPoint: CALL_ACCESSOR(NSPoint, self, sel)]; + } + else if (strcmp(@encode(NSRange), type) == 0) + { + return [NSValue valueWithRange: CALL_ACCESSOR(NSRange, self, sel)]; + } + else if (strcmp(@encode(NSRect), type) == 0) + { + return [NSValue valueWithRect: CALL_ACCESSOR(NSRect, self, sel)]; + } + else if (strcmp(@encode(NSSize), type) == 0) + { + return [NSValue valueWithSize: CALL_ACCESSOR(NSSize, self, sel)]; + } + else + { + NSInvocation *inv = [NSInvocation invocationWithMethodSignature: sig]; + [inv invokeWithTarget: self]; + { + size_t retSize = [sig methodReturnLength]; + char ret[retSize]; + [inv getReturnValue: ret]; + return [NSValue valueWithBytes: ret objCType: type]; + } + } + default: + return [self valueForUndefinedKey: [NSString stringWithUTF8String: key]]; + } } /** diff --git a/Source/GSNumberTypes.h b/Source/GSNumberTypes.h index a2b270019..ef733c2e0 100644 --- a/Source/GSNumberTypes.h +++ b/Source/GSNumberTypes.h @@ -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 diff --git a/Source/NSNumber.m b/Source/NSNumber.m index af8ee7e69..de9195716 100644 --- a/Source/NSNumber.m +++ b/Source/NSNumber.m @@ -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];\ diff --git a/Source/NSNumberMethods.h b/Source/NSNumberMethods.h index 325175d17..e26badf19 100644 --- a/Source/NSNumberMethods.h +++ b/Source/NSNumberMethods.h @@ -1,4 +1,4 @@ -#define INTEGER_MACRO(type, name, ignored) \ +#define INTEGER_MACRO(encoding, type, name, ignored) \ - (type) name ## Value\ {\ return (type)VALUE;\