| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517 |
- //
- // FLEXTypeEncodingParser.m
- // FLEX
- //
- // Created by Tanner Bennett on 8/22/19.
- // Copyright © 2019 Flipboard. All rights reserved.
- //
- #import "FLEXTypeEncodingParser.h"
- #import "FLEXRuntimeUtility.h"
- #define S(ch) [NSString stringWithFormat:@"%c" , ch]
- BOOL FLEXGetSizeAndAlignment(const char *type, NSUInteger *sizep, NSUInteger *alignp) {
- NSInteger size = 0, align = 0;
- size = [FLEXTypeEncodingParser sizeForTypeEncoding:@(type) alignment:&align];
-
- if (size == -1) {
- return NO;
- }
-
- if (sizep) {
- *sizep = (NSUInteger)size;
- }
-
- if (alignp) {
- *alignp = (NSUInteger)size;
- }
-
- return YES;
- }
- @interface FLEXTypeEncodingParser ()
- @property (nonatomic, readonly) NSScanner *scan;
- @property (nonatomic, readonly) NSString *scanned;
- @property (nonatomic, readonly) NSString *unscanned;
- @end
- @implementation FLEXTypeEncodingParser
- - (NSString *)scanned {
- return [self.scan.string substringToIndex:self.scan.scanLocation];
- }
- - (NSString *)unscanned {
- return [self.scan.string substringFromIndex:self.scan.scanLocation];
- }
- #pragma mark Initialization
- - (id)initWithObjCTypes:(NSString *)typeEncoding {
- self = [super init];
- if (self) {
- _scan = [NSScanner scannerWithString:typeEncoding];
- _scan.caseSensitive = YES;
- }
- return self;
- }
- #pragma mark Public
- + (BOOL)methodTypeEncodingSupported:(NSString *)typeEncoding {
- FLEXTypeEncodingParser *parser = [[self alloc] initWithObjCTypes:typeEncoding];
- while (!parser.scan.isAtEnd) {
- if ([parser scanAndGetSizeAndAlignForNextType:nil] == -1) {
- return NO;
- }
- }
-
- return YES;
- }
- + (NSString *)type:(NSString *)typeEncoding forMethodArgumentAtIndex:(NSUInteger)idx {
- FLEXTypeEncodingParser *parser = [[self alloc] initWithObjCTypes:typeEncoding];
- // Scan up to the argument we want
- for (NSUInteger i = 0; i < idx; i++) {
- if (![parser scanPastArg]) {
- [NSException raise:NSRangeException
- format:@"Index %lu out of bounds for type encoding '%@'", idx, typeEncoding];
- }
- }
- return [parser scanArg];
- }
- + (ssize_t)size:(NSString *)typeEncoding forMethodArgumentAtIndex:(NSUInteger)idx {
- return [self sizeForTypeEncoding:[self type:typeEncoding forMethodArgumentAtIndex:idx] alignment:nil];
- }
- + (ssize_t)sizeForTypeEncoding:(NSString *)type alignment:(ssize_t *)alignOut {
- return [self sizeForTypeEncoding:type alignment:alignOut unaligned:NO];
- }
- + (ssize_t)sizeForTypeEncoding:(NSString *)type alignment:(ssize_t *)alignOut unaligned:(BOOL)unaligned {
- ssize_t align = 0;
- ssize_t size = [[[self alloc] initWithObjCTypes:type] scanAndGetSizeAndAlignForNextType:&align];
- if (size == -1) {
- return size;
- }
-
- if (alignOut) {
- *alignOut = align;
- }
- if (unaligned) {
- return size;
- } else {
- size += size % align;
- return size;
- }
- }
- #pragma mark Private
- /// Size in BYTES
- - (ssize_t)sizeForType:(FLEXTypeEncoding)type {
- switch (type) {
- case FLEXTypeEncodingChar: return sizeof(char);
- case FLEXTypeEncodingInt: return sizeof(int);
- case FLEXTypeEncodingShort: return sizeof(short);
- case FLEXTypeEncodingLong: return sizeof(long);
- case FLEXTypeEncodingLongLong: return sizeof(long long);
- case FLEXTypeEncodingUnsignedChar: return sizeof(unsigned char);
- case FLEXTypeEncodingUnsignedInt: return sizeof(unsigned int);
- case FLEXTypeEncodingUnsignedShort: return sizeof(unsigned short);
- case FLEXTypeEncodingUnsignedLong: return sizeof(unsigned long);
- case FLEXTypeEncodingUnsignedLongLong: return sizeof(unsigned long long);
- case FLEXTypeEncodingFloat: return sizeof(float);
- case FLEXTypeEncodingDouble: return sizeof(double);
- case FLEXTypeEncodingLongDouble: return sizeof(long double);
- case FLEXTypeEncodingCBool: return sizeof(_Bool);
- case FLEXTypeEncodingVoid: return 0;
- case FLEXTypeEncodingCString: return sizeof(char *);
- case FLEXTypeEncodingObjcObject: return sizeof(id);
- case FLEXTypeEncodingObjcClass: return sizeof(Class);
- case FLEXTypeEncodingSelector: return sizeof(SEL);
- // Unknown / '?' is typically a pointer. In the rare case
- // it isn't, such as in '{?=...}', it is never passed here.
- case FLEXTypeEncodingUnknown:
- case FLEXTypeEncodingPointer: return sizeof(uintptr_t);
- default: return -1;
- }
- }
- /// Size in bytes
- - (ssize_t)scanAndGetSizeAndAlignForNextType:(ssize_t *)alignment {
- NSUInteger start = self.scan.scanLocation;
- // Check for void first
- if ([self scanChar:FLEXTypeEncodingVoid]) {
- // Skip argument frame for method signatures
- [self scanSize];
- return 0;
- }
- // Scan optional const
- [self scanChar:FLEXTypeEncodingConst];
- // Check for pointer, then scan next
- if ([self scanChar:FLEXTypeEncodingPointer]) {
- // Recurse to scan something else
- if ([self scanPastArg]) {
- // Skip optional frame offset
- [self scanSize];
-
- ssize_t size = [self sizeForType:FLEXTypeEncodingPointer];
- if (alignment) {
- *alignment = size;
- }
- return size;
- } else {
- // Scan failed, abort
- self.scan.scanLocation = start;
- return -1;
- }
- }
- // Check for struct/union/array
- if ([self canScanChar:FLEXTypeEncodingStructBegin] ||
- [self canScanChar:FLEXTypeEncodingUnionBegin] ||
- [self canScanChar:FLEXTypeEncodingArrayBegin]) {
- NSUInteger backup = self.scan.scanLocation;
- // Ensure we have a closing tag
- if (![self scanPair:FLEXTypeEncodingStructBegin close:FLEXTypeEncodingStructEnd] &&
- ![self scanPair:FLEXTypeEncodingUnionBegin close:FLEXTypeEncodingUnionEnd] &&
- ![self scanPair:FLEXTypeEncodingArrayBegin close:FLEXTypeEncodingArrayEnd]) {
- // Scan failed, abort
- self.scan.scanLocation = start;
- return -1;
- }
- // Scan the next thing until we scan the closing tag
- BOOL structOrUnion = NO;
- NSInteger arrayCount = -1;
- self.scan.scanLocation = backup;
- FLEXTypeEncoding closing;
- if ([self scanChar:FLEXTypeEncodingStructBegin]) {
- closing = FLEXTypeEncodingStructEnd;
- structOrUnion = YES;
- } else if ([self scanChar:FLEXTypeEncodingUnionBegin]) {
- closing = FLEXTypeEncodingUnionEnd;
- structOrUnion = YES;
- } else {
- // Assert because code above did confirm a closing tag exists
- assert([self scanChar:FLEXTypeEncodingArrayBegin]);
- closing = FLEXTypeEncodingArrayEnd;
-
- arrayCount = [self scanSize];
- if (!arrayCount) {
- // Arrays must have a count after the opening brace
- self.scan.scanLocation = start;
- return -1;
- }
- }
- if (structOrUnion) {
- // If we encounter the ?= portion of something like {?=b8b4b1b1b18[8S]}
- // then we skip over it, since it means nothing to us in this context.
- // It is completely optional, and if it fails, we go right back where we were.
- [self scanTypeName];
- }
- // Sum sizes of members together:
- // Scan for bitfields before checking for other members
- //
- // Arrays will only have one "member," but
- // this logic still works for them
- ssize_t sizeSoFar = 0;
- ssize_t maxAlign = 0;
- while (![self scanChar:closing]) {
- // Check for bitfields, which we cannot support because
- // type encodings for bitfields do not include alignment info
- if ([self scanChar:FLEXTypeEncodingBitField]) {
- self.scan.scanLocation = start;
- return -1;
- }
- // Structure fields could be named
- [self scanPair:FLEXTypeEncodingQuote close:FLEXTypeEncodingQuote];
- ssize_t align = 0;
- ssize_t size = [self scanAndGetSizeAndAlignForNextType:&align];
- if (size == -1) {
- self.scan.scanLocation = start;
- return -1;
- }
-
- sizeSoFar += structOrUnion ? size : (size * arrayCount);
- maxAlign = MAX(maxAlign, align);
- }
-
- // Skip optional frame offset
- [self scanSize];
- if (alignment) {
- *alignment = maxAlign;
- }
- return sizeSoFar;
- }
- // Scan single thing and possible size and return
- ssize_t size = 0;
- FLEXTypeEncoding t;
- if ([self scanChar:FLEXTypeEncodingUnknown into:&t] ||
- [self scanChar:FLEXTypeEncodingChar into:&t] ||
- [self scanChar:FLEXTypeEncodingInt into:&t] ||
- [self scanChar:FLEXTypeEncodingShort into:&t] ||
- [self scanChar:FLEXTypeEncodingLong into:&t] ||
- [self scanChar:FLEXTypeEncodingLongLong into:&t] ||
- [self scanChar:FLEXTypeEncodingUnsignedChar into:&t] ||
- [self scanChar:FLEXTypeEncodingUnsignedInt into:&t] ||
- [self scanChar:FLEXTypeEncodingUnsignedShort into:&t] ||
- [self scanChar:FLEXTypeEncodingUnsignedLong into:&t] ||
- [self scanChar:FLEXTypeEncodingUnsignedLongLong into:&t] ||
- [self scanChar:FLEXTypeEncodingFloat into:&t] ||
- [self scanChar:FLEXTypeEncodingDouble into:&t] ||
- [self scanChar:FLEXTypeEncodingLongDouble into:&t] ||
- [self scanChar:FLEXTypeEncodingCBool into:&t] ||
- [self scanChar:FLEXTypeEncodingCString into:&t] ||
- [self scanChar:FLEXTypeEncodingSelector into:&t] ||
- [self scanChar:FLEXTypeEncodingBitField into:&t]) {
- // Skip optional frame offset
- [self scanSize];
-
- if (t == FLEXTypeEncodingBitField) {
- self.scan.scanLocation = start;
- return -1;
- } else {
- // Compute size
- size = [self sizeForType:t];
- }
- }
- // These might have numbers OR quotes after them
- else if ([self scanChar:FLEXTypeEncodingObjcObject] || [self scanChar:FLEXTypeEncodingObjcClass]) {
- // Skip optional frame offset
- [self scanSize];
- [self scanPair:FLEXTypeEncodingQuote close:FLEXTypeEncodingQuote];
- size = sizeof(id);
- }
- if (size) {
- // Alignment of scalar types is its size
- if (alignment) {
- *alignment = size;
- }
- return size;
- }
- self.scan.scanLocation = start;
- return -1;
- }
- - (BOOL)scanString:(NSString *)str {
- return [self.scan scanString:str intoString:nil];
- }
- - (BOOL)canScanString:(NSString *)str {
- if ([self scanString:str]) {
- self.scan.scanLocation -= str.length;
- return YES;
- }
- return NO;
- }
- - (BOOL)canScanChar:(char)c {
- return [self canScanString:S(c)];
- }
- - (BOOL)scanChar:(char)c {
- return [self scanString:S(c)];
- }
- - (BOOL)scanChar:(char)c into:(char *)ref {
- if ([self scanString:S(c)]) {
- *ref = c;
- return YES;
- }
- return NO;
- }
- - (ssize_t)scanSize {
- NSInteger size = 0;
- if ([self.scan scanInteger:&size]) {
- return size;
- }
- return 0;
- }
- - (NSString *)scanPair:(char)c1 close:(char)c2 {
- // Starting position and string variables
- NSUInteger start = self.scan.scanLocation;
- NSString *s1 = S(c1);
- // Scan opening tag
- if (![self scanChar:c1]) {
- self.scan.scanLocation = start;
- return nil;
- }
- // Character set for scanning up to either symbol
- NSCharacterSet *bothChars = ({
- NSString *bothCharsStr = [NSString stringWithFormat:@"%c%c" , c1, c2];
- [NSCharacterSet characterSetWithCharactersInString:bothCharsStr];
- });
- // Stack for finding pairs, starting with the opening symbol
- NSMutableArray *stack = [NSMutableArray arrayWithObject:s1];
- // Algorithm for scanning to the closing end of a pair of opening/closing symbols
- // scanUpToCharactersFromSet:intoString: returns NO if you're already at one of the chars,
- // so we need to check if we can actually scan one if it returns NO
- while ([self.scan scanUpToCharactersFromSet:bothChars intoString:nil] ||
- [self canScanChar:c1] || [self canScanChar:c2]) {
- // Closing symbol found
- if ([self scanChar:c2]) {
- if (!stack.count) {
- // Abort, no matching opening symbol
- self.scan.scanLocation = start;
- return nil;
- }
- // Pair found, pop opening symbol
- [stack removeLastObject];
- // Exit loop if we reached the closing brace we needed
- if (!stack.count) {
- break;
- }
- }
- // Opening symbol found
- if ([self scanChar:c1]) {
- // Begin pair
- [stack addObject:s1];
- }
- }
- if (stack.count) {
- // Abort, no matching closing symbol
- self.scan.scanLocation = start;
- return nil;
- }
- // Slice out the string we just scanned
- return [self.scan.string
- substringWithRange:NSMakeRange(start, self.scan.scanLocation - start)
- ];
- }
- - (BOOL)scanPastArg {
- NSUInteger start = self.scan.scanLocation;
- // Check for void first
- if ([self scanChar:FLEXTypeEncodingVoid]) {
- return YES;
- }
- // Scan optional const
- [self scanChar:FLEXTypeEncodingConst];
- // Check for pointer, then scan next
- if ([self scanChar:FLEXTypeEncodingPointer]) {
- // Recurse to scan something else
- if ([self scanPastArg]) {
- return YES;
- } else {
- // Scan failed, abort
- self.scan.scanLocation = start;
- return NO;
- }
- }
- // Check for struct/union/array, scan past it
- if ([self scanPair:FLEXTypeEncodingStructBegin close:FLEXTypeEncodingStructEnd] ||
- [self scanPair:FLEXTypeEncodingUnionBegin close:FLEXTypeEncodingUnionEnd] ||
- [self scanPair:FLEXTypeEncodingArrayBegin close:FLEXTypeEncodingArrayEnd]) {
- return YES;
- }
- // Scan single thing and possible size and return
- if ([self scanChar:FLEXTypeEncodingUnknown] ||
- [self scanChar:FLEXTypeEncodingChar] ||
- [self scanChar:FLEXTypeEncodingInt] ||
- [self scanChar:FLEXTypeEncodingShort] ||
- [self scanChar:FLEXTypeEncodingLong] ||
- [self scanChar:FLEXTypeEncodingLongLong] ||
- [self scanChar:FLEXTypeEncodingUnsignedChar] ||
- [self scanChar:FLEXTypeEncodingUnsignedInt] ||
- [self scanChar:FLEXTypeEncodingUnsignedShort] ||
- [self scanChar:FLEXTypeEncodingUnsignedLong] ||
- [self scanChar:FLEXTypeEncodingUnsignedLongLong] ||
- [self scanChar:FLEXTypeEncodingFloat] ||
- [self scanChar:FLEXTypeEncodingDouble] ||
- [self scanChar:FLEXTypeEncodingLongDouble] ||
- [self scanChar:FLEXTypeEncodingCBool] ||
- [self scanChar:FLEXTypeEncodingCString] ||
- [self scanChar:FLEXTypeEncodingSelector] ||
- [self scanChar:FLEXTypeEncodingBitField]) {
- // Size is optional
- [self scanSize];
- return YES;
- }
- // These might have numbers OR quotes after them
- if ([self scanChar:FLEXTypeEncodingObjcObject] || [self scanChar:FLEXTypeEncodingObjcClass]) {
- [self scanSize] || [self scanPair:FLEXTypeEncodingQuote close:FLEXTypeEncodingQuote];
- return YES;
- }
- self.scan.scanLocation = start;
- return NO;
- }
- - (NSString *)scanArg {
- NSUInteger start = self.scan.scanLocation;
- if (![self scanPastArg]) {
- return nil;
- }
- return [self.scan.string
- substringWithRange:NSMakeRange(start, self.scan.scanLocation - start)
- ];
- }
- - (BOOL)scanTypeName {
- NSUInteger start = self.scan.scanLocation;
- // The ?= portion of something like {?=b8b4b1b1b18[8S]}
- if ([self scanChar:FLEXTypeEncodingUnknown]) {
- if (![self scanString:@"="]) {
- // No size information available for strings like {?}
- self.scan.scanLocation = start;
- return NO;
- }
- } else if ([self.scan scanCharactersFromSet:[NSCharacterSet letterCharacterSet] intoString:nil]) {
- if (![self scanString:@"="]) {
- // No size information available for strings like {CGPoint}
- self.scan.scanLocation = start;
- return NO;
- }
- }
- return YES;
- }
- @end
|