[
  {
    "path": ".gitignore",
    "content": "# Xcode\n#\nbuild/\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mode2v3\n!default.mode2v3\n*.perspectivev3\n!default.perspectivev3\nxcuserdata\n*.xccheckout\n*.moved-aside\nDerivedData\n*.hmap\n*.ipa\n*.xcuserstate\n\n# CocoaPods\n#\n# We recommend against adding the Pods directory to your .gitignore. However\n# you should judge for yourself, the pros and cons are mentioned at:\n# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control\n#\n# Pods/\n"
  },
  {
    "path": "Aspects-LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2014 Peter Steinberger, steipete@gmail.com\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE."
  },
  {
    "path": "Aspects.h",
    "content": "//\n//  Aspects.h\n//  Aspects - A delightful, simple library for aspect oriented programming.\n//\n//  Copyright (c) 2014 Peter Steinberger. Licensed under the MIT license.\n//\n\n#import <Foundation/Foundation.h>\n\ntypedef NS_OPTIONS(NSUInteger, AspectOptions) {\n    AspectPositionAfter   = 0,            /// Called after the original implementation (default)\n    AspectPositionInstead = 1,            /// Will replace the original implementation.\n    AspectPositionBefore  = 2,            /// Called before the original implementation.\n\n    AspectOptionAutomaticRemoval = 1 << 3 /// Will remove the hook after the first execution.\n};\n\n/// Opaque Aspect Token that allows to deregister the hook.\n@protocol AspectToken <NSObject>\n\n/// Deregisters an aspect.\n/// @return YES if deregistration is successful, otherwise NO.\n- (BOOL)remove;\n\n@end\n\n/// The AspectInfo protocol is the first parameter of our block syntax.\n@protocol AspectInfo <NSObject>\n\n/// The instance that is currently hooked.\n- (id)instance;\n\n/// The original invocation of the hooked method.\n- (NSInvocation *)originalInvocation;\n\n/// All method arguments, boxed. This is lazily evaluated.\n- (NSArray *)arguments;\n\n@end\n\n/**\n Aspects uses Objective-C message forwarding to hook into messages. This will create some overhead. Don't add aspects to methods that are called a lot. Aspects is meant for view/controller code that is not called a 1000 times per second.\n\n Adding aspects returns an opaque token which can be used to deregister again. All calls are thread safe.\n */\n@interface NSObject (Aspects)\n\n/// Adds a block of code before/instead/after the current `selector` for a specific class.\n///\n/// @param block Aspects replicates the type signature of the method being hooked.\n/// The first parameter will be `id<AspectInfo>`, followed by all parameters of the method.\n/// These parameters are optional and will be filled to match the block signature.\n/// You can even use an empty block, or one that simple gets `id<AspectInfo>`.\n///\n/// @note Hooking static methods is not supported.\n/// @return A token which allows to later deregister the aspect.\n+ (id<AspectToken>)aspect_hookSelector:(SEL)selector\n                      withOptions:(AspectOptions)options\n                       usingBlock:(id)block\n                            error:(NSError *__autoreleasing*)error;\n\n/// Adds a block of code before/instead/after the current `selector` for a specific instance.\n- (id<AspectToken>)aspect_hookSelector:(SEL)selector\n                      withOptions:(AspectOptions)options\n                       usingBlock:(id)block\n                            error:(NSError *__autoreleasing*)error;\n\n@end\n\n\ntypedef NS_ENUM(NSUInteger, AspectErrorCode) {\n    AspectErrorSelectorBlacklisted,                   /// Selectors like release, retain, autorelease are blacklisted.\n    AspectErrorDoesNotRespondToSelector,              /// Selector could not be found.\n    AspectErrorSelectorDeallocPosition,               /// When hooking dealloc, only AspectPositionBefore is allowed.\n    AspectErrorSelectorAlreadyHookedInClassHierarchy, /// Statically hooking the same method in subclasses is not allowed.\n    AspectErrorFailedToAllocateClassPair,             /// The runtime failed creating a class pair.\n    AspectErrorMissingBlockSignature,                 /// The block misses compile time signature info and can't be called.\n    AspectErrorIncompatibleBlockSignature,            /// The block signature does not match the method or is too large.\n\n    AspectErrorRemoveObjectAlreadyDeallocated = 100   /// (for removing) The object hooked is already deallocated.\n};\n\nextern NSString *const AspectErrorDomain;\n"
  },
  {
    "path": "Aspects.m",
    "content": "//\n//  Aspects.m\n//  Aspects - A delightful, simple library for aspect oriented programming.\n//\n//  Copyright (c) 2014 Peter Steinberger. Licensed under the MIT license.\n//\n\n#import \"Aspects.h\"\n#import <libkern/OSAtomic.h>\n#import <objc/runtime.h>\n#import <objc/message.h>\n\n#define AspectLog(...)\n//#define AspectLog(...) do { NSLog(__VA_ARGS__); }while(0)\n#define AspectLogError(...) do { NSLog(__VA_ARGS__); }while(0)\n\n// Block internals.\ntypedef NS_OPTIONS(int, AspectBlockFlags) {\n\tAspectBlockFlagsHasCopyDisposeHelpers = (1 << 25),\n\tAspectBlockFlagsHasSignature          = (1 << 30)\n};\ntypedef struct _AspectBlock {\n\t__unused Class isa;\n\tAspectBlockFlags flags;\n\t__unused int reserved;\n\tvoid (__unused *invoke)(struct _AspectBlock *block, ...);\n\tstruct {\n\t\tunsigned long int reserved;\n\t\tunsigned long int size;\n\t\t// requires AspectBlockFlagsHasCopyDisposeHelpers\n\t\tvoid (*copy)(void *dst, const void *src);\n\t\tvoid (*dispose)(const void *);\n\t\t// requires AspectBlockFlagsHasSignature\n\t\tconst char *signature;\n\t\tconst char *layout;\n\t} *descriptor;\n\t// imported variables\n} *AspectBlockRef;\n\n@interface AspectInfo : NSObject <AspectInfo>\n- (id)initWithInstance:(__unsafe_unretained id)instance invocation:(NSInvocation *)invocation;\n@property (nonatomic, unsafe_unretained, readonly) id instance;\n@property (nonatomic, strong, readonly) NSArray *arguments;\n@property (nonatomic, strong, readonly) NSInvocation *originalInvocation;\n@end\n\n// Tracks a single aspect.\n@interface AspectIdentifier : NSObject\n+ (instancetype)identifierWithSelector:(SEL)selector object:(id)object options:(AspectOptions)options block:(id)block error:(NSError *__autoreleasing*)error;\n- (BOOL)invokeWithInfo:(id<AspectInfo>)info;\n@property (nonatomic, assign) SEL selector;\n@property (nonatomic, strong) id block;\n@property (nonatomic, strong) NSMethodSignature *blockSignature;\n@property (nonatomic, weak) id object;\n@property (nonatomic, assign) AspectOptions options;\n@end\n\n// Tracks all aspects for an object/class.\n@interface AspectsContainer : NSObject\n- (void)addAspect:(AspectIdentifier *)aspect withOptions:(AspectOptions)injectPosition;\n- (BOOL)removeAspect:(id)aspect;\n- (BOOL)hasAspects;\n@property (atomic, copy) NSArray *beforeAspects;\n@property (atomic, copy) NSArray *insteadAspects;\n@property (atomic, copy) NSArray *afterAspects;\n@end\n\n@interface AspectTracker : NSObject\n- (id)initWithTrackedClass:(Class)trackedClass parent:(AspectTracker *)parent;\n@property (nonatomic, strong) Class trackedClass;\n@property (nonatomic, strong) NSMutableSet *selectorNames;\n@property (nonatomic, weak) AspectTracker *parentEntry;\n@end\n\n@interface NSInvocation (Aspects)\n- (NSArray *)aspects_arguments;\n@end\n\n#define AspectPositionFilter 0x07\n\n#define AspectError(errorCode, errorDescription) do { \\\nAspectLogError(@\"Aspects: %@\", errorDescription); \\\nif (error) { *error = [NSError errorWithDomain:AspectErrorDomain code:errorCode userInfo:@{NSLocalizedDescriptionKey: errorDescription}]; }}while(0)\n\nNSString *const AspectErrorDomain = @\"AspectErrorDomain\";\nstatic NSString *const AspectsSubclassSuffix = @\"_Aspects_\";\nstatic NSString *const AspectsMessagePrefix = @\"aspects_\";\n\n@implementation NSObject (Aspects)\n\n///////////////////////////////////////////////////////////////////////////////////////////\n#pragma mark - Public Aspects API\n\n+ (id<AspectToken>)aspect_hookSelector:(SEL)selector\n                      withOptions:(AspectOptions)options\n                       usingBlock:(id)block\n                            error:(NSError *__autoreleasing*)error {\n    return aspect_add((id)self, selector, options, block, error);\n}\n\n/// @return A token which allows to later deregister the aspect.\n- (id<AspectToken>)aspect_hookSelector:(SEL)selector\n                      withOptions:(AspectOptions)options\n                       usingBlock:(id)block\n                            error:(NSError *__autoreleasing*)error {\n    return aspect_add(self, selector, options, block, error);\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////\n#pragma mark - Private Helper\n\nstatic id aspect_add(id self, SEL selector, AspectOptions options, id block, NSError *__autoreleasing*error) {\n    NSCParameterAssert(self);\n    NSCParameterAssert(selector);\n    NSCParameterAssert(block);\n\n    __block AspectIdentifier *identifier = nil;\n    aspect_performLocked(^{\n        if (aspect_isSelectorAllowedAndTrack(self, selector, options, error)) {\n            AspectsContainer *aspectContainer = aspect_getContainerForObject(self, selector);\n            identifier = [AspectIdentifier identifierWithSelector:selector object:self options:options block:block error:error];\n            if (identifier) {\n                [aspectContainer addAspect:identifier withOptions:options];\n\n                // Modify the class to allow message interception.\n                aspect_prepareClassAndHookSelector(self, selector, error);\n            }\n        }\n    });\n    return identifier;\n}\n\nstatic BOOL aspect_remove(AspectIdentifier *aspect, NSError *__autoreleasing*error) {\n    NSCAssert([aspect isKindOfClass:AspectIdentifier.class], @\"Must have correct type.\");\n\n    __block BOOL success = NO;\n    aspect_performLocked(^{\n        id self = aspect.object; // strongify\n        if (self) {\n            AspectsContainer *aspectContainer = aspect_getContainerForObject(self, aspect.selector);\n            success = [aspectContainer removeAspect:aspect];\n\n            aspect_cleanupHookedClassAndSelector(self, aspect.selector);\n            // destroy token\n            aspect.object = nil;\n            aspect.block = nil;\n            aspect.selector = NULL;\n        } else {\n            NSString *errrorDesc = [NSString stringWithFormat:@\"Unable to deregister hook. Object already deallocated: %@\", aspect];\n            AspectError(AspectErrorRemoveObjectAlreadyDeallocated, errrorDesc);\n        }\n    });\n    return success;\n}\n\nstatic void aspect_performLocked(dispatch_block_t block) {\n    static OSSpinLock aspect_lock = OS_SPINLOCK_INIT;\n    OSSpinLockLock(&aspect_lock);\n    block();\n    OSSpinLockUnlock(&aspect_lock);\n}\n\nstatic SEL aspect_aliasForSelector(SEL selector) {\n    NSCParameterAssert(selector);\n\treturn NSSelectorFromString([AspectsMessagePrefix stringByAppendingFormat:@\"_%@\", NSStringFromSelector(selector)]);\n}\n\nstatic NSMethodSignature *aspect_blockMethodSignature(id block, NSError *__autoreleasing*error) {\n    AspectBlockRef layout = (__bridge void *)block;\n\tif (!(layout->flags & AspectBlockFlagsHasSignature)) {\n        NSString *description = [NSString stringWithFormat:@\"The block %@ doesn't contain a type signature.\", block];\n        AspectError(AspectErrorMissingBlockSignature, description);\n        return nil;\n    }\n\tvoid *desc = layout->descriptor;\n\tdesc += 2 * sizeof(unsigned long int);\n\tif (layout->flags & AspectBlockFlagsHasCopyDisposeHelpers) {\n\t\tdesc += 2 * sizeof(void *);\n    }\n\tif (!desc) {\n        NSString *description = [NSString stringWithFormat:@\"The block %@ doesn't has a type signature.\", block];\n        AspectError(AspectErrorMissingBlockSignature, description);\n        return nil;\n    }\n\tconst char *signature = (*(const char **)desc);\n\treturn [NSMethodSignature signatureWithObjCTypes:signature];\n}\n\nstatic BOOL aspect_isCompatibleBlockSignature(NSMethodSignature *blockSignature, id object, SEL selector, NSError *__autoreleasing*error) {\n    NSCParameterAssert(blockSignature);\n    NSCParameterAssert(object);\n    NSCParameterAssert(selector);\n\n    BOOL signaturesMatch = YES;\n    NSMethodSignature *methodSignature = [[object class] instanceMethodSignatureForSelector:selector];\n    if (blockSignature.numberOfArguments > methodSignature.numberOfArguments) {\n        signaturesMatch = NO;\n    } else {\n        if (blockSignature.numberOfArguments > 1) {\n            const char *blockType = [blockSignature getArgumentTypeAtIndex:1];\n            if (blockType[0] != '@') {\n                signaturesMatch = NO;\n            }\n        }\n        // Argument 0 is self/block, argument 1 is SEL or id<AspectInfo>. We start comparing at argument 2.\n        // The block can have less arguments than the method, that's ok.\n        if (signaturesMatch) {\n            for (NSUInteger idx = 2; idx < blockSignature.numberOfArguments; idx++) {\n                const char *methodType = [methodSignature getArgumentTypeAtIndex:idx];\n                const char *blockType = [blockSignature getArgumentTypeAtIndex:idx];\n                // Only compare parameter, not the optional type data.\n                if (!methodType || !blockType || methodType[0] != blockType[0]) {\n                    signaturesMatch = NO; break;\n                }\n            }\n        }\n    }\n\n    if (!signaturesMatch) {\n        NSString *description = [NSString stringWithFormat:@\"Blog signature %@ doesn't match %@.\", blockSignature, methodSignature];\n        AspectError(AspectErrorIncompatibleBlockSignature, description);\n        return NO;\n    }\n    return YES;\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////\n#pragma mark - Class + Selector Preparation\n\nstatic BOOL aspect_isMsgForwardIMP(IMP impl) {\n    return impl == _objc_msgForward\n#if !defined(__arm64__)\n    || impl == (IMP)_objc_msgForward_stret\n#endif\n    ;\n}\n\nstatic IMP aspect_getMsgForwardIMP(NSObject *self, SEL selector) {\n    IMP msgForwardIMP = _objc_msgForward;\n#if !defined(__arm64__)\n    // As an ugly internal runtime implementation detail in the 32bit runtime, we need to determine of the method we hook returns a struct or anything larger than id.\n    // https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/LowLevelABI/000-Introduction/introduction.html\n    // https://github.com/ReactiveCocoa/ReactiveCocoa/issues/783\n    // http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042e/IHI0042E_aapcs.pdf (Section 5.4)\n    Method method = class_getInstanceMethod(self.class, selector);\n    const char *encoding = method_getTypeEncoding(method);\n    BOOL methodReturnsStructValue = encoding[0] == _C_STRUCT_B;\n    if (methodReturnsStructValue) {\n        @try {\n            NSUInteger valueSize = 0;\n            NSGetSizeAndAlignment(encoding, &valueSize, NULL);\n\n            if (valueSize == 1 || valueSize == 2 || valueSize == 4 || valueSize == 8) {\n                methodReturnsStructValue = NO;\n            }\n        } @catch (__unused NSException *e) {}\n    }\n    if (methodReturnsStructValue) {\n        msgForwardIMP = (IMP)_objc_msgForward_stret;\n    }\n#endif\n    return msgForwardIMP;\n}\n\nstatic void aspect_prepareClassAndHookSelector(NSObject *self, SEL selector, NSError *__autoreleasing*error) {\n    NSCParameterAssert(selector);\n    Class klass = aspect_hookClass(self, error);\n    Method targetMethod = class_getInstanceMethod(klass, selector);\n    IMP targetMethodIMP = method_getImplementation(targetMethod);\n    if (!aspect_isMsgForwardIMP(targetMethodIMP)) {\n        // Make a method alias for the existing method implementation, it not already copied.\n        const char *typeEncoding = method_getTypeEncoding(targetMethod);\n        SEL aliasSelector = aspect_aliasForSelector(selector);\n        if (![klass instancesRespondToSelector:aliasSelector]) {\n            BOOL addedAlias = class_addMethod(klass, aliasSelector, method_getImplementation(targetMethod), typeEncoding);\n            NSCAssert(addedAlias, @\"Original implementation for %@ is already copied to %@ on %@\", NSStringFromSelector(selector), NSStringFromSelector(aliasSelector), klass);\n        }\n\n        // We use forwardInvocation to hook in.\n        class_replaceMethod(klass, selector, aspect_getMsgForwardIMP(self, selector), typeEncoding);\n        AspectLog(@\"Aspects: Installed hook for -[%@ %@].\", klass, NSStringFromSelector(selector));\n    }\n}\n\n// Will undo the runtime changes made.\nstatic void aspect_cleanupHookedClassAndSelector(NSObject *self, SEL selector) {\n    NSCParameterAssert(self);\n    NSCParameterAssert(selector);\n\n\tClass klass = object_getClass(self);\n    BOOL isMetaClass = class_isMetaClass(klass);\n    if (isMetaClass) {\n        klass = (Class)self;\n    }\n\n    // Check if the method is marked as forwarded and undo that.\n    Method targetMethod = class_getInstanceMethod(klass, selector);\n    IMP targetMethodIMP = method_getImplementation(targetMethod);\n    if (aspect_isMsgForwardIMP(targetMethodIMP)) {\n        // Restore the original method implementation.\n        const char *typeEncoding = method_getTypeEncoding(targetMethod);\n        SEL aliasSelector = aspect_aliasForSelector(selector);\n        Method originalMethod = class_getInstanceMethod(klass, aliasSelector);\n        IMP originalIMP = method_getImplementation(originalMethod);\n        NSCAssert(originalMethod, @\"Original implementation for %@ not found %@ on %@\", NSStringFromSelector(selector), NSStringFromSelector(aliasSelector), klass);\n\n        class_replaceMethod(klass, selector, originalIMP, typeEncoding);\n        AspectLog(@\"Aspects: Removed hook for -[%@ %@].\", klass, NSStringFromSelector(selector));\n    }\n\n    // Deregister global tracked selector\n    aspect_deregisterTrackedSelector(self, selector);\n\n    // Get the aspect container and check if there are any hooks remaining. Clean up if there are not.\n    AspectsContainer *container = aspect_getContainerForObject(self, selector);\n    if (!container.hasAspects) {\n        // Destroy the container\n        aspect_destroyContainerForObject(self, selector);\n\n        // Figure out how the class was modified to undo the changes.\n        NSString *className = NSStringFromClass(klass);\n        if ([className hasSuffix:AspectsSubclassSuffix]) {\n            Class originalClass = NSClassFromString([className stringByReplacingOccurrencesOfString:AspectsSubclassSuffix withString:@\"\"]);\n            NSCAssert(originalClass != nil, @\"Original class must exist\");\n            object_setClass(self, originalClass);\n            AspectLog(@\"Aspects: %@ has been restored.\", NSStringFromClass(originalClass));\n\n            // We can only dispose the class pair if we can ensure that no instances exist using our subclass.\n            // Since we don't globally track this, we can't ensure this - but there's also not much overhead in keeping it around.\n            //objc_disposeClassPair(object.class);\n        } else {\n            // Class is most likely swizzled in place. Undo that.\n            if (isMetaClass) {\n                aspect_undoSwizzleClassInPlace((Class)self);\n            }\n        }\n    }\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////\n#pragma mark - Hook Class\n\nstatic Class aspect_hookClass(NSObject *self, NSError *__autoreleasing*error) {\n    NSCParameterAssert(self);\n\tClass statedClass = self.class;\n\tClass baseClass = object_getClass(self);\n\tNSString *className = NSStringFromClass(baseClass);\n\n    // Already subclassed\n\tif ([className hasSuffix:AspectsSubclassSuffix]) {\n\t\treturn baseClass;\n\n        // We swizzle a class object, not a single object.\n\t} else if (class_isMetaClass(baseClass)) {\n        return aspect_swizzleClassInPlace((Class)self);\n        // Probably a KVO'ed class. Swizzle in place. Also swizzle meta classes in place.\n    } else if (statedClass != baseClass) {\n        return aspect_swizzleClassInPlace(baseClass);\n    }\n\n    // Default case. Create dynamic subclass.\n\tconst char *subclassName = [className stringByAppendingString:AspectsSubclassSuffix].UTF8String;\n\tClass subclass = objc_getClass(subclassName);\n\n\tif (subclass == nil) {\n\t\tsubclass = objc_allocateClassPair(baseClass, subclassName, 0);\n\t\tif (subclass == nil) {\n            NSString *errrorDesc = [NSString stringWithFormat:@\"objc_allocateClassPair failed to allocate class %s.\", subclassName];\n            AspectError(AspectErrorFailedToAllocateClassPair, errrorDesc);\n            return nil;\n        }\n\n\t\taspect_swizzleForwardInvocation(subclass);\n\t\taspect_hookedGetClass(subclass, statedClass);\n\t\taspect_hookedGetClass(object_getClass(subclass), statedClass);\n\t\tobjc_registerClassPair(subclass);\n\t}\n\n\tobject_setClass(self, subclass);\n\treturn subclass;\n}\n\nstatic NSString *const AspectsForwardInvocationSelectorName = @\"__aspects_forwardInvocation:\";\nstatic void aspect_swizzleForwardInvocation(Class klass) {\n    NSCParameterAssert(klass);\n    // If there is no method, replace will act like class_addMethod.\n    IMP originalImplementation = class_replaceMethod(klass, @selector(forwardInvocation:), (IMP)__ASPECTS_ARE_BEING_CALLED__, \"v@:@\");\n    if (originalImplementation) {\n        class_addMethod(klass, NSSelectorFromString(AspectsForwardInvocationSelectorName), originalImplementation, \"v@:@\");\n    }\n    AspectLog(@\"Aspects: %@ is now aspect aware.\", NSStringFromClass(klass));\n}\n\nstatic void aspect_undoSwizzleForwardInvocation(Class klass) {\n    NSCParameterAssert(klass);\n    Method originalMethod = class_getInstanceMethod(klass, NSSelectorFromString(AspectsForwardInvocationSelectorName));\n    Method objectMethod = class_getInstanceMethod(NSObject.class, @selector(forwardInvocation:));\n    // There is no class_removeMethod, so the best we can do is to retore the original implementation, or use a dummy.\n    IMP originalImplementation = method_getImplementation(originalMethod ?: objectMethod);\n    class_replaceMethod(klass, @selector(forwardInvocation:), originalImplementation, \"v@:@\");\n\n    AspectLog(@\"Aspects: %@ has been restored.\", NSStringFromClass(klass));\n}\n\nstatic void aspect_hookedGetClass(Class class, Class statedClass) {\n    NSCParameterAssert(class);\n    NSCParameterAssert(statedClass);\n\tMethod method = class_getInstanceMethod(class, @selector(class));\n\tIMP newIMP = imp_implementationWithBlock(^(id self) {\n\t\treturn statedClass;\n\t});\n\tclass_replaceMethod(class, @selector(class), newIMP, method_getTypeEncoding(method));\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////\n#pragma mark - Swizzle Class In Place\n\nstatic void _aspect_modifySwizzledClasses(void (^block)(NSMutableSet *swizzledClasses)) {\n    static NSMutableSet *swizzledClasses;\n    static dispatch_once_t pred;\n    dispatch_once(&pred, ^{\n        swizzledClasses = [NSMutableSet new];\n    });\n    @synchronized(swizzledClasses) {\n        block(swizzledClasses);\n    }\n}\n\nstatic Class aspect_swizzleClassInPlace(Class klass) {\n    NSCParameterAssert(klass);\n    NSString *className = NSStringFromClass(klass);\n\n    _aspect_modifySwizzledClasses(^(NSMutableSet *swizzledClasses) {\n        if (![swizzledClasses containsObject:className]) {\n            aspect_swizzleForwardInvocation(klass);\n            [swizzledClasses addObject:className];\n        }\n    });\n    return klass;\n}\n\nstatic void aspect_undoSwizzleClassInPlace(Class klass) {\n    NSCParameterAssert(klass);\n    NSString *className = NSStringFromClass(klass);\n\n    _aspect_modifySwizzledClasses(^(NSMutableSet *swizzledClasses) {\n        if ([swizzledClasses containsObject:className]) {\n            aspect_undoSwizzleForwardInvocation(klass);\n            [swizzledClasses removeObject:className];\n        }\n    });\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////\n#pragma mark - Aspect Invoke Point\n\n// This is a macro so we get a cleaner stack trace.\n#define aspect_invoke(aspects, info) \\\nfor (AspectIdentifier *aspect in aspects) {\\\n    [aspect invokeWithInfo:info];\\\n    if (aspect.options & AspectOptionAutomaticRemoval) { \\\n        aspectsToRemove = [aspectsToRemove?:@[] arrayByAddingObject:aspect]; \\\n    } \\\n}\n\n// This is the swizzled forwardInvocation: method.\nstatic void __ASPECTS_ARE_BEING_CALLED__(__unsafe_unretained NSObject *self, SEL selector, NSInvocation *invocation) {\n    NSCParameterAssert(self);\n    NSCParameterAssert(invocation);\n    SEL originalSelector = invocation.selector;\n\tSEL aliasSelector = aspect_aliasForSelector(invocation.selector);\n    invocation.selector = aliasSelector;\n    AspectsContainer *objectContainer = objc_getAssociatedObject(self, aliasSelector);\n    AspectsContainer *classContainer = aspect_getContainerForClass(object_getClass(self), aliasSelector);\n    AspectInfo *info = [[AspectInfo alloc] initWithInstance:self invocation:invocation];\n    NSArray *aspectsToRemove = nil;\n\n    // Before hooks.\n    aspect_invoke(classContainer.beforeAspects, info);\n    aspect_invoke(objectContainer.beforeAspects, info);\n\n    // Instead hooks.\n    BOOL respondsToAlias = YES;\n    if (objectContainer.insteadAspects.count || classContainer.insteadAspects.count) {\n        aspect_invoke(classContainer.insteadAspects, info);\n        aspect_invoke(objectContainer.insteadAspects, info);\n    } else {\n        Class klass = object_getClass(invocation.target);\n        do {\n            if ((respondsToAlias = [klass instancesRespondToSelector:aliasSelector])) {\n                [invocation invoke];\n                break;\n            }\n        }while (!respondsToAlias && (klass = class_getSuperclass(klass)));\n    }\n\n    // After hooks.\n    aspect_invoke(classContainer.afterAspects, info);\n    aspect_invoke(objectContainer.afterAspects, info);\n\n    // If no hooks are installed, call original implementation (usually to throw an exception)\n    if (!respondsToAlias) {\n        invocation.selector = originalSelector;\n        SEL originalForwardInvocationSEL = NSSelectorFromString(AspectsForwardInvocationSelectorName);\n        if ([self respondsToSelector:originalForwardInvocationSEL]) {\n            ((void( *)(id, SEL, NSInvocation *))objc_msgSend)(self, originalForwardInvocationSEL, invocation);\n        } else {\n            [self doesNotRecognizeSelector:invocation.selector];\n        }\n    }\n\n    // Remove any hooks that are queued for deregistration.\n    [aspectsToRemove makeObjectsPerformSelector:@selector(remove)];\n}\n#undef aspect_invoke\n\n///////////////////////////////////////////////////////////////////////////////////////////\n#pragma mark - Aspect Container Management\n\n// Loads or creates the aspect container.\nstatic AspectsContainer *aspect_getContainerForObject(NSObject *self, SEL selector) {\n    NSCParameterAssert(self);\n    SEL aliasSelector = aspect_aliasForSelector(selector);\n    AspectsContainer *aspectContainer = objc_getAssociatedObject(self, aliasSelector);\n    if (!aspectContainer) {\n        aspectContainer = [AspectsContainer new];\n        objc_setAssociatedObject(self, aliasSelector, aspectContainer, OBJC_ASSOCIATION_RETAIN);\n    }\n    return aspectContainer;\n}\n\nstatic AspectsContainer *aspect_getContainerForClass(Class klass, SEL selector) {\n    NSCParameterAssert(klass);\n    AspectsContainer *classContainer = nil;\n    do {\n        classContainer = objc_getAssociatedObject(klass, selector);\n        if (classContainer.hasAspects) break;\n    }while ((klass = class_getSuperclass(klass)));\n\n    return classContainer;\n}\n\nstatic void aspect_destroyContainerForObject(id<NSObject> self, SEL selector) {\n    NSCParameterAssert(self);\n    SEL aliasSelector = aspect_aliasForSelector(selector);\n    objc_setAssociatedObject(self, aliasSelector, nil, OBJC_ASSOCIATION_RETAIN);\n}\n\n///////////////////////////////////////////////////////////////////////////////////////////\n#pragma mark - Selector Blacklist Checking\n\nstatic NSMutableDictionary *aspect_getSwizzledClassesDict() {\n    static NSMutableDictionary *swizzledClassesDict;\n    static dispatch_once_t pred;\n    dispatch_once(&pred, ^{\n        swizzledClassesDict = [NSMutableDictionary new];\n    });\n    return swizzledClassesDict;\n}\n\nstatic BOOL aspect_isSelectorAllowedAndTrack(NSObject *self, SEL selector, AspectOptions options, NSError *__autoreleasing*error) {\n    static NSSet *disallowedSelectorList;\n    static dispatch_once_t pred;\n    dispatch_once(&pred, ^{\n        disallowedSelectorList = [NSSet setWithObjects:@\"retain\", @\"release\", @\"autorelease\", @\"forwardInvocation:\", nil];\n    });\n\n    // Check against the blacklist.\n    NSString *selectorName = NSStringFromSelector(selector);\n    if ([disallowedSelectorList containsObject:selectorName]) {\n        NSString *errorDescription = [NSString stringWithFormat:@\"Selector %@ is blacklisted.\", selectorName];\n        AspectError(AspectErrorSelectorBlacklisted, errorDescription);\n        return NO;\n    }\n\n    // Additional checks.\n    AspectOptions position = options&AspectPositionFilter;\n    if ([selectorName isEqualToString:@\"dealloc\"] && position != AspectPositionBefore) {\n        NSString *errorDesc = @\"AspectPositionBefore is the only valid position when hooking dealloc.\";\n        AspectError(AspectErrorSelectorDeallocPosition, errorDesc);\n        return NO;\n    }\n\n    if (![self respondsToSelector:selector] && ![self.class instancesRespondToSelector:selector]) {\n        NSString *errorDesc = [NSString stringWithFormat:@\"Unable to find selector -[%@ %@].\", NSStringFromClass(self.class), selectorName];\n        AspectError(AspectErrorDoesNotRespondToSelector, errorDesc);\n        return NO;\n    }\n\n    // Search for the current class and the class hierarchy IF we are modifying a class object\n    if (class_isMetaClass(object_getClass(self))) {\n        Class klass = [self class];\n        NSMutableDictionary *swizzledClassesDict = aspect_getSwizzledClassesDict();\n        Class currentClass = [self class];\n        do {\n            AspectTracker *tracker = swizzledClassesDict[currentClass];\n            if ([tracker.selectorNames containsObject:selectorName]) {\n\n                // Find the topmost class for the log.\n                if (tracker.parentEntry) {\n                    AspectTracker *topmostEntry = tracker.parentEntry;\n                    while (topmostEntry.parentEntry) {\n                        topmostEntry = topmostEntry.parentEntry;\n                    }\n                    NSString *errorDescription = [NSString stringWithFormat:@\"Error: %@ already hooked in %@. A method can only be hooked once per class hierarchy.\", selectorName, NSStringFromClass(topmostEntry.trackedClass)];\n                    AspectError(AspectErrorSelectorAlreadyHookedInClassHierarchy, errorDescription);\n                    return NO;\n                } else if (klass == currentClass) {\n                    // Already modified and topmost!\n                    return YES;\n                }\n            }\n        }while ((currentClass = class_getSuperclass(currentClass)));\n\n        // Add the selector as being modified.\n        currentClass = klass;\n        AspectTracker *parentTracker = nil;\n        do {\n            AspectTracker *tracker = swizzledClassesDict[currentClass];\n            if (!tracker) {\n                tracker = [[AspectTracker alloc] initWithTrackedClass:currentClass parent:parentTracker];\n                swizzledClassesDict[(id<NSCopying>)currentClass] = tracker;\n            }\n            [tracker.selectorNames addObject:selectorName];\n            // All superclasses get marked as having a subclass that is modified.\n            parentTracker = tracker;\n        }while ((currentClass = class_getSuperclass(currentClass)));\n    }\n\n    return YES;\n}\n\nstatic void aspect_deregisterTrackedSelector(id self, SEL selector) {\n    if (!class_isMetaClass(object_getClass(self))) return;\n\n    NSMutableDictionary *swizzledClassesDict = aspect_getSwizzledClassesDict();\n    NSString *selectorName = NSStringFromSelector(selector);\n    Class currentClass = [self class];\n    do {\n        AspectTracker *tracker = swizzledClassesDict[currentClass];\n        if (tracker) {\n            [tracker.selectorNames removeObject:selectorName];\n            if (tracker.selectorNames.count == 0) {\n                [swizzledClassesDict removeObjectForKey:tracker];\n            }\n        }\n    }while ((currentClass = class_getSuperclass(currentClass)));\n}\n\n@end\n\n@implementation AspectTracker\n\n- (id)initWithTrackedClass:(Class)trackedClass parent:(AspectTracker *)parent {\n    if ((self = [super init])) {\n        _trackedClass = trackedClass;\n        _parentEntry = parent;\n        _selectorNames = [NSMutableSet new];\n    }\n    return self;\n}\n- (NSString *)description {\n    return [NSString stringWithFormat:@\"<%@: %@, trackedClass: %@, selectorNames:%@, parent:%p>\", self.class, self, NSStringFromClass(self.trackedClass), self.selectorNames, self.parentEntry];\n}\n\n@end\n\n///////////////////////////////////////////////////////////////////////////////////////////\n#pragma mark - NSInvocation (Aspects)\n\n@implementation NSInvocation (Aspects)\n\n// Thanks to the ReactiveCocoa team for providing a generic solution for this.\n- (id)aspect_argumentAtIndex:(NSUInteger)index {\n\tconst char *argType = [self.methodSignature getArgumentTypeAtIndex:index];\n\t// Skip const type qualifier.\n\tif (argType[0] == _C_CONST) argType++;\n\n#define WRAP_AND_RETURN(type) do { type val = 0; [self getArgument:&val atIndex:(NSInteger)index]; return @(val); } while (0)\n\tif (strcmp(argType, @encode(id)) == 0 || strcmp(argType, @encode(Class)) == 0) {\n\t\t__autoreleasing id returnObj;\n\t\t[self getArgument:&returnObj atIndex:(NSInteger)index];\n\t\treturn returnObj;\n\t} else if (strcmp(argType, @encode(SEL)) == 0) {\n        SEL selector = 0;\n        [self getArgument:&selector atIndex:(NSInteger)index];\n        return NSStringFromSelector(selector);\n    } else if (strcmp(argType, @encode(Class)) == 0) {\n        __autoreleasing Class theClass = Nil;\n        [self getArgument:&theClass atIndex:(NSInteger)index];\n        return theClass;\n        // Using this list will box the number with the appropriate constructor, instead of the generic NSValue.\n\t} else if (strcmp(argType, @encode(char)) == 0) {\n\t\tWRAP_AND_RETURN(char);\n\t} else if (strcmp(argType, @encode(int)) == 0) {\n\t\tWRAP_AND_RETURN(int);\n\t} else if (strcmp(argType, @encode(short)) == 0) {\n\t\tWRAP_AND_RETURN(short);\n\t} else if (strcmp(argType, @encode(long)) == 0) {\n\t\tWRAP_AND_RETURN(long);\n\t} else if (strcmp(argType, @encode(long long)) == 0) {\n\t\tWRAP_AND_RETURN(long long);\n\t} else if (strcmp(argType, @encode(unsigned char)) == 0) {\n\t\tWRAP_AND_RETURN(unsigned char);\n\t} else if (strcmp(argType, @encode(unsigned int)) == 0) {\n\t\tWRAP_AND_RETURN(unsigned int);\n\t} else if (strcmp(argType, @encode(unsigned short)) == 0) {\n\t\tWRAP_AND_RETURN(unsigned short);\n\t} else if (strcmp(argType, @encode(unsigned long)) == 0) {\n\t\tWRAP_AND_RETURN(unsigned long);\n\t} else if (strcmp(argType, @encode(unsigned long long)) == 0) {\n\t\tWRAP_AND_RETURN(unsigned long long);\n\t} else if (strcmp(argType, @encode(float)) == 0) {\n\t\tWRAP_AND_RETURN(float);\n\t} else if (strcmp(argType, @encode(double)) == 0) {\n\t\tWRAP_AND_RETURN(double);\n\t} else if (strcmp(argType, @encode(BOOL)) == 0) {\n\t\tWRAP_AND_RETURN(BOOL);\n\t} else if (strcmp(argType, @encode(bool)) == 0) {\n\t\tWRAP_AND_RETURN(BOOL);\n\t} else if (strcmp(argType, @encode(char *)) == 0) {\n\t\tWRAP_AND_RETURN(const char *);\n\t} else if (strcmp(argType, @encode(void (^)(void))) == 0) {\n\t\t__unsafe_unretained id block = nil;\n\t\t[self getArgument:&block atIndex:(NSInteger)index];\n\t\treturn [block copy];\n\t} else {\n\t\tNSUInteger valueSize = 0;\n\t\tNSGetSizeAndAlignment(argType, &valueSize, NULL);\n\n\t\tunsigned char valueBytes[valueSize];\n\t\t[self getArgument:valueBytes atIndex:(NSInteger)index];\n\n\t\treturn [NSValue valueWithBytes:valueBytes objCType:argType];\n\t}\n\treturn nil;\n#undef WRAP_AND_RETURN\n}\n\n- (NSArray *)aspects_arguments {\n\tNSMutableArray *argumentsArray = [NSMutableArray array];\n\tfor (NSUInteger idx = 2; idx < self.methodSignature.numberOfArguments; idx++) {\n\t\t[argumentsArray addObject:[self aspect_argumentAtIndex:idx] ?: NSNull.null];\n\t}\n\treturn [argumentsArray copy];\n}\n\n@end\n\n///////////////////////////////////////////////////////////////////////////////////////////\n#pragma mark - AspectIdentifier\n\n@implementation AspectIdentifier\n\n+ (instancetype)identifierWithSelector:(SEL)selector object:(id)object options:(AspectOptions)options block:(id)block error:(NSError *__autoreleasing*)error {\n    NSCParameterAssert(block);\n    NSCParameterAssert(selector);\n    NSMethodSignature *blockSignature = aspect_blockMethodSignature(block, error); // TODO: check signature compatibility, etc.\n    if (!aspect_isCompatibleBlockSignature(blockSignature, object, selector, error)) {\n        return nil;\n    }\n\n    AspectIdentifier *identifier = nil;\n    if (blockSignature) {\n        identifier = [AspectIdentifier new];\n        identifier.selector = selector;\n        identifier.block = block;\n        identifier.blockSignature = blockSignature;\n        identifier.options = options;\n        identifier.object = object; // weak\n    }\n    return identifier;\n}\n\n- (BOOL)invokeWithInfo:(id<AspectInfo>)info {\n    NSInvocation *blockInvocation = [NSInvocation invocationWithMethodSignature:self.blockSignature];\n    NSInvocation *originalInvocation = info.originalInvocation;\n    NSUInteger numberOfArguments = self.blockSignature.numberOfArguments;\n\n    // Be extra paranoid. We already check that on hook registration.\n    if (numberOfArguments > originalInvocation.methodSignature.numberOfArguments) {\n        AspectLogError(@\"Block has too many arguments. Not calling %@\", info);\n        return NO;\n    }\n\n    // The `self` of the block will be the AspectInfo. Optional.\n    if (numberOfArguments > 1) {\n        [blockInvocation setArgument:&info atIndex:1];\n    }\n\n\tvoid *argBuf = NULL;\n    for (NSUInteger idx = 2; idx < numberOfArguments; idx++) {\n        const char *type = [originalInvocation.methodSignature getArgumentTypeAtIndex:idx];\n\t\tNSUInteger argSize;\n\t\tNSGetSizeAndAlignment(type, &argSize, NULL);\n\n\t\tif (!(argBuf = reallocf(argBuf, argSize))) {\n            AspectLogError(@\"Failed to allocate memory for block invocation.\");\n\t\t\treturn NO;\n\t\t}\n\n\t\t[originalInvocation getArgument:argBuf atIndex:idx];\n\t\t[blockInvocation setArgument:argBuf atIndex:idx];\n    }\n\n    [blockInvocation invokeWithTarget:self.block];\n\n    if (argBuf != NULL) {\n        free(argBuf);\n    }\n    return YES;\n}\n\n- (NSString *)description {\n    return [NSString stringWithFormat:@\"<%@: %p, SEL:%@ object:%@ options:%tu block:%@ (#%tu args)>\", self.class, self, NSStringFromSelector(self.selector), self.object, self.options, self.block, self.blockSignature.numberOfArguments];\n}\n\n- (BOOL)remove {\n    return aspect_remove(self, NULL);\n}\n\n@end\n\n///////////////////////////////////////////////////////////////////////////////////////////\n#pragma mark - AspectsContainer\n\n@implementation AspectsContainer\n\n- (BOOL)hasAspects {\n    return self.beforeAspects.count > 0 || self.insteadAspects.count > 0 || self.afterAspects.count > 0;\n}\n\n- (void)addAspect:(AspectIdentifier *)aspect withOptions:(AspectOptions)options {\n    NSParameterAssert(aspect);\n    NSUInteger position = options&AspectPositionFilter;\n    switch (position) {\n        case AspectPositionBefore:  self.beforeAspects  = [(self.beforeAspects ?:@[]) arrayByAddingObject:aspect]; break;\n        case AspectPositionInstead: self.insteadAspects = [(self.insteadAspects?:@[]) arrayByAddingObject:aspect]; break;\n        case AspectPositionAfter:   self.afterAspects   = [(self.afterAspects  ?:@[]) arrayByAddingObject:aspect]; break;\n    }\n}\n\n- (BOOL)removeAspect:(id)aspect {\n    for (NSString *aspectArrayName in @[NSStringFromSelector(@selector(beforeAspects)),\n                                        NSStringFromSelector(@selector(insteadAspects)),\n                                        NSStringFromSelector(@selector(afterAspects))]) {\n        NSArray *array = [self valueForKey:aspectArrayName];\n        NSUInteger index = [array indexOfObjectIdenticalTo:aspect];\n        if (array && index != NSNotFound) {\n            NSMutableArray *newArray = [NSMutableArray arrayWithArray:array];\n            [newArray removeObjectAtIndex:index];\n            [self setValue:newArray forKey:aspectArrayName];\n            return YES;\n        }\n    }\n    return NO;\n}\n\n- (NSString *)description {\n    return [NSString stringWithFormat:@\"<%@: %p, before:%@, instead:%@, after:%@>\", self.class, self, self.beforeAspects, self.insteadAspects, self.afterAspects];\n}\n\n@end\n\n///////////////////////////////////////////////////////////////////////////////////////////\n#pragma mark - AspectInfo\n\n@implementation AspectInfo\n\n@synthesize arguments = _arguments;\n\n- (id)initWithInstance:(__unsafe_unretained id)instance invocation:(NSInvocation *)invocation {\n    NSCParameterAssert(instance);\n    NSCParameterAssert(invocation);\n    if ((self = [super init])) {\n        _instance = instance;\n        _originalInvocation = invocation;\n    }\n    return self;\n}\n\n- (NSArray *)arguments {\n    // Lazily evaluate arguments, boxing is expensive.\n    if (!_arguments) {\n        _arguments = self.originalInvocation.aspects_arguments;\n    }\n    return _arguments;\n}\n\n@end\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2015 Peter Steinberger\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
  },
  {
    "path": "PSTEnableUIKitDebugging.m",
    "content": "//\n//  PSTEnableUIKitDebugging.m\n//\n//  Copyright (c) 2015 Peter Steinberger. Licensed under the MIT license.\n//\n\n#import \"Aspects.h\"\n#import \"fishhook.h\"\n#import <dlfcn.h>\n\n// UIAnimationDragCoefficient\n// UISimulatedApplicationResizeGestureEnabled\n// UIExternalTouchSloppinessFactor\n// UIEnableParallaxEffects\n// UIDeviceUsesLowQualityGraphics\n\n// UIMotionEffectsEnabled\n// UIMotionEffectMotionUpdateFrequency\n// UIMotionEffectMotionUpdateSlowFrequency\n// UIMotionEffectMinimumBacklightLevel\n// UIMotionEffectHysteresisExitThreshold\n// UIMotionEffectHysteresisEntranceThreshold\n// UIMotionEffectUIUpdateFrequency\n// UIMotionEffectUIUpdateSlowFrequency\n\n// UIDocumentConsoleLogLevel\n// UIDocumentFileLogLevel\n// GestureFailureMapLogging\n// UIPopoverControllerForceAttemptsToAvoidKeyboard\n\n// _UISiriAnimationSpeed ...\n\n// UIBackdropViewNoBlur\n// UIBackdropViewNoComputedColorSettingsKey\n\nstatic NSDictionary *UIKitOverrides() {\n    return @{@\"UIPopoverControllerPaintsTargetRect\" : @YES, // only works on iOS 7\n             @\"TouchLogging\" : @YES,\n             @\"AnimationLogging\" : @YES,\n             @\"UIUseAugmentedPopGesture\" : @YES,\n             @\"UIKitDecorateFallbackImagesFromScale\" : @YES,\n             @\"UIScreenEdgePanRecognizerShouldLog\" : @YES,\n             @\"SystemGestureGateLogging\" : @YES,\n             @\"GestureLogging\" : @YES,\n             @\"GestureFailureMapLogging\" : @YES,\n             @\"UIKeyboardTypingSpeedLogger\" : @YES,\n             @\"UICatchCAPackageDecodingExceptions\" : @YES\n             };\n}\n\nstatic BOOL (*GetBoolAnswer)(NSString *capability);\nstatic BOOL PSTGetBoolAnswer(NSString *capability) {\n    return [capability isEqual:@\"InternalBuild\"] ? YES : GetBoolAnswer(capability);\n}\n\n__attribute__((constructor)) static void PSTEnableUIKitDebugMode() {\n    // Enable Internal Build mode.\n    GetBoolAnswer = dlsym(dlopen(\"/usr/lib/libMobileGestalt.dylib\", RTLD_LAZY), \"MGGetBoolAnswer\");\n    rebind_symbols((struct rebinding[1]){{\"MGGetBoolAnswer\", PSTGetBoolAnswer}}, 1);\n\n    // Install custom overrides.\n    NSDictionary *overrides = UIKitOverrides();\n\n    [[NSUserDefaults standardUserDefaults] aspect_hookSelector:@selector(persistentDomainForName:) withOptions:0 usingBlock:^(id<AspectInfo> info, NSString *domainName) {\n        if ([domainName hasSuffix:@\"com.apple.UIKit\"]) {\n            __autoreleasing NSDictionary *dictionary;\n            [[info originalInvocation] invoke];\n            [[info originalInvocation] getReturnValue:&dictionary];\n            NSMutableDictionary *mutable = [NSMutableDictionary dictionaryWithDictionary:dictionary];\n            [mutable addEntriesFromDictionary:overrides];\n            dictionary = [mutable copy];\n            [[info originalInvocation] setReturnValue:&dictionary];\n        }\n    } error:NULL];\n\n    [NSUserDefaults aspect_hookSelector:@selector(objectForKey:) withOptions:0 usingBlock:^(id<AspectInfo> info, NSString *key) {\n        if (overrides[key]) {\n            __autoreleasing id value = overrides[key];\n            [[info originalInvocation] setReturnValue:&value];\n        }\n    } error:NULL];\n}\n"
  },
  {
    "path": "README.md",
    "content": "# UIKitDebugging\nA set of files that enables various debug flags in UIKit\n\nSee http://petersteinberger.com/blog/2015/uikit-debug-mode/ for details.\n\nMIT licensed."
  },
  {
    "path": "fishhook-LICENSE",
    "content": "// Copyright (c) 2013, Facebook, Inc.\n// All rights reserved.\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are met:\n//   * Redistributions of source code must retain the above copyright notice,\n//     this list of conditions and the following disclaimer.\n//   * Redistributions in binary form must reproduce the above copyright notice,\n//     this list of conditions and the following disclaimer in the documentation\n//     and/or other materials provided with the distribution.\n//   * Neither the name Facebook nor the names of its contributors may be used to\n//     endorse or promote products derived from this software without specific\n//     prior written permission.\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
  },
  {
    "path": "fishhook.c",
    "content": "// Copyright (c) 2013, Facebook, Inc.\n// All rights reserved.\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are met:\n//   * Redistributions of source code must retain the above copyright notice,\n//     this list of conditions and the following disclaimer.\n//   * Redistributions in binary form must reproduce the above copyright notice,\n//     this list of conditions and the following disclaimer in the documentation\n//     and/or other materials provided with the distribution.\n//   * Neither the name Facebook nor the names of its contributors may be used to\n//     endorse or promote products derived from this software without specific\n//     prior written permission.\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n#import \"fishhook.h\"\n\n#import <dlfcn.h>\n#import <stdlib.h>\n#import <string.h>\n#import <sys/types.h>\n#import <mach-o/dyld.h>\n#import <mach-o/loader.h>\n#import <mach-o/nlist.h>\n\n#ifdef __LP64__\ntypedef struct mach_header_64 mach_header_t;\ntypedef struct segment_command_64 segment_command_t;\ntypedef struct section_64 section_t;\ntypedef struct nlist_64 nlist_t;\n#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT_64\n#else\ntypedef struct mach_header mach_header_t;\ntypedef struct segment_command segment_command_t;\ntypedef struct section section_t;\ntypedef struct nlist nlist_t;\n#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT\n#endif\n\nstruct rebindings_entry {\n  struct rebinding *rebindings;\n  size_t rebindings_nel;\n  struct rebindings_entry *next;\n};\n\nstatic struct rebindings_entry *_rebindings_head;\n\nstatic int prepend_rebindings(struct rebindings_entry **rebindings_head,\n                              struct rebinding rebindings[],\n                              size_t nel) {\n  struct rebindings_entry *new_entry = malloc(sizeof(struct rebindings_entry));\n  if (!new_entry) {\n    return -1;\n  }\n  new_entry->rebindings = malloc(sizeof(struct rebinding) * nel);\n  if (!new_entry->rebindings) {\n    free(new_entry);\n    return -1;\n  }\n  memcpy(new_entry->rebindings, rebindings, sizeof(struct rebinding) * nel);\n  new_entry->rebindings_nel = nel;\n  new_entry->next = *rebindings_head;\n  *rebindings_head = new_entry;\n  return 0;\n}\n\nstatic void perform_rebinding_with_section(struct rebindings_entry *rebindings,\n                                           section_t *section,\n                                           intptr_t slide,\n                                           nlist_t *symtab,\n                                           char *strtab,\n                                           uint32_t *indirect_symtab) {\n  uint32_t *indirect_symbol_indices = indirect_symtab + section->reserved1;\n  void **indirect_symbol_bindings = (void **)((uintptr_t)slide + section->addr);\n  for (uint i = 0; i < section->size / sizeof(void *); i++) {\n    uint32_t symtab_index = indirect_symbol_indices[i];\n    if (symtab_index == INDIRECT_SYMBOL_ABS || symtab_index == INDIRECT_SYMBOL_LOCAL ||\n        symtab_index == (INDIRECT_SYMBOL_LOCAL   | INDIRECT_SYMBOL_ABS)) {\n      continue;\n    }\n    uint32_t strtab_offset = symtab[symtab_index].n_un.n_strx;\n    char *symbol_name = strtab + strtab_offset;\n    struct rebindings_entry *cur = rebindings;\n    while (cur) {\n      for (uint j = 0; j < cur->rebindings_nel; j++) {\n        if (strlen(symbol_name) > 1 &&\n            strcmp(&symbol_name[1], cur->rebindings[j].name) == 0) {\n          indirect_symbol_bindings[i] = cur->rebindings[j].replacement;\n          goto symbol_loop;\n        }\n      }\n      cur = cur->next;\n    }\n  symbol_loop:;\n  }\n}\n\nstatic void rebind_symbols_for_image(struct rebindings_entry *rebindings,\n                                     const struct mach_header *header,\n                                     intptr_t slide) {\n  Dl_info info;\n  if (dladdr(header, &info) == 0) {\n    return;\n  }\n  uintptr_t cur = (uintptr_t)header + sizeof(mach_header_t);\n  segment_command_t *cur_seg_cmd;\n  segment_command_t *linkedit_segment = NULL;\n  section_t *lazy_symbols = NULL;\n  section_t *non_lazy_symbols = NULL;\n  struct symtab_command* symtab_cmd = NULL;\n  struct dysymtab_command* dysymtab_cmd = NULL;\n  for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) {\n    cur_seg_cmd = (segment_command_t *)cur;\n    if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) {\n      if (strcmp(cur_seg_cmd->segname, SEG_LINKEDIT) == 0) {\n        linkedit_segment = cur_seg_cmd;\n        continue;\n      }\n      if (strcmp(cur_seg_cmd->segname, SEG_DATA) != 0) {\n        continue;\n      }\n      for (uint j = 0; j < cur_seg_cmd->nsects; j++) {\n        section_t *sect =\n          (section_t *)(cur + sizeof(segment_command_t)) + j;\n        if ((sect->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS) {\n          lazy_symbols = sect;\n        }\n        if ((sect->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) {\n          non_lazy_symbols = sect;\n        }\n      }\n    } else if (cur_seg_cmd->cmd == LC_SYMTAB) {\n      symtab_cmd = (struct symtab_command*)cur_seg_cmd;\n    } else if (cur_seg_cmd->cmd == LC_DYSYMTAB) {\n      dysymtab_cmd = (struct dysymtab_command*)cur_seg_cmd;\n    }\n  }\n  if (!symtab_cmd || !dysymtab_cmd || !linkedit_segment ||\n      !dysymtab_cmd->nindirectsyms) {\n    return;\n  }\n  // Find base symbol/string table addresses\n  uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff;\n  nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff);\n  char *strtab = (char *)(linkedit_base + symtab_cmd->stroff);\n  // Get indirect symbol table (array of uint32_t indices into symbol table)\n  uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff);\n  if (lazy_symbols) {\n    perform_rebinding_with_section(rebindings, lazy_symbols, slide, symtab, strtab, indirect_symtab);\n  }\n  if (non_lazy_symbols) {\n    perform_rebinding_with_section(rebindings, non_lazy_symbols, slide, symtab, strtab, indirect_symtab);\n  }\n}\n\nstatic void _rebind_symbols_for_image(const struct mach_header *header,\n                                      intptr_t slide) {\n    rebind_symbols_for_image(_rebindings_head, header, slide);\n}\n\nint rebind_symbols_image(void *header,\n                         intptr_t slide,\n                         struct rebinding rebindings[],\n                         size_t rebindings_nel) {\n    struct rebindings_entry *rebindings_head = NULL;\n    int retval = prepend_rebindings(&rebindings_head, rebindings, rebindings_nel);\n    rebind_symbols_for_image(rebindings_head, header, slide);\n    free(rebindings_head);\n    return retval;\n}\n\nint rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel) {\n  int retval = prepend_rebindings(&_rebindings_head, rebindings, rebindings_nel);\n  if (retval < 0) {\n    return retval;\n  }\n  // If this was the first call, register callback for image additions (which is also invoked for\n  // existing images, otherwise, just run on existing images\n  if (!_rebindings_head->next) {\n    _dyld_register_func_for_add_image(_rebind_symbols_for_image);\n  } else {\n    uint32_t c = _dyld_image_count();\n    for (uint32_t i = 0; i < c; i++) {\n      _rebind_symbols_for_image(_dyld_get_image_header(i), _dyld_get_image_vmaddr_slide(i));\n    }\n  }\n  return retval;\n}"
  },
  {
    "path": "fishhook.h",
    "content": "// Copyright (c) 2013, Facebook, Inc.\n// All rights reserved.\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are met:\n//   * Redistributions of source code must retain the above copyright notice,\n//     this list of conditions and the following disclaimer.\n//   * Redistributions in binary form must reproduce the above copyright notice,\n//     this list of conditions and the following disclaimer in the documentation\n//     and/or other materials provided with the distribution.\n//   * Neither the name Facebook nor the names of its contributors may be used to\n//     endorse or promote products derived from this software without specific\n//     prior written permission.\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n#ifndef fishhook_h\n#define fishhook_h\n\n#include <stddef.h>\n#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif //__cplusplus\n\n/*\n * A structure representing a particular intended rebinding from a symbol\n * name to its replacement\n */\nstruct rebinding {\n  char *name;\n  void *replacement;\n};\n\n/*\n * For each rebinding in rebindings, rebinds references to external, indirect\n * symbols with the specified name to instead point at replacement for each\n * image in the calling process as well as for all future images that are loaded\n * by the process. If rebind_functions is called more than once, the symbols to\n * rebind are added to the existing list of rebindings, and if a given symbol\n * is rebound more than once, the later rebinding will take precedence.\n */\nint rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel);\n\n/*\n * Rebinds as above, but only in the specified image. The header should point\n * to the mach-o header, the slide should be the slide offset. Others as above.\n */\nint rebind_symbols_image(void *header,\n                         intptr_t slide,\n                         struct rebinding rebindings[],\n                         size_t rebindings_nel);\n\n#ifdef __cplusplus\n}\n#endif //__cplusplus\n\n#endif //fishhook_h\n\n"
  }
]