Skip to content

Commit

Permalink
Adds new runtime addition void class_swizzleSelectorWithBlock(Class c…
Browse files Browse the repository at this point in the history
…lass, SEL originalSelector, SEL unusedSelector, id block);.
  • Loading branch information
OliverLetterer committed Sep 3, 2012
1 parent 62baaee commit 99d3dc7
Show file tree
Hide file tree
Showing 10 changed files with 612 additions and 1 deletion.
Expand Up @@ -7,6 +7,12 @@
objects = {

/* Begin PBXBuildFile section */
A7620EDE15F359680016F414 /* CTBlockDescription.h in Headers */ = {isa = PBXBuildFile; fileRef = A7620EDC15F359680016F414 /* CTBlockDescription.h */; };
A7620EDF15F359680016F414 /* CTBlockDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = A7620EDD15F359680016F414 /* CTBlockDescription.m */; };
A7620EE315F360700016F414 /* CTSwizzleBlockImplementation.h in Headers */ = {isa = PBXBuildFile; fileRef = A7620EE115F360700016F414 /* CTSwizzleBlockImplementation.h */; };
A7620EE415F360700016F414 /* CTSwizzleBlockImplementation.m in Sources */ = {isa = PBXBuildFile; fileRef = A7620EE215F360700016F414 /* CTSwizzleBlockImplementation.m */; };
A7620F2915F36CEF0016F414 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A7620F2415F36CDF0016F414 /* libffi.a */; };
A7620F2B15F394430016F414 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A7620F2A15F394430016F414 /* CoreGraphics.framework */; };
A76AC25B154BDACB00B93FEF /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A76AC25A154BDACB00B93FEF /* Foundation.framework */; };
A76AC261154BDACB00B93FEF /* CTObjectiveCRuntimeAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = A76AC260154BDACB00B93FEF /* CTObjectiveCRuntimeAdditions.m */; };
A76AC269154BDACC00B93FEF /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A76AC268154BDACC00B93FEF /* SenTestingKit.framework */; };
Expand All @@ -19,6 +25,27 @@
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
A7620F2315F36CDF0016F414 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = A7620F1B15F36CDF0016F414 /* libffi.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = F6F980BA147386130008F121;
remoteInfo = "libffi iOS";
};
A7620F2515F36CDF0016F414 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = A7620F1B15F36CDF0016F414 /* libffi.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 6C43CB3D1534E9D100162364;
remoteInfo = "libffi OS X";
};
A7620F2715F36CE70016F414 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = A7620F1B15F36CDF0016F414 /* libffi.xcodeproj */;
proxyType = 1;
remoteGlobalIDString = F6F980B9147386130008F121;
remoteInfo = "libffi iOS";
};
A76AC26D154BDACC00B93FEF /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = A76AC24E154BDACB00B93FEF /* Project object */;
Expand All @@ -29,6 +56,12 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
A7620EDC15F359680016F414 /* CTBlockDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CTBlockDescription.h; sourceTree = "<group>"; };
A7620EDD15F359680016F414 /* CTBlockDescription.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CTBlockDescription.m; sourceTree = "<group>"; };
A7620EE115F360700016F414 /* CTSwizzleBlockImplementation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CTSwizzleBlockImplementation.h; sourceTree = "<group>"; };
A7620EE215F360700016F414 /* CTSwizzleBlockImplementation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CTSwizzleBlockImplementation.m; sourceTree = "<group>"; };
A7620F1B15F36CDF0016F414 /* libffi.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = libffi.xcodeproj; path = ../../libffi/libffi.xcodeproj; sourceTree = "<group>"; };
A7620F2A15F394430016F414 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
A76AC257154BDACB00B93FEF /* libCTObjectiveCRuntimeAdditions.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libCTObjectiveCRuntimeAdditions.a; sourceTree = BUILT_PRODUCTS_DIR; };
A76AC25A154BDACB00B93FEF /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
A76AC25E154BDACB00B93FEF /* CTObjectiveCRuntimeAdditions-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CTObjectiveCRuntimeAdditions-Prefix.pch"; sourceTree = "<group>"; };
Expand All @@ -51,6 +84,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
A7620F2915F36CEF0016F414 /* libffi.a in Frameworks */,
A76AC25B154BDACB00B93FEF /* Foundation.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand All @@ -59,6 +93,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
A7620F2B15F394430016F414 /* CoreGraphics.framework in Frameworks */,
A76AC269154BDACC00B93FEF /* SenTestingKit.framework in Frameworks */,
A76AC26C154BDACC00B93FEF /* Foundation.framework in Frameworks */,
A76AC26F154BDACC00B93FEF /* libCTObjectiveCRuntimeAdditions.a in Frameworks */,
Expand All @@ -68,6 +103,23 @@
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
A7620EEF15F363AA0016F414 /* libffi */ = {
isa = PBXGroup;
children = (
A7620F1B15F36CDF0016F414 /* libffi.xcodeproj */,
);
name = libffi;
sourceTree = "<group>";
};
A7620F1C15F36CDF0016F414 /* Products */ = {
isa = PBXGroup;
children = (
A7620F2415F36CDF0016F414 /* libffi.a */,
A7620F2615F36CDF0016F414 /* libffi.a */,
);
name = Products;
sourceTree = "<group>";
};
A76AC24C154BDACB00B93FEF = {
isa = PBXGroup;
children = (
Expand All @@ -90,6 +142,7 @@
A76AC259154BDACB00B93FEF /* Frameworks */ = {
isa = PBXGroup;
children = (
A7620F2A15F394430016F414 /* CoreGraphics.framework */,
A76AC25A154BDACB00B93FEF /* Foundation.framework */,
A76AC268154BDACC00B93FEF /* SenTestingKit.framework */,
);
Expand All @@ -99,6 +152,11 @@
A76AC25C154BDACB00B93FEF /* CTObjectiveCRuntimeAdditions */ = {
isa = PBXGroup;
children = (
A7620EEF15F363AA0016F414 /* libffi */,
A7620EE115F360700016F414 /* CTSwizzleBlockImplementation.h */,
A7620EE215F360700016F414 /* CTSwizzleBlockImplementation.m */,
A7620EDC15F359680016F414 /* CTBlockDescription.h */,
A7620EDD15F359680016F414 /* CTBlockDescription.m */,
A76AC25F154BDACB00B93FEF /* CTObjectiveCRuntimeAdditions.h */,
A76AC260154BDACB00B93FEF /* CTObjectiveCRuntimeAdditions.m */,
A76AC25D154BDACB00B93FEF /* Supporting Files */,
Expand Down Expand Up @@ -144,6 +202,8 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
A7620EDE15F359680016F414 /* CTBlockDescription.h in Headers */,
A7620EE315F360700016F414 /* CTSwizzleBlockImplementation.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -161,6 +221,7 @@
buildRules = (
);
dependencies = (
A7620F2815F36CE70016F414 /* PBXTargetDependency */,
);
name = CTObjectiveCRuntimeAdditions;
productName = CTObjectiveCRuntimeAdditions;
Expand Down Expand Up @@ -206,6 +267,12 @@
mainGroup = A76AC24C154BDACB00B93FEF;
productRefGroup = A76AC258154BDACB00B93FEF /* Products */;
projectDirPath = "";
projectReferences = (
{
ProductGroup = A7620F1C15F36CDF0016F414 /* Products */;
ProjectRef = A7620F1B15F36CDF0016F414 /* libffi.xcodeproj */;
},
);
projectRoot = "";
targets = (
A76AC256154BDACB00B93FEF /* CTObjectiveCRuntimeAdditions */,
Expand All @@ -214,6 +281,23 @@
};
/* End PBXProject section */

/* Begin PBXReferenceProxy section */
A7620F2415F36CDF0016F414 /* libffi.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libffi.a;
remoteRef = A7620F2315F36CDF0016F414 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
A7620F2615F36CDF0016F414 /* libffi.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libffi.a;
remoteRef = A7620F2515F36CDF0016F414 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
/* End PBXReferenceProxy section */

/* Begin PBXResourcesBuildPhase section */
A76AC264154BDACC00B93FEF /* Resources */ = {
isa = PBXResourcesBuildPhase;
Expand Down Expand Up @@ -247,6 +331,8 @@
buildActionMask = 2147483647;
files = (
A76AC261154BDACB00B93FEF /* CTObjectiveCRuntimeAdditions.m in Sources */,
A7620EDF15F359680016F414 /* CTBlockDescription.m in Sources */,
A7620EE415F360700016F414 /* CTSwizzleBlockImplementation.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -263,6 +349,11 @@
/* End PBXSourcesBuildPhase section */

/* Begin PBXTargetDependency section */
A7620F2815F36CE70016F414 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = "libffi iOS";
targetProxy = A7620F2715F36CE70016F414 /* PBXContainerItemProxy */;
};
A76AC26E154BDACC00B93FEF /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = A76AC256154BDACB00B93FEF /* CTObjectiveCRuntimeAdditions */;
Expand Down Expand Up @@ -305,6 +396,10 @@
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/../libffi/ios/include\"",
);
IPHONEOS_DEPLOYMENT_TARGET = 4.0;
SDKROOT = iphoneos;
};
Expand All @@ -326,6 +421,10 @@
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/../libffi/ios/include\"",
);
IPHONEOS_DEPLOYMENT_TARGET = 4.0;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
Expand Down
@@ -0,0 +1,48 @@
//
// CTBlockDescription.h
// CTBlockDescription
//
// Created by Oliver Letterer on 01.09.12.
// Copyright (c) 2012 olettere. All rights reserved.
//

struct CTBlockLiteral {
void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock
int flags;
int reserved;
void (*invoke)(void *, ...);
struct block_descriptor {
unsigned long int reserved; // NULL
unsigned long int size; // sizeof(struct Block_literal_1)
// optional helper functions
void (*copy_helper)(void *dst, void *src); // IFF (1<<25)
void (*dispose_helper)(void *src); // IFF (1<<25)
// required ABI.2010.3.16
const char *signature; // IFF (1<<30)
} *descriptor;
// imported variables
};

enum {
CTBlockDescriptionFlagsHasCopyDispose = (1 << 25),
CTBlockDescriptionFlagsHasCtor = (1 << 26), // helpers have C++ code
CTBlockDescriptionFlagsIsGlobal = (1 << 28),
CTBlockDescriptionFlagsHasStret = (1 << 29), // IFF BLOCK_HAS_SIGNATURE
CTBlockDescriptionFlagsHasSignature = (1 << 30)
};
typedef int CTBlockDescriptionFlags;



@interface CTBlockDescription : NSObject

@property (nonatomic, readonly) CTBlockDescriptionFlags flags;
@property (nonatomic, readonly) NSMethodSignature *blockSignature;
@property (nonatomic, readonly) unsigned long int size;
@property (nonatomic, readonly) id block;

- (id)initWithBlock:(id)block;

- (BOOL)isCompatibleForBlockSwizzlingWithMethodSignature:(NSMethodSignature *)methodSignature;

@end
@@ -0,0 +1,74 @@
//
// CTBlockDescription.m
// CTBlockDescription
//
// Created by Oliver Letterer on 01.09.12.
// Copyright (c) 2012 olettere. All rights reserved.
//

#import "CTBlockDescription.h"

@implementation CTBlockDescription

- (id)initWithBlock:(id)block
{
if (self = [super init]) {
_block = block;

struct CTBlockLiteral *blockRef = (__bridge struct CTBlockLiteral *)block;
_flags = blockRef->flags;
_size = blockRef->descriptor->size;

if (_flags & CTBlockDescriptionFlagsHasSignature) {
void *signatureLocation = blockRef->descriptor;
signatureLocation += sizeof(unsigned long int);
signatureLocation += sizeof(unsigned long int);

if (_flags & CTBlockDescriptionFlagsHasCopyDispose) {
signatureLocation += sizeof(void(*)(void *dst, void *src));
signatureLocation += sizeof(void (*)(void *src));
}

const char *signature = (*(const char **)signatureLocation);
_blockSignature = [NSMethodSignature signatureWithObjCTypes:signature];
}
}
return self;
}

- (BOOL)isCompatibleForBlockSwizzlingWithMethodSignature:(NSMethodSignature *)methodSignature
{
if (_blockSignature.numberOfArguments != methodSignature.numberOfArguments + 1) {
return NO;
}

if (strcmp(_blockSignature.methodReturnType, methodSignature.methodReturnType) != 0) {
return NO;
}

for (int i = 0; i < methodSignature.numberOfArguments; i++) {
if (i == 1) {
// SEL in method, IMP in block
if (strcmp([methodSignature getArgumentTypeAtIndex:i], ":") != 0) {
return NO;
}

if (strcmp([_blockSignature getArgumentTypeAtIndex:i + 1], "^?") != 0) {
return NO;
}
} else {
if (strcmp([methodSignature getArgumentTypeAtIndex:i], [_blockSignature getArgumentTypeAtIndex:i + 1]) != 0) {
return NO;
}
}
}

return YES;
}

- (NSString *)description
{
return [NSString stringWithFormat:@"%@: %@", [super description], _blockSignature.description];
}

@end
Expand Up @@ -28,3 +28,9 @@ void class_enumerateMethodList(Class class, CTMethodEnumertor enumerator);
@return A subclass of class which passes test.
*/
Class class_subclassPassingTest(Class class, CTClassTest test);

/**
@abstract Swizzles originalSelector with block and places original implementation in unusedSelector.
@warning if originalSelector's argument list is (id self, SEL _cmd, ...), then block's argument list must be (id self, IMP originalImplemenation, ...)
*/
void class_swizzleSelectorWithBlock(Class class, SEL originalSelector, SEL unusedSelector, id block);
Expand Up @@ -7,6 +7,17 @@
//

#import "CTObjectiveCRuntimeAdditions.h"
#import "CTBlockDescription.h"
#import "CTSwizzleBlockImplementation.h"

static NSMutableArray *CTBlockImplementations;

__attribute((constructor))void CTObjectiveCRuntimeAdditionsInitialization(void)
{
@autoreleasepool {
CTBlockImplementations = [NSMutableArray array];
}
}

void class_swizzleSelector(Class class, SEL originalSelector, SEL newSelector)
{
Expand Down Expand Up @@ -110,3 +121,25 @@ Class class_subclassPassingTest(Class class, CTClassTest test)

return testPassingClass;
}

void class_swizzleSelectorWithBlock(Class class, SEL originalSelector, SEL unusedSelector, id block)
{
NSCAssert(block != nil, @"block cannot be nil");

Method originalMethod = class_getInstanceMethod(class, originalSelector);
NSMethodSignature *methodSignature = [NSMethodSignature signatureWithObjCTypes:method_getTypeEncoding(originalMethod)];

CTBlockDescription *blockDescription = [[CTBlockDescription alloc] initWithBlock:block];
NSCAssert([blockDescription isCompatibleForBlockSwizzlingWithMethodSignature:methodSignature], @"block is not compatible for swizzling selector %@ of class %@", NSStringFromSelector(originalSelector), NSStringFromClass(class));

CTSwizzleBlockImplementation *blockImplementation = [[CTSwizzleBlockImplementation alloc] initWithBlock:block
methodSignature:methodSignature
swizzledSelector:unusedSelector
originalClass:class];
[CTBlockImplementations addObject:blockImplementation];

BOOL success = class_addMethod(class, unusedSelector, blockImplementation.implementation, method_getTypeEncoding(originalMethod));
NSCAssert(success, @"An implementation for selector %@ of class %@ already exists", NSStringFromSelector(unusedSelector), NSStringFromClass(class));

class_swizzleSelector(class, originalSelector, unusedSelector);
}

0 comments on commit 99d3dc7

Please sign in to comment.