├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── Runtime.plist ├── control ├── log.xm ├── runtest_sources ├── inc_long.h ├── inc_short.h └── main.m ├── sources ├── RTClass.h ├── RTClass.m ├── RTDecoding.h ├── RTDecoding.m ├── RTIvar.h ├── RTIvar.m ├── RTMethod.h ├── RTMethod.m ├── RTObject.h ├── RTObject.m ├── RTProperty.h ├── RTProperty.m ├── RTPropertyAttribute.h ├── RTPropertyAttribute.m ├── RTProtocol.h ├── RTProtocol.m ├── RTSelector.h ├── RTSelector.m ├── RT_Runtime.h ├── Runtime.h └── Runtime.m └── travis ├── before └── script /.gitignore: -------------------------------------------------------------------------------- 1 | .theos 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: 2 | - linux 3 | - osx 4 | 5 | dist: trusty 6 | sudo: required 7 | osx_image: xcode7.2 8 | 9 | env: 10 | - THEOS=theos 11 | 12 | before_script: 13 | - git clone --recursive https://github.com/theos/theos.git 14 | - ./travis/before 15 | 16 | script: 17 | - ./travis/script 18 | 19 | compiler: 20 | - clang 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 uroboro 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | export TARGET = native 2 | ifeq ($(shell uname -p), i386) 3 | export ARCHS = x86_64 4 | else 5 | export ARCHS = armv7 arm64 6 | endif 7 | 8 | findfiles = $(foreach ext, c cpp m mm x xm xi xmi, $(wildcard $(1)/*.$(ext))) 9 | 10 | include $(THEOS)/makefiles/common.mk 11 | 12 | LIBRARY_NAME = Runtime 13 | Runtime_FILES = $(call findfiles,sources) 14 | #Runtime_FRAMEWORKS = CoreFoundation Foundation 15 | 16 | TWEAK_NAME = RuntimeTweak 17 | RuntimeTweak_FILES = log.xm 18 | 19 | TOOL_NAME = runtest 20 | runtest_FILES = $(call findfiles,runtest_sources) 21 | runtest_CFLAGS = -Isources 22 | 23 | include $(THEOS_MAKE_PATH)/library.mk 24 | include $(THEOS_MAKE_PATH)/tool.mk 25 | 26 | test: Runtime runtest 27 | ls $(THEOS_OBJ_DIR) 28 | @$(or $(THEOS_OBJ_DIR),./)/$(TOOL_NAME) $(THEOS_OBJ_DIR) 29 | 30 | remove: 31 | $(ECHO_NOTHING)exec ssh -p $(THEOS_DEVICE_PORT) root@$(THEOS_DEVICE_IP) "apt-get -y remove $(THEOS_PACKAGE_NAME)"$(ECHO_END) 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Runtime: *Objective-C runtime access in Objective-C* 2 | 3 | [![Travis Status](https://travis-ci.org/uroboro/Runtime.svg)](https://travis-ci.org/uroboro/Runtime) 4 | 5 | For use with Cycript. 6 | 7 | Currently can read this: 8 | 9 | ``` objc 10 | @protocol XYZProtocol 11 | - (void)aMethod; 12 | @end 13 | @interface XYZClass : NSObject { 14 | int **I[9]; 15 | void *(*P)(int, char *); 16 | CGFloat CGF[3]; 17 | CGFloat asdf[2][3][4]; 18 | union ting { long L; char C[4]; struct hongs { char C:4; int B[2]; } hongs; } tong; 19 | struct thing { void *(*P[2])(); int I; } thang; 20 | union thongs { char C:4; int B[2]; } thungs[9875]; 21 | } 22 | @property char c; 23 | @property (nonatomic, retain) id object; 24 | @property (nonatomic, assign) union thong1 { int II:4; int B[2]; } thung1; 25 | @property (nonatomic, assign) union thong2 { int B[2]; char C:4; } thung2; 26 | @property (nonatomic, assign) struct thing3 { int I; } thang3; 27 | @property (atomic, assign, getter=isOtherObject, setter=otherObjectIs:) BOOL otherObject; 28 | - (void)aMethodWithArg:(id)arg; 29 | - (int *)aMethodWithArg:(id)arg0 andArg:(int[2])arg1; 30 | - (void)aMethod; 31 | @end 32 | @implementation XYZClass 33 | - (void)aMethod { 34 | rLog(@"I got called"); 35 | } 36 | - (void)aMethodWithArg:(id)arg { 37 | rLog(@"I got called with arg: %@", [arg description]); 38 | } 39 | - (int *)aMethodWithArg:(id)arg0 andArg:(int[2])arg1 { 40 | rLog(@"I got called with arg0:%@ and arg1:%d", [arg0 description], arg1[0]); 41 | static int a[2] = { 0, 1 }; 42 | return a; 43 | } 44 | @end 45 | ``` 46 | 47 | Into this: 48 | ```objc 49 | @interface XYZClass { 50 | int **I[9]; 51 | /* fp */ void *(**P)(void *); 52 | float CGF[3]; 53 | float asdf[2][3][4]; 54 | union ting { long L; char C[4]; struct hongs { char C:4; int B[2]; } hongs; } tong; 55 | struct thing { /* fp */ void *(**P[2])(void *); int I; } thang; 56 | union thongs { char C:4; int B[2]; } thungs[9875]; 57 | } 58 | @property (assign) char c; 59 | @property (nonatomic, retain) id object; 60 | @property (nonatomic, assign) union thong1 { char II:4; int B[2]; } thung1; 61 | @property (nonatomic, assign) union thong2 { int B[2]; char C:4; } thung2; 62 | @property (nonatomic, assign) struct thing3 { int I; } thang3; 63 | @property (assign, getter=isOtherObject, setter=otherObjectIs:) char otherObject; 64 | - (void)aMethod; 65 | - (void)aMethodWithArg:(id)arg0; 66 | - (int *)aMethodWithArg:(id)arg0 andArg:(int [2])arg1; 67 | @end 68 | ``` 69 | -------------------------------------------------------------------------------- /Runtime.plist: -------------------------------------------------------------------------------- 1 | { 2 | Filter = { 3 | Bundles = ( 4 | "com.apple.Security" 5 | ); 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /control: -------------------------------------------------------------------------------- 1 | Package: com.uroboro.runtime 2 | Name: Runtime 3 | Description: A simple MobileSubstrate tweak! 4 | Version: 0.0.1 5 | Priority: optional 6 | Section: Tweaks 7 | Architecture: iphoneos-arm 8 | Depends: mobilesubstrate 9 | Maintainer: uroboro 10 | Author: uroboro 11 | -------------------------------------------------------------------------------- /log.xm: -------------------------------------------------------------------------------- 1 | %hookf(void, NSLogv, NSString *format, va_list args) { 2 | } 3 | 4 | %hookf(void, CFLog, int32_t lev, CFStringRef format, ...) { 5 | } 6 | 7 | %ctor { 8 | NSArray * blacklist = @[@"BlueTool"]; 9 | if (![blacklist containsObject:NSBundle.mainBundle.bundleIdentifier]) { 10 | %init; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /runtest_sources/inc_long.h: -------------------------------------------------------------------------------- 1 | @interface XYZClass : NSObject { 2 | int **I[9]; 3 | void *(*P)(int, char *); 4 | CGFloat CGF[3]; 5 | CGFloat asdf[2][3][4]; 6 | union ting { long L; char C[4]; struct hongs { char C:4; int B[2]; } hongs; } tong; 7 | struct thing { void *(*P[2])(); int I; } thang; 8 | union thongs { char C:4; int B[2]; } thungs[9875]; 9 | } 10 | @property char c; 11 | @property (nonatomic, retain) id object; 12 | @property (nonatomic, assign) union thong1 { int II:4; int B[2]; } thung1; 13 | @property (nonatomic, assign) union thong2 { int B[2]; char C:4; } thung2; 14 | @property (nonatomic, assign) struct thing3 { int I; } thang3; 15 | @property (atomic, assign, getter=isOtherObject, setter=otherObjectIs:) BOOL otherObject; 16 | @property id *QuantumThang; 17 | @property (retain) XYZClass *QuantumThing; 18 | @property (copy) XYZClass *QuantumThong; 19 | - (void)aMethodWithArg:(id)arg; 20 | - (int *)aMethodWithArg:(id)arg0 andArg:(int[2])arg1; 21 | - (void)aMethod; 22 | @end -------------------------------------------------------------------------------- /runtest_sources/inc_short.h: -------------------------------------------------------------------------------- 1 | @interface XYZClass : NSObject 2 | @property (assign) XYZClass *QuantumThing; 3 | @property (assign) XYZClass *QuantumThong; 4 | @property (assign) id *QuantumThung; 5 | @end -------------------------------------------------------------------------------- /runtest_sources/main.m: -------------------------------------------------------------------------------- 1 | #include 2 | #import 3 | #import 4 | 5 | @protocol XYZProtocol 6 | - (void)aMethod; 7 | @end 8 | 9 | #if 0 10 | #import "inc_short.h" 11 | @implementation XYZClass 12 | - (void)aMethod { 13 | } 14 | @end 15 | #else 16 | #import "inc_long.h" 17 | @implementation XYZClass 18 | - (void)aMethod { 19 | rLog(@"I got called"); 20 | } 21 | - (void)aMethodWithArg:(id)arg { 22 | rLog(@"I got called with arg: %@", [arg description]); 23 | } 24 | - (int *)aMethodWithArg:(id)arg0 andArg:(int[2])arg1 { 25 | rLog(@"I got called with arg0:%@ and arg1:%d", [arg0 description], arg1[0]); 26 | static int a[2] = { 0, 1 }; 27 | return a; 28 | } 29 | @end 30 | #endif 31 | //#define $ctor __attribute__((constructor)) static void ctor ## __LINE__ (int argc, char **argv, char **envp) 32 | //$ctor { rLog(@"ctor loading"); } 33 | //%dtor { rLog(@"dtor unloading"); } 34 | 35 | int main(int argc, char **argv, char **envp, char **apple) { 36 | @autoreleasepool { 37 | if (argc < 2) { 38 | return 1; 39 | } 40 | 41 | char str[256]; 42 | strcpy(str, argv[1]); 43 | strcat(str, "/Runtime.dylib"); 44 | printf("string: %s\n", str); 45 | void * dylib = dlopen(str, RTLD_NOW); 46 | 47 | Class RTRuntime = objc_getClass("RTRuntime"); 48 | for (NSString * imageName in [RTRuntime imageNames]) { 49 | printf("%s:\n[ %s ]\n", imageName.UTF8String, [[RTRuntime classNamesForImage:imageName] componentsJoinedByString:@", "].UTF8String); 50 | } 51 | 52 | /*printf("\n\e[36m%s\e[m\n", [[objc_getClass("RTClass") classWithClass:NSString.class] description].UTF8String);*/ 53 | 54 | dlclose(dylib); 55 | } 56 | 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /sources/RTClass.h: -------------------------------------------------------------------------------- 1 | #import "Runtime.h" 2 | 3 | @class RTIvar; 4 | 5 | @interface RTClass : NSObject 6 | @property (nonatomic) Class internalClass; 7 | + (instancetype)classWithClass:(Class)cls; 8 | - (instancetype)initWithClass:(Class)cls; 9 | - (RTIvar *)getInstanceVariableWithName:(NSString *)name; 10 | @end 11 | -------------------------------------------------------------------------------- /sources/RTClass.m: -------------------------------------------------------------------------------- 1 | #import "RTClass.h" 2 | #import "RTIvar.h" 3 | #import "RTProperty.h" 4 | #import "RTMethod.h" 5 | #import "RTSelector.h" 6 | #import "RTProtocol.h" 7 | 8 | @implementation RTClass 9 | 10 | + (instancetype)classWithClass:(Class)cls { 11 | return [[[self alloc] initWithClass:cls] autorelease]; 12 | } 13 | - (instancetype)initWithClass:(Class)cls { 14 | if ((self = [super init])) { 15 | _internalClass = cls; 16 | } 17 | return self; 18 | } 19 | - (Class)internalClass { 20 | return _internalClass; 21 | } 22 | 23 | - (NSString *)name { 24 | return [NSString stringWithUTF8String:class_getName(_internalClass)]; 25 | } 26 | - (RTClass *)getSuperclass { 27 | return [RTClass classWithClass:class_getSuperclass(_internalClass)]; 28 | } 29 | - (BOOL)isMetaClass { 30 | return class_isMetaClass(_internalClass); 31 | } 32 | - (size_t)getInstanceSize { 33 | return class_getInstanceSize(_internalClass); 34 | } 35 | - (RTIvar *)getClassVariableWithName:(NSString *)name { 36 | return [RTIvar ivarWithIvar:class_getClassVariable(_internalClass, name.UTF8String) andOwner:self]; 37 | } 38 | 39 | - (int)getVersion { 40 | return class_getVersion(_internalClass); 41 | } 42 | - (void)setVersion:(int)version { 43 | class_setVersion(_internalClass, version); 44 | } 45 | 46 | #pragma mark - Class Ivars 47 | - (NSArray *)ivars { 48 | unsigned int outCount = 0; 49 | Ivar *ivars = class_copyIvarList(_internalClass, &outCount); 50 | //rLog(@"ivars in class %@: %d", NSStringFromClass(_internalClass), outCount); 51 | CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, (CFIndex)outCount, NULL); 52 | for (unsigned int i = 0; i < outCount; i++) { 53 | CFArrayAppendValue(array, [RTIvar ivarWithIvar:ivars[i] andOwner:self]); 54 | } 55 | free(ivars); 56 | NSArray *r = [(NSMutableArray *)array copy]; 57 | CFRelease(array); 58 | return r; 59 | } 60 | - (RTIvar *)getInstanceVariableWithName:(NSString *)name { 61 | return [RTIvar ivarWithIvar:class_getInstanceVariable(_internalClass, name.UTF8String) andOwner:self]; 62 | } 63 | - (BOOL)addIvarWithName:(NSString *)name size:(size_t)size alignment:(uint8_t)alignment types:(NSString *)types { 64 | return class_addIvar(_internalClass, name.UTF8String, size, alignment, types.UTF8String); 65 | } 66 | - (const uint8_t *)getIvarLayout { 67 | return class_getIvarLayout(_internalClass); 68 | } 69 | - (void)setIvarLayout:(const uint8_t *)layout { 70 | class_setIvarLayout(_internalClass, layout); 71 | } 72 | - (const uint8_t *)getWeakIvarLayout { 73 | return class_getWeakIvarLayout(_internalClass); 74 | } 75 | - (void)setWeakIvarLayout:(const uint8_t *)layout { 76 | class_setWeakIvarLayout(_internalClass, layout); 77 | } 78 | 79 | #pragma mark - Class Properties 80 | - (NSArray *)properties { 81 | unsigned int outCount = 0; 82 | Property *properties = class_copyPropertyList(_internalClass, &outCount); 83 | //rLog(@"properties in class %@: %d", NSStringFromClass(_internalClass), outCount); 84 | CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, (CFIndex)outCount, NULL); 85 | for (unsigned int i = 0; i < outCount; i++) { 86 | CFArrayAppendValue(array, [RTProperty propertyWithProperty:properties[i] andOwner:self]); 87 | } 88 | free(properties); 89 | NSArray *r = [(NSMutableArray *)array copy]; 90 | CFRelease(array); 91 | return r; 92 | } 93 | - (RTProperty *)getPropertyWithName:(NSString *)name { 94 | return [RTProperty propertyWithProperty:class_getProperty(_internalClass, name.UTF8String) andOwner:self]; 95 | } 96 | 97 | #pragma mark - Class Methods 98 | - (NSArray *)methods { 99 | unsigned int outCount = 0; 100 | Method *methods = class_copyMethodList(_internalClass, &outCount); 101 | //rLog(@"methods in class %@: %d", NSStringFromClass(_internalClass), outCount); 102 | CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, (CFIndex)outCount, NULL); 103 | for (unsigned int i = 0; i < outCount; i++) { 104 | CFArrayAppendValue(array, [RTMethod methodWithMethod:methods[i] andOwner:self]); 105 | } 106 | free(methods); 107 | NSArray *r = [(NSMutableArray *)array copy]; 108 | CFRelease(array); 109 | return r; 110 | } 111 | - (BOOL)addMethodForSelector:(RTSelector *)selector implementation:(Implementation)imp types:(NSString *)types { 112 | return class_addMethod(_internalClass, selector.internalSelector, imp, types.UTF8String); 113 | } 114 | - (RTMethod *)getInstanceMethodForSelector:(RTSelector *)selector { 115 | RTMethod *m = [RTMethod methodWithMethod:class_getInstanceMethod(_internalClass, selector.internalSelector) andOwner:self]; 116 | m.isClassMethod = NO; 117 | return m; 118 | } 119 | - (RTMethod *)getClassMethodForSelector:(RTSelector *)selector { 120 | RTMethod *m = [RTMethod methodWithMethod:class_getClassMethod(_internalClass, selector.internalSelector) andOwner:self]; 121 | m.isClassMethod = YES; 122 | return m; 123 | } 124 | - (Implementation)replaceMethodForSelector:(RTSelector *)selector implementation:(Implementation)imp types:(NSString *)types { 125 | return class_replaceMethod(_internalClass, selector.internalSelector, imp, types.UTF8String); 126 | } 127 | - (Implementation)getMethodImplementationForSelector:(RTSelector *)selector { 128 | return class_getMethodImplementation(_internalClass, selector.internalSelector); 129 | } 130 | #if !__LP64__ 131 | - (Implementation)getMethodImplementation_stretForSelector:(RTSelector *)selector { 132 | return class_getMethodImplementation_stret(_internalClass, selector.internalSelector); 133 | } 134 | #endif /* __LP64__ */ 135 | - (BOOL)respondsToSelector:(Selector)selector { 136 | return class_respondsToSelector(_internalClass, selector); 137 | } 138 | 139 | #pragma mark - Class Protocols 140 | - (NSArray *)protocols { 141 | unsigned int outCount = 0; 142 | Protocol **protocols = class_copyProtocolList(_internalClass, &outCount); 143 | //rLog(@"protocols in class %@: %d", NSStringFromClass(_internalClass), outCount); 144 | CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, (CFIndex)outCount, NULL); 145 | for (unsigned int i = 0; i < outCount; i++) { 146 | CFArrayAppendValue(array, [RTProtocol protocolWithProtocol:protocols[i]]); 147 | } 148 | free(protocols); 149 | NSArray *r = [(NSMutableArray *)array copy]; 150 | CFRelease(array); 151 | return r; 152 | } 153 | - (BOOL)addProtocol:(RTProtocol *)protocol { 154 | return class_addProtocol(_internalClass, protocol.internalProtocol); 155 | } 156 | - (BOOL)addPropertyWithName:(NSString *)name attributes:(NSArray *)attributesArray { 157 | unsigned int attributeCount = (unsigned int)CFArrayGetCount((CFArrayRef)attributesArray); 158 | objc_property_attribute_t *attributes = (objc_property_attribute_t *)malloc(attributeCount * sizeof(objc_property_attribute_t)); 159 | for (unsigned int i = 0; i < attributeCount; i++) { 160 | attributes[i] = *(objc_property_attribute_t *)CFArrayGetValueAtIndex((CFArrayRef)attributesArray, i); 161 | } 162 | BOOL r = class_addProperty(_internalClass, name.UTF8String, attributes, attributeCount); 163 | free(attributes); 164 | return r; 165 | } 166 | - (void)replacePropertyWithName:(NSString *)name attributes:(NSArray *)attributesArray { 167 | unsigned int attributeCount = (unsigned int)CFArrayGetCount((CFArrayRef)attributesArray); 168 | objc_property_attribute_t *attributes = (objc_property_attribute_t *)malloc(attributeCount * sizeof(objc_property_attribute_t)); 169 | for (unsigned int i = 0; i < attributeCount; i++) { 170 | attributes[i] = *(objc_property_attribute_t *)CFArrayGetValueAtIndex((CFArrayRef)attributesArray, i); 171 | } 172 | class_replaceProperty(_internalClass, name.UTF8String, attributes, attributeCount); 173 | free(attributes); 174 | return; 175 | } 176 | - (BOOL)conformsToProtocol:(Protocol *)protocol { 177 | return class_conformsToProtocol(_internalClass, protocol); 178 | } 179 | 180 | #pragma mark - Adding Classes 181 | - (RTClass *)objc_allocateClassPairWithName:(NSString *)name size:(size_t)extraBytes { 182 | return [RTClass classWithClass:objc_allocateClassPair(_internalClass, name.UTF8String, extraBytes)]; 183 | } 184 | - (void)objc_disposeClassPair { 185 | objc_disposeClassPair(_internalClass); 186 | } 187 | - (void)objc_registerClassPair { 188 | objc_registerClassPair(_internalClass); 189 | } 190 | 191 | #pragma mark - Instantiating Classes 192 | - (id)createInstanceWithSize:(size_t)extraBytes { 193 | return class_createInstance(_internalClass, extraBytes); 194 | } 195 | - (id)objc_constructInstanceWithBytes:(void *)bytes { 196 | return objc_constructInstance(_internalClass, bytes); 197 | } 198 | - (void *)objc_destructInstance:(id)obj { 199 | return objc_destructInstance(obj); 200 | } 201 | 202 | #pragma mark - Working with Libraries 203 | - (NSString *)class_getImageName { 204 | return [NSString stringWithUTF8String:class_getImageName(_internalClass)]; 205 | } 206 | 207 | #pragma mark - Description 208 | 209 | - (NSString *)description { 210 | NSMutableArray *protocols = ({ NSArray *a = [self protocols]; NSMutableArray *b = [a mutableCopy]; [a release]; b; }); 211 | NSMutableArray *ivars = ({ NSArray *a = [self ivars]; NSMutableArray *b = [a mutableCopy]; [a release]; b; }); 212 | NSMutableArray *properties = ({ NSArray *a = [self properties]; NSMutableArray *b = [a mutableCopy]; [a release]; b; }); 213 | NSMutableArray *methods = ({ NSArray *a = [self methods]; NSMutableArray *b = [a mutableCopy]; [a release]; b; }); 214 | 215 | NSMutableString *description = [[NSString stringWithFormat:@"@interface %@", self.name] mutableCopy]; 216 | 217 | if (protocols.count > 0) { 218 | [description appendFormat:@" <%@>", [protocols componentsJoinedByString:@", "]]; 219 | } 220 | 221 | #define FILTER_REDUNDANCIES 01 222 | #if FILTER_REDUNDANCIES 223 | [properties enumerateObjectsUsingBlock:^(RTProperty *property, NSUInteger idx, BOOL *stop) { 224 | [ivars enumerateObjectsUsingBlock:^(RTIvar *ivar, NSUInteger idx, BOOL *stop) { 225 | if ([ivar.name isEqualToString:property.backingIvar]) { 226 | *stop = YES; 227 | [ivars removeObject:ivar]; 228 | } 229 | }]; 230 | [methods enumerateObjectsUsingBlock:^(RTMethod *method, NSUInteger idx, BOOL *stop) { 231 | if ([method.name isEqualToString:property.getter]) { 232 | *stop = YES; 233 | [methods removeObject:method]; 234 | } 235 | }]; 236 | [methods enumerateObjectsUsingBlock:^(RTMethod *method, NSUInteger idx, BOOL *stop) { 237 | if ([method.name isEqualToString:property.setter]) { 238 | *stop = YES; 239 | [methods removeObject:method]; 240 | } 241 | }]; 242 | }]; 243 | #endif 244 | 245 | if (ivars.count > 0) { 246 | [description appendFormat:@" {\n\t%@;\n}", [ivars componentsJoinedByString:@";\n\t"]]; 247 | } 248 | [description appendFormat:@"\n"]; 249 | 250 | if (properties.count > 0) { 251 | [description appendFormat:@"%@;\n", [properties componentsJoinedByString:@";\n"]]; 252 | } 253 | 254 | if (methods.count > 0) { 255 | [description appendFormat:@"%@", [methods componentsJoinedByString:@";\n"]]; 256 | [description appendFormat:@";\n"]; 257 | } 258 | 259 | [description appendFormat:@"@end"]; 260 | 261 | [protocols release]; 262 | [ivars release]; 263 | [properties release]; 264 | [methods release]; 265 | 266 | return description; 267 | } 268 | 269 | #pragma mark - Comparison 270 | 271 | - (NSComparisonResult)caseInsensitiveCompare:(RTClass *)rhs { 272 | return [self.name caseInsensitiveCompare:rhs.name]; 273 | } 274 | 275 | @end 276 | -------------------------------------------------------------------------------- /sources/RTDecoding.h: -------------------------------------------------------------------------------- 1 | NSString *rtTypeForEncoding(NSString *encodingString, NSString *varName); 2 | -------------------------------------------------------------------------------- /sources/RTDecoding.m: -------------------------------------------------------------------------------- 1 | #import "Runtime.h" 2 | 3 | typedef enum RTVarType { 4 | RTVarTypePrimitive, 5 | RTVarTypeBitfield, 6 | RTVarTypeArray, 7 | RTVarTypeStruct, 8 | RTVarTypeUnion, 9 | RTVarTypePointer, 10 | RTVarTypeUnknown, 11 | RTVarTypeProperty, 12 | RTVarTypeNone 13 | } RTVarType; 14 | 15 | typedef enum RTStructMode { 16 | RTStructModeField, 17 | RTStructModeType, 18 | RTStructModeNone 19 | } RTStructMode; 20 | 21 | NSArray *rtTypeForStructEncoding_(NSString *encodingString, NSString *varName, NSUInteger number); 22 | NSArray *rtTypeForStructEncoding(NSString *encodingString, NSString *varName) { 23 | return rtTypeForStructEncoding_(encodingString, varName, 0); 24 | } 25 | 26 | NSString *rtTypeForEncoding_(NSString *encodingString, NSString *varName, NSUInteger number); 27 | NSString *rtTypeForEncoding(NSString *encodingString, NSString *varName) { 28 | NSString *removableIdentifier = @"$$$$$$"; 29 | varName = varName.length > 0 ? varName : removableIdentifier; 30 | NSString *typedString = rtTypeForEncoding_(encodingString, varName, 0); 31 | typedString = [typedString stringByReplacingOccurrencesOfString:removableIdentifier withString:@""]; 32 | return typedString; 33 | } 34 | 35 | NSArray *rtTypeForStructEncoding_(NSString *encodingString, NSString *varName, NSUInteger number) { 36 | char *encoding = strdup(encodingString.UTF8String); 37 | int encodingLength = strlen(encoding); 38 | 39 | NSArray *typedArray = nil; 40 | NSString *typedString = nil; 41 | RTStructMode mode = RTStructModeNone; 42 | 43 | char *fieldString = calloc(strlen(encoding), sizeof(char)); 44 | if (fieldString == NULL) { 45 | goto fieldStringLabel; 46 | } 47 | int fieldStringIndex = 0; 48 | 49 | char *typeString = calloc(strlen(encoding), sizeof(char)); 50 | if (typeString == NULL) { 51 | goto typeStringLabel; 52 | } 53 | int typeStringIndex = 0; 54 | 55 | int escapeLevel = 0; 56 | 57 | char c = -1; 58 | rLog(@"struct DECODING: START \e[31m%@\e[m", encodingString); 59 | for (int idx = 0; idx < encodingLength; idx++) { 60 | c = encoding[idx]; 61 | switch (mode) { 62 | case RTStructModeField: 63 | switch (c) { 64 | case '"': 65 | mode = RTStructModeType; 66 | memset(typeString, 0, typeStringIndex); 67 | typeStringIndex = 0; 68 | break; 69 | default: 70 | fieldString[fieldStringIndex++] = c; 71 | break; 72 | } 73 | break; 74 | 75 | case RTStructModeType: 76 | switch (c) { 77 | case '"': 78 | if (escapeLevel > 0) { 79 | typeString[typeStringIndex++] = c; 80 | } else { 81 | mode = RTStructModeField; 82 | typedString = rtTypeForEncoding_(@(typeString), @(fieldString), number); 83 | typedArray = typedArray ? [typedArray arrayByAddingObject:typedString] : @[typedString]; 84 | memset(fieldString, 0, fieldStringIndex); 85 | fieldStringIndex = 0; 86 | } 87 | break; 88 | case '[': 89 | case '{': 90 | case '(': 91 | escapeLevel++; 92 | typeString[typeStringIndex++] = c; 93 | break; 94 | case ']': 95 | case ')': 96 | case '}': 97 | escapeLevel--; 98 | typeString[typeStringIndex++] = c; 99 | break; 100 | default: 101 | typeString[typeStringIndex++] = c; 102 | break; 103 | } 104 | break; 105 | 106 | default: 107 | case RTStructModeNone: 108 | switch (c) { 109 | case '"': 110 | mode = RTStructModeField; 111 | break; 112 | 113 | case '[': 114 | case '{': 115 | case '(': 116 | if (escapeLevel == 0) { 117 | if (typeStringIndex > 0) { 118 | typedString = rtTypeForEncoding_(@(typeString), nil, number++); 119 | typedArray = typedArray ? [typedArray arrayByAddingObject:typedString] : @[typedString]; 120 | } 121 | memset(typeString, 0, typeStringIndex); 122 | typeStringIndex = 0; 123 | } 124 | escapeLevel++; 125 | typeString[typeStringIndex++] = c; 126 | break; 127 | 128 | case ']': 129 | case ')': 130 | case '}': 131 | escapeLevel--; 132 | typeString[typeStringIndex++] = c; 133 | if (escapeLevel == 0) { 134 | typedString = rtTypeForEncoding_(@(typeString), nil, number++); 135 | typedArray = typedArray ? [typedArray arrayByAddingObject:typedString] : @[typedString]; 136 | memset(typeString, 0, typeStringIndex); 137 | typeStringIndex = 0; 138 | } 139 | break; 140 | 141 | default: 142 | typeString[typeStringIndex++] = c; 143 | break; 144 | } 145 | break; 146 | } 147 | } 148 | 149 | // Add remaining type 150 | if (fieldStringIndex != 0 || typeStringIndex != 0) { 151 | typedString = rtTypeForEncoding_(@(typeString), @(fieldString), number); 152 | typedArray = typedArray ? [typedArray arrayByAddingObject:typedString] : @[typedString]; 153 | } 154 | rLog(@"struct DECODING: END"); 155 | free(typeString); 156 | goto typeStringLabel; typeStringLabel:; 157 | 158 | free(fieldString); 159 | goto fieldStringLabel; fieldStringLabel:; 160 | 161 | return typedArray; 162 | } 163 | 164 | NSString *rtTypeForEncoding_(NSString *encodingString, NSString *varName, NSUInteger number) { 165 | char *encoding = strdup(encodingString.UTF8String); 166 | int encodingLength = strlen(encoding); 167 | 168 | NSString *typedString = nil; 169 | 170 | RTVarType varType = RTVarTypeNone; 171 | 172 | NSUInteger arrayNumber = 0; 173 | BOOL arrayStringEnded = NO; 174 | 175 | NSUInteger bitfieldNumber = 0; 176 | BOOL bitfieldStringEnded = NO; 177 | 178 | char *structName = calloc(strlen(encoding), sizeof(char)); 179 | if (structName == NULL) { 180 | goto structNameLabel; 181 | } 182 | int structNameIndex = 0; 183 | BOOL structStringEnded = NO; 184 | 185 | char *unionName = calloc(strlen(encoding), sizeof(char)); 186 | if (unionName == NULL) { 187 | goto unionNameLabel; 188 | } 189 | int unionNameIndex = 0; 190 | BOOL unionStringEnded = NO; 191 | 192 | #define className unionName 193 | #define classNameIndex unionNameIndex 194 | 195 | #define protocolName structName 196 | #define protocolNameIndex structNameIndex 197 | 198 | BOOL isDecodingSubstring = NO; 199 | BOOL isDecodingProtocol = NO; 200 | 201 | NSDictionary *varTypes = @{ 202 | @"c":@"char", 203 | @"i":@"int", 204 | @"s":@"short", 205 | @"l":@"long", 206 | @"q":@"long long", 207 | @"C":@"unsigned char", 208 | @"I":@"unsigned int", 209 | @"S":@"unsigned short", 210 | @"L":@"unsigned long", 211 | @"Q":@"unsigned long long", 212 | @"f":@"float", 213 | @"d":@"double", 214 | @"B":@"bool", 215 | @"v":@"void", 216 | @"*":@"char *", 217 | @"@":@"id", 218 | @"#":@"Class", 219 | @":":@"SEL" 220 | }; 221 | 222 | #if 0 223 | NSDictionary *methodTypes = @{ 224 | @"r":@"const", 225 | @"n":@"in", 226 | @"N":@"inout", 227 | @"o":@"out", 228 | @"O":@"bycopy", 229 | @"R":@"byref", 230 | @"V":@"oneway" 231 | }; 232 | #endif 233 | NSString *internalType = nil; 234 | NSArray *internalStructTypeArray = nil; 235 | 236 | char c = -1; 237 | rLog(@"type ENCODING: START \e[31m%@\e[m \e[36m%@\e[m", encodingString, varName); 238 | for (int idx = 0; idx < encodingLength; idx++) { 239 | c = encoding[idx]; 240 | switch (varType) { 241 | case RTVarTypeNone: 242 | switch (c) { 243 | case '^': // Pointer 244 | varType = RTVarTypePointer; 245 | rLog(@"pointer to"); 246 | break; 247 | case '?': // Unknown 248 | varType = RTVarTypeUnknown; 249 | rLog(@"unknown"); 250 | break; 251 | case 'b': // Bitfield 252 | varType = RTVarTypeBitfield; 253 | rLog(@"bit field"); 254 | break; 255 | case '[': // Array 256 | varType = RTVarTypeArray; 257 | rLog(@"array start"); 258 | break; 259 | case '{': // Struct 260 | varType = RTVarTypeStruct; 261 | rLog(@"struct start"); 262 | break; 263 | case '(': // Union 264 | varType = RTVarTypeUnion; 265 | rLog(@"union start"); 266 | break; 267 | case 'T': // Property 268 | varType = RTVarTypeProperty; 269 | rLog(@"property"); 270 | break; 271 | default : // Primitive 272 | varType = RTVarTypePrimitive; 273 | rLog(@"%@", varTypes[@( ({char s[2] = {c, 0}; s;}) )]); 274 | break; 275 | } 276 | break; 277 | 278 | case RTVarTypeBitfield: 279 | if (!isdigit(c)) { 280 | if (!bitfieldStringEnded) { 281 | rLog(@"B:::%lu", (unsigned long)bitfieldNumber); 282 | } 283 | bitfieldStringEnded = YES; 284 | } else if (!bitfieldStringEnded) { 285 | bitfieldNumber = bitfieldNumber * 10 + c - '0'; 286 | } else { 287 | //rLog(@"X"); 288 | } 289 | break; 290 | 291 | case RTVarTypeArray: 292 | if (!isdigit(c)) { 293 | if (!arrayStringEnded) { 294 | rLog(@"A::[%lu]", (unsigned long)arrayNumber); 295 | } 296 | arrayStringEnded = YES; 297 | if (!isDecodingSubstring) { 298 | isDecodingSubstring = YES; 299 | internalType = rtTypeForEncoding_([encodingString substringWithRange:NSMakeRange(idx, encodingLength - idx - 1)], varName, number); 300 | rLog(@"A::\e[34m%@\e[m", [internalType stringByReplacingOccurrencesOfString:@"\e[m" withString:@"\e[m\e[34m"]); 301 | } 302 | } else if (!arrayStringEnded) { 303 | arrayNumber = arrayNumber * 10 + c - '0'; 304 | } else { 305 | //rLog(@"X"); 306 | } 307 | break; 308 | 309 | case RTVarTypeStruct: 310 | if (c == '=') { 311 | structStringEnded = YES; 312 | rLog(@"S::{%s}", structName); 313 | } else if (!structStringEnded) { 314 | structName = realloc(structName, (structNameIndex + 1) * sizeof(char)); 315 | structName[structNameIndex++] = c; 316 | } else { 317 | if (!isDecodingSubstring) { 318 | isDecodingSubstring = YES; 319 | internalStructTypeArray = rtTypeForStructEncoding_([encodingString substringWithRange:NSMakeRange(idx, encodingLength - idx - 1)], varName, number); 320 | rLog(@"S::\e[34m%@\e[m", [internalStructTypeArray.description stringByReplacingOccurrencesOfString:@"\e[m" withString:@"\e[m\e[34m"]); 321 | } 322 | } 323 | break; 324 | 325 | case RTVarTypeUnion: 326 | if (c == '=') { 327 | unionStringEnded = YES; 328 | rLog(@"U::(%s)", unionName); 329 | } else if (!unionStringEnded) { 330 | unionName = realloc(unionName, (unionNameIndex + 1) * sizeof(char)); 331 | unionName[unionNameIndex++] = c; 332 | } else { 333 | if (!isDecodingSubstring) { 334 | isDecodingSubstring = YES; 335 | internalStructTypeArray = rtTypeForStructEncoding_([encodingString substringWithRange:NSMakeRange(idx, encodingLength - idx - 1)], varName, number); 336 | rLog(@"U::\e[34m%@\e[m", [internalStructTypeArray.description stringByReplacingOccurrencesOfString:@"\e[m" withString:@"\e[m\e[34m"]); 337 | } 338 | } 339 | break; 340 | 341 | case RTVarTypePointer: 342 | internalType = rtTypeForEncoding_([encodingString substringWithRange:NSMakeRange(idx, encodingLength - idx)], varName, number); 343 | idx = encodingLength; 344 | rLog(@"P::\e[34m%@\e[m", [internalType stringByReplacingOccurrencesOfString:@"\e[m" withString:@"\e[m\e[34m"]); 345 | break; 346 | 347 | case RTVarTypeUnknown: 348 | rLog(@"u::\e[34m%@\e[m", @"¯\\_(ツ)_/¯"); 349 | break; 350 | case RTVarTypeProperty: 351 | internalType = rtTypeForEncoding_([encodingString substringWithRange:NSMakeRange(idx, encodingLength - idx)], varName, number); 352 | rLog(@"A::\e[34m%@\e[m", [internalType stringByReplacingOccurrencesOfString:@"\e[m" withString:@"\e[m\e[34m"]); 353 | idx = encodingLength; 354 | break; 355 | 356 | case RTVarTypePrimitive: 357 | if (c == '"') { 358 | if (!isDecodingSubstring) { 359 | isDecodingSubstring = YES; 360 | rLog(@"C started class type"); 361 | } else { 362 | isDecodingSubstring = NO; 363 | rLog(@"C (%s)", className); 364 | rLog(@"C stopped class type"); 365 | } 366 | } else if (c == '<' || c == '>') { 367 | if (!isDecodingProtocol) { 368 | isDecodingProtocol = YES; 369 | rLog(@"C started protocol type"); 370 | } else { 371 | isDecodingProtocol = NO; 372 | rLog(@"P (%s)", structName); 373 | rLog(@"C stopped protocol type"); 374 | } 375 | } else { 376 | if (isDecodingSubstring && !isDecodingProtocol) { 377 | className = realloc(className, (classNameIndex + 1) * sizeof(char)); 378 | className[classNameIndex++] = c; 379 | } else { 380 | protocolName = realloc(protocolName, (protocolNameIndex + 1) * sizeof(char)); 381 | protocolName[protocolNameIndex++] = c; 382 | } 383 | } 384 | break; 385 | 386 | default: 387 | rLog(@"X (%c)", c); 388 | } // type switch 389 | } // for loop 390 | rLog(@"type ENCODING: END"); 391 | 392 | // Decide output formatting 393 | switch (varType) { 394 | case RTVarTypePrimitive: 395 | internalType = (classNameIndex == 0) ? varTypes[encodingString] : (protocolNameIndex == 0) ? [NSString stringWithFormat:@"%s *", className] : [NSString stringWithFormat:@"%s<%s> *", className, protocolName]; 396 | if (varName == nil) { 397 | typedString = [NSString stringWithFormat:@"%@ $%lu", internalType, (unsigned long)number]; 398 | } else { 399 | typedString = [NSString stringWithFormat:@"%@ %@", internalType, varName]; 400 | } 401 | break; 402 | 403 | case RTVarTypeBitfield: 404 | if (varName == nil) { 405 | typedString = [NSString stringWithFormat:@"char $%lu:%lu", (unsigned long)number, (unsigned long)bitfieldNumber]; 406 | } else { 407 | typedString = [NSString stringWithFormat:@"char %@:%lu", varName, (unsigned long)bitfieldNumber]; 408 | } 409 | break; 410 | 411 | case RTVarTypeArray: 412 | rLog(@"A::\e[31m%@\e[m as internalType", internalType); 413 | if (varName == nil) { 414 | typedString = [NSString stringWithFormat:@"$%lu[%lu]", (unsigned long)number, (unsigned long)arrayNumber]; 415 | typedString = [internalType stringByReplacingOccurrencesOfString:[NSString stringWithFormat:@"$%lu", (unsigned long)number] withString:typedString]; 416 | } else { 417 | typedString = [NSString stringWithFormat:@"%@[%lu]", varName, (unsigned long)arrayNumber]; 418 | typedString = [internalType stringByReplacingOccurrencesOfString:varName withString:typedString]; 419 | rLog(@"%@", typedString); 420 | } 421 | break; 422 | 423 | case RTVarTypeStruct: 424 | if (varName == nil) { 425 | typedString = [NSString stringWithFormat:@"struct %s { %@; }", structName, [internalStructTypeArray componentsJoinedByString:@"; "]]; 426 | } else { 427 | typedString = [NSString stringWithFormat:@"struct %s { %@; } %@", structName, [internalStructTypeArray componentsJoinedByString:@"; "], varName]; 428 | } 429 | break; 430 | 431 | case RTVarTypeUnion: 432 | if (varName == nil) { 433 | typedString = [NSString stringWithFormat:@"union %s { %@; }", unionName, [internalStructTypeArray componentsJoinedByString:@"; "]]; 434 | } else { 435 | typedString = [NSString stringWithFormat:@"union %s { %@; } %@", unionName, [internalStructTypeArray componentsJoinedByString:@"; "], varName]; 436 | } 437 | break; 438 | 439 | case RTVarTypePointer: 440 | if (varName == nil) { 441 | typedString = [NSString stringWithFormat:@"%@ *", internalType]; 442 | } else { 443 | typedString = [NSString stringWithFormat:@"*%@", varName]; 444 | typedString = [internalType stringByReplacingOccurrencesOfString:varName withString:typedString]; 445 | rLog(@"%@", typedString); 446 | //typedString = [NSString stringWithFormat:@"%@ *%@", internalType, varName]; 447 | } 448 | break; 449 | 450 | case RTVarTypeUnknown: 451 | if (varName == nil) { 452 | typedString = [NSString stringWithFormat:@"/* fp */ void *(*)(void *)"]; 453 | } else { 454 | typedString = [NSString stringWithFormat:@"/* fp */ void *(%@)(void *)", varName]; 455 | } 456 | break; 457 | 458 | case RTVarTypeProperty: 459 | if (varName == nil) { 460 | typedString = [NSString stringWithString:internalType]; 461 | } else { 462 | typedString = [NSString stringWithFormat:@"%@ %@", internalType, varName]; 463 | } 464 | break; 465 | 466 | default: 467 | break; 468 | } 469 | 470 | free(encoding); 471 | goto encodingLabel; encodingLabel:; 472 | 473 | #undef className 474 | #undef classNameIndex 475 | 476 | #undef protocolName 477 | #undef protocolNameIndex 478 | 479 | free(unionName); 480 | goto unionNameLabel; unionNameLabel:; 481 | 482 | free(structName); 483 | goto structNameLabel; structNameLabel:; 484 | 485 | rLog(@"==> \e[32m%@\e[m", [typedString stringByReplacingOccurrencesOfString:@"\e[m" withString:@"\e[m\e[32m"]); 486 | return typedString; 487 | } 488 | -------------------------------------------------------------------------------- /sources/RTIvar.h: -------------------------------------------------------------------------------- 1 | #import "Runtime.h" 2 | 3 | @interface RTIvar : NSObject 4 | @property (nonatomic) Ivar internalIvar; 5 | @property (nonatomic, assign) id owner; 6 | + (instancetype)ivarWithIvar:(Ivar)ivar andOwner:(id)owner; 7 | - (instancetype)initWithIvar:(Ivar)ivar andOwner:(id)owner; 8 | 9 | - (NSString *)name; 10 | - (NSString *)type; 11 | - (NSString *)typeWithName:(NSString *)name; 12 | @end 13 | -------------------------------------------------------------------------------- /sources/RTIvar.m: -------------------------------------------------------------------------------- 1 | #import "RTIvar.h" 2 | #import "RTDecoding.h" 3 | 4 | @implementation RTIvar 5 | 6 | + (instancetype)ivarWithIvar:(Ivar)ivar { 7 | return [[[self alloc] initWithIvar:ivar] autorelease]; 8 | } 9 | + (instancetype)ivarWithIvar:(Ivar)ivar andOwner:(id)owner { 10 | return [[[self alloc] initWithIvar:ivar andOwner:owner] autorelease]; 11 | } 12 | - (instancetype)initWithIvar:(Ivar)ivar { 13 | return [self initWithIvar:ivar andOwner:nil]; 14 | } 15 | - (instancetype)initWithIvar:(Ivar)ivar andOwner:(id)owner { 16 | if ((self = [super init])) { 17 | _internalIvar = ivar; 18 | _owner = owner; 19 | } 20 | return self; 21 | } 22 | - (Ivar)internalIvar { 23 | return _internalIvar; 24 | } 25 | 26 | - (NSString *)name { 27 | return [NSString stringWithUTF8String:ivar_getName(_internalIvar)]; 28 | } 29 | - (NSString *)typeEncoding { 30 | return [NSString stringWithUTF8String:ivar_getTypeEncoding(_internalIvar)]; 31 | } 32 | - (ptrdiff_t)offset { 33 | return ivar_getOffset(_internalIvar); 34 | } 35 | 36 | - (NSString *)type { 37 | return rtTypeForEncoding(self.typeEncoding, nil); 38 | } 39 | - (NSString *)typeWithName:(NSString *)name { 40 | return rtTypeForEncoding(self.typeEncoding, name); 41 | } 42 | 43 | #pragma mark - Description 44 | 45 | - (NSString *)description { 46 | return [self typeWithName:self.name]; 47 | } 48 | 49 | #pragma mark - Comparison 50 | 51 | - (NSComparisonResult)caseInsensitiveCompare:(RTIvar *)rhs { 52 | return [self.name caseInsensitiveCompare:rhs.name]; 53 | } 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /sources/RTMethod.h: -------------------------------------------------------------------------------- 1 | #import "Runtime.h" 2 | 3 | @interface RTMethod : NSObject 4 | @property (nonatomic) Method internalMethod; 5 | @property (nonatomic, assign) id owner; 6 | + (instancetype)methodWithMethod:(Method)method andOwner:(id)owner; 7 | - (instancetype)initWithMethod:(Method)method andOwner:(id)owner; 8 | 9 | - (NSString *)name; 10 | @property (nonatomic, assign) BOOL isClassMethod; 11 | @end 12 | -------------------------------------------------------------------------------- /sources/RTMethod.m: -------------------------------------------------------------------------------- 1 | #import "RTMethod.h" 2 | #import "RTDecoding.h" 3 | 4 | @implementation RTMethod 5 | 6 | + (instancetype)methodWithMethod:(Method)method { 7 | return [[[self alloc] initWithMethod:method] autorelease]; 8 | } 9 | + (instancetype)methodWithMethod:(Method)method andOwner:(id)owner { 10 | return [[[self alloc] initWithMethod:method andOwner:owner] autorelease]; 11 | } 12 | - (instancetype)initWithMethod:(Method)method { 13 | return [self initWithMethod:method andOwner:nil]; 14 | } 15 | - (instancetype)initWithMethod:(Method)method andOwner:(id)owner { 16 | if ((self = [super init])) { 17 | _internalMethod = method; 18 | _owner = owner; 19 | } 20 | return self; 21 | } 22 | - (Method)internalMethod { 23 | return _internalMethod; 24 | } 25 | 26 | - (NSString *)name { 27 | return [NSString stringWithUTF8String:sel_getName(method_getName(_internalMethod))]; 28 | } 29 | - (NSString *)typeEncoding { 30 | return [NSString stringWithUTF8String:method_getTypeEncoding(_internalMethod)]; 31 | } 32 | - (NSString *)returnType { 33 | char *r = method_copyReturnType(_internalMethod); 34 | NSString *ret = [NSString stringWithUTF8String:r]; 35 | free(r); 36 | return ret; 37 | } 38 | - (NSString *)argumentTypeAtIndex:(NSUInteger)index { 39 | char *r = method_copyArgumentType(_internalMethod, index); 40 | NSString *ret = [NSString stringWithUTF8String:r]; 41 | free(r); 42 | return ret; 43 | } 44 | - (NSUInteger)numberOfArguments { 45 | return (NSUInteger)method_getNumberOfArguments(_internalMethod); 46 | } 47 | - (NSArray *)argumentTypes { 48 | NSMutableArray *array = [NSMutableArray arrayWithCapacity:self.numberOfArguments]; 49 | for (NSUInteger i = 0; i < self.numberOfArguments; i++) { 50 | [array addObject:[self argumentTypeAtIndex:i]]; 51 | } 52 | return array; 53 | } 54 | 55 | #if 0 56 | id method_invoke(id receiver, Method m, ...) 57 | void method_invoke_stret(id receiver, Method m, ...) 58 | Implementation method_getImplementation( Method method) 59 | MethodDescription *method_getDescription( Method m) 60 | Implementation method_setImplementation( Method method, Implementation imp) 61 | void method_exchangeImplementations( Method m1, Method m2) 62 | 63 | #pragma mark - Using Objective-C Language Features 64 | Implementation imp_implementationWithBlock(id block) 65 | id imp_getBlock( Implementation anImp) 66 | BOOL imp_removeBlock( Implementation anImp) 67 | #endif 68 | 69 | #pragma mark - Description 70 | 71 | - (NSString *)description { 72 | NSArray *types = self.argumentTypes; 73 | types = [types subarrayWithRange:NSMakeRange(2, types.count - 2)]; 74 | NSArray *nameSegments = [self.name componentsSeparatedByString:@":"]; 75 | 76 | NSMutableArray *fullNameArray = [NSMutableArray arrayWithCapacity:nameSegments.count]; 77 | if (types.count > 0) { 78 | [types enumerateObjectsUsingBlock:^(NSString *type, NSUInteger idx, BOOL *stop) { 79 | [fullNameArray addObject:[NSString stringWithFormat:@"%@:(%@)arg%d", nameSegments[idx], [rtTypeForEncoding(type, @"") stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]], (int)idx]]; 80 | }]; 81 | } else { 82 | [fullNameArray addObject:nameSegments[0]]; 83 | } 84 | 85 | return [NSString stringWithFormat:@"%c (%@)%@", self.isClassMethod ? '+':'-', [rtTypeForEncoding(self.returnType, @"") stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]], [fullNameArray componentsJoinedByString:@" "]]; 86 | } 87 | 88 | #pragma mark - Comparison 89 | 90 | - (NSComparisonResult)caseInsensitiveCompare:(RTMethod *)rhs { 91 | return [self.name caseInsensitiveCompare:rhs.name]; 92 | } 93 | 94 | @end 95 | -------------------------------------------------------------------------------- /sources/RTObject.h: -------------------------------------------------------------------------------- 1 | #import "Runtime.h" 2 | 3 | @interface RTObject : NSObject 4 | @property (nonatomic, assign) NSObject * internalObject; 5 | + (instancetype)objectWithObject:(NSObject *)object; 6 | - (instancetype)initWithObject:(NSObject *)object; 7 | @end 8 | -------------------------------------------------------------------------------- /sources/RTObject.m: -------------------------------------------------------------------------------- 1 | #import "RTObject.h" 2 | #import "RTClass.h" 3 | #import "RTIvar.h" 4 | #import "RT_Runtime.h" 5 | 6 | @implementation RTObject 7 | 8 | + (instancetype)objectWithObject:(NSObject *)object { 9 | return [[[self alloc] initWithObject:object] autorelease]; 10 | } 11 | - (instancetype)initWithObject:(NSObject *)object { 12 | if ((self = [super init])) { 13 | _internalObject = object; 14 | } 15 | return self; 16 | } 17 | - (NSObject *)internalObject { 18 | return _internalObject; 19 | } 20 | 21 | #pragma mark - Working with Instances 22 | - (id)copyWithSize:(size_t)size { 23 | return object_copy(_internalObject, size); 24 | } 25 | - (id)dispose { 26 | return object_dispose(_internalObject); 27 | } 28 | - (RTIvar *)setInstanceVariableWithName:(NSString *)name value:(void *)value { 29 | return [RTIvar ivarWithIvar:object_setInstanceVariable(_internalObject, name.UTF8String, value) andOwner:self]; 30 | } 31 | - (RTIvar *)getInstanceVariableWithName:(NSString *)name value:(void **)outValue { 32 | return [RTIvar ivarWithIvar:object_getInstanceVariable(_internalObject, name.UTF8String, outValue) andOwner:self]; 33 | } 34 | - (void *)getIndexedIvars { 35 | return object_getIndexedIvars(_internalObject); 36 | } 37 | - (id)getIvar:(RTIvar *)ivar { 38 | return object_getIvar(_internalObject, ivar.internalIvar); 39 | } 40 | - (void)setIvar:(RTIvar *)ivar value:(id)value { 41 | return object_setIvar(_internalObject, ivar.internalIvar, value); 42 | } 43 | - (NSString *)getClassName { 44 | return [NSString stringWithUTF8String:object_getClassName(_internalObject)]; 45 | } 46 | - (RTClass *)getClass { 47 | return [RTClass classWithClass:object_getClass(_internalObject)]; 48 | } 49 | - (RTClass *)setClass:(RTClass *)cls { 50 | return [RTClass classWithClass:object_setClass(_internalObject, cls.internalClass)]; 51 | } 52 | 53 | #pragma mark - Associative References 54 | - (void)setAssociatedObjectForKey:(const void *)key value:(id)value policy:(NSString *)policyName { 55 | objc_setAssociatedObject(_internalObject, key, value, [RTRuntime associationPolicyWithName:policyName]); 56 | } 57 | - (id)getAssociatedObjectForKey:(const void *)key { 58 | return objc_getAssociatedObject(_internalObject, key); 59 | } 60 | - (void)removeAssociatedObjects { 61 | objc_removeAssociatedObjects(_internalObject); 62 | } 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /sources/RTProperty.h: -------------------------------------------------------------------------------- 1 | #import "Runtime.h" 2 | 3 | @interface RTProperty : NSObject 4 | @property (nonatomic) Property internalProperty; 5 | @property (nonatomic, assign) id owner; 6 | + (instancetype)propertyWithProperty:(Property)property andOwner:(id)owner; 7 | - (instancetype)initWithProperty:(Property)property andOwner:(id)owner; 8 | 9 | - (NSString *)backingIvar; 10 | - (NSString *)getter; 11 | - (NSString *)setter; 12 | @end 13 | -------------------------------------------------------------------------------- /sources/RTProperty.m: -------------------------------------------------------------------------------- 1 | #import "RTProperty.h" 2 | #import "RTPropertyAttribute.h" 3 | #import "RTClass.h" 4 | #import "RTIvar.h" 5 | 6 | @implementation RTProperty 7 | 8 | + (instancetype)propertyWithProperty:(Property)property { 9 | return [[[self alloc] initWithProperty:property] autorelease]; 10 | } 11 | + (instancetype)propertyWithProperty:(Property)property andOwner:(id)owner { 12 | return [[[self alloc] initWithProperty:property andOwner:owner] autorelease]; 13 | } 14 | - (instancetype)initWithProperty:(Property)property { 15 | return [self initWithProperty:property andOwner:nil]; 16 | } 17 | - (instancetype)initWithProperty:(Property)property andOwner:(id)owner { 18 | if ((self = [super init])) { 19 | _internalProperty = property; 20 | _owner = owner; 21 | } 22 | return self; 23 | } 24 | - (Property)internalProperty { 25 | return _internalProperty; 26 | } 27 | 28 | - (NSString *)name { 29 | return [NSString stringWithUTF8String:property_getName(_internalProperty)]; 30 | } 31 | - (NSDictionary *)attributes { 32 | NSString *attributesString = [NSString stringWithUTF8String:property_getAttributes(_internalProperty)]; 33 | NSArray *attributesArray = [attributesString componentsSeparatedByString:@","]; 34 | __block NSMutableDictionary *dict_m = [NSMutableDictionary dictionary]; 35 | [attributesArray enumerateObjectsUsingBlock:^(NSString *obj, NSUInteger idx, BOOL *stop) { 36 | dict_m[obj[0]] = [obj substringWithRange:NSMakeRange(1, obj.length - 1)]; 37 | }]; 38 | return dict_m; 39 | } 40 | - (NSString *)copyAttributeValueWithName:(NSString *)attributeName { 41 | char *value = property_copyAttributeValue(_internalProperty, attributeName.UTF8String); 42 | NSString *string = [NSString stringWithUTF8String:value]; 43 | free(value); 44 | return string; 45 | } 46 | - (NSArray *)copyAttributeList { 47 | return nil; 48 | unsigned int outCount = 0; 49 | PropertyAttribute attributes = property_copyAttributeList(_internalProperty, &outCount); 50 | //rLog(@"attributes in property %p: %d", _internalProperty, outCount); 51 | CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, (CFIndex)outCount, NULL); 52 | for (unsigned int i = 0; i < outCount; i++) { 53 | CFArrayAppendValue(array, [RTPropertyAttribute propertyAttributeWithPropertyAttribute:&attributes[i]]); 54 | } 55 | free(attributes); 56 | NSArray *r = [(NSMutableArray *)array copy]; 57 | CFRelease(array); 58 | return r; 59 | } 60 | 61 | - (NSString *)type { 62 | return self.attributes[@"T"]; 63 | } 64 | - (NSString *)backingIvar { 65 | return self.attributes[@"V"]; 66 | } 67 | - (BOOL)hasCustomGetter { 68 | return self.attributes[@"G"] != nil; 69 | } 70 | - (NSString *)getter { 71 | return self.hasCustomGetter ? self.attributes[@"G"] : self.name; 72 | } 73 | - (BOOL)hasCustomSetter { 74 | return self.attributes[@"S"] != nil; 75 | } 76 | - (NSString *)setter { 77 | return self.hasCustomSetter ? self.attributes[@"S"] : [NSString stringWithFormat:@"set%@:", [self.name capitalizedString]]; 78 | } 79 | 80 | - (BOOL)isReadOnly { 81 | return self.attributes[@"R"] != nil; 82 | } 83 | - (BOOL)isCopy { 84 | return self.attributes[@"C"] != nil; 85 | } 86 | - (BOOL)isRetain { 87 | return self.attributes[@"&"] != nil; 88 | } 89 | - (BOOL)isNonatomic { 90 | return self.attributes[@"N"] != nil; 91 | } 92 | - (BOOL)isDynamic { 93 | return self.attributes[@"D"] != nil; 94 | } 95 | - (BOOL)isWeak { 96 | return self.attributes[@"W"] != nil; 97 | } 98 | - (BOOL)isGarbageCollected { 99 | return self.attributes[@"P"] != nil; 100 | } 101 | 102 | #pragma mark - Description 103 | 104 | - (NSString *)description { 105 | NSMutableArray *attributesArray = [NSMutableArray array]; 106 | if (self.isNonatomic) { [attributesArray addObject:@"nonatomic"]; } 107 | if (self.isReadOnly) { [attributesArray addObject:@"readonly"]; } 108 | if (self.isCopy) { [attributesArray addObject:@"copy"]; } 109 | if (self.isRetain) { [attributesArray addObject:@"retain"]; } 110 | if (self.isDynamic) { [attributesArray addObject:@"dynamic"]; } 111 | if (self.isWeak) { [attributesArray addObject:@"weak"]; } 112 | if (self.isGarbageCollected) { [attributesArray addObject:@"garbageCollected"]; } 113 | if (!(self.isReadOnly || self.isCopy || self.isRetain || self.isWeak)) { [attributesArray addObject:@"assign"]; } 114 | if (self.hasCustomGetter) { [attributesArray addObject:[NSString stringWithFormat:@"getter=%@", self.getter]]; } 115 | if (self.hasCustomSetter) { [attributesArray addObject:[NSString stringWithFormat:@"setter=%@", self.setter]]; } 116 | 117 | //NSString *typeString = [RTRuntime typeForEncoding:self.type varName:self.name]; 118 | RTIvar *ivar = [(RTClass *)_owner getInstanceVariableWithName:self.backingIvar]; 119 | NSString *typeString = [ivar typeWithName:self.name]; 120 | NSString *attributesString = nil; 121 | if (attributesArray.count > 0) { 122 | attributesString = [NSString stringWithFormat:@"@property (%@) %@", [attributesArray componentsJoinedByString:@", "], typeString]; 123 | } else { 124 | attributesString = [NSString stringWithFormat:@"@property %@", typeString]; 125 | } 126 | return attributesString; 127 | } 128 | 129 | #pragma mark - Comparison 130 | 131 | - (NSComparisonResult)caseInsensitiveCompare:(RTProperty *)rhs { 132 | return [self.name caseInsensitiveCompare:rhs.name]; 133 | } 134 | 135 | @end 136 | -------------------------------------------------------------------------------- /sources/RTPropertyAttribute.h: -------------------------------------------------------------------------------- 1 | #import "Runtime.h" 2 | 3 | @interface RTPropertyAttribute : NSObject 4 | @property (nonatomic) PropertyAttribute internalPropertyAttribute; 5 | + (instancetype)propertyAttributeWithPropertyAttribute:(PropertyAttribute)propertyAttribute; 6 | - (instancetype)initWithPropertyAttribute:(PropertyAttribute)propertyAttribute; 7 | @end 8 | -------------------------------------------------------------------------------- /sources/RTPropertyAttribute.m: -------------------------------------------------------------------------------- 1 | #import "RTPropertyAttribute.h" 2 | 3 | @implementation RTPropertyAttribute : NSObject 4 | 5 | + (instancetype)propertyAttributeWithPropertyAttribute:(PropertyAttribute)propertyAttribute { 6 | return [[[self alloc] initWithPropertyAttribute:propertyAttribute] autorelease]; 7 | } 8 | - (instancetype)initWithPropertyAttribute:(PropertyAttribute)propertyAttribute { 9 | if ((self = [super init])) { 10 | _internalPropertyAttribute = propertyAttribute; 11 | } 12 | return self; 13 | } 14 | - (PropertyAttribute)internalPropertyAttribute { 15 | return _internalPropertyAttribute; 16 | } 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /sources/RTProtocol.h: -------------------------------------------------------------------------------- 1 | #import "Runtime.h" 2 | 3 | @interface RTProtocol : NSObject 4 | @property (nonatomic, assign) Protocol * internalProtocol; 5 | + (instancetype)protocolWithProtocol:(Protocol *)protocol; 6 | - (instancetype)initWithProtocol:(Protocol *)protocol; 7 | @end 8 | -------------------------------------------------------------------------------- /sources/RTProtocol.m: -------------------------------------------------------------------------------- 1 | #import "RTProtocol.h" 2 | #import "RTProperty.h" 3 | #import "RTSelector.h" 4 | 5 | @implementation RTProtocol 6 | 7 | + (instancetype)protocolWithProtocol:(Protocol *)protocol { 8 | return [[[self alloc] initWithProtocol:protocol] autorelease]; 9 | } 10 | - (instancetype)initWithProtocol:(Protocol *)protocol { 11 | if ((self = [super init])) { 12 | _internalProtocol = protocol; 13 | } 14 | return self; 15 | } 16 | - (Protocol *)internalProtocol { 17 | return _internalProtocol; 18 | } 19 | 20 | - (NSString *)name { 21 | return [NSString stringWithUTF8String:protocol_getName(_internalProtocol)]; 22 | } 23 | - (BOOL)isEqualToProtocol:(Protocol *)rhs { 24 | return protocol_isEqual(_internalProtocol, rhs); 25 | } 26 | 27 | - (Protocol *)objc_getProtocol:(NSString *)name { 28 | return objc_getProtocol(name.UTF8String); 29 | } 30 | - (Protocol *)objc_allocateProtocol:(NSString *)name { 31 | return objc_allocateProtocol(name.UTF8String); 32 | } 33 | - (void)objc_registerProtocol:(Protocol *)protocol { 34 | objc_registerProtocol(protocol); 35 | } 36 | 37 | - (void)addMethodDescriptionForSelector:(Selector)selector types:(NSString *)types 38 | isRequiredMethod:(BOOL)isRequiredMethod isInstanceMethod:(BOOL)isInstanceMethod { 39 | protocol_addMethodDescription(_internalProtocol, selector, types.UTF8String, isRequiredMethod, isInstanceMethod); 40 | } 41 | - (void)addProtocolAddition:(Protocol *)addition { 42 | protocol_addProtocol(_internalProtocol, addition); 43 | } 44 | - (void)addPropertyWithName:(NSString *)name 45 | attributes:(const objc_property_attribute_t *)attributes attributeCount:(unsigned int)attributeCount 46 | isRequiredProperty:(BOOL)isRequiredProperty isInstanceProperty:(BOOL)isInstanceProperty { 47 | protocol_addProperty(_internalProtocol, name.UTF8String, attributes, attributeCount, isRequiredProperty, isInstanceProperty); 48 | } 49 | #pragma mark - Convert to array of custom class 50 | - (NSArray *)copyMethodDescriptionList:(BOOL)b 51 | isRequiredMethod:(BOOL)isRequiredMethod isInstanceMethod:(BOOL)isInstanceMethod { 52 | return nil; 53 | unsigned int outCount = 0; 54 | MethodDescription *descriptions = protocol_copyMethodDescriptionList(_internalProtocol, isRequiredMethod, isInstanceMethod, &outCount); 55 | /**/rLog(@"method descriptions in protocol %@: %d", NSStringFromProtocol(_internalProtocol), outCount); 56 | CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, (CFIndex)outCount, NULL); 57 | for (unsigned int i = 0; i < outCount; i++) { 58 | CFArrayAppendValue(array, &descriptions[i]); 59 | } 60 | free(descriptions); 61 | NSArray *r = [(NSMutableArray *)array copy]; 62 | CFRelease(array); 63 | return r; 64 | } 65 | - (MethodDescription)getMethodDescriptionForSelector:(RTSelector *)selector 66 | isRequiredMethod:(BOOL)isRequiredMethod isInstanceMethod:(BOOL)isInstanceMethod { 67 | return protocol_getMethodDescription(_internalProtocol, selector.internalSelector, isRequiredMethod, isInstanceMethod); 68 | } 69 | - (NSArray *)properties { 70 | unsigned int outCount = 0; 71 | Property *properties = protocol_copyPropertyList(_internalProtocol, &outCount); 72 | /**/rLog(@"properties in protocol %@: %d", NSStringFromProtocol(_internalProtocol), outCount); 73 | CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, (CFIndex)outCount, NULL); 74 | for (unsigned int i = 0; i < outCount; i++) { 75 | CFArrayAppendValue(array, [RTProperty propertyWithProperty:properties[i] andOwner:self]); 76 | } 77 | free(properties); 78 | NSArray *r = [(NSMutableArray *)array copy]; 79 | CFRelease(array); 80 | return r; 81 | } 82 | - (RTProperty *)getPropertyWithName:(NSString *)name 83 | isRequiredProperty:(BOOL)isRequiredProperty isInstanceProperty:(BOOL)isInstanceProperty { 84 | return [RTProperty propertyWithProperty:protocol_getProperty(_internalProtocol, name.UTF8String, isRequiredProperty, isInstanceProperty) andOwner:self]; 85 | } 86 | - (NSArray *)copyProtocolList { 87 | unsigned int *outCount = 0; 88 | Protocol **protocols = protocol_copyProtocolList(_internalProtocol, outCount); 89 | /**/rLog(@"protocols in protocol %@: %d", NSStringFromProtocol(_internalProtocol), *outCount); 90 | CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, (CFIndex)*outCount, NULL); 91 | for (unsigned int i = 0; i < *outCount; i++) { 92 | CFArrayAppendValue(array, [RTProtocol protocolWithProtocol:protocols[i]]); 93 | } 94 | free(protocols); 95 | NSArray *r = [(NSMutableArray *)array copy]; 96 | CFRelease(array); 97 | return r; 98 | } 99 | - (BOOL)conformsToProtocol:(Protocol *)rhs { 100 | return protocol_conformsToProtocol(_internalProtocol, rhs); 101 | } 102 | 103 | #pragma mark - Description 104 | 105 | - (NSString *)description { 106 | return self.name; 107 | } 108 | 109 | #pragma mark - Comparison 110 | 111 | - (NSComparisonResult)caseInsensitiveCompare:(RTProtocol *)rhs { 112 | return [self.name caseInsensitiveCompare:rhs.name]; 113 | } 114 | 115 | @end 116 | -------------------------------------------------------------------------------- /sources/RTSelector.h: -------------------------------------------------------------------------------- 1 | #import "Runtime.h" 2 | 3 | @interface RTSelector : NSObject 4 | @property (nonatomic) Selector internalSelector; 5 | @property (nonatomic, assign) id owner; 6 | + (instancetype)selectorWithSelector:(Selector)selector andOwner:(id)owner; 7 | - (instancetype)initWithSelector:(Selector)selector andOwner:(id)owner; 8 | @end 9 | -------------------------------------------------------------------------------- /sources/RTSelector.m: -------------------------------------------------------------------------------- 1 | #import "RTSelector.h" 2 | 3 | @implementation RTSelector 4 | 5 | + (instancetype)selectorWithSelector:(Selector)selector { 6 | return [[[self alloc] initWithSelector:selector] autorelease]; 7 | } 8 | + (instancetype)selectorWithSelector:(Selector)selector andOwner:(id)owner { 9 | return [[[self alloc] initWithSelector:selector andOwner:owner] autorelease]; 10 | } 11 | - (instancetype)initWithSelector:(Selector)selector { 12 | return [self initWithSelector:selector andOwner:nil]; 13 | } 14 | - (instancetype)initWithSelector:(Selector)selector andOwner:(id)owner { 15 | if ((self = [super init])) { 16 | _internalSelector = selector; 17 | _owner = owner; 18 | } 19 | return self; 20 | } 21 | - (Selector)internalSelector { 22 | return _internalSelector; 23 | } 24 | 25 | - (NSString *)name { 26 | return [NSString stringWithUTF8String:sel_getName(_internalSelector)]; 27 | } 28 | - (Selector)registerName:(NSString *)name { 29 | return sel_registerName(name.UTF8String); 30 | } 31 | - (BOOL)isEqualToSelector:(Selector)selector { 32 | return sel_isEqual(_internalSelector, selector); 33 | } 34 | 35 | #pragma mark - Description 36 | 37 | - (NSString *)description { 38 | return self.name; 39 | } 40 | 41 | #pragma mark - Comparison 42 | 43 | - (NSComparisonResult)caseInsensitiveCompare:(RTSelector *)rhs { 44 | return [self.name caseInsensitiveCompare:rhs.name]; 45 | } 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /sources/RT_Runtime.h: -------------------------------------------------------------------------------- 1 | #import "RTDecoding.h" 2 | #import "RTClass.h" 3 | #import "RTIvar.h" 4 | #import "RTProperty.h" 5 | #import "RTPropertyAttribute.h" 6 | #import "RTMethod.h" 7 | #import "RTSelector.h" 8 | #import "RTProtocol.h" 9 | #import "RTObject.h" 10 | 11 | @interface RTRuntime : NSObject 12 | + (NSArray *)classes; 13 | + (NSArray *)classesConformingToProtocol:(Protocol *)protocol; 14 | + (RTClass *)lookUpClass:(NSString *)name; 15 | + (RTClass *)classNamed:(NSString *)name; 16 | + (RTClass *)metaClassNamed:(NSString *)name; 17 | + (RTClass *)ZeroLink; 18 | + (NSArray *)protocols; 19 | + (NSArray *)imageNames; 20 | + (NSArray *)classNamesForImage:(NSString *)image; 21 | + (id)loadWeak:(id *)address; 22 | + (id)storeWeak:(id *)address object:(id)object; 23 | + (NSString *)typeForEncoding:(NSString *)enc varName:(NSString *)varName; 24 | + (AssociationPolicy)associationPolicyWithName:(NSString *)policyName; 25 | @end 26 | -------------------------------------------------------------------------------- /sources/Runtime.h: -------------------------------------------------------------------------------- 1 | //https://developer.apple.com/reference/objectivec/objective_c_runtime 2 | #include 3 | 4 | #define LOG_STUFF 01 5 | #if LOG_STUFF 6 | #define rLog(format, ...) jWo75h4R78(format, ##__VA_ARGS__) 7 | static inline void jWo75h4R78(NSString *format, ...) { 8 | @autoreleasepool { 9 | va_list args; 10 | va_start(args, format); 11 | NSString * message = [[[NSString alloc] initWithFormat:format arguments:args] autorelease]; 12 | va_end(args); 13 | 14 | #if 01 15 | printf("%s\n", message.UTF8String); 16 | #else 17 | HBLogInfo(@"%@", message); 18 | #endif 19 | } 20 | } 21 | #else 22 | #define rLog(format, ...) 23 | #endif 24 | 25 | typedef struct objc_property * Property; 26 | typedef objc_property_attribute_t * PropertyAttribute; 27 | typedef struct objc_method_description MethodDescription; 28 | typedef SEL Selector; 29 | typedef IMP Implementation; 30 | typedef objc_AssociationPolicy AssociationPolicy; 31 | 32 | @interface NSString (indexCategory) 33 | - (NSString *)objectAtIndexedSubscript:(NSInteger)idx; 34 | @end 35 | -------------------------------------------------------------------------------- /sources/Runtime.m: -------------------------------------------------------------------------------- 1 | #import "RT_Runtime.h" 2 | 3 | @implementation NSString (indexCategory) 4 | - (NSString *)objectAtIndexedSubscript:(NSInteger)idx { 5 | return [self substringWithRange:NSMakeRange(idx, 1)]; 6 | } 7 | @end 8 | 9 | @implementation RTRuntime 10 | 11 | + (void)load { 12 | rLog(@"+load"); 13 | } 14 | + (void)initialize { 15 | rLog(@"+initialize"); 16 | } 17 | 18 | #pragma mark - Obtaining Class Definitions 19 | 20 | + (NSArray *)classes { 21 | unsigned int outCount; 22 | Class *classes = objc_copyClassList(&outCount); 23 | //rLog(@"registered classes: %d at %p", outCount, classes); 24 | NSMutableArray *array = [NSMutableArray arrayWithCapacity:outCount]; 25 | for (unsigned int i = 0; classes[i]; i++) { 26 | [array addObject:[RTClass classWithClass:classes[i]]]; 27 | } 28 | free(classes); 29 | [array sortUsingSelector:@selector(caseInsensitiveCompare:)]; 30 | return [array copy]; 31 | } 32 | + (NSArray *)classesConformingToProtocol:(Protocol *)protocol { 33 | NSArray *array = [[self classes] filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id obj, NSDictionary *bind){ 34 | return [obj conformsToProtocol:protocol]; 35 | }]]; 36 | //sort protocol array 37 | return array; 38 | } 39 | + (RTClass *)lookUpClass:(NSString *)name { 40 | return [RTClass classWithClass:objc_lookUpClass(name.UTF8String)]; 41 | } 42 | + (RTClass *)classNamed:(NSString *)name { 43 | return [RTClass classWithClass:objc_getClass(name.UTF8String)]; 44 | } 45 | + (RTClass *)metaClassNamed:(NSString *)name { 46 | return [RTClass classWithClass:objc_getMetaClass(name.UTF8String)]; 47 | } 48 | + (RTClass *)ZeroLink { 49 | //crash process 50 | return [RTClass classWithClass:objc_getRequiredClass("ZeroLink")]; 51 | } 52 | 53 | #pragma mark - Obtaining Protocol Definitions 54 | 55 | + (NSArray *)protocols { 56 | unsigned int outCount = 0; 57 | Protocol **protocols = objc_copyProtocolList(&outCount); 58 | /**/rLog(@"registered protocols: %d", outCount); 59 | CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, (CFIndex)outCount, NULL); 60 | for (unsigned int i = 0; i < outCount; i++) { 61 | CFArrayAppendValue(array, [RTProtocol protocolWithProtocol:protocols[i]]); 62 | } 63 | free(protocols); 64 | NSArray *r = [(NSMutableArray *)array copy]; 65 | CFRelease(array); 66 | return r; 67 | } 68 | 69 | #pragma mark - Working with Libraries 70 | 71 | + (NSArray *)imageNames { 72 | unsigned int outCount; 73 | const char **imageNames = objc_copyImageNames(&outCount); 74 | //rLog(@"registered classes: %d at %p", outCount, classes); 75 | NSMutableArray *array = [NSMutableArray arrayWithCapacity:outCount]; 76 | for (unsigned int i = 0; imageNames[i]; i++) { 77 | [array addObject:[NSString stringWithUTF8String:imageNames[i]]]; 78 | } 79 | free(imageNames); 80 | [array sortUsingSelector:@selector(caseInsensitiveCompare:)]; 81 | return [array copy]; 82 | } 83 | 84 | + (NSArray *)classNamesForImage:(NSString *)image { 85 | unsigned int outCount; 86 | const char **classNames = objc_copyClassNamesForImage(image.UTF8String, &outCount); 87 | //rLog(@"registered classes: %d at %p", outCount, classes); 88 | NSMutableArray *array = [NSMutableArray arrayWithCapacity:outCount]; 89 | for (unsigned int i = 0; classNames[i]; i++) { 90 | [array addObject:[NSString stringWithUTF8String:classNames[i]]]; 91 | } 92 | free(classNames); 93 | [array sortUsingSelector:@selector(caseInsensitiveCompare:)]; 94 | return [array copy]; 95 | } 96 | 97 | // Sending Messages 98 | 99 | #pragma mark - Using Objective-C Language Features 100 | 101 | + (id)loadWeak:(id *)address { 102 | return objc_loadWeak(address); 103 | } 104 | 105 | + (id)storeWeak:(id *)address object:(id)object { 106 | return objc_storeWeak(address, object); 107 | } 108 | 109 | #pragma mark - Utilities 110 | 111 | + (NSString *)typeForEncoding:(NSString *)encodingString varName:(NSString *)varName { 112 | return rtTypeForEncoding(encodingString, varName); 113 | } 114 | 115 | + (AssociationPolicy)associationPolicyWithName:(NSString *)policyName { 116 | NSDictionary *policies = @{ 117 | @"OBJC_ASSOCIATION_ASSIGN" : @(OBJC_ASSOCIATION_ASSIGN) 118 | , @"OBJC_ASSOCIATION_RETAIN_NONATOMIC" : @(OBJC_ASSOCIATION_RETAIN_NONATOMIC) 119 | , @"OBJC_ASSOCIATION_COPY_NONATOMIC" : @(OBJC_ASSOCIATION_COPY_NONATOMIC) 120 | , @"OBJC_ASSOCIATION_RETAIN" : @(OBJC_ASSOCIATION_RETAIN) 121 | , @"OBJC_ASSOCIATION_COPY" : @(OBJC_ASSOCIATION_COPY) 122 | }; 123 | return policies[policyName] ? ((NSNumber *)policies[policyName]).integerValue : OBJC_ASSOCIATION_ASSIGN; 124 | } 125 | 126 | @end 127 | -------------------------------------------------------------------------------- /travis/before: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | main() { 4 | #argc=$# 5 | #argv=($@) 6 | #for (( i = 0; i < $argc; i++ )); do 7 | #echo "argv[$i] = ${argv[$i]}" 8 | #done 9 | 10 | case "$TRAVIS_OS_NAME" in 11 | 12 | osx) 13 | brew update; 14 | brew install dpkg ldid; 15 | ;; 16 | 17 | linux) 18 | curl -L https://sdks.website/dl/iPhoneOS8.1.sdk.tbz2 | tar -jxf - -C $THEOS/sdks/ 19 | wget https://sdks.website/dl/toolchain.zip -O temp.zip; unzip temp.zip -d $THEOS/toolchain/; rm temp.zip 20 | ;; 21 | 22 | esac 23 | } 24 | 25 | main $0 $@ 26 | -------------------------------------------------------------------------------- /travis/script: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | main() { 4 | #argc=$# 5 | #argv=($@) 6 | #for (( i = 0; i < $argc; i++ )); do 7 | #echo "argv[$i] = ${argv[$i]}" 8 | #done 9 | 10 | echo "Doing 'make'" 11 | make 12 | } 13 | 14 | main $0 $@ 15 | --------------------------------------------------------------------------------