Index: ObjFW.xcodeproj/project.pbxproj ================================================================== --- ObjFW.xcodeproj/project.pbxproj +++ ObjFW.xcodeproj/project.pbxproj @@ -784,10 +784,16 @@ 4B6025A219B76A5C00694BCC /* OFSHA384Or512Hash.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B60259C19B76A5C00694BCC /* OFSHA384Or512Hash.m */; }; 4B6025A319B76A5C00694BCC /* OFSHA512Hash.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B60259D19B76A5C00694BCC /* OFSHA512Hash.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B6025A419B76A5C00694BCC /* OFSHA512Hash.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B60259E19B76A5C00694BCC /* OFSHA512Hash.m */; }; 4B6025A719B76B5000694BCC /* OFSHA384HashTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B6025A519B76B5000694BCC /* OFSHA384HashTests.m */; }; 4B6025A819B76B5000694BCC /* OFSHA512HashTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B6025A619B76B5000694BCC /* OFSHA512HashTests.m */; }; + 4B61CB041F647CC60000DDDA /* OFMethodSignature.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B61CB021F647CC60000DDDA /* OFMethodSignature.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4B61CB051F647CC60000DDDA /* OFMethodSignature.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B61CB021F647CC60000DDDA /* OFMethodSignature.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4B61CB061F647CC60000DDDA /* OFMethodSignature.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B61CB031F647CC60000DDDA /* OFMethodSignature.m */; }; + 4B61CB071F647CC60000DDDA /* OFMethodSignature.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B61CB031F647CC60000DDDA /* OFMethodSignature.m */; }; + 4B61CB091F6497C40000DDDA /* OFMethodSignatureTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B61CB081F6497C40000DDDA /* OFMethodSignatureTests.m */; }; + 4B61CB0A1F6497FB0000DDDA /* OFMethodSignatureTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B61CB081F6497C40000DDDA /* OFMethodSignatureTests.m */; }; 4B6255161EC274FA003D49F4 /* OFHTTPCookieManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B6255141EC274FA003D49F4 /* OFHTTPCookieManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B6255171EC274FA003D49F4 /* OFHTTPCookieManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B6255141EC274FA003D49F4 /* OFHTTPCookieManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B6255181EC274FA003D49F4 /* OFHTTPCookieManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B6255151EC274FA003D49F4 /* OFHTTPCookieManager.m */; }; 4B6255191EC274FA003D49F4 /* OFHTTPCookieManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B6255151EC274FA003D49F4 /* OFHTTPCookieManager.m */; }; 4B62ED1518566FCA0004E0E3 /* OFCopyItemFailedException.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B62ED1318566FCA0004E0E3 /* OFCopyItemFailedException.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -1421,10 +1427,13 @@ 4B60259C19B76A5C00694BCC /* OFSHA384Or512Hash.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFSHA384Or512Hash.m; path = src/OFSHA384Or512Hash.m; sourceTree = ""; }; 4B60259D19B76A5C00694BCC /* OFSHA512Hash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFSHA512Hash.h; path = src/OFSHA512Hash.h; sourceTree = ""; }; 4B60259E19B76A5C00694BCC /* OFSHA512Hash.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFSHA512Hash.m; path = src/OFSHA512Hash.m; sourceTree = ""; }; 4B6025A519B76B5000694BCC /* OFSHA384HashTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFSHA384HashTests.m; path = tests/OFSHA384HashTests.m; sourceTree = ""; }; 4B6025A619B76B5000694BCC /* OFSHA512HashTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFSHA512HashTests.m; path = tests/OFSHA512HashTests.m; sourceTree = ""; }; + 4B61CB021F647CC60000DDDA /* OFMethodSignature.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFMethodSignature.h; path = src/OFMethodSignature.h; sourceTree = ""; }; + 4B61CB031F647CC60000DDDA /* OFMethodSignature.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFMethodSignature.m; path = src/OFMethodSignature.m; sourceTree = ""; }; + 4B61CB081F6497C40000DDDA /* OFMethodSignatureTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFMethodSignatureTests.m; path = tests/OFMethodSignatureTests.m; sourceTree = ""; }; 4B6255141EC274FA003D49F4 /* OFHTTPCookieManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFHTTPCookieManager.h; path = src/OFHTTPCookieManager.h; sourceTree = ""; }; 4B6255151EC274FA003D49F4 /* OFHTTPCookieManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFHTTPCookieManager.m; path = src/OFHTTPCookieManager.m; sourceTree = ""; }; 4B62ED1318566FCA0004E0E3 /* OFCopyItemFailedException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFCopyItemFailedException.h; path = src/exceptions/OFCopyItemFailedException.h; sourceTree = ""; }; 4B62ED1418566FCA0004E0E3 /* OFCopyItemFailedException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFCopyItemFailedException.m; path = src/exceptions/OFCopyItemFailedException.m; sourceTree = ""; }; 4B6736111E2B2F6F00681F2C /* codepage_858.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = codepage_858.m; path = src/encodings/codepage_858.m; sourceTree = ""; }; @@ -2131,10 +2140,12 @@ 4BF1BCC211C9663F0025511F /* OFMD5Hash.h */, 4BF1BCC311C9663F0025511F /* OFMD5Hash.m */, 4BCAA9AD1772432E003EF859 /* OFMessagePackExtension.h */, 4BCAA9AE1772432E003EF859 /* OFMessagePackExtension.m */, 4B879A8B177231F000EBCEA4 /* OFMessagePackRepresentation.h */, + 4B61CB021F647CC60000DDDA /* OFMethodSignature.h */, + 4B61CB031F647CC60000DDDA /* OFMethodSignature.m */, 4B67996F1099E7C50041064A /* OFMutableArray.h */, 4B6799701099E7C50041064A /* OFMutableArray.m */, 4B2B3E77140D430500EC2F7C /* OFMutableArray_adjacent.h */, 4B2B3E78140D430500EC2F7C /* OFMutableArray_adjacent.m */, 4B1223111F23E6B300D9F8FF /* OFMutableData.h */, @@ -2356,10 +2367,11 @@ 4B5B02C018B2897500CE6AE4 /* OFINIFileTests.m */, 4BAA60C714D09699006F068D /* OFJSONTests.m */, 4B22BE1F1AE594A000CD320A /* OFKernelEventObserverTests.m */, 4B6EF6721235358D0076B512 /* OFListTests.m */, 4B6EF6731235358D0076B512 /* OFMD5HashTests.m */, + 4B61CB081F6497C40000DDDA /* OFMethodSignatureTests.m */, 4B6EF6741235358D0076B512 /* OFNumberTests.m */, 4B6EF6751235358D0076B512 /* OFObjectTests.m */, 4B6EF6761235358D0076B512 /* OFPluginTests.m */, 4BEC83BB19B7CBDE00E4BB08 /* OFRIPEMD160HashTests.m */, 4B3D5693139A617D0010A78F /* OFSerializationTests.m */, @@ -2519,10 +2531,11 @@ 4B2C21FB1DA292BE00735907 /* OFLocking.h in Headers */, 4B2C21FC1DA292BE00735907 /* OFMapTable.h in Headers */, 4B2C21FD1DA292BE00735907 /* OFMD5Hash.h in Headers */, 4B2C21FE1DA292BE00735907 /* OFMessagePackExtension.h in Headers */, 4B2C21FF1DA292BE00735907 /* OFMessagePackRepresentation.h in Headers */, + 4B61CB051F647CC60000DDDA /* OFMethodSignature.h in Headers */, 4B2C22001DA292BE00735907 /* OFMutableArray.h in Headers */, 4B1223181F23E6C100D9F8FF /* OFMutableData.h in Headers */, 4B2C22011DA292BE00735907 /* OFMutableDictionary.h in Headers */, 4B3DE1281F5F4F2F0090AA3E /* OFMutablePair.h in Headers */, 4B2C22021DA292BE00735907 /* OFMutableSet.h in Headers */, @@ -2752,10 +2765,11 @@ 4B674402163C395900EB1E59 /* OFLocking.h in Headers */, 4B3B0798166978780044E634 /* OFMapTable.h in Headers */, 4B3D23CC1337FCB000DD29B8 /* OFMD5Hash.h in Headers */, 4BCAA9AF1772432F003EF859 /* OFMessagePackExtension.h in Headers */, 4B879A8E177231F000EBCEA4 /* OFMessagePackRepresentation.h in Headers */, + 4B61CB041F647CC60000DDDA /* OFMethodSignature.h in Headers */, 4B3D23CD1337FCB000DD29B8 /* OFMutableArray.h in Headers */, 4B1223151F23E6C000D9F8FF /* OFMutableData.h in Headers */, 4B3D23CE1337FCB000DD29B8 /* OFMutableDictionary.h in Headers */, 4B3DE1271F5F4F2F0090AA3E /* OFMutablePair.h in Headers */, 4B39844713D3AFB400E6F825 /* OFMutableSet.h in Headers */, @@ -3318,10 +3332,11 @@ 4B2C21441DA292BE00735907 /* OFList.m in Sources */, 4B46E2241E235EA700121ED6 /* OFLocalization.m in Sources */, 4B2C21451DA292BE00735907 /* OFMapTable.m in Sources */, 4B2C21461DA292BE00735907 /* OFMD5Hash.m in Sources */, 4B2C21471DA292BE00735907 /* OFMessagePackExtension.m in Sources */, + 4B61CB071F647CC60000DDDA /* OFMethodSignature.m in Sources */, 4B2C21481DA292BE00735907 /* OFMutableArray.m in Sources */, 4B2C21491DA292BE00735907 /* OFMutableArray_adjacent.m in Sources */, 4B1223191F23E6C100D9F8FF /* OFMutableData.m in Sources */, 4B2C214A1DA292BE00735907 /* OFMutableDictionary.m in Sources */, 4B2C214B1DA292BE00735907 /* OFMutableDictionary_hashtable.m in Sources */, @@ -3336,10 +3351,11 @@ 4B92FDE21F35DFBC000D541D /* OFMutableZIPArchiveEntry.m in Sources */, 4B2C21501DA292BE00735907 /* OFMutex.m in Sources */, 4B2C21511DA292BE00735907 /* OFNull.m in Sources */, 4B2C21521DA292BE00735907 /* OFNumber.m in Sources */, 4B2C21531DA292BE00735907 /* OFObject.m in Sources */, + 4B2C21991DA292BE00735907 /* OFObject+KeyValueCoding.m in Sources */, 4B2C21541DA292BE00735907 /* OFObject+Serialization.m in Sources */, 4B2C21551DA292BE00735907 /* OFOptionsParser.m in Sources */, 4B3DE12E1F5F4F2F0090AA3E /* OFPair.m in Sources */, 4B5D70681DA2F7B400B3B2D7 /* OFPlugin.m in Sources */, 4B2C21581DA292BE00735907 /* OFRecursiveMutex.m in Sources */, @@ -3403,11 +3419,10 @@ 4B2C21921DA292BE00735907 /* scrypt.m in Sources */, 4B2C21931DA292BE00735907 /* socket.m in Sources */, 4B2C21941DA292BE00735907 /* threading.m in Sources */, 4B2C21961DA292BE00735907 /* unicode.m in Sources */, 4B2C21981DA292BE00735907 /* OFAcceptFailedException.m in Sources */, - 4B2C21991DA292BE00735907 /* OFObject+KeyValueCoding.m in Sources */, 4B2C219A1DA292BE00735907 /* OFAddressTranslationFailedException.m in Sources */, 4B2C219B1DA292BE00735907 /* OFAllocFailedException.m in Sources */, 4B2C219C1DA292BE00735907 /* OFAlreadyConnectedException.m in Sources */, 4B2C219D1DA292BE00735907 /* OFBindFailedException.m in Sources */, 4B2C219E1DA292BE00735907 /* OFChangeCurrentDirectoryPathFailedException.m in Sources */, @@ -3524,10 +3539,11 @@ 4B3D23991337FC0D00DD29B8 /* OFList.m in Sources */, 4B46E2231E235EA700121ED6 /* OFLocalization.m in Sources */, 4B3B0799166978780044E634 /* OFMapTable.m in Sources */, 4B3D239A1337FC0D00DD29B8 /* OFMD5Hash.m in Sources */, 4BCAA9B01772432F003EF859 /* OFMessagePackExtension.m in Sources */, + 4B61CB061F647CC60000DDDA /* OFMethodSignature.m in Sources */, 4B3D239B1337FC0D00DD29B8 /* OFMutableArray.m in Sources */, 4B2B3E82140D430500EC2F7C /* OFMutableArray_adjacent.m in Sources */, 4B1223161F23E6C000D9F8FF /* OFMutableData.m in Sources */, 4B3D239C1337FC0D00DD29B8 /* OFMutableDictionary.m in Sources */, 4B2B3E84140D430500EC2F7C /* OFMutableDictionary_hashtable.m in Sources */, @@ -3542,10 +3558,11 @@ 4B92FDE11F35DFBB000D541D /* OFMutableZIPArchiveEntry.m in Sources */, 4B674404163C395900EB1E59 /* OFMutex.m in Sources */, 4B511B7D139C0A34003764A5 /* OFNull.m in Sources */, 4B3D239E1337FC0D00DD29B8 /* OFNumber.m in Sources */, 4B3D239F1337FC0D00DD29B8 /* OFObject.m in Sources */, + 4BC176311D04963000C32718 /* OFObject+KeyValueCoding.m in Sources */, 4BB25E89139C388A00F574EA /* OFObject+Serialization.m in Sources */, 4BAFC169182EAA7800BE5E57 /* OFOptionsParser.m in Sources */, 4B3DE12D1F5F4F2F0090AA3E /* OFPair.m in Sources */, 4B3D23A01337FC0D00DD29B8 /* OFPlugin.m in Sources */, 4BB524C2143D1E4E0085FBCC /* OFProcess.m in Sources */, @@ -3610,11 +3627,10 @@ 4B6994481D47FB1A007F34DF /* scrypt.m in Sources */, 4B40EC1B189FE2650031E19E /* socket.m in Sources */, 4B3379CF1979326A0088E97E /* threading.m in Sources */, 4B3D23B91337FC0D00DD29B8 /* unicode.m in Sources */, 4B90B79F133AD87D00BD33CB /* OFAcceptFailedException.m in Sources */, - 4BC176311D04963000C32718 /* OFObject+KeyValueCoding.m in Sources */, 4B90B7A1133AD87D00BD33CB /* OFAddressTranslationFailedException.m in Sources */, 4B17FF80133A2D17003E6DCD /* OFAllocFailedException.m in Sources */, 4B90B78E133AD46700BD33CB /* OFAlreadyConnectedException.m in Sources */, 4B90B7A3133AD87D00BD33CB /* OFBindFailedException.m in Sources */, 4B067FBC177BA6F900B8CFDA /* OFChangeCurrentDirectoryPathFailedException.m in Sources */, @@ -3712,10 +3728,11 @@ 4BD9CA091DA2C61E00E5AD52 /* OFINIFileTests.m in Sources */, 4BD9CA0A1DA2C62000E5AD52 /* OFJSONTests.m in Sources */, 4BD9CA0B1DA2C62200E5AD52 /* OFKernelEventObserverTests.m in Sources */, 4BD9CA0C1DA2C62500E5AD52 /* OFListTests.m in Sources */, 4BD9CA0D1DA2C62800E5AD52 /* OFMD5HashTests.m in Sources */, + 4B61CB0A1F6497FB0000DDDA /* OFMethodSignatureTests.m in Sources */, 4BD9CA0E1DA2C62B00E5AD52 /* OFNumberTests.m in Sources */, 4BD9CA0F1DA2C62D00E5AD52 /* OFObjectTests.m in Sources */, 4B5D70691DA2F81700B3B2D7 /* OFPluginTests.m in Sources */, 4BD9CA111DA2C63200E5AD52 /* OFRIPEMD160HashTests.m in Sources */, 4BD9CA121DA2C63800E5AD52 /* OFSerializationTests.m in Sources */, @@ -3765,10 +3782,11 @@ 4BF33B07133807A20059CEF7 /* OFPluginTests.m in Sources */, 4BEC83BC19B7CBDE00E4BB08 /* OFRIPEMD160HashTests.m in Sources */, 4B3D5694139A617D0010A78F /* OFSerializationTests.m in Sources */, 4B4B6904191437D500334775 /* OFSetTests.m in Sources */, 4BF33B08133807A20059CEF7 /* OFSHA1HashTests.m in Sources */, + 4B61CB091F6497C40000DDDA /* OFMethodSignatureTests.m in Sources */, 4B24593019B53BC80059F271 /* OFSHA224HashTests.m in Sources */, 4B24593119B53BC80059F271 /* OFSHA256HashTests.m in Sources */, 4B6025A719B76B5000694BCC /* OFSHA384HashTests.m in Sources */, 4B6025A819B76B5000694BCC /* OFSHA512HashTests.m in Sources */, 4BF33B09133807A20059CEF7 /* OFStreamTests.m in Sources */, Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -33,10 +33,11 @@ OFList.m \ OFLocalization.m \ OFMapTable.m \ OFMD5Hash.m \ OFMessagePackExtension.m \ + OFMethodSignature.m \ OFMutableArray.m \ OFMutableData.m \ OFMutableDictionary.m \ OFMutablePair.m \ OFMutableSet.m \ ADDED src/OFMethodSignature.h Index: src/OFMethodSignature.h ================================================================== --- src/OFMethodSignature.h +++ src/OFMethodSignature.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 + * 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 "OFObject.h" + +OF_ASSUME_NONNULL_BEGIN + +@class OFMutableData; + +/*! + * @class OFMethodSignature OFMethodSignature.h ObjFW/OFMethodSignature.h + * + * @brief A class for parsing type encodings and accessing them. + */ +@interface OFMethodSignature: OFObject +{ + char *_types; + OFMutableData *_typesPointers; +} + +/*! + * The number of arguments of the method. + */ +@property (readonly, nonatomic) size_t numberOfArguments; + +/*! + * The return type of the method. + */ +@property (readonly, nonatomic) const char *methodReturnType; + +/*! + * @brief Creates a new, autoreleased OFMethodSignature with the specified + * ObjC types. + * + * @param types The ObjC types of the method + * @return A new, autoreleased OFMethodSignature + */ ++ (instancetype)signatureWithObjCTypes: (const char *)types; + +/*! + * @brief Initializes an already allocated OFMethodSignature with the specified + * ObjC types. + * + * @param types The ObjC types of the method + * @return An Initialized OFMethodSignature + */ +- initWithObjCTypes: (const char *)types; + +/*! + * @brief Returns the ObjC type for the argument at the specified index. + * + * @param index The index for which to return the ObjC type + * @return The ObjC type for the argument at the specified index + */ +- (const char *)argumentTypeAtIndex: (size_t)index; +@end + +OF_ASSUME_NONNULL_END ADDED src/OFMethodSignature.m Index: src/OFMethodSignature.m ================================================================== --- src/OFMethodSignature.m +++ src/OFMethodSignature.m @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 + * 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. + */ + +#include "config.h" + +#include + +#import "OFMethodSignature.h" +#import "OFData.h" + +#import "OFInvalidArgumentException.h" +#import "OFInvalidFormatException.h" + +@implementation OFMethodSignature ++ (instancetype)signatureWithObjCTypes: (const char*)types +{ + return [[[self alloc] initWithObjCTypes: types] autorelease]; +} + +- initWithObjCTypes: (const char *)types +{ + self = [super init]; + + @try { + size_t length; + const char *last; + + if (types == NULL) + @throw [OFInvalidArgumentException exception]; + + length = strlen(types); + + if (length == 0) + @throw [OFInvalidFormatException exception]; + + _types = [self allocMemoryWithSize: length + 1]; + memcpy(_types, types, length); + + _typesPointers = [[OFMutableData alloc] + initWithItemSize: sizeof(char *)]; + + last = _types; + for (size_t i = 0; i < length; i++) { + if (isdigit(_types[i])) { + if (last == _types + i) + @throw [OFInvalidFormatException + exception]; + + _types[i] = '\0'; + [_typesPointers addItem: &last]; + + i++; + for (; i < length && isdigit(_types[i]); i++); + + last = _types + i; + i--; + } else if (_types[i] == '{') { + size_t depth = 0; + + for (; i < length; i++) { + if (_types[i] == '{') + depth++; + else if (_types[i] == '}') { + if (--depth == 0) + break; + } + } + + if (depth != 0) + @throw [OFInvalidFormatException + exception]; + } else if (_types[i] == '(') { + size_t depth = 0; + + for (; i < length; i++) { + if (_types[i] == '(') + depth++; + else if (_types[i] == ')') { + if (--depth == 0) + break; + } + } + + if (depth != 0) + @throw [OFInvalidFormatException + exception]; + } + } + + if (last < _types + length) + @throw [OFInvalidFormatException exception]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_typesPointers release]; + + [super dealloc]; +} + +- (size_t)numberOfArguments +{ + return [_typesPointers count] - 1; +} + +- (const char *)methodReturnType +{ + return *(const char **)[_typesPointers firstItem]; +} + +- (const char *)argumentTypeAtIndex: (size_t)index +{ + return *(const char **)[_typesPointers itemAtIndex: index + 1]; +} +@end Index: src/OFObject+KeyValueCoding.m ================================================================== --- src/OFObject+KeyValueCoding.m +++ src/OFObject+KeyValueCoding.m @@ -18,38 +18,29 @@ #include #import "OFObject.h" #import "OFObject+KeyValueCoding.h" +#import "OFMethodSignature.h" #import "OFString.h" #import "OFNumber.h" #import "OFInvalidArgumentException.h" #import "OFOutOfMemoryException.h" #import "OFUndefinedKeyException.h" int _OFObject_KeyValueCoding_reference; -static char OF_INLINE -nextType(const char **typeEncoding) -{ - char ret = *(*typeEncoding)++; - - while (**typeEncoding >= '0' && **typeEncoding <= '9') - (*typeEncoding)++; - - return ret; -} - @implementation OFObject (KeyValueCoding) - (id)valueForKey: (OFString *)key { SEL selector = sel_registerName([key UTF8String]); - const char *typeEncoding = [self typeEncodingForSelector: selector]; + OFMethodSignature *methodSignature = + [self methodSignatureForSelector: selector]; id ret; - if (typeEncoding == NULL) { + if (methodSignature == nil) { size_t keyLength; char *name; if ((keyLength = [key UTF8StringLength]) < 1) return [self valueForUndefinedKey: key]; @@ -68,18 +59,28 @@ selector = sel_registerName(name); } @finally { free(name); } - typeEncoding = [self typeEncodingForSelector: selector]; + methodSignature = [self methodSignatureForSelector: selector]; + + if (methodSignature == NULL) + return [self valueForUndefinedKey: key]; - if (typeEncoding == NULL || - *typeEncoding == '@' || *typeEncoding == '#') + switch (*[methodSignature methodReturnType]) { + case '@': + case '#': return [self valueForUndefinedKey: key]; + } } - switch (nextType(&typeEncoding)) { + if ([methodSignature numberOfArguments] != 2 || + *[methodSignature argumentTypeAtIndex: 0] != '@' || + *[methodSignature argumentTypeAtIndex: 1] != ':') + return [self valueForUndefinedKey: key]; + + switch (*[methodSignature methodReturnType]) { case '@': case '#': ret = [self performSelector: selector]; break; #define CASE(encoding, type, method) \ @@ -106,14 +107,10 @@ #undef CASE default: return [self valueForUndefinedKey: key]; } - if (nextType(&typeEncoding) != '@' || nextType(&typeEncoding) != ':' || - *typeEncoding != 0) - return [self valueForUndefinedKey: key]; - return ret; } - (id)valueForUndefinedKey: (OFString *)key { @@ -125,12 +122,12 @@ forKey: (OFString *)key { size_t keyLength; char *name; SEL selector; - const char *typeEncoding; - char valueType; + OFMethodSignature *methodSignature; + const char *valueType; if ((keyLength = [key UTF8StringLength]) < 1) { [self setValue: value forUndefinedKey: key]; return; @@ -150,33 +147,30 @@ selector = sel_registerName(name); } @finally { free(name); } - typeEncoding = [self typeEncodingForSelector: selector]; + methodSignature = [self methodSignatureForSelector: selector]; - if (typeEncoding == NULL || nextType(&typeEncoding) != 'v' || - nextType(&typeEncoding) != '@' || nextType(&typeEncoding) != ':') { + if (methodSignature == nil || + [methodSignature numberOfArguments] != 3 || + *[methodSignature methodReturnType] != 'v' || + *[methodSignature argumentTypeAtIndex: 0] != '@' || + *[methodSignature argumentTypeAtIndex: 1] != ':') { [self setValue: value forUndefinedKey: key]; return; } - valueType = nextType(&typeEncoding); - - if (*typeEncoding != 0) { - [self setValue: value - forUndefinedKey: key]; - return; - } - - if (valueType != '@' && valueType != '#' && value == nil) { + valueType = [methodSignature argumentTypeAtIndex: 2]; + + if (*valueType != '@' && *valueType != '#' && value == nil) { [self setNilValueForKey: key]; return; } - switch (valueType) { + switch (*valueType) { case '@': case '#': { void (*setter)(id, SEL, id) = (void (*)(id, SEL, id)) [self methodForSelector: selector]; Index: src/OFObject.h ================================================================== --- src/OFObject.h +++ src/OFObject.h @@ -186,10 +186,11 @@ }; return rectangle; } +@class OFMethodSignature; @class OFString; @class OFThread; /*! * @protocol OFObject OFObject.h ObjFW/OFObject.h @@ -251,18 +252,10 @@ * @param selector The selector for which the method should be returned * @return The implementation for the specified selector */ - (nullable IMP)methodForSelector: (SEL)selector; -/*! - * @brief Returns the type encoding for the specified selector. - * - * @param selector The selector for which the type encoding should be returned - * @return The type encoding for the specified selector - */ -- (nullable const char *)typeEncodingForSelector: (SEL)selector; - /*! * @brief Performs the specified selector. * * @param selector The selector to perform * @return The object returned by the method specified by the selector @@ -501,17 +494,20 @@ * or `nil` if it isn't implemented */ + (nullable IMP)instanceMethodForSelector: (SEL)selector; /*! - * @brief Returns the type encoding of the instance method for the specified + * @brief Returns the method signature of the instance method for the specified * selector. * - * @param selector The selector for which the type encoding should be returned - * @return The type encoding of the instance method for the specified selector + * @param selector The selector for which the method signature should be + * returned + * @return The method signature of the instance method for the specified + * selector */ -+ (nullable const char *)typeEncodingForInstanceSelector: (SEL)selector; ++ (nullable OFMethodSignature *) + instanceMethodSignatureForSelector: (SEL)selector; /*! * @brief Returns a description for the class, which is usually the class name. * * This is mostly for debugging purposes. @@ -539,42 +535,10 @@ * @return The old implementation */ + (nullable IMP)replaceInstanceMethod: (SEL)selector withMethodFromClass: (Class)class_; -/*! - * @brief Replaces or adds a class method. - * - * If the method already exists, it is replaced and the old implementation - * returned. If the method does not exist, it is added with the specified type - * encoding. - * - * @param selector The selector for the new method - * @param implementation The implementation for the new method - * @param typeEncoding The type encoding for the new method - * @return The old implementation or `nil` if the method was added - */ -+ (nullable IMP)replaceClassMethod: (SEL)selector - withImplementation: (IMP)implementation - typeEncoding: (const char *)typeEncoding; - -/*! - * @brief Replaces or adds an instance method. - * - * If the method already exists, it is replaced and the old implementation - * returned. If the method does not exist, it is added with the specified type - * encoding. - * - * @param selector The selector for the new method - * @param implementation The implementation for the new method - * @param typeEncoding The type encoding for the new method - * @return The old implementation or `nil` if the method was added - */ -+ (nullable IMP)replaceInstanceMethod: (SEL)selector - withImplementation: (IMP)implementation - typeEncoding: (const char *)typeEncoding; - /*! * @brief Adds all methods from the specified class to the class that is the * receiver. * * Methods implemented by the receiving class itself will not be overridden, @@ -636,10 +600,19 @@ * * @return An initialized object */ - init; +/*! + * @brief Returns the method signature for the specified selector. + * + * @param selector The selector for which the method signature should be + * returned + * @return The method signature for the specified selector + */ +- (nullable OFMethodSignature *)methodSignatureForSelector: (SEL)selector; + /*! * @brief Returns the name of the object's class. * * @return The name of the object's class */ Index: src/OFObject.m ================================================================== --- src/OFObject.m +++ src/OFObject.m @@ -29,13 +29,14 @@ #endif #import "OFObject.h" #import "OFArray.h" #import "OFLocalization.h" -#import "OFTimer.h" +#import "OFMethodSignature.h" #import "OFRunLoop.h" #import "OFThread.h" +#import "OFTimer.h" #import "OFAllocFailedException.h" #import "OFEnumerationMutationException.h" #import "OFInitializationFailedException.h" #import "OFInvalidArgumentException.h" @@ -95,10 +96,25 @@ static struct { Class isa; } allocFailedException; uint32_t of_hash_seed; + +static const char * +typeEncodingForSelector(Class class, SEL selector) +{ +#if defined(OF_OBJFW_RUNTIME) + return class_getMethodTypeEncoding(class, selector); +#else + Method m; + + if ((m = class_getInstanceMethod(class, selector)) == NULL) + return NULL; + + return method_getTypeEncoding(m); +#endif +} #if !defined(OF_APPLE_RUNTIME) || defined(__OBJC2__) static void uncaughtExceptionHandler(id exception) { @@ -318,22 +334,18 @@ + (IMP)instanceMethodForSelector: (SEL)selector { return class_getMethodImplementation(self, selector); } -+ (const char *)typeEncodingForInstanceSelector: (SEL)selector -{ -#if defined(OF_OBJFW_RUNTIME) - return class_getMethodTypeEncoding(self, selector); -#else - Method m; - - if ((m = class_getInstanceMethod(self, selector)) == NULL) - return NULL; - - return method_getTypeEncoding(m); -#endif ++ (OFMethodSignature *)instanceMethodSignatureForSelector: (SEL)selector +{ + const char *typeEncoding = typeEncodingForSelector(self, selector); + + if (typeEncoding == NULL) + return nil; + + return [OFMethodSignature signatureWithObjCTypes: typeEncoding]; } + (OFString *)description { return [self className]; @@ -340,49 +352,21 @@ } + (IMP)replaceClassMethod: (SEL)selector withMethodFromClass: (Class)class { - IMP newImp; - const char *typeEncoding; - - newImp = [class methodForSelector: selector]; - typeEncoding = [class typeEncodingForSelector: selector]; - - return [self replaceClassMethod: selector - withImplementation: newImp - typeEncoding: typeEncoding]; + return class_replaceMethod(object_getClass(self), selector, + [class methodForSelector: selector], + typeEncodingForSelector(object_getClass(class), selector)); } + (IMP)replaceInstanceMethod: (SEL)selector withMethodFromClass: (Class)class { - IMP newImp; - const char *typeEncoding; - - newImp = [class instanceMethodForSelector: selector]; - typeEncoding = [class typeEncodingForInstanceSelector: selector]; - - return [self replaceInstanceMethod: selector - withImplementation: newImp - typeEncoding: typeEncoding]; -} - -+ (IMP)replaceInstanceMethod: (SEL)selector - withImplementation: (IMP)implementation - typeEncoding: (const char *)typeEncoding -{ - return class_replaceMethod(self, selector, implementation, - typeEncoding); -} - -+ (IMP)replaceClassMethod: (SEL)selector - withImplementation: (IMP)implementation - typeEncoding: (const char *)typeEncoding -{ - return class_replaceMethod(object_getClass(self), selector, - implementation, typeEncoding); + return class_replaceMethod(self, selector, + [class instanceMethodForSelector: selector], + typeEncodingForSelector(class, selector)); } + (void)inheritMethodsFromClass: (Class)class { Class superclass = [self superclass]; @@ -778,23 +762,19 @@ objc_autoreleasePoolPop(pool); } #endif -- (const char *)typeEncodingForSelector: (SEL)selector -{ -#if defined(OF_OBJFW_RUNTIME) - return class_getMethodTypeEncoding(object_getClass(self), selector); -#else - Method m; - - if ((m = class_getInstanceMethod(object_getClass(self), - selector)) == NULL) - return NULL; - - return method_getTypeEncoding(m); -#endif +- (OFMethodSignature *)methodSignatureForSelector: (SEL)selector +{ + const char *typeEncoding = + typeEncodingForSelector(object_getClass(self), selector); + + if (typeEncoding == NULL) + return nil; + + return [OFMethodSignature signatureWithObjCTypes: typeEncoding]; } - (bool)isEqual: (id)object { return (self == object); Index: src/ObjFW.h ================================================================== --- src/ObjFW.h +++ src/ObjFW.h @@ -30,14 +30,18 @@ #import "OFMapTable.h" #import "OFSet.h" #import "OFCountedSet.h" +#import "OFPair.h" +#import "OFTriple.h" + #import "OFEnumerator.h" #import "OFNull.h" +#import "OFMethodSignature.h" #import "OFIntrospection.h" #import "OFNumber.h" #import "OFDate.h" #import "OFURL.h" Index: tests/ForwardingTests.m ================================================================== --- tests/ForwardingTests.m +++ tests/ForwardingTests.m @@ -71,13 +71,12 @@ + (BOOL)resolveClassMethod: (SEL)selector { forwardings++; if (sel_isEqual(selector, @selector(test))) { - [self replaceClassMethod: @selector(test) - withImplementation: (IMP)test - typeEncoding: "v#:"]; + class_replaceMethod(object_getClass(self), @selector(test), + (IMP)test, "v#:"); return YES; } return NO; } @@ -85,13 +84,11 @@ + (BOOL)resolveInstanceMethod: (SEL)selector { forwardings++; if (sel_isEqual(selector, @selector(test))) { - [self replaceInstanceMethod: @selector(test) - withImplementation: (IMP)test - typeEncoding: "v@:"]; + class_replaceMethod(self, @selector(test), (IMP)test, "v@:"); return YES; } return NO; } Index: tests/Makefile ================================================================== --- tests/Makefile +++ tests/Makefile @@ -16,10 +16,11 @@ OFDictionaryTests.m \ OFHTTPCookieTests.m \ OFHTTPCookieManagerTests.m \ OFJSONTests.m \ OFListTests.m \ + OFMethodSignatureTests.m \ OFNumberTests.m \ OFObjectTests.m \ OFSetTests.m \ OFStreamTests.m \ OFStringTests.m \ ADDED tests/OFMethodSignatureTests.m Index: tests/OFMethodSignatureTests.m ================================================================== --- tests/OFMethodSignatureTests.m +++ tests/OFMethodSignatureTests.m @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 + * 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. + */ + +#include "config.h" + +#include + +#import "OFMethodSignature.h" +#import "OFAutoreleasePool.h" + +#import "OFInvalidFormatException.h" + +#import "TestsAppDelegate.h" + +static OFString *module = @"OFMethodSignature"; + +@implementation TestsAppDelegate (OFMethodSignatureTests) +- (void)methodSignatureTests +{ + OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; + OFMethodSignature *ms; + + TEST(@"-[:signatureWithObjCTypes:] #1", + (ms = [OFMethodSignature signatureWithObjCTypes: + "i28@0:8S16*20"]) && [ms numberOfArguments] == 4 && + strcmp([ms methodReturnType], "i") == 0 && + strcmp([ms argumentTypeAtIndex: 0], "@") == 0 && + strcmp([ms argumentTypeAtIndex: 1], ":") == 0 && + strcmp([ms argumentTypeAtIndex: 2], "S") == 0 && + strcmp([ms argumentTypeAtIndex: 3], "*") == 0) + + TEST(@"-[signatureWithObjCTypes:] #2", + (ms = [OFMethodSignature signatureWithObjCTypes: + "{s0=csi(u1={s2=iii{s3=(u4=ic^v*)}})}24@0:8" + "^{s0=csi(u1={s2=iii{s3=(u4=ic^v*)}})}16"]) && + [ms numberOfArguments] == 3 && + strcmp([ms methodReturnType], + "{s0=csi(u1={s2=iii{s3=(u4=ic^v*)}})}") == 0 && + strcmp([ms argumentTypeAtIndex: 0], "@") == 0 && + strcmp([ms argumentTypeAtIndex: 1], ":") == 0 && + strcmp([ms argumentTypeAtIndex: 2], + "^{s0=csi(u1={s2=iii{s3=(u4=ic^v*)}})}") == 0) + + EXPECT_EXCEPTION(@"-[signatureWithObjCTypes:] #3", + OFInvalidFormatException, + [OFMethodSignature signatureWithObjCTypes: "{ii"]) + + EXPECT_EXCEPTION(@"-[signatureWithObjCTypes:] #4", + OFInvalidFormatException, + [OFMethodSignature signatureWithObjCTypes: ""]) + + EXPECT_EXCEPTION(@"-[signatureWithObjCTypes:] #5", + OFInvalidFormatException, + [OFMethodSignature signatureWithObjCTypes: "0"]) + + EXPECT_EXCEPTION(@"-[signatureWithObjCTypes:] #6", + OFInvalidFormatException, + [OFMethodSignature signatureWithObjCTypes: "{{}0"]) + + [pool drain]; +} +@end Index: tests/TestsAppDelegate.h ================================================================== --- tests/TestsAppDelegate.h +++ tests/TestsAppDelegate.h @@ -125,10 +125,14 @@ @end @interface TestsAppDelegate (OFMD5HashTests) - (void)MD5HashTests; @end + +@interface TestsAppDelegate (OFMethodSignatureTests) +- (void)methodSignatureTests; +@end @interface TestsAppDelegate (OFNumberTests) - (void)numberTests; @end Index: tests/TestsAppDelegate.m ================================================================== --- tests/TestsAppDelegate.m +++ tests/TestsAppDelegate.m @@ -378,10 +378,11 @@ [[OFFileManager defaultManager] changeCurrentDirectoryPath: @"/apps/objfw-tests"]; #endif [self runtimeTests]; + [self methodSignatureTests]; [self forwardingTests]; [self objectTests]; #ifdef OF_HAVE_BLOCKS [self blockTests]; #endif