ivarSlide.m 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. /*
  2. TEST_BUILD
  3. $C{COMPILE} -fobjc-weak $DIR/ivarSlide1.m $DIR/ivarSlide.m -o ivarSlide.exe
  4. END
  5. */
  6. #include "test.h"
  7. #include <string.h>
  8. #include <stdint.h>
  9. #include <objc/objc-runtime.h>
  10. #include <objc/objc-auto.h>
  11. // fixme should check ARC layout handling
  12. // current test checks GC layout handling which is dead
  13. #define FIXME_CHECK_ARC_LAYOUTS 0
  14. // ARC doesn't like __strong void* or __weak void*
  15. #define gc_weak
  16. #define gc_strong
  17. #define OLD 1
  18. #include "ivarSlide.h"
  19. #define ustrcmp(a, b) strcmp((char *)a, (char *)b)
  20. #ifdef __cplusplus
  21. class CXX {
  22. public:
  23. static uintptr_t count;
  24. uintptr_t magic;
  25. CXX() : magic(1) { }
  26. ~CXX() { count += magic; }
  27. };
  28. uintptr_t CXX::count;
  29. #endif
  30. @interface Bitfields : Super {
  31. uint8_t uint8_ivar;
  32. uint8_t uint8_bitfield1 :7;
  33. uint8_t uint8_bitfield2 :1;
  34. id id_ivar;
  35. uintptr_t uintptr_ivar;
  36. uintptr_t /*uintptr_bitfield1*/ :31; // anonymous (rdar://5723893)
  37. uintptr_t uintptr_bitfield2 :1;
  38. id id_ivar2;
  39. }
  40. @end
  41. @implementation Bitfields @end
  42. @interface Sub : Super {
  43. @public
  44. uintptr_t subIvar;
  45. gc_strong void* subIvar2;
  46. gc_weak void* subIvar3;
  47. #ifdef __cplusplus
  48. CXX cxx;
  49. #else
  50. // same layout as cxx
  51. uintptr_t cxx_magic;
  52. #endif
  53. }
  54. @end
  55. @implementation Sub @end
  56. @interface Sub2 : ShrinkingSuper {
  57. @public
  58. gc_weak void* subIvar;
  59. gc_strong void* subIvar2;
  60. }
  61. @end
  62. @implementation Sub2 @end
  63. @interface MoreStrongSub : MoreStrongSuper { id subIvar; } @end
  64. @interface LessStrongSub : LessStrongSuper { id subIvar; } @end
  65. @interface MoreWeakSub : MoreWeakSuper { id subIvar; } @end
  66. @interface MoreWeak2Sub : MoreWeak2Super { id subIvar; } @end
  67. @interface LessWeakSub : LessWeakSuper { id subIvar; } @end
  68. @interface LessWeak2Sub : LessWeak2Super { id subIvar; } @end
  69. @implementation MoreStrongSub @end
  70. @implementation LessStrongSub @end
  71. @implementation MoreWeakSub @end
  72. @implementation MoreWeak2Sub @end
  73. @implementation LessWeakSub @end
  74. @implementation LessWeak2Sub @end
  75. @interface NoGCChangeSub : NoGCChangeSuper {
  76. @public
  77. char subc3;
  78. }
  79. @end
  80. @implementation NoGCChangeSub @end
  81. @interface RunsOf15Sub : RunsOf15 {
  82. @public
  83. char sub;
  84. }
  85. @end
  86. @implementation RunsOf15Sub @end
  87. int main(int argc __attribute__((unused)), char **argv)
  88. {
  89. #if __has_feature(objc_arc)
  90. testwarn("fixme check ARC layouts too");
  91. #endif
  92. /*
  93. Bitfield ivars.
  94. rdar://5723893 anonymous bitfield ivars crash when slid
  95. rdar://5724385 bitfield ivar alignment incorrect
  96. Compile-time layout of Bitfields:
  97. [0 scan] isa
  98. [1 skip] uint8_ivar, uint8_bitfield
  99. [2 scan] id_ivar
  100. [3 skip] uintptr_ivar
  101. [4 skip] uintptr_bitfield
  102. [5 scan] id_ivar2
  103. Runtime layout of Bitfields:
  104. [0 scan] isa
  105. [1 skip] superIvar
  106. [2 skip] uint8_ivar, uint8_bitfield
  107. [3 scan] id_ivar
  108. [4 skip] uintptr_ivar
  109. [5 skip] uintptr_bitfield
  110. [6 scan] id_ivar2
  111. */
  112. [Bitfields class];
  113. testassert(class_getInstanceSize([Bitfields class]) == 7*sizeof(void*));
  114. if (FIXME_CHECK_ARC_LAYOUTS) {
  115. const uint8_t *bitfieldlayout;
  116. bitfieldlayout = class_getIvarLayout([Bitfields class]);
  117. testassert(0 == ustrcmp(bitfieldlayout, "\x01\x21\x21"));
  118. bitfieldlayout = class_getWeakIvarLayout([Bitfields class]);
  119. testassert(bitfieldlayout == NULL);
  120. }
  121. /*
  122. Compile-time layout of Sub:
  123. [0 scan] isa
  124. [1 skip] subIvar
  125. [2 scan] subIvar2
  126. [3 weak] subIvar3
  127. [6 skip] cxx
  128. Runtime layout of Sub:
  129. [0 scan] isa
  130. [1 skip] superIvar
  131. [2 skip] subIvar
  132. [3 scan] subIvar2
  133. [4 weak] subIvar3
  134. [6 skip] cxx
  135. Also, superIvar is only one byte, so subIvar's alignment must
  136. be handled correctly.
  137. fixme test more layouts
  138. */
  139. Ivar ivar;
  140. static Sub * volatile sub;
  141. sub = [Sub new];
  142. sub->subIvar = 10;
  143. uintptr_t *subwords = (uintptr_t *)(__bridge void*)sub;
  144. testassert(subwords[2] == 10);
  145. #ifdef __cplusplus
  146. testassert(subwords[5] == 1);
  147. testassert(sub->cxx.magic == 1);
  148. sub->cxx.magic++;
  149. testassert(subwords[5] == 2);
  150. testassert(sub->cxx.magic == 2);
  151. # if __has_feature(objc_arc)
  152. sub = nil;
  153. # else
  154. [sub dealloc];
  155. # endif
  156. testassert(CXX::count == 2);
  157. #endif
  158. testassert(class_getInstanceSize([Sub class]) == 6*sizeof(void*));
  159. ivar = class_getInstanceVariable([Sub class], "subIvar");
  160. testassert(ivar);
  161. testassert(2*sizeof(void*) == (size_t)ivar_getOffset(ivar));
  162. testassert(0 == strcmp(ivar_getName(ivar), "subIvar"));
  163. testassert(0 == strcmp(ivar_getTypeEncoding(ivar),
  164. #if __LP64__
  165. "Q"
  166. #else
  167. "L"
  168. #endif
  169. ));
  170. #ifdef __cplusplus
  171. ivar = class_getInstanceVariable([Sub class], "cxx");
  172. testassert(ivar);
  173. #endif
  174. ivar = class_getInstanceVariable([Super class], "superIvar");
  175. testassert(ivar);
  176. testassert(sizeof(void*) == (size_t)ivar_getOffset(ivar));
  177. testassert(0 == strcmp(ivar_getName(ivar), "superIvar"));
  178. testassert(0 == strcmp(ivar_getTypeEncoding(ivar), "c"));
  179. ivar = class_getInstanceVariable([Super class], "subIvar");
  180. testassert(!ivar);
  181. if (FIXME_CHECK_ARC_LAYOUTS) {
  182. const uint8_t *superlayout;
  183. const uint8_t *sublayout;
  184. superlayout = class_getIvarLayout([Super class]);
  185. sublayout = class_getIvarLayout([Sub class]);
  186. testassert(0 == ustrcmp(superlayout, "\x01\x10"));
  187. testassert(0 == ustrcmp(sublayout, "\x01\x21\x20"));
  188. superlayout = class_getWeakIvarLayout([Super class]);
  189. sublayout = class_getWeakIvarLayout([Sub class]);
  190. testassert(superlayout == NULL);
  191. testassert(0 == ustrcmp(sublayout, "\x41\x10"));
  192. }
  193. /*
  194. Shrinking superclass.
  195. Subclass ivars do not compact, but the GC layout needs to
  196. update, including the gap that the superclass no longer spans.
  197. Compile-time layout of Sub2:
  198. [0 scan] isa
  199. [1-5 scan] superIvar
  200. [6-10 weak] superIvar2
  201. [11 weak] subIvar
  202. [12 scan] subIvar2
  203. Runtime layout of Sub2:
  204. [0 scan] isa
  205. [1-10 skip] was superIvar
  206. [11 weak] subIvar
  207. [12 scan] subIvar2
  208. */
  209. Sub2 *sub2 = [Sub2 new];
  210. uintptr_t *sub2words = (uintptr_t *)(__bridge void*)sub2;
  211. sub2->subIvar = (void *)10;
  212. testassert(sub2words[11] == 10);
  213. testassert(class_getInstanceSize([Sub2 class]) == 13*sizeof(void*));
  214. ivar = class_getInstanceVariable([Sub2 class], "subIvar");
  215. testassert(ivar);
  216. testassert(11*sizeof(void*) == (size_t)ivar_getOffset(ivar));
  217. testassert(0 == strcmp(ivar_getName(ivar), "subIvar"));
  218. ivar = class_getInstanceVariable([ShrinkingSuper class], "superIvar");
  219. testassert(!ivar);
  220. if (FIXME_CHECK_ARC_LAYOUTS) {
  221. const uint8_t *superlayout;
  222. const uint8_t *sublayout;
  223. superlayout = class_getIvarLayout([ShrinkingSuper class]);
  224. sublayout = class_getIvarLayout([Sub2 class]);
  225. // only `isa` is left; superIvar[] and superIvar2[] are gone
  226. testassert(superlayout == NULL || 0 == ustrcmp(superlayout, "\x01"));
  227. testassert(0 == ustrcmp(sublayout, "\x01\xb1"));
  228. superlayout = class_getWeakIvarLayout([ShrinkingSuper class]);
  229. sublayout = class_getWeakIvarLayout([Sub2 class]);
  230. testassert(superlayout == NULL);
  231. testassert(0 == ustrcmp(sublayout, "\xb1\x10"));
  232. }
  233. /*
  234. Ivars slide but GC layouts stay the same
  235. Here, the last word of the superclass is misaligned, but
  236. its GC layout includes a bit for that whole word.
  237. Additionally, all of the subclass ivars fit into that word too,
  238. both before and after sliding.
  239. The runtime will try to slide the GC layout and must not be
  240. confused (rdar://6851700). Note that the second skip-word may or may
  241. not actually be included, because it crosses the end of the object.
  242. Compile-time layout of NoGCChangeSub:
  243. [0 scan] isa
  244. [1 skip] d
  245. [2 skip] superc1, subc3
  246. Runtime layout of NoGCChangeSub:
  247. [0 scan] isa
  248. [1 skip] d
  249. [2 skip] superc1, superc2, subc3
  250. */
  251. if (FIXME_CHECK_ARC_LAYOUTS) {
  252. Ivar ivar1 = class_getInstanceVariable([NoGCChangeSub class], "superc1");
  253. testassert(ivar1);
  254. Ivar ivar2 = class_getInstanceVariable([NoGCChangeSub class], "superc2");
  255. testassert(ivar2);
  256. Ivar ivar3 = class_getInstanceVariable([NoGCChangeSub class], "subc3");
  257. testassert(ivar3);
  258. testassert(ivar_getOffset(ivar1) != ivar_getOffset(ivar2) &&
  259. ivar_getOffset(ivar1) != ivar_getOffset(ivar3) &&
  260. ivar_getOffset(ivar2) != ivar_getOffset(ivar3));
  261. }
  262. /* Ivar layout includes runs of 15 words.
  263. rdar://6859875 this would generate a truncated GC layout.
  264. */
  265. if (FIXME_CHECK_ARC_LAYOUTS) {
  266. const uint8_t *layout =
  267. class_getIvarLayout(objc_getClass("RunsOf15Sub"));
  268. testassert(layout);
  269. int totalSkip = 0;
  270. int totalScan = 0;
  271. // should find 30+ each of skip and scan
  272. uint8_t c;
  273. while ((c = *layout++)) {
  274. totalSkip += c>>4;
  275. totalScan += c&0xf;
  276. }
  277. testassert(totalSkip >= 30);
  278. testassert(totalScan >= 30);
  279. }
  280. /*
  281. Non-strong -> strong
  282. Classes do not change size, but GC layouts must be updated.
  283. Both new and old ABI detect this case (rdar://5774578)
  284. Compile-time layout of MoreStrongSub:
  285. [0 scan] isa
  286. [1 skip] superIvar
  287. [2 scan] subIvar
  288. Runtime layout of MoreStrongSub:
  289. [0 scan] isa
  290. [1 scan] superIvar
  291. [2 scan] subIvar
  292. */
  293. testassert(class_getInstanceSize([MoreStrongSub class]) == 3*sizeof(void*));
  294. if (FIXME_CHECK_ARC_LAYOUTS) {
  295. const uint8_t *layout;
  296. layout = class_getIvarLayout([MoreStrongSub class]);
  297. testassert(layout == NULL);
  298. layout = class_getWeakIvarLayout([MoreStrongSub class]);
  299. testassert(layout == NULL);
  300. }
  301. /*
  302. Strong -> weak
  303. Classes do not change size, but GC layouts must be updated.
  304. Old ABI intentionally does not detect this case (rdar://5774578)
  305. Compile-time layout of MoreWeakSub:
  306. [0 scan] isa
  307. [1 scan] superIvar
  308. [2 scan] subIvar
  309. Runtime layout of MoreWeakSub:
  310. [0 scan] isa
  311. [1 weak] superIvar
  312. [2 scan] subIvar
  313. */
  314. testassert(class_getInstanceSize([MoreWeakSub class]) == 3*sizeof(void*));
  315. if (FIXME_CHECK_ARC_LAYOUTS) {
  316. const uint8_t *layout;
  317. layout = class_getIvarLayout([MoreWeakSub class]);
  318. testassert(0 == ustrcmp(layout, "\x01\x11"));
  319. layout = class_getWeakIvarLayout([MoreWeakSub class]);
  320. testassert(0 == ustrcmp(layout, "\x11\x10"));
  321. }
  322. /*
  323. Non-strong -> weak
  324. Classes do not change size, but GC layouts must be updated.
  325. Old ABI intentionally does not detect this case (rdar://5774578)
  326. Compile-time layout of MoreWeak2Sub:
  327. [0 scan] isa
  328. [1 skip] superIvar
  329. [2 scan] subIvar
  330. Runtime layout of MoreWeak2Sub:
  331. [0 scan] isa
  332. [1 weak] superIvar
  333. [2 scan] subIvar
  334. */
  335. testassert(class_getInstanceSize([MoreWeak2Sub class]) == 3*sizeof(void*));
  336. if (FIXME_CHECK_ARC_LAYOUTS) {
  337. const uint8_t *layout;
  338. layout = class_getIvarLayout([MoreWeak2Sub class]);
  339. testassert(0 == ustrcmp(layout, "\x01\x11") ||
  340. 0 == ustrcmp(layout, "\x01\x10\x01"));
  341. layout = class_getWeakIvarLayout([MoreWeak2Sub class]);
  342. testassert(0 == ustrcmp(layout, "\x11\x10"));
  343. }
  344. /*
  345. Strong -> non-strong
  346. Classes do not change size, but GC layouts must be updated.
  347. Old ABI intentionally does not detect this case (rdar://5774578)
  348. Compile-time layout of LessStrongSub:
  349. [0 scan] isa
  350. [1 scan] superIvar
  351. [2 scan] subIvar
  352. Runtime layout of LessStrongSub:
  353. [0 scan] isa
  354. [1 skip] superIvar
  355. [2 scan] subIvar
  356. */
  357. testassert(class_getInstanceSize([LessStrongSub class]) == 3*sizeof(void*));
  358. if (FIXME_CHECK_ARC_LAYOUTS) {
  359. const uint8_t *layout;
  360. layout = class_getIvarLayout([LessStrongSub class]);
  361. testassert(0 == ustrcmp(layout, "\x01\x11"));
  362. layout = class_getWeakIvarLayout([LessStrongSub class]);
  363. testassert(layout == NULL);
  364. }
  365. /*
  366. Weak -> strong
  367. Classes do not change size, but GC layouts must be updated.
  368. Both new and old ABI detect this case (rdar://5774578 rdar://6924114)
  369. Compile-time layout of LessWeakSub:
  370. [0 scan] isa
  371. [1 weak] superIvar
  372. [2 scan] subIvar
  373. Runtime layout of LessWeakSub:
  374. [0 scan] isa
  375. [1 scan] superIvar
  376. [2 scan] subIvar
  377. */
  378. testassert(class_getInstanceSize([LessWeakSub class]) == 3*sizeof(void*));
  379. if (FIXME_CHECK_ARC_LAYOUTS) {
  380. const uint8_t *layout;
  381. layout = class_getIvarLayout([LessWeakSub class]);
  382. testassert(layout == NULL);
  383. layout = class_getWeakIvarLayout([LessWeakSub class]);
  384. testassert(layout == NULL);
  385. }
  386. /*
  387. Weak -> non-strong
  388. Classes do not change size, but GC layouts must be updated.
  389. Old ABI intentionally does not detect this case (rdar://5774578)
  390. Compile-time layout of LessWeak2Sub:
  391. [0 scan] isa
  392. [1 weak] superIvar
  393. [2 scan] subIvar
  394. Runtime layout of LessWeak2Sub:
  395. [0 scan] isa
  396. [1 skip] superIvar
  397. [2 scan] subIvar
  398. */
  399. testassert(class_getInstanceSize([LessWeak2Sub class]) == 3*sizeof(void*));
  400. if (FIXME_CHECK_ARC_LAYOUTS) {
  401. const uint8_t *layout;
  402. layout = class_getIvarLayout([LessWeak2Sub class]);
  403. testassert(0 == ustrcmp(layout, "\x01\x11") ||
  404. 0 == ustrcmp(layout, "\x01\x10\x01"));
  405. layout = class_getWeakIvarLayout([LessWeak2Sub class]);
  406. testassert(layout == NULL);
  407. }
  408. succeed(basename(argv[0]));
  409. return 0;
  410. }