this repo has no description
at trunk 558 lines 21 kB view raw
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