FLEXTypeEncodingParser.m 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  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) [NSString stringWithFormat:@"%c" , ch]
  11. BOOL FLEXGetSizeAndAlignment(const char *type, NSUInteger *sizep, NSUInteger *alignp) {
  12. NSInteger size = 0, align = 0;
  13. size = [FLEXTypeEncodingParser sizeForTypeEncoding:@(type) alignment:&align];
  14. if (size == -1) {
  15. return NO;
  16. }
  17. if (sizep) {
  18. *sizep = (NSUInteger)size;
  19. }
  20. if (alignp) {
  21. *alignp = (NSUInteger)size;
  22. }
  23. return YES;
  24. }
  25. @interface FLEXTypeEncodingParser ()
  26. @property (nonatomic, readonly) NSScanner *scan;
  27. @property (nonatomic, readonly) NSString *scanned;
  28. @property (nonatomic, readonly) NSString *unscanned;
  29. @end
  30. @implementation FLEXTypeEncodingParser
  31. - (NSString *)scanned {
  32. return [self.scan.string substringToIndex:self.scan.scanLocation];
  33. }
  34. - (NSString *)unscanned {
  35. return [self.scan.string substringFromIndex:self.scan.scanLocation];
  36. }
  37. #pragma mark Initialization
  38. - (id)initWithObjCTypes:(NSString *)typeEncoding {
  39. self = [super init];
  40. if (self) {
  41. _scan = [NSScanner scannerWithString:typeEncoding];
  42. _scan.caseSensitive = YES;
  43. }
  44. return self;
  45. }
  46. #pragma mark Public
  47. + (BOOL)methodTypeEncodingSupported:(NSString *)typeEncoding {
  48. FLEXTypeEncodingParser *parser = [[self alloc] initWithObjCTypes:typeEncoding];
  49. while (!parser.scan.isAtEnd) {
  50. if ([parser scanAndGetSizeAndAlignForNextType:nil] == -1) {
  51. return NO;
  52. }
  53. }
  54. return YES;
  55. }
  56. + (NSString *)type:(NSString *)typeEncoding forMethodArgumentAtIndex:(NSUInteger)idx {
  57. FLEXTypeEncodingParser *parser = [[self alloc] initWithObjCTypes:typeEncoding];
  58. // Scan up to the argument we want
  59. for (NSUInteger i = 0; i < idx; i++) {
  60. if (![parser scanPastArg]) {
  61. [NSException raise:NSRangeException
  62. format:@"Index %lu out of bounds for type encoding '%@'", idx, typeEncoding];
  63. }
  64. }
  65. return [parser scanArg];
  66. }
  67. + (ssize_t)size:(NSString *)typeEncoding forMethodArgumentAtIndex:(NSUInteger)idx {
  68. return [self sizeForTypeEncoding:[self type:typeEncoding forMethodArgumentAtIndex:idx] alignment:nil];
  69. }
  70. + (ssize_t)sizeForTypeEncoding:(NSString *)type alignment:(ssize_t *)alignOut {
  71. return [self sizeForTypeEncoding:type alignment:alignOut unaligned:NO];
  72. }
  73. + (ssize_t)sizeForTypeEncoding:(NSString *)type alignment:(ssize_t *)alignOut unaligned:(BOOL)unaligned {
  74. ssize_t align = 0;
  75. ssize_t size = [[[self alloc] initWithObjCTypes:type] scanAndGetSizeAndAlignForNextType:&align];
  76. if (size == -1) {
  77. return size;
  78. }
  79. if (alignOut) {
  80. *alignOut = align;
  81. }
  82. if (unaligned) {
  83. return size;
  84. } else {
  85. size += size % align;
  86. return size;
  87. }
  88. }
  89. #pragma mark Private
  90. /// Size in BYTES
  91. - (ssize_t)sizeForType:(FLEXTypeEncoding)type {
  92. switch (type) {
  93. case FLEXTypeEncodingChar: return sizeof(char);
  94. case FLEXTypeEncodingInt: return sizeof(int);
  95. case FLEXTypeEncodingShort: return sizeof(short);
  96. case FLEXTypeEncodingLong: return sizeof(long);
  97. case FLEXTypeEncodingLongLong: return sizeof(long long);
  98. case FLEXTypeEncodingUnsignedChar: return sizeof(unsigned char);
  99. case FLEXTypeEncodingUnsignedInt: return sizeof(unsigned int);
  100. case FLEXTypeEncodingUnsignedShort: return sizeof(unsigned short);
  101. case FLEXTypeEncodingUnsignedLong: return sizeof(unsigned long);
  102. case FLEXTypeEncodingUnsignedLongLong: return sizeof(unsigned long long);
  103. case FLEXTypeEncodingFloat: return sizeof(float);
  104. case FLEXTypeEncodingDouble: return sizeof(double);
  105. case FLEXTypeEncodingLongDouble: return sizeof(long double);
  106. case FLEXTypeEncodingCBool: return sizeof(_Bool);
  107. case FLEXTypeEncodingVoid: return 0;
  108. case FLEXTypeEncodingCString: return sizeof(char *);
  109. case FLEXTypeEncodingObjcObject: return sizeof(id);
  110. case FLEXTypeEncodingObjcClass: return sizeof(Class);
  111. case FLEXTypeEncodingSelector: return sizeof(SEL);
  112. // Unknown / '?' is typically a pointer. In the rare case
  113. // it isn't, such as in '{?=...}', it is never passed here.
  114. case FLEXTypeEncodingUnknown:
  115. case FLEXTypeEncodingPointer: return sizeof(uintptr_t);
  116. default: return -1;
  117. }
  118. }
  119. /// Size in bytes
  120. - (ssize_t)scanAndGetSizeAndAlignForNextType:(ssize_t *)alignment {
  121. NSUInteger start = self.scan.scanLocation;
  122. // Check for void first
  123. if ([self scanChar:FLEXTypeEncodingVoid]) {
  124. // Skip argument frame for method signatures
  125. [self scanSize];
  126. return 0;
  127. }
  128. // Scan optional const
  129. [self scanChar:FLEXTypeEncodingConst];
  130. // Check for pointer, then scan next
  131. if ([self scanChar:FLEXTypeEncodingPointer]) {
  132. // Recurse to scan something else
  133. if ([self scanPastArg]) {
  134. // Skip optional frame offset
  135. [self scanSize];
  136. ssize_t size = [self sizeForType:FLEXTypeEncodingPointer];
  137. if (alignment) {
  138. *alignment = size;
  139. }
  140. return size;
  141. } else {
  142. // Scan failed, abort
  143. self.scan.scanLocation = start;
  144. return -1;
  145. }
  146. }
  147. // Check for struct/union/array
  148. if ([self canScanChar:FLEXTypeEncodingStructBegin] ||
  149. [self canScanChar:FLEXTypeEncodingUnionBegin] ||
  150. [self canScanChar:FLEXTypeEncodingArrayBegin]) {
  151. NSUInteger backup = self.scan.scanLocation;
  152. // Ensure we have a closing tag
  153. if (![self scanPair:FLEXTypeEncodingStructBegin close:FLEXTypeEncodingStructEnd] &&
  154. ![self scanPair:FLEXTypeEncodingUnionBegin close:FLEXTypeEncodingUnionEnd] &&
  155. ![self scanPair:FLEXTypeEncodingArrayBegin close:FLEXTypeEncodingArrayEnd]) {
  156. // Scan failed, abort
  157. self.scan.scanLocation = start;
  158. return -1;
  159. }
  160. // Scan the next thing until we scan the closing tag
  161. BOOL structOrUnion = NO;
  162. NSInteger arrayCount = -1;
  163. self.scan.scanLocation = backup;
  164. FLEXTypeEncoding closing;
  165. if ([self scanChar:FLEXTypeEncodingStructBegin]) {
  166. closing = FLEXTypeEncodingStructEnd;
  167. structOrUnion = YES;
  168. } else if ([self scanChar:FLEXTypeEncodingUnionBegin]) {
  169. closing = FLEXTypeEncodingUnionEnd;
  170. structOrUnion = YES;
  171. } else {
  172. // Assert because code above did confirm a closing tag exists
  173. assert([self scanChar:FLEXTypeEncodingArrayBegin]);
  174. closing = FLEXTypeEncodingArrayEnd;
  175. arrayCount = [self scanSize];
  176. if (!arrayCount) {
  177. // Arrays must have a count after the opening brace
  178. self.scan.scanLocation = start;
  179. return -1;
  180. }
  181. }
  182. if (structOrUnion) {
  183. // If we encounter the ?= portion of something like {?=b8b4b1b1b18[8S]}
  184. // then we skip over it, since it means nothing to us in this context.
  185. // It is completely optional, and if it fails, we go right back where we were.
  186. [self scanTypeName];
  187. }
  188. // Sum sizes of members together:
  189. // Scan for bitfields before checking for other members
  190. //
  191. // Arrays will only have one "member," but
  192. // this logic still works for them
  193. ssize_t sizeSoFar = 0;
  194. ssize_t maxAlign = 0;
  195. while (![self scanChar:closing]) {
  196. // Check for bitfields, which we cannot support because
  197. // type encodings for bitfields do not include alignment info
  198. if ([self scanChar:FLEXTypeEncodingBitField]) {
  199. self.scan.scanLocation = start;
  200. return -1;
  201. }
  202. // Structure fields could be named
  203. [self scanPair:FLEXTypeEncodingQuote close:FLEXTypeEncodingQuote];
  204. ssize_t align = 0;
  205. ssize_t size = [self scanAndGetSizeAndAlignForNextType:&align];
  206. if (size == -1) {
  207. self.scan.scanLocation = start;
  208. return -1;
  209. }
  210. sizeSoFar += structOrUnion ? size : (size * arrayCount);
  211. maxAlign = MAX(maxAlign, align);
  212. }
  213. // Skip optional frame offset
  214. [self scanSize];
  215. if (alignment) {
  216. *alignment = maxAlign;
  217. }
  218. return sizeSoFar;
  219. }
  220. // Scan single thing and possible size and return
  221. ssize_t size = 0;
  222. FLEXTypeEncoding t;
  223. if ([self scanChar:FLEXTypeEncodingUnknown into:&t] ||
  224. [self scanChar:FLEXTypeEncodingChar into:&t] ||
  225. [self scanChar:FLEXTypeEncodingInt into:&t] ||
  226. [self scanChar:FLEXTypeEncodingShort into:&t] ||
  227. [self scanChar:FLEXTypeEncodingLong into:&t] ||
  228. [self scanChar:FLEXTypeEncodingLongLong into:&t] ||
  229. [self scanChar:FLEXTypeEncodingUnsignedChar into:&t] ||
  230. [self scanChar:FLEXTypeEncodingUnsignedInt into:&t] ||
  231. [self scanChar:FLEXTypeEncodingUnsignedShort into:&t] ||
  232. [self scanChar:FLEXTypeEncodingUnsignedLong into:&t] ||
  233. [self scanChar:FLEXTypeEncodingUnsignedLongLong into:&t] ||
  234. [self scanChar:FLEXTypeEncodingFloat into:&t] ||
  235. [self scanChar:FLEXTypeEncodingDouble into:&t] ||
  236. [self scanChar:FLEXTypeEncodingLongDouble into:&t] ||
  237. [self scanChar:FLEXTypeEncodingCBool into:&t] ||
  238. [self scanChar:FLEXTypeEncodingCString into:&t] ||
  239. [self scanChar:FLEXTypeEncodingSelector into:&t] ||
  240. [self scanChar:FLEXTypeEncodingBitField into:&t]) {
  241. // Skip optional frame offset
  242. [self scanSize];
  243. if (t == FLEXTypeEncodingBitField) {
  244. self.scan.scanLocation = start;
  245. return -1;
  246. } else {
  247. // Compute size
  248. size = [self sizeForType:t];
  249. }
  250. }
  251. // These might have numbers OR quotes after them
  252. else if ([self scanChar:FLEXTypeEncodingObjcObject] || [self scanChar:FLEXTypeEncodingObjcClass]) {
  253. // Skip optional frame offset
  254. [self scanSize];
  255. [self scanPair:FLEXTypeEncodingQuote close:FLEXTypeEncodingQuote];
  256. size = sizeof(id);
  257. }
  258. if (size) {
  259. // Alignment of scalar types is its size
  260. if (alignment) {
  261. *alignment = size;
  262. }
  263. return size;
  264. }
  265. self.scan.scanLocation = start;
  266. return -1;
  267. }
  268. - (BOOL)scanString:(NSString *)str {
  269. return [self.scan scanString:str intoString:nil];
  270. }
  271. - (BOOL)canScanString:(NSString *)str {
  272. if ([self scanString:str]) {
  273. self.scan.scanLocation -= str.length;
  274. return YES;
  275. }
  276. return NO;
  277. }
  278. - (BOOL)canScanChar:(char)c {
  279. return [self canScanString:S(c)];
  280. }
  281. - (BOOL)scanChar:(char)c {
  282. return [self scanString:S(c)];
  283. }
  284. - (BOOL)scanChar:(char)c into:(char *)ref {
  285. if ([self scanString:S(c)]) {
  286. *ref = c;
  287. return YES;
  288. }
  289. return NO;
  290. }
  291. - (ssize_t)scanSize {
  292. NSInteger size = 0;
  293. if ([self.scan scanInteger:&size]) {
  294. return size;
  295. }
  296. return 0;
  297. }
  298. - (NSString *)scanPair:(char)c1 close:(char)c2 {
  299. // Starting position and string variables
  300. NSUInteger start = self.scan.scanLocation;
  301. NSString *s1 = S(c1);
  302. // Scan opening tag
  303. if (![self scanChar:c1]) {
  304. self.scan.scanLocation = start;
  305. return nil;
  306. }
  307. // Character set for scanning up to either symbol
  308. NSCharacterSet *bothChars = ({
  309. NSString *bothCharsStr = [NSString stringWithFormat:@"%c%c" , c1, c2];
  310. [NSCharacterSet characterSetWithCharactersInString:bothCharsStr];
  311. });
  312. // Stack for finding pairs, starting with the opening symbol
  313. NSMutableArray *stack = [NSMutableArray arrayWithObject:s1];
  314. // Algorithm for scanning to the closing end of a pair of opening/closing symbols
  315. // scanUpToCharactersFromSet:intoString: returns NO if you're already at one of the chars,
  316. // so we need to check if we can actually scan one if it returns NO
  317. while ([self.scan scanUpToCharactersFromSet:bothChars intoString:nil] ||
  318. [self canScanChar:c1] || [self canScanChar:c2]) {
  319. // Closing symbol found
  320. if ([self scanChar:c2]) {
  321. if (!stack.count) {
  322. // Abort, no matching opening symbol
  323. self.scan.scanLocation = start;
  324. return nil;
  325. }
  326. // Pair found, pop opening symbol
  327. [stack removeLastObject];
  328. // Exit loop if we reached the closing brace we needed
  329. if (!stack.count) {
  330. break;
  331. }
  332. }
  333. // Opening symbol found
  334. if ([self scanChar:c1]) {
  335. // Begin pair
  336. [stack addObject:s1];
  337. }
  338. }
  339. if (stack.count) {
  340. // Abort, no matching closing symbol
  341. self.scan.scanLocation = start;
  342. return nil;
  343. }
  344. // Slice out the string we just scanned
  345. return [self.scan.string
  346. substringWithRange:NSMakeRange(start, self.scan.scanLocation - start)
  347. ];
  348. }
  349. - (BOOL)scanPastArg {
  350. NSUInteger start = self.scan.scanLocation;
  351. // Check for void first
  352. if ([self scanChar:FLEXTypeEncodingVoid]) {
  353. return YES;
  354. }
  355. // Scan optional const
  356. [self scanChar:FLEXTypeEncodingConst];
  357. // Check for pointer, then scan next
  358. if ([self scanChar:FLEXTypeEncodingPointer]) {
  359. // Recurse to scan something else
  360. if ([self scanPastArg]) {
  361. return YES;
  362. } else {
  363. // Scan failed, abort
  364. self.scan.scanLocation = start;
  365. return NO;
  366. }
  367. }
  368. // Check for struct/union/array, scan past it
  369. if ([self scanPair:FLEXTypeEncodingStructBegin close:FLEXTypeEncodingStructEnd] ||
  370. [self scanPair:FLEXTypeEncodingUnionBegin close:FLEXTypeEncodingUnionEnd] ||
  371. [self scanPair:FLEXTypeEncodingArrayBegin close:FLEXTypeEncodingArrayEnd]) {
  372. return YES;
  373. }
  374. // Scan single thing and possible size and return
  375. if ([self scanChar:FLEXTypeEncodingUnknown] ||
  376. [self scanChar:FLEXTypeEncodingChar] ||
  377. [self scanChar:FLEXTypeEncodingInt] ||
  378. [self scanChar:FLEXTypeEncodingShort] ||
  379. [self scanChar:FLEXTypeEncodingLong] ||
  380. [self scanChar:FLEXTypeEncodingLongLong] ||
  381. [self scanChar:FLEXTypeEncodingUnsignedChar] ||
  382. [self scanChar:FLEXTypeEncodingUnsignedInt] ||
  383. [self scanChar:FLEXTypeEncodingUnsignedShort] ||
  384. [self scanChar:FLEXTypeEncodingUnsignedLong] ||
  385. [self scanChar:FLEXTypeEncodingUnsignedLongLong] ||
  386. [self scanChar:FLEXTypeEncodingFloat] ||
  387. [self scanChar:FLEXTypeEncodingDouble] ||
  388. [self scanChar:FLEXTypeEncodingLongDouble] ||
  389. [self scanChar:FLEXTypeEncodingCBool] ||
  390. [self scanChar:FLEXTypeEncodingCString] ||
  391. [self scanChar:FLEXTypeEncodingSelector] ||
  392. [self scanChar:FLEXTypeEncodingBitField]) {
  393. // Size is optional
  394. [self scanSize];
  395. return YES;
  396. }
  397. // These might have numbers OR quotes after them
  398. if ([self scanChar:FLEXTypeEncodingObjcObject] || [self scanChar:FLEXTypeEncodingObjcClass]) {
  399. [self scanSize] || [self scanPair:FLEXTypeEncodingQuote close:FLEXTypeEncodingQuote];
  400. return YES;
  401. }
  402. self.scan.scanLocation = start;
  403. return NO;
  404. }
  405. - (NSString *)scanArg {
  406. NSUInteger start = self.scan.scanLocation;
  407. if (![self scanPastArg]) {
  408. return nil;
  409. }
  410. return [self.scan.string
  411. substringWithRange:NSMakeRange(start, self.scan.scanLocation - start)
  412. ];
  413. }
  414. - (BOOL)scanTypeName {
  415. NSUInteger start = self.scan.scanLocation;
  416. // The ?= portion of something like {?=b8b4b1b1b18[8S]}
  417. if ([self scanChar:FLEXTypeEncodingUnknown]) {
  418. if (![self scanString:@"="]) {
  419. // No size information available for strings like {?}
  420. self.scan.scanLocation = start;
  421. return NO;
  422. }
  423. } else if ([self.scan scanCharactersFromSet:[NSCharacterSet letterCharacterSet] intoString:nil]) {
  424. if (![self scanString:@"="]) {
  425. // No size information available for strings like {CGPoint}
  426. self.scan.scanLocation = start;
  427. return NO;
  428. }
  429. }
  430. return YES;
  431. }
  432. @end