]>
Commit | Line | Data |
---|---|---|
13ba007e A |
1 | /* |
2 | TEST_RUN_OUTPUT | |
3 | objc\[\d+\]: Class Sub is implemented in both [^\s]+ \(0x[0-9a-f]+\) and [^\s]+ \(0x[0-9a-f]+\)\. One of the two will be used\. Which one is undefined\. | |
4 | OK: readClassPair.m | |
5 | END | |
6 | */ | |
7 | ||
8 | #include "test.h" | |
9 | #include <objc/objc-internal.h> | |
10 | ||
11 | // Reuse evil-class-def.m as a non-evil class definition. | |
12 | ||
13 | #define EVIL_SUPER 0 | |
14 | #define EVIL_SUPER_META 0 | |
15 | #define EVIL_SUB 0 | |
16 | #define EVIL_SUB_META 0 | |
17 | ||
18 | #define OMIT_SUPER 1 | |
19 | #define OMIT_NL_SUPER 1 | |
20 | #define OMIT_SUB 1 | |
21 | #define OMIT_NL_SUB 1 | |
22 | ||
23 | #include "evil-class-def.m" | |
24 | ||
25 | int main() | |
26 | { | |
27 | // This definition is ABI and is never allowed to change. | |
28 | testassert(OBJC_MAX_CLASS_SIZE == 32*sizeof(void*)); | |
29 | ||
30 | struct objc_image_info ii = { 0, 0 }; | |
31 | ||
32 | // Read a root class. | |
33 | testassert(!objc_getClass("Super")); | |
34 | ||
35 | extern intptr_t OBJC_CLASS_$_Super[OBJC_MAX_CLASS_SIZE/sizeof(void*)]; | |
36 | Class Super = objc_readClassPair((__bridge Class)(void*)&OBJC_CLASS_$_Super, &ii); | |
37 | testassert(Super); | |
38 | ||
39 | testassert(objc_getClass("Super") == Super); | |
40 | testassert(0 == strcmp(class_getName(Super), "Super")); | |
41 | testassert(class_getSuperclass(Super) == nil); | |
42 | testassert(class_getClassMethod(Super, @selector(load))); | |
43 | testassert(class_getInstanceMethod(Super, @selector(load))); | |
44 | testassert(class_getInstanceVariable(Super, "super_ivar")); | |
45 | testassert(class_getInstanceSize(Super) == sizeof(void*)); | |
46 | [Super load]; | |
47 | ||
48 | // Read a non-root class. | |
49 | testassert(!objc_getClass("Sub")); | |
50 | ||
34d5b5e8 A |
51 | // Clang assumes too much alignment on this by default (rdar://problem/60881608), |
52 | // so tell it that it's only as aligned as an intptr_t. | |
53 | extern _Alignas(intptr_t) intptr_t OBJC_CLASS_$_Sub[OBJC_MAX_CLASS_SIZE/sizeof(void*)]; | |
13ba007e A |
54 | // Make a duplicate of class Sub for use later. |
55 | intptr_t Sub2_buf[OBJC_MAX_CLASS_SIZE/sizeof(void*)]; | |
56 | memcpy(Sub2_buf, &OBJC_CLASS_$_Sub, sizeof(Sub2_buf)); | |
34d5b5e8 A |
57 | // Re-sign the isa and super pointers in the new location. |
58 | ((Class __ptrauth_objc_isa_pointer *)(void *)Sub2_buf)[0] = ((Class __ptrauth_objc_isa_pointer *)(void *)&OBJC_CLASS_$_Sub)[0]; | |
59 | ((Class __ptrauth_objc_super_pointer *)(void *)Sub2_buf)[1] = ((Class __ptrauth_objc_super_pointer *)(void *)&OBJC_CLASS_$_Sub)[1]; | |
60 | ||
13ba007e A |
61 | Class Sub = objc_readClassPair((__bridge Class)(void*)&OBJC_CLASS_$_Sub, &ii); |
62 | testassert(Sub); | |
63 | ||
64 | testassert(0 == strcmp(class_getName(Sub), "Sub")); | |
65 | testassert(objc_getClass("Sub") == Sub); | |
66 | testassert(class_getSuperclass(Sub) == Super); | |
67 | testassert(class_getClassMethod(Sub, @selector(load))); | |
68 | testassert(class_getInstanceMethod(Sub, @selector(load))); | |
69 | testassert(class_getInstanceVariable(Sub, "sub_ivar")); | |
70 | testassert(class_getInstanceSize(Sub) == 2*sizeof(void*)); | |
71 | [Sub load]; | |
72 | ||
73 | // Reading a class whose name already exists succeeds | |
74 | // with a duplicate warning. | |
75 | Class Sub2 = objc_readClassPair((__bridge Class)(void*)Sub2_buf, &ii); | |
76 | testassert(Sub2); | |
77 | testassert(Sub2 != Sub); | |
78 | testassert(objc_getClass("Sub") == Sub); // didn't change | |
79 | testassert(0 == strcmp(class_getName(Sub2), "Sub")); | |
80 | testassert(class_getSuperclass(Sub2) == Super); | |
81 | testassert(class_getClassMethod(Sub2, @selector(load))); | |
82 | testassert(class_getInstanceMethod(Sub2, @selector(load))); | |
83 | testassert(class_getInstanceVariable(Sub2, "sub_ivar")); | |
84 | testassert(class_getInstanceSize(Sub2) == 2*sizeof(void*)); | |
85 | [Sub2 load]; | |
86 | ||
87 | succeed(__FILE__); | |
88 | } |