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