Index: src/OFDate.h ================================================================== --- src/OFDate.h +++ src/OFDate.h @@ -27,11 +27,13 @@ /*! * @class OFDate OFDate.h ObjFW/OFDate.h * * @brief A class for storing, accessing and comparing dates. */ +#ifndef OF_DATE_M OF_SUBCLASSING_RESTRICTED +#endif @interface OFDate: OFObject { of_time_interval_t _seconds; } @@ -215,11 +217,12 @@ * time since 1970-01-01T00:00:00Z. * * @param seconds The seconds since 1970-01-01T00:00:00Z * @return An initialized OFDate with the specified date and time */ -- (instancetype)initWithTimeIntervalSince1970: (of_time_interval_t)seconds; +- (instancetype)initWithTimeIntervalSince1970: (of_time_interval_t)seconds + OF_DESIGNATED_INITIALIZER; /*! * @brief Initializes an already allocated OFDate with the specified date and * time since now. * Index: src/OFDate.m ================================================================== --- src/OFDate.m +++ src/OFDate.m @@ -12,10 +12,12 @@ * Alternatively, it may be distributed under the terms of the GNU General * Public License, either version 2 or 3, which can be found in the file * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this * file. */ + +#define OF_DATE_M #include "config.h" #include #include @@ -35,18 +37,77 @@ #import "OFXMLElement.h" #import "OFInitializationFailedException.h" #import "OFInvalidArgumentException.h" #import "OFInvalidFormatException.h" +#import "OFOutOfMemoryException.h" #import "OFOutOfRangeException.h" #import "of_strptime.h" #ifdef OF_AMIGAOS_M68K /* amiga-gcc does not have trunc() */ # define trunc(x) ((int64_t)(x)) #endif + +@interface OFDate () ++ (instancetype)of_alloc; +@end + +@interface OFDateSingleton: OFDate +@end + +@interface OFDatePlaceholder: OFDateSingleton +@end + +#if defined(OF_OBJFW_RUNTIME) && UINTPTR_MAX == UINT64_MAX +@interface OFTaggedPointerDate: OFDateSingleton +@end +#endif + +static struct { + Class isa; +} placeholder; + +static OFDateSingleton *zeroDate, *distantFuture, *distantPast; +#if defined(OF_OBJFW_RUNTIME) && UINTPTR_MAX == UINT64_MAX +static int dateTag; +#endif + +static void +initZeroDate(void) +{ + zeroDate = [[OFDateSingleton alloc] initWithTimeIntervalSince1970: 0]; +} + +static void +initDistantFuture(void) +{ + distantFuture = [[OFDateSingleton alloc] + initWithTimeIntervalSince1970: 64060588800.0]; +} + +static void +initDistantPast(void) +{ + distantPast = [[OFDateSingleton alloc] + initWithTimeIntervalSince1970: -62167219200.0]; +} + +static of_time_interval_t +now(void) +{ + struct timeval tv; + of_time_interval_t seconds; + + OF_ENSURE(gettimeofday(&tv, NULL) == 0); + + seconds = tv.tv_sec; + seconds += (of_time_interval_t)tv.tv_usec / 1000000; + + return seconds; +} #if (!defined(HAVE_GMTIME_R) || !defined(HAVE_LOCALTIME_R)) && \ defined(OF_HAVE_THREADS) static OFMutex *mutex; #endif @@ -55,38 +116,41 @@ static __time64_t (*func__mktime64)(struct tm *); #endif #ifdef HAVE_GMTIME_R # define GMTIME_RET(field) \ - time_t seconds = (time_t)_seconds; \ + of_time_interval_t timeInterval = self.timeIntervalSince1970; \ + time_t seconds = (time_t)timeInterval; \ struct tm tm; \ \ - if (seconds != trunc(_seconds)) \ + if (seconds != trunc(timeInterval)) \ @throw [OFOutOfRangeException exception]; \ \ if (gmtime_r(&seconds, &tm) == NULL) \ @throw [OFOutOfRangeException exception]; \ \ return tm.field; # define LOCALTIME_RET(field) \ - time_t seconds = (time_t)_seconds; \ + of_time_interval_t timeInterval = self.timeIntervalSince1970; \ + time_t seconds = (time_t)timeInterval; \ struct tm tm; \ \ - if (seconds != trunc(_seconds)) \ + if (seconds != trunc(timeInterval)) \ @throw [OFOutOfRangeException exception]; \ \ if (localtime_r(&seconds, &tm) == NULL) \ @throw [OFOutOfRangeException exception]; \ \ return tm.field; #else # ifdef OF_HAVE_THREADS # define GMTIME_RET(field) \ - time_t seconds = (time_t)_seconds; \ + of_time_interval_t timeInterval = self.timeIntervalSince1970; \ + time_t seconds = (time_t)timeInterval; \ struct tm *tm; \ \ - if (seconds != trunc(_seconds)) \ + if (seconds != trunc(timeInterval)) \ @throw [OFOutOfRangeException exception]; \ \ [mutex lock]; \ \ @try { \ @@ -96,14 +160,15 @@ return tm->field; \ } @finally { \ [mutex unlock]; \ } # define LOCALTIME_RET(field) \ - time_t seconds = (time_t)_seconds; \ + of_time_interval_t timeInterval = self.timeIntervalSince1970; \ + time_t seconds = (time_t)timeInterval; \ struct tm *tm; \ \ - if (seconds != trunc(_seconds)) \ + if (seconds != trunc(timeInterval)) \ @throw [OFOutOfRangeException exception]; \ \ [mutex lock]; \ \ @try { \ @@ -114,25 +179,27 @@ } @finally { \ [mutex unlock]; \ } # else # define GMTIME_RET(field) \ - time_t seconds = (time_t)_seconds; \ + of_time_interval_t timeInterval = self.timeIntervalSince1970; \ + time_t seconds = (time_t)timeInterval; \ struct tm *tm; \ \ - if (seconds != trunc(_seconds)) \ + if (seconds != trunc(timeInterval)) \ @throw [OFOutOfRangeException exception]; \ \ if ((tm = gmtime(&seconds)) == NULL) \ @throw [OFOutOfRangeException exception]; \ \ return tm->field; # define LOCALTIME_RET(field) \ - time_t seconds = (time_t)_seconds; \ + of_time_interval_t timeInterval = self.timeIntervalSince1970; \ + time_t seconds = (time_t)timeInterval; \ struct tm *tm; \ \ - if (seconds != trunc(_seconds)) \ + if (seconds != trunc(timeInterval)) \ @throw [OFOutOfRangeException exception]; \ \ if ((tm = localtime(&seconds)) == NULL) \ @throw [OFOutOfRangeException exception]; \ \ @@ -186,10 +253,85 @@ /* Time zone */ seconds += -(double)*tz * 60; return seconds; } + +@implementation OFDateSingleton +- (instancetype)autorelease +{ + return self; +} + +- (instancetype)retain +{ + return self; +} + +- (void)release +{ +} + +- (unsigned int)retainCount +{ + return OF_RETAIN_COUNT_MAX; +} +@end + +@implementation OFDatePlaceholder +#ifdef __clang__ +/* We intentionally don't call into super, so silence the warning. */ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunknown-pragmas" +# pragma clang diagnostic ignored "-Wobjc-designated-initializers" +#endif +- (instancetype)initWithTimeIntervalSince1970: (of_time_interval_t)seconds +{ +#if defined(OF_OBJFW_RUNTIME) && UINTPTR_MAX == UINT64_MAX + uint64_t value; +#endif + + if (seconds == 0) { + static of_once_t once = OF_ONCE_INIT; + of_once(&once, initZeroDate); + return (id)zeroDate; + } + +#if defined(OF_OBJFW_RUNTIME) && UINTPTR_MAX == UINT64_MAX + value = OF_BSWAP64_IF_LE(OF_DOUBLE_TO_INT_RAW(OF_BSWAP_DOUBLE_IF_LE( + seconds))); + + /* Almost all dates fall into this range. */ + if (value & (UINT64_C(4) << 60)) { + id ret = objc_createTaggedPointer(dateTag, + value & ~(UINT64_C(4) << 60)); + + if (ret != nil) + return ret; + } +#endif + + return (id)[[OFDate of_alloc] initWithTimeIntervalSince1970: seconds]; +} +#ifdef __clang__ +# pragma clang diagnostic pop +#endif +@end + +#if defined(OF_OBJFW_RUNTIME) && UINTPTR_MAX == UINT64_MAX +@implementation OFTaggedPointerDate +- (of_time_interval_t)timeIntervalSince1970 +{ + uint64_t value = (uint64_t)object_getTaggedPointerValue(self); + + value |= UINT64_C(4) << 60; + + return OF_BSWAP_DOUBLE_IF_LE(OF_INT_TO_DOUBLE_RAW(OF_BSWAP64_IF_LE( + value))); +} +@end +#endif @implementation OFDate + (void)initialize { #ifdef OF_WINDOWS @@ -196,10 +338,12 @@ HMODULE module; #endif if (self != [OFDate class]) return; + + placeholder.isa = [OFDatePlaceholder class]; #if (!defined(HAVE_GMTIME_R) || !defined(HAVE_LOCALTIME_R)) && \ defined(OF_HAVE_THREADS) mutex = [[OFMutex alloc] init]; #endif @@ -207,10 +351,27 @@ #ifdef OF_WINDOWS if ((module = LoadLibrary("msvcrt.dll")) != NULL) func__mktime64 = (__time64_t (*)(struct tm *)) GetProcAddress(module, "_mktime64"); #endif + +#if defined(OF_OBJFW_RUNTIME) && UINTPTR_MAX == UINT64_MAX + dateTag = objc_registerTaggedPointerClass([OFTaggedPointerDate class]); +#endif +} + ++ (instancetype)of_alloc +{ + return [super alloc]; +} + ++ (instancetype)alloc +{ + if (self == [OFDate class]) + return (id)&placeholder; + + return [super alloc]; } + (instancetype)date { return [[[self alloc] init] autorelease]; @@ -242,32 +403,25 @@ format: format] autorelease]; } + (instancetype)distantFuture { - return [[[self alloc] - initWithTimeIntervalSince1970: 64060588800.0] autorelease]; + static of_once_t once = OF_ONCE_INIT; + of_once(&once, initDistantFuture); + return distantFuture; } + (instancetype)distantPast { - return [[[self alloc] - initWithTimeIntervalSince1970: -62167219200.0] autorelease]; + static of_once_t once = OF_ONCE_INIT; + of_once(&once, initDistantPast); + return distantPast; } - (instancetype)init { - struct timeval t; - - self = [super init]; - - OF_ENSURE(gettimeofday(&t, NULL) == 0); - - _seconds = t.tv_sec; - _seconds += (of_time_interval_t)t.tv_usec / 1000000; - - return self; + return [self initWithTimeIntervalSince1970: now()]; } - (instancetype)initWithTimeIntervalSince1970: (of_time_interval_t)seconds { self = [super init]; @@ -277,114 +431,89 @@ return self; } - (instancetype)initWithTimeIntervalSinceNow: (of_time_interval_t)seconds { - self = [self init]; - - _seconds += seconds; - - return self; + return [self initWithTimeIntervalSince1970: now() + seconds]; } - (instancetype)initWithDateString: (OFString *)string format: (OFString *)format { - self = [super init]; - - @try { - const char *UTF8String = string.UTF8String; - struct tm tm = { 0 }; - int16_t tz = 0; - - tm.tm_isdst = -1; - - if (of_strptime(UTF8String, format.UTF8String, - &tm, &tz) != UTF8String + string.UTF8StringLength) - @throw [OFInvalidFormatException exception]; - - _seconds = tmAndTzToTime(&tm, &tz); - } @catch (id e) { - [self release]; - @throw e; - } - - return self; + const char *UTF8String = string.UTF8String; + struct tm tm = { .tm_isdst = -1 }; + int16_t tz = 0; + + if (of_strptime(UTF8String, format.UTF8String, &tm, &tz) != + UTF8String + string.UTF8StringLength) + @throw [OFInvalidFormatException exception]; + + return [self initWithTimeIntervalSince1970: tmAndTzToTime(&tm, &tz)]; } - (instancetype)initWithLocalDateString: (OFString *)string format: (OFString *)format { - self = [super init]; - - @try { - const char *UTF8String = string.UTF8String; - struct tm tm = { 0 }; - /* - * of_strptime() can never set this to INT16_MAX, no matter - * what is passed to it, so this is a safe way to figure out if - * the date contains a time zone. - */ - int16_t tz = INT16_MAX; - - tm.tm_isdst = -1; - - if (of_strptime(UTF8String, format.UTF8String, - &tm, &tz) != UTF8String + string.UTF8StringLength) - @throw [OFInvalidFormatException exception]; - - if (tz == INT16_MAX) { -#ifdef OF_WINDOWS - if (func__mktime64 != NULL) { - if ((_seconds = func__mktime64(&tm)) == -1) - @throw [OFInvalidFormatException - exception]; - } else { -#endif - if ((_seconds = mktime(&tm)) == -1) - @throw [OFInvalidFormatException - exception]; -#ifdef OF_WINDOWS - } -#endif - } else - _seconds = tmAndTzToTime(&tm, &tz); - } @catch (id e) { - [self release]; - @throw e; - } - - return self; + const char *UTF8String = string.UTF8String; + struct tm tm = { .tm_isdst = -1 }; + /* + * of_strptime() can never set this to INT16_MAX, no matter what is + * passed to it, so this is a safe way to figure out if the date + * contains a time zone. + */ + int16_t tz = INT16_MAX; + of_time_interval_t seconds; + + if (of_strptime(UTF8String, format.UTF8String, &tm, &tz) != + UTF8String + string.UTF8StringLength) + @throw [OFInvalidFormatException exception]; + + if (tz == INT16_MAX) { +#ifdef OF_WINDOWS + if (func__mktime64 != NULL) { + if ((seconds = func__mktime64(&tm)) == -1) + @throw [OFInvalidFormatException exception]; + } else { +#endif + if ((seconds = mktime(&tm)) == -1) + @throw [OFInvalidFormatException exception]; +#ifdef OF_WINDOWS + } +#endif + } else + seconds = tmAndTzToTime(&tm, &tz); + + return [self initWithTimeIntervalSince1970: seconds]; } - (instancetype)initWithSerialization: (OFXMLElement *)element { - self = [super init]; + of_time_interval_t seconds; @try { void *pool = objc_autoreleasePoolPush(); unsigned long long value; - if (![element.name isEqual: self.className] || + if (![element.name isEqual: @"OFDate"] || ![element.namespace isEqual: OF_SERIALIZATION_NS]) @throw [OFInvalidArgumentException exception]; value = [element unsignedLongLongValueWithBase: 16]; if (value > UINT64_MAX) @throw [OFOutOfRangeException exception]; - _seconds = OF_BSWAP_DOUBLE_IF_LE(OF_INT_TO_DOUBLE_RAW( + seconds = OF_BSWAP_DOUBLE_IF_LE(OF_INT_TO_DOUBLE_RAW( OF_BSWAP64_IF_LE(value))); objc_autoreleasePoolPop(pool); } @catch (id e) { [self release]; @throw e; } - return self; + return [self initWithTimeIntervalSince1970: seconds]; } - (bool)isEqual: (id)object { OFDate *otherDate; @@ -395,11 +524,11 @@ if (![object isKindOfClass: [OFDate class]]) return false; otherDate = object; - if (otherDate->_seconds != _seconds) + if (otherDate.timeIntervalSince1970 != self.timeIntervalSince1970) return false; return true; } @@ -408,11 +537,11 @@ uint32_t hash; double tmp; OF_HASH_INIT(hash); - tmp = OF_BSWAP_DOUBLE_IF_BE(_seconds); + tmp = OF_BSWAP_DOUBLE_IF_BE(self.timeIntervalSince1970); for (size_t i = 0; i < sizeof(double); i++) OF_HASH_ADD(hash, ((char *)&tmp)[i]); OF_HASH_FINALIZE(hash); @@ -432,13 +561,13 @@ if (![(id)object isKindOfClass: [OFDate class]]) @throw [OFInvalidArgumentException exception]; otherDate = (OFDate *)object; - if (_seconds < otherDate->_seconds) + if (self.timeIntervalSince1970 < otherDate.timeIntervalSince1970) return OF_ORDERED_ASCENDING; - if (_seconds > otherDate->_seconds) + if (self.timeIntervalSince1970 > otherDate.timeIntervalSince1970) return OF_ORDERED_DESCENDING; return OF_ORDERED_SAME; } @@ -450,16 +579,16 @@ - (OFXMLElement *)XMLElementBySerializing { void *pool = objc_autoreleasePoolPush(); OFXMLElement *element; - element = [OFXMLElement elementWithName: self.className + element = [OFXMLElement elementWithName: @"OFDate" namespace: OF_SERIALIZATION_NS]; element.stringValue = [OFString stringWithFormat: @"%016" PRIx64, OF_BSWAP64_IF_LE(OF_DOUBLE_TO_INT_RAW(OF_BSWAP_DOUBLE_IF_LE( - _seconds)))]; + self.timeIntervalSince1970)))]; [element retain]; objc_autoreleasePoolPop(pool); @@ -467,13 +596,14 @@ } - (OFData *)messagePackRepresentation { void *pool = objc_autoreleasePoolPush(); - int64_t seconds = (int64_t)_seconds; + of_time_interval_t timeInterval = self.timeIntervalSince1970; + int64_t seconds = (int64_t)timeInterval; uint32_t nanoseconds = - (uint32_t)((_seconds - trunc(_seconds)) * 1000000000); + (uint32_t)((timeInterval - trunc(timeInterval)) * 1000000000); OFData *ret; if (seconds >= 0 && seconds < 0x400000000) { if (seconds <= UINT32_MAX && nanoseconds == 0) { uint32_t seconds32 = (uint32_t)seconds; @@ -522,11 +652,13 @@ return [ret autorelease]; } - (uint32_t)microsecond { - return (uint32_t)((_seconds - trunc(_seconds)) * 1000000); + of_time_interval_t timeInterval = self.timeIntervalSince1970; + + return (uint32_t)((timeInterval - trunc(timeInterval)) * 1000000); } - (uint8_t)second { GMTIME_RET(tm_sec) @@ -603,20 +735,21 @@ } - (OFString *)dateStringWithFormat: (OFConstantString *)format { OFString *ret; - time_t seconds = (time_t)_seconds; + of_time_interval_t timeInterval = self.timeIntervalSince1970; + time_t seconds = (time_t)timeInterval; struct tm tm; size_t pageSize; #ifndef OF_WINDOWS char *buffer; #else wchar_t *buffer; #endif - if (seconds != trunc(_seconds)) + if (seconds != trunc(timeInterval)) @throw [OFOutOfRangeException exception]; #ifdef HAVE_GMTIME_R if (gmtime_r(&seconds, &tm) == NULL) @throw [OFOutOfRangeException exception]; @@ -638,11 +771,13 @@ } # endif #endif pageSize = [OFSystemInfo pageSize]; - buffer = [self allocMemoryWithSize: pageSize]; + if ((buffer = malloc(pageSize)) == NULL) + @throw [OFOutOfMemoryException + exceptionWithRequestedSize: pageSize]; @try { #ifndef OF_WINDOWS if (strftime(buffer, pageSize, format.UTF8String, &tm) == 0) @throw [OFOutOfRangeException exception]; @@ -654,29 +789,30 @@ @throw [OFOutOfRangeException exception]; ret = [OFString stringWithUTF16String: buffer]; #endif } @finally { - [self freeMemory: buffer]; + free(buffer); } return ret; } - (OFString *)localDateStringWithFormat: (OFConstantString *)format { OFString *ret; - time_t seconds = (time_t)_seconds; + of_time_interval_t timeInterval = self.timeIntervalSince1970; + time_t seconds = (time_t)timeInterval; struct tm tm; size_t pageSize; #ifndef OF_WINDOWS char *buffer; #else wchar_t *buffer; #endif - if (seconds != trunc(_seconds)) + if (seconds != trunc(timeInterval)) @throw [OFOutOfRangeException exception]; #ifdef HAVE_LOCALTIME_R if (localtime_r(&seconds, &tm) == NULL) @throw [OFOutOfRangeException exception]; @@ -698,11 +834,13 @@ } # endif #endif pageSize = [OFSystemInfo pageSize]; - buffer = [self allocMemoryWithSize: pageSize]; + if ((buffer = malloc(pageSize)) == NULL) + @throw [OFOutOfMemoryException + exceptionWithRequestedSize: pageSize]; @try { #ifndef OF_WINDOWS if (strftime(buffer, pageSize, format.UTF8String, &tm) == 0) @throw [OFOutOfRangeException exception]; @@ -714,11 +852,11 @@ @throw [OFOutOfRangeException exception]; ret = [OFString stringWithUTF16String: buffer]; #endif } @finally { - [self freeMemory: buffer]; + free(buffer); } return ret; } @@ -749,11 +887,11 @@ return _seconds; } - (of_time_interval_t)timeIntervalSinceDate: (OFDate *)otherDate { - return _seconds - otherDate->_seconds; + return self.timeIntervalSince1970 - otherDate.timeIntervalSince1970; } - (of_time_interval_t)timeIntervalSinceNow { struct timeval t; @@ -762,13 +900,14 @@ OF_ENSURE(gettimeofday(&t, NULL) == 0); seconds = t.tv_sec; seconds += (of_time_interval_t)t.tv_usec / 1000000; - return _seconds - seconds; + return self.timeIntervalSince1970 - seconds; } - (OFDate *)dateByAddingTimeInterval: (of_time_interval_t)seconds { - return [OFDate dateWithTimeIntervalSince1970: _seconds + seconds]; + return [OFDate dateWithTimeIntervalSince1970: + self.timeIntervalSince1970 + seconds]; } @end Index: src/OFNumber.h ================================================================== --- src/OFNumber.h +++ src/OFNumber.h @@ -51,15 +51,10 @@ union of_number_value { double float_; long long signed_; unsigned long long unsigned_; } _value; - enum of_number_type { - OF_NUMBER_TYPE_FLOAT = 1, - OF_NUMBER_TYPE_SIGNED, - OF_NUMBER_TYPE_UNSIGNED - } _type; const char *_typeEncoding; } /*! * @brief The OFNumber as a `bool`. Index: src/OFNumber.m ================================================================== --- src/OFNumber.m +++ src/OFNumber.m @@ -40,10 +40,30 @@ @interface OFNumberPlaceholder: OFNumber @end @interface OFNumberSingleton: OFNumber @end + +#ifdef OF_OBJFW_RUNTIME +enum { + TAG_CHAR, + TAG_SHORT, + TAG_INT, + TAG_LONG, + TAG_LONG_LONG, + TAG_UNSIGNED_CHAR, + TAG_UNSIGNED_SHORT, + TAG_UNSIGNED_INT, + TAG_UNSIGNED_LONG, + TAG_UNSIGNED_LONG_LONG, +}; +# define TAG_BITS 4 +# define TAG_MASK 0xF + +@interface OFTaggedPointerNumber: OFNumberSingleton +@end +#endif static struct { Class isa; } placeholder; @@ -68,10 +88,67 @@ SINGLETON(unsignedLongZeroNumber, initWithUnsignedLong:, 0) SINGLETON(unsignedLongLongZeroNumber, initWithUnsignedLongLong:, 0) SINGLETON(floatZeroNumber, initWithFloat:, 0) SINGLETON(doubleZeroNumber, initWithDouble:, 0) #undef SINGLETON + +#ifdef OF_OBJFW_RUNTIME +static int numberTag; +#endif + +static bool +isUnsigned(OFNumber *number) +{ + switch (*number.objCType) { + case 'B': + return true; + case 'C': + return true; + case 'S': + return true; + case 'I': + return true; + case 'L': + return true; + case 'Q': + return true; + default: + return false; + } +} + +static bool +isSigned(OFNumber *number) +{ + switch (*number.objCType) { + case 'c': + return true; + case 's': + return true; + case 'i': + return true; + case 'l': + return true; + case 'q': + return true; + default: + return false; + } +} + +static bool +isFloat(OFNumber *number) +{ + switch (*number.objCType) { + case 'f': + return true; + case 'd': + return true; + default: + return false; + } +} @implementation OFNumberPlaceholder - (instancetype)initWithBool: (bool)value { if (value) { @@ -83,16 +160,28 @@ of_once(&once, falseNumberInit); return (id)falseNumber; } } +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif - (instancetype)initWithChar: (signed char)value { if (value == 0) { static of_once_t once = OF_ONCE_INIT; of_once(&once, charZeroNumberInit); return (id)charZeroNumber; +#ifdef OF_OBJFW_RUNTIME + } else if ((unsigned char)value <= (UINTPTR_MAX >> TAG_BITS)) { + id ret = objc_createTaggedPointer(numberTag, + ((uintptr_t)(unsigned char)value << TAG_BITS) | TAG_CHAR); + + if (ret != nil) + return ret; +#endif } return (id)[[OFNumber of_alloc] initWithChar: value]; } @@ -100,10 +189,18 @@ { if (value == 0) { static of_once_t once = OF_ONCE_INIT; of_once(&once, shortZeroNumberInit); return (id)shortZeroNumber; +#ifdef OF_OBJFW_RUNTIME + } else if ((unsigned short)value <= (UINTPTR_MAX >> TAG_BITS)) { + id ret = objc_createTaggedPointer(numberTag, + ((uintptr_t)(unsigned short)value << TAG_BITS) | TAG_SHORT); + + if (ret != nil) + return ret; +#endif } return (id)[[OFNumber of_alloc] initWithShort: value]; } @@ -111,10 +208,18 @@ { if (value == 0) { static of_once_t once = OF_ONCE_INIT; of_once(&once, intZeroNumberInit); return (id)intZeroNumber; +#ifdef OF_OBJFW_RUNTIME + } else if ((unsigned int)value <= (UINTPTR_MAX >> TAG_BITS)) { + id ret = objc_createTaggedPointer(numberTag, + ((uintptr_t)(unsigned int)value << TAG_BITS) | TAG_INT); + + if (ret != nil) + return ret; +#endif } return (id)[[OFNumber of_alloc] initWithInt: value]; } @@ -122,10 +227,18 @@ { if (value == 0) { static of_once_t once = OF_ONCE_INIT; of_once(&once, longZeroNumberInit); return (id)longZeroNumber; +#ifdef OF_OBJFW_RUNTIME + } else if ((unsigned long)value <= (UINTPTR_MAX >> TAG_BITS)) { + id ret = objc_createTaggedPointer(numberTag, + ((uintptr_t)(unsigned long)value << TAG_BITS) | TAG_LONG); + + if (ret != nil) + return ret; +#endif } return (id)[[OFNumber of_alloc] initWithLong: value]; } @@ -133,10 +246,19 @@ { if (value == 0) { static of_once_t once = OF_ONCE_INIT; of_once(&once, longLongZeroNumberInit); return (id)longLongZeroNumber; +#ifdef OF_OBJFW_RUNTIME + } else if ((unsigned long long)value <= (UINTPTR_MAX >> TAG_BITS)) { + id ret = objc_createTaggedPointer(numberTag, + ((uintptr_t)(unsigned long long)value << TAG_BITS) | + TAG_LONG_LONG); + + if (ret != nil) + return ret; +#endif } return (id)[[OFNumber of_alloc] initWithLongLong: value]; } @@ -144,10 +266,18 @@ { if (value == 0) { static of_once_t once = OF_ONCE_INIT; of_once(&once, unsignedCharZeroNumberInit); return (id)unsignedCharZeroNumber; +#ifdef OF_OBJFW_RUNTIME + } else if (value <= (UINTPTR_MAX >> TAG_BITS)) { + id ret = objc_createTaggedPointer(numberTag, + ((uintptr_t)value << TAG_BITS) | TAG_UNSIGNED_CHAR); + + if (ret != nil) + return ret; +#endif } return (id)[[OFNumber of_alloc] initWithUnsignedChar: value]; } @@ -155,10 +285,18 @@ { if (value == 0) { static of_once_t once = OF_ONCE_INIT; of_once(&once, unsignedShortZeroNumberInit); return (id)unsignedShortZeroNumber; +#ifdef OF_OBJFW_RUNTIME + } else if (value <= (UINTPTR_MAX >> TAG_BITS)) { + id ret = objc_createTaggedPointer(numberTag, + ((uintptr_t)value << TAG_BITS) | TAG_UNSIGNED_SHORT); + + if (ret != nil) + return ret; +#endif } return (id)[[OFNumber of_alloc] initWithUnsignedShort: value]; } @@ -166,10 +304,18 @@ { if (value == 0) { static of_once_t once = OF_ONCE_INIT; of_once(&once, unsignedIntZeroNumberInit); return (id)unsignedIntZeroNumber; +#ifdef OF_OBJFW_RUNTIME + } else if (value <= (UINTPTR_MAX >> TAG_BITS)) { + id ret = objc_createTaggedPointer(numberTag, + ((uintptr_t)value << TAG_BITS) | TAG_UNSIGNED_INT); + + if (ret != nil) + return ret; +#endif } return (id)[[OFNumber of_alloc] initWithUnsignedInt: value]; } @@ -177,10 +323,18 @@ { if (value == 0) { static of_once_t once = OF_ONCE_INIT; of_once(&once, unsignedLongZeroNumberInit); return (id)unsignedLongZeroNumber; +#ifdef OF_OBJFW_RUNTIME + } else if (value <= (UINTPTR_MAX >> TAG_BITS)) { + id ret = objc_createTaggedPointer(numberTag, + ((uintptr_t)value << TAG_BITS) | TAG_UNSIGNED_LONG); + + if (ret != nil) + return ret; +#endif } return (id)[[OFNumber of_alloc] initWithUnsignedLong: value]; } @@ -188,10 +342,18 @@ { if (value == 0) { static of_once_t once = OF_ONCE_INIT; of_once(&once, unsignedLongLongZeroNumberInit); return (id)unsignedLongLongZeroNumber; +#ifdef OF_OBJFW_RUNTIME + } else if (value <= (UINTPTR_MAX >> TAG_BITS)) { + id ret = objc_createTaggedPointer(numberTag, + ((uintptr_t)value << TAG_BITS) | TAG_UNSIGNED_LONG_LONG); + + if (ret != nil) + return ret; +#endif } return (id)[[OFNumber of_alloc] initWithUnsignedLongLong: value]; } @@ -219,10 +381,13 @@ - (instancetype)initWithSerialization: (OFXMLElement *)element { return (id)[[OFNumber of_alloc] initWithSerialization: element]; } +#ifdef __clang__ +# pragma clang diagnostic pop +#endif @end @implementation OFNumberSingleton - (instancetype)autorelease { @@ -241,16 +406,100 @@ - (unsigned int)retainCount { return OF_RETAIN_COUNT_MAX; } @end + +#ifdef OF_OBJFW_RUNTIME +@implementation OFTaggedPointerNumber +- (const char *)objCType +{ + uintptr_t value = object_getTaggedPointerValue(self); + + switch (value & TAG_MASK) { + case TAG_CHAR: + return @encode(signed char); + case TAG_SHORT: + return @encode(short); + case TAG_INT: + return @encode(int); + case TAG_LONG: + return @encode(long); + case TAG_LONG_LONG: + return @encode(long long); + case TAG_UNSIGNED_CHAR: + return @encode(unsigned char); + case TAG_UNSIGNED_SHORT: + return @encode(unsigned short); + case TAG_UNSIGNED_INT: + return @encode(unsigned int); + case TAG_UNSIGNED_LONG: + return @encode(unsigned long); + case TAG_UNSIGNED_LONG_LONG: + return @encode(unsigned long long); + default: + @throw [OFInvalidArgumentException exception]; + } +} + +# define RETURN_VALUE \ + uintptr_t value = object_getTaggedPointerValue(self); \ + \ + switch (value & TAG_MASK) { \ + case TAG_CHAR: \ + return (signed char)(unsigned char)(value >> TAG_BITS); \ + case TAG_SHORT: \ + return (short)(unsigned short)(value >> TAG_BITS); \ + case TAG_INT: \ + return (int)(unsigned int)(value >> TAG_BITS); \ + case TAG_LONG: \ + return (long)(unsigned long)(value >> TAG_BITS); \ + case TAG_LONG_LONG: \ + return (long long)(unsigned long long)(value >> TAG_BITS); \ + case TAG_UNSIGNED_CHAR: \ + return (unsigned char)(value >> TAG_BITS); \ + case TAG_UNSIGNED_SHORT: \ + return (unsigned short)(value >> TAG_BITS); \ + case TAG_UNSIGNED_INT: \ + return (unsigned int)(value >> TAG_BITS); \ + case TAG_UNSIGNED_LONG: \ + return (unsigned long)(value >> TAG_BITS); \ + case TAG_UNSIGNED_LONG_LONG: \ + return (unsigned long long)(value >> TAG_BITS); \ + default: \ + @throw [OFInvalidArgumentException exception]; \ + } +- (long long)longLongValue +{ + RETURN_VALUE +} + +- (unsigned long long)unsignedLongLongValue +{ + RETURN_VALUE +} + +- (double)doubleValue +{ + RETURN_VALUE +} +@end +# undef RETURN_VALUE +#endif @implementation OFNumber + (void)initialize { - if (self == [OFNumber class]) - placeholder.isa = [OFNumberPlaceholder class]; + if (self != [OFNumber class]) + return; + + placeholder.isa = [OFNumberPlaceholder class]; + +#ifdef OF_OBJFW_RUNTIME + numberTag = + objc_registerTaggedPointerClass([OFTaggedPointerNumber class]); +#endif } + (instancetype)of_alloc { return [super alloc]; @@ -337,11 +586,10 @@ - (instancetype)initWithBool: (bool)value { self = [super init]; _value.unsigned_ = value; - _type = OF_NUMBER_TYPE_UNSIGNED; _typeEncoding = @encode(bool); return self; } @@ -348,11 +596,10 @@ - (instancetype)initWithChar: (signed char)value { self = [super init]; _value.signed_ = value; - _type = OF_NUMBER_TYPE_SIGNED; _typeEncoding = @encode(signed char); return self; } @@ -359,11 +606,10 @@ - (instancetype)initWithShort: (short)value { self = [super init]; _value.signed_ = value; - _type = OF_NUMBER_TYPE_SIGNED; _typeEncoding = @encode(short); return self; } @@ -370,11 +616,10 @@ - (instancetype)initWithInt: (int)value { self = [super init]; _value.signed_ = value; - _type = OF_NUMBER_TYPE_SIGNED; _typeEncoding = @encode(int); return self; } @@ -381,11 +626,10 @@ - (instancetype)initWithLong: (long)value { self = [super init]; _value.signed_ = value; - _type = OF_NUMBER_TYPE_SIGNED; _typeEncoding = @encode(long); return self; } @@ -392,11 +636,10 @@ - (instancetype)initWithLongLong: (long long)value { self = [super init]; _value.signed_ = value; - _type = OF_NUMBER_TYPE_SIGNED; _typeEncoding = @encode(long long); return self; } @@ -403,11 +646,10 @@ - (instancetype)initWithUnsignedChar: (unsigned char)value { self = [super init]; _value.unsigned_ = value; - _type = OF_NUMBER_TYPE_UNSIGNED; _typeEncoding = @encode(unsigned long); return self; } @@ -414,11 +656,10 @@ - (instancetype)initWithUnsignedShort: (unsigned short)value { self = [super init]; _value.unsigned_ = value; - _type = OF_NUMBER_TYPE_UNSIGNED; _typeEncoding = @encode(unsigned short); return self; } @@ -425,11 +666,10 @@ - (instancetype)initWithUnsignedInt: (unsigned int)value { self = [super init]; _value.unsigned_ = value; - _type = OF_NUMBER_TYPE_UNSIGNED; _typeEncoding = @encode(unsigned int); return self; } @@ -436,11 +676,10 @@ - (instancetype)initWithUnsignedLong: (unsigned long)value { self = [super init]; _value.unsigned_ = value; - _type = OF_NUMBER_TYPE_UNSIGNED; _typeEncoding = @encode(unsigned long); return self; } @@ -447,11 +686,10 @@ - (instancetype)initWithUnsignedLongLong: (unsigned long long)value { self = [super init]; _value.unsigned_ = value; - _type = OF_NUMBER_TYPE_UNSIGNED; _typeEncoding = @encode(unsigned long long); return self; } @@ -458,11 +696,10 @@ - (instancetype)initWithPtrDiff: (ptrdiff_t)value { self = [super init]; _value.signed_ = value; - _type = OF_NUMBER_TYPE_SIGNED; _typeEncoding = @encode(ptrdiff_t); return self; } @@ -469,11 +706,10 @@ - (instancetype)initWithIntPtr: (intptr_t)value { self = [super init]; _value.signed_ = value; - _type = OF_NUMBER_TYPE_SIGNED; _typeEncoding = @encode(intptr_t); return self; } @@ -480,11 +716,10 @@ - (instancetype)initWithUIntPtr: (uintptr_t)value { self = [super init]; _value.unsigned_ = value; - _type = OF_NUMBER_TYPE_UNSIGNED; _typeEncoding = @encode(uintptr_t); return self; } @@ -491,11 +726,10 @@ - (instancetype)initWithFloat: (float)value { self = [super init]; _value.float_ = value; - _type = OF_NUMBER_TYPE_FLOAT; _typeEncoding = @encode(float); return self; } @@ -502,11 +736,10 @@ - (instancetype)initWithDouble: (double)value { self = [super init]; _value.float_ = value; - _type = OF_NUMBER_TYPE_FLOAT; _typeEncoding = @encode(double); return self; } @@ -516,11 +749,11 @@ @try { void *pool = objc_autoreleasePoolPush(); OFString *typeString; - if (![element.name isEqual: self.className] || + if (![element.name isEqual: @"OFNumber"] || ![element.namespace isEqual: OF_SERIALIZATION_NS]) @throw [OFInvalidArgumentException exception]; typeString = [element attributeForName: @"type"].stringValue; @@ -564,116 +797,125 @@ } - (void)getValue: (void *)value size: (size_t)size { - switch (*_typeEncoding) { -#define CASE(enc, type, field) \ + switch (*self.objCType) { +#define CASE(enc, type, property) \ case enc: { \ - type tmp = (type)_value.field; \ + type tmp = (type)self.property; \ \ if (size != sizeof(type)) \ @throw [OFOutOfRangeException exception]; \ \ memcpy(value, &tmp, size); \ break; \ } - CASE('B', bool, unsigned_) - CASE('c', signed char, signed_) - CASE('s', short, signed_) - CASE('i', int, signed_) - CASE('l', long, signed_) - CASE('q', long long, signed_) - CASE('C', unsigned char, unsigned_) - CASE('S', unsigned short, unsigned_) - CASE('I', unsigned int, unsigned_) - CASE('L', unsigned long, unsigned_) - CASE('Q', unsigned long long, unsigned_) - CASE('f', float, float_) - CASE('d', double, float_) + CASE('B', bool, unsignedLongLongValue) + CASE('c', signed char, longLongValue) + CASE('s', short, longLongValue) + CASE('i', int, longLongValue) + CASE('l', long, longLongValue) + CASE('q', long long, longLongValue) + CASE('C', unsigned char, unsignedLongLongValue) + CASE('S', unsigned short, unsignedLongLongValue) + CASE('I', unsigned int, unsignedLongLongValue) + CASE('L', unsigned long, unsignedLongLongValue) + CASE('Q', unsigned long long, unsignedLongLongValue) + CASE('f', float, doubleValue) + CASE('d', double, doubleValue) #undef CASE default: @throw [OFInvalidFormatException exception]; } } -#define RETURN_AS(t) \ - switch (_type) { \ - case OF_NUMBER_TYPE_FLOAT: \ - return (t)_value.float_; \ - case OF_NUMBER_TYPE_SIGNED: \ - return (t)_value.signed_; \ - case OF_NUMBER_TYPE_UNSIGNED: \ - return (t)_value.unsigned_; \ - default: \ - @throw [OFInvalidFormatException exception]; \ - } +- (long long)longLongValue +{ + if (isFloat(self)) + return _value.float_; + else if (isSigned(self)) + return _value.signed_; + else if (isUnsigned(self)) + return _value.unsigned_; + else + @throw [OFInvalidFormatException exception]; +} + +- (unsigned long long)unsignedLongLongValue +{ + if (isFloat(self)) + return _value.float_; + else if (isSigned(self)) + return _value.signed_; + else if (isUnsigned(self)) + return _value.unsigned_; + else + @throw [OFInvalidFormatException exception]; +} + +- (double)doubleValue +{ + if (isFloat(self)) + return _value.float_; + else if (isSigned(self)) + return _value.signed_; + else if (isUnsigned(self)) + return _value.unsigned_; + else + @throw [OFInvalidFormatException exception]; +} + - (bool)boolValue { - RETURN_AS(bool) + return (bool)self.unsignedLongLongValue; } - (signed char)charValue { - RETURN_AS(signed char) + return (signed char)self.longLongValue; } - (short)shortValue { - RETURN_AS(short) + return (short)self.longLongValue; } - (int)intValue { - RETURN_AS(int) + return (int)self.longLongValue; } - (long)longValue { - RETURN_AS(long) -} - -- (long long)longLongValue -{ - RETURN_AS(long long) + return (long)self.longLongValue; } - (unsigned char)unsignedCharValue { - RETURN_AS(unsigned char) + return (unsigned char)self.unsignedLongLongValue; } - (unsigned short)unsignedShortValue { - RETURN_AS(unsigned short) + return (unsigned short)self.unsignedLongLongValue; } - (unsigned int)unsignedIntValue { - RETURN_AS(unsigned int) + return (unsigned int)self.unsignedLongLongValue; } - (unsigned long)unsignedLongValue { - RETURN_AS(unsigned long) -} - -- (unsigned long long)unsignedLongLongValue -{ - RETURN_AS(unsigned long long) + return (unsigned long)self.unsignedLongLongValue; } - (float)floatValue { - RETURN_AS(float) -} - -- (double)doubleValue -{ - RETURN_AS(double) -} -#undef RETURN_AS + return (float)self.doubleValue; +} - (bool)isEqual: (id)object { OFNumber *number; @@ -683,12 +925,11 @@ if (![object isKindOfClass: [OFNumber class]]) return false; number = object; - if (_type == OF_NUMBER_TYPE_FLOAT || - number->_type == OF_NUMBER_TYPE_FLOAT) { + if (isFloat(self) || isFloat(number)) { double value1 = number.doubleValue; double value2 = self.doubleValue; if (isnan(value1) && isnan(value2)) return true; @@ -696,12 +937,11 @@ return false; return (value1 == value2); } - if (_type == OF_NUMBER_TYPE_SIGNED || - number->_type == OF_NUMBER_TYPE_SIGNED) + if (isSigned(self) || isSigned(number)) return (number.longLongValue == self.longLongValue); return (number.unsignedLongLongValue == self.unsignedLongLongValue); } @@ -712,23 +952,21 @@ if (![(id)object isKindOfClass: [OFNumber class]]) @throw [OFInvalidArgumentException exception]; number = (OFNumber *)object; - if (_type == OF_NUMBER_TYPE_FLOAT || - number->_type == OF_NUMBER_TYPE_FLOAT) { + if (isFloat(self) || isFloat(number)) { double double1 = self.doubleValue; double double2 = number.doubleValue; if (double1 > double2) return OF_ORDERED_DESCENDING; if (double1 < double2) return OF_ORDERED_ASCENDING; return OF_ORDERED_SAME; - } else if (_type == OF_NUMBER_TYPE_SIGNED || - number->_type == OF_NUMBER_TYPE_SIGNED) { + } else if (isSigned(self) || isSigned(number)) { long long int1 = self.longLongValue; long long int2 = number.longLongValue; if (int1 > int2) return OF_ORDERED_DESCENDING; @@ -749,27 +987,25 @@ } } - (uint32_t)hash { - enum of_number_type type = _type; uint32_t hash; OF_HASH_INIT(hash); - if (type == OF_NUMBER_TYPE_FLOAT) { + if (isFloat(self)) { double d; if (isnan(self.doubleValue)) return 0; d = OF_BSWAP_DOUBLE_IF_BE(self.doubleValue); for (uint_fast8_t i = 0; i < sizeof(double); i++) OF_HASH_ADD(hash, ((char *)&d)[i]); - } else if (type == OF_NUMBER_TYPE_SIGNED || - type == OF_NUMBER_TYPE_UNSIGNED) { + } else if (isSigned(self) || isUnsigned(self)) { unsigned long long value = self.unsignedLongLongValue; while (value != 0) { OF_HASH_ADD(hash, value & 0xFF); value >>= 8; @@ -792,45 +1028,46 @@ return [self stringValue]; } - (OFString *)stringValue { - if (*_typeEncoding == 'B') - return (_value.unsigned_ ? @"true" : @"false"); - if (_type == OF_NUMBER_TYPE_FLOAT) - return [OFString stringWithFormat: @"%g", _value.float_]; - if (_type == OF_NUMBER_TYPE_SIGNED) - return [OFString stringWithFormat: @"%lld", _value.signed_]; - if (_type == OF_NUMBER_TYPE_UNSIGNED) - return [OFString stringWithFormat: @"%llu", _value.unsigned_]; + if (*self.objCType == 'B') + return (self.boolValue ? @"true" : @"false"); + if (isFloat(self)) + return [OFString stringWithFormat: @"%g", self.doubleValue]; + if (isSigned(self)) + return [OFString stringWithFormat: @"%lld", self.longLongValue]; + if (isUnsigned(self)) + return [OFString stringWithFormat: @"%llu", + self.unsignedLongLongValue]; @throw [OFInvalidFormatException exception]; } - (OFXMLElement *)XMLElementBySerializing { void *pool = objc_autoreleasePoolPush(); OFXMLElement *element; - element = [OFXMLElement elementWithName: self.className + element = [OFXMLElement elementWithName: @"OFNumber" namespace: OF_SERIALIZATION_NS stringValue: self.description]; - if (*_typeEncoding == 'B') + if (*self.objCType == 'B') [element addAttributeWithName: @"type" stringValue: @"bool"]; - else if (_type == OF_NUMBER_TYPE_FLOAT) { + else if (isFloat(self)) { [element addAttributeWithName: @"type" stringValue: @"float"]; element.stringValue = [OFString stringWithFormat: @"%016" PRIx64, OF_BSWAP64_IF_LE(OF_DOUBLE_TO_INT_RAW(OF_BSWAP_DOUBLE_IF_LE( - _value.float_)))]; - } else if (_type == OF_NUMBER_TYPE_SIGNED) + self.doubleValue)))]; + } else if (isSigned(self)) [element addAttributeWithName: @"type" stringValue: @"signed"]; - else if (_type == OF_NUMBER_TYPE_UNSIGNED) + else if (isUnsigned(self)) [element addAttributeWithName: @"type" stringValue: @"unsigned"]; else @throw [OFInvalidFormatException exception]; @@ -856,12 +1093,12 @@ - (OFString *)of_JSONRepresentationWithOptions: (int)options depth: (size_t)depth { double doubleValue; - if (*_typeEncoding == 'B') - return (_value.unsigned_ ? @"true" : @"false"); + if (*self.objCType == 'B') + return (self.boolValue ? @"true" : @"false"); doubleValue = self.doubleValue; if (isinf(doubleValue)) { if (options & OF_JSON_REPRESENTATION_JSON5) { if (doubleValue > 0) @@ -876,37 +1113,38 @@ } - (OFData *)messagePackRepresentation { OFMutableData *data; + const char *typeEncoding = self.objCType; - if (*_typeEncoding == 'B') { - uint8_t type = (_value.unsigned_ ? 0xC3 : 0xC2); + if (*typeEncoding == 'B') { + uint8_t type = (self.boolValue ? 0xC3 : 0xC2); data = [OFMutableData dataWithItems: &type count: 1]; - } else if (*_typeEncoding == 'f') { + } else if (*typeEncoding == 'f') { uint8_t type = 0xCA; - float tmp = OF_BSWAP_FLOAT_IF_LE((float)_value.float_); + float tmp = OF_BSWAP_FLOAT_IF_LE(self.floatValue); data = [OFMutableData dataWithItemSize: 1 capacity: 5]; [data addItem: &type]; [data addItems: &tmp count: sizeof(tmp)]; - } else if (*_typeEncoding == 'd') { + } else if (*typeEncoding == 'd') { uint8_t type = 0xCB; - double tmp = OF_BSWAP_DOUBLE_IF_LE(_value.float_); + double tmp = OF_BSWAP_DOUBLE_IF_LE(self.doubleValue); data = [OFMutableData dataWithItemSize: 1 capacity: 9]; [data addItem: &type]; [data addItems: &tmp count: sizeof(tmp)]; - } else if (_type == OF_NUMBER_TYPE_SIGNED) { + } else if (isSigned(self)) { long long value = self.longLongValue; if (value >= -32 && value < 0) { uint8_t tmp = 0xE0 | ((uint8_t)(value - 32) & 0x1F); @@ -951,11 +1189,11 @@ [data addItem: &type]; [data addItems: &tmp count: sizeof(tmp)]; } else @throw [OFOutOfRangeException exception]; - } else if (_type == OF_NUMBER_TYPE_UNSIGNED) { + } else if (isUnsigned(self)) { unsigned long long value = self.unsignedLongLongValue; if (value <= 127) { uint8_t tmp = ((uint8_t)value & 0x7F); Index: src/OFObject.m ================================================================== --- src/OFObject.m +++ src/OFObject.m @@ -331,10 +331,15 @@ objc_setEnumerationMutationHandler(enumerationMutationHandler); do { of_hash_seed = of_random32(); } while (of_hash_seed == 0); + +#ifdef OF_OBJFW_RUNTIME + objc_setTaggedPointerSecret(sizeof(uintptr_t) == 4 + ? (uintptr_t)of_random32() : (uintptr_t)of_random64()); +#endif } + (void)unload { } Index: src/macros.h ================================================================== --- src/macros.h +++ src/macros.h @@ -415,17 +415,17 @@ } \ \ abort(); #endif #ifdef __clang__ -# define OF_DEALLOC_UNSUPPORTED \ - [self doesNotRecognizeSelector: _cmd]; \ - \ - abort(); \ - \ - _Pragma("clang diagnostic push ignore \"-Wunreachable-code\""); \ - [super dealloc]; /* Get rid of a stupid warning */ \ +# define OF_DEALLOC_UNSUPPORTED \ + [self doesNotRecognizeSelector: _cmd]; \ + \ + abort(); \ + \ + _Pragma("clang diagnostic push ignored \"-Wunreachable-code\""); \ + [super dealloc]; /* Get rid of a stupid warning */ \ _Pragma("clang diagnostic pop"); #else # define OF_DEALLOC_UNSUPPORTED \ [self doesNotRecognizeSelector: _cmd]; \ \ Index: src/runtime/Makefile ================================================================== --- src/runtime/Makefile +++ src/runtime/Makefile @@ -29,10 +29,11 @@ protocol.m \ selector.m \ sparsearray.m \ static-instances.m \ synchronized.m \ + tagged-pointer.m \ ${USE_SRCS_THREADS} SRCS_THREADS = threading.m \ ../mutex.m \ ../once.m \ ../tlskey.m Index: src/runtime/ObjFWRT.h ================================================================== --- src/runtime/ObjFWRT.h +++ src/runtime/ObjFWRT.h @@ -605,10 +605,59 @@ * @param object The object to add to the topmost autorelease pool * @return The autoreleased object */ extern id _Nullable _objc_rootAutorelease(id _Nullable object); +/*! + * @brief Sets the tagged pointer secret. + * + * @param secret A secret, random value that will be used to XOR all tagged + * pointers with + */ +extern void objc_setTaggedPointerSecret(uintptr_t secret); + +/*! + * @brief Registers a class for tagged pointers. + * + * @param class The class to register for tagged pointers + * @return The tagged pointer ID for the registered class + */ +extern int objc_registerTaggedPointerClass(Class _Nonnull class); + +/*! + * @brief Returns whether the specified object is a tagged pointer. + * + * @param object The object to inspect + * @return Whether the specified object is a tagged pointer + */ +static inline bool +object_isTaggedPointer(id _Nullable object) +{ + uintptr_t pointer = (uintptr_t)object; + + return pointer & 1; +} + +extern Class _Nullable object_getTaggedPointerClass(id _Nonnull object); + +/*! + * @brief Returns the value of the specified tagged pointer. + * + * @param object The object whose tagged pointer value should be returned + * @return The tagged pointer value of the object + */ +extern uintptr_t object_getTaggedPointerValue(id _Nonnull object); + +/*! + * @brief Creates a new tagged pointer. + * + * @param class The tag ID for the tagged pointer class to use + * @param value The value the tagged pointer should have + * @return A tagged pointer, or `nil` if it could not be created + */ +extern id _Nullable objc_createTaggedPointer(int class, uintptr_t value); + /* * Used by the compiler, but can also be called manually. * * These declarations are also required to prevent Clang's implicit * declarations which include __declspec(dllimport) on Windows. Index: src/runtime/amiga-glue.m ================================================================== --- src/runtime/amiga-glue.m +++ src/runtime/amiga-glue.m @@ -797,5 +797,46 @@ { M68K_ARG(struct objc_hashtable *, table, a0) objc_hashtable_free(table); } + +void __saveds +glue_objc_setTaggedPointerSecret PPC_PARAMS(uintptr_t secret) +{ + M68K_ARG(uintptr_t, secret, d0) + + objc_setTaggedPointerSecret(secret); +} + +int __saveds +glue_objc_registerTaggedPointerClass PPC_PARAMS(Class class) +{ + M68K_ARG(Class, class, a0) + + return objc_registerTaggedPointerClass(class); +} + +Class __saveds +glue_object_getTaggedPointerClass PPC_PARAMS(id object) +{ + M68K_ARG(id, object, a0) + + return object_getTaggedPointerClass(object); +} + +uintptr_t __saveds +glue_object_getTaggedPointerValue PPC_PARAMS(id object) +{ + M68K_ARG(id, object, a0) + + return object_getTaggedPointerValue(object); +} + +id __saveds +glue_objc_createTaggedPointer PPC_PARAMS(int class, uintptr_t value) +{ + M68K_ARG(int, class, d0) + M68K_ARG(uintptr_t, value, d1) + + return objc_createTaggedPointer(class, value); +} Index: src/runtime/amiga-library.m ================================================================== --- src/runtime/amiga-library.m +++ src/runtime/amiga-library.m @@ -145,10 +145,15 @@ extern struct objc_hashtable *glue_objc_hashtable_new(void); extern void glue_objc_hashtable_set(void); extern void *glue_objc_hashtable_get(void); extern void glue_objc_hashtable_delete(void); extern void glue_objc_hashtable_free(void); +extern void glue_objc_setTaggedPointerSecret(void); +extern int glue_objc_registerTaggedPointerClass(void); +extern Class _Nullable glue_object_getTaggedPointerClass(void); +extern uintptr_t glue_object_getTaggedPointerValue(void); +extern id _Nullable glue_objc_createTaggedPointer(void); #ifdef OF_MORPHOS const ULONG __abox__ = 1; #endif struct ExecBase *SysBase; @@ -671,10 +676,15 @@ (CONST_APTR)glue_objc_hashtable_new, (CONST_APTR)glue_objc_hashtable_set, (CONST_APTR)glue_objc_hashtable_get, (CONST_APTR)glue_objc_hashtable_delete, (CONST_APTR)glue_objc_hashtable_free, + (CONST_APTR)glue_objc_setTaggedPointerSecret, + (CONST_APTR)glue_objc_registerTaggedPointerClass, + (CONST_APTR)glue_object_getTaggedPointerClass, + (CONST_APTR)glue_object_getTaggedPointerValue, + (CONST_APTR)glue_objc_createTaggedPointer, (CONST_APTR)-1, #ifdef OF_MORPHOS (CONST_APTR)FUNCARRAY_END #endif }; Index: src/runtime/amigaos3.sfd ================================================================== --- src/runtime/amigaos3.sfd +++ src/runtime/amigaos3.sfd @@ -87,6 +87,12 @@ struct objc_hashtable *_Nonnull glue_objc_hashtable_new(objc_hashtable_hash_func hash, objc_hashtable_equal_func equal, uint32_t size)(a0,a1,d0) void glue_objc_hashtable_set(struct objc_hashtable *_Nonnull table, const void *_Nonnull key, const void *_Nonnull object)(a0,a1,a2) void *_Nullable glue_objc_hashtable_get(struct objc_hashtable *_Nonnull table, const void *_Nonnull key)(a0,a1) void glue_objc_hashtable_delete(struct objc_hashtable *_Nonnull table, const void *_Nonnull key)(a0,a1) void glue_objc_hashtable_free(struct objc_hashtable *_Nonnull table)(a0) +* Public functions again +void glue_objc_setTaggedPointerSecret(uintptr_t secret)(d0) +int glue_objc_registerTaggedPointerClass(Class _Nonnull class_)(a0) +Class _Nullable glue_object_getTaggedPointerClass(id _Nonnull object)(a0) +uintptr_t glue_object_getTaggedPointerValue(id _Nonnull object)(a0) +id _Nullable glue_objc_createTaggedPointer(int class_, uintptr_t value)(d0,d1) ==end Index: src/runtime/class.m ================================================================== --- src/runtime/class.m +++ src/runtime/class.m @@ -844,10 +844,13 @@ struct objc_object *object; if (object_ == nil) return Nil; + if (object_isTaggedPointer(object_)) + return object_getTaggedPointerClass(object_); + object = (struct objc_object *)object_; return object->isa; } Index: src/runtime/linklib/linklib.m ================================================================== --- src/runtime/linklib/linklib.m +++ src/runtime/linklib/linklib.m @@ -705,5 +705,35 @@ void objc_hashtable_free(struct objc_hashtable *table) { glue_objc_hashtable_free(table); } + +void +objc_setTaggedPointerSecret(uintptr_t secret) +{ + glue_objc_setTaggedPointerSecret(secret); +} + +int +objc_registerTaggedPointerClass(Class class) +{ + return glue_objc_registerTaggedPointerClass(class); +} + +Class +object_getTaggedPointerClass(id object) +{ + return glue_object_getTaggedPointerClass(object); +} + +uintptr_t +object_getTaggedPointerValue(id object) +{ + return glue_object_getTaggedPointerValue(object); +} + +id +objc_createTaggedPointer(int class, uintptr_t value) +{ + return objc_createTaggedPointer(class, value); +} Index: src/runtime/lookup-asm/lookup-asm-arm-elf.S ================================================================== --- src/runtime/lookup-asm/lookup-asm-arm-elf.S +++ src/runtime/lookup-asm/lookup-asm-arm-elf.S @@ -28,10 +28,13 @@ .macro generate_lookup name not_found \name: cmp r0, #0 beq ret_nil + tst r0, #1 + bne .Ltagged_pointer_\name + ldr r2, [r0, #0] ldr r2, [r2, #32] .Lmain_\name: #ifndef OF_BIG_ENDIAN @@ -57,12 +60,35 @@ cmp r2, #0 beq \not_found(PLT) mov r0, r2 bx lr + +.Ltagged_pointer_\name: + ldr r2, .Lgot$indirect_.Ltagged_pointer_\name + add r2, pc, r2 + + ldr r3, .Lgot$indirect_.Ltagged_pointer_\name+4 + ldr r3, [r2, r3] + ldr r3, [r3] + eor r0, r0, r3 + and r0, r0, #0xE + lsl r0, r0, #1 + + ldr r3, .Lgot$indirect_.Ltagged_pointer_\name+8 + ldr r3, [r2, r3] + ldr r2, [r3, r0] + ldr r2, [r2, #32] + + b .Lmain_\name .type \name, %function .size \name, .-\name + +.Lgot$indirect_.Ltagged_pointer_\name: + .long _GLOBAL_OFFSET_TABLE_-(.Ltagged_pointer_\name+12) + .long objc_tagged_pointer_secret(GOT) + .long objc_tagged_pointer_classes(GOT) .endm .macro generate_lookup_super name lookup \name: mov r2, r0 Index: src/runtime/lookup-asm/lookup-asm-arm64-elf.S ================================================================== --- src/runtime/lookup-asm/lookup-asm-arm64-elf.S +++ src/runtime/lookup-asm/lookup-asm-arm64-elf.S @@ -25,41 +25,56 @@ .globl objc_msg_lookup_super_stret .section .text .macro generate_lookup name not_found \name: - cmp x0, #0 - beq ret_nil + cbz x0, ret_nil + + tst x0, #1 + b.ne .Ltagged_pointer_\name - ldr x2, [x0, #0] + ldr x2, [x0] ldr x2, [x2, #64] .Lmain_\name: #ifdef OF_SELUID24 ldrb w3, [x1, #2] ldr x2, [x2, x3, lsl #3] #endif ldrb w3, [x1, #1] ldr x2, [x2, x3, lsl #3] - ldrb w3, [x1, #0] + ldrb w3, [x1] ldr x2, [x2, x3, lsl #3] - cmp x2, #0 - beq \not_found + cbz x2, \not_found mov x0, x2 ret + +.Ltagged_pointer_\name: + adrp x2, :got:objc_tagged_pointer_secret + ldr x2, [x2, #:got_lo12:objc_tagged_pointer_secret] + ldr x2, [x2] + eor x0, x0, x2 + and x0, x0, #0xE + lsl x0, x0, #2 + + adrp x2, :got:objc_tagged_pointer_classes + ldr x2, [x2, #:got_lo12:objc_tagged_pointer_classes] + ldr x2, [x2, x0] + ldr x2, [x2, #64] + + b .Lmain_\name .type \name, %function .size \name, .-\name .endm .macro generate_lookup_super name lookup \name: mov x2, x0 - ldr x0, [x0, #0] - cmp x0, #0 - beq ret_nil + ldr x0, [x0] + cbz x0, ret_nil ldr x2, [x2, #8] ldr x2, [x2, #64] b .Lmain_\lookup Index: src/runtime/lookup-asm/lookup-asm-mips-elf.S ================================================================== --- src/runtime/lookup-asm/lookup-asm-mips-elf.S +++ src/runtime/lookup-asm/lookup-asm-mips-elf.S @@ -27,10 +27,13 @@ .section .text .macro generate_lookup name not_found \name: beqz $a0, 0f + andi $t0, $a0, 1 + bnez $t0, .Ltagged_pointer_\name + lw $t0, 0($a0) lw $t0, 32($t0) .Lmain_\name: #ifdef OF_BIG_ENDIAN @@ -87,10 +90,38 @@ addiu $gp, $gp, 1b-\name lw $t9, %call16(\not_found)($gp) jr $t9 #endif + +.Ltagged_pointer_\name: +#ifdef OF_PIC +0: + lui $gp, %hi(_gp_disp) + addiu $gp, $gp, %lo(_gp_disp) + addu $gp, $gp, $t9 + addiu $gp, $gp, 0b-\name + + lw $t0, %got(objc_tagged_pointer_secret)($gp) +#else + la $t0, objc_tagged_pointer_secret +#endif + lw $t0, 0($t0) + xor $t0, $a0, $t0 + and $t0, $t0, 0xE + sll $t0, $t0, 1 + +#ifdef OF_PIC + lw $t1, %got(objc_tagged_pointer_classes)($gp) +#else + la $t1, objc_tagged_pointer_classes +#endif + addu $t0, $t1, $t0 + ld $t0, ($t0) + ld $t0, 32($t0) + + b .Lmain_\name .type \name, %function .size \name, .-\name .endm .macro generate_lookup_super name lookup Index: src/runtime/lookup-asm/lookup-asm-mips64-n64-elf.S ================================================================== --- src/runtime/lookup-asm/lookup-asm-mips64-n64-elf.S +++ src/runtime/lookup-asm/lookup-asm-mips64-n64-elf.S @@ -27,11 +27,14 @@ .section .text .macro generate_lookup name not_found \name: beqz $a0, 0f - ld $t0, 0($a0) + andi $t0, $a0, 1 + bnez $t0, .Ltagged_pointer_\name + + ld $t0, ($a0) ld $t0, 64($t0) .Lmain_\name: #ifdef OF_BIG_ENDIAN # ifdef OF_SELUID24 @@ -42,11 +45,11 @@ #else # ifdef OF_SELUID24 lbu $t1, 2($a1) # endif lbu $t2, 1($a1) - lbu $t3, 0($a1) + lbu $t3, ($a1) #endif #ifdef OF_SELUID24 sll $t1, $t1, 3 #endif @@ -53,43 +56,61 @@ sll $t2, $t2, 3 sll $t3, $t3, 3 #ifdef OF_SELUID24 daddu $t0, $t0, $t1 - ld $t0, 0($t0) + ld $t0, ($t0) #endif daddu $t0, $t0, $t2 - ld $t0, 0($t0) + ld $t0, ($t0) daddu $t0, $t0, $t3 - ld $t0, 0($t0) + ld $t0, ($t0) beqz $t0, 1f move $v0, $t0 jr $ra 0: lui $v0, %hi(%neg(%gp_rel(\name))) - daddu $v0, $v0, $t9 daddiu $v0, $v0, %lo(%neg(%gp_rel(\name))) + daddu $v0, $v0, $t9 ld $v0, %got_disp(nil_method)($v0) jr $ra 1: lui $t0, %hi(%neg(%gp_rel(\name))) - daddu $t0, $t0, $t9 daddiu $t0, $t0, %lo(%neg(%gp_rel(\name))) + daddu $t0, $t0, $t9 ld $t9, %got_disp(\not_found)($t0) jr $t9 + +.Ltagged_pointer_\name: + lui $t0, %hi(%neg(%gp_rel(\name))) + daddiu $t0, $t0, %lo(%neg(%gp_rel(\name))) + daddu $t0, $t0, $t9 + + ld $t1, %got_disp(objc_tagged_pointer_secret)($t0) + ld $t1, 0($t1) + xor $t1, $a0, $t1 + and $t1, $t1, 0xE + dsll $t1, $t1, 2 + + ld $t0, %got_disp(objc_tagged_pointer_classes)($t0) + daddu $t0, $t0, $t1 + ld $t0, ($t0) + ld $t0, 64($t0) + + b .Lmain_\name .type \name, %function .size \name, .-\name .endm .macro generate_lookup_super name lookup \name: move $t0, $a0 - ld $a0, 0($a0) + ld $a0, ($a0) beqz $a0, 0f ld $t0, 8($t0) ld $t0, 64($t0) @@ -96,12 +117,12 @@ daddiu $t9, $t9, \lookup-\name b .Lmain_\lookup 0: lui $v0, %hi(%neg(%gp_rel(\name))) - daddu $v0, $v0, $t9 daddiu $v0, $v0, %lo(%neg(%gp_rel(\name))) + daddu $v0, $v0, $t9 ld $v0, %got_disp(nil_method)($v0) jr $ra .type \name, %function .size \name, .-\name .endm Index: src/runtime/lookup-asm/lookup-asm-powerpc-elf.S ================================================================== --- src/runtime/lookup-asm/lookup-asm-powerpc-elf.S +++ src/runtime/lookup-asm/lookup-asm-powerpc-elf.S @@ -28,10 +28,13 @@ .macro generate_lookup name not_found \name: cmpwi %r3, 0 beq- ret_nil + andi. %r0, %r3, 1 + bne- .Ltagged_pointer_\name + lwz %r5, 0(%r3) lwz %r5, 32(%r5) .Lmain_\name: lwz %r8, 0(%r4) @@ -52,11 +55,50 @@ mr %r3, %r5 blr 0: - b \not_found@plt + stwu %r1, -16(%r1) + mflr %r0 + stw %r0, 20(%r1) + stw %r30, 8(%r1) + + bl 0f +0: + mflr %r30 + addis %r30, %r30, .Lbiased_got2-0b@ha + addi %r30, %r30, .Lbiased_got2-0b@l + + lwz %r0, .Lgot_\not_found-.Lbiased_got2(%r30) + mtctr %r0 + + lwz %r30, 8(%r1) + lwz %r0, 20(%r1) + addi %r1, %r1, 16 + mtlr %r0 + + bctr + +.Ltagged_pointer_\name: + mflr %r7 + bl 0f +0: + mflr %r6 + mtlr %r7 + addis %r6, %r6, .Lbiased_got2-0b@ha + addi %r6, %r6, .Lbiased_got2-0b@l + + lwz %r5, .Lgot_objc_tagged_pointer_secret-.Lbiased_got2(%r6) + lwz %r5, 0(%r5) + xor %r5, %r3, %r5 + rlwinm %r5, %r5, 1, 0x1C + + lwz %r6, .Lgot_objc_tagged_pointer_classes-.Lbiased_got2(%r6) + lwzx %r5, %r6, %r5 + lwz %r5, 32(%r5) + + b .Lmain_\name .type \name, @function .size \name, .-\name .endm .macro generate_lookup_super name lookup @@ -93,8 +135,19 @@ get_pc: mflr %r3 blr +.section .got2, "aw" +.Lbiased_got2 = .+0x8000 +.Lgot_objc_method_not_found: + .long objc_method_not_found +.Lgot_objc_method_not_found_stret: + .long objc_method_not_found_stret +.Lgot_objc_tagged_pointer_secret: + .long objc_tagged_pointer_secret +.Lgot_objc_tagged_pointer_classes: + .long objc_tagged_pointer_classes + #ifdef OF_LINUX .section .note.GNU-stack, "", @progbits #endif Index: src/runtime/lookup-asm/lookup-asm-sparc-elf.S ================================================================== --- src/runtime/lookup-asm/lookup-asm-sparc-elf.S +++ src/runtime/lookup-asm/lookup-asm-sparc-elf.S @@ -25,12 +25,14 @@ .globl objc_msg_lookup_super_stret .section .text .macro generate_lookup name not_found \name: - cmp %o0, 0 - be ret_nil + tst %o0 + bz ret_nil + btst 1, %o0 + bnz .Ltagged_pointer_\name nop ld [%o0], %o2 ld [%o2 + 32], %o2 @@ -62,10 +64,41 @@ 0: mov %o7, %g1 call \not_found mov %g1, %o7 + +.Ltagged_pointer_\name: +#ifdef OF_PIC + mov %o7, %g1 + sethi %hi(_GLOBAL_OFFSET_TABLE_ - 4), %o3 + call 0f + or %o3, %lo(_GLOBAL_OFFSET_TABLE_ + 4), %o3 +0: + add %o7, %o3, %o3 + mov %g1, %o7 +#endif + + sethi %hi(objc_tagged_pointer_secret), %o2 + or %o2, %lo(objc_tagged_pointer_secret), %o2 +#ifdef OF_PIC + ld [%o3 + %o2], %o2 +#endif + ld [%o2], %o2 + xor %o0, %o2, %o0 + and %o0, 0xE, %o0 + sll %o0, 1, %o0 + + sethi %hi(objc_tagged_pointer_classes), %o2 + or %o2, %lo(objc_tagged_pointer_classes), %o2 +#ifdef OF_PIC + ld [%o3 + %o2], %o2 +#endif + + ld [%o2 + %o0], %o2 + ba .Lmain_\name + ld [%o2 + 32], %o2 .type \name, %function .size \name, .-\name .endm .macro generate_lookup_super name lookup Index: src/runtime/lookup-asm/lookup-asm-sparc64-elf.S ================================================================== --- src/runtime/lookup-asm/lookup-asm-sparc64-elf.S +++ src/runtime/lookup-asm/lookup-asm-sparc64-elf.S @@ -26,10 +26,12 @@ .section .text .macro generate_lookup name not_found \name: brz,pn %o0, ret_nil + and %o0, 1, %o2 + brnz,pn %o2, .Ltagged_pointer_\name nop ldx [%o0], %o2 ldx [%o2 + 64], %o2 @@ -50,21 +52,51 @@ ldx [%o2 + %o3], %o2 #endif ldx [%o2 + %o4], %o2 ldx [%o2 + %o5], %o2 - cmp %o2, 0 - be,pn %xcc, 0f + brz,pn %o2, 0f nop retl mov %o2, %o0 0: mov %o7, %g1 call \not_found mov %g1, %o7 + +.Ltagged_pointer_\name: +#ifdef OF_PIC + mov %o7, %g1 + sethi %hi(_GLOBAL_OFFSET_TABLE_ - 4), %o3 + call 0f + or %o3, %lo(_GLOBAL_OFFSET_TABLE_ + 4), %o3 +0: + add %o7, %o3, %o3 + mov %g1, %o7 +#endif + + sethi %hi(objc_tagged_pointer_secret), %o2 + or %o2, %lo(objc_tagged_pointer_secret), %o2 +#ifdef OF_PIC + ldx [%o3 + %o2], %o2 +#endif + ldx [%o2], %o2 + xor %o0, %o2, %o0 + and %o0, 0xE, %o0 + sll %o0, 2, %o0 + + sethi %hi(objc_tagged_pointer_classes), %o2 + or %o2, %lo(objc_tagged_pointer_classes), %o2 +#ifdef OF_PIC + ldx [%o3 + %o2], %o2 +#endif + + ldx [%o2 + %o0], %o2 + ba .Lmain_\name + ldx [%o2 + 64], %o2 .type \name, %function .size \name, .-\name .endm .macro generate_lookup_super name lookup @@ -90,11 +122,11 @@ #ifdef OF_PIC mov %o7, %g1 sethi %hi(_GLOBAL_OFFSET_TABLE_ - 4), %o1 call 0f - add %o1, %lo(_GLOBAL_OFFSET_TABLE_ + 4), %o1 + or %o1, %lo(_GLOBAL_OFFSET_TABLE_ + 4), %o1 0: add %o7, %o1, %o1 sethi %hi(nil_method), %o0 or %o0, %lo(nil_method), %o0 Index: src/runtime/lookup-asm/lookup-asm-x86-elf.S ================================================================== --- src/runtime/lookup-asm/lookup-asm-x86-elf.S +++ src/runtime/lookup-asm/lookup-asm-x86-elf.S @@ -31,10 +31,13 @@ \name: mov edx, [esp+4] test edx, edx jz short ret_nil + bt edx, 0 + jc short .Ltagged_pointer_\name + mov edx, [edx] mov edx, [edx+32] .Lmain_\name: mov eax, [esp+8] @@ -56,10 +59,25 @@ 0: call get_eip add eax, offset _GLOBAL_OFFSET_TABLE_ lea eax, [eax+\not_found@GOTOFF] jmp eax + +.Ltagged_pointer_\name: + call get_eip + add eax, offset _GLOBAL_OFFSET_TABLE_ + + lea ecx, [eax+objc_tagged_pointer_secret@GOTOFF] + xor edx, [ecx] + and dl, 0xE + movzx edx, dl + + lea eax, [eax+objc_tagged_pointer_classes@GOTOFF] + mov edx, [eax+edx*2] + mov edx, [edx+32] + + jmp short .Lmain_\name .type \name, %function .size \name, .-\name .endm .macro generate_lookup_super name lookup Index: src/runtime/lookup-asm/lookup-asm-x86-win32.S ================================================================== --- src/runtime/lookup-asm/lookup-asm-x86-win32.S +++ src/runtime/lookup-asm/lookup-asm-x86-win32.S @@ -29,10 +29,13 @@ \name: mov edx, [esp+4] test edx, edx jz short ret_nil + bt edx, 0 + jc short .Ltagged_pointer_\name + mov edx, [edx] mov edx, [edx+32] .Lmain_\name: mov eax, [esp+8] @@ -45,16 +48,23 @@ mov edx, [edx+ecx*4] movzx ecx, byte ptr [eax] mov eax, [edx+ecx*4] test eax, eax - jz short 0f + jz \not_found ret -0: - jmp \not_found +.Ltagged_pointer_\name: + xor edx, _objc_tagged_pointer_secret + and dl, 0xE + movzx edx, dl + + mov edx, [_objc_tagged_pointer_classes+edx*2] + mov edx, [edx+32] + + jmp short .Lmain_\name .endm .macro generate_lookup_super name lookup \name: mov edx, [esp+4] Index: src/runtime/lookup-asm/lookup-asm-x86_64-elf.S ================================================================== --- src/runtime/lookup-asm/lookup-asm-x86_64-elf.S +++ src/runtime/lookup-asm/lookup-asm-x86_64-elf.S @@ -30,10 +30,13 @@ .macro generate_lookup name not_found \name: test rdi, rdi jz short ret_nil + bt edi, 0 + jc short .Ltagged_pointer_\name + mov r8, [rdi] mov r8, [r8+64] .Lmain_\name: mov rax, [rsi] @@ -49,10 +52,22 @@ test rax, rax jz short \not_found@PLT ret + +.Ltagged_pointer_\name: + mov rax, [rip+objc_tagged_pointer_secret@GOTPCREL] + xor rdi, [rax] + and dil, 0xE + movzx r8, dil + + mov rax, [rip+objc_tagged_pointer_classes@GOTPCREL] + mov r8, [rax+r8*4] + mov r8, [r8+64] + + jmp short .Lmain_\name .type \name, %function .size \name, .-\name .endm .macro generate_lookup_super name lookup Index: src/runtime/lookup-asm/lookup-asm-x86_64-macho.S ================================================================== --- src/runtime/lookup-asm/lookup-asm-x86_64-macho.S +++ src/runtime/lookup-asm/lookup-asm-x86_64-macho.S @@ -28,10 +28,13 @@ .macro generate_lookup $0: test rdi, rdi jz ret_nil + bt edi, 0 + jc Ltagged_pointer_$0 + mov r8, [rdi] mov r8, [r8+64] Lmain_$0: mov rax, [rsi] @@ -47,10 +50,22 @@ test rax, rax jz $1 ret + +Ltagged_pointer_$0: + mov rax, [rip+_objc_tagged_pointer_secret@GOTPCREL] + xor rdi, [rax] + and dil, 0xE + movzx r8, dil + + mov rax, [rip+_objc_tagged_pointer_classes@GOTPCREL] + mov r8, [rax+r8*4] + mov r8, [r8+64] + + jmp Lmain_$0 .endmacro .macro generate_lookup_super $0: mov r8, rdi Index: src/runtime/lookup-asm/lookup-asm-x86_64-win64.S ================================================================== --- src/runtime/lookup-asm/lookup-asm-x86_64-win64.S +++ src/runtime/lookup-asm/lookup-asm-x86_64-win64.S @@ -25,13 +25,16 @@ .globl objc_msg_lookup_super_stret .section .text .macro generate_lookup name not_found \name: - test %rcx, %rcx + test rcx, rcx jz short ret_nil + bt ecx, 0 + jc short .Ltagged_pointer_\name + mov r8, [rcx] mov r8, [r8+56] .Lmain_\name: mov r10, rcx @@ -55,10 +58,20 @@ 0: mov rcx, r10 mov rdx, r11 jmp \not_found + +.Ltagged_pointer_\name: + xor rcx, objc_tagged_pointer_secret + and cl, 0xE + movzx r8, cl + + mov r8, [objc_tagged_pointer_classes+r8*4] + mov r8, [r8+56] + + jmp short .Lmain_\name .endm .macro generate_lookup_super name lookup \name: mov r8, rcx Index: src/runtime/morphos-clib.h ================================================================== --- src/runtime/morphos-clib.h +++ src/runtime/morphos-clib.h @@ -77,10 +77,16 @@ void *glue_objc_destructInstance(id); void *glue_objc_autoreleasePoolPush(void); void glue_objc_autoreleasePoolPop(void *); id glue__objc_rootAutorelease(id); /* The following functions are private! Don't use! */ -struct objc_hashtable *glue_objc_hashtable_new(objc_hashtable_hash_func hash, objc_hashtable_equal_func equal, uint32_t size); -void glue_objc_hashtable_set(struct objc_hashtable *table, const void *key, const void *object); -void *glue_objc_hashtable_get(struct objc_hashtable *table, const void *key); -void glue_objc_hashtable_delete(struct objc_hashtable *table, const void *key); -void glue_objc_hashtable_free(struct objc_hashtable *table); +struct objc_hashtable *glue_objc_hashtable_new(objc_hashtable_hash_func, objc_hashtable_equal_func, uint32_t); +void glue_objc_hashtable_set(struct objc_hashtable *, const void *, const void *); +void *glue_objc_hashtable_get(struct objc_hashtable *, const void *); +void glue_objc_hashtable_delete(struct objc_hashtable *, const void *); +void glue_objc_hashtable_free(struct objc_hashtable *); +/* Public functions again */ +void glue_objc_setTaggedPointerSecret(uintptr_t); +int glue_objc_registerTaggedPointerClass(Class); +Class _Nullable glue_object_getTaggedPointerClass(id); +uintptr_t glue_object_getTaggedPointerValue(id); +id glue_objc_createTaggedPointer(int, uintptr_t); Index: src/runtime/morphos.fd ================================================================== --- src/runtime/morphos.fd +++ src/runtime/morphos.fd @@ -85,6 +85,12 @@ glue_objc_hashtable_new(hash,equal,size)(sysv,r12base) glue_objc_hashtable_set(table,key,object)(sysv,r12base) glue_objc_hashtable_get(table,key)(sysv,r12base) glue_objc_hashtable_delete(table,key)(sysv,r12base) glue_objc_hashtable_free(table)(sysv,r12base) +* Public functions again +glue_objc_setTaggedPointerSecret(secret)(sysv,r12base) +glue_objc_registerTaggedPointerClass(class_)(sysv,r12base) +glue_object_getTaggedPointerClass(object)(sysv,r12base) +glue_object_getTaggedPointerValue(object)(sysv,r12base) +glue_objc_createTaggedPointer(class_,value)(sysv,r12base) ##end ADDED src/runtime/tagged-pointer.m Index: src/runtime/tagged-pointer.m ================================================================== --- src/runtime/tagged-pointer.m +++ src/runtime/tagged-pointer.m @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019, 2020 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#import "ObjFWRT.h" + +#import "private.h" + +#define TAGGED_POINTER_BITS 4 +#define NUM_TAGGED_POINTER_CLASSES (1 << (TAGGED_POINTER_BITS - 1)) + +Class objc_tagged_pointer_classes[NUM_TAGGED_POINTER_CLASSES]; +static int taggedPointerClassesCount; +uintptr_t objc_tagged_pointer_secret; + +void +objc_setTaggedPointerSecret(uintptr_t secret) +{ + objc_tagged_pointer_secret = secret & ~(uintptr_t)1; +} + +int +objc_registerTaggedPointerClass(Class class) +{ + int i; + + objc_global_mutex_lock(); + + if (taggedPointerClassesCount == NUM_TAGGED_POINTER_CLASSES) { + objc_global_mutex_unlock(); + return -1; + } + + i = taggedPointerClassesCount++; + objc_tagged_pointer_classes[i] = class; + + objc_global_mutex_unlock(); + + return i; +} + +Class +object_getTaggedPointerClass(id object) +{ + uintptr_t pointer = (uintptr_t)object ^ objc_tagged_pointer_secret; + + pointer &= (1 << TAGGED_POINTER_BITS) - 1; + pointer >>= 1; + + if (pointer >= NUM_TAGGED_POINTER_CLASSES) + return Nil; + + return objc_tagged_pointer_classes[pointer]; +} + +uintptr_t +object_getTaggedPointerValue(id object) +{ + uintptr_t pointer = (uintptr_t)object ^ objc_tagged_pointer_secret; + + pointer >>= TAGGED_POINTER_BITS; + + return pointer; +} + +id +objc_createTaggedPointer(int class, uintptr_t value) +{ + uintptr_t pointer; + + if (class < 0 || class >= NUM_TAGGED_POINTER_CLASSES) + return nil; + + if (value > (UINTPTR_MAX >> TAGGED_POINTER_BITS)) + return nil; + + pointer = (class << 1) | 1; + pointer |= (value << TAGGED_POINTER_BITS); + + return (id)(pointer ^ objc_tagged_pointer_secret); +} Index: tests/OFNumberTests.m ================================================================== --- tests/OFNumberTests.m +++ tests/OFNumberTests.m @@ -36,9 +36,59 @@ TEST(@"-[hash]", num.hash == 0x82D8BC42) TEST(@"-[charValue]", num.charValue == 21) TEST(@"-[doubleValue]", num.doubleValue == 123456789.L) + + TEST(@"signed char minimum & maximum unmodified", + (num = [OFNumber numberWithChar: SCHAR_MIN]) && + num.charValue == SCHAR_MIN && + (num = [OFNumber numberWithChar: SCHAR_MAX]) && + num.charValue == SCHAR_MAX) + + TEST(@"short minimum & maximum unmodified", + (num = [OFNumber numberWithShort: SHRT_MIN]) && + num.shortValue == SHRT_MIN && + (num = [OFNumber numberWithShort: SHRT_MAX]) && + num.shortValue == SHRT_MAX) + + TEST(@"int minimum & maximum unmodified", + (num = [OFNumber numberWithInt: INT_MIN]) && + num.intValue == INT_MIN && + (num = [OFNumber numberWithInt: INT_MAX]) && + num.intValue == INT_MAX) + + TEST(@"long minimum & maximum unmodified", + (num = [OFNumber numberWithLong: LONG_MIN]) && + num.longValue == LONG_MIN && + (num = [OFNumber numberWithLong: LONG_MAX]) && + num.longValue == LONG_MAX) + + TEST(@"long long minimum & maximum unmodified", + (num = [OFNumber numberWithLongLong: LLONG_MIN]) && + num.longLongValue == LLONG_MIN && + (num = [OFNumber numberWithLongLong: LLONG_MAX]) && + num.longLongValue == LLONG_MAX) + + TEST(@"unsigned char maximum unmodified", + (num = [OFNumber numberWithUnsignedChar: UCHAR_MAX]) && + num.unsignedCharValue == UCHAR_MAX) + + TEST(@"unsigned short maximum unmodified", + (num = [OFNumber numberWithUnsignedShort: USHRT_MAX]) && + num.unsignedShortValue == USHRT_MAX) + + TEST(@"unsigned int maximum unmodified", + (num = [OFNumber numberWithUnsignedInt: UINT_MAX]) && + num.unsignedIntValue == UINT_MAX) + + TEST(@"unsigned long maximum unmodified", + (num = [OFNumber numberWithUnsignedLong: ULONG_MAX]) && + num.unsignedLongValue == ULONG_MAX) + + TEST(@"unsigned long long maximum unmodified", + (num = [OFNumber numberWithUnsignedLongLong: ULLONG_MAX]) && + num.unsignedLongLongValue == ULLONG_MAX) objc_autoreleasePoolPop(pool); } @end Index: tests/RuntimeTests.m ================================================================== --- tests/RuntimeTests.m +++ tests/RuntimeTests.m @@ -65,10 +65,15 @@ - (void)runtimeTests { void *pool = objc_autoreleasePoolPush(); RuntimeTest *rt = [[[RuntimeTest alloc] init] autorelease]; OFString *t, *foo; +#ifdef OF_OBJFW_RUNTIME + int cid1, cid2; + uintmax_t value; + id object; +#endif EXPECT_EXCEPTION(@"Calling a non-existent method via super", OFNotImplementedException, [rt superTest]) TEST(@"Calling a method via a super with self == nil", @@ -81,9 +86,28 @@ TEST(@"copy, nonatomic properties", [rt.foo isEqual: foo] && rt.foo != foo && rt.foo.retainCount == 1) rt.bar = t; TEST(@"retain, atomic properties", rt.bar == t && t.retainCount == 3) + +#ifdef OF_OBJFW_RUNTIME + if (sizeof(uintptr_t) == 8) + value = 0xDEADBEEFDEADBEF; + else if (sizeof(uintptr_t) == 4) + value = 0xDEADBEF; + else + abort(); + + TEST(@"Tagged pointers", + (cid1 = objc_registerTaggedPointerClass([OFString class])) != -1 && + (cid2 = objc_registerTaggedPointerClass([OFNumber class])) != -1 && + (object = objc_createTaggedPointer(cid2, (uintptr_t)value)) && + object_getTaggedPointerClass(object) == [OFNumber class] && + [object class] == [OFNumber class] && + object_getTaggedPointerValue(object) == value && + objc_createTaggedPointer(cid2, UINTPTR_MAX >> 4) != nil && + objc_createTaggedPointer(cid2, (UINTPTR_MAX >> 4) + 1) == nil) +#endif objc_autoreleasePoolPop(pool); } @end