this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include "layout.h"
3
4#include "gtest/gtest.h"
5
6#include "objects.h"
7#include "runtime.h"
8#include "test-utils.h"
9
10namespace py {
11
12using LayoutTest = testing::RuntimeFixture;
13
14TEST(AttributeInfoTest, WithoutFlags) {
15 AttributeInfo info(123, 0);
16 EXPECT_EQ(info.offset(), 123);
17 EXPECT_FALSE(info.isInObject());
18}
19
20TEST(AttributeInfoTest, WithFlags) {
21 AttributeInfo info(123, AttributeFlags::kInObject);
22 EXPECT_EQ(info.offset(), 123);
23 EXPECT_TRUE(info.isInObject());
24}
25
26TEST_F(LayoutTest, SmallIntHasEmptyInObjectAttributes) {
27 RawLayout layout = Layout::cast(runtime_->layoutAt(LayoutId::kSmallInt));
28 EXPECT_EQ(Tuple::cast(layout.inObjectAttributes()).length(), 0);
29}
30
31TEST_F(LayoutTest, SmallStrHasEmptyInObjectAttributes) {
32 RawLayout layout = Layout::cast(runtime_->layoutAt(LayoutId::kSmallStr));
33 EXPECT_EQ(Tuple::cast(layout.inObjectAttributes()).length(), 0);
34}
35
36TEST_F(LayoutTest, SmallBytesHasEmptyInObjectAttributes) {
37 RawLayout layout = Layout::cast(runtime_->layoutAt(LayoutId::kSmallBytes));
38 EXPECT_EQ(Tuple::cast(layout.inObjectAttributes()).length(), 0);
39}
40
41TEST_F(LayoutTest, BoolHasEmptyInObjectAttributes) {
42 RawLayout layout = Layout::cast(runtime_->layoutAt(LayoutId::kBool));
43 EXPECT_EQ(Tuple::cast(layout.inObjectAttributes()).length(), 0);
44}
45
46TEST_F(LayoutTest, NoneTypeHasEmptyInObjectAttributes) {
47 RawLayout layout = Layout::cast(runtime_->layoutAt(LayoutId::kNoneType));
48 EXPECT_EQ(Tuple::cast(layout.inObjectAttributes()).length(), 0);
49}
50
51TEST_F(LayoutTest, NotImplementedTypeHasEmptyInObjectAttributes) {
52 RawLayout layout =
53 Layout::cast(runtime_->layoutAt(LayoutId::kNotImplementedType));
54 EXPECT_EQ(Tuple::cast(layout.inObjectAttributes()).length(), 0);
55}
56
57TEST_F(LayoutTest, UnboundHasEmptyInObjectAttributes) {
58 RawLayout layout = Layout::cast(runtime_->layoutAt(LayoutId::kUnbound));
59 EXPECT_EQ(Tuple::cast(layout.inObjectAttributes()).length(), 0);
60}
61
62TEST_F(LayoutTest, SmallIntHasZeroInObjectAttributes) {
63 EXPECT_EQ(Layout::cast(runtime_->layoutAt(LayoutId::kSmallInt))
64 .numInObjectAttributes(),
65 0);
66}
67
68TEST_F(LayoutTest, SmallStrHasZeroInObjectAttributes) {
69 EXPECT_EQ(Layout::cast(runtime_->layoutAt(LayoutId::kSmallStr))
70 .numInObjectAttributes(),
71 0);
72}
73
74TEST_F(LayoutTest, SmallBytesHasZeroInObjectAttributes) {
75 EXPECT_EQ(Layout::cast(runtime_->layoutAt(LayoutId::kSmallBytes))
76 .numInObjectAttributes(),
77 0);
78}
79
80TEST_F(LayoutTest, BoolHasZeroInObjectAttributes) {
81 EXPECT_EQ(
82 Layout::cast(runtime_->layoutAt(LayoutId::kBool)).numInObjectAttributes(),
83 0);
84}
85
86TEST_F(LayoutTest, NoneTypeHasZeroInObjectAttributes) {
87 EXPECT_EQ(Layout::cast(runtime_->layoutAt(LayoutId::kNoneType))
88 .numInObjectAttributes(),
89 0);
90}
91
92TEST_F(LayoutTest, NotImplementedTypeHasZeroInObjectAttributes) {
93 EXPECT_EQ(Layout::cast(runtime_->layoutAt(LayoutId::kNotImplementedType))
94 .numInObjectAttributes(),
95 0);
96}
97
98TEST_F(LayoutTest, UnboundHasZeroInObjectAttributes) {
99 EXPECT_EQ(Layout::cast(runtime_->layoutAt(LayoutId::kUnbound))
100 .numInObjectAttributes(),
101 0);
102}
103
104TEST_F(LayoutTest, SmallIntHasZeroInstanceSize) {
105 EXPECT_EQ(
106 Layout::cast(runtime_->layoutAt(LayoutId::kSmallInt)).instanceSize(), 0);
107}
108
109TEST_F(LayoutTest, SmallStrHasZeroInstanceSize) {
110 EXPECT_EQ(
111 Layout::cast(runtime_->layoutAt(LayoutId::kSmallStr)).instanceSize(), 0);
112}
113
114TEST_F(LayoutTest, SmallBytesHasZeroInstanceSize) {
115 EXPECT_EQ(
116 Layout::cast(runtime_->layoutAt(LayoutId::kSmallBytes)).instanceSize(),
117 0);
118}
119
120TEST_F(LayoutTest, BoolHasZeroInstanceSize) {
121 EXPECT_EQ(Layout::cast(runtime_->layoutAt(LayoutId::kBool)).instanceSize(),
122 0);
123}
124
125TEST_F(LayoutTest, NoneTypeHasZeroInstanceSize) {
126 EXPECT_EQ(
127 Layout::cast(runtime_->layoutAt(LayoutId::kNoneType)).instanceSize(), 0);
128}
129
130TEST_F(LayoutTest, NotImplementedTypeHasZeroInstanceSize) {
131 EXPECT_EQ(Layout::cast(runtime_->layoutAt(LayoutId::kNotImplementedType))
132 .instanceSize(),
133 0);
134}
135
136TEST_F(LayoutTest, UnboundHasZeroInstanceSize) {
137 EXPECT_EQ(Layout::cast(runtime_->layoutAt(LayoutId::kUnbound)).instanceSize(),
138 0);
139}
140
141TEST_F(LayoutTest, FindAttribute) {
142 HandleScope scope(thread_);
143 Layout layout(&scope, testing::layoutCreateEmpty(thread_));
144
145 // Should fail to find an attribute that isn't present
146 Object attr(&scope, Runtime::internStrFromCStr(thread_, "myattr"));
147 AttributeInfo info;
148 EXPECT_FALSE(Runtime::layoutFindAttribute(*layout, attr, &info));
149
150 // Update the layout to include the new attribute as an in-object attribute
151 Object info_obj(&scope,
152 AttributeInfo(2222, AttributeFlags::kInObject).asSmallInt());
153 Tuple entry(&scope, runtime_->newTupleWith2(attr, info_obj));
154 Tuple array(&scope, runtime_->newTupleWith1(entry));
155 Layout::cast(*layout).setInObjectAttributes(*array);
156
157 // Should find the attribute
158 ASSERT_TRUE(Runtime::layoutFindAttribute(*layout, attr, &info));
159 EXPECT_EQ(info.offset(), 2222);
160 EXPECT_TRUE(info.isInObject());
161}
162
163TEST_F(LayoutTest, AddNewAttributes) {
164 HandleScope scope(thread_);
165 Layout layout(&scope, testing::layoutCreateEmpty(thread_));
166 runtime_->layoutSetTupleOverflow(*layout);
167
168 // Should fail to find an attribute that isn't present
169 Object attr(&scope, Runtime::internStrFromCStr(thread_, "myattr"));
170 AttributeInfo info;
171 ASSERT_FALSE(Runtime::layoutFindAttribute(*layout, attr, &info));
172
173 // Adding a new attribute should result in a new layout being created
174 AttributeInfo info2;
175 Layout layout2(
176 &scope, runtime_->layoutAddAttribute(thread_, layout, attr, 0, &info2));
177 ASSERT_NE(*layout, *layout2);
178 EXPECT_TRUE(info2.isOverflow());
179 EXPECT_EQ(info2.offset(), 0);
180
181 // Should be able find the attribute as an overflow attribute in the new
182 // layout
183 ASSERT_TRUE(Runtime::layoutFindAttribute(*layout2, attr, &info));
184 EXPECT_TRUE(info.isOverflow());
185 EXPECT_EQ(info.offset(), 0);
186
187 // Adding another attribute should transition the layout again
188 Object attr2(&scope, Runtime::internStrFromCStr(thread_, "another_attr"));
189 ASSERT_FALSE(Runtime::layoutFindAttribute(*layout2, attr2, &info));
190 AttributeInfo info3;
191 Layout layout3(
192 &scope, runtime_->layoutAddAttribute(thread_, layout2, attr2, 0, &info3));
193 ASSERT_NE(*layout2, *layout3);
194 EXPECT_TRUE(info3.isOverflow());
195 EXPECT_EQ(info3.offset(), 1);
196
197 // We should be able to find both attributes in the new layout
198 ASSERT_TRUE(Runtime::layoutFindAttribute(*layout3, attr, &info));
199 EXPECT_TRUE(info.isOverflow());
200 EXPECT_EQ(info.offset(), 0);
201 ASSERT_TRUE(Runtime::layoutFindAttribute(*layout3, attr2, &info));
202 EXPECT_TRUE(info.isOverflow());
203 EXPECT_EQ(info.offset(), 1);
204}
205
206TEST_F(LayoutTest, AddDuplicateAttributes) {
207 HandleScope scope(thread_);
208 Layout layout(&scope, testing::layoutCreateEmpty(thread_));
209 runtime_->layoutSetTupleOverflow(*layout);
210
211 // Add an attribute
212 Object attr(&scope, Runtime::internStrFromCStr(thread_, "myattr"));
213 AttributeInfo info;
214 ASSERT_FALSE(Runtime::layoutFindAttribute(*layout, attr, &info));
215
216 // Adding a new attribute should result in a new layout being created
217 Layout layout2(&scope,
218 runtime_->layoutAddAttribute(thread_, layout, attr, 0, &info));
219 EXPECT_NE(*layout, *layout2);
220 EXPECT_EQ(info.offset(), 0);
221 EXPECT_TRUE(info.isOverflow());
222
223 // Adding the attribute on the old layout should follow the edge and result
224 // in the same layout being returned
225 Layout layout3(&scope,
226 runtime_->layoutAddAttribute(thread_, layout, attr, 0, &info));
227 EXPECT_EQ(*layout2, *layout3);
228 EXPECT_EQ(info.offset(), 0);
229 EXPECT_TRUE(info.isOverflow());
230
231 // Should be able to find the attribute in the new layout
232 ASSERT_TRUE(Runtime::layoutFindAttribute(*layout3, attr, &info));
233 EXPECT_EQ(info.offset(), 0);
234 EXPECT_TRUE(info.isOverflow());
235}
236
237TEST_F(LayoutTest, TransitionTypeReturnsNewLayout) {
238 HandleScope scope(thread_);
239 Layout from_layout(&scope, testing::layoutCreateEmpty(thread_));
240
241 Object attr(&scope, Runtime::internStrFromCStr(thread_, "__class__"));
242 AttributeInfo info;
243 ASSERT_FALSE(Runtime::layoutFindAttribute(*from_layout, attr, &info));
244
245 // Transitioning the type will result in a new layout
246 ASSERT_FALSE(testing::runFromCStr(runtime_, R"(
247class A:
248 pass
249)")
250 .isError());
251 Type to_type(&scope, testing::mainModuleAt(runtime_, "A"));
252 Layout target_layout(
253 &scope, runtime_->layoutSetDescribedType(thread_, from_layout, to_type));
254 EXPECT_NE(*from_layout, *target_layout);
255
256 // Transitioning more than once will result in the same layout
257 Layout target_layout2(
258 &scope, runtime_->layoutSetDescribedType(thread_, from_layout, to_type));
259 EXPECT_EQ(*target_layout2, *target_layout);
260}
261
262TEST_F(LayoutTest, TransitionTableResizesWhenNecessary) {
263 HandleScope scope(thread_);
264 Layout from_layout(&scope, testing::layoutCreateEmpty(thread_));
265
266 Object attr(&scope, Runtime::internStrFromCStr(thread_, "__class__"));
267 AttributeInfo info;
268 ASSERT_FALSE(Runtime::layoutFindAttribute(*from_layout, attr, &info));
269
270 Tuple transitions(&scope, runtime_->newMutableTuple(1));
271 runtime_->setLayoutTypeTransitions(*transitions);
272
273 Type type(&scope, runtime_->newType());
274 Layout to_layout(
275 &scope, runtime_->layoutSetDescribedType(thread_, from_layout, type));
276
277 EXPECT_NE(*to_layout, *from_layout);
278
279 transitions = runtime_->layoutTypeTransitions();
280 EXPECT_GT(transitions.length(), 1);
281}
282
283TEST_F(LayoutTest, TransitionTableHoldsWeakRefToLayouts) {
284 HandleScope scope(thread_);
285 Layout from_layout(&scope, testing::layoutCreateEmpty(thread_));
286
287 Object attr(&scope, Runtime::internStrFromCStr(thread_, "__class__"));
288 AttributeInfo info;
289 ASSERT_FALSE(Runtime::layoutFindAttribute(*from_layout, attr, &info));
290
291 // Transitioning the type will result in a new layout
292 Object to_ref(&scope, NoneType::object());
293 Object target_ref(&scope, NoneType::object());
294 {
295 ASSERT_FALSE(testing::runFromCStr(runtime_, R"(
296class A:
297 pass
298)")
299 .isError());
300 Type to_type(&scope, testing::mainModuleAt(runtime_, "A"));
301 ASSERT_FALSE(testing::runFromCStr(runtime_, R"(
302del A
303)")
304 .isError());
305 WeakRef to_ref_inner(&scope, runtime_->newWeakRef(thread_, to_type));
306 to_ref = *to_ref_inner;
307
308 Layout target_layout(&scope, runtime_->layoutSetDescribedType(
309 thread_, from_layout, to_type));
310 WeakRef target_ref_inner(&scope,
311 runtime_->newWeakRef(thread_, target_layout));
312 EXPECT_NE(*from_layout, *target_layout);
313 target_ref = *target_ref_inner;
314
315 runtime_->collectGarbage();
316 EXPECT_EQ(to_ref_inner.referent(), *to_type);
317 EXPECT_EQ(target_ref_inner.referent(), *target_layout);
318 }
319
320 // Layouts are collected
321 runtime_->collectGarbage();
322 EXPECT_EQ(WeakRef::cast(*to_ref).referent(), NoneType::object());
323 EXPECT_EQ(WeakRef::cast(*target_ref).referent(), NoneType::object());
324}
325
326TEST_F(LayoutTest, InstanceTransitionTypeReturnsInstanceWithNewLayout) {
327 ASSERT_FALSE(testing::runFromCStr(runtime_, R"(
328class A:
329 def __init__(self):
330 self.a = "a"
331 self.b = "b"
332
333class B:
334 def __init__(self):
335 self.b = "b"
336 self.c = "c"
337
338a_instance = A()
339b_instance = B()
340 )")
341 .isError());
342 HandleScope scope(thread_);
343 Instance a_instance(&scope, testing::mainModuleAt(runtime_, "a_instance"));
344 Instance b_instance(&scope, testing::mainModuleAt(runtime_, "b_instance"));
345 Layout from_layout(&scope, runtime_->layoutOf(*a_instance));
346 Layout to_layout(&scope, runtime_->layoutOf(*b_instance));
347 EXPECT_NE(*from_layout, *to_layout);
348
349 Object a_attr(&scope, Runtime::internStrFromCStr(thread_, "a"));
350 Object b_attr(&scope, Runtime::internStrFromCStr(thread_, "b"));
351 Object c_attr(&scope, Runtime::internStrFromCStr(thread_, "c"));
352 AttributeInfo info;
353 // a_instance has a, b; does not have c
354 ASSERT_TRUE(Runtime::layoutFindAttribute(*from_layout, a_attr, &info));
355 ASSERT_TRUE(Runtime::layoutFindAttribute(*from_layout, b_attr, &info));
356 ASSERT_FALSE(Runtime::layoutFindAttribute(*from_layout, c_attr, &info));
357
358 // b_instance has b, c; does not have a
359 ASSERT_FALSE(Runtime::layoutFindAttribute(*to_layout, a_attr, &info));
360 ASSERT_TRUE(Runtime::layoutFindAttribute(*to_layout, b_attr, &info));
361 ASSERT_TRUE(Runtime::layoutFindAttribute(*to_layout, c_attr, &info));
362
363 ASSERT_FALSE(testing::runFromCStr(runtime_, R"(
364a_instance.__class__ = B
365 )")
366 .isError());
367
368 // a_instance still has a, b, but does not magically gain c
369 Layout target_layout(&scope, runtime_->layoutOf(*a_instance));
370 EXPECT_NE(*to_layout, *target_layout);
371 EXPECT_TRUE(Runtime::layoutFindAttribute(*target_layout, a_attr, &info));
372 EXPECT_TRUE(Runtime::layoutFindAttribute(*target_layout, b_attr, &info));
373 EXPECT_FALSE(Runtime::layoutFindAttribute(*target_layout, c_attr, &info));
374}
375
376TEST_F(LayoutTest, DeleteInObjectAttribute) {
377 HandleScope scope(thread_);
378
379 // Create a new layout with a single in-object attribute
380 Object attr(&scope, Runtime::internStrFromCStr(thread_, "myattr"));
381 Object info_obj(&scope,
382 AttributeInfo(2222, AttributeFlags::kInObject).asSmallInt());
383 Tuple entry(&scope, runtime_->newTupleWith2(attr, info_obj));
384 Tuple array(&scope, runtime_->newTupleWith1(entry));
385 Layout layout(&scope, testing::layoutCreateEmpty(thread_));
386 layout.setInObjectAttributes(*array);
387
388 // Deleting the attribute should succeed and return a new layout
389 AttributeInfo info;
390 ASSERT_TRUE(Runtime::layoutFindAttribute(*layout, attr, &info));
391 RawObject result =
392 runtime_->layoutDeleteAttribute(thread_, layout, attr, info);
393 ASSERT_TRUE(result.isLayout());
394 Layout layout2(&scope, result);
395 EXPECT_NE(layout.id(), layout2.id());
396
397 // The new layout should have the entry for the attribute marked as deleted
398 ASSERT_TRUE(layout2.inObjectAttributes().isTuple());
399 Tuple inobject(&scope, layout2.inObjectAttributes());
400 ASSERT_EQ(inobject.length(), 1);
401 ASSERT_TRUE(inobject.at(0).isTuple());
402 entry = inobject.at(0);
403 EXPECT_EQ(entry.at(0), SmallInt::fromWord(0));
404 ASSERT_TRUE(entry.at(1).isSmallInt());
405 EXPECT_EQ(AttributeInfo(entry.at(1)).flags(), 2);
406
407 // Performing the same deletion should follow the edge created by the
408 // previous deletion and arrive at the same layout
409 ASSERT_TRUE(Runtime::layoutFindAttribute(*layout, attr, &info));
410 result = runtime_->layoutDeleteAttribute(thread_, layout, attr, info);
411 ASSERT_TRUE(result.isLayout());
412 Layout layout3(&scope, result);
413 EXPECT_EQ(*layout3, *layout2);
414}
415
416TEST_F(LayoutTest, DeleteOverflowAttribute) {
417 HandleScope scope(thread_);
418
419 // Create a new layout with several overflow attributes
420 Object attr(&scope, Runtime::internStrFromCStr(thread_, "myattr"));
421 Object info_obj1(&scope, AttributeInfo(0, 0).asSmallInt());
422 Tuple entry1(&scope, runtime_->newTupleWith2(attr, info_obj1));
423
424 Object attr2(&scope, Runtime::internStrFromCStr(thread_, "myattr2"));
425 Object info_obj2(&scope, AttributeInfo(1, 0).asSmallInt());
426 Tuple entry2(&scope, runtime_->newTupleWith2(attr2, info_obj2));
427
428 Object attr3(&scope, Runtime::internStrFromCStr(thread_, "myattr3"));
429 Object info_obj3(&scope, AttributeInfo(2, 0).asSmallInt());
430 Tuple entry3(&scope, runtime_->newTupleWith2(attr3, info_obj3));
431
432 Layout layout(&scope, testing::layoutCreateEmpty(thread_));
433 Tuple attrs(&scope, runtime_->newTupleWith3(entry1, entry2, entry3));
434 layout.setOverflowAttributes(*attrs);
435
436 // Delete the middle attribute. Make sure a new layout is created and the
437 // entry after the deleted attribute has its offset updated correctly.
438 AttributeInfo attr2_info;
439 ASSERT_TRUE(Runtime::layoutFindAttribute(*layout, attr, &attr2_info));
440 RawObject result =
441 runtime_->layoutDeleteAttribute(thread_, layout, attr2, attr2_info);
442 ASSERT_TRUE(result.isLayout());
443 Layout layout2(&scope, result);
444 EXPECT_NE(layout2.id(), layout.id());
445 // The first and third attribute should have the same offset
446 AttributeInfo info;
447 ASSERT_TRUE(Runtime::layoutFindAttribute(*layout2, attr, &info));
448 EXPECT_EQ(info.offset(), 0);
449 ASSERT_TRUE(Runtime::layoutFindAttribute(*layout2, attr3, &info));
450 EXPECT_EQ(info.offset(), 2);
451 // The second attribute should not exist in the new layout.
452 ASSERT_FALSE(Runtime::layoutFindAttribute(*layout2, attr2, &info));
453
454 // Delete the first attribute. A new layout should be created and the last
455 // entry is shifted into the first position.
456 AttributeInfo attr_info;
457 ASSERT_TRUE(Runtime::layoutFindAttribute(*layout2, attr, &attr_info));
458 result = runtime_->layoutDeleteAttribute(thread_, layout2, attr, attr_info);
459 ASSERT_TRUE(result.isLayout());
460 Layout layout3(&scope, result);
461 EXPECT_NE(layout3.id(), layout.id());
462 EXPECT_NE(layout3.id(), layout2.id());
463 // The first and second attribute should not exist
464 EXPECT_FALSE(Runtime::layoutFindAttribute(*layout3, attr, &info));
465 EXPECT_FALSE(Runtime::layoutFindAttribute(*layout3, attr2, &info));
466 // The third attribute should still exist.
467 EXPECT_TRUE(Runtime::layoutFindAttribute(*layout3, attr3, &info));
468 EXPECT_EQ(info.offset(), 2);
469
470 // Delete the remaining attribute. A new layout should be created and the
471 // overflow array should be empty.
472 AttributeInfo attr3_info;
473 ASSERT_TRUE(Runtime::layoutFindAttribute(*layout3, attr3, &attr3_info));
474 result = runtime_->layoutDeleteAttribute(thread_, layout3, attr3, attr3_info);
475 ASSERT_TRUE(result.isLayout());
476 Layout layout4(&scope, result);
477 EXPECT_NE(layout4.id(), layout.id());
478 EXPECT_NE(layout4.id(), layout2.id());
479 EXPECT_NE(layout4.id(), layout3.id());
480 // No attributes should exist
481 EXPECT_FALSE(Runtime::layoutFindAttribute(*layout4, attr, &info));
482 EXPECT_FALSE(Runtime::layoutFindAttribute(*layout4, attr2, &info));
483 EXPECT_FALSE(Runtime::layoutFindAttribute(*layout4, attr3, &info));
484
485 // Appending to layout2 should not use the offset of any of the remaining
486 // attributes there.
487 result = runtime_->layoutAddAttribute(thread_, layout2, attr2, 0, &info);
488 ASSERT_TRUE(result.isLayout());
489 Layout layout2_added(&scope, result);
490 EXPECT_NE(layout2_added.id(), layout2.id());
491 ASSERT_TRUE(Runtime::layoutFindAttribute(*layout2_added, attr2, &info));
492 EXPECT_NE(info.offset(), 0);
493 EXPECT_NE(info.offset(), 2);
494}
495
496static RawObject createLayoutAttribute(Thread* thread, Runtime* runtime,
497 const Object& name, uword flags) {
498 HandleScope scope(thread);
499 Object info(&scope, AttributeInfo(0, flags).asSmallInt());
500 Tuple entry(&scope, runtime->newTupleWith2(name, info));
501 Tuple result(&scope, runtime->newTupleWith1(entry));
502 return *result;
503}
504
505TEST_F(LayoutTest, DeleteAndAddInObjectAttribute) {
506 HandleScope scope(thread_);
507
508 // Create a new layout with one overflow attribute and one in-object
509 // attribute
510 Layout layout(&scope, testing::layoutCreateEmpty(thread_));
511 Object inobject(&scope, Runtime::internStrFromCStr(thread_, "inobject"));
512 layout.setInObjectAttributes(createLayoutAttribute(
513 thread_, runtime_, inobject, AttributeFlags::kInObject));
514 Object overflow(&scope, runtime_->newStrFromCStr("overflow"));
515 layout.setOverflowAttributes(
516 createLayoutAttribute(thread_, runtime_, overflow, 0));
517
518 // Delete the in-object attribute and add it back. It should be re-added as
519 // an overflow attribute.
520 AttributeInfo info;
521 ASSERT_TRUE(Runtime::layoutFindAttribute(*layout, inobject, &info));
522 RawObject result =
523 runtime_->layoutDeleteAttribute(thread_, layout, inobject, info);
524 ASSERT_TRUE(result.isLayout());
525 Layout layout2(&scope, result);
526 result = runtime_->layoutAddAttribute(thread_, layout2, inobject, 0, &info);
527 ASSERT_TRUE(result.isLayout());
528 Layout layout3(&scope, result);
529 AttributeInfo info2;
530 ASSERT_TRUE(Runtime::layoutFindAttribute(*layout3, inobject, &info2));
531 EXPECT_EQ(info2.offset(), 1);
532 EXPECT_TRUE(info2.isOverflow());
533}
534
535TEST_F(LayoutTest, VerifyChildLayout) {
536 HandleScope scope(thread_);
537 Layout parent(&scope, testing::layoutCreateEmpty(thread_));
538 runtime_->layoutSetTupleOverflow(*parent);
539 Object attr(&scope, Runtime::internStrFromCStr(thread_, "foo"));
540 AttributeInfo info;
541 Layout child(&scope,
542 runtime_->layoutAddAttribute(Thread::current(), parent, attr,
543 AttributeFlags::kNone, &info));
544
545 EXPECT_NE(child.id(), parent.id());
546 EXPECT_EQ(child.numInObjectAttributes(), parent.numInObjectAttributes());
547 EXPECT_EQ(child.inObjectAttributes(), parent.inObjectAttributes());
548 // Child should have an additional overflow attribute
549 EXPECT_NE(child.overflowAttributes(), parent.overflowAttributes());
550 EXPECT_NE(child.additions(), parent.additions());
551 EXPECT_EQ(List::cast(child.additions()).numItems(), 0);
552 EXPECT_NE(child.deletions(), parent.deletions());
553 EXPECT_EQ(List::cast(child.deletions()).numItems(), 0);
554 EXPECT_EQ(child.describedType(), parent.describedType());
555 EXPECT_EQ(child.instanceSize(), parent.instanceSize());
556}
557
558} // namespace py