FLEXTypeEncodingParser.m 25 KB

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