1/* Test the Modern GNU Objective-C Runtime API. 2 3 This is test 'class-meta', covering calling functions starting with 4 'class' but using a meta class as argument. 5 6 Functions that manipulate methods (adding, replacing methods) 7 usually take a meta class argument to manipulate the class methods 8 instead of the instance ones. This is an important part of the API 9 that needs testing. 10 11 Functions that manipulate instances, instance variables, properties 12 and protocols at the moment must take a normal class as argument; 13 calling them with a meta class as argument is of no particular use 14 and generally produces a behavior that is undocumented and/or 15 undefined (and this is true with all runtimes). As in the future 16 this behavior may be defined or documented (for example, if class 17 variables are implemented as instance variables of meta classes) we 18 avoid testing it for compatibility with future runtimes. */ 19 20/* { dg-do run } */ 21/* { dg-skip-if "No API#2 pre-Darwin9" { *-*-darwin[5-8]* } { "-fnext-runtime" } { "" } } */ 22/* { dg-xfail-run-if "Needs OBJC2 ABI" { *-*-darwin* && { lp64 && { ! objc2 } } } { "-fnext-runtime" } { "" } } */ 23 24/* To get the modern GNU Objective-C Runtime API, you include 25 objc/runtime.h. */ 26#include <objc/runtime.h> 27#include <stdlib.h> 28#include <iostream> 29#include <cstring> 30 31@interface MyRootClass 32{ Class isa; } 33+ alloc; 34- init; 35+ initialize; 36@end 37 38@implementation MyRootClass 39+ alloc { return class_createInstance (self, 0); } 40- init { return self; } 41+ initialize { return self; } 42@end 43 44static id static_variable = nil; 45 46@interface MySubClass : MyRootClass 47+ (void) setVariable: (id)value; 48+ (id) variable; 49@end 50 51@implementation MySubClass 52+ (void) setVariable: (id)value { static_variable = value; } 53+ (id) variable { return static_variable; } 54@end 55 56@interface DifferentClass : MyRootClass 57+ (id) myClass; 58@end 59 60@implementation DifferentClass 61+ (id) myClass { return self; } 62@end 63 64@interface MySubClass (MySelf) 65+ (id) mySelf; 66@end 67 68int main () 69{ 70 /* Functions are tested in alphabetical order. */ 71 72 /* Calling class_addIvar() with a meta class is not documented and 73 (currently) of no use. */ 74 /* std::cout << "Testing class_addIvar ()...\n"; */ 75 76 std::cout << "Testing class_addMethod () on a meta class...\n"; 77 { 78 /* Here we test adding methods to meta classes, ie, adding class methods. */ 79 Class new_class = objc_allocateClassPair (objc_getClass ("MyRootClass"), "MySubClass2", 0); 80 Method method1 = class_getInstanceMethod (objc_getMetaClass ("MySubClass"), @selector (setVariable:)); 81 Method method2 = class_getInstanceMethod (objc_getMetaClass ("MySubClass"), @selector (variable)); 82 83 if (new_class == Nil) 84 abort (); 85 86 if (! class_addMethod (object_getClass (new_class), @selector (setVariable:), method_getImplementation (method1), 87 method_getTypeEncoding (method1))) 88 abort (); 89 90 if (! class_addMethod (object_getClass (new_class), @selector (variable), method_getImplementation (method2), 91 method_getTypeEncoding (method2))) 92 abort (); 93 94 /* Test that if the method already exists in the class, 95 class_addMethod() returns NO. */ 96 if (class_addMethod (object_getClass (new_class), @selector (variable), method_getImplementation (method2), 97 method_getTypeEncoding (method2))) 98 abort (); 99 100 objc_registerClassPair (new_class); 101 102 /* Now, MySubClass2 is basically the same as MySubClass! We'll 103 use the +variable and +setVariable: methods on it. */ 104 { 105 Class c = objc_getClass ("MySubClass2"); 106 id o = [[MyRootClass alloc] init]; 107 108 [c setVariable: o]; 109 110 if ([c variable] != o) 111 abort (); 112 } 113 114 /* Now, try that if you take an existing class and try to add an 115 already existing method, class_addMethod returns NO. This is 116 subtly different from before, when 'new_class' was still in 117 construction. Now it's a real class and the libobjc internals 118 differ between the two cases. */ 119 if (class_addMethod (object_getClass (new_class), @selector (variable), method_getImplementation (method2), 120 method_getTypeEncoding (method2))) 121 abort (); 122 } 123 124 /* Calling class_addProtocol() on a meta class is not documented and 125 (currently) of no use. */ 126 /* std::cout << "Testing class_addProtocol () on a meta class...\n"; */ 127 128 /* Calling class_conformsToProtocol() on a meta class is not 129 documented and (currently) of no use. */ 130 /* std::cout << "Testing class_conformsToProtocol () on a meta class...\n"; */ 131 132 /* Calling class_copyIvarList() on a meta class is not documented 133 and (currently) of no use. */ 134 /* std::cout << "Testing class_copyIvarList () on a meta class...\n"; */ 135 136 std::cout << "Testing class_copyMethodList () on a meta class...\n"; 137 { 138 /* Test that you can copy the method list of a meta class. They 139 are the class methods of the class. */ 140 unsigned int count; 141 Method * list = class_copyMethodList (objc_getMetaClass ("MySubClass"), &count); 142 143 if (count != 2) 144 abort (); 145 146 if (! ((std::strcmp (sel_getName (method_getName (list[0])), "variable") == 0 147 && std::strcmp (sel_getName (method_getName (list[1])), "setVariable:") == 0) 148 || (std::strcmp (sel_getName (method_getName (list[0])), "setVariable:") == 0 149 && std::strcmp (sel_getName (method_getName (list[1])), "variable") == 0))) 150 abort (); 151 152 if (list[2] != NULL) 153 abort (); 154 } 155 156 /* Calling class_copyPropertyList() on a meta class is not 157 documented and (currently) of no use. */ 158 /* std::cout << "Testing class_copyPropertyList () on a meta class...\n"; */ 159 160 /* Calling class_copyProtocolList() on a meta class is not 161 documented and (currently) of no use. */ 162 /* std::cout << "Testing class_copyProtocolList () on a meta class...\n"; */ 163 164 /* Calling class_createInstance() on a meta class is not documented 165 and (currently) of no use. */ 166 /* std::cout << "Testing class_createInstance () on a meta class...\n"; */ 167 168 /* Calling class_getClassMethod () on a meta class is not documented 169 and (currently) of no use. */ 170 /* std::cout << "Testing class_getClassMethod () on a meta class...\n"; */ 171 172 /* Calling class_getClassVariable () on a meta class is not 173 documented and (currently) of no use. */ 174 /* std::cout << "Testing class_getClassVariable () on a meta class ...\n"; */ 175 176 std::cout << "Testing class_getInstanceMethod () on a meta class...\n"; 177 { 178 /* The instance method of a meta class is the class method with 179 the same name of the class. */ 180 Method method_1 = class_getInstanceMethod (objc_getMetaClass ("MySubClass"), 181 @selector(variable)); 182 Method method_2 = class_getClassMethod (objc_getClass ("MySubClass"), 183 @selector(variable)); 184 185 if (method_1 == NULL || method_2 == NULL) 186 abort (); 187 188 if (method_1 != method_2) 189 abort (); 190 191 if (std::strcmp (sel_getName (method_getName (method_1)), "variable") != 0) 192 abort (); 193 } 194 195 /* Calling class_getInstanceSize() with a meta class is not 196 documented and (currently) of no use. */ 197 /* std::cout << "Testing class_getInstanceSize () on a meta class...\n"; */ 198 199 /* Calling class_getInstanceVariable() with a meta class is not 200 documented and (currently) of no use. */ 201 /* std::cout << "Testing class_getInstanceVariable () on a meta class...\n"; */ 202 203 /* Calling class_getIvarLayout() with a meta class is not documented 204 and (currently) of no use. */ 205 /* std::cout << "Testing class_getIvarLayout () on a meta class...\n"; */ 206 207 std::cout << "Testing class_getMethodImplementation () on a meta class...\n"; 208 { 209 /* Getting the method implementation with a meta class returns a 210 class method. */ 211 MySubClass *object = [[MySubClass alloc] init]; 212 IMP imp = class_getMethodImplementation (objc_getMetaClass ("MySubClass"), 213 @selector(variable)); 214 215 if (imp == NULL) 216 abort (); 217 218 [MySubClass setVariable: object]; 219 220 if ((*imp)(objc_getClass ("MySubClass"), @selector(variable)) != object) 221 abort (); 222 } 223 224 /* This function does not exist with the GNU runtime. */ 225 /* std::cout << "Testing class_getMethodImplementation_stret () on a meta class...\n"; */ 226 227 std::cout << "Testing class_getName () on a meta class...\n"; 228 { 229 /* Traditionally, a meta class has the same name as the class. */ 230 if (std::strcmp (class_getName (objc_getMetaClass ("MyRootClass")), 231 "MyRootClass") != 0) 232 abort (); 233 } 234 235 /* Calling class_getProperty() with a meta class is not documented 236 and (currently) of no use. */ 237 /* std::cout << "Testing class_getProperty ()...\n"; */ 238 239 std::cout << "Testing class_getSuperclass () on a meta class...\n"; 240 { 241 /* The superclass of a meta class is the meta class of the superclass. */ 242 if (class_getSuperclass (objc_getMetaClass ("MySubClass")) != objc_getMetaClass ("MyRootClass")) 243 abort (); 244 245 /* Test that it works on a newly created, but not registered, class. */ 246 { 247 Class new_class = objc_allocateClassPair (objc_getClass ("MyRootClass"), "MySubClass3", 0); 248 249 if (class_getSuperclass (object_getClass (new_class)) != object_getClass (objc_getClass ("MyRootClass"))) 250 abort (); 251 } 252 } 253 254 /* Calling class_getVersion() with a meta class is not documented 255 and (currently) of no use. */ 256 /* std::cout << "Testing class_getVersion ()...\n"; */ 257 258 /* Calling class_getWeakIvarLayout() with a meta class is not 259 documented and (currently) of no use. */ 260 /* std::cout << "Testing class_getWeakIvarLayout () on a meta class...\n"; */ 261 262 /* class_isMetaClass() is already tested in gnu-api-2-class.m */ 263 /* std::cout << "Testing class_isMetaClass ()...\n"; */ 264 265 std::cout << "Testing class_replaceMethod () on a meta class...\n"; 266 { 267 /* We are going to replace the [MySubclass +variable] method with 268 the [DifferentClass +myClass] one. */ 269 Method new_method = class_getClassMethod (objc_getClass ("DifferentClass"), 270 @selector (myClass)); 271 Method old_method = class_getClassMethod (objc_getClass ("MySubClass"), 272 @selector (variable)); 273 const char *new_types = method_getTypeEncoding (new_method); 274 IMP new_imp = method_getImplementation (new_method); 275 const char *old_types = method_getTypeEncoding (old_method); 276 IMP old_imp = class_replaceMethod (objc_getMetaClass ("MySubClass"), @selector (variable), 277 method_getImplementation (new_method), 278 method_getTypeEncoding (new_method)); 279 id o = [[MyRootClass alloc] init]; 280 281 [MySubClass setVariable: o]; 282 283 /* Try the new method implementation. */ 284 if ([MySubClass variable] != objc_getClass ("MySubClass")) 285 abort (); 286 287 /* Put the original method back. */ 288 class_replaceMethod (objc_getMetaClass ("MySubClass"), @selector (variable), 289 old_imp, old_types); 290 291 /* Test it's back to what it was. */ 292 if ([MySubClass variable] != o) 293 abort (); 294 295 { 296 /* Finally, try adding a new method. */ 297 class_replaceMethod (objc_getMetaClass ("DifferentClass"), @selector (mySelf), 298 new_imp, new_types); 299 300 if ([(Class)objc_getClass ("DifferentClass") mySelf] != objc_getClass ("DifferentClass")) 301 abort (); 302 } 303 } 304 305 std::cout << "Testing class_respondsToSelector () on a meta class...\n"; 306 { 307 /* A meta class responds to a selector if and only if the class 308 responds to the corresponding class method. */ 309 if (! class_respondsToSelector (objc_getMetaClass ("MySubClass"), @selector(setVariable:))) 310 abort (); 311 312 if (class_respondsToSelector (objc_getMetaClass ("MyRootClass"), @selector(setVariable:))) 313 abort (); 314 } 315 316 /* This is not really implemented with the GNU runtime. */ 317 /* std::cout << "Testing class_setIvarLayout () on a meta class...\n"; */ 318 319 /* Calling class_setVersion() with a meta class is not documented 320 and (currently) of no use. */ 321 /* std::cout << "Testing class_setVersion () on a meta class...\n"; */ 322 323 /* This is not really implemented with the GNU runtime. */ 324 /* std::cout << "Testing class_setWeakIvarLayout () on a meta class...\n"; */ 325 326return (0); 327} 328