├── .github
└── workflows
│ └── test.yml
├── .gitignore
├── ClassDump.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── xcshareddata
│ └── xcschemes
│ ├── ClassDump.xcscheme
│ └── ClassDumpTests.xcscheme
├── ClassDump
├── ClassDump.h
├── ClassDump.xctestplan
├── Models
│ ├── CDGenerationOptions.h
│ ├── CDGenerationOptions.m
│ ├── CDSemanticString.h
│ ├── CDSemanticString.m
│ ├── CDVariableModel.h
│ ├── CDVariableModel.m
│ ├── NSArray+CDFiltering.h
│ ├── NSArray+CDFiltering.m
│ ├── ParseTypes
│ │ ├── CDArrayType.h
│ │ ├── CDArrayType.m
│ │ ├── CDBitFieldType.h
│ │ ├── CDBitFieldType.m
│ │ ├── CDBlockType.h
│ │ ├── CDBlockType.m
│ │ ├── CDObjectType.h
│ │ ├── CDObjectType.m
│ │ ├── CDParseType.h
│ │ ├── CDParseType.m
│ │ ├── CDPointerType.h
│ │ ├── CDPointerType.m
│ │ ├── CDPrimitiveType.h
│ │ ├── CDPrimitiveType.m
│ │ ├── CDRecordType.h
│ │ └── CDRecordType.m
│ └── Reflections
│ │ ├── CDClassModel.h
│ │ ├── CDClassModel.m
│ │ ├── CDIvarModel.h
│ │ ├── CDIvarModel.m
│ │ ├── CDMethodModel.h
│ │ ├── CDMethodModel.m
│ │ ├── CDPropertyAttribute.h
│ │ ├── CDPropertyAttribute.m
│ │ ├── CDPropertyModel.h
│ │ ├── CDPropertyModel.m
│ │ ├── CDProtocolModel+Conformance.h
│ │ ├── CDProtocolModel+Conformance.m
│ │ ├── CDProtocolModel.h
│ │ └── CDProtocolModel.m
└── Services
│ ├── CDTypeParser.h
│ ├── CDTypeParser.m
│ ├── CDUtilities.h
│ └── CDUtilities.m
├── ClassDumpTests
├── CDObjCTests.m
├── CDParseAdvancedTests.m
├── CDParseCppTests.mm
├── CDParsePrimitiveTests.m
├── CDProtocolTest.m
└── Info.plist
├── LICENSE.md
├── Makefile
├── Package.swift
├── README.md
├── Sources
└── ClassDumpRuntime
│ ├── ClassDump
│ └── include
│ └── ClassDump
│ ├── CDArrayType.h
│ ├── CDBitFieldType.h
│ ├── CDBlockType.h
│ ├── CDClassModel.h
│ ├── CDGenerationOptions.h
│ ├── CDIvarModel.h
│ ├── CDMethodModel.h
│ ├── CDObjectType.h
│ ├── CDParseType.h
│ ├── CDPointerType.h
│ ├── CDPrimitiveType.h
│ ├── CDPropertyAttribute.h
│ ├── CDPropertyModel.h
│ ├── CDProtocolModel+Conformance.h
│ ├── CDProtocolModel.h
│ ├── CDRecordType.h
│ ├── CDSemanticString.h
│ ├── CDTypeParser.h
│ ├── CDUtilities.h
│ ├── CDVariableModel.h
│ └── ClassDump.h
└── control
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Run Swift Package test
2 |
3 | on:
4 | push:
5 |
6 | jobs:
7 | test:
8 | runs-on: macos-14
9 | steps:
10 | - name: Checkout
11 | uses: actions/checkout@v4
12 |
13 | - name: Test
14 | run: |
15 | swift test
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /Packages
4 | xcuserdata/
5 | DerivedData/
6 | .swiftpm/configuration/registries.json
7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
8 | .netrc
9 |
--------------------------------------------------------------------------------
/ClassDump.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ClassDump.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ClassDump.xcodeproj/xcshareddata/xcschemes/ClassDump.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
34 |
35 |
36 |
37 |
47 |
48 |
54 |
55 |
61 |
62 |
63 |
64 |
66 |
67 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/ClassDump.xcodeproj/xcshareddata/xcschemes/ClassDumpTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
18 |
19 |
23 |
29 |
30 |
31 |
32 |
33 |
43 |
44 |
50 |
51 |
53 |
54 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/ClassDump/ClassDump.h:
--------------------------------------------------------------------------------
1 | //
2 | // ClassDump.h
3 | // ClassDump
4 | //
5 | // Created by Leptos on 3/24/19.
6 | // Copyright © 2019 Leptos. All rights reserved.
7 | //
8 |
9 | // to support building as both an Xcode framework and a Swift Package,
10 | // all headers that are marked as "public" for the Xcode framework
11 | // should have a symlink in `Sources/ClassDumpRuntime/include/ClassDump`;
12 | // all those files should then be imported below.
13 | //
14 | // you can generate these imports using a shell script such as
15 | // `ls ClassDump/*.h | while read HEADER; do printf "#import <${HEADER}>\n"; done`
16 | // (run from `Sources/ClassDumpRuntime/include`)
17 |
18 | #import
19 | #import
20 | #import
21 | #import
22 | #import
23 | #import
24 | #import
25 | #import
26 | #import
27 | #import
28 | #import
29 | #import
30 | #import
31 | #import
32 | #import
33 | #import
34 | #import
35 | #import
36 | #import
37 | #import
38 |
--------------------------------------------------------------------------------
/ClassDump/ClassDump.xctestplan:
--------------------------------------------------------------------------------
1 | {
2 | "configurations" : [
3 | {
4 | "id" : "31CD5771-2F31-4142-AFFF-9E0550C35A54",
5 | "name" : "DefaultConfig",
6 | "options" : {
7 |
8 | }
9 | }
10 | ],
11 | "defaultOptions" : {
12 | "addressSanitizer" : {
13 | "detectStackUseAfterReturn" : true,
14 | "enabled" : true
15 | },
16 | "undefinedBehaviorSanitizerEnabled" : true
17 | },
18 | "testTargets" : [
19 | {
20 | "target" : {
21 | "containerPath" : "container:ClassDump.xcodeproj",
22 | "identifier" : "FA2A011823AEB15700B52F1D",
23 | "name" : "ClassDumpTests"
24 | }
25 | }
26 | ],
27 | "version" : 1
28 | }
29 |
--------------------------------------------------------------------------------
/ClassDump/Models/CDGenerationOptions.h:
--------------------------------------------------------------------------------
1 | //
2 | // CDGenerationOptions.h
3 | // ClassDump
4 | //
5 | // Created by Leptos on 2/25/24.
6 | // Copyright © 2024 Leptos. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | /// Options with which a header file may be generated with
12 | @interface CDGenerationOptions : NSObject
13 | /// @c YES means hide properties and methods that are required by a protocol the type conforms to
14 | ///
15 | /// This property applies to both classes and protocols.
16 | @property (nonatomic) BOOL stripProtocolConformance;
17 | /// @c YES means hide properties and methods that are inherited from the class hierachy
18 | ///
19 | /// This property only applies to classes. Protocols can require conformances to other
20 | /// protocols, however they do not have inheritance.
21 | /// Eligible properties are only hidden if the types match between the property in the
22 | /// current class and class nearest in the hierachy.
23 | /// For example, if `AAView` has a property `AALayer *layer`
24 | /// and a subclass `BBView` has a property `BBLayer *layer`,
25 | /// the property would not be hidden since the types are different.
26 | /// @see stripProtocolConformance
27 | @property (nonatomic) BOOL stripOverrides;
28 | /// @c YES means hide duplicate occurrences of a property or method,
29 | /// @c NO means transcribe the objects reported by the runtime
30 | @property (nonatomic) BOOL stripDuplicates;
31 | /// @c YES means hide methods and ivars that are synthesized from a property
32 | ///
33 | /// This property applies to both classes and protocols.
34 | @property (nonatomic) BOOL stripSynthesized;
35 | /// @c YES means hide @c .cxx_construct method,
36 | /// @c NO means show the method if it exists
37 | ///
38 | /// This property only applies to classes.
39 | @property (nonatomic) BOOL stripCtorMethod;
40 | /// @c YES means hide @c .cxx_destruct method,
41 | /// @c NO means show the method if it exists
42 | ///
43 | /// This property only applies to classes.
44 | @property (nonatomic) BOOL stripDtorMethod;
45 | /// @c YES means add comments above each eligible declaration
46 | /// with the symbol name and image path the object is found in,
47 | /// @c NO means do not add comments for symbol or image source
48 | ///
49 | /// This property applies to both classes and protocols.
50 | @property (nonatomic) BOOL addSymbolImageComments;
51 |
52 | @end
53 |
--------------------------------------------------------------------------------
/ClassDump/Models/CDGenerationOptions.m:
--------------------------------------------------------------------------------
1 | //
2 | // CDGenerationOptions.m
3 | // ClassDump
4 | //
5 | // Created by Leptos on 2/25/24.
6 | // Copyright © 2024 Leptos. All rights reserved.
7 | //
8 |
9 | #import "CDGenerationOptions.h"
10 |
11 | @implementation CDGenerationOptions
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/ClassDump/Models/CDSemanticString.h:
--------------------------------------------------------------------------------
1 | //
2 | // CDSemanticString.h
3 | // ClassDump
4 | //
5 | // Created by Leptos on 1/1/23.
6 | // Copyright © 2023 Leptos. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | /// The semantic types that a string may represent in an Objective-C header file
12 | typedef NS_ENUM(NSUInteger, CDSemanticType) {
13 | // whitespace, colons (':'), semicolons (';'), pointers ('*'),
14 | // braces ('(', ')'), brackets ('{','}', '[', ']', '<', '>')
15 | CDSemanticTypeStandard,
16 | // characters used to start and end a comment, and the contents of the comment
17 | CDSemanticTypeComment,
18 | // struct, union, type modifiers, language provided primitive types
19 | CDSemanticTypeKeyword,
20 | // the name of a variable- this includes both declaration and usage sites
21 | CDSemanticTypeVariable,
22 | // the name portion of a struct or union definition
23 | CDSemanticTypeRecordName,
24 | // an Obj-C class (e.g. NSString)
25 | CDSemanticTypeClass,
26 | // an Obj-C protocol (e.g. NSFastEnumeration)
27 | CDSemanticTypeProtocol,
28 | // a number literal (e.g. 2, 18, 1e5, 7.1)
29 | CDSemanticTypeNumeric,
30 |
31 | /// The number of valid cases there are in @c CDSemanticType
32 | CDSemanticTypeCount
33 | };
34 |
35 | /// A string composed of substrings that may have different semantic meanings
36 | @interface CDSemanticString : NSObject
37 | /// The length of the string
38 | @property (readonly) NSUInteger length;
39 | /// Append another semantic string to the end of this string,
40 | /// keeping all of the semantics of both the parameter and receiver
41 | - (void)appendSemanticString:(nonnull CDSemanticString *)semanticString;
42 | /// Append a string with a semantic type to the end of this string
43 | - (void)appendString:(nullable NSString *)string semanticType:(CDSemanticType)type;
44 | /// Whether the first character in this string is equal to @c character
45 | - (BOOL)startsWithChar:(char)character;
46 | /// Whether the last character in this string is equal to @c character
47 | - (BOOL)endWithChar:(char)character;
48 | /// Enumerate the substrings and the associated semantic type that compose this string
49 | - (void)enumerateTypesUsingBlock:(void (NS_NOESCAPE ^_Nonnull)(NSString *_Nonnull string, CDSemanticType type))block;
50 | /// Enumerate the longest effective substrings and the associated semantic type that compose this string
51 | ///
52 | /// Each invocation of @c block will have the longest substring of @c type such that the next
53 | /// invocation will have a different @c type
54 | - (void)enumerateLongestEffectiveRangesUsingBlock:(void (NS_NOESCAPE ^_Nonnull)(NSString *_Nonnull string, CDSemanticType type))block;
55 |
56 | /// The string representation without semantics
57 | - (nonnull NSString *)string;
58 |
59 | @end
60 |
--------------------------------------------------------------------------------
/ClassDump/Models/CDSemanticString.m:
--------------------------------------------------------------------------------
1 | //
2 | // CDSemanticString.m
3 | // ClassDump
4 | //
5 | // Created by Leptos on 1/1/23.
6 | // Copyright © 2023 Leptos. All rights reserved.
7 | //
8 |
9 | #import "CDSemanticString.h"
10 |
11 | @interface CDSemanticStringStaple : NSObject
12 | @property (strong, nonatomic) NSString *string;
13 | @property (nonatomic) CDSemanticType type;
14 | @end
15 |
16 | @implementation CDSemanticStringStaple
17 | @end
18 |
19 |
20 | @implementation CDSemanticString {
21 | NSMutableArray *_components;
22 | }
23 |
24 | - (instancetype)init {
25 | if (self = [super init]) {
26 | _length = 0;
27 | _components = [NSMutableArray array];
28 | }
29 | return self;
30 | }
31 |
32 | - (void)appendSemanticString:(CDSemanticString *)semanticString {
33 | [_components addObjectsFromArray:semanticString->_components];
34 | _length += semanticString.length;
35 | }
36 |
37 | - (void)appendString:(NSString *)string semanticType:(CDSemanticType)type {
38 | if (string.length > 0) {
39 | CDSemanticStringStaple *staple = [CDSemanticStringStaple new];
40 | staple.string = string;
41 | staple.type = type;
42 | [_components addObject:staple];
43 | _length += string.length;
44 | }
45 | }
46 |
47 | - (BOOL)startsWithChar:(char)character {
48 | char *bytes = &character;
49 | NSString *suffix = [[NSString alloc] initWithBytesNoCopy:bytes length:1 encoding:NSASCIIStringEncoding freeWhenDone:NO];
50 | return [_components.firstObject.string hasPrefix:suffix];
51 | }
52 |
53 | - (BOOL)endWithChar:(char)character {
54 | char *bytes = &character;
55 | NSString *suffix = [[NSString alloc] initWithBytesNoCopy:bytes length:1 encoding:NSASCIIStringEncoding freeWhenDone:NO];
56 | return [_components.lastObject.string hasSuffix:suffix];
57 | }
58 |
59 | - (void)enumerateTypesUsingBlock:(void (NS_NOESCAPE ^)(NSString *string, CDSemanticType type))block {
60 | for (CDSemanticStringStaple *staple in _components) {
61 | block(staple.string, staple.type);
62 | }
63 | }
64 |
65 | - (void)enumerateLongestEffectiveRangesUsingBlock:(void (NS_NOESCAPE ^)(NSString *string, CDSemanticType type))block {
66 | CDSemanticType activeStapleType = CDSemanticTypeStandard;
67 | NSMutableString *concatString = nil;
68 | for (CDSemanticStringStaple *staple in _components) {
69 | if ((concatString == nil) || (staple.type != activeStapleType)) {
70 | if (concatString != nil) {
71 | block([concatString copy], activeStapleType);
72 | }
73 | concatString = [NSMutableString stringWithString:staple.string];
74 | activeStapleType = staple.type;
75 | } else {
76 | [concatString appendString:staple.string];
77 | }
78 | }
79 | if (concatString != nil) {
80 | block([concatString copy], activeStapleType);
81 | }
82 | }
83 |
84 | - (NSString *)string {
85 | NSMutableString *build = [NSMutableString string];
86 | for (CDSemanticStringStaple *staple in _components) {
87 | [build appendString:staple.string];
88 | }
89 | return [build copy];
90 | }
91 |
92 | @end
93 |
--------------------------------------------------------------------------------
/ClassDump/Models/CDVariableModel.h:
--------------------------------------------------------------------------------
1 | //
2 | // CDVariableModel.h
3 | // ClassDump
4 | //
5 | // Created by Leptos on 12/8/22.
6 | // Copyright © 2022 Leptos. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | #import
12 |
13 | @interface CDVariableModel : NSObject
14 |
15 | @property (strong, nonatomic) NSString *name;
16 | @property (strong, nonatomic) CDParseType *type;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/ClassDump/Models/CDVariableModel.m:
--------------------------------------------------------------------------------
1 | //
2 | // CDVariableModel.m
3 | // ClassDump
4 | //
5 | // Created by Leptos on 12/8/22.
6 | // Copyright © 2022 Leptos. All rights reserved.
7 | //
8 |
9 | #import "CDVariableModel.h"
10 |
11 | @implementation CDVariableModel
12 |
13 | - (BOOL)isEqual:(id)object {
14 | if ([object isKindOfClass:[self class]]) {
15 | __typeof(self) casted = (__typeof(casted))object;
16 | return (self.name == casted.name || [self.name isEqualToString:casted.name]) &&
17 | (self.type == casted.type || [self.type isEqual:casted.type]);
18 | }
19 | return NO;
20 | }
21 |
22 | - (NSString *)description {
23 | return [self.type stringForVariableName:self.name];
24 | }
25 |
26 | - (NSString *)debugDescription {
27 | return [NSString stringWithFormat:@"<%@: %p> {name: '%@', type: %@}",
28 | [self class], self, self.name, self.type.debugDescription];
29 | }
30 |
31 | @end
32 |
--------------------------------------------------------------------------------
/ClassDump/Models/NSArray+CDFiltering.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSArray+CDFiltering.h
3 | // ClassDump
4 | //
5 | // Created by Leptos on 2/26/24.
6 | // Copyright © 2024 Leptos. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface NSArray<__covariant ObjectType> (CDFiltering)
12 |
13 | - (NSArray *)cd_uniqueObjects;
14 | - (NSArray *)cd_filterObjectsIgnoring:(NSSet *)ignoreSet;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/ClassDump/Models/NSArray+CDFiltering.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSArray+CDFiltering.m
3 | // ClassDump
4 | //
5 | // Created by Leptos on 2/26/24.
6 | // Copyright © 2024 Leptos. All rights reserved.
7 | //
8 |
9 | #import "NSArray+CDFiltering.h"
10 |
11 | @implementation NSArray (CDFiltering)
12 |
13 | - (NSArray *)cd_uniqueObjects {
14 | NSMutableArray *result = [NSMutableArray arrayWithCapacity:self.count];
15 |
16 | for (id object in self) {
17 | if ([result containsObject:object]) {
18 | continue;
19 | }
20 | [result addObject:object];
21 | }
22 |
23 | return result;
24 | }
25 |
26 | - (NSArray *)cd_filterObjectsIgnoring:(NSSet *)ignoreSet {
27 | NSMutableArray *result = [NSMutableArray arrayWithCapacity:self.count];
28 |
29 | for (id object in self) {
30 | if ([ignoreSet containsObject:object]) {
31 | continue;
32 | }
33 | [result addObject:object];
34 | }
35 |
36 | return result;
37 | }
38 |
39 | @end
40 |
--------------------------------------------------------------------------------
/ClassDump/Models/ParseTypes/CDArrayType.h:
--------------------------------------------------------------------------------
1 | //
2 | // CDArrayType.h
3 | // ClassDump
4 | //
5 | // Created by Leptos on 12/8/22.
6 | // Copyright © 2022 Leptos. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | /// Type representing a C array
12 | @interface CDArrayType : CDParseType
13 | /// Type of elements in the array
14 | @property (strong, nonatomic) CDParseType *type;
15 | /// Number of elements in the array
16 | @property (nonatomic) NSUInteger size;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/ClassDump/Models/ParseTypes/CDArrayType.m:
--------------------------------------------------------------------------------
1 | //
2 | // CDArrayType.m
3 | // ClassDump
4 | //
5 | // Created by Leptos on 12/8/22.
6 | // Copyright © 2022 Leptos. All rights reserved.
7 | //
8 |
9 | #import "CDArrayType.h"
10 |
11 | @implementation CDArrayType
12 |
13 | - (CDSemanticString *)semanticStringForVariableName:(NSString *)varName {
14 | CDSemanticString *build = [CDSemanticString new];
15 | CDSemanticString *modifiersString = [self modifiersSemanticString];
16 | if (modifiersString.length > 0) {
17 | [build appendSemanticString:modifiersString];
18 | [build appendString:@" " semanticType:CDSemanticTypeStandard];
19 | }
20 |
21 | NSMutableArray *arrayStack = [NSMutableArray array];
22 |
23 | CDParseType *headType = self;
24 | while ([headType isKindOfClass:[CDArrayType class]]) {
25 | CDArrayType *arrayType = (__kindof CDParseType *)headType;
26 | [arrayStack addObject:arrayType];
27 | headType = arrayType.type;
28 | }
29 |
30 | [build appendSemanticString:[headType semanticStringForVariableName:varName]];
31 |
32 | [arrayStack enumerateObjectsUsingBlock:^(CDArrayType *arrayType, NSUInteger idx, BOOL *stop) {
33 | [build appendString:@"[" semanticType:CDSemanticTypeStandard];
34 | [build appendString:[NSString stringWithFormat:@"%lu", (unsigned long)arrayType.size] semanticType:CDSemanticTypeNumeric];
35 | [build appendString:@"]" semanticType:CDSemanticTypeStandard];
36 | }];
37 |
38 | return build;
39 | }
40 |
41 | - (NSSet *)classReferences {
42 | return [self.type classReferences];
43 | }
44 |
45 | - (NSSet *)protocolReferences {
46 | return [self.type protocolReferences];
47 | }
48 |
49 | - (BOOL)isEqual:(id)object {
50 | if ([object isKindOfClass:[self class]]) {
51 | __typeof(self) casted = (__typeof(casted))object;
52 | return (self.modifiers == casted.modifiers || [self.modifiers isEqualToArray:casted.modifiers]) &&
53 | (self.type == casted.type || [self.type isEqual:casted.type]) &&
54 | (self.size == casted.size);
55 | }
56 | return NO;
57 | }
58 |
59 | - (NSString *)debugDescription {
60 | return [NSString stringWithFormat:@"<%@: %p> {modifiers: '%@', type: %@, size: %lu}",
61 | [self class], self, [self modifiersString], self.type.debugDescription, (unsigned long)self.size];
62 | }
63 |
64 | @end
65 |
--------------------------------------------------------------------------------
/ClassDump/Models/ParseTypes/CDBitFieldType.h:
--------------------------------------------------------------------------------
1 | //
2 | // CDBitFieldType.h
3 | // ClassDump
4 | //
5 | // Created by Leptos on 12/8/22.
6 | // Copyright © 2022 Leptos. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | /// Type representing a bit-field in a record
12 | @interface CDBitFieldType : CDParseType
13 | /// Width of the bit-fields (in bits)
14 | @property (nonatomic) NSUInteger width;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/ClassDump/Models/ParseTypes/CDBitFieldType.m:
--------------------------------------------------------------------------------
1 | //
2 | // CDBitFieldType.m
3 | // ClassDump
4 | //
5 | // Created by Leptos on 12/8/22.
6 | // Copyright © 2022 Leptos. All rights reserved.
7 | //
8 |
9 | #import "CDBitFieldType.h"
10 |
11 | @implementation CDBitFieldType
12 |
13 | - (CDSemanticString *)semanticStringForVariableName:(NSString *)varName {
14 | CDSemanticString *build = [CDSemanticString new];
15 | CDSemanticString *modifiersString = [self modifiersSemanticString];
16 | if (modifiersString.length > 0) {
17 | [build appendSemanticString:modifiersString];
18 | [build appendString:@" " semanticType:CDSemanticTypeStandard];
19 | }
20 |
21 | NSUInteger const bitWidth = self.width;
22 |
23 | NSString *type = nil;
24 | #ifndef __CHAR_BIT__
25 | # error __CHAR_BIT__ must be defined
26 | #endif
27 | /* all bitwidth base-types are unsigned, because that's the typical use case */
28 | if (bitWidth <= __CHAR_BIT__) {
29 | type = @"unsigned char";
30 | }
31 | #ifdef __SIZEOF_SHORT__
32 | else if (bitWidth <= (__SIZEOF_SHORT__ * __CHAR_BIT__)) {
33 | type = @"unsigned short";
34 | }
35 | #endif
36 | #ifdef __SIZEOF_INT__
37 | else if (bitWidth <= (__SIZEOF_INT__ * __CHAR_BIT__)) {
38 | type = @"unsigned int";
39 | }
40 | #endif
41 | #ifdef __SIZEOF_LONG__
42 | else if (bitWidth <= (__SIZEOF_LONG__ * __CHAR_BIT__)) {
43 | type = @"unsigned long";
44 | }
45 | #endif
46 | #ifdef __SIZEOF_LONG_LONG__
47 | else if (bitWidth <= (__SIZEOF_LONG_LONG__ * __CHAR_BIT__)) {
48 | type = @"unsigned long long";
49 | }
50 | #endif
51 | #ifdef __SIZEOF_INT128__
52 | else if (bitWidth <= (__SIZEOF_INT128__ * __CHAR_BIT__)) {
53 | type = @"unsigned __int128";
54 | }
55 | #endif
56 | else {
57 | NSAssert(NO, @"width of bit-field exceeds width of any known type");
58 | type = @"unsigned";
59 | }
60 |
61 | [build appendString:type semanticType:CDSemanticTypeKeyword];
62 |
63 | if (varName != nil) {
64 | [build appendString:@" " semanticType:CDSemanticTypeStandard];
65 | [build appendString:varName semanticType:CDSemanticTypeVariable];
66 | }
67 |
68 | [build appendString:@" : " semanticType:CDSemanticTypeStandard];
69 | [build appendString:[NSString stringWithFormat:@"%lu", (unsigned long)self.width] semanticType:CDSemanticTypeNumeric];
70 | return build;
71 | }
72 |
73 | - (BOOL)isEqual:(id)object {
74 | if ([object isKindOfClass:[self class]]) {
75 | __typeof(self) casted = (__typeof(casted))object;
76 | return (self.modifiers == casted.modifiers || [self.modifiers isEqualToArray:casted.modifiers]) &&
77 | (self.width == casted.width);
78 | }
79 | return NO;
80 | }
81 |
82 | - (NSString *)debugDescription {
83 | return [NSString stringWithFormat:@"<%@: %p> {modifiers: '%@', width: %lu}",
84 | [self class], self, [self modifiersString], (unsigned long)self.width];
85 | }
86 |
87 | @end
88 |
--------------------------------------------------------------------------------
/ClassDump/Models/ParseTypes/CDBlockType.h:
--------------------------------------------------------------------------------
1 | //
2 | // CDBlockType.h
3 | // ClassDump
4 | //
5 | // Created by Leptos on 12/15/22.
6 | // Copyright © 2022 Leptos. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | /// Type representing a block
12 | @interface CDBlockType : CDParseType
13 | /// The type that this block returns
14 | @property (nullable, strong, nonatomic) CDParseType *returnType;
15 | /// The types of the parameters to this block
16 | @property (nullable, strong, nonatomic) NSArray *parameterTypes;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/ClassDump/Models/ParseTypes/CDBlockType.m:
--------------------------------------------------------------------------------
1 | //
2 | // CDBlockType.m
3 | // ClassDump
4 | //
5 | // Created by Leptos on 12/15/22.
6 | // Copyright © 2022 Leptos. All rights reserved.
7 | //
8 |
9 | #import "CDBlockType.h"
10 |
11 | @implementation CDBlockType
12 |
13 | - (CDSemanticString *)semanticStringForVariableName:(NSString *)varName {
14 | CDSemanticString *build = [CDSemanticString new];
15 | CDSemanticString *modifiersString = [self modifiersSemanticString];
16 |
17 | if (self.returnType != nil && self.parameterTypes != nil) {
18 | [build appendSemanticString:[self.returnType semanticStringForVariableName:nil]];
19 | [build appendString:@" (^" semanticType:CDSemanticTypeStandard];
20 |
21 | if (modifiersString.length > 0) {
22 | [build appendSemanticString:modifiersString];
23 | }
24 | if (modifiersString.length > 0 && varName != nil) {
25 | [build appendString:@" " semanticType:CDSemanticTypeStandard];
26 | }
27 | if (varName != nil) {
28 | [build appendString:varName semanticType:CDSemanticTypeVariable];
29 | }
30 | [build appendString:@")(" semanticType:CDSemanticTypeStandard];
31 |
32 | NSUInteger const paramCount = self.parameterTypes.count;
33 | if (paramCount == 0) {
34 | [build appendString:@"void" semanticType:CDSemanticTypeKeyword];
35 | } else {
36 | [self.parameterTypes enumerateObjectsUsingBlock:^(CDParseType *paramType, NSUInteger idx, BOOL *stop) {
37 | [build appendSemanticString:[paramType semanticStringForVariableName:nil]];
38 | if ((idx + 1) < paramCount) {
39 | [build appendString:@", " semanticType:CDSemanticTypeStandard];
40 | }
41 | }];
42 | }
43 | [build appendString:@")" semanticType:CDSemanticTypeStandard];
44 | } else {
45 | if (modifiersString.length > 0) {
46 | [build appendSemanticString:modifiersString];
47 | [build appendString:@" " semanticType:CDSemanticTypeStandard];
48 | }
49 | [build appendString:@"id" semanticType:CDSemanticTypeKeyword];
50 | [build appendString:@" " semanticType:CDSemanticTypeStandard];
51 | [build appendString:@"/* block */" semanticType:CDSemanticTypeComment];
52 | if (varName != nil) {
53 | [build appendString:@" " semanticType:CDSemanticTypeStandard];
54 | [build appendString:varName semanticType:CDSemanticTypeVariable];
55 | }
56 | }
57 | return build;
58 | }
59 |
60 | - (NSSet *)classReferences {
61 | NSMutableSet *build = [NSMutableSet set];
62 | NSSet *returnReferences = [self.returnType classReferences];
63 | if (returnReferences != nil) {
64 | [build unionSet:returnReferences];
65 | }
66 | for (CDParseType *paramType in self.parameterTypes) {
67 | NSSet *paramReferences = [paramType classReferences];
68 | if (paramReferences != nil) {
69 | [build unionSet:paramReferences];
70 | }
71 | }
72 | return build;
73 | }
74 |
75 | - (NSSet *)protocolReferences {
76 | NSMutableSet *build = [NSMutableSet set];
77 | NSSet *returnReferences = [self.returnType protocolReferences];
78 | if (returnReferences != nil) {
79 | [build unionSet:returnReferences];
80 | }
81 | for (CDParseType *paramType in self.parameterTypes) {
82 | NSSet *paramReferences = [paramType protocolReferences];
83 | if (paramReferences != nil) {
84 | [build unionSet:paramReferences];
85 | }
86 | }
87 | return build;
88 | }
89 |
90 | - (BOOL)isEqual:(id)object {
91 | if ([object isKindOfClass:[self class]]) {
92 | __typeof(self) casted = (__typeof(casted))object;
93 | return (self.modifiers == casted.modifiers || [self.modifiers isEqualToArray:casted.modifiers]) &&
94 | (self.returnType == casted.returnType || [self.returnType isEqual:casted.returnType]) &&
95 | (self.parameterTypes == casted.parameterTypes || [self.parameterTypes isEqualToArray:casted.parameterTypes]);
96 | }
97 | return NO;
98 | }
99 |
100 | - (NSString *)debugDescription {
101 | return [NSString stringWithFormat:@"<%@: %p> {modifiers: '%@', returnType: %@, parameterTypes: %@}",
102 | [self class], self, [self modifiersString], self.returnType, self.parameterTypes];
103 | }
104 |
105 | @end
106 |
--------------------------------------------------------------------------------
/ClassDump/Models/ParseTypes/CDObjectType.h:
--------------------------------------------------------------------------------
1 | //
2 | // CDObjectType.h
3 | // ClassDump
4 | //
5 | // Created by Leptos on 12/8/22.
6 | // Copyright © 2022 Leptos. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | /// Type representing an Objective-C object
12 | @interface CDObjectType : CDParseType
13 | /// The name of the class of the object
14 | ///
15 | /// If this value is @c nil the type is @c id
16 | @property (nullable, strong, nonatomic) NSString *className;
17 | /// The names of the protocols the object conforms to
18 | @property (nullable, strong, nonatomic) NSArray *protocolNames;
19 |
20 | @end
21 |
--------------------------------------------------------------------------------
/ClassDump/Models/ParseTypes/CDObjectType.m:
--------------------------------------------------------------------------------
1 | //
2 | // CDObjectType.m
3 | // ClassDump
4 | //
5 | // Created by Leptos on 12/8/22.
6 | // Copyright © 2022 Leptos. All rights reserved.
7 | //
8 |
9 | #import "CDObjectType.h"
10 |
11 | @implementation CDObjectType
12 |
13 | - (CDSemanticString *)semanticStringForVariableName:(NSString *)varName {
14 | CDSemanticString *build = [CDSemanticString new];
15 | CDSemanticString *modifiersString = [self modifiersSemanticString];
16 | if (modifiersString.length > 0) {
17 | [build appendSemanticString:modifiersString];
18 | [build appendString:@" " semanticType:CDSemanticTypeStandard];
19 | }
20 |
21 | BOOL const hasClassName = (self.className != nil);
22 |
23 | if (hasClassName) {
24 | [build appendString:self.className semanticType:CDSemanticTypeClass];
25 | } else {
26 | [build appendString:@"id" semanticType:CDSemanticTypeKeyword];
27 | }
28 |
29 | NSArray *protocolNames = self.protocolNames;
30 | NSUInteger const protocolNameCount = protocolNames.count;
31 | if (protocolNames.count > 0) {
32 | [build appendString:@"<" semanticType:CDSemanticTypeStandard];
33 | [protocolNames enumerateObjectsUsingBlock:^(NSString *protocolName, NSUInteger idx, BOOL *stop) {
34 | [build appendString:protocolName semanticType:CDSemanticTypeProtocol];
35 | if ((idx + 1) < protocolNameCount) {
36 | [build appendString:@", " semanticType:CDSemanticTypeStandard];
37 | }
38 | }];
39 | [build appendString:@">" semanticType:CDSemanticTypeStandard];
40 | }
41 | if (hasClassName) {
42 | [build appendString:@" *" semanticType:CDSemanticTypeStandard];
43 | }
44 |
45 | if (varName != nil) {
46 | if (!hasClassName) {
47 | [build appendString:@" " semanticType:CDSemanticTypeStandard];
48 | }
49 | [build appendString:varName semanticType:CDSemanticTypeVariable];
50 | }
51 | return build;
52 | }
53 |
54 | - (NSSet *)classReferences {
55 | NSString *className = self.className;
56 | if (className != nil) {
57 | return [NSSet setWithObject:className];
58 | }
59 | return nil;
60 | }
61 |
62 | - (NSSet *)protocolReferences {
63 | NSArray *protocolNames = self.protocolNames;
64 | if (protocolNames != nil) {
65 | return [NSSet setWithArray:protocolNames];
66 | }
67 | return nil;
68 | }
69 |
70 | - (BOOL)isEqual:(id)object {
71 | if ([object isKindOfClass:[self class]]) {
72 | __typeof(self) casted = (__typeof(casted))object;
73 | return (self.modifiers == casted.modifiers || [self.modifiers isEqualToArray:casted.modifiers]) &&
74 | (self.className == casted.className || [self.className isEqualToString:casted.className]) &&
75 | (self.protocolNames == casted.protocolNames || [self.protocolNames isEqualToArray:casted.protocolNames]);
76 | }
77 | return NO;
78 | }
79 |
80 | - (NSString *)debugDescription {
81 | return [NSString stringWithFormat:@"<%@: %p> {modifiers: '%@', className: '%@', protocolNames: %@}",
82 | [self class], self, [self modifiersString], self.className, self.protocolNames];
83 | }
84 |
85 | @end
86 |
--------------------------------------------------------------------------------
/ClassDump/Models/ParseTypes/CDParseType.h:
--------------------------------------------------------------------------------
1 | //
2 | // CDParseType.h
3 | // ClassDump
4 | //
5 | // Created by Leptos on 12/8/22.
6 | // Copyright © 2022 Leptos. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | typedef NS_ENUM(NSUInteger, CDTypeModifier) {
13 | CDTypeModifierConst,
14 | CDTypeModifierComplex,
15 | CDTypeModifierAtomic,
16 |
17 | CDTypeModifierIn,
18 | CDTypeModifierInOut,
19 | CDTypeModifierOut,
20 | CDTypeModifierBycopy,
21 | CDTypeModifierByref,
22 | CDTypeModifierOneway,
23 |
24 | /// The number of valid cases there are in @c CDTypeModifier
25 | CDTypeModifierCount
26 | };
27 |
28 | OBJC_EXTERN NSString *_Nullable NSStringFromCDTypeModifier(CDTypeModifier);
29 |
30 | /// Base class to represent a type that a variable may be
31 | @interface CDParseType : NSObject
32 |
33 | @property (nullable, strong, nonatomic) NSArray *modifiers; // array of CDTypeModifier
34 |
35 | /// A string as this type would appear in code for a given variable name.
36 | ///
37 | /// @param varName The name of the variable this type is for
38 | - (nonnull NSString *)stringForVariableName:(nullable NSString *)varName;
39 |
40 | - (nonnull NSString *)modifiersString;
41 |
42 | - (nonnull CDSemanticString *)semanticStringForVariableName:(nullable NSString *)varName;
43 |
44 | - (nonnull CDSemanticString *)modifiersSemanticString;
45 |
46 | /// Classes this type references
47 | ///
48 | /// For example, `NSCache *` references the "NSCache" class
49 | - (nullable NSSet *)classReferences;
50 | /// Protocols this type references
51 | ///
52 | /// For example, `NSCache *` references the "NSFastEnumeration" protocol
53 | - (nullable NSSet *)protocolReferences;
54 |
55 | @end
56 |
--------------------------------------------------------------------------------
/ClassDump/Models/ParseTypes/CDParseType.m:
--------------------------------------------------------------------------------
1 | //
2 | // CDParseType.m
3 | // ClassDump
4 | //
5 | // Created by Leptos on 12/8/22.
6 | // Copyright © 2022 Leptos. All rights reserved.
7 | //
8 |
9 | #import "CDParseType.h"
10 |
11 | NSString *NSStringFromCDTypeModifier(CDTypeModifier modifier) {
12 | switch (modifier) {
13 | case CDTypeModifierConst:
14 | return @"const";
15 | case CDTypeModifierComplex:
16 | return @"_Complex";
17 | case CDTypeModifierAtomic:
18 | return @"_Atomic";
19 | case CDTypeModifierIn:
20 | return @"in";
21 | case CDTypeModifierInOut:
22 | return @"inout";
23 | case CDTypeModifierOut:
24 | return @"out";
25 | case CDTypeModifierBycopy:
26 | return @"bycopy";
27 | case CDTypeModifierByref:
28 | return @"byref";
29 | case CDTypeModifierOneway:
30 | return @"oneway";
31 | default:
32 | NSCAssert(NO, @"Unknown CDTypeModifier value");
33 | return nil;
34 | }
35 | }
36 |
37 | @implementation CDParseType
38 |
39 | - (NSString *)stringForVariableName:(NSString *)varName {
40 | return [[self semanticStringForVariableName:varName] string];
41 | }
42 |
43 | - (CDSemanticString *)semanticStringForVariableName:(NSString *)varName {
44 | NSAssert(NO, @"Subclasses must implement %@", NSStringFromSelector(_cmd));
45 | return [CDSemanticString new];
46 | }
47 |
48 | - (NSString *)modifiersString {
49 | NSArray *const modifiers = self.modifiers;
50 | NSMutableArray *strings = [NSMutableArray arrayWithCapacity:modifiers.count];
51 | [modifiers enumerateObjectsUsingBlock:^(NSNumber *value, NSUInteger idx, BOOL *stop) {
52 | strings[idx] = NSStringFromCDTypeModifier(value.unsignedIntegerValue);
53 | }];
54 | return [strings componentsJoinedByString:@" "];
55 | }
56 |
57 | - (CDSemanticString *)modifiersSemanticString {
58 | NSArray *const modifiers = self.modifiers;
59 | CDSemanticString *build = [CDSemanticString new];
60 | NSUInteger const modifierCount = self.modifiers.count;
61 | [modifiers enumerateObjectsUsingBlock:^(NSNumber *value, NSUInteger idx, BOOL *stop) {
62 | NSString *string = NSStringFromCDTypeModifier(value.unsignedIntegerValue);
63 | [build appendString:string semanticType:CDSemanticTypeKeyword];
64 | if ((idx + 1) < modifierCount) {
65 | [build appendString:@" " semanticType:CDSemanticTypeStandard];
66 | }
67 | }];
68 | return build;
69 | }
70 |
71 | - (NSSet *)classReferences {
72 | return nil;
73 | }
74 |
75 | - (NSSet *)protocolReferences {
76 | return nil;
77 | }
78 |
79 | - (BOOL)isEqual:(id)object {
80 | if ([object isKindOfClass:[self class]]) {
81 | __typeof(self) casted = (__typeof(casted))object;
82 | return (self.modifiers == casted.modifiers || [self.modifiers isEqualToArray:casted.modifiers]);
83 | }
84 | return NO;
85 | }
86 |
87 | - (NSString *)description {
88 | return [self stringForVariableName:nil];
89 | }
90 |
91 | - (NSString *)debugDescription {
92 | return [NSString stringWithFormat:@"<%@: %p> {modifiers: '%@'}",
93 | [self class], self, [self modifiersString]];
94 | }
95 |
96 | @end
97 |
--------------------------------------------------------------------------------
/ClassDump/Models/ParseTypes/CDPointerType.h:
--------------------------------------------------------------------------------
1 | //
2 | // CDPointerType.h
3 | // ClassDump
4 | //
5 | // Created by Leptos on 12/8/22.
6 | // Copyright © 2022 Leptos. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | /// Type representing a pointer
12 | @interface CDPointerType : CDParseType
13 | /// The type that this pointer points to
14 | @property (nullable, strong, nonatomic) CDParseType *pointee;
15 |
16 | + (nonnull instancetype)pointerToPointee:(nonnull CDParseType *)pointee;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/ClassDump/Models/ParseTypes/CDPointerType.m:
--------------------------------------------------------------------------------
1 | //
2 | // CDPointerType.m
3 | // ClassDump
4 | //
5 | // Created by Leptos on 12/8/22.
6 | // Copyright © 2022 Leptos. All rights reserved.
7 | //
8 |
9 | #import "CDPointerType.h"
10 |
11 | @implementation CDPointerType
12 |
13 | + (nonnull instancetype)pointerToPointee:(nonnull CDParseType *)pointee {
14 | CDPointerType *ret = [self new];
15 | ret.pointee = pointee;
16 | return ret;
17 | }
18 |
19 | - (CDSemanticString *)semanticStringForVariableName:(NSString *)varName {
20 | CDSemanticString *build = [CDSemanticString new];
21 | CDSemanticString *modifiersString = [self modifiersSemanticString];
22 | if (modifiersString.length > 0) {
23 | [build appendSemanticString:modifiersString];
24 | [build appendString:@" " semanticType:CDSemanticTypeStandard];
25 | }
26 | [build appendSemanticString:[self.pointee semanticStringForVariableName:nil]];
27 | if (![build endWithChar:'*']) {
28 | [build appendString:@" " semanticType:CDSemanticTypeStandard];
29 | }
30 | [build appendString:@"*" semanticType:CDSemanticTypeStandard];
31 | if (varName != nil) {
32 | [build appendString:varName semanticType:CDSemanticTypeVariable];
33 | }
34 | return build;
35 | }
36 |
37 | - (NSSet *)classReferences {
38 | return [self.pointee classReferences];
39 | }
40 |
41 | - (NSSet *)protocolReferences {
42 | return [self.pointee protocolReferences];
43 | }
44 |
45 | - (BOOL)isEqual:(id)object {
46 | if ([object isKindOfClass:[self class]]) {
47 | __typeof(self) casted = (__typeof(casted))object;
48 | return (self.modifiers == casted.modifiers || [self.modifiers isEqualToArray:casted.modifiers]) &&
49 | (self.pointee == casted.pointee || [self.pointee isEqual:casted.pointee]);
50 | }
51 | return NO;
52 | }
53 |
54 | - (NSString *)debugDescription {
55 | return [NSString stringWithFormat:@"<%@: %p> {modifiers: '%@', pointee: %@}",
56 | [self class], self, [self modifiersString], self.pointee.debugDescription];
57 | }
58 |
59 | @end
60 |
--------------------------------------------------------------------------------
/ClassDump/Models/ParseTypes/CDPrimitiveType.h:
--------------------------------------------------------------------------------
1 | //
2 | // CDPrimitiveType.h
3 | // ClassDump
4 | //
5 | // Created by Leptos on 12/8/22.
6 | // Copyright © 2022 Leptos. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | typedef NS_ENUM(NSUInteger, CDPrimitiveRawType) {
12 | CDPrimitiveRawTypeVoid,
13 |
14 | CDPrimitiveRawTypeChar,
15 | CDPrimitiveRawTypeInt,
16 | CDPrimitiveRawTypeShort,
17 | CDPrimitiveRawTypeLong,
18 | CDPrimitiveRawTypeLongLong,
19 | CDPrimitiveRawTypeInt128,
20 |
21 | CDPrimitiveRawTypeUnsignedChar,
22 | CDPrimitiveRawTypeUnsignedInt,
23 | CDPrimitiveRawTypeUnsignedShort,
24 | CDPrimitiveRawTypeUnsignedLong,
25 | CDPrimitiveRawTypeUnsignedLongLong,
26 | CDPrimitiveRawTypeUnsignedInt128,
27 |
28 | CDPrimitiveRawTypeFloat,
29 | CDPrimitiveRawTypeDouble,
30 | CDPrimitiveRawTypeLongDouble,
31 |
32 | CDPrimitiveRawTypeBool,
33 | CDPrimitiveRawTypeClass,
34 | CDPrimitiveRawTypeSel,
35 |
36 | CDPrimitiveRawTypeFunction,
37 |
38 | /// @note This is not a real type.
39 | /// @discussion A blank type represents a type that
40 | /// is encoded to a space character. There are multiple
41 | /// types that are encoded to a space character, and it
42 | /// is not possible for us to discern the difference
43 | /// between them.
44 | CDPrimitiveRawTypeBlank,
45 | /// @note This is not a real type.
46 | /// @discussion An empty type represents a type that
47 | /// was not encoded. Usually this occurs when types
48 | /// that do not exist in Objective-C are bridged into
49 | /// Objective-C (this should only occur at runtime).
50 | CDPrimitiveRawTypeEmpty,
51 | };
52 |
53 | OBJC_EXTERN NSString *_Nullable NSStringFromCDPrimitiveRawType(CDPrimitiveRawType);
54 |
55 | @interface CDPrimitiveType : CDParseType
56 |
57 | @property (nonatomic) CDPrimitiveRawType rawType;
58 |
59 | + (nonnull instancetype)primitiveWithRawType:(CDPrimitiveRawType)rawType;
60 |
61 | @end
62 |
--------------------------------------------------------------------------------
/ClassDump/Models/ParseTypes/CDPrimitiveType.m:
--------------------------------------------------------------------------------
1 | //
2 | // CDPrimitiveType.m
3 | // ClassDump
4 | //
5 | // Created by Leptos on 12/8/22.
6 | // Copyright © 2022 Leptos. All rights reserved.
7 | //
8 |
9 | #import "CDPrimitiveType.h"
10 |
11 | NSString *NSStringFromCDPrimitiveRawType(CDPrimitiveRawType rawType) {
12 | switch (rawType) {
13 | case CDPrimitiveRawTypeVoid:
14 | return @"void";
15 | case CDPrimitiveRawTypeChar:
16 | return @"char";
17 | case CDPrimitiveRawTypeInt:
18 | return @"int";
19 | case CDPrimitiveRawTypeShort:
20 | return @"short";
21 | case CDPrimitiveRawTypeLong:
22 | return @"long";
23 | case CDPrimitiveRawTypeLongLong:
24 | return @"long long";
25 | case CDPrimitiveRawTypeInt128:
26 | return @"__int128";
27 | case CDPrimitiveRawTypeUnsignedChar:
28 | return @"unsigned char";
29 | case CDPrimitiveRawTypeUnsignedInt:
30 | return @"unsigned int";
31 | case CDPrimitiveRawTypeUnsignedShort:
32 | return @"unsigned short";
33 | case CDPrimitiveRawTypeUnsignedLong:
34 | return @"unsigned long";
35 | case CDPrimitiveRawTypeUnsignedLongLong:
36 | return @"unsigned long long";
37 | case CDPrimitiveRawTypeUnsignedInt128:
38 | return @"unsigned __int128";
39 | case CDPrimitiveRawTypeFloat:
40 | return @"float";
41 | case CDPrimitiveRawTypeDouble:
42 | return @"double";
43 | case CDPrimitiveRawTypeLongDouble:
44 | return @"long double";
45 | case CDPrimitiveRawTypeBool:
46 | return @"BOOL";
47 | case CDPrimitiveRawTypeClass:
48 | return @"Class";
49 | case CDPrimitiveRawTypeSel:
50 | return @"SEL";
51 | case CDPrimitiveRawTypeFunction:
52 | return @"void /* function */";
53 | case CDPrimitiveRawTypeBlank:
54 | return @"void /* unknown type, blank encoding */";
55 | case CDPrimitiveRawTypeEmpty:
56 | return @"void /* unknown type, empty encoding */";
57 | }
58 | }
59 |
60 | @implementation CDPrimitiveType
61 |
62 | + (nonnull instancetype)primitiveWithRawType:(CDPrimitiveRawType)rawType {
63 | CDPrimitiveType *ret = [self new];
64 | ret.rawType = rawType;
65 | return ret;
66 | }
67 |
68 | - (CDSemanticString *)semanticStringForVariableName:(NSString *)varName {
69 | CDSemanticString *build = [CDSemanticString new];
70 | CDSemanticString *modifiersString = [self modifiersSemanticString];
71 | if (modifiersString.length > 0) {
72 | [build appendSemanticString:modifiersString];
73 | [build appendString:@" " semanticType:CDSemanticTypeStandard];
74 | }
75 | switch (self.rawType) {
76 | case CDPrimitiveRawTypeVoid:
77 | [build appendString:@"void" semanticType:CDSemanticTypeKeyword];
78 | break;
79 | case CDPrimitiveRawTypeChar:
80 | [build appendString:@"char" semanticType:CDSemanticTypeKeyword];
81 | break;
82 | case CDPrimitiveRawTypeInt:
83 | [build appendString:@"int" semanticType:CDSemanticTypeKeyword];
84 | break;
85 | case CDPrimitiveRawTypeShort:
86 | [build appendString:@"short" semanticType:CDSemanticTypeKeyword];
87 | break;
88 | case CDPrimitiveRawTypeLong:
89 | [build appendString:@"long" semanticType:CDSemanticTypeKeyword];
90 | break;
91 | case CDPrimitiveRawTypeLongLong:
92 | [build appendString:@"long long" semanticType:CDSemanticTypeKeyword];
93 | break;
94 | case CDPrimitiveRawTypeInt128:
95 | [build appendString:@"__int128" semanticType:CDSemanticTypeKeyword];
96 | break;
97 | case CDPrimitiveRawTypeUnsignedChar:
98 | [build appendString:@"unsigned char" semanticType:CDSemanticTypeKeyword];
99 | break;
100 | case CDPrimitiveRawTypeUnsignedInt:
101 | [build appendString:@"unsigned int" semanticType:CDSemanticTypeKeyword];
102 | break;
103 | case CDPrimitiveRawTypeUnsignedShort:
104 | [build appendString:@"unsigned short" semanticType:CDSemanticTypeKeyword];
105 | break;
106 | case CDPrimitiveRawTypeUnsignedLong:
107 | [build appendString:@"unsigned long" semanticType:CDSemanticTypeKeyword];
108 | break;
109 | case CDPrimitiveRawTypeUnsignedLongLong:
110 | [build appendString:@"unsigned long long" semanticType:CDSemanticTypeKeyword];
111 | break;
112 | case CDPrimitiveRawTypeUnsignedInt128:
113 | [build appendString:@"unsigned __int128" semanticType:CDSemanticTypeKeyword];
114 | break;
115 | case CDPrimitiveRawTypeFloat:
116 | [build appendString:@"float" semanticType:CDSemanticTypeKeyword];
117 | break;
118 | case CDPrimitiveRawTypeDouble:
119 | [build appendString:@"double" semanticType:CDSemanticTypeKeyword];
120 | break;
121 | case CDPrimitiveRawTypeLongDouble:
122 | [build appendString:@"long double" semanticType:CDSemanticTypeKeyword];
123 | break;
124 | case CDPrimitiveRawTypeBool:
125 | [build appendString:@"BOOL" semanticType:CDSemanticTypeKeyword];
126 | break;
127 | case CDPrimitiveRawTypeClass:
128 | [build appendString:@"Class" semanticType:CDSemanticTypeKeyword];
129 | break;
130 | case CDPrimitiveRawTypeSel:
131 | [build appendString:@"SEL" semanticType:CDSemanticTypeKeyword];
132 | break;
133 | case CDPrimitiveRawTypeFunction:
134 | [build appendString:@"void" semanticType:CDSemanticTypeKeyword];
135 | [build appendString:@" " semanticType:CDSemanticTypeStandard];
136 | [build appendString:@"/* function */" semanticType:CDSemanticTypeComment];
137 | break;
138 | case CDPrimitiveRawTypeBlank:
139 | [build appendString:@"void" semanticType:CDSemanticTypeKeyword];
140 | [build appendString:@" " semanticType:CDSemanticTypeStandard];
141 | [build appendString:@"/* unknown type, blank encoding */" semanticType:CDSemanticTypeComment];
142 | break;
143 | case CDPrimitiveRawTypeEmpty:
144 | [build appendString:@"void" semanticType:CDSemanticTypeKeyword];
145 | [build appendString:@" " semanticType:CDSemanticTypeStandard];
146 | [build appendString:@"/* unknown type, empty encoding */" semanticType:CDSemanticTypeComment];
147 | break;
148 | }
149 | if (varName != nil) {
150 | [build appendString:@" " semanticType:CDSemanticTypeStandard];
151 | [build appendString:varName semanticType:CDSemanticTypeVariable];
152 | }
153 | return build;
154 | }
155 |
156 | - (BOOL)isEqual:(id)object {
157 | if ([object isKindOfClass:[self class]]) {
158 | __typeof(self) casted = (__typeof(casted))object;
159 | return (self.modifiers == casted.modifiers || [self.modifiers isEqualToArray:casted.modifiers]) &&
160 | self.rawType == casted.rawType;
161 | }
162 | return NO;
163 | }
164 |
165 | - (NSString *)debugDescription {
166 | return [NSString stringWithFormat:@"<%@: %p> {modifiers: '%@', rawType: '%@'}",
167 | [self class], self, [self modifiersString], NSStringFromCDPrimitiveRawType(self.rawType)];
168 | }
169 |
170 | @end
171 |
--------------------------------------------------------------------------------
/ClassDump/Models/ParseTypes/CDRecordType.h:
--------------------------------------------------------------------------------
1 | //
2 | // CDRecordType.h
3 | // ClassDump
4 | //
5 | // Created by Leptos on 12/8/22.
6 | // Copyright © 2022 Leptos. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | /// Type representing a @c struct or @c union
13 | @interface CDRecordType : CDParseType
14 | /// The name of the record
15 | ///
16 | /// If the type is anonymous, this value will be @c nil
17 | @property (nullable, strong, nonatomic) NSString *name;
18 | /// @c YES if the receiver represents a @c union
19 | /// otherwise the receiver represents a @c struct
20 | @property (nonatomic) BOOL isUnion;
21 | /// The fields of the record
22 | ///
23 | /// A @c struct stores a value in each field.
24 | /// A @c union stores a single value that can be accessed
25 | /// as different types by each field.
26 | /// @note If this value is @c nil the type is incomplete.
27 | /// If the value is non-nil but empty (i.e. an empty array),
28 | /// the record is defined to have no fields.
29 | @property (nullable, strong, nonatomic) NSArray *fields;
30 |
31 | @end
32 |
--------------------------------------------------------------------------------
/ClassDump/Models/ParseTypes/CDRecordType.m:
--------------------------------------------------------------------------------
1 | //
2 | // CDRecordType.m
3 | // ClassDump
4 | //
5 | // Created by Leptos on 12/8/22.
6 | // Copyright © 2022 Leptos. All rights reserved.
7 | //
8 |
9 | #import "CDRecordType.h"
10 |
11 | @implementation CDRecordType
12 |
13 | - (CDSemanticString *)semanticStringForVariableName:(NSString *)varName {
14 | CDSemanticString *build = [CDSemanticString new];
15 | CDSemanticString *modifiersString = [self modifiersSemanticString];
16 | if (modifiersString.length > 0) {
17 | [build appendSemanticString:modifiersString];
18 | [build appendString:@" " semanticType:CDSemanticTypeStandard];
19 | }
20 | [build appendString:(self.isUnion ? @"union" : @"struct") semanticType:CDSemanticTypeKeyword];
21 | if (self.name != nil) {
22 | [build appendString:@" " semanticType:CDSemanticTypeStandard];
23 | [build appendString:self.name semanticType:CDSemanticTypeRecordName];
24 | }
25 | if (self.fields != nil) {
26 | [build appendString:@" { " semanticType:CDSemanticTypeStandard];
27 |
28 | unsigned fieldName = 0;
29 |
30 | for (CDVariableModel *variableModel in self.fields) {
31 | NSString *variableName = variableModel.name;
32 | if (variableName == nil) {
33 | variableName = [NSString stringWithFormat:@"x%u", fieldName++];
34 | }
35 | [build appendSemanticString:[variableModel.type semanticStringForVariableName:variableName]];
36 | [build appendString:@"; " semanticType:CDSemanticTypeStandard];
37 | }
38 | [build appendString:@"}" semanticType:CDSemanticTypeStandard];
39 | }
40 | if (varName != nil) {
41 | [build appendString:@" " semanticType:CDSemanticTypeStandard];
42 | [build appendString:varName semanticType:CDSemanticTypeVariable];
43 | }
44 | return build;
45 | }
46 |
47 | - (NSSet *)classReferences {
48 | NSMutableSet *build = [NSMutableSet set];
49 | for (CDVariableModel *variableModel in self.fields) {
50 | NSSet *paramReferences = [variableModel.type classReferences];
51 | if (paramReferences != nil) {
52 | [build unionSet:paramReferences];
53 | }
54 | }
55 | return build;
56 | }
57 |
58 | - (NSSet *)protocolReferences {
59 | NSMutableSet *build = [NSMutableSet set];
60 | for (CDVariableModel *variableModel in self.fields) {
61 | NSSet *paramReferences = [variableModel.type protocolReferences];
62 | if (paramReferences != nil) {
63 | [build unionSet:paramReferences];
64 | }
65 | }
66 | return build;
67 | }
68 |
69 | - (BOOL)isEqual:(id)object {
70 | if ([object isKindOfClass:[self class]]) {
71 | __typeof(self) casted = (__typeof(casted))object;
72 | return (self.modifiers == casted.modifiers || [self.modifiers isEqualToArray:casted.modifiers]) &&
73 | (self.name == casted.name || [self.name isEqualToString:casted.name]) &&
74 | self.isUnion == casted.isUnion &&
75 | (self.fields == casted.fields || [self.fields isEqualToArray:casted.fields]);
76 | }
77 | return NO;
78 | }
79 |
80 | - (NSString *)debugDescription {
81 | return [NSString stringWithFormat:@"<%@: %p> {modifiers: '%@', name: '%@', isUnion: %@, fields: %@}",
82 | [self class], self, [self modifiersString], self.name, self.isUnion ? @"YES" : @"NO", self.fields.debugDescription];
83 | }
84 |
85 | @end
86 |
--------------------------------------------------------------------------------
/ClassDump/Models/Reflections/CDClassModel.h:
--------------------------------------------------------------------------------
1 | //
2 | // CDClassModel.h
3 | // ClassDump
4 | //
5 | // Created by Leptos on 4/7/19.
6 | // Copyright © 2019 Leptos. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | #import
12 | #import
13 | #import
14 | #import
15 | #import
16 |
17 | @interface CDClassModel : NSObject
18 | // the Class property must be unsafe_unretained because not all
19 | // classes can be stored with either a strong or weak reference
20 | /// The Obj-C runtime @c Class
21 | @property (unsafe_unretained, nonatomic, readonly) Class backing;
22 | /// The name of the class, e.g. @c NSObject
23 | @property (strong, nonatomic, readonly) NSString *name;
24 | /// The protocols the class conforms to
25 | @property (strong, nonatomic, readonly) NSArray *protocols;
26 |
27 | @property (strong, nonatomic, readonly) NSArray *classProperties;
28 | @property (strong, nonatomic, readonly) NSArray *instanceProperties;
29 |
30 | @property (strong, nonatomic, readonly) NSArray *classMethods;
31 | @property (strong, nonatomic, readonly) NSArray *instanceMethods;
32 | /// Instance variables, including values synthesized from properties
33 | @property (strong, nonatomic, readonly) NSArray *ivars;
34 |
35 | - (instancetype)initWithClass:(Class)cls;
36 | + (instancetype)modelWithClass:(Class)cls;
37 |
38 | /// Generate an @c interface for the class
39 | /// @param comments Generate comments with information such as the
40 | /// image or category the declaration was found in
41 | /// @param synthesizeStrip Remove methods and ivars synthesized from properties
42 | - (NSString *)linesWithComments:(BOOL)comments synthesizeStrip:(BOOL)synthesizeStrip;
43 | /// Generate an @c interface for the class
44 | /// @param comments Generate comments with information such as the
45 | /// image or category the declaration was found in
46 | /// @param synthesizeStrip Remove methods and ivars synthesized from properties
47 | - (CDSemanticString *)semanticLinesWithComments:(BOOL)comments synthesizeStrip:(BOOL)synthesizeStrip;
48 |
49 | /// Generate an @c interface for the class
50 | - (CDSemanticString *)semanticLinesWithOptions:(CDGenerationOptions *)options;
51 |
52 | /// Classes the class references in the declaration
53 | ///
54 | /// In other words, all the classes that the compiler would need to see
55 | /// for the header to pass the type checking stage of compilation.
56 | - (NSSet *)classReferences;
57 | /// Protocols the class references in the declaration
58 | ///
59 | /// In other words, all the protocols that the compiler would need to see
60 | /// for the header to pass the type checking stage of compilation.
61 | - (NSSet *)protocolReferences;
62 |
63 | @end
64 |
--------------------------------------------------------------------------------
/ClassDump/Models/Reflections/CDClassModel.m:
--------------------------------------------------------------------------------
1 | //
2 | // CDClassModel.m
3 | // ClassDump
4 | //
5 | // Created by Leptos on 4/7/19.
6 | // Copyright © 2019 Leptos. All rights reserved.
7 | //
8 |
9 | #import "CDClassModel.h"
10 | #import "CDProtocolModel+Conformance.h"
11 | #import "../../Services/CDTypeParser.h"
12 | #import "../NSArray+CDFiltering.h"
13 |
14 | #import
15 |
16 | @implementation CDClassModel {
17 | NSArray *_classPropertySynthesizedMethods;
18 | NSArray *_instancePropertySynthesizedMethods;
19 | NSArray *_instancePropertySynthesizedVars;
20 | }
21 |
22 | + (instancetype)modelWithClass:(Class)cls {
23 | return [[self alloc] initWithClass:cls];
24 | }
25 |
26 | - (instancetype)initWithClass:(Class)cls {
27 | if (self = [self init]) {
28 | _backing = cls;
29 | _name = NSStringFromClass(cls);
30 |
31 | Class const metaClass = object_getClass(cls);
32 |
33 | unsigned int count, index;
34 |
35 | Protocol *__unsafe_unretained *protocolList = class_copyProtocolList(cls, &count);
36 | if (protocolList) {
37 | NSMutableArray *protocols = [NSMutableArray arrayWithCapacity:count];
38 | for (index = 0; index < count; index++) {
39 | Protocol *objc_protocol = protocolList[index];
40 | [protocols addObject:[CDProtocolModel modelWithProtocol:objc_protocol]];
41 | }
42 | free(protocolList);
43 | _protocols = [protocols copy];
44 | }
45 |
46 | objc_property_t *classPropertyList = class_copyPropertyList(metaClass, &count);
47 | if (classPropertyList) {
48 | NSMutableArray *synthMeths = [NSMutableArray array];
49 | NSMutableArray *properties = [NSMutableArray arrayWithCapacity:count];
50 | for (index = 0; index < count; index++) {
51 | objc_property_t objc_property = classPropertyList[index];
52 | CDPropertyModel *propertyRep = [CDPropertyModel modelWithProperty:objc_property isClass:YES];
53 | [properties addObject:propertyRep];
54 | NSString *synthMethodName;
55 | if ((synthMethodName = propertyRep.getter)) {
56 | [synthMeths addObject:synthMethodName];
57 | }
58 | if ((synthMethodName = propertyRep.setter)) {
59 | [synthMeths addObject:synthMethodName];
60 | }
61 | }
62 | free(classPropertyList);
63 | _classPropertySynthesizedMethods = [synthMeths copy];
64 | _classProperties = [properties copy];
65 | }
66 |
67 | objc_property_t *propertyList = class_copyPropertyList(cls, &count);
68 | if (propertyList) {
69 | NSMutableArray *synthMeths = [NSMutableArray array];
70 | NSMutableArray *syntVars = [NSMutableArray array];
71 | NSMutableArray *properties = [NSMutableArray arrayWithCapacity:count];
72 | for (index = 0; index < count; index++) {
73 | objc_property_t objc_property = propertyList[index];
74 | CDPropertyModel *propertyRep = [CDPropertyModel modelWithProperty:objc_property isClass:NO];
75 | [properties addObject:propertyRep];
76 | NSString *synthCompName;
77 | if ((synthCompName = propertyRep.getter)) {
78 | [synthMeths addObject:synthCompName];
79 | }
80 | if ((synthCompName = propertyRep.setter)) {
81 | [synthMeths addObject:synthCompName];
82 | }
83 | if ((synthCompName = propertyRep.iVar)) {
84 | [syntVars addObject:synthCompName];
85 | }
86 | }
87 | free(propertyList);
88 | _instancePropertySynthesizedMethods = [synthMeths copy];
89 | _instancePropertySynthesizedVars = [syntVars copy];
90 | _instanceProperties = [properties copy];
91 | }
92 |
93 | Ivar *ivarList = class_copyIvarList(cls, &count);
94 | if (ivarList) {
95 | NSMutableArray *ivars = [NSMutableArray arrayWithCapacity:count];
96 |
97 | NSMutableArray *eligibleProperties = [NSMutableArray arrayWithArray:self.instanceProperties];
98 | NSUInteger eligiblePropertiesCount = eligibleProperties.count;
99 |
100 | for (index = 0; index < count; index++) {
101 | CDIvarModel *model = [CDIvarModel modelWithIvar:ivarList[index]];
102 |
103 | for (NSUInteger eligibleIndex = 0; eligibleIndex < eligiblePropertiesCount; eligibleIndex++) {
104 | CDPropertyModel *propModel = eligibleProperties[eligibleIndex];
105 | if ([propModel.iVar isEqualToString:model.name]) {
106 | [propModel overrideType:model.type];
107 |
108 | eligiblePropertiesCount--;
109 | // constant time operation
110 | // since we decremented eligiblePropertiesCount, this object is now unreachable
111 | [eligibleProperties exchangeObjectAtIndex:eligibleIndex withObjectAtIndex:eligiblePropertiesCount];
112 |
113 | break;
114 | }
115 | }
116 | [ivars addObject:model];
117 | }
118 | free(ivarList);
119 | _ivars = [ivars copy];
120 | }
121 |
122 | Method *classMethodList = class_copyMethodList(metaClass, &count);
123 | if (classMethodList) {
124 | NSMutableArray *methods = [NSMutableArray arrayWithCapacity:count];
125 | for (index = 0; index < count; index++) {
126 | [methods addObject:[CDMethodModel modelWithMethod:*method_getDescription(classMethodList[index]) isClass:YES]];
127 | }
128 | _classMethods = [methods copy];
129 | free(classMethodList);
130 | }
131 |
132 | Method *methodList = class_copyMethodList(cls, &count);
133 | if (methodList) {
134 | NSMutableArray *methods = [NSMutableArray arrayWithCapacity:count];
135 | for (index = 0; index < count; index++) {
136 | [methods addObject:[CDMethodModel modelWithMethod:*method_getDescription(methodList[index]) isClass:NO]];
137 | }
138 | _instanceMethods = [methods copy];
139 | free(methodList);
140 | }
141 |
142 | }
143 | return self;
144 | }
145 |
146 | - (NSString *)linesWithComments:(BOOL)comments synthesizeStrip:(BOOL)synthesizeStrip {
147 | return [[self semanticLinesWithComments:comments synthesizeStrip:synthesizeStrip] string];
148 | }
149 |
150 | - (CDSemanticString *)semanticLinesWithComments:(BOOL)comments synthesizeStrip:(BOOL)synthesizeStrip {
151 | CDGenerationOptions *options = [CDGenerationOptions new];
152 | options.addSymbolImageComments = comments;
153 | options.stripSynthesized = synthesizeStrip;
154 | return [self semanticLinesWithOptions:options];
155 | }
156 |
157 | - (CDSemanticString *)semanticLinesWithOptions:(CDGenerationOptions *)options {
158 | Dl_info info;
159 |
160 | CDSemanticString *build = [CDSemanticString new];
161 |
162 | NSSet *forwardClasses = [self _forwardDeclarableClassReferences];
163 | NSUInteger const forwardClassCount = forwardClasses.count;
164 | if (forwardClassCount > 0) {
165 | [build appendString:@"@class" semanticType:CDSemanticTypeKeyword];
166 | [build appendString:@" " semanticType:CDSemanticTypeStandard];
167 |
168 | NSUInteger classNamesRemaining = forwardClassCount;
169 | for (NSString *className in forwardClasses) {
170 | [build appendString:className semanticType:CDSemanticTypeClass];
171 | classNamesRemaining--;
172 | if (classNamesRemaining > 0) {
173 | [build appendString:@", " semanticType:CDSemanticTypeStandard];
174 | }
175 | }
176 | [build appendString:@";\n" semanticType:CDSemanticTypeStandard];
177 | }
178 |
179 | NSSet *forwardProtocols = [self _forwardDeclarableProtocolReferences];
180 | NSUInteger const forwardProtocolCount = forwardProtocols.count;
181 | if (forwardProtocolCount > 0) {
182 | [build appendString:@"@protocol" semanticType:CDSemanticTypeKeyword];
183 | [build appendString:@" " semanticType:CDSemanticTypeStandard];
184 |
185 | NSUInteger protocolNamesRemaining = forwardProtocolCount;
186 | for (NSString *protocolNames in forwardProtocols) {
187 | [build appendString:protocolNames semanticType:CDSemanticTypeProtocol];
188 | protocolNamesRemaining--;
189 | if (protocolNamesRemaining > 0) {
190 | [build appendString:@", " semanticType:CDSemanticTypeStandard];
191 | }
192 | }
193 | [build appendString:@";\n" semanticType:CDSemanticTypeStandard];
194 | }
195 |
196 | if (forwardClassCount > 0 || forwardProtocolCount > 0) {
197 | [build appendString:@"\n" semanticType:CDSemanticTypeStandard];
198 | }
199 |
200 | if (options.addSymbolImageComments) {
201 | NSString *comment = nil;
202 | if (dladdr((__bridge const void *)self.backing, &info)) {
203 | comment = [NSString stringWithFormat:@"/* %s in %s */", info.dli_sname ?: "(anonymous)", info.dli_fname];
204 | } else {
205 | comment = @"/* no symbol found */";
206 | }
207 | [build appendString:comment semanticType:CDSemanticTypeComment];
208 | [build appendString:@"\n" semanticType:CDSemanticTypeStandard];
209 | }
210 | [build appendString:@"@interface" semanticType:CDSemanticTypeKeyword];
211 | [build appendString:@" " semanticType:CDSemanticTypeStandard];
212 | [build appendString:self.name semanticType:CDSemanticTypeClass];
213 |
214 | Class superclass = class_getSuperclass(self.backing);
215 | if (superclass) {
216 | [build appendString:@" : " semanticType:CDSemanticTypeStandard];
217 | [build appendString:NSStringFromClass(superclass) semanticType:CDSemanticTypeClass];
218 | }
219 |
220 | NSArray *protocols = self.protocols;
221 | NSUInteger const protocolCount = protocols.count;
222 | if (protocolCount > 0) {
223 | [build appendString:@" " semanticType:CDSemanticTypeStandard];
224 | [build appendString:@"<" semanticType:CDSemanticTypeStandard];
225 | [protocols enumerateObjectsUsingBlock:^(CDProtocolModel *protocol, NSUInteger idx, BOOL *stop) {
226 | [build appendString:protocol.name semanticType:CDSemanticTypeProtocol];
227 | if ((idx + 1) < protocolCount) {
228 | [build appendString:@", " semanticType:CDSemanticTypeStandard];
229 | }
230 | }];
231 | [build appendString:@">" semanticType:CDSemanticTypeStandard];
232 | }
233 |
234 | NSArray *synthedClassMethds = nil, *synthedInstcMethds = nil, *synthedVars = nil;
235 | if (options.stripSynthesized) {
236 | synthedClassMethds = _classPropertySynthesizedMethods;
237 | synthedInstcMethds = _instancePropertySynthesizedMethods;
238 | synthedVars = _instancePropertySynthesizedVars;
239 | }
240 |
241 | if (self.ivars.count - synthedVars.count) {
242 | [build appendString:@" {\n" semanticType:CDSemanticTypeStandard];
243 | for (CDIvarModel *ivar in self.ivars) {
244 | if ([synthedVars containsObject:ivar.name]) {
245 | continue;
246 | }
247 | if (options.addSymbolImageComments) {
248 | NSString *comment = nil;
249 | if (dladdr(ivar.backing, &info)) {
250 | comment = [NSString stringWithFormat:@"/* in %s */", info.dli_fname];
251 | } else {
252 | comment = @"/* no symbol found */";
253 | }
254 | [build appendString:@"\n " semanticType:CDSemanticTypeStandard];
255 | [build appendString:comment semanticType:CDSemanticTypeComment];
256 | [build appendString:@"\n" semanticType:CDSemanticTypeStandard];
257 | }
258 | [build appendString:@" " semanticType:CDSemanticTypeStandard];
259 | [build appendSemanticString:[ivar semanticString]];
260 | [build appendString:@";\n" semanticType:CDSemanticTypeStandard];
261 | }
262 | [build appendString:@"}" semanticType:CDSemanticTypeStandard];
263 | }
264 |
265 | [build appendString:@"\n" semanticType:CDSemanticTypeStandard];
266 |
267 | NSMutableSet *classPropertyIgnoreSet = [NSMutableSet set];
268 | NSMutableSet *instancePropertyIgnoreSet = [NSMutableSet set];
269 |
270 | NSMutableSet *classMethodIgnoreSet = [NSMutableSet set];
271 | NSMutableSet *instanceMethodIgnoreSet = [NSMutableSet set];
272 |
273 | if (options.stripOverrides) {
274 | NSMutableSet *classPropertyIgnoreNames = [NSMutableSet set];
275 | NSMutableSet *instancePropertyIgnoreNames = [NSMutableSet set];
276 |
277 | NSMutableSet *classMethodIgnoreNames = [NSMutableSet set];
278 | NSMutableSet *instanceMethodIgnoreNames = [NSMutableSet set];
279 |
280 | Class checkClass = class_getSuperclass(self.backing);
281 | while (checkClass != NULL) {
282 | CDClassModel *superclassModel = [CDClassModel modelWithClass:checkClass];
283 |
284 | for (CDPropertyModel *property in superclassModel.classProperties) {
285 | if ([classPropertyIgnoreNames containsObject:property.name]) {
286 | continue;
287 | }
288 | [classPropertyIgnoreNames addObject:property.name];
289 | [classPropertyIgnoreSet addObject:property];
290 | }
291 |
292 | for (CDPropertyModel *property in superclassModel.instanceProperties) {
293 | if ([instancePropertyIgnoreNames containsObject:property.name]) {
294 | continue;
295 | }
296 | [instancePropertyIgnoreNames addObject:property.name];
297 | [instancePropertyIgnoreSet addObject:property];
298 | }
299 |
300 | for (CDMethodModel *method in superclassModel.classMethods) {
301 | if ([classMethodIgnoreNames containsObject:method.name]) {
302 | continue;
303 | }
304 | [classMethodIgnoreNames addObject:method.name];
305 | [classMethodIgnoreSet addObject:method];
306 | }
307 |
308 | for (CDMethodModel *method in superclassModel.instanceMethods) {
309 | if ([instanceMethodIgnoreNames containsObject:method.name]) {
310 | continue;
311 | }
312 | [instanceMethodIgnoreNames addObject:method.name];
313 | [instanceMethodIgnoreSet addObject:method];
314 | }
315 |
316 | checkClass = class_getSuperclass(checkClass);
317 | }
318 | }
319 |
320 | if (options.stripProtocolConformance) {
321 | [classPropertyIgnoreSet addObjectsFromArray:[CDProtocolModel requiredClassPropertiesToConform:self.protocols]];
322 | [instancePropertyIgnoreSet addObjectsFromArray:[CDProtocolModel requiredInstancePropertiesToConform:self.protocols]];
323 | [classMethodIgnoreSet addObjectsFromArray:[CDProtocolModel requiredClassMethodsToConform:self.protocols]];
324 | [instanceMethodIgnoreSet addObjectsFromArray:[CDProtocolModel requiredInstanceMethodsToConform:self.protocols]];
325 | }
326 |
327 | NSArray *classProperties = self.classProperties;
328 | NSArray *instanceProperties = self.instanceProperties;
329 |
330 | NSArray *classMethods = self.classMethods;
331 | NSArray *instanceMethods = self.instanceMethods;
332 |
333 | if (options.stripDuplicates) {
334 | classProperties = [classProperties cd_uniqueObjects];
335 | instanceProperties = [instanceProperties cd_uniqueObjects];
336 |
337 | classMethods = [classMethods cd_uniqueObjects];
338 | instanceMethods = [instanceMethods cd_uniqueObjects];
339 | }
340 |
341 | classProperties = [classProperties cd_filterObjectsIgnoring:classPropertyIgnoreSet];
342 | instanceProperties = [instanceProperties cd_filterObjectsIgnoring:instancePropertyIgnoreSet];
343 |
344 | classMethods = [classMethods cd_filterObjectsIgnoring:classMethodIgnoreSet];
345 | instanceMethods = [instanceMethods cd_filterObjectsIgnoring:instanceMethodIgnoreSet];
346 |
347 | [self _appendLines:build properties:classProperties comments:options.addSymbolImageComments];
348 | [self _appendLines:build properties:instanceProperties comments:options.addSymbolImageComments];
349 |
350 | [self _appendLines:build methods:classMethods synthesized:synthedClassMethds comments:options.addSymbolImageComments stripCtor:NO stripDtor:NO];
351 | [self _appendLines:build methods:instanceMethods synthesized:synthedInstcMethds comments:options.addSymbolImageComments stripCtor:options.stripCtorMethod stripDtor:options.stripDtorMethod];
352 |
353 | [build appendString:@"\n" semanticType:CDSemanticTypeStandard];
354 | [build appendString:@"@end" semanticType:CDSemanticTypeKeyword];
355 | [build appendString:@"\n" semanticType:CDSemanticTypeStandard];
356 |
357 | return build;
358 | }
359 |
360 | - (void)_appendLines:(CDSemanticString *)build properties:(NSArray *)properties comments:(BOOL)comments {
361 | if (properties.count) {
362 | [build appendString:@"\n" semanticType:CDSemanticTypeStandard];
363 |
364 | Dl_info info;
365 | for (CDPropertyModel *prop in properties) {
366 | if (comments) {
367 | NSString *comment = nil;
368 | if (dladdr(prop.backing, &info)) {
369 | comment = [NSString stringWithFormat:@"/* in %s */", info.dli_fname];
370 | } else {
371 | comment = @"/* no symbol found */";
372 | }
373 | [build appendString:@"\n" semanticType:CDSemanticTypeStandard];
374 | [build appendString:comment semanticType:CDSemanticTypeComment];
375 | [build appendString:@"\n" semanticType:CDSemanticTypeStandard];
376 | }
377 | [build appendSemanticString:[prop semanticString]];
378 | [build appendString:@";\n" semanticType:CDSemanticTypeStandard];
379 | }
380 | }
381 | }
382 |
383 | - (void)_appendLines:(CDSemanticString *)build methods:(NSArray *)methods synthesized:(NSArray *)synthesized comments:(BOOL)comments stripCtor:(BOOL)stripCtor stripDtor:(BOOL)stripDtor {
384 | if (methods.count - synthesized.count) {
385 | [build appendString:@"\n" semanticType:CDSemanticTypeStandard];
386 |
387 | Dl_info info;
388 | NSMutableArray *synthed = [NSMutableArray arrayWithArray:synthesized];
389 | if (stripCtor) {
390 | [synthed addObject:@".cxx_construct"];
391 | }
392 | if (stripDtor) {
393 | [synthed addObject:@".cxx_destruct"];
394 | }
395 | NSUInteger synthedCount = synthed.count;
396 | for (CDMethodModel *methd in methods) {
397 | // find and remove instead of just find so we don't have to search the entire
398 | // array everytime, when we know the objects that we've already filtered out won't come up again
399 | NSUInteger const searchResult = [synthed indexOfObject:methd.name inRange:NSMakeRange(0, synthedCount)];
400 | if (searchResult != NSNotFound) {
401 | synthedCount--;
402 | // optimized version of remove since the
403 | // order of synthed doesn't matter to us.
404 | // exchange is O(1) instead of remove is O(n)
405 | [synthed exchangeObjectAtIndex:searchResult withObjectAtIndex:synthedCount];
406 | continue;
407 | }
408 |
409 | if (comments) {
410 | Method objcMethod = NULL;
411 | if (methd.isClass) {
412 | objcMethod = class_getClassMethod(self.backing, methd.backing.name);
413 | } else {
414 | objcMethod = class_getInstanceMethod(self.backing, methd.backing.name);
415 | }
416 | IMP const methdImp = method_getImplementation(objcMethod);
417 |
418 | NSString *comment = nil;
419 | if (dladdr(methdImp, &info)) {
420 | comment = [NSString stringWithFormat:@"/* %s in %s */", info.dli_sname ?: "(anonymous)", info.dli_fname];
421 | } else {
422 | comment = @"/* no symbol found */";
423 | }
424 | [build appendString:@"\n" semanticType:CDSemanticTypeStandard];
425 | [build appendString:comment semanticType:CDSemanticTypeComment];
426 | [build appendString:@"\n" semanticType:CDSemanticTypeStandard];
427 | }
428 | [build appendSemanticString:[methd semanticString]];
429 | [build appendString:@";\n" semanticType:CDSemanticTypeStandard];
430 | }
431 | }
432 | }
433 |
434 | - (NSSet *)_forwardDeclarableClassReferences {
435 | NSMutableSet *build = [NSMutableSet set];
436 |
437 | [self _unionReferences:build sources:self.classProperties resolve:^NSSet *(CDPropertyModel *model) {
438 | return [model.type classReferences];
439 | }];
440 | [self _unionReferences:build sources:self.instanceProperties resolve:^NSSet *(CDPropertyModel *model) {
441 | return [model.type classReferences];
442 | }];
443 |
444 | [self _unionReferences:build sources:self.classMethods resolve:^NSSet *(CDMethodModel *model) {
445 | return [model classReferences];
446 | }];
447 | [self _unionReferences:build sources:self.instanceMethods resolve:^NSSet *(CDMethodModel *model) {
448 | return [model classReferences];
449 | }];
450 |
451 | [self _unionReferences:build sources:self.ivars resolve:^NSSet *(CDIvarModel *model) {
452 | return [model.type classReferences];
453 | }];
454 |
455 | [build removeObject:self.name];
456 | return build;
457 | }
458 |
459 | - (NSSet *)_forwardDeclarableProtocolReferences {
460 | NSMutableSet *build = [NSMutableSet set];
461 |
462 | [self _unionReferences:build sources:self.classProperties resolve:^NSSet *(CDPropertyModel *model) {
463 | return [model.type protocolReferences];
464 | }];
465 | [self _unionReferences:build sources:self.instanceProperties resolve:^NSSet *(CDPropertyModel *model) {
466 | return [model.type protocolReferences];
467 | }];
468 |
469 | [self _unionReferences:build sources:self.classMethods resolve:^NSSet *(CDMethodModel *model) {
470 | return [model protocolReferences];
471 | }];
472 | [self _unionReferences:build sources:self.instanceMethods resolve:^NSSet *(CDMethodModel *model) {
473 | return [model protocolReferences];
474 | }];
475 |
476 | [self _unionReferences:build sources:self.ivars resolve:^NSSet *(CDIvarModel *model) {
477 | return [model.type protocolReferences];
478 | }];
479 |
480 | return build;
481 | }
482 |
483 | - (NSSet *)classReferences {
484 | NSMutableSet *build = [NSMutableSet set];
485 |
486 | NSSet *forwardDeclarable = [self _forwardDeclarableClassReferences];
487 | if (forwardDeclarable != nil) {
488 | [build unionSet:forwardDeclarable];
489 | }
490 |
491 | Class const superclass = class_getSuperclass(self.backing);
492 | if (superclass != NULL) {
493 | [build addObject:NSStringFromClass(superclass)];
494 | }
495 |
496 | return build;
497 | }
498 |
499 | - (NSSet *)protocolReferences {
500 | NSMutableSet *build = [NSMutableSet set];
501 |
502 | NSSet *forwardDeclarable = [self _forwardDeclarableProtocolReferences];
503 | if (forwardDeclarable != nil) {
504 | [build unionSet:forwardDeclarable];
505 | }
506 |
507 | for (CDProtocolModel *protocol in self.protocols) {
508 | [build addObject:protocol.name];
509 | }
510 |
511 | return build;
512 | }
513 |
514 | - (void)_unionReferences:(NSMutableSet *)build sources:(NSArray *)sources resolve:(NSSet *(NS_NOESCAPE ^)(id))resolver {
515 | for (id source in sources) {
516 | NSSet *refs = resolver(source);
517 | if (refs != nil) {
518 | [build unionSet:refs];
519 | }
520 | }
521 | }
522 |
523 | - (NSString *)description {
524 | return self.name;
525 | }
526 |
527 | - (NSString *)debugDescription {
528 | return [NSString stringWithFormat:@"<%@: %p> {name: '%@', protocols: %@, "
529 | "classProperties: %@, instanceProperties: %@, classMethods: %@, instanceMethods: %@, ivars: %@}",
530 | [self class], self, self.name, self.protocols,
531 | self.classProperties, self.instanceProperties, self.classMethods, self.instanceMethods, self.ivars];
532 | }
533 |
534 | @end
535 |
--------------------------------------------------------------------------------
/ClassDump/Models/Reflections/CDIvarModel.h:
--------------------------------------------------------------------------------
1 | //
2 | // CDIvarModel.h
3 | // ClassDump
4 | //
5 | // Created by Leptos on 4/8/19.
6 | // Copyright © 2019 Leptos. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | #import
13 |
14 | @interface CDIvarModel : NSObject
15 | /// The Obj-C runtime @c Ivar
16 | @property (nonatomic, readonly) Ivar backing;
17 | /// The name of the ivar, e.g. @c _name
18 | @property (strong, nonatomic, readonly) NSString *name;
19 | /// The type of the ivar
20 | @property (strong, nonatomic, readonly) CDParseType *type;
21 |
22 | - (instancetype)initWithIvar:(Ivar)ivar;
23 | + (instancetype)modelWithIvar:(Ivar)ivar;
24 |
25 | - (CDSemanticString *)semanticString;
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/ClassDump/Models/Reflections/CDIvarModel.m:
--------------------------------------------------------------------------------
1 | //
2 | // CDIvarModel.m
3 | // ClassDump
4 | //
5 | // Created by Leptos on 4/8/19.
6 | // Copyright © 2019 Leptos. All rights reserved.
7 | //
8 |
9 | #import "CDIvarModel.h"
10 | #import "../../Services/CDTypeParser.h"
11 |
12 | @implementation CDIvarModel
13 |
14 | + (instancetype)modelWithIvar:(Ivar)ivar {
15 | return [[self alloc] initWithIvar:ivar];
16 | }
17 |
18 | - (instancetype)initWithIvar:(Ivar)ivar {
19 | if (self = [self init]) {
20 | _backing = ivar;
21 | _name = @(ivar_getName(ivar));
22 | _type = [CDTypeParser typeForEncoding:(ivar_getTypeEncoding(ivar) ?: "")];
23 | }
24 | return self;
25 | }
26 |
27 | - (CDSemanticString *)semanticString {
28 | return [self.type semanticStringForVariableName:self.name];
29 | }
30 |
31 | - (BOOL)isEqual:(id)object {
32 | if ([object isKindOfClass:[self class]]) {
33 | __typeof(self) casted = (__typeof(casted))object;
34 | Ivar const sVar = self.backing, cVar = casted.backing;
35 | return [self.name isEqual:casted.name] &&
36 | (strstr(ivar_getTypeEncoding(sVar), ivar_getTypeEncoding(cVar)) == 0);
37 | }
38 | return NO;
39 | }
40 |
41 | - (NSString *)description {
42 | return [self.type stringForVariableName:self.name];
43 | }
44 |
45 | @end
46 |
--------------------------------------------------------------------------------
/ClassDump/Models/Reflections/CDMethodModel.h:
--------------------------------------------------------------------------------
1 | //
2 | // CDMethodModel.h
3 | // ClassDump
4 | //
5 | // Created by Leptos on 4/7/19.
6 | // Copyright © 2019 Leptos. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | #import
13 |
14 | @interface CDMethodModel : NSObject
15 | /// The Obj-C runtime @c objc_method_description
16 | @property (nonatomic, readonly) struct objc_method_description backing;
17 | /// The signature of the method, e.g. @c initWithMethod:isClass:
18 | @property (strong, nonatomic, readonly) NSString *name;
19 | /// The types of the arguments to the method, e.g. @c [id, @c BOOL]
20 | @property (strong, nonatomic, readonly) NSArray *argumentTypes;
21 | /// The return type of the method
22 | @property (strong, nonatomic, readonly) CDParseType *returnType;
23 | /// If the method is a class method, otherwise an instance method
24 | @property (nonatomic, readonly) BOOL isClass;
25 |
26 | - (instancetype)initWithMethod:(struct objc_method_description)methd isClass:(BOOL)isClass;
27 | + (instancetype)modelWithMethod:(struct objc_method_description)methd isClass:(BOOL)isClass;
28 |
29 | - (CDSemanticString *)semanticString;
30 |
31 | /// Classes the method references in the declaration
32 | ///
33 | /// In other words, all the classes that the compiler would need to see
34 | /// for the header to pass the type checking stage of compilation.
35 | - (NSSet *)classReferences;
36 | /// Protocols the method references in the declaration
37 | ///
38 | /// In other words, all the protocols that the compiler would need to see
39 | /// for the header to pass the type checking stage of compilation.
40 | - (NSSet *)protocolReferences;
41 |
42 | @end
43 |
--------------------------------------------------------------------------------
/ClassDump/Models/Reflections/CDMethodModel.m:
--------------------------------------------------------------------------------
1 | //
2 | // CDMethodModel.m
3 | // ClassDump
4 | //
5 | // Created by Leptos on 4/7/19.
6 | // Copyright © 2019 Leptos. All rights reserved.
7 | //
8 |
9 | #import "CDMethodModel.h"
10 | #import "../../Services/CDTypeParser.h"
11 |
12 | /*
13 | * @APPLE_LICENSE_HEADER_START@
14 | *
15 | * This file contains Original Code and/or Modifications of Original Code
16 | * as defined in and that are subject to the Apple Public Source License
17 | * Version 2.0 (the 'License'). You may not use this file except in
18 | * compliance with the License. Please obtain a copy of the License at
19 | * http://www.opensource.apple.com/apsl/ and read it before using this
20 | * file.
21 | *
22 | * The Original Code and all software distributed under the License are
23 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
24 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
25 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
26 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
27 | * Please see the License for the specific language governing rights and
28 | * limitations under the License.
29 | *
30 | * @APPLE_LICENSE_HEADER_END@
31 | */
32 |
33 | /// Returns the number of times a character occurs in a null-terminated stringb
34 | static size_t characterCount(const char *str, const char c) {
35 | size_t ret = 0;
36 | while (*str) {
37 | if (*str++ == c) {
38 | ret++;
39 | }
40 | }
41 | return ret;
42 | }
43 |
44 | @implementation CDMethodModel
45 |
46 | + (instancetype)modelWithMethod:(struct objc_method_description)methd isClass:(BOOL)isClass {
47 | return [[self alloc] initWithMethod:methd isClass:isClass];
48 | }
49 |
50 | - (instancetype)initWithMethod:(struct objc_method_description)methd isClass:(BOOL)isClass {
51 | if (self = [self init]) {
52 | _backing = methd;
53 | _isClass = isClass;
54 | _name = NSStringFromSelector(methd.name);
55 |
56 | const char *typedesc = methd.types;
57 | // this code is heavily modified from, but based on encoding_getArgumentInfo
58 | // https://github.com/apple-oss-distributions/objc4/blob/689525d556/runtime/objc-typeencoding.mm#L168-L272
59 | const char *type = typedesc;
60 | typedesc = [CDTypeParser endOfTypeEncoding:type];
61 | _returnType = [CDTypeParser typeForEncodingStart:type end:typedesc error:NULL];
62 |
63 | NSUInteger const expectedArguments = characterCount(sel_getName(methd.name), ':');
64 | NSMutableArray *arguments = [NSMutableArray arrayWithCapacity:expectedArguments + 2];
65 |
66 | // skip stack size
67 | while (isnumber(*typedesc)) {
68 | typedesc++;
69 | }
70 |
71 | while (*typedesc) {
72 | type = typedesc;
73 | typedesc = [CDTypeParser endOfTypeEncoding:type];
74 | [arguments addObject:[CDTypeParser typeForEncodingStart:type end:typedesc error:NULL]];
75 |
76 | // Skip GNU runtime's register parameter hint
77 | if (*typedesc == '+') {
78 | typedesc++;
79 | }
80 | // Skip negative sign in offset
81 | if (*typedesc == '-') {
82 | typedesc++;
83 | }
84 | while (isnumber(*typedesc)) {
85 | typedesc++;
86 | }
87 | }
88 | // if there were less arguments than expected, fill in the rest with empty types
89 | for (NSUInteger argumentIndex = arguments.count; argumentIndex < expectedArguments; argumentIndex++) {
90 | [arguments addObject:[CDTypeParser typeForEncoding:""]]; // add an empty encoding
91 | }
92 | // if there were more arguments than expected, trim from the beginning.
93 | // usually `self` (type `id`) and `_cmd` (type `SEL`) are the first two parameters,
94 | // however they are not included in expectedArguments. `_cmd` may not be included
95 | // if the method is backed by a block instead of a selector.
96 | _argumentTypes = [arguments subarrayWithRange:NSMakeRange(arguments.count - expectedArguments, expectedArguments)];
97 | }
98 | return self;
99 | }
100 |
101 | - (CDSemanticString *)semanticString {
102 | CDSemanticString *build = [CDSemanticString new];
103 | [build appendString:(self.isClass ? @"+" : @"-") semanticType:CDSemanticTypeStandard];
104 | [build appendString:@" (" semanticType:CDSemanticTypeStandard];
105 | [build appendSemanticString:[self.returnType semanticStringForVariableName:nil]];
106 | [build appendString:@")" semanticType:CDSemanticTypeStandard];
107 |
108 | NSArray *argumentTypes = self.argumentTypes;
109 | NSUInteger const argumentTypeCount = argumentTypes.count;
110 | if (argumentTypeCount > 0) {
111 | NSArray *brokenupName = [self.name componentsSeparatedByString:@":"];
112 |
113 | [argumentTypes enumerateObjectsUsingBlock:^(CDParseType *argumentType, NSUInteger idx, BOOL *stop) {
114 | [build appendString:brokenupName[idx] semanticType:CDSemanticTypeStandard];
115 | [build appendString:@":" semanticType:CDSemanticTypeStandard];
116 | [build appendString:@"(" semanticType:CDSemanticTypeStandard];
117 | [build appendSemanticString:[argumentType semanticStringForVariableName:nil]];
118 | [build appendString:@")" semanticType:CDSemanticTypeStandard];
119 | [build appendString:[NSString stringWithFormat:@"a%lu", (unsigned long)idx] semanticType:CDSemanticTypeVariable];
120 | if ((idx + 1) < argumentTypeCount) { // if there are still arguments left, add a space to separate
121 | [build appendString:@" " semanticType:CDSemanticTypeStandard];
122 | }
123 | }];
124 | } else {
125 | [build appendString:self.name semanticType:CDSemanticTypeStandard];
126 | }
127 | return build;
128 | }
129 |
130 | - (NSSet *)classReferences {
131 | NSMutableSet *build = [NSMutableSet set];
132 | NSSet *returnReferences = [self.returnType classReferences];
133 | if (returnReferences != nil) {
134 | [build unionSet:returnReferences];
135 | }
136 | for (CDParseType *paramType in self.argumentTypes) {
137 | NSSet *paramReferences = [paramType classReferences];
138 | if (paramReferences != nil) {
139 | [build unionSet:paramReferences];
140 | }
141 | }
142 | return build;
143 | }
144 |
145 | - (NSSet *)protocolReferences {
146 | NSMutableSet *build = [NSMutableSet set];
147 | NSSet *returnReferences = [self.returnType protocolReferences];
148 | if (returnReferences != nil) {
149 | [build unionSet:returnReferences];
150 | }
151 | for (CDParseType *paramType in self.argumentTypes) {
152 | NSSet *paramReferences = [paramType protocolReferences];
153 | if (paramReferences != nil) {
154 | [build unionSet:paramReferences];
155 | }
156 | }
157 | return build;
158 | }
159 |
160 | - (BOOL)isEqual:(id)object {
161 | if ([object isKindOfClass:[self class]]) {
162 | __typeof(self) casted = (__typeof(casted))object;
163 | return [self.name isEqual:casted.name] &&
164 | [self.argumentTypes isEqual:casted.argumentTypes] &&
165 | [self.returnType isEqual:casted.returnType];
166 | }
167 | return NO;
168 | }
169 |
170 | - (NSUInteger)hash {
171 | return self.name.hash;
172 | }
173 |
174 | - (NSString *)description {
175 | return [[self semanticString] string];
176 | }
177 |
178 | - (NSString *)debugDescription {
179 | return [NSString stringWithFormat:@"<%@: %p> {signature: '%@', argumentTypes: %@, "
180 | "returnType: '%@', isClass: %@}",
181 | [self class], self, self.name, self.argumentTypes,
182 | self.returnType, self.isClass ? @"YES" : @"NO"];
183 | }
184 |
185 | @end
186 |
--------------------------------------------------------------------------------
/ClassDump/Models/Reflections/CDPropertyAttribute.h:
--------------------------------------------------------------------------------
1 | //
2 | // CDPropertyAttribute.h
3 | // ClassDump
4 | //
5 | // Created by Leptos on 1/6/23.
6 | // Copyright © 2023 Leptos. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface CDPropertyAttribute : NSObject
12 | /// The name of a property attribute, e.g. @c strong, @c nonatomic, @c getter
13 | @property (strong, nonatomic, readonly) NSString *name;
14 | /// The value of a property attribute, e.g. the method name for @c getter= or @c setter=
15 | @property (strong, nonatomic, readonly) NSString *value;
16 |
17 | - (instancetype)initWithName:(NSString *)name value:(NSString *)value;
18 | + (instancetype)attributeWithName:(NSString *)name value:(NSString *)value;
19 |
20 | @end
21 |
--------------------------------------------------------------------------------
/ClassDump/Models/Reflections/CDPropertyAttribute.m:
--------------------------------------------------------------------------------
1 | //
2 | // CDPropertyAttribute.m
3 | // ClassDump
4 | //
5 | // Created by Leptos on 1/6/23.
6 | // Copyright © 2023 Leptos. All rights reserved.
7 | //
8 |
9 | #import "CDPropertyAttribute.h"
10 |
11 | @implementation CDPropertyAttribute
12 |
13 | + (instancetype)attributeWithName:(NSString *)name value:(NSString *)value {
14 | return [[self alloc] initWithName:name value:value];
15 | }
16 |
17 | - (instancetype)initWithName:(NSString *)name value:(NSString *)value {
18 | if (self = [self init]) {
19 | _name = name;
20 | _value = value;
21 | }
22 | return self;
23 | }
24 |
25 | - (BOOL)isEqual:(id)object {
26 | if ([object isKindOfClass:[self class]]) {
27 | __typeof(self) casted = (__typeof(casted))object;
28 | return (self.name == casted.name || [self.name isEqual:casted.name]) &&
29 | (self.value == casted.value || [self.value isEqual:casted.value]);
30 | }
31 | return NO;
32 | }
33 |
34 | - (NSString *)description {
35 | NSString *name = self.name;
36 | NSString *value = self.value;
37 | if (value != nil) {
38 | return [[name stringByAppendingString:@"="] stringByAppendingString:value];
39 | }
40 | return name;
41 | }
42 |
43 | - (NSString *)debugDescription {
44 | return [NSString stringWithFormat:@"<%@: %p> {name: '%@', value: '%@'}",
45 | [self class], self, self.name, self.value];
46 | }
47 |
48 | @end
49 |
--------------------------------------------------------------------------------
/ClassDump/Models/Reflections/CDPropertyModel.h:
--------------------------------------------------------------------------------
1 | //
2 | // CDPropertyModel.h
3 | // ClassDump
4 | //
5 | // Created by Leptos on 3/24/19.
6 | // Copyright © 2019 Leptos. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | #import
13 | #import
14 |
15 | @interface CDPropertyModel : NSObject
16 | /// The Obj-C runtime @c objc_property_t
17 | @property (nonatomic, readonly) objc_property_t backing;
18 | /// The name of the property, e.g. @c name
19 | @property (strong, nonatomic, readonly) NSString *name;
20 | /// The type of the property
21 | @property (strong, nonatomic, readonly) CDParseType *type;
22 | /// The attributes of the property
23 | @property (strong, nonatomic, readonly) NSArray *attributes;
24 | /// The name of the backing instance variable
25 | @property (strong, nonatomic, readonly) NSString *iVar;
26 | /// The signature of the getter method, e.g. @c count
27 | @property (strong, nonatomic, readonly) NSString *getter;
28 | /// The signature of the setter method, e.g. @c setName:
29 | @property (strong, nonatomic, readonly) NSString *setter;
30 |
31 | - (instancetype)initWithProperty:(objc_property_t)property isClass:(BOOL)isClass;
32 | + (instancetype)modelWithProperty:(objc_property_t)property isClass:(BOOL)isClass;
33 |
34 | /// Override the @c type of the property.
35 | /// Used when the corresponding ivar is found with more type information;
36 | /// e.g. An ivar may know the type is @c NSString @c *
37 | /// however the property only has @c id as the type
38 | - (void)overrideType:(CDParseType *)type;
39 |
40 | - (CDSemanticString *)semanticString;
41 |
42 | @end
43 |
--------------------------------------------------------------------------------
/ClassDump/Models/Reflections/CDPropertyModel.m:
--------------------------------------------------------------------------------
1 | //
2 | // CDPropertyModel.m
3 | // ClassDump
4 | //
5 | // Created by Leptos on 3/24/19.
6 | // Copyright © 2019 Leptos. All rights reserved.
7 | //
8 |
9 | #import "CDPropertyModel.h"
10 | #import "../../Services/CDTypeParser.h"
11 |
12 | @implementation CDPropertyModel
13 |
14 | + (instancetype)modelWithProperty:(objc_property_t)property isClass:(BOOL)isClass {
15 | return [[self alloc] initWithProperty:property isClass:isClass];
16 | }
17 |
18 | - (instancetype)initWithProperty:(objc_property_t)property isClass:(BOOL)isClass {
19 | if (self = [self init]) {
20 | _backing = property;
21 | _name = @(property_getName(property));
22 |
23 | BOOL isReadOnly = NO, isDynamic = NO;
24 |
25 | const char *const propAttribs = property_getAttributes(property);
26 | NSMutableArray *attributes = [NSMutableArray array];
27 | if (isClass) {
28 | [attributes addObject:[CDPropertyAttribute attributeWithName:@"class" value:nil]];
29 | }
30 |
31 | for (const char *propSeek = propAttribs; propSeek < (propAttribs + strlen(propAttribs)); propSeek++) {
32 | const char switchOnMe = *propSeek++;
33 |
34 | NSString *attributeName = nil;
35 | NSString *attributeValue = nil;
36 |
37 | const char *const attribHead = propSeek;
38 | while (*propSeek && *propSeek != ',') {
39 | switch (*propSeek) {
40 | case '"': {
41 | propSeek = strchr(++propSeek, '"');
42 | } break;
43 | case '{': {
44 | unsigned openTokens = 1;
45 | while (openTokens) {
46 | switch (*++propSeek) {
47 | case '{':
48 | openTokens++;
49 | break;
50 | case '}':
51 | openTokens--;
52 | break;
53 | }
54 | }
55 | } break;
56 | case '(': {
57 | unsigned openTokens = 1;
58 | while (openTokens) {
59 | switch (*++propSeek) {
60 | case '(':
61 | openTokens++;
62 | break;
63 | case ')':
64 | openTokens--;
65 | break;
66 | }
67 | }
68 | } break;
69 | }
70 | propSeek++;
71 | }
72 |
73 | NSUInteger const valueLen = propSeek - attribHead;
74 | if (valueLen > 0) {
75 | attributeValue = [[NSString alloc] initWithBytes:attribHead length:valueLen encoding:NSUTF8StringEncoding];
76 | }
77 |
78 | /* per https://github.com/llvm/llvm-project/blob/b7f97d3661/clang/lib/AST/ASTContext.cpp#L7878-L7973
79 | *
80 | * enum PropertyAttributes {
81 | * kPropertyReadOnly = 'R', // property is read-only.
82 | * kPropertyBycopy = 'C', // property is a copy of the value last assigned
83 | * kPropertyByref = '&', // property is a reference to the value last assigned
84 | * kPropertyDynamic = 'D', // property is dynamic
85 | * kPropertyGetter = 'G', // followed by getter selector name
86 | * kPropertySetter = 'S', // followed by setter selector name
87 | * kPropertyInstanceVariable = 'V', // followed by instance variable name
88 | * kPropertyType = 'T', // followed by old-style type encoding.
89 | * kPropertyWeak = 'W', // 'weak' property
90 | * kPropertyStrong = 'P', // property GC'able
91 | * kPropertyNonAtomic = 'N', // property non-atomic
92 | * kPropertyOptional = '?', // property optional
93 | * };
94 | */
95 | switch (switchOnMe) {
96 | case 'R':
97 | attributeName = @"readonly";
98 | isReadOnly = YES;
99 | break;
100 | case 'C':
101 | attributeName = @"copy";
102 | break;
103 | case '&':
104 | attributeName = @"retain";
105 | break;
106 | case 'D':
107 | isDynamic = YES;
108 | break;
109 | case 'G':
110 | attributeName = @"getter";
111 | _getter = attributeValue;
112 | break;
113 | case 'S':
114 | attributeName = @"setter";
115 | _setter = attributeValue;
116 | break;
117 | case 'V':
118 | _iVar = attributeValue;
119 | break;
120 | case 'T':
121 | _type = [CDTypeParser typeForEncodingStart:attribHead end:propSeek error:NULL];
122 | break;
123 | case 'W':
124 | attributeName = @"weak";
125 | break;
126 | case 'P':
127 | // eligible for garbage collection, no notation
128 | break;
129 | case 'N':
130 | attributeName = @"nonatomic";
131 | break;
132 | case '?':
133 | // @optional in a protocol
134 | break;
135 | default:
136 | NSAssert(NO, @"Unknown attribute code: %c", switchOnMe);
137 | break;
138 | }
139 |
140 | if (attributeName) {
141 | [attributes addObject:[CDPropertyAttribute attributeWithName:attributeName value:attributeValue]];
142 | }
143 | }
144 |
145 | _attributes = [attributes copy];
146 | if (!isDynamic) {
147 | if (!self.getter) {
148 | _getter = [self.name copy];
149 | }
150 | if (!self.setter && !isReadOnly) {
151 | // per https://github.com/llvm/llvm-project/blob/86616443bf/clang/lib/Basic/IdentifierTable.cpp#L756-L762
152 | unichar const realFirstChar = [self.name characterAtIndex:0];
153 | NSString *firstChar = [NSString stringWithCharacters:&realFirstChar length:1];
154 | _setter = [NSString stringWithFormat:@"set%@%@:", firstChar.uppercaseString, [self.name substringFromIndex:1]];
155 | }
156 | }
157 | }
158 | return self;
159 | }
160 |
161 | - (void)overrideType:(CDParseType *)type {
162 | _type = type;
163 | }
164 |
165 | - (CDSemanticString *)semanticString {
166 | CDSemanticString *build = [CDSemanticString new];
167 | [build appendString:@"@property" semanticType:CDSemanticTypeKeyword];
168 | [build appendString:@" " semanticType:CDSemanticTypeStandard];
169 |
170 | NSArray *attributes = self.attributes;
171 | NSUInteger const attributeCount = attributes.count;
172 | if (attributeCount > 0) {
173 | [build appendString:@"(" semanticType:CDSemanticTypeStandard];
174 | [attributes enumerateObjectsUsingBlock:^(CDPropertyAttribute *attribute, NSUInteger idx, BOOL *stop) {
175 | [build appendString:attribute.name semanticType:CDSemanticTypeKeyword];
176 |
177 | NSString *attributeValue = attribute.value;
178 | if (attributeValue != nil) {
179 | [build appendString:@"=" semanticType:CDSemanticTypeStandard];
180 | [build appendString:attributeValue semanticType:CDSemanticTypeVariable];
181 | }
182 | if ((idx + 1) < attributeCount) {
183 | [build appendString:@", " semanticType:CDSemanticTypeStandard];
184 | }
185 | }];
186 | [build appendString:@") " semanticType:CDSemanticTypeStandard];
187 | }
188 | [build appendSemanticString:[self.type semanticStringForVariableName:self.name]];
189 | return build;
190 | }
191 |
192 | static BOOL _NSStringNullableEqual(NSString *a, NSString *b) {
193 | return (!a && !b) || [a isEqual:b];
194 | }
195 | - (BOOL)isEqual:(id)object {
196 | if ([object isKindOfClass:[self class]]) {
197 | __typeof(self) casted = (__typeof(casted))object;
198 | return [self.name isEqual:casted.name] && [self.attributes isEqual:casted.attributes] &&
199 | _NSStringNullableEqual(self.iVar, casted.iVar) &&
200 | _NSStringNullableEqual(self.getter, casted.getter) &&
201 | _NSStringNullableEqual(self.setter, casted.setter);
202 | }
203 | return NO;
204 | }
205 |
206 | - (NSUInteger)hash {
207 | return self.name.hash;
208 | }
209 |
210 | - (NSString *)description {
211 | return [[self semanticString] string];
212 | }
213 |
214 | - (NSString *)debugDescription {
215 | return [NSString stringWithFormat:@"<%@: %p> {type: %@, attributes: %@, "
216 | "ivar: '%@', getter: '%@', setter: '%@'}",
217 | [self class], self, self.type, self.attributes,
218 | self.iVar, self.getter, self.setter];
219 | }
220 |
221 | @end
222 |
--------------------------------------------------------------------------------
/ClassDump/Models/Reflections/CDProtocolModel+Conformance.h:
--------------------------------------------------------------------------------
1 | //
2 | // CDProtocolModel+Conformance.h
3 | // ClassDump
4 | //
5 | // Created by Leptos on 3/3/24.
6 | // Copyright © 2024 Leptos. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface CDProtocolModel (Conformance)
12 |
13 | /// The class properties required for a type conforming to the given protocols to provide
14 | ///
15 | /// A property with a given name will only appear once in this collection.
16 | + (NSArray *)requiredClassPropertiesToConform:(NSArray *)protocols;
17 | /// The instance properties required for a type conforming to the given protocols to provide
18 | ///
19 | /// A property with a given name will only appear once in this collection.
20 | + (NSArray *)requiredInstancePropertiesToConform:(NSArray *)protocols;
21 |
22 | /// The class methods required for a type conforming to the given protocols to provide
23 | ///
24 | /// A method with a given selector will only appear once in this collection.
25 | + (NSArray *)requiredClassMethodsToConform:(NSArray *)protocols;
26 | /// The instance methods required for a type conforming to the given protocols to provide
27 | ///
28 | /// A method with a given selector will only appear once in this collection.
29 | + (NSArray *)requiredInstanceMethodsToConform:(NSArray *)protocols;
30 |
31 | @end
32 |
--------------------------------------------------------------------------------
/ClassDump/Models/Reflections/CDProtocolModel+Conformance.m:
--------------------------------------------------------------------------------
1 | //
2 | // CDProtocolModel+Conformance.m
3 | // ClassDump
4 | //
5 | // Created by Leptos on 3/3/24.
6 | // Copyright © 2024 Leptos. All rights reserved.
7 | //
8 |
9 | #import "CDProtocolModel+Conformance.h"
10 |
11 | #import
12 |
13 | @implementation CDProtocolModel (Conformance)
14 |
15 | + (NSArray *)requiredClassPropertiesToConform:(NSArray *)protocols {
16 | return [self _genericRequiredForConformance:protocols property:@selector(requiredClassProperties) identifiable:^NSString *(CDPropertyModel *model) {
17 | return model.name;
18 | }];
19 | }
20 | + (NSArray *)requiredInstancePropertiesToConform:(NSArray *)protocols {
21 | return [self _genericRequiredForConformance:protocols property:@selector(requiredInstanceProperties) identifiable:^NSString *(CDPropertyModel *model) {
22 | return model.name;
23 | }];
24 | }
25 |
26 | + (NSArray *)requiredClassMethodsToConform:(NSArray *)protocols {
27 | return [self _genericRequiredForConformance:protocols property:@selector(requiredClassMethods) identifiable:^NSString *(CDMethodModel *model) {
28 | return model.name;
29 | }];
30 | }
31 | + (NSArray *)requiredInstanceMethodsToConform:(NSArray *)protocols {
32 | return [self _genericRequiredForConformance:protocols property:@selector(requiredInstanceMethods) identifiable:^NSString *(CDMethodModel *model) {
33 | return model.name;
34 | }];
35 | }
36 |
37 | // returns NSArray
38 | // mode: KeyPath>
39 | // identifiable: (T) -> T.ID
40 | + (NSArray *)_genericRequiredForConformance:(NSArray *)protocols property:(SEL)mode identifiable:(id(NS_NOESCAPE ^)(id))identifiableResolver {
41 | NSMutableArray *build = [NSMutableArray array];
42 | NSMutableSet *trackingSet = [NSMutableSet set];
43 | [self _genericRequiredForConformance:protocols build:build tracking:trackingSet property:mode identifiable:identifiableResolver];
44 | return build;
45 | }
46 |
47 | + (void)_genericRequiredForConformance:(NSArray *)protocols build:(NSMutableArray *)build tracking:(NSMutableSet *)trackingSet property:(SEL)mode identifiable:(id(NS_NOESCAPE ^)(id))identifiableResolver {
48 | // the order here is very important for protocols that have
49 | // confliciting declarations for a given identifier
50 | for (CDProtocolModel *protocol in protocols) {
51 | NSArray *recursiveResolve = ((id (*)(id, SEL))objc_msgSend)(protocol, mode);
52 | for (id object in recursiveResolve) {
53 | id const identifier = identifiableResolver(object);
54 |
55 | if ([trackingSet containsObject:identifier]) {
56 | continue;
57 | }
58 | [trackingSet addObject:identifier];
59 | [build addObject:object];
60 | }
61 | [self _genericRequiredForConformance:protocol.protocols build:build tracking:trackingSet property:mode identifiable:identifiableResolver];
62 | }
63 | }
64 |
65 | @end
66 |
--------------------------------------------------------------------------------
/ClassDump/Models/Reflections/CDProtocolModel.h:
--------------------------------------------------------------------------------
1 | //
2 | // CDProtocolModel.h
3 | // ClassDump
4 | //
5 | // Created by Leptos on 4/7/19.
6 | // Copyright © 2019 Leptos. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | #import
12 | #import
13 | #import
14 |
15 | @interface CDProtocolModel : NSObject
16 | /// The Obj-C runtime @c Protocol
17 | @property (strong, nonatomic, readonly) Protocol *backing;
18 | /// The name of the protocol, e.g. @c NSObject
19 | @property (strong, nonatomic, readonly) NSString *name;
20 | /// The protocols the protocol conforms to
21 | @property (strong, nonatomic, readonly) NSArray *protocols;
22 |
23 | @property (strong, nonatomic, readonly) NSArray *requiredClassProperties;
24 | @property (strong, nonatomic, readonly) NSArray *requiredInstanceProperties;
25 |
26 | @property (strong, nonatomic, readonly) NSArray *requiredClassMethods;
27 | @property (strong, nonatomic, readonly) NSArray *requiredInstanceMethods;
28 |
29 | @property (strong, nonatomic, readonly) NSArray *optionalClassProperties;
30 | @property (strong, nonatomic, readonly) NSArray *optionalInstanceProperties;
31 |
32 | @property (strong, nonatomic, readonly) NSArray *optionalClassMethods;
33 | @property (strong, nonatomic, readonly) NSArray *optionalInstanceMethods;
34 |
35 | - (instancetype)initWithProtocol:(Protocol *)prcl;
36 | + (instancetype)modelWithProtocol:(Protocol *)prcl;
37 |
38 | /// Generate an @c interface for the protocol
39 | /// @param comments Generate comments with information such as the
40 | /// image the declaration was found in
41 | /// @param synthesizeStrip Remove methods and ivars synthesized from properties
42 | - (NSString *)linesWithComments:(BOOL)comments synthesizeStrip:(BOOL)synthesizeStrip;
43 | /// Generate an @c interface for the protocol
44 | /// @param comments Generate comments with information such as the
45 | /// image the declaration was found in
46 | /// @param synthesizeStrip Remove methods and ivars synthesized from properties
47 | - (CDSemanticString *)semanticLinesWithComments:(BOOL)comments synthesizeStrip:(BOOL)synthesizeStrip;
48 |
49 | /// Generate an @c interface for the protocol
50 | - (CDSemanticString *)semanticLinesWithOptions:(CDGenerationOptions *)options;
51 |
52 | /// Classes the protocol references in the declaration
53 | ///
54 | /// In other words, all the classes that the compiler would need to see
55 | /// for the header to pass the type checking stage of compilation.
56 | - (NSSet *)classReferences;
57 | /// Protocols the protocol references in the declaration
58 | ///
59 | /// In other words, all the protocols that the compiler would need to see
60 | /// for the header to pass the type checking stage of compilation.
61 | - (NSSet *)protocolReferences;
62 |
63 | @end
64 |
--------------------------------------------------------------------------------
/ClassDump/Models/Reflections/CDProtocolModel.m:
--------------------------------------------------------------------------------
1 | //
2 | // CDProtocolModel.m
3 | // ClassDump
4 | //
5 | // Created by Leptos on 4/7/19.
6 | // Copyright © 2019 Leptos. All rights reserved.
7 | //
8 |
9 | #import "CDProtocolModel.h"
10 |
11 | #import
12 |
13 | @implementation CDProtocolModel {
14 | NSArray *_classPropertySynthesizedMethods;
15 | NSArray *_instancePropertySynthesizedMethods;
16 | }
17 |
18 | + (instancetype)modelWithProtocol:(Protocol *)prcl {
19 | return [[self alloc] initWithProtocol:prcl];
20 | }
21 |
22 | - (instancetype)initWithProtocol:(Protocol *)prcl {
23 | if (self = [self init]) {
24 | _backing = prcl;
25 | _name = NSStringFromProtocol(prcl);
26 |
27 | unsigned int count, index;
28 |
29 | Protocol *__unsafe_unretained *protocolList = protocol_copyProtocolList(prcl, &count);
30 | if (protocolList) {
31 | NSMutableArray *protocols = [NSMutableArray arrayWithCapacity:count];
32 | for (index = 0; index < count; index++) {
33 | /* circular dependecies are illegal */
34 | Protocol *objc_protocol = protocolList[index];
35 | [protocols addObject:[CDProtocolModel modelWithProtocol:objc_protocol]];
36 | }
37 | free(protocolList);
38 | _protocols = [protocols copy];
39 | }
40 |
41 | NSMutableArray *classSynthMeths = [NSMutableArray array];
42 | NSMutableArray *instcSynthMeths = [NSMutableArray array];
43 |
44 | #if 0 /* this appears to not be working properly, depending on version combinations between runtime enviorment and target image */
45 | objc_property_t *reqClassProps = protocol_copyPropertyList2(prcl, &count, YES, NO);
46 | if (reqClassProps) {
47 | NSMutableArray *properties = [NSMutableArray arrayWithCapacity:count];
48 | for (index = 0; index < count; index++) {
49 | CDPropertyModel *propertyRep = [CDPropertyModel modelWithProperty:reqClassProps[index] isClass:YES];
50 | [properties addObject:propertyRep];
51 | NSString *synthMethodName;
52 | if ((synthMethodName = propertyRep.getter)) {
53 | [classSynthMeths addObject:synthMethodName];
54 | }
55 | if ((synthMethodName = propertyRep.setter)) {
56 | [classSynthMeths addObject:synthMethodName];
57 | }
58 | }
59 | free(reqClassProps);
60 | _requiredClassProperties = [properties copy];
61 | }
62 | #endif
63 | struct objc_method_description *reqClassMeths = protocol_copyMethodDescriptionList(prcl, YES, NO, &count);
64 | if (reqClassMeths) {
65 | NSMutableArray *methods = [NSMutableArray arrayWithCapacity:count];
66 | for (index = 0; index < count; index++) {
67 | [methods addObject:[CDMethodModel modelWithMethod:reqClassMeths[index] isClass:YES]];
68 | }
69 | free(reqClassMeths);
70 | _requiredClassMethods = [methods copy];
71 | }
72 |
73 | objc_property_t *reqInstProps = protocol_copyPropertyList2(prcl, &count, YES, YES);
74 | if (reqInstProps) {
75 | NSMutableArray *properties = [NSMutableArray arrayWithCapacity:count];
76 | for (index = 0; index < count; index++) {
77 | CDPropertyModel *propertyRep = [CDPropertyModel modelWithProperty:reqInstProps[index] isClass:NO];
78 | [properties addObject:propertyRep];
79 | NSString *synthMethodName;
80 | if ((synthMethodName = propertyRep.getter)) {
81 | [instcSynthMeths addObject:synthMethodName];
82 | }
83 | if ((synthMethodName = propertyRep.setter)) {
84 | [instcSynthMeths addObject:synthMethodName];
85 | }
86 | }
87 | free(reqInstProps);
88 | _requiredInstanceProperties = [properties copy];
89 | }
90 | struct objc_method_description *reqInstMeths = protocol_copyMethodDescriptionList(prcl, YES, YES, &count);
91 | if (reqInstMeths) {
92 | NSMutableArray *methods = [NSMutableArray arrayWithCapacity:count];
93 | for (index = 0; index < count; index++) {
94 | [methods addObject:[CDMethodModel modelWithMethod:reqInstMeths[index] isClass:NO]];
95 | }
96 | free(reqInstMeths);
97 | _requiredInstanceMethods = [methods copy];
98 | }
99 |
100 | objc_property_t *optClassProps = protocol_copyPropertyList2(prcl, &count, NO, NO);
101 | if (optClassProps) {
102 | NSMutableArray *properties = [NSMutableArray arrayWithCapacity:count];
103 | for (index = 0; index < count; index++) {
104 | CDPropertyModel *propertyRep = [CDPropertyModel modelWithProperty:optClassProps[index] isClass:YES];
105 | [properties addObject:propertyRep];
106 | NSString *synthMethodName;
107 | if ((synthMethodName = propertyRep.getter)) {
108 | [classSynthMeths addObject:synthMethodName];
109 | }
110 | if ((synthMethodName = propertyRep.setter)) {
111 | [classSynthMeths addObject:synthMethodName];
112 | }
113 | }
114 | free(optClassProps);
115 | _optionalClassProperties = [properties copy];
116 | }
117 | struct objc_method_description *optClassMeths = protocol_copyMethodDescriptionList(prcl, NO, NO, &count);
118 | if (optClassMeths) {
119 | NSMutableArray *methods = [NSMutableArray arrayWithCapacity:count];
120 | for (index = 0; index < count; index++) {
121 | [methods addObject:[CDMethodModel modelWithMethod:(optClassMeths[index]) isClass:YES]];
122 | }
123 | free(optClassMeths);
124 | _requiredClassMethods = [methods copy];
125 | }
126 |
127 | objc_property_t *optInstProps = protocol_copyPropertyList2(prcl, &count, NO, YES);
128 | if (optInstProps) {
129 | NSMutableArray *properties = [NSMutableArray arrayWithCapacity:count];
130 | for (index = 0; index < count; index++) {
131 | CDPropertyModel *propertyRep = [CDPropertyModel modelWithProperty:optInstProps[index] isClass:NO];
132 | [properties addObject:propertyRep];
133 | NSString *synthMethodName;
134 | if ((synthMethodName = propertyRep.getter)) {
135 | [instcSynthMeths addObject:synthMethodName];
136 | }
137 | if ((synthMethodName = propertyRep.setter)) {
138 | [instcSynthMeths addObject:synthMethodName];
139 | }
140 | }
141 | free(optInstProps);
142 | _optionalInstanceProperties = [properties copy];
143 | }
144 | struct objc_method_description *optInstMeths = protocol_copyMethodDescriptionList(prcl, NO, YES, &count);
145 | if (optInstMeths) {
146 | NSMutableArray *methods = [NSMutableArray arrayWithCapacity:count];
147 | for (index = 0; index < count; index++) {
148 | [methods addObject:[CDMethodModel modelWithMethod:optInstMeths[index] isClass:NO]];
149 | }
150 | free(optInstMeths);
151 | _optionalInstanceMethods = [methods copy];
152 | }
153 |
154 | _classPropertySynthesizedMethods = [classSynthMeths copy];
155 | _instancePropertySynthesizedMethods = [instcSynthMeths copy];
156 | }
157 | return self;
158 | }
159 |
160 | - (NSString *)linesWithComments:(BOOL)comments synthesizeStrip:(BOOL)synthesizeStrip {
161 | return [[self semanticLinesWithComments:comments synthesizeStrip:synthesizeStrip] string];
162 | }
163 |
164 | - (CDSemanticString *)semanticLinesWithComments:(BOOL)comments synthesizeStrip:(BOOL)synthesizeStrip {
165 | CDGenerationOptions *options = [CDGenerationOptions new];
166 | options.addSymbolImageComments = comments;
167 | options.stripSynthesized = synthesizeStrip;
168 | return [self semanticLinesWithOptions:options];
169 | }
170 |
171 | - (CDSemanticString *)semanticLinesWithOptions:(CDGenerationOptions *)options {
172 | CDSemanticString *build = [CDSemanticString new];
173 |
174 | NSSet *forwardClasses = [self _forwardDeclarableClassReferences];
175 | NSUInteger const forwardClassCount = forwardClasses.count;
176 | if (forwardClassCount > 0) {
177 | [build appendString:@"@class" semanticType:CDSemanticTypeKeyword];
178 | [build appendString:@" " semanticType:CDSemanticTypeStandard];
179 |
180 | NSUInteger classNamesRemaining = forwardClassCount;
181 | for (NSString *className in forwardClasses) {
182 | [build appendString:className semanticType:CDSemanticTypeClass];
183 | classNamesRemaining--;
184 | if (classNamesRemaining > 0) {
185 | [build appendString:@", " semanticType:CDSemanticTypeStandard];
186 | }
187 | }
188 | [build appendString:@";\n" semanticType:CDSemanticTypeStandard];
189 | }
190 |
191 | NSSet *forwardProtocols = [self _forwardDeclarableProtocolReferences];
192 | NSUInteger const forwardProtocolCount = forwardProtocols.count;
193 | if (forwardProtocolCount > 0) {
194 | [build appendString:@"@protocol" semanticType:CDSemanticTypeKeyword];
195 | [build appendString:@" " semanticType:CDSemanticTypeStandard];
196 |
197 | NSUInteger protocolNamesRemaining = forwardProtocolCount;
198 | for (NSString *protocolNames in forwardProtocols) {
199 | [build appendString:protocolNames semanticType:CDSemanticTypeProtocol];
200 | protocolNamesRemaining--;
201 | if (protocolNamesRemaining > 0) {
202 | [build appendString:@", " semanticType:CDSemanticTypeStandard];
203 | }
204 | }
205 | [build appendString:@";\n" semanticType:CDSemanticTypeStandard];
206 | }
207 |
208 | if (forwardClassCount > 0 || forwardProtocolCount > 0) {
209 | [build appendString:@"\n" semanticType:CDSemanticTypeStandard];
210 | }
211 |
212 | if (options.addSymbolImageComments) {
213 | NSString *comment = nil;
214 | Dl_info info;
215 | if (dladdr((__bridge void *)self.backing, &info)) {
216 | comment = [NSString stringWithFormat:@"/* %s in %s */", info.dli_sname ?: "(anonymous)", info.dli_fname];
217 | } else {
218 | comment = @"/* no symbol found */";
219 | }
220 | [build appendString:comment semanticType:CDSemanticTypeComment];
221 | [build appendString:@"\n" semanticType:CDSemanticTypeStandard];
222 | }
223 | [build appendString:@"@protocol" semanticType:CDSemanticTypeKeyword];
224 | [build appendString:@" " semanticType:CDSemanticTypeStandard];
225 | [build appendString:self.name semanticType:CDSemanticTypeProtocol];
226 |
227 | NSArray *protocols = self.protocols;
228 | NSUInteger const protocolCount = protocols.count;
229 | if (protocolCount > 0) {
230 | [build appendString:@" " semanticType:CDSemanticTypeStandard];
231 | [build appendString:@"<" semanticType:CDSemanticTypeStandard];
232 | [protocols enumerateObjectsUsingBlock:^(CDProtocolModel *protocol, NSUInteger idx, BOOL *stop) {
233 | [build appendString:protocol.name semanticType:CDSemanticTypeProtocol];
234 | if ((idx + 1) < protocolCount) {
235 | [build appendString:@", " semanticType:CDSemanticTypeStandard];
236 | }
237 | }];
238 | [build appendString:@">" semanticType:CDSemanticTypeStandard];
239 | }
240 | [build appendString:@"\n" semanticType:CDSemanticTypeStandard];
241 |
242 | [self _appendLines:build properties:self.requiredClassProperties comments:options.addSymbolImageComments];
243 | [self _appendLines:build methods:self.requiredClassMethods synthesized:(options.stripSynthesized ? _classPropertySynthesizedMethods : nil) comments:options.addSymbolImageComments];
244 |
245 | [self _appendLines:build properties:self.requiredInstanceProperties comments:options.addSymbolImageComments];
246 | [self _appendLines:build methods:self.requiredInstanceMethods synthesized:(options.stripSynthesized ? _instancePropertySynthesizedMethods : nil) comments:options.addSymbolImageComments];
247 |
248 | if (self.optionalClassProperties.count || self.optionalClassMethods.count ||
249 | self.optionalInstanceProperties.count || self.optionalInstanceMethods.count) {
250 | [build appendString:@"\n" semanticType:CDSemanticTypeStandard];
251 | [build appendString:@"@optional" semanticType:CDSemanticTypeKeyword];
252 | [build appendString:@"\n" semanticType:CDSemanticTypeStandard];
253 | }
254 |
255 | [self _appendLines:build properties:self.optionalClassProperties comments:options.addSymbolImageComments];
256 | [self _appendLines:build methods:self.optionalClassMethods synthesized:(options.stripSynthesized ? _classPropertySynthesizedMethods : nil) comments:options.addSymbolImageComments];
257 |
258 | [self _appendLines:build properties:self.optionalInstanceProperties comments:options.addSymbolImageComments];
259 | [self _appendLines:build methods:self.optionalInstanceMethods synthesized:(options.stripSynthesized ? _instancePropertySynthesizedMethods : nil) comments:options.addSymbolImageComments];
260 |
261 | [build appendString:@"\n" semanticType:CDSemanticTypeStandard];
262 | [build appendString:@"@end" semanticType:CDSemanticTypeKeyword];
263 | [build appendString:@"\n" semanticType:CDSemanticTypeStandard];
264 |
265 | return build;
266 | }
267 |
268 | - (void)_appendLines:(CDSemanticString *)build properties:(NSArray *)properties comments:(BOOL)comments {
269 | if (properties.count) {
270 | [build appendString:@"\n" semanticType:CDSemanticTypeStandard];
271 |
272 | Dl_info info;
273 | for (CDPropertyModel *prop in properties) {
274 | if (comments) {
275 | NSString *comment = nil;
276 | if (dladdr(prop.backing, &info)) {
277 | comment = [NSString stringWithFormat:@"/* in %s */", info.dli_fname];
278 | } else {
279 | comment = @"/* no symbol found */";
280 | }
281 | [build appendString:@"\n" semanticType:CDSemanticTypeStandard];
282 | [build appendString:comment semanticType:CDSemanticTypeComment];
283 | [build appendString:@"\n" semanticType:CDSemanticTypeStandard];
284 | }
285 | [build appendSemanticString:[prop semanticString]];
286 | [build appendString:@";\n" semanticType:CDSemanticTypeStandard];
287 | }
288 | }
289 | }
290 |
291 | - (void)_appendLines:(CDSemanticString *)build methods:(NSArray *)methods synthesized:(NSArray *)synthesized comments:(BOOL)comments {
292 | if (methods.count) {
293 | [build appendString:@"\n" semanticType:CDSemanticTypeStandard];
294 |
295 | Dl_info info;
296 | NSMutableArray *synthed = [NSMutableArray arrayWithArray:synthesized];
297 | NSUInteger synthedCount = synthed.count;
298 | for (CDMethodModel *methd in methods) {
299 | // find and remove instead of just find so we don't have to search the entire
300 | // array everytime, when we know the objects that we've already filtered out won't come up again
301 | NSUInteger const searchResult = [synthed indexOfObject:methd.name inRange:NSMakeRange(0, synthedCount)];
302 | if (searchResult != NSNotFound) {
303 | synthedCount--;
304 | // optimized version of remove since the
305 | // order of synthed doesn't matter to us.
306 | // exchange is O(1) instead of remove is O(n)
307 | [synthed exchangeObjectAtIndex:searchResult withObjectAtIndex:synthedCount];
308 | continue;
309 | }
310 |
311 | if (comments) {
312 | NSString *comment = nil;
313 | if (dladdr(methd.backing.types, &info)) {
314 | comment = [NSString stringWithFormat:@"/* in %s */", info.dli_fname];
315 | } else {
316 | comment = @"/* no symbol found */";
317 | }
318 | [build appendString:@"\n" semanticType:CDSemanticTypeStandard];
319 | [build appendString:comment semanticType:CDSemanticTypeComment];
320 | [build appendString:@"\n" semanticType:CDSemanticTypeStandard];
321 | }
322 | [build appendSemanticString:[methd semanticString]];
323 | [build appendString:@";\n" semanticType:CDSemanticTypeStandard];
324 | }
325 | }
326 | }
327 |
328 | - (NSSet *)_forwardDeclarableClassReferences {
329 | NSMutableSet *build = [NSMutableSet set];
330 |
331 | [self _unionReferences:build sources:self.requiredClassProperties resolve:^NSSet *(CDPropertyModel *model) {
332 | return [model.type classReferences];
333 | }];
334 | [self _unionReferences:build sources:self.requiredInstanceProperties resolve:^NSSet *(CDPropertyModel *model) {
335 | return [model.type classReferences];
336 | }];
337 |
338 | [self _unionReferences:build sources:self.requiredClassMethods resolve:^NSSet *(CDMethodModel *model) {
339 | return [model classReferences];
340 | }];
341 | [self _unionReferences:build sources:self.requiredInstanceMethods resolve:^NSSet *(CDMethodModel *model) {
342 | return [model classReferences];
343 | }];
344 |
345 | [self _unionReferences:build sources:self.optionalClassProperties resolve:^NSSet *(CDPropertyModel *model) {
346 | return [model.type classReferences];
347 | }];
348 | [self _unionReferences:build sources:self.optionalInstanceProperties resolve:^NSSet *(CDPropertyModel *model) {
349 | return [model.type classReferences];
350 | }];
351 |
352 | [self _unionReferences:build sources:self.optionalClassMethods resolve:^NSSet *(CDMethodModel *model) {
353 | return [model classReferences];
354 | }];
355 | [self _unionReferences:build sources:self.optionalInstanceMethods resolve:^NSSet *(CDMethodModel *model) {
356 | return [model classReferences];
357 | }];
358 |
359 | return build;
360 | }
361 |
362 | - (NSSet *)_forwardDeclarableProtocolReferences {
363 | NSMutableSet *build = [NSMutableSet set];
364 |
365 | [self _unionReferences:build sources:self.requiredClassProperties resolve:^NSSet *(CDPropertyModel *model) {
366 | return [model.type protocolReferences];
367 | }];
368 | [self _unionReferences:build sources:self.requiredInstanceProperties resolve:^NSSet *(CDPropertyModel *model) {
369 | return [model.type protocolReferences];
370 | }];
371 |
372 | [self _unionReferences:build sources:self.requiredClassMethods resolve:^NSSet *(CDMethodModel *model) {
373 | return [model protocolReferences];
374 | }];
375 | [self _unionReferences:build sources:self.requiredInstanceMethods resolve:^NSSet *(CDMethodModel *model) {
376 | return [model protocolReferences];
377 | }];
378 |
379 | [self _unionReferences:build sources:self.optionalClassProperties resolve:^NSSet *(CDPropertyModel *model) {
380 | return [model.type protocolReferences];
381 | }];
382 | [self _unionReferences:build sources:self.optionalInstanceProperties resolve:^NSSet *(CDPropertyModel *model) {
383 | return [model.type protocolReferences];
384 | }];
385 |
386 | [self _unionReferences:build sources:self.optionalClassMethods resolve:^NSSet *(CDMethodModel *model) {
387 | return [model protocolReferences];
388 | }];
389 | [self _unionReferences:build sources:self.optionalInstanceMethods resolve:^NSSet *(CDMethodModel *model) {
390 | return [model protocolReferences];
391 | }];
392 |
393 | [build removeObject:self.name];
394 | return build;
395 | }
396 |
397 | - (NSSet *)classReferences {
398 | return [self _forwardDeclarableClassReferences];
399 | }
400 |
401 | - (NSSet *)protocolReferences {
402 | NSMutableSet