FLEXTypeEncodingParser.m 24 KB

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