/ NSMethodSignature.m
NSMethodSignature.m
1 // 2 // NSMethodSignature.m 3 // CoreFoundation 4 // 5 // Copyright (c) 2014 Apportable. All rights reserved. 6 // 7 8 #import <Foundation/NSMethodSignature.h> 9 #import "NSObjCRuntimeInternal.h" 10 #import "NSObjectInternal.h" 11 12 #define ALIGN_TO(value, alignment) \ 13 (((value) % (alignment)) ? \ 14 ((value) + (alignment) - ((value) % (alignment))) : \ 15 (value) \ 16 ) 17 18 extern void __CFStringAppendBytes(CFMutableStringRef, const char *, CFIndex, CFStringEncoding); 19 20 @implementation NSMethodSignature 21 22 - (instancetype)initWithObjCTypes:(const char *)types 23 { 24 self = [super init]; 25 26 if (self == nil) 27 { 28 return nil; 29 } 30 31 _count = 0; 32 _frameLength = 0; 33 _typeString = CFStringCreateMutable(NULL, strlen(types)); 34 // strlen(types) is a safe overapproximation to the actual number of types. 35 _types = calloc(sizeof(NSMethodType), strlen(types)); 36 37 if (UNLIKELY(_types == NULL)) 38 { 39 [self release]; 40 return nil; 41 } 42 43 const char *currentType = types; 44 const char *nextType = types; 45 46 #ifdef __LP64__ 47 // On x86-64, the first few arguments are passed in registers as long as 48 // they satisfy certain conditions. __CF_forwarding_prep and __invoke__ 49 // pack and unpack all register values into a 0xe0-sized block preceeding 50 // the actual stack frame contents. This means frameLength always starts 51 // at 0xe0 and grows from there if there are any on-stack arguments. 52 unsigned short usedGPRegisters = 0; 53 unsigned short usedSSERegisters = 0; 54 _frameLength = 0xe0; 55 #endif 56 57 while (nextType[0]) 58 { 59 NSMethodType *ms = &_types[_count]; 60 61 if (nextType[0] == '>' && nextType[1] == '\0') { 62 // handle the case where we initialize the signature using the extended type description of a block parameter 63 // (this is so that we don't have to copy the string and null terminate it in `_signatureForBlockAtArgumentIndex:`) 64 break; 65 } 66 67 currentType = nextType; 68 nextType = NSGetSizeAndAlignment(currentType, &ms->size, &ms->alignment); 69 70 // append the type info WITHOUT the extended type info 71 __CFStringAppendBytes(_typeString, currentType, nextType - currentType, kCFStringEncodingUTF8); 72 73 // we need to be able to handle extended type encodings. 74 // these don't affect the size or alignment of the type, but they are considered part of the type and we need to store them 75 if (nextType[0] == '"') { 76 // this describes what kind of object the argument expects. 77 // this case is simple; no nesting possible 78 nextType = strchr(nextType + 1, '"'); 79 if (!nextType) { 80 // no closing quotation mark? invalid type encoding. 81 [NSException raise: NSInvalidArgumentException format: @"Invalid type encoding: expected closing quotation mark"]; 82 } 83 ++nextType; // skip the closing quotation mark 84 } else if (nextType[0] == '<') { 85 // this describes the block signature. 86 // this case is little more complicated; nesting is possible 87 size_t nestLevel = 1; 88 ++nextType; 89 for (; nestLevel > 0 && nextType[0] != '\0'; ++nextType) { 90 if (nextType[0] == '<') { 91 ++nestLevel; 92 } else if (nextType[0] == '>') { 93 --nestLevel; 94 } 95 } 96 if (nestLevel > 0) { 97 // still missing a closing angle bracket? invalid type encoding. 98 [NSException raise: NSInvalidArgumentException format: @"Invalid type encoding: expected closing angle bracket"]; 99 } 100 } 101 102 // there might other extended type encodings i haven't encountered, but those two should be the two most important ones 103 104 ms->type = calloc(nextType - currentType + 1, 1); 105 if (UNLIKELY(ms->type == NULL)) 106 { 107 [self release]; 108 return nil; 109 } 110 strncpy(ms->type, currentType, nextType - currentType); 111 112 // Skip advisory size 113 // (but record the size start offset so we can append into the type string) 114 const char* sizeStart = nextType; 115 strtol(nextType, (char **)&nextType, 10); 116 117 // append the size info 118 if (nextType - sizeStart > 0) { 119 __CFStringAppendBytes(_typeString, sizeStart, nextType - sizeStart, kCFStringEncodingUTF8); 120 } 121 122 NSUInteger frameAlignment = MAX(ms->alignment, sizeof(int)); 123 NSUInteger frameSize = ALIGN_TO(ms->size, frameAlignment); 124 125 if (_count == 0) 126 { 127 // Determine whether the method is stret, based on the 128 // type of the return value. 129 switch (*stripQualifiersAndComments(_types[0].type)) 130 { 131 case _C_STRUCT_B: 132 { 133 if (frameSize > sizeof(int)) 134 { 135 // Account for the stret return pointer. 136 _frameLength += sizeof(void *); 137 _stret = YES; 138 #if __LP64__ 139 // on x86_64, the first GP register (rdi) is used to pass the stret pointer 140 ++usedGPRegisters; 141 #endif 142 } 143 break; 144 } 145 146 default: 147 // All other cases are non-stret. 148 break; 149 } 150 } 151 else 152 { 153 #if __arm__ 154 _frameLength = ALIGN_TO(_frameLength, frameAlignment); 155 #elif __LP64__ 156 // FIXME: This is far from being a complete implementation of 157 // the x86-64 calling convention. 158 BOOL isFP = currentType[0] == _C_FLT || currentType[0] == _C_DBL; 159 if (isFP) { 160 unsigned short registersNeeded = ALIGN_TO(frameSize, 16) / 16; 161 if (usedSSERegisters + registersNeeded > 8) { 162 _types[_count].offset = _frameLength; 163 _frameLength += frameSize; 164 } else { 165 _types[_count].offset = 0x30 + usedSSERegisters * 16; 166 usedSSERegisters += registersNeeded; 167 } 168 } else { 169 unsigned short registersNeeded = ALIGN_TO(frameSize, 8) / 8; 170 if (usedGPRegisters + registersNeeded > 6 || frameSize > 16) { 171 _types[_count].offset = _frameLength; 172 _frameLength += frameSize; 173 } else { 174 _types[_count].offset = usedGPRegisters * 8; 175 usedGPRegisters += registersNeeded; 176 } 177 } 178 #else 179 _types[_count].offset = _frameLength; 180 _frameLength += frameSize; 181 #endif 182 } 183 184 _count++; 185 } 186 187 // Check whether the method is oneway by reading all the 188 // qualifiers of the return type. 189 static const char *qualifiers = "nNoOrRV"; 190 char *cur = _types[0].type; 191 while (strchr(qualifiers, *cur)) { 192 if (*cur == 'V') { 193 _isOneway = YES; 194 break; 195 } 196 cur++; 197 } 198 199 return self; 200 } 201 202 + (NSMethodSignature *)signatureWithObjCTypes:(const char *)types 203 { 204 return [[[self alloc] initWithObjCTypes:types] autorelease]; 205 } 206 207 - (void)dealloc 208 { 209 for (NSUInteger idx = 0; idx < _count; idx++) 210 { 211 if (_types[idx].type != NULL) 212 { 213 free(_types[idx].type); 214 } 215 } 216 217 if (_types != NULL) 218 { 219 free(_types); 220 } 221 222 CFRelease(_typeString); 223 [super dealloc]; 224 } 225 226 - (NSUInteger)numberOfArguments 227 { 228 return _count - 1; 229 } 230 231 - (const char *)getArgumentTypeAtIndex:(NSUInteger)idx 232 { 233 return _types[idx + 1].type; 234 } 235 236 - (NSUInteger)frameLength 237 { 238 return _frameLength; 239 } 240 241 - (BOOL)isOneway 242 { 243 return _isOneway; 244 } 245 246 - (const char *)methodReturnType 247 { 248 return _types[0].type; 249 } 250 251 - (NSUInteger)methodReturnLength 252 { 253 return _types[0].size; 254 } 255 256 - (NSMethodType *)_argInfo:(NSUInteger)index 257 { 258 return &_types[index]; 259 } 260 261 - (BOOL)_stret 262 { 263 return _stret; 264 } 265 266 - (CFStringRef) _typeString 267 { 268 return _typeString; 269 } 270 271 - (NSMethodSignature*)_signatureForBlockAtArgumentIndex: (NSUInteger)index 272 { 273 const char* argType = _types[index].type; 274 argType = strchr(argType, '<'); 275 if (!argType) { 276 return nil; 277 } 278 return [NSMethodSignature signatureWithObjCTypes: argType + 1]; 279 } 280 281 - (Class)_classForObjectAtArgumentIndex: (NSUInteger)index 282 { 283 const char* argType = _types[index].type; 284 argType = strchr(argType, '"'); 285 if (!argType) { 286 return nil; 287 } 288 return objc_getClass(argType + 1); 289 } 290 291 @end