forked from RetVal/objc-runtime
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcopyMethodList.m
155 lines (131 loc) · 4.52 KB
/
copyMethodList.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// TEST_CFLAGS -Wl,-no_objc_category_merging
#include "test.h"
#include "testroot.i"
#include <malloc/malloc.h>
#include <objc/runtime.h>
@interface SuperMethods : TestRoot { } @end
@implementation SuperMethods
+(BOOL)SuperMethodClass { return NO; }
+(BOOL)SuperMethodClass2 { return NO; }
-(BOOL)SuperMethodInstance { return NO; }
-(BOOL)SuperMethodInstance2 { return NO; }
@end
@interface SubMethods : SuperMethods { } @end
@implementation SubMethods
+(BOOL)SubMethodClass { return NO; }
+(BOOL)SubMethodClass2 { return NO; }
-(BOOL)SubMethodInstance { return NO; }
-(BOOL)SubMethodInstance2 { return NO; }
@end
@interface SuperMethods (Category) @end
@implementation SuperMethods (Category)
+(BOOL)SuperMethodClass { return YES; }
+(BOOL)SuperMethodClass2 { return YES; }
-(BOOL)SuperMethodInstance { return YES; }
-(BOOL)SuperMethodInstance2 { return YES; }
@end
@interface SubMethods (Category) @end
@implementation SubMethods (Category)
+(BOOL)SubMethodClass { return YES; }
+(BOOL)SubMethodClass2 { return YES; }
-(BOOL)SubMethodInstance { return YES; }
-(BOOL)SubMethodInstance2 { return YES; }
@end
@interface FourMethods : TestRoot @end
@implementation FourMethods
-(void)one { }
-(void)two { }
-(void)three { }
-(void)four { }
@end
@interface NoMethods : TestRoot @end
@implementation NoMethods @end
static void checkReplacement(Method *list, const char *name)
{
Method first = NULL, second = NULL;
SEL sel = sel_registerName(name);
int i;
testassert(list);
// Find the methods. There should be two.
for (i = 0; list[i]; i++) {
if (method_getName(list[i]) == sel) {
if (!first) first = list[i];
else if (!second) second = list[i];
else testassert(0);
}
}
// Call the methods. The first should be the category (returns YES).
BOOL isCat;
isCat = ((BOOL(*)(id, Method))method_invoke)(NULL, first);
testassert(isCat);
isCat = ((BOOL(*)(id, Method))method_invoke)(NULL, second);
testassert(! isCat);
}
int main()
{
// Class SubMethods has not yet been touched, so runtime must attach
// the lazy categories
Method *methods;
unsigned int count;
Class cls;
cls = objc_getClass("SubMethods");
testassert(cls);
testprintf("calling class_copyMethodList(SubMethods) (should be unmethodized)\n");
count = 100;
methods = class_copyMethodList(cls, &count);
testassert(methods);
testassert(count == 4);
// methods[] should be null-terminated
testassert(methods[4] == NULL);
// Class and category methods may be mixed in the method list thanks
// to linker / shared cache sorting, but a category's replacement should
// always precede the class's implementation.
checkReplacement(methods, "SubMethodInstance");
checkReplacement(methods, "SubMethodInstance2");
free(methods);
testprintf("calling class_copyMethodList(SubMethods(meta)) (should be unmethodized)\n");
count = 100;
methods = class_copyMethodList(object_getClass(cls), &count);
testassert(methods);
testassert(count == 4);
// methods[] should be null-terminated
testassert(methods[4] == NULL);
// Class and category methods may be mixed in the method list thanks
// to linker / shared cache sorting, but a category's replacement should
// always precede the class's implementation.
checkReplacement(methods, "SubMethodClass");
checkReplacement(methods, "SubMethodClass2");
free(methods);
// Check null-termination - this method list block would be 16 bytes
// if it weren't for the terminator
count = 100;
cls = objc_getClass("FourMethods");
methods = class_copyMethodList(cls, &count);
testassert(methods);
testassert(count == 4);
testassert(malloc_size(methods) >= (4+1) * sizeof(Method));
testassert(methods[3] != NULL);
testassert(methods[4] == NULL);
free(methods);
// Check NULL count parameter
methods = class_copyMethodList(cls, NULL);
testassert(methods);
testassert(methods[4] == NULL);
testassert(methods[3] != NULL);
free(methods);
// Check NULL class parameter
count = 100;
methods = class_copyMethodList(NULL, &count);
testassert(!methods);
testassert(count == 0);
// Check NULL class and count
methods = class_copyMethodList(NULL, NULL);
testassert(!methods);
// Check class with no methods
count = 100;
cls = objc_getClass("NoMethods");
methods = class_copyMethodList(cls, &count);
testassert(!methods);
testassert(count == 0);
succeed(__FILE__);
}