Skip to content

Commit 94dd469

Browse files
committed
More clean-up in ITKSuperBuilder
1 parent ba5e0a4 commit 94dd469

File tree

1 file changed

+84
-60
lines changed

1 file changed

+84
-60
lines changed

Sources/ITKSuperBuilder/src/ITKSuperBuilder.m

Lines changed: 84 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,74 @@
1-
#if __APPLE__
21
#import "ITKSuperBuilder.h"
32

43
@import ObjectiveC.message;
54
@import ObjectiveC.runtime;
65

7-
NS_ASSUME_NONNULL_BEGIN
8-
9-
NSString *const ITKSuperBuilderErrorDomain = @"com.steipete.InterposeKit";
10-
11-
void msgSendSuperTrampoline(void);
12-
void msgSendSuperStretTrampoline(void);
13-
146
#define let const __auto_type
157
#define var __auto_type
168

17-
static IMP ITKGetTrampolineForTypeEncoding(__unused const char *typeEncoding) {
18-
BOOL requiresStructDispatch = NO;
19-
#if defined (__arm64__)
20-
// arm64 doesn't use stret dispatch. Yay!
21-
#elif defined (__x86_64__)
22-
// On x86_64, stret dispatch is ~used whenever return type doesn't fit into two registers
23-
//
24-
// http://www.sealiesoftware.com/blog/archive/2008/10/30/objc_explain_objc_msgSend_stret.html
25-
// x86_64 is more complicated, including rules for returning floating-point struct fields in FPU registers, and ppc64's rules and exceptions will make your head spin. The gory details are documented in the Mac OS X ABI Guide, though as usual if the documentation and the compiler disagree then the documentation is wrong.
26-
NSUInteger returnTypeActualSize = 0;
27-
NSGetSizeAndAlignment(typeEncoding, &returnTypeActualSize, NULL);
28-
requiresStructDispatch = returnTypeActualSize > (sizeof(void *) * 2);
29-
#else
30-
// Unknown architecture
31-
// https://devblogs.microsoft.com/xamarin/apple-new-processor-architecture/
32-
// watchOS uses arm64_32 since series 4, before armv7k. watch Simulator uses i386.
33-
// See ILP32: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0490a/ar01s01.html
34-
#endif
9+
NS_ASSUME_NONNULL_BEGIN
3510

36-
return requiresStructDispatch ? (IMP)msgSendSuperStretTrampoline : (IMP)msgSendSuperTrampoline;
37-
}
11+
NSString *const ITKSuperBuilderErrorDomain = @"com.steipete.InterposeKit";
3812

39-
#define ERROR_AND_RETURN(CODE, STRING)\
40-
if (error) { *error = [NSError errorWithDomain:ITKSuperBuilderErrorDomain code:CODE userInfo:@{NSLocalizedDescriptionKey: STRING}];} return NO;
13+
static IMP ITKGetTrampolineForTypeEncoding(__unused const char *typeEncoding);
14+
static BOOL ITKMethodIsSuperTrampoline(Method method);
4115

4216
@implementation ITKSuperBuilder
4317

44-
+ (BOOL)isSuperTrampolineForClass:(Class)originalClass selector:(SEL)selector {
45-
let method = class_getInstanceMethod(originalClass, selector);
46-
return ITKMethodIsSuperTrampoline(method);
47-
}
48-
49-
+ (BOOL)addSuperInstanceMethodToClass:(Class)originalClass selector:(SEL)selector error:(NSError **)error {
18+
+ (BOOL)addSuperInstanceMethodToClass:(Class)originalClass
19+
selector:(SEL)selector
20+
error:(NSError **)error
21+
{
5022
// Check that class has a superclass
51-
let superClass = class_getSuperclass(originalClass);
52-
if (superClass == nil) {
53-
let msg = [NSString stringWithFormat:@"Unable to find superclass for %@", NSStringFromClass(originalClass)];
54-
ERROR_AND_RETURN(SuperBuilderErrorCodeNoSuperClass, msg)
23+
let superclass = class_getSuperclass(originalClass);
24+
25+
if (superclass == nil) {
26+
if (error) {
27+
let message = [NSString stringWithFormat:@"Unable to find superclass for %@", NSStringFromClass(originalClass)];
28+
*error = [NSError errorWithDomain:ITKSuperBuilderErrorDomain
29+
code:SuperBuilderErrorCodeNoSuperClass
30+
userInfo:@{NSLocalizedDescriptionKey: message}];
31+
return NO;
32+
}
5533
}
56-
34+
5735
// Fetch method called with super
58-
let method = class_getInstanceMethod(superClass, selector);
36+
let method = class_getInstanceMethod(superclass, selector);
5937
if (method == NULL) {
60-
let msg = [NSString stringWithFormat:@"No dynamically dispatched method with selector %@ is available on any of the superclasses of %@", NSStringFromSelector(selector), NSStringFromClass(originalClass)];
61-
ERROR_AND_RETURN(SuperBuilderErrorCodeNoDynamicallyDispatchedMethodAvailable, msg)
38+
if (error) {
39+
let message = [NSString stringWithFormat:@"No dynamically dispatched method with selector %@ is available on any of the superclasses of %@", NSStringFromSelector(selector), NSStringFromClass(originalClass)];
40+
*error = [NSError errorWithDomain:ITKSuperBuilderErrorDomain
41+
code:SuperBuilderErrorCodeNoDynamicallyDispatchedMethodAvailable
42+
userInfo:@{NSLocalizedDescriptionKey: message}];
43+
return NO;
44+
}
6245
}
63-
46+
6447
// Add trampoline
6548
let typeEncoding = method_getTypeEncoding(method);
6649
let trampoline = ITKGetTrampolineForTypeEncoding(typeEncoding);
6750
let methodAdded = class_addMethod(originalClass, selector, trampoline, typeEncoding);
6851
if (!methodAdded) {
69-
let msg = [NSString stringWithFormat:@"Failed to add method for selector %@ to class %@", NSStringFromSelector(selector), NSStringFromClass(originalClass)];
70-
ERROR_AND_RETURN(SuperBuilderErrorCodeFailedToAddMethod, msg)
52+
if (error) {
53+
let message = [NSString stringWithFormat:@"Failed to add method for selector %@ to class %@", NSStringFromSelector(selector), NSStringFromClass(originalClass)];
54+
*error = [NSError errorWithDomain:ITKSuperBuilderErrorDomain
55+
code:SuperBuilderErrorCodeFailedToAddMethod
56+
userInfo:@{NSLocalizedDescriptionKey: message}];
57+
return NO;
58+
}
7159
}
7260
return methodAdded;
7361
}
7462

63+
+ (BOOL)isSuperTrampolineForClass:(Class)originalClass
64+
selector:(SEL)selector
65+
{
66+
let method = class_getInstanceMethod(originalClass, selector);
67+
return ITKMethodIsSuperTrampoline(method);
68+
}
69+
70+
@end
71+
7572
// Control if the trampoline should also push/pop the floating point registers.
7673
// This is slightly slower and not needed for our simple implementation
7774
// However, even if you just use memcpy, you will want to enable this.
@@ -81,12 +78,50 @@ + (BOOL)addSuperInstanceMethodToClass:(Class)originalClass selector:(SEL)selecto
8178
// One thread local per thread should be enough
8279
_Thread_local struct objc_super _threadSuperStorage;
8380

81+
void msgSendSuperTrampoline(void);
82+
83+
#if defined (__x86_64__)
84+
void msgSendSuperStretTrampoline(void);
85+
#endif
86+
87+
static IMP ITKGetTrampolineForTypeEncoding(__unused const char *typeEncoding) {
88+
#if defined (__arm64__)
89+
// arm64 doesn't use stret dispatch. Yay!
90+
return (IMP)msgSendSuperTrampoline;
91+
#elif defined (__x86_64__)
92+
// On x86_64, stret dispatch is ~used whenever return type doesn't fit into two registers
93+
//
94+
// http://www.sealiesoftware.com/blog/archive/2008/10/30/objc_explain_objc_msgSend_stret.html
95+
// x86_64 is more complicated, including rules for returning floating-point struct fields in FPU registers, and ppc64's rules and exceptions will make your head spin. The gory details are documented in the Mac OS X ABI Guide, though as usual if the documentation and the compiler disagree then the documentation is wrong.
96+
NSUInteger returnTypeActualSize = 0;
97+
NSGetSizeAndAlignment(typeEncoding, &returnTypeActualSize, NULL);
98+
BOOL requiresStructDispatch = returnTypeActualSize > (sizeof(void *) * 2);
99+
return requiresStructDispatch ? (IMP)msgSendSuperStretTrampoline : (IMP)msgSendSuperTrampoline;
100+
#else
101+
// Unknown architecture
102+
// https://devblogs.microsoft.com/xamarin/apple-new-processor-architecture/
103+
// watchOS uses arm64_32 since series 4, before armv7k. watch Simulator uses i386.
104+
// See ILP32: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0490a/ar01s01.html
105+
return NO;
106+
#endif
107+
}
108+
84109
static BOOL ITKMethodIsSuperTrampoline(Method method) {
85110
let methodIMP = method_getImplementation(method);
86-
return methodIMP == (IMP)msgSendSuperTrampoline || methodIMP == (IMP)msgSendSuperStretTrampoline;
111+
112+
if (methodIMP == (IMP)msgSendSuperTrampoline) {
113+
return YES;
114+
}
115+
116+
#if defined (__x86_64__)
117+
if (methodIMP == (IMP)msgSendSuperStretTrampoline) {
118+
return YES;
119+
}
120+
#endif
121+
122+
return NO;
87123
}
88124

89-
struct objc_super *ITKReturnThreadSuper(__unsafe_unretained id obj, SEL _cmd);
90125
struct objc_super *ITKReturnThreadSuper(__unsafe_unretained id obj, SEL _cmd) {
91126
/**
92127
Assume you have a class hierarchy made of four classes `Level1` <- `Level2` <- `Level3` <- `Level4`,
@@ -120,8 +155,6 @@ static BOOL ITKMethodIsSuperTrampoline(Method method) {
120155
return _super;
121156
}
122157

123-
@end
124-
125158
/**
126159
Inline assembly is used to perfectly forward all parameters to objc_msgSendSuper,
127160
while also looking up the target on-the-fly.
@@ -193,9 +226,6 @@ asm volatile (
193226
: : : "x0", "x1");
194227
}
195228

196-
// arm64 doesn't use _stret variants.
197-
void msgSendSuperStretTrampoline(void) {}
198-
199229
#elif defined(__x86_64__)
200230

201231
__attribute__((__naked__))
@@ -265,7 +295,6 @@ asm volatile (
265295
: : : "rsi", "rdi");
266296
}
267297

268-
269298
__attribute__((__naked__))
270299
void msgSendSuperStretTrampoline(void) {
271300
asm volatile (
@@ -312,11 +341,6 @@ asm volatile (
312341
: : : "rsi", "rdi");
313342
}
314343

315-
#else
316-
// Unknown architecture - time to write some assembly :)
317-
void msgSendSuperTrampoline(void) {}
318-
void msgSendSuperStretTrampoline(void) {}
319344
#endif
320345

321346
NS_ASSUME_NONNULL_END
322-
#endif

0 commit comments

Comments
 (0)