FLEXTypeEncodingParser.m 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826
  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) {
  269. // Arrays must have a count after the opening brace
  270. self.scan.scanLocation = start;
  271. return -1;
  272. }
  273. } else {
  274. // If we encounter the ?= portion of something like {?=b8b4b1b1b18[8S]}
  275. // then we skip over it, since it means nothing to us in this context.
  276. // It is completely optional, and if it fails, we go right back where we were.
  277. [self scanTypeName];
  278. }
  279. // Sum sizes of members together:
  280. // Scan for bitfields before checking for other members
  281. //
  282. // Arrays will only have one "member," but
  283. // this logic still works for them
  284. ssize_t sizeSoFar = 0;
  285. ssize_t maxAlign = 0;
  286. NSMutableString *cleanedBackup = self.cleaned.mutableCopy;
  287. while (![self scanChar:closing]) {
  288. next = self.nextChar;
  289. // Check for bitfields, which we cannot support because
  290. // type encodings for bitfields do not include alignment info
  291. if (next == FLEXTypeEncodingBitField) {
  292. self.scan.scanLocation = start;
  293. return -1;
  294. }
  295. // Structure fields could be named
  296. if (next == FLEXTypeEncodingQuote) {
  297. [self scanPair:FLEXTypeEncodingQuote close:FLEXTypeEncodingQuote];
  298. }
  299. ssize_t align = 0;
  300. ssize_t size = [self parseNextType:&align hasUnion:containsUnion];
  301. if (size == -1) {
  302. // The above call is the only time in this method where
  303. // cleaned might be mutated recursively, so this is the
  304. // only place where we need to keep and restore a backup
  305. //
  306. // For instance, if we've been iterating over the members
  307. // of a struct and we've encountered a few pointers so far
  308. // that we needed to clean, and suddenly we come across
  309. // an unsupported member, we need to be able to "rewind"
  310. // and undo any chances to self.cleaned so that the parent
  311. // call in the call stack can wipe the current structure
  312. // clean entirely if needed. Example below:
  313. //
  314. // Initial: ^{foo=^{pair<d,d>}{^pair<i,i>}{invalid_type<d>}}
  315. // v-- here
  316. // 1st clean: ^{foo=^{?=}{^pair<i,i>}{invalid_type<d>}
  317. // v-- here
  318. // 2nd clean: ^{foo=^{?=}{?=}{invalid_type<d>}
  319. // v-- here
  320. // Can't clean: ^{foo=^{?=}{?=}{invalid_type<d>}
  321. // v-- to here
  322. // Rewind: ^{foo=^{pair<d,d>}{^pair<i,i>}{invalid_type<d>}}
  323. // Final clean: ^{foo=}
  324. self.cleaned = cleanedBackup;
  325. self.scan.scanLocation = start;
  326. return -1;
  327. }
  328. // Unions are the size of their largest member,
  329. // arrays are element.size x length, and
  330. // structs are the sum of their members
  331. if (structOrUnion) {
  332. if (isUnion) { // Union
  333. sizeSoFar = MAX(sizeSoFar, size);
  334. } else { // Struct
  335. sizeSoFar += size;
  336. }
  337. } else { // Array
  338. sizeSoFar = size * arrayCount;
  339. }
  340. maxAlign = MAX(maxAlign, align);
  341. }
  342. // Skip optional frame offset
  343. [self scanSize];
  344. if (alignment) {
  345. *alignment = maxAlign;
  346. }
  347. return sizeSoFar;
  348. }
  349. // Scan single thing and possible size and return
  350. ssize_t size = 0;
  351. char t = self.nextChar;
  352. switch (t) {
  353. case FLEXTypeEncodingUnknown:
  354. case FLEXTypeEncodingChar:
  355. case FLEXTypeEncodingInt:
  356. case FLEXTypeEncodingShort:
  357. case FLEXTypeEncodingLong:
  358. case FLEXTypeEncodingLongLong:
  359. case FLEXTypeEncodingUnsignedChar:
  360. case FLEXTypeEncodingUnsignedInt:
  361. case FLEXTypeEncodingUnsignedShort:
  362. case FLEXTypeEncodingUnsignedLong:
  363. case FLEXTypeEncodingUnsignedLongLong:
  364. case FLEXTypeEncodingFloat:
  365. case FLEXTypeEncodingDouble:
  366. case FLEXTypeEncodingLongDouble:
  367. case FLEXTypeEncodingCBool:
  368. case FLEXTypeEncodingCString:
  369. case FLEXTypeEncodingSelector:
  370. case FLEXTypeEncodingBitField: {
  371. self.scan.scanLocation++;
  372. // Skip optional frame offset
  373. [self scanSize];
  374. if (t == FLEXTypeEncodingBitField) {
  375. self.scan.scanLocation = start;
  376. return -1;
  377. } else {
  378. // Compute size
  379. size = [self sizeForType:t];
  380. }
  381. }
  382. break;
  383. case FLEXTypeEncodingObjcObject:
  384. case FLEXTypeEncodingObjcClass: {
  385. self.scan.scanLocation++;
  386. // These might have numbers OR quotes after them
  387. // Skip optional frame offset
  388. [self scanSize];
  389. [self scanPair:FLEXTypeEncodingQuote close:FLEXTypeEncodingQuote];
  390. size = sizeof(id);
  391. }
  392. break;
  393. default: break;
  394. }
  395. if (size) {
  396. // Alignment of scalar types is its size
  397. if (alignment) {
  398. *alignment = size;
  399. }
  400. return size;
  401. }
  402. self.scan.scanLocation = start;
  403. return -1;
  404. }
  405. - (BOOL)scanString:(NSString *)str {
  406. return [self.scan scanString:str intoString:nil];
  407. }
  408. - (BOOL)canScanString:(NSString *)str {
  409. NSScanner *scan = self.scan;
  410. NSUInteger len = str.length;
  411. unichar buff1[len], buff2[len];
  412. [str getCharacters:buff1];
  413. [scan.string getCharacters:buff2 range:NSMakeRange(scan.scanLocation, len)];
  414. if (memcmp(buff1, buff2, len) == 0) {
  415. return YES;
  416. }
  417. return NO;
  418. }
  419. - (BOOL)canScanChar:(char)c {
  420. NSScanner *scan = self.scan;
  421. if (scan.scanLocation >= scan.string.length) return NO;
  422. return [scan.string characterAtIndex:scan.scanLocation] == c;
  423. }
  424. - (BOOL)scanChar:(char)c {
  425. if ([self canScanChar:c]) {
  426. self.scan.scanLocation++;
  427. return YES;
  428. }
  429. return NO;
  430. }
  431. - (BOOL)scanChar:(char)c into:(char *)ref {
  432. if ([self scanChar:c]) {
  433. *ref = c;
  434. return YES;
  435. }
  436. return NO;
  437. }
  438. - (ssize_t)scanSize {
  439. NSInteger size = 0;
  440. if ([self.scan scanInteger:&size]) {
  441. return size;
  442. }
  443. return 0;
  444. }
  445. - (NSString *)scanPair:(char)c1 close:(char)c2 {
  446. // Starting position and string variables
  447. NSUInteger start = self.scan.scanLocation;
  448. NSString *s1 = S(c1);
  449. // Scan opening tag
  450. if (![self scanChar:c1]) {
  451. self.scan.scanLocation = start;
  452. return nil;
  453. }
  454. // Character set for scanning up to either symbol
  455. NSCharacterSet *bothChars = ({
  456. unichar buff[2] = { c1, c2 };
  457. NSString *bothCharsStr = [[NSString alloc] initWithCharacters:buff length:2];
  458. [NSCharacterSet characterSetWithCharactersInString:bothCharsStr];
  459. });
  460. // Stack for finding pairs, starting with the opening symbol
  461. NSMutableArray *stack = [NSMutableArray arrayWithObject:s1];
  462. // Algorithm for scanning to the closing end of a pair of opening/closing symbols
  463. // scanUpToCharactersFromSet:intoString: returns NO if you're already at one of the chars,
  464. // so we need to check if we can actually scan one if it returns NO
  465. while ([self.scan scanUpToCharactersFromSet:bothChars intoString:nil] ||
  466. [self canScanChar:c1] || [self canScanChar:c2]) {
  467. // Closing symbol found
  468. if ([self scanChar:c2]) {
  469. if (!stack.count) {
  470. // Abort, no matching opening symbol
  471. self.scan.scanLocation = start;
  472. return nil;
  473. }
  474. // Pair found, pop opening symbol
  475. [stack removeLastObject];
  476. // Exit loop if we reached the closing brace we needed
  477. if (!stack.count) {
  478. break;
  479. }
  480. }
  481. // Opening symbol found
  482. if ([self scanChar:c1]) {
  483. // Begin pair
  484. [stack addObject:s1];
  485. }
  486. }
  487. if (stack.count) {
  488. // Abort, no matching closing symbol
  489. self.scan.scanLocation = start;
  490. return nil;
  491. }
  492. // Slice out the string we just scanned
  493. return [self.scan.string
  494. substringWithRange:NSMakeRange(start, self.scan.scanLocation - start)
  495. ];
  496. }
  497. - (BOOL)scanPastArg {
  498. NSUInteger start = self.scan.scanLocation;
  499. // Check for void first
  500. if ([self scanChar:FLEXTypeEncodingVoid]) {
  501. return YES;
  502. }
  503. // Scan optional const
  504. [self scanChar:FLEXTypeEncodingConst];
  505. // Check for pointer, then scan next
  506. if ([self scanChar:FLEXTypeEncodingPointer]) {
  507. // Recurse to scan something else
  508. if ([self scanPastArg]) {
  509. return YES;
  510. } else {
  511. // Scan failed, abort
  512. self.scan.scanLocation = start;
  513. return NO;
  514. }
  515. }
  516. char next = self.nextChar;
  517. // Check for struct/union/array, scan past it
  518. FLEXTypeEncoding opening = FLEXTypeEncodingNull, closing = FLEXTypeEncodingNull;
  519. BOOL checkPair = YES;
  520. switch (next) {
  521. case FLEXTypeEncodingStructBegin:
  522. opening = FLEXTypeEncodingStructBegin;
  523. closing = FLEXTypeEncodingStructEnd;
  524. break;
  525. case FLEXTypeEncodingUnionBegin:
  526. opening = FLEXTypeEncodingUnionBegin;
  527. closing = FLEXTypeEncodingUnionEnd;
  528. break;
  529. case FLEXTypeEncodingArrayBegin:
  530. opening = FLEXTypeEncodingArrayBegin;
  531. closing = FLEXTypeEncodingArrayEnd;
  532. break;
  533. default:
  534. checkPair = NO;
  535. break;
  536. }
  537. if (checkPair && [self scanPair:opening close:closing]) {
  538. return YES;
  539. }
  540. // Scan single thing and possible size and return
  541. switch (next) {
  542. case FLEXTypeEncodingUnknown:
  543. case FLEXTypeEncodingChar:
  544. case FLEXTypeEncodingInt:
  545. case FLEXTypeEncodingShort:
  546. case FLEXTypeEncodingLong:
  547. case FLEXTypeEncodingLongLong:
  548. case FLEXTypeEncodingUnsignedChar:
  549. case FLEXTypeEncodingUnsignedInt:
  550. case FLEXTypeEncodingUnsignedShort:
  551. case FLEXTypeEncodingUnsignedLong:
  552. case FLEXTypeEncodingUnsignedLongLong:
  553. case FLEXTypeEncodingFloat:
  554. case FLEXTypeEncodingDouble:
  555. case FLEXTypeEncodingLongDouble:
  556. case FLEXTypeEncodingCBool:
  557. case FLEXTypeEncodingCString:
  558. case FLEXTypeEncodingSelector:
  559. case FLEXTypeEncodingBitField: {
  560. self.scan.scanLocation++;
  561. // Size is optional
  562. [self scanSize];
  563. return YES;
  564. }
  565. case FLEXTypeEncodingObjcObject:
  566. case FLEXTypeEncodingObjcClass: {
  567. self.scan.scanLocation++;
  568. // These might have numbers OR quotes after them
  569. [self scanSize] || [self scanPair:FLEXTypeEncodingQuote close:FLEXTypeEncodingQuote];
  570. return YES;
  571. }
  572. default: break;
  573. }
  574. self.scan.scanLocation = start;
  575. return NO;
  576. }
  577. - (NSString *)scanArg {
  578. NSUInteger start = self.scan.scanLocation;
  579. if (![self scanPastArg]) {
  580. return nil;
  581. }
  582. return [self.scan.string
  583. substringWithRange:NSMakeRange(start, self.scan.scanLocation - start)
  584. ];
  585. }
  586. - (BOOL)scanTypeName {
  587. NSUInteger start = self.scan.scanLocation;
  588. // The ?= portion of something like {?=b8b4b1b1b18[8S]}
  589. if ([self scanChar:FLEXTypeEncodingUnknown]) {
  590. if (![self scanString:@"="]) {
  591. // No size information available for strings like {?=}
  592. self.scan.scanLocation = start;
  593. return NO;
  594. }
  595. } else {
  596. if (![self scanIdentifier] || ![self scanString:@"="]) {
  597. // 1. Not a valid identifier
  598. // 2. No size information available for strings like {CGPoint}
  599. self.scan.scanLocation = start;
  600. return NO;
  601. }
  602. }
  603. return YES;
  604. }
  605. - (NSString *)extractTypeNameFromScanLocation {
  606. NSUInteger start = self.scan.scanLocation;
  607. NSString *typeName = nil;
  608. // The ?= portion of something like {?=b8b4b1b1b18[8S]}
  609. if ([self scanChar:FLEXTypeEncodingUnknown]) {
  610. typeName = @"?";
  611. } else {
  612. typeName = [self scanIdentifier];
  613. // = is non-optional
  614. if (!typeName || ![self scanString:@"="]) {
  615. // Did not scan an identifier
  616. self.scan.scanLocation = start;
  617. return nil;
  618. }
  619. }
  620. return typeName;
  621. }
  622. - (NSString *)cleanPointeeTypeAtLocation:(NSUInteger)scanLocation {
  623. NSUInteger start = self.scan.scanLocation;
  624. self.scan.scanLocation = scanLocation;
  625. // The return / cleanup code for when the scanned type is already clean
  626. NSString * (^typeIsClean)() = ^NSString * {
  627. NSString *clean = [self.scan.string
  628. substringWithRange:NSMakeRange(scanLocation, self.scan.scanLocation - scanLocation)
  629. ];
  630. // Reset scan location even on success, because this method is not supposed to change it
  631. self.scan.scanLocation = start;
  632. return clean;
  633. };
  634. // No void, this is not a return type
  635. // Scan optional const
  636. [self scanChar:FLEXTypeEncodingConst];
  637. char next = self.nextChar;
  638. switch (next) {
  639. case FLEXTypeEncodingPointer:
  640. // Recurse to scan something else
  641. [self scanChar:next];
  642. return [self cleanPointeeTypeAtLocation:self.scan.scanLocation];
  643. case FLEXTypeEncodingArrayBegin:
  644. // All arrays are supported, scan past them
  645. if ([self scanPair:FLEXTypeEncodingArrayBegin close:FLEXTypeEncodingArrayEnd]) {
  646. return typeIsClean();
  647. }
  648. break;
  649. case FLEXTypeEncodingUnionBegin:
  650. // Unions are not supported at all in NSMethodSignature
  651. // We could check for the closing token to be safe, but eh
  652. self.scan.scanLocation = start;
  653. return @"?";
  654. case FLEXTypeEncodingStructBegin: {
  655. BOOL containsUnion = NO;
  656. if ([self parseNextType:nil hasUnion:&containsUnion] != -1 && !containsUnion) {
  657. return typeIsClean();
  658. }
  659. // The structure we just tried to scan is unsupported, so just return its name
  660. // if it has one. If not, just return a question mark.
  661. self.scan.scanLocation++; // Skip past {
  662. NSString *name = [self extractTypeNameFromScanLocation];
  663. if (name) {
  664. // Got the name, scan past the closing token
  665. [self.scan scanUpToString:@"}" intoString:nil];
  666. if (![self scanChar:FLEXTypeEncodingStructEnd]) {
  667. // Missing struct close token
  668. self.scan.scanLocation = start;
  669. return nil;
  670. }
  671. } else {
  672. // Did not scan valid identifier, possibly a C++ type
  673. self.scan.scanLocation = start;
  674. return @"{?=}";
  675. }
  676. // Reset scan location even on success, because this method is not supposed to change it
  677. self.scan.scanLocation = start;
  678. return ({ // "{name=}"
  679. NSMutableString *format = @"{".mutableCopy;
  680. [format appendString:name];
  681. [format appendString:@"=}"];
  682. format;
  683. });
  684. }
  685. default:
  686. break;
  687. }
  688. // Check for other types, which in theory are all valid but whatever
  689. if ([self parseNextType:nil hasUnion:nil] != -1) {
  690. return typeIsClean();
  691. }
  692. self.scan.scanLocation = start;
  693. return @"?";
  694. }
  695. - (NSUInteger)cleanedReplacingOffset {
  696. return self.scan.string.length - self.cleaned.length;
  697. }
  698. @end