1// Needed with glibc to expose vasprintf
2#define _GNU_SOURCE
3#include <time.h>
4#include <stdio.h>
5#include <assert.h>
6#include <string.h>
7#include <stdarg.h>
8#include "../objc/runtime.h"
9#include "../objc/hooks.h"
10
11//#define assert(x) if (!(x)) { printf("Failed %d\n", __LINE__); }
12
13
14typedef struct { int a,b,c,d,e; } s;
15@interface Fake
16- (int)izero;
17- (float)fzero;
18- (double)dzero;
19- (long double)ldzero;
20@end
21
22Class TestCls;
23#ifdef __has_attribute
24#if __has_attribute(objc_root_class)
25__attribute__((objc_root_class))
26#endif
27#endif
28@interface MsgTest { id isa; } @end
29@interface MsgTest (Dynamic)
30+ (void)manyArgs: (int)a0
31         : (int) a1
32         : (int) a2
33         : (int) a3
34         : (int) a4
35         : (int) a5
36         : (int) a6
37         : (int) a7
38         : (int) a8
39         : (int) a9
40         : (int) a10
41         : (float) f0
42         : (float) f1
43         : (float) f2
44         : (float) f3
45         : (float) f4
46         : (float) f5
47         : (float) f6
48         : (float) f7
49         : (float) f8
50         : (float) f9
51         : (float) f10;
52@end
53@implementation MsgTest
54- foo
55{
56	assert((id)1 == self);
57	assert(strcmp("foo", sel_getName(_cmd)) == 0);
58	return (id)0x42;
59}
60+ foo
61{
62	assert(TestCls == self);
63	assert(strcmp("foo", sel_getName(_cmd)) == 0);
64	return (id)0x42;
65}
66+ (s)sret
67{
68	assert(TestCls == self);
69	assert(strcmp("sret", sel_getName(_cmd)) == 0);
70	s st = {1,2,3,4,5};
71	return st;
72}
73- (s)sret
74{
75	assert((id)3 == self);
76	assert(strcmp("sret", sel_getName(_cmd)) == 0);
77	s st = {1,2,3,4,5};
78	return st;
79}
80+ (void)printf: (const char*)str, ...
81{
82	va_list ap;
83	char s[100];
84
85	va_start(ap, str);
86
87	vsnprintf(s, 100, str, ap);
88	va_end(ap);
89	assert(strcmp(s, "Format string 42 42.000000\n") ==0);
90}
91+ (void)initialize
92{
93	[self printf: "Format %s %d %f%c", "string", 42, 42.0, '\n'];
94	@throw self;
95}
96+ nothing { return 0; }
97@end
98
99int forwardcalls;
100void fwdMany(id self,
101             SEL _cmd,
102             int a0,
103             int a1,
104             int a2,
105             int a3,
106             int a4,
107             int a5,
108             int a6,
109             int a7,
110             int a8,
111             int a9,
112             int a10,
113             float f0,
114             float f1,
115             float f2,
116             float f3,
117             float f4,
118             float f5,
119             float f6,
120             float f7,
121             float f8,
122             float f9,
123             float f10)
124{
125	forwardcalls++;
126	assert(self == objc_getClass("MsgTest"));
127	if (sel_isEqual(_cmd, sel_registerName("manyArgs:::::::::::::::::::::")))
128	assert(a0 == 0);
129	assert(a1 == 1);
130	assert(a2 == 2);
131	assert(a3 == 3);
132	assert(a4 == 4);
133	assert(a5 == 5);
134	assert(a6 == 6);
135	assert(a7 == 7);
136	assert(a8 == 8);
137	assert(a9 == 9);
138	assert(a10 == 10);
139	assert(f0 == 0);
140	assert(f1 == 1);
141	assert(f2 == 2);
142	assert(f3 == 3);
143	assert(f4 == 4);
144	assert(f5 == 5);
145	assert(f6 == 6);
146	assert(f7 == 7);
147	assert(f8 == 8);
148	assert(f9 == 9);
149	assert(f10 == 10);
150}
151
152void fwd(void)
153{
154	forwardcalls++;
155}
156
157IMP forward(id o, SEL s)
158{
159	assert(o == objc_getClass("MsgTest"));
160	if (sel_isEqual(s, sel_registerName("missing")))
161	{
162		return (IMP)fwd;
163	}
164	return (IMP)fwdMany;
165}
166
167static struct objc_slot slot;
168struct objc_slot *forward_slot(id o, SEL s)
169{
170	slot.method = (IMP)fwd;
171	return &slot;
172}
173
174
175
176int main(void)
177{
178	__objc_msg_forward2 = forward;
179	__objc_msg_forward3 = forward_slot;
180	TestCls = objc_getClass("MsgTest");
181	int exceptionThrown = 0;
182	@try {
183		objc_msgSend(TestCls, @selector(foo));
184	} @catch (id e)
185	{
186		assert((TestCls == e) && "Exceptions propagate out of +initialize");
187		exceptionThrown = 1;
188	}
189	assert(exceptionThrown && "An exception was thrown");
190	assert((id)0x42 == objc_msgSend(TestCls, @selector(foo)));
191	objc_msgSend(TestCls, @selector(nothing));
192	objc_msgSend(TestCls, @selector(missing));
193	assert(forwardcalls == 1);
194	assert(0 == objc_msgSend(0, @selector(nothing)));
195	id a = objc_msgSend(objc_getClass("MsgTest"), @selector(foo));
196	assert((id)0x42 == a);
197	a = objc_msgSend(TestCls, @selector(foo));
198	assert((id)0x42 == a);
199	assert(objc_registerSmallObjectClass_np(objc_getClass("MsgTest"), 1));
200	a = objc_msgSend((id)01, @selector(foo));
201	assert((id)0x42 == a);
202	s ret = ((s(*)(id, SEL))objc_msgSend_stret)(TestCls, @selector(sret));
203	assert(ret.a == 1);
204	assert(ret.b == 2);
205	assert(ret.c == 3);
206	assert(ret.d == 4);
207	assert(ret.e == 5);
208	if (sizeof(id) == 8)
209	{
210		assert(objc_registerSmallObjectClass_np(objc_getClass("MsgTest"), 3));
211		ret = ((s(*)(id, SEL))objc_msgSend_stret)((id)3, @selector(sret));
212		assert(ret.a == 1);
213		assert(ret.b == 2);
214		assert(ret.c == 3);
215		assert(ret.d == 4);
216		assert(ret.e == 5);
217	}
218	Fake *f = nil;
219	assert(0 == [f izero]);
220	assert(0 == [f dzero]);
221	assert(0 == [f ldzero]);
222	assert(0 == [f fzero]);
223	[TestCls manyArgs: 0 : 1 : 2 : 3: 4: 5: 6: 7: 8: 9: 10 : 0 : 1 : 2 : 3 : 4 : 5 : 6 : 7 : 8 : 9 : 10];
224	assert(forwardcalls == 2);
225#ifdef BENCHMARK
226	const int iterations = 1000000000;
227	double times[3];
228	clock_t c1, c2;
229	c1 = clock();
230	for (int i=0 ; i<iterations ; i++)
231	{
232		[TestCls nothing];
233	}
234	c2 = clock();
235	times[0] = ((double)c2 - (double)c1) / (double)CLOCKS_PER_SEC;
236	fprintf(stderr, "Traditional message send took %f seconds. \n", times[0]);
237	c1 = clock();
238	for (int i=0 ; i<iterations ; i++)
239	{
240		objc_msgSend(TestCls, @selector(nothing));
241	}
242	c2 = clock();
243	times[1] = ((double)c2 - (double)c1) / (double)CLOCKS_PER_SEC;
244	fprintf(stderr, "objc_msgSend() message send took %f seconds. \n", times[1]);
245	IMP nothing = objc_msg_lookup(TestCls, @selector(nothing));
246	c1 = clock();
247	for (int i=0 ; i<iterations ; i++)
248	{
249		nothing(TestCls, @selector(nothing));
250	}
251	c2 = clock();
252	times[2] = ((double)c2 - (double)c1) / (double)CLOCKS_PER_SEC;
253	fprintf(stderr, "Direct IMP call took %f seconds. \n", times[2]);
254	printf("%f\t%f\t%f\n", times[0], times[1], times[2]);
255#endif
256	return 0;
257}
258