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+
84109static 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);
90125struct 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__))
270299void 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
321346NS_ASSUME_NONNULL_END
322- #endif
0 commit comments