Bladeren bron

Add named struct field parsing support to fix the struct iVar issues

Chaoshuai Lu 6 jaren geleden
bovenliggende
commit
6cbfa63d48

+ 11 - 6
Classes/Editing/ArgumentInputViews/FLEXArgumentInputViewFactory.m

@@ -17,6 +17,7 @@
 #import "FLEXArgumentInputFontView.h"
 #import "FLEXArgumentInputColorView.h"
 #import "FLEXArgumentInputDateView.h"
+#import "FLEXRuntimeUtility.h"
 
 @implementation FLEXArgumentInputViewFactory
 
@@ -33,11 +34,15 @@
         // The unsupported view shows "nil" and does not allow user input.
         subclass = [FLEXArgumentInputNotSupportedView class];
     }
-    return [[subclass alloc] initWithArgumentTypeEncoding:typeEncoding];
+    // Remove the field name if there is any (e.g. \"width\"d -> d)
+    const NSUInteger fieldNameOffset = [FLEXRuntimeUtility fieldNameOffsetForTypeEncoding:typeEncoding];
+    return [[subclass alloc] initWithArgumentTypeEncoding:typeEncoding + fieldNameOffset];
 }
 
 + (Class)argumentInputViewSubclassForTypeEncoding:(const char *)typeEncoding currentValue:(id)currentValue
 {
+    // Remove the field name if there is any (e.g. \"width\"d -> d)
+    const NSUInteger fieldNameOffset = [FLEXRuntimeUtility fieldNameOffsetForTypeEncoding:typeEncoding];
     Class argumentInputViewSubclass = nil;
     NSArray<Class> *inputViewClasses = @[[FLEXArgumentInputColorView class],
                                          [FLEXArgumentInputFontView class],
@@ -47,17 +52,17 @@
                                          [FLEXArgumentInputDateView class],
                                          [FLEXArgumentInputNumberView class],
                                          [FLEXArgumentInputObjectView class]];
-    
+
     // Note that order is important here since multiple subclasses may support the same type.
     // An example is the number subclass and the bool subclass for the type @encode(BOOL).
     // Both work, but we'd prefer to use the bool subclass.
-    for (Class inputView in inputViewClasses) {
-        if ([inputView supportsObjCType:typeEncoding withCurrentValue:currentValue]) {
-            argumentInputViewSubclass = inputView;
+    for (Class inputViewClass in inputViewClasses) {
+        if ([inputViewClass supportsObjCType:typeEncoding + fieldNameOffset withCurrentValue:currentValue]) {
+            argumentInputViewSubclass = inputViewClass;
             break;
         }
     }
-    
+
     return argumentInputViewSubclass;
 }
 

+ 4 - 0
Classes/Utility/FLEXRuntimeUtility.h

@@ -53,6 +53,7 @@ typedef NS_ENUM(char, FLEXTypeEncoding)
     FLEXTypeEncodingStructEnd        = '}',
     FLEXTypeEncodingUnionBegin       = '(',
     FLEXTypeEncodingUnionEnd         = ')',
+    FLEXTypeEncodingQuote            = '\"',
     FLEXTypeEncodingBitField         = 'b',
     FLEXTypeEncodingPointer          = '^',
     FLEXTypeEncodingConst            = 'r'
@@ -67,6 +68,9 @@ typedef NS_ENUM(char, FLEXTypeEncoding)
 + (BOOL)pointerIsValidObjcObject:(const void *)pointer;
 /// Unwraps raw pointers to objects stored in NSValue, and re-boxes C strings into NSStrings.
 + (id)potentiallyUnwrapBoxedPointer:(id)returnedObjectOrNil type:(const FLEXTypeEncoding *)returnType;
+/// Some fields have a name in their encoded string (e.g. \"width\"d)
+/// @return the offset to skip the field name, 0 if there is no name
++ (NSUInteger)fieldNameOffsetForTypeEncoding:(const FLEXTypeEncoding *)typeEncoding;
 
 /// @return The class hierarchy for the given object or class,
 /// from the current class to the root-most class.

+ 76 - 22
Classes/Utility/FLEXRuntimeUtility.m

@@ -88,6 +88,19 @@ const unsigned int kFLEXNumberOfImplicitArgs = 2;
     return returnedObjectOrNil;
 }
 
++ (NSUInteger)fieldNameOffsetForTypeEncoding:(const FLEXTypeEncoding *)typeEncoding
+{
+    NSUInteger beginIndex = 0;
+    while (typeEncoding[beginIndex] == FLEXTypeEncodingQuote) {
+        NSUInteger endIndex = beginIndex + 1;
+        while (typeEncoding[endIndex] != FLEXTypeEncodingQuote) {
+            ++endIndex;
+        }
+        beginIndex = endIndex + 1;
+    }
+    return beginIndex;
+}
+
 + (NSArray<Class> *)classHierarchyOfObject:(id)objectOrClass
 {
     NSMutableArray<Class> *superClasses = [NSMutableArray new];
@@ -682,10 +695,30 @@ const unsigned int kFLEXNumberOfImplicitArgs = 2;
     // The use of macros here was inspired by https://www.mikeash.com/pyblog/friday-qa-2013-02-08-lets-build-key-value-coding.html
     const char *encodingCString = encodingString.UTF8String;
 
+    // Some fields have a name, such as {Size=\"width\"d\"height\"d}, we need to extract the name out and recursive
+    const NSUInteger fieldNameOffset = [FLEXRuntimeUtility fieldNameOffsetForTypeEncoding:encodingCString];
+    if (fieldNameOffset > 0) {
+        // According to https://github.com/nygard/class-dump/commit/33fb5ed221810685f57c192e1ce8ab6054949a7c,
+        // there are some consecutive quoted strings, so use `_` to concatenate the names.
+        NSString *const fieldNamesString = [encodingString substringWithRange:NSMakeRange(0, fieldNameOffset)];
+        NSArray<NSString *> *const fieldNames = [fieldNamesString componentsSeparatedByString:[NSString stringWithFormat:@"%c", FLEXTypeEncodingQuote]];
+        NSMutableString *finalFieldNamesString = [NSMutableString string];
+        for (NSString *const fieldName in fieldNames) {
+            if (fieldName.length > 0) {
+                if (finalFieldNamesString.length > 0) {
+                    [finalFieldNamesString appendString:@"_"];
+                }
+                [finalFieldNamesString appendString:fieldName];
+            }
+        }
+        NSString *const recursiveType = [self readableTypeForEncoding:[encodingString substringFromIndex:fieldNameOffset]];
+        return [NSString stringWithFormat:@"%@ %@", recursiveType, finalFieldNamesString];
+    }
+
     // Objects
     if (encodingCString[0] == FLEXTypeEncodingObjcObject) {
         NSString *class = [encodingString substringFromIndex:1];
-        class = [class stringByReplacingOccurrencesOfString:@"\"" withString:@""];
+        class = [class stringByReplacingOccurrencesOfString:[NSString stringWithFormat:@"%c", FLEXTypeEncodingQuote] withString:@""];
         if (class.length == 0 || (class.length == 1 && [class characterAtIndex:0] == FLEXTypeEncodingUnknown)) {
             class = @"id";
         } else {
@@ -694,7 +727,29 @@ const unsigned int kFLEXNumberOfImplicitArgs = 2;
         return class;
     }
 
-    // C Types
+    // Qualifier Prefixes
+    // Do this first since some of the direct translations (i.e. Method) contain a prefix.
+#define RECURSIVE_TRANSLATE(prefix, formatString) \
+    if (encodingCString[0] == prefix) { \
+        NSString *recursiveType = [self readableTypeForEncoding:[encodingString substringFromIndex:1]]; \
+        return [NSString stringWithFormat:formatString, recursiveType]; \
+    }
+
+    // If there's a qualifier prefix on the encoding, translate it and then
+    // recursively call this method with the rest of the encoding string.
+    RECURSIVE_TRANSLATE('^', @"%@ *");
+    RECURSIVE_TRANSLATE('r', @"const %@");
+    RECURSIVE_TRANSLATE('n', @"in %@");
+    RECURSIVE_TRANSLATE('N', @"inout %@");
+    RECURSIVE_TRANSLATE('o', @"out %@");
+    RECURSIVE_TRANSLATE('O', @"bycopy %@");
+    RECURSIVE_TRANSLATE('R', @"byref %@");
+    RECURSIVE_TRANSLATE('V', @"oneway %@");
+    RECURSIVE_TRANSLATE('b', @"bitfield(%@)");
+
+#undef RECURSIVE_TRANSLATE
+
+  // C Types
 #define TRANSLATE(ctype) \
     if (strcmp(encodingCString, @encode(ctype)) == 0) { \
         return (NSString *)CFSTR(#ctype); \
@@ -746,34 +801,33 @@ const unsigned int kFLEXNumberOfImplicitArgs = 2;
 
 #undef TRANSLATE
 
-    // Qualifier Prefixes
-    // Do this after the checks above since some of the direct translations (i.e. Method) contain a prefix.
-#define RECURSIVE_TRANSLATE(prefix, formatString) \
-    if (encodingCString[0] == prefix) { \
-        NSString *recursiveType = [self readableTypeForEncoding:[encodingString substringFromIndex:1]]; \
-        return [NSString stringWithFormat:formatString, recursiveType]; \
+    // For structs, we only use the name of the structs
+    if (encodingCString[0] == FLEXTypeEncodingStructBegin) {
+        const char *equals = strchr(encodingCString, '=');
+        if (equals) {
+            const char *nameStart = encodingCString + 1;
+            // For anonymous structs
+            if (nameStart[0] == FLEXTypeEncodingUnknown) {
+                return @"anonymous struct";
+            } else {
+                NSString *const structName = [encodingString substringWithRange:NSMakeRange(nameStart - encodingCString, equals - nameStart)];
+                return structName;
+            }
+        }
     }
 
-    // If there's a qualifier prefix on the encoding, translate it and then
-    // recursively call this method with the rest of the encoding string.
-    RECURSIVE_TRANSLATE('^', @"%@ *");
-    RECURSIVE_TRANSLATE('r', @"const %@");
-    RECURSIVE_TRANSLATE('n', @"in %@");
-    RECURSIVE_TRANSLATE('N', @"inout %@");
-    RECURSIVE_TRANSLATE('o', @"out %@");
-    RECURSIVE_TRANSLATE('O', @"bycopy %@");
-    RECURSIVE_TRANSLATE('R', @"byref %@");
-    RECURSIVE_TRANSLATE('V', @"oneway %@");
-    RECURSIVE_TRANSLATE('b', @"bitfield(%@)");
-
-#undef RECURSIVE_TRANSLATE
-
     // If we couldn't translate, just return the original encoding string
     return encodingString;
 }
 
 + (NSValue *)valueForPrimitivePointer:(void *)pointer objCType:(const char *)type
 {
+    // Remove the field name if there is any (e.g. \"width\"d -> d)
+    const NSUInteger fieldNameOffset = [FLEXRuntimeUtility fieldNameOffsetForTypeEncoding:type];
+    if (fieldNameOffset > 0) {
+        return [self valueForPrimitivePointer:pointer objCType:type + fieldNameOffset];
+    }
+
     // CASE macro inspired by https://www.mikeash.com/pyblog/friday-qa-2013-02-08-lets-build-key-value-coding.html
 #define CASE(ctype, selectorpart) \
     if (strcmp(type, @encode(ctype)) == 0) { \