FLEXTypeEncodingParser.m 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828
  1. //
  2. // FLEXTypeEncodingParser.m
  3. // FLEX
  4. //
  5. // Created by Tanner Bennett on 8/22/19.
  6. // Copyright © 2019 Flipboard. All rights reserved.
  7. //
  8. #import "FLEXTypeEncodingParser.h"
  9. #import "FLEXRuntimeUtility.h"
  10. #define S(__ch) ({ \
  11. unichar __c = __ch; \
  12. [[NSString alloc] initWithCharacters:&__c length:1]; \
  13. })
  14. BOOL FLEXGetSizeAndAlignment(const char *type, NSUInteger *sizep, NSUInteger *alignp) {
  15. NSInteger size = 0, align = 0;
  16. size = [FLEXTypeEncodingParser sizeForTypeEncoding:@(type) alignment:&align];
  17. if (size == -1) {
  18. return NO;
  19. }
  20. if (sizep) {
  21. *sizep = (NSUInteger)size;
  22. }
  23. if (alignp) {
  24. *alignp = (NSUInteger)size;
  25. }
  26. return YES;
  27. }
  28. @interface FLEXTypeEncodingParser ()
  29. @property (nonatomic, readonly) NSScanner *scan;
  30. @property (nonatomic, readonly) NSString *scanned;
  31. @property (nonatomic, readonly) NSString *unscanned;
  32. @property (nonatomic, readonly) char nextChar;
  33. /// Replacements are made to this string as we scan as needed
  34. @property (nonatomic) NSMutableString *cleaned;
  35. /// Offset for \e further replacements to be made within \c cleaned
  36. @property (nonatomic, readonly) NSUInteger cleanedReplacingOffset;
  37. @end
  38. @implementation FLEXTypeEncodingParser
  39. - (NSString *)scanned {
  40. return [self.scan.string substringToIndex:self.scan.scanLocation];
  41. }
  42. - (NSString *)unscanned {
  43. return [self.scan.string substringFromIndex:self.scan.scanLocation];
  44. }
  45. #pragma mark Initialization
  46. - (id)initWithObjCTypes:(NSString *)typeEncoding {
  47. self = [super init];
  48. if (self) {
  49. _scan = [NSScanner scannerWithString:typeEncoding];
  50. _scan.caseSensitive = YES;
  51. _cleaned = typeEncoding.mutableCopy;
  52. }
  53. return self;
  54. }
  55. #pragma mark Public
  56. + (BOOL)methodTypeEncodingSupported:(NSString *)typeEncoding cleaned:(NSString * __autoreleasing *)cleanedEncoding {
  57. FLEXTypeEncodingParser *parser = [[self alloc] initWithObjCTypes:typeEncoding];
  58. while (!parser.scan.isAtEnd) {
  59. BOOL containsUnion = NO;
  60. if ([parser parseNextType:nil hasUnion:&containsUnion] == -1 || containsUnion) {
  61. return NO;
  62. }
  63. }
  64. if (cleanedEncoding) {
  65. *cleanedEncoding = parser.cleaned.copy;
  66. }
  67. return YES;
  68. }
  69. + (NSString *)type:(NSString *)typeEncoding forMethodArgumentAtIndex:(NSUInteger)idx {
  70. FLEXTypeEncodingParser *parser = [[self alloc] initWithObjCTypes:typeEncoding];
  71. // Scan up to the argument we want
  72. for (NSUInteger i = 0; i < idx; i++) {
  73. if (![parser scanPastArg]) {
  74. [NSException raise:NSRangeException
  75. format:@"Index %lu out of bounds for type encoding '%@'", idx, typeEncoding];
  76. }
  77. }
  78. return [parser scanArg];
  79. }
  80. + (ssize_t)size:(NSString *)typeEncoding forMethodArgumentAtIndex:(NSUInteger)idx {
  81. return [self sizeForTypeEncoding:[self type:typeEncoding forMethodArgumentAtIndex:idx] alignment:nil];
  82. }
  83. + (ssize_t)sizeForTypeEncoding:(NSString *)type alignment:(ssize_t *)alignOut {
  84. return [self sizeForTypeEncoding:type alignment:alignOut unaligned:NO];
  85. }
  86. + (ssize_t)sizeForTypeEncoding:(NSString *)type alignment:(ssize_t *)alignOut unaligned:(BOOL)unaligned {
  87. return [self parseType:type alignment:alignOut unaligned:unaligned hasUnion:nil];
  88. }
  89. /// @return the size of the type
  90. + (ssize_t)parseType:(NSString *)type
  91. alignment:(ssize_t *)alignOut
  92. unaligned:(BOOL)unaligned
  93. hasUnion:(BOOL *)containsUnion {
  94. ssize_t align = 0;
  95. ssize_t size = [[[self alloc] initWithObjCTypes:type] parseNextType:&align hasUnion:containsUnion];
  96. if (size == -1 || size == 0) {
  97. return size;
  98. }
  99. if (alignOut) {
  100. *alignOut = align;
  101. }
  102. if (unaligned) {
  103. return size;
  104. } else {
  105. size += size % align;
  106. return size;
  107. }
  108. }
  109. #pragma mark Private
  110. - (NSCharacterSet *)identifierFirstCharCharacterSet {
  111. static NSCharacterSet *identifierFirstSet = nil;
  112. static dispatch_once_t onceToken;
  113. dispatch_once(&onceToken, ^{
  114. NSString *allowed = @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$";
  115. identifierFirstSet = [NSCharacterSet characterSetWithCharactersInString:allowed];
  116. });
  117. return identifierFirstSet;
  118. }
  119. - (NSCharacterSet *)identifierCharacterSet {
  120. static NSCharacterSet *identifierSet = nil;
  121. static dispatch_once_t onceToken;
  122. dispatch_once(&onceToken, ^{
  123. NSString *allowed = @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$1234567890";
  124. identifierSet = [NSCharacterSet characterSetWithCharactersInString:allowed];
  125. });
  126. return identifierSet;
  127. }
  128. - (char)nextChar {
  129. NSScanner *scan = self.scan;
  130. return [scan.string characterAtIndex:scan.scanLocation];
  131. }
  132. /// For scanning struct/class names
  133. - (NSString *)scanIdentifier {
  134. NSString *prefix = nil, *suffix = nil;
  135. // Identifiers cannot start with a number
  136. if (![self.scan scanCharactersFromSet:self.identifierFirstCharCharacterSet intoString:&prefix]) {
  137. return nil;
  138. }
  139. // Optional because identifier may just be one character
  140. [self.scan scanCharactersFromSet:self.identifierCharacterSet intoString:&suffix];
  141. if (suffix) {
  142. return [prefix stringByAppendingString:suffix];
  143. }
  144. return prefix;
  145. }
  146. /// @return the size in bytes
  147. - (ssize_t)sizeForType:(FLEXTypeEncoding)type {
  148. switch (type) {
  149. case FLEXTypeEncodingChar: return sizeof(char);
  150. case FLEXTypeEncodingInt: return sizeof(int);
  151. case FLEXTypeEncodingShort: return sizeof(short);
  152. case FLEXTypeEncodingLong: return sizeof(long);
  153. case FLEXTypeEncodingLongLong: return sizeof(long long);
  154. case FLEXTypeEncodingUnsignedChar: return sizeof(unsigned char);
  155. case FLEXTypeEncodingUnsignedInt: return sizeof(unsigned int);
  156. case FLEXTypeEncodingUnsignedShort: return sizeof(unsigned short);
  157. case FLEXTypeEncodingUnsignedLong: return sizeof(unsigned long);
  158. case FLEXTypeEncodingUnsignedLongLong: return sizeof(unsigned long long);
  159. case FLEXTypeEncodingFloat: return sizeof(float);
  160. case FLEXTypeEncodingDouble: return sizeof(double);
  161. case FLEXTypeEncodingLongDouble: return sizeof(long double);
  162. case FLEXTypeEncodingCBool: return sizeof(_Bool);
  163. case FLEXTypeEncodingVoid: return 0;
  164. case FLEXTypeEncodingCString: return sizeof(char *);
  165. case FLEXTypeEncodingObjcObject: return sizeof(id);
  166. case FLEXTypeEncodingObjcClass: return sizeof(Class);
  167. case FLEXTypeEncodingSelector: return sizeof(SEL);
  168. // Unknown / '?' is typically a pointer. In the rare case
  169. // it isn't, such as in '{?=...}', it is never passed here.
  170. case FLEXTypeEncodingUnknown:
  171. case FLEXTypeEncodingPointer: return sizeof(uintptr_t);
  172. default: return -1;
  173. }
  174. }
  175. /// @param containsUnion an out param indicating whether the given type contains
  176. /// a union, exlcuding pointers to/with unions. Must be initialized to \c NO
  177. /// @return the (unaligned) size of the type in bytes
  178. - (ssize_t)parseNextType:(ssize_t *)alignment hasUnion:(BOOL *)containsUnion {
  179. NSUInteger start = self.scan.scanLocation;
  180. // Check for void first
  181. if ([self scanChar:FLEXTypeEncodingVoid]) {
  182. // Skip argument frame for method signatures
  183. [self scanSize];
  184. return 0;
  185. }
  186. // Scan optional const
  187. [self scanChar:FLEXTypeEncodingConst];
  188. // Check for pointer, then scan next
  189. if ([self scanChar:FLEXTypeEncodingPointer]) {
  190. // Recurse to scan something else
  191. NSUInteger pointerTypeStart = self.scan.scanLocation;
  192. if ([self scanPastArg]) {
  193. // Make sure the pointer type is supported, and clean it if not
  194. NSUInteger pointerTypeLength = self.scan.scanLocation - pointerTypeStart;
  195. NSString *pointerType = [self.scan.string
  196. substringWithRange:NSMakeRange(pointerTypeStart, pointerTypeLength)
  197. ];
  198. BOOL pointeeHasUnion = NO;
  199. BOOL unsupported = -1 == [self.class
  200. parseType:pointerType
  201. alignment:nil
  202. unaligned:NO
  203. hasUnion:&pointeeHasUnion
  204. ];
  205. // Clean the type if it is unsupported or contains a union
  206. // (Unions are supported by NSGetSizeAndAlignment but not
  207. // supported by NSMethodSignature for some reason)
  208. if (unsupported || pointeeHasUnion) {
  209. // We don't set *containsUnion here since we can clean it out
  210. NSString *cleaned = [self cleanPointeeTypeAtLocation:pointerTypeStart];
  211. [self.cleaned replaceCharactersInRange:NSMakeRange(
  212. pointerTypeStart - self.cleanedReplacingOffset, pointerTypeLength
  213. ) withString:cleaned];
  214. }
  215. // Skip optional frame offset
  216. [self scanSize];
  217. ssize_t size = [self sizeForType:FLEXTypeEncodingPointer];
  218. if (alignment) {
  219. *alignment = size;
  220. }
  221. return size;
  222. } else {
  223. // Scan failed, abort
  224. self.scan.scanLocation = start;
  225. return -1;
  226. }
  227. }
  228. // Check for struct/union/array
  229. char next = self.nextChar;
  230. BOOL didScanSUA = YES, structOrUnion = NO, isUnion = NO;
  231. FLEXTypeEncoding opening = FLEXTypeEncodingNull, closing = FLEXTypeEncodingNull;
  232. switch (next) {
  233. case FLEXTypeEncodingStructBegin:
  234. structOrUnion = YES;
  235. opening = FLEXTypeEncodingStructBegin;
  236. closing = FLEXTypeEncodingStructEnd;
  237. break;
  238. case FLEXTypeEncodingUnionBegin:
  239. structOrUnion = isUnion = YES;
  240. opening = FLEXTypeEncodingUnionBegin;
  241. closing = FLEXTypeEncodingUnionEnd;
  242. break;
  243. case FLEXTypeEncodingArrayBegin:
  244. opening = FLEXTypeEncodingArrayBegin;
  245. closing = FLEXTypeEncodingArrayEnd;
  246. break;
  247. default:
  248. didScanSUA = NO;
  249. break;
  250. }
  251. if (didScanSUA) {
  252. // We found a union, set this flag
  253. if (isUnion && containsUnion) {
  254. *containsUnion = YES;
  255. }
  256. NSUInteger backup = self.scan.scanLocation;
  257. // Ensure we have a closing tag
  258. if (![self scanPair:opening close:closing]) {
  259. // Scan failed, abort
  260. self.scan.scanLocation = start;
  261. return -1;
  262. }
  263. // Move cursor just after opening tag (struct/union/array)
  264. NSInteger arrayCount = -1;
  265. self.scan.scanLocation = backup + 1;
  266. if (!structOrUnion) {
  267. arrayCount = [self scanSize];
  268. if (!arrayCount || self.nextChar == FLEXTypeEncodingArrayEnd) {
  269. // Malformed array type:
  270. // 1. Arrays must have a count after the opening brace
  271. // 2. Arrays must have an element type after the count
  272. self.scan.scanLocation = start;
  273. return -1;
  274. }
  275. } else {
  276. // If we encounter the ?= portion of something like {?=b8b4b1b1b18[8S]}
  277. // then we skip over it, since it means nothing to us in this context.
  278. // It is completely optional, and if it fails, we go right back where we were.
  279. [self scanTypeName];
  280. }
  281. // Sum sizes of members together:
  282. // Scan for bitfields before checking for other members
  283. //
  284. // Arrays will only have one "member," but
  285. // this logic still works for them
  286. ssize_t sizeSoFar = 0;
  287. ssize_t maxAlign = 0;
  288. NSMutableString *cleanedBackup = self.cleaned.mutableCopy;
  289. while (![self scanChar:closing]) {
  290. next = self.nextChar;
  291. // Check for bitfields, which we cannot support because
  292. // type encodings for bitfields do not include alignment info
  293. if (next == FLEXTypeEncodingBitField) {
  294. self.scan.scanLocation = start;
  295. return -1;
  296. }
  297. // Structure fields could be named
  298. if (next == FLEXTypeEncodingQuote) {
  299. [self scanPair:FLEXTypeEncodingQuote close:FLEXTypeEncodingQuote];
  300. }
  301. ssize_t align = 0;
  302. ssize_t size = [self parseNextType:&align hasUnion:containsUnion];
  303. if (size == -1) {
  304. // The above call is the only time in this method where
  305. // cleaned might be mutated recursively, so this is the
  306. // only place where we need to keep and restore a backup
  307. //
  308. // For instance, if we've been iterating over the members
  309. // of a struct and we've encountered a few pointers so far
  310. // that we needed to clean, and suddenly we come across
  311. // an unsupported member, we need to be able to "rewind"
  312. // and undo any chances to self.cleaned so that the parent
  313. // call in the call stack can wipe the current structure
  314. // clean entirely if needed. Example below:
  315. //
  316. // Initial: ^{foo=^{pair<d,d>}{^pair<i,i>}{invalid_type<d>}}
  317. // v-- here
  318. // 1st clean: ^{foo=^{?=}{^pair<i,i>}{invalid_type<d>}
  319. // v-- here
  320. // 2nd clean: ^{foo=^{?=}{?=}{invalid_type<d>}
  321. // v-- here
  322. // Can't clean: ^{foo=^{?=}{?=}{invalid_type<d>}
  323. // v-- to here
  324. // Rewind: ^{foo=^{pair<d,d>}{^pair<i,i>}{invalid_type<d>}}
  325. // Final clean: ^{foo=}
  326. self.cleaned = cleanedBackup;
  327. self.scan.scanLocation = start;
  328. return -1;
  329. }
  330. // Unions are the size of their largest member,
  331. // arrays are element.size x length, and
  332. // structs are the sum of their members
  333. if (structOrUnion) {
  334. if (isUnion) { // Union
  335. sizeSoFar = MAX(sizeSoFar, size);
  336. } else { // Struct
  337. sizeSoFar += size;
  338. }
  339. } else { // Array
  340. sizeSoFar = size * arrayCount;
  341. }
  342. maxAlign = MAX(maxAlign, align);
  343. }
  344. // Skip optional frame offset
  345. [self scanSize];
  346. if (alignment) {
  347. *alignment = maxAlign;
  348. }
  349. return sizeSoFar;
  350. }
  351. // Scan single thing and possible size and return
  352. ssize_t size = 0;
  353. char t = self.nextChar;
  354. switch (t) {
  355. case FLEXTypeEncodingUnknown:
  356. case FLEXTypeEncodingChar:
  357. case FLEXTypeEncodingInt:
  358. case FLEXTypeEncodingShort:
  359. case FLEXTypeEncodingLong:
  360. case FLEXTypeEncodingLongLong:
  361. case FLEXTypeEncodingUnsignedChar:
  362. case FLEXTypeEncodingUnsignedInt:
  363. case FLEXTypeEncodingUnsignedShort:
  364. case FLEXTypeEncodingUnsignedLong:
  365. case FLEXTypeEncodingUnsignedLongLong:
  366. case FLEXTypeEncodingFloat:
  367. case FLEXTypeEncodingDouble:
  368. case FLEXTypeEncodingLongDouble:
  369. case FLEXTypeEncodingCBool:
  370. case FLEXTypeEncodingCString:
  371. case FLEXTypeEncodingSelector:
  372. case FLEXTypeEncodingBitField: {
  373. self.scan.scanLocation++;
  374. // Skip optional frame offset
  375. [self scanSize];
  376. if (t == FLEXTypeEncodingBitField) {
  377. self.scan.scanLocation = start;
  378. return -1;
  379. } else {
  380. // Compute size
  381. size = [self sizeForType:t];
  382. }
  383. }
  384. break;
  385. case FLEXTypeEncodingObjcObject:
  386. case FLEXTypeEncodingObjcClass: {
  387. self.scan.scanLocation++;
  388. // These might have numbers OR quotes after them
  389. // Skip optional frame offset
  390. [self scanSize];
  391. [self scanPair:FLEXTypeEncodingQuote close:FLEXTypeEncodingQuote];
  392. size = sizeof(id);
  393. }
  394. break;
  395. default: break;
  396. }
  397. if (size) {
  398. // Alignment of scalar types is its size
  399. if (alignment) {
  400. *alignment = size;
  401. }
  402. return size;
  403. }
  404. self.scan.scanLocation = start;
  405. return -1;
  406. }
  407. - (BOOL)scanString:(NSString *)str {
  408. return [self.scan scanString:str intoString:nil];
  409. }
  410. - (BOOL)canScanString:(NSString *)str {
  411. NSScanner *scan = self.scan;
  412. NSUInteger len = str.length;
  413. unichar buff1[len], buff2[len];
  414. [str getCharacters:buff1];
  415. [scan.string getCharacters:buff2 range:NSMakeRange(scan.scanLocation, len)];
  416. if (memcmp(buff1, buff2, len) == 0) {
  417. return YES;
  418. }
  419. return NO;
  420. }
  421. - (BOOL)canScanChar:(char)c {
  422. NSScanner *scan = self.scan;
  423. if (scan.scanLocation >= scan.string.length) return NO;
  424. return [scan.string characterAtIndex:scan.scanLocation] == c;
  425. }
  426. - (BOOL)scanChar:(char)c {
  427. if ([self canScanChar:c]) {
  428. self.scan.scanLocation++;
  429. return YES;
  430. }
  431. return NO;
  432. }
  433. - (BOOL)scanChar:(char)c into:(char *)ref {
  434. if ([self scanChar:c]) {
  435. *ref = c;
  436. return YES;
  437. }
  438. return NO;
  439. }
  440. - (ssize_t)scanSize {
  441. NSInteger size = 0;
  442. if ([self.scan scanInteger:&size]) {
  443. return size;
  444. }
  445. return 0;
  446. }
  447. - (NSString *)scanPair:(char)c1 close:(char)c2 {
  448. // Starting position and string variables
  449. NSUInteger start = self.scan.scanLocation;
  450. NSString *s1 = S(c1);
  451. // Scan opening tag
  452. if (![self scanChar:c1]) {
  453. self.scan.scanLocation = start;
  454. return nil;
  455. }
  456. // Character set for scanning up to either symbol
  457. NSCharacterSet *bothChars = ({
  458. unichar buff[2] = { c1, c2 };
  459. NSString *bothCharsStr = [[NSString alloc] initWithCharacters:buff length:2];
  460. [NSCharacterSet characterSetWithCharactersInString:bothCharsStr];
  461. });
  462. // Stack for finding pairs, starting with the opening symbol
  463. NSMutableArray *stack = [NSMutableArray arrayWithObject:s1];
  464. // Algorithm for scanning to the closing end of a pair of opening/closing symbols
  465. // scanUpToCharactersFromSet:intoString: returns NO if you're already at one of the chars,
  466. // so we need to check if we can actually scan one if it returns NO
  467. while ([self.scan scanUpToCharactersFromSet:bothChars intoString:nil] ||
  468. [self canScanChar:c1] || [self canScanChar:c2]) {
  469. // Closing symbol found
  470. if ([self scanChar:c2]) {
  471. if (!stack.count) {
  472. // Abort, no matching opening symbol
  473. self.scan.scanLocation = start;
  474. return nil;
  475. }
  476. // Pair found, pop opening symbol
  477. [stack removeLastObject];
  478. // Exit loop if we reached the closing brace we needed
  479. if (!stack.count) {
  480. break;
  481. }
  482. }
  483. // Opening symbol found
  484. if ([self scanChar:c1]) {
  485. // Begin pair
  486. [stack addObject:s1];
  487. }
  488. }
  489. if (stack.count) {
  490. // Abort, no matching closing symbol
  491. self.scan.scanLocation = start;
  492. return nil;
  493. }
  494. // Slice out the string we just scanned
  495. return [self.scan.string
  496. substringWithRange:NSMakeRange(start, self.scan.scanLocation - start)
  497. ];
  498. }
  499. - (BOOL)scanPastArg {
  500. NSUInteger start = self.scan.scanLocation;
  501. // Check for void first
  502. if ([self scanChar:FLEXTypeEncodingVoid]) {
  503. return YES;
  504. }
  505. // Scan optional const
  506. [self scanChar:FLEXTypeEncodingConst];
  507. // Check for pointer, then scan next
  508. if ([self scanChar:FLEXTypeEncodingPointer]) {
  509. // Recurse to scan something else
  510. if ([self scanPastArg]) {
  511. return YES;
  512. } else {
  513. // Scan failed, abort
  514. self.scan.scanLocation = start;
  515. return NO;
  516. }
  517. }
  518. char next = self.nextChar;
  519. // Check for struct/union/array, scan past it
  520. FLEXTypeEncoding opening = FLEXTypeEncodingNull, closing = FLEXTypeEncodingNull;
  521. BOOL checkPair = YES;
  522. switch (next) {
  523. case FLEXTypeEncodingStructBegin:
  524. opening = FLEXTypeEncodingStructBegin;
  525. closing = FLEXTypeEncodingStructEnd;
  526. break;
  527. case FLEXTypeEncodingUnionBegin:
  528. opening = FLEXTypeEncodingUnionBegin;
  529. closing = FLEXTypeEncodingUnionEnd;
  530. break;
  531. case FLEXTypeEncodingArrayBegin:
  532. opening = FLEXTypeEncodingArrayBegin;
  533. closing = FLEXTypeEncodingArrayEnd;
  534. break;
  535. default:
  536. checkPair = NO;
  537. break;
  538. }
  539. if (checkPair && [self scanPair:opening close:closing]) {
  540. return YES;
  541. }
  542. // Scan single thing and possible size and return
  543. switch (next) {
  544. case FLEXTypeEncodingUnknown:
  545. case FLEXTypeEncodingChar:
  546. case FLEXTypeEncodingInt:
  547. case FLEXTypeEncodingShort:
  548. case FLEXTypeEncodingLong:
  549. case FLEXTypeEncodingLongLong:
  550. case FLEXTypeEncodingUnsignedChar:
  551. case FLEXTypeEncodingUnsignedInt:
  552. case FLEXTypeEncodingUnsignedShort:
  553. case FLEXTypeEncodingUnsignedLong:
  554. case FLEXTypeEncodingUnsignedLongLong:
  555. case FLEXTypeEncodingFloat:
  556. case FLEXTypeEncodingDouble:
  557. case FLEXTypeEncodingLongDouble:
  558. case FLEXTypeEncodingCBool:
  559. case FLEXTypeEncodingCString:
  560. case FLEXTypeEncodingSelector:
  561. case FLEXTypeEncodingBitField: {
  562. self.scan.scanLocation++;
  563. // Size is optional
  564. [self scanSize];
  565. return YES;
  566. }
  567. case FLEXTypeEncodingObjcObject:
  568. case FLEXTypeEncodingObjcClass: {
  569. self.scan.scanLocation++;
  570. // These might have numbers OR quotes after them
  571. [self scanSize] || [self scanPair:FLEXTypeEncodingQuote close:FLEXTypeEncodingQuote];
  572. return YES;
  573. }
  574. default: break;
  575. }
  576. self.scan.scanLocation = start;
  577. return NO;
  578. }
  579. - (NSString *)scanArg {
  580. NSUInteger start = self.scan.scanLocation;
  581. if (![self scanPastArg]) {
  582. return nil;
  583. }
  584. return [self.scan.string
  585. substringWithRange:NSMakeRange(start, self.scan.scanLocation - start)
  586. ];
  587. }
  588. - (BOOL)scanTypeName {
  589. NSUInteger start = self.scan.scanLocation;
  590. // The ?= portion of something like {?=b8b4b1b1b18[8S]}
  591. if ([self scanChar:FLEXTypeEncodingUnknown]) {
  592. if (![self scanString:@"="]) {
  593. // No size information available for strings like {?=}
  594. self.scan.scanLocation = start;
  595. return NO;
  596. }
  597. } else {
  598. if (![self scanIdentifier] || ![self scanString:@"="]) {
  599. // 1. Not a valid identifier
  600. // 2. No size information available for strings like {CGPoint}
  601. self.scan.scanLocation = start;
  602. return NO;
  603. }
  604. }
  605. return YES;
  606. }
  607. - (NSString *)extractTypeNameFromScanLocation {
  608. NSUInteger start = self.scan.scanLocation;
  609. NSString *typeName = nil;
  610. // The ?= portion of something like {?=b8b4b1b1b18[8S]}
  611. if ([self scanChar:FLEXTypeEncodingUnknown]) {
  612. typeName = @"?";
  613. } else {
  614. typeName = [self scanIdentifier];
  615. // = is non-optional
  616. if (!typeName || ![self scanString:@"="]) {
  617. // Did not scan an identifier
  618. self.scan.scanLocation = start;
  619. return nil;
  620. }
  621. }
  622. return typeName;
  623. }
  624. - (NSString *)cleanPointeeTypeAtLocation:(NSUInteger)scanLocation {
  625. NSUInteger start = self.scan.scanLocation;
  626. self.scan.scanLocation = scanLocation;
  627. // The return / cleanup code for when the scanned type is already clean
  628. NSString * (^typeIsClean)() = ^NSString * {
  629. NSString *clean = [self.scan.string
  630. substringWithRange:NSMakeRange(scanLocation, self.scan.scanLocation - scanLocation)
  631. ];
  632. // Reset scan location even on success, because this method is not supposed to change it
  633. self.scan.scanLocation = start;
  634. return clean;
  635. };
  636. // No void, this is not a return type
  637. // Scan optional const
  638. [self scanChar:FLEXTypeEncodingConst];
  639. char next = self.nextChar;
  640. switch (next) {
  641. case FLEXTypeEncodingPointer:
  642. // Recurse to scan something else
  643. [self scanChar:next];
  644. return [self cleanPointeeTypeAtLocation:self.scan.scanLocation];
  645. case FLEXTypeEncodingArrayBegin:
  646. // All arrays are supported, scan past them
  647. if ([self scanPair:FLEXTypeEncodingArrayBegin close:FLEXTypeEncodingArrayEnd]) {
  648. return typeIsClean();
  649. }
  650. break;
  651. case FLEXTypeEncodingUnionBegin:
  652. // Unions are not supported at all in NSMethodSignature
  653. // We could check for the closing token to be safe, but eh
  654. self.scan.scanLocation = start;
  655. return @"?";
  656. case FLEXTypeEncodingStructBegin: {
  657. BOOL containsUnion = NO;
  658. if ([self parseNextType:nil hasUnion:&containsUnion] != -1 && !containsUnion) {
  659. return typeIsClean();
  660. }
  661. // The structure we just tried to scan is unsupported, so just return its name
  662. // if it has one. If not, just return a question mark.
  663. self.scan.scanLocation++; // Skip past {
  664. NSString *name = [self extractTypeNameFromScanLocation];
  665. if (name) {
  666. // Got the name, scan past the closing token
  667. [self.scan scanUpToString:@"}" intoString:nil];
  668. if (![self scanChar:FLEXTypeEncodingStructEnd]) {
  669. // Missing struct close token
  670. self.scan.scanLocation = start;
  671. return nil;
  672. }
  673. } else {
  674. // Did not scan valid identifier, possibly a C++ type
  675. self.scan.scanLocation = start;
  676. return @"{?=}";
  677. }
  678. // Reset scan location even on success, because this method is not supposed to change it
  679. self.scan.scanLocation = start;
  680. return ({ // "{name=}"
  681. NSMutableString *format = @"{".mutableCopy;
  682. [format appendString:name];
  683. [format appendString:@"=}"];
  684. format;
  685. });
  686. }
  687. default:
  688. break;
  689. }
  690. // Check for other types, which in theory are all valid but whatever
  691. if ([self parseNextType:nil hasUnion:nil] != -1) {
  692. return typeIsClean();
  693. }
  694. self.scan.scanLocation = start;
  695. return @"?";
  696. }
  697. - (NSUInteger)cleanedReplacingOffset {
  698. return self.scan.string.length - self.cleaned.length;
  699. }
  700. @end