1 // Copyright 2015 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "src/base/utils/random-number-generator.h" 6 #include "src/ic/stub-cache.h" 7 #include "src/isolate.h" 8 #include "test/cctest/compiler/code-assembler-tester.h" 9 #include "test/cctest/compiler/function-tester.h" 10 11 namespace v8 { 12 namespace internal { 13 14 using compiler::FunctionTester; 15 using compiler::Node; 16 17 typedef compiler::CodeAssemblerTesterImpl<CodeStubAssembler> 18 CodeStubAssemblerTester; 19 20 TEST(FixedArrayAccessSmiIndex) { 21 Isolate* isolate(CcTest::InitIsolateOnce()); 22 VoidDescriptor descriptor(isolate); 23 CodeStubAssemblerTester m(isolate, descriptor); 24 Handle<FixedArray> array = isolate->factory()->NewFixedArray(5); 25 array->set(4, Smi::FromInt(733)); 26 m.Return(m.LoadFixedArrayElement(m.HeapConstant(array), 27 m.SmiTag(m.Int32Constant(4)), 0, 28 CodeStubAssembler::SMI_PARAMETERS)); 29 Handle<Code> code = m.GenerateCode(); 30 FunctionTester ft(descriptor, code); 31 MaybeHandle<Object> result = ft.Call(); 32 CHECK_EQ(733, Handle<Smi>::cast(result.ToHandleChecked())->value()); 33 } 34 35 TEST(LoadHeapNumberValue) { 36 Isolate* isolate(CcTest::InitIsolateOnce()); 37 VoidDescriptor descriptor(isolate); 38 CodeStubAssemblerTester m(isolate, descriptor); 39 Handle<HeapNumber> number = isolate->factory()->NewHeapNumber(1234); 40 m.Return(m.SmiTag( 41 m.ChangeFloat64ToUint32(m.LoadHeapNumberValue(m.HeapConstant(number))))); 42 Handle<Code> code = m.GenerateCode(); 43 FunctionTester ft(descriptor, code); 44 MaybeHandle<Object> result = ft.Call(); 45 CHECK_EQ(1234, Handle<Smi>::cast(result.ToHandleChecked())->value()); 46 } 47 48 TEST(LoadInstanceType) { 49 Isolate* isolate(CcTest::InitIsolateOnce()); 50 VoidDescriptor descriptor(isolate); 51 CodeStubAssemblerTester m(isolate, descriptor); 52 Handle<HeapObject> undefined = isolate->factory()->undefined_value(); 53 m.Return(m.SmiTag(m.LoadInstanceType(m.HeapConstant(undefined)))); 54 Handle<Code> code = m.GenerateCode(); 55 FunctionTester ft(descriptor, code); 56 MaybeHandle<Object> result = ft.Call(); 57 CHECK_EQ(InstanceType::ODDBALL_TYPE, 58 Handle<Smi>::cast(result.ToHandleChecked())->value()); 59 } 60 61 TEST(BitFieldDecode) { 62 Isolate* isolate(CcTest::InitIsolateOnce()); 63 VoidDescriptor descriptor(isolate); 64 CodeStubAssemblerTester m(isolate, descriptor); 65 66 class TestBitField : public BitField<unsigned, 3, 3> {}; 67 m.Return(m.SmiTag(m.BitFieldDecode<TestBitField>(m.Int32Constant(0x2f)))); 68 Handle<Code> code = m.GenerateCode(); 69 FunctionTester ft(descriptor, code); 70 MaybeHandle<Object> result = ft.Call(); 71 // value = 00101111 72 // mask = 00111000 73 // result = 101 74 CHECK_EQ(5, Handle<Smi>::cast(result.ToHandleChecked())->value()); 75 } 76 77 TEST(JSFunction) { 78 const int kNumParams = 3; // Receiver, left, right. 79 Isolate* isolate(CcTest::InitIsolateOnce()); 80 CodeStubAssemblerTester m(isolate, kNumParams); 81 m.Return(m.SmiFromWord32(m.Int32Add(m.SmiToWord32(m.Parameter(1)), 82 m.SmiToWord32(m.Parameter(2))))); 83 84 Handle<Code> code = m.GenerateCode(); 85 FunctionTester ft(code, kNumParams); 86 87 MaybeHandle<Object> result = ft.Call(isolate->factory()->undefined_value(), 88 handle(Smi::FromInt(23), isolate), 89 handle(Smi::FromInt(34), isolate)); 90 CHECK_EQ(57, Handle<Smi>::cast(result.ToHandleChecked())->value()); 91 } 92 93 TEST(ComputeIntegerHash) { 94 Isolate* isolate(CcTest::InitIsolateOnce()); 95 const int kNumParams = 2; 96 CodeStubAssemblerTester m(isolate, kNumParams); 97 m.Return(m.SmiFromWord32(m.ComputeIntegerHash( 98 m.SmiToWord32(m.Parameter(0)), m.SmiToWord32(m.Parameter(1))))); 99 100 Handle<Code> code = m.GenerateCode(); 101 FunctionTester ft(code, kNumParams); 102 103 Handle<Smi> hash_seed = isolate->factory()->hash_seed(); 104 105 base::RandomNumberGenerator rand_gen(FLAG_random_seed); 106 107 for (int i = 0; i < 1024; i++) { 108 int k = rand_gen.NextInt(Smi::kMaxValue); 109 110 Handle<Smi> key(Smi::FromInt(k), isolate); 111 Handle<Object> result = ft.Call(key, hash_seed).ToHandleChecked(); 112 113 uint32_t hash = ComputeIntegerHash(k, hash_seed->value()); 114 Smi* expected = Smi::FromInt(hash & Smi::kMaxValue); 115 CHECK_EQ(expected, Smi::cast(*result)); 116 } 117 } 118 119 TEST(TryToName) { 120 typedef CodeStubAssembler::Label Label; 121 typedef CodeStubAssembler::Variable Variable; 122 Isolate* isolate(CcTest::InitIsolateOnce()); 123 124 const int kNumParams = 3; 125 CodeStubAssemblerTester m(isolate, kNumParams); 126 127 enum Result { kKeyIsIndex, kKeyIsUnique, kBailout }; 128 { 129 Node* key = m.Parameter(0); 130 Node* expected_result = m.Parameter(1); 131 Node* expected_arg = m.Parameter(2); 132 133 Label passed(&m), failed(&m); 134 Label if_keyisindex(&m), if_keyisunique(&m), if_bailout(&m); 135 Variable var_index(&m, MachineRepresentation::kWord32); 136 137 m.TryToName(key, &if_keyisindex, &var_index, &if_keyisunique, &if_bailout); 138 139 m.Bind(&if_keyisindex); 140 m.GotoUnless( 141 m.WordEqual(expected_result, m.SmiConstant(Smi::FromInt(kKeyIsIndex))), 142 &failed); 143 m.Branch(m.Word32Equal(m.SmiToWord32(expected_arg), var_index.value()), 144 &passed, &failed); 145 146 m.Bind(&if_keyisunique); 147 m.GotoUnless( 148 m.WordEqual(expected_result, m.SmiConstant(Smi::FromInt(kKeyIsUnique))), 149 &failed); 150 m.Branch(m.WordEqual(expected_arg, key), &passed, &failed); 151 152 m.Bind(&if_bailout); 153 m.Branch( 154 m.WordEqual(expected_result, m.SmiConstant(Smi::FromInt(kBailout))), 155 &passed, &failed); 156 157 m.Bind(&passed); 158 m.Return(m.BooleanConstant(true)); 159 160 m.Bind(&failed); 161 m.Return(m.BooleanConstant(false)); 162 } 163 164 Handle<Code> code = m.GenerateCode(); 165 FunctionTester ft(code, kNumParams); 166 167 Handle<Object> expect_index(Smi::FromInt(kKeyIsIndex), isolate); 168 Handle<Object> expect_unique(Smi::FromInt(kKeyIsUnique), isolate); 169 Handle<Object> expect_bailout(Smi::FromInt(kBailout), isolate); 170 171 { 172 // TryToName(<zero smi>) => if_keyisindex: smi value. 173 Handle<Object> key(Smi::FromInt(0), isolate); 174 ft.CheckTrue(key, expect_index, key); 175 } 176 177 { 178 // TryToName(<positive smi>) => if_keyisindex: smi value. 179 Handle<Object> key(Smi::FromInt(153), isolate); 180 ft.CheckTrue(key, expect_index, key); 181 } 182 183 { 184 // TryToName(<negative smi>) => bailout. 185 Handle<Object> key(Smi::FromInt(-1), isolate); 186 ft.CheckTrue(key, expect_bailout); 187 } 188 189 { 190 // TryToName(<symbol>) => if_keyisunique: <symbol>. 191 Handle<Object> key = isolate->factory()->NewSymbol(); 192 ft.CheckTrue(key, expect_unique, key); 193 } 194 195 { 196 // TryToName(<internalized string>) => if_keyisunique: <internalized string> 197 Handle<Object> key = isolate->factory()->InternalizeUtf8String("test"); 198 ft.CheckTrue(key, expect_unique, key); 199 } 200 201 { 202 // TryToName(<internalized number string>) => if_keyisindex: number. 203 Handle<Object> key = isolate->factory()->InternalizeUtf8String("153"); 204 Handle<Object> index(Smi::FromInt(153), isolate); 205 ft.CheckTrue(key, expect_index, index); 206 } 207 208 { 209 // TryToName(<non-internalized string>) => bailout. 210 Handle<Object> key = isolate->factory()->NewStringFromAsciiChecked("test"); 211 ft.CheckTrue(key, expect_bailout); 212 } 213 } 214 215 namespace { 216 217 template <typename Dictionary> 218 void TestNameDictionaryLookup() { 219 typedef CodeStubAssembler::Label Label; 220 typedef CodeStubAssembler::Variable Variable; 221 Isolate* isolate(CcTest::InitIsolateOnce()); 222 223 const int kNumParams = 4; 224 CodeStubAssemblerTester m(isolate, kNumParams); 225 226 enum Result { kFound, kNotFound }; 227 { 228 Node* dictionary = m.Parameter(0); 229 Node* unique_name = m.Parameter(1); 230 Node* expected_result = m.Parameter(2); 231 Node* expected_arg = m.Parameter(3); 232 233 Label passed(&m), failed(&m); 234 Label if_found(&m), if_not_found(&m); 235 Variable var_name_index(&m, MachineRepresentation::kWord32); 236 237 m.NameDictionaryLookup<Dictionary>(dictionary, unique_name, &if_found, 238 &var_name_index, &if_not_found); 239 m.Bind(&if_found); 240 m.GotoUnless( 241 m.WordEqual(expected_result, m.SmiConstant(Smi::FromInt(kFound))), 242 &failed); 243 m.Branch(m.Word32Equal(m.SmiToWord32(expected_arg), var_name_index.value()), 244 &passed, &failed); 245 246 m.Bind(&if_not_found); 247 m.Branch( 248 m.WordEqual(expected_result, m.SmiConstant(Smi::FromInt(kNotFound))), 249 &passed, &failed); 250 251 m.Bind(&passed); 252 m.Return(m.BooleanConstant(true)); 253 254 m.Bind(&failed); 255 m.Return(m.BooleanConstant(false)); 256 } 257 258 Handle<Code> code = m.GenerateCode(); 259 FunctionTester ft(code, kNumParams); 260 261 Handle<Object> expect_found(Smi::FromInt(kFound), isolate); 262 Handle<Object> expect_not_found(Smi::FromInt(kNotFound), isolate); 263 264 Handle<Dictionary> dictionary = Dictionary::New(isolate, 40); 265 PropertyDetails fake_details = PropertyDetails::Empty(); 266 267 Factory* factory = isolate->factory(); 268 Handle<Name> keys[] = { 269 factory->InternalizeUtf8String("0"), 270 factory->InternalizeUtf8String("42"), 271 factory->InternalizeUtf8String("-153"), 272 factory->InternalizeUtf8String("0.0"), 273 factory->InternalizeUtf8String("4.2"), 274 factory->InternalizeUtf8String(""), 275 factory->InternalizeUtf8String("name"), 276 factory->NewSymbol(), 277 factory->NewPrivateSymbol(), 278 }; 279 280 for (size_t i = 0; i < arraysize(keys); i++) { 281 Handle<Object> value = factory->NewPropertyCell(); 282 dictionary = Dictionary::Add(dictionary, keys[i], value, fake_details); 283 } 284 285 for (size_t i = 0; i < arraysize(keys); i++) { 286 int entry = dictionary->FindEntry(keys[i]); 287 int name_index = 288 Dictionary::EntryToIndex(entry) + Dictionary::kEntryKeyIndex; 289 CHECK_NE(Dictionary::kNotFound, entry); 290 291 Handle<Object> expected_name_index(Smi::FromInt(name_index), isolate); 292 ft.CheckTrue(dictionary, keys[i], expect_found, expected_name_index); 293 } 294 295 Handle<Name> non_existing_keys[] = { 296 factory->InternalizeUtf8String("1"), 297 factory->InternalizeUtf8String("-42"), 298 factory->InternalizeUtf8String("153"), 299 factory->InternalizeUtf8String("-1.0"), 300 factory->InternalizeUtf8String("1.3"), 301 factory->InternalizeUtf8String("a"), 302 factory->InternalizeUtf8String("boom"), 303 factory->NewSymbol(), 304 factory->NewPrivateSymbol(), 305 }; 306 307 for (size_t i = 0; i < arraysize(non_existing_keys); i++) { 308 int entry = dictionary->FindEntry(non_existing_keys[i]); 309 CHECK_EQ(Dictionary::kNotFound, entry); 310 311 ft.CheckTrue(dictionary, non_existing_keys[i], expect_not_found); 312 } 313 } 314 315 } // namespace 316 317 TEST(NameDictionaryLookup) { TestNameDictionaryLookup<NameDictionary>(); } 318 319 TEST(GlobalDictionaryLookup) { TestNameDictionaryLookup<GlobalDictionary>(); } 320 321 namespace { 322 323 template <typename Dictionary> 324 void TestNumberDictionaryLookup() { 325 typedef CodeStubAssembler::Label Label; 326 typedef CodeStubAssembler::Variable Variable; 327 Isolate* isolate(CcTest::InitIsolateOnce()); 328 329 const int kNumParams = 4; 330 CodeStubAssemblerTester m(isolate, kNumParams); 331 332 enum Result { kFound, kNotFound }; 333 { 334 Node* dictionary = m.Parameter(0); 335 Node* key = m.SmiToWord32(m.Parameter(1)); 336 Node* expected_result = m.Parameter(2); 337 Node* expected_arg = m.Parameter(3); 338 339 Label passed(&m), failed(&m); 340 Label if_found(&m), if_not_found(&m); 341 Variable var_entry(&m, MachineRepresentation::kWord32); 342 343 m.NumberDictionaryLookup<Dictionary>(dictionary, key, &if_found, &var_entry, 344 &if_not_found); 345 m.Bind(&if_found); 346 m.GotoUnless( 347 m.WordEqual(expected_result, m.SmiConstant(Smi::FromInt(kFound))), 348 &failed); 349 m.Branch(m.Word32Equal(m.SmiToWord32(expected_arg), var_entry.value()), 350 &passed, &failed); 351 352 m.Bind(&if_not_found); 353 m.Branch( 354 m.WordEqual(expected_result, m.SmiConstant(Smi::FromInt(kNotFound))), 355 &passed, &failed); 356 357 m.Bind(&passed); 358 m.Return(m.BooleanConstant(true)); 359 360 m.Bind(&failed); 361 m.Return(m.BooleanConstant(false)); 362 } 363 364 Handle<Code> code = m.GenerateCode(); 365 FunctionTester ft(code, kNumParams); 366 367 Handle<Object> expect_found(Smi::FromInt(kFound), isolate); 368 Handle<Object> expect_not_found(Smi::FromInt(kNotFound), isolate); 369 370 const int kKeysCount = 1000; 371 Handle<Dictionary> dictionary = Dictionary::New(isolate, kKeysCount); 372 uint32_t keys[kKeysCount]; 373 374 Handle<Object> fake_value(Smi::FromInt(42), isolate); 375 PropertyDetails fake_details = PropertyDetails::Empty(); 376 377 base::RandomNumberGenerator rand_gen(FLAG_random_seed); 378 379 for (int i = 0; i < kKeysCount; i++) { 380 int random_key = rand_gen.NextInt(Smi::kMaxValue); 381 keys[i] = static_cast<uint32_t>(random_key); 382 if (dictionary->FindEntry(keys[i]) != Dictionary::kNotFound) continue; 383 384 dictionary = Dictionary::Add(dictionary, keys[i], fake_value, fake_details); 385 } 386 387 // Now try querying existing keys. 388 for (int i = 0; i < kKeysCount; i++) { 389 int entry = dictionary->FindEntry(keys[i]); 390 CHECK_NE(Dictionary::kNotFound, entry); 391 392 Handle<Object> key(Smi::FromInt(keys[i]), isolate); 393 Handle<Object> expected_entry(Smi::FromInt(entry), isolate); 394 ft.CheckTrue(dictionary, key, expect_found, expected_entry); 395 } 396 397 // Now try querying random keys which do not exist in the dictionary. 398 for (int i = 0; i < kKeysCount;) { 399 int random_key = rand_gen.NextInt(Smi::kMaxValue); 400 int entry = dictionary->FindEntry(random_key); 401 if (entry != Dictionary::kNotFound) continue; 402 i++; 403 404 Handle<Object> key(Smi::FromInt(random_key), isolate); 405 ft.CheckTrue(dictionary, key, expect_not_found); 406 } 407 } 408 409 } // namespace 410 411 TEST(SeededNumberDictionaryLookup) { 412 TestNumberDictionaryLookup<SeededNumberDictionary>(); 413 } 414 415 TEST(UnseededNumberDictionaryLookup) { 416 TestNumberDictionaryLookup<UnseededNumberDictionary>(); 417 } 418 419 namespace { 420 421 void AddProperties(Handle<JSObject> object, Handle<Name> names[], 422 size_t count) { 423 Isolate* isolate = object->GetIsolate(); 424 for (size_t i = 0; i < count; i++) { 425 Handle<Object> value(Smi::FromInt(static_cast<int>(42 + i)), isolate); 426 JSObject::AddProperty(object, names[i], value, NONE); 427 } 428 } 429 430 Handle<AccessorPair> CreateAccessorPair(FunctionTester* ft, 431 const char* getter_body, 432 const char* setter_body) { 433 Handle<AccessorPair> pair = ft->isolate->factory()->NewAccessorPair(); 434 if (getter_body) { 435 pair->set_getter(*ft->NewFunction(getter_body)); 436 } 437 if (setter_body) { 438 pair->set_setter(*ft->NewFunction(setter_body)); 439 } 440 return pair; 441 } 442 443 void AddProperties(Handle<JSObject> object, Handle<Name> names[], 444 size_t names_count, Handle<Object> values[], 445 size_t values_count, int seed = 0) { 446 Isolate* isolate = object->GetIsolate(); 447 for (size_t i = 0; i < names_count; i++) { 448 Handle<Object> value = values[(seed + i) % values_count]; 449 if (value->IsAccessorPair()) { 450 Handle<AccessorPair> pair = Handle<AccessorPair>::cast(value); 451 Handle<Object> getter(pair->getter(), isolate); 452 Handle<Object> setter(pair->setter(), isolate); 453 JSObject::DefineAccessor(object, names[i], getter, setter, NONE).Check(); 454 } else { 455 JSObject::AddProperty(object, names[i], value, NONE); 456 } 457 } 458 } 459 460 } // namespace 461 462 TEST(TryHasOwnProperty) { 463 typedef CodeStubAssembler::Label Label; 464 Isolate* isolate(CcTest::InitIsolateOnce()); 465 466 const int kNumParams = 4; 467 CodeStubAssemblerTester m(isolate, kNumParams); 468 469 enum Result { kFound, kNotFound, kBailout }; 470 { 471 Node* object = m.Parameter(0); 472 Node* unique_name = m.Parameter(1); 473 Node* expected_result = m.Parameter(2); 474 475 Label passed(&m), failed(&m); 476 Label if_found(&m), if_not_found(&m), if_bailout(&m); 477 478 Node* map = m.LoadMap(object); 479 Node* instance_type = m.LoadMapInstanceType(map); 480 481 m.TryHasOwnProperty(object, map, instance_type, unique_name, &if_found, 482 &if_not_found, &if_bailout); 483 484 m.Bind(&if_found); 485 m.Branch(m.WordEqual(expected_result, m.SmiConstant(Smi::FromInt(kFound))), 486 &passed, &failed); 487 488 m.Bind(&if_not_found); 489 m.Branch( 490 m.WordEqual(expected_result, m.SmiConstant(Smi::FromInt(kNotFound))), 491 &passed, &failed); 492 493 m.Bind(&if_bailout); 494 m.Branch( 495 m.WordEqual(expected_result, m.SmiConstant(Smi::FromInt(kBailout))), 496 &passed, &failed); 497 498 m.Bind(&passed); 499 m.Return(m.BooleanConstant(true)); 500 501 m.Bind(&failed); 502 m.Return(m.BooleanConstant(false)); 503 } 504 505 Handle<Code> code = m.GenerateCode(); 506 FunctionTester ft(code, kNumParams); 507 508 Handle<Object> expect_found(Smi::FromInt(kFound), isolate); 509 Handle<Object> expect_not_found(Smi::FromInt(kNotFound), isolate); 510 Handle<Object> expect_bailout(Smi::FromInt(kBailout), isolate); 511 512 Factory* factory = isolate->factory(); 513 514 Handle<Name> deleted_property_name = 515 factory->InternalizeUtf8String("deleted"); 516 517 Handle<Name> names[] = { 518 factory->InternalizeUtf8String("a"), 519 factory->InternalizeUtf8String("bb"), 520 factory->InternalizeUtf8String("ccc"), 521 factory->InternalizeUtf8String("dddd"), 522 factory->InternalizeUtf8String("eeeee"), 523 factory->InternalizeUtf8String(""), 524 factory->InternalizeUtf8String("name"), 525 factory->NewSymbol(), 526 factory->NewPrivateSymbol(), 527 }; 528 529 std::vector<Handle<JSObject>> objects; 530 531 { 532 // Fast object, no inobject properties. 533 int inobject_properties = 0; 534 Handle<Map> map = Map::Create(isolate, inobject_properties); 535 Handle<JSObject> object = factory->NewJSObjectFromMap(map); 536 AddProperties(object, names, arraysize(names)); 537 CHECK_EQ(JS_OBJECT_TYPE, object->map()->instance_type()); 538 CHECK_EQ(inobject_properties, object->map()->GetInObjectProperties()); 539 CHECK(!object->map()->is_dictionary_map()); 540 objects.push_back(object); 541 } 542 543 { 544 // Fast object, all inobject properties. 545 int inobject_properties = arraysize(names) * 2; 546 Handle<Map> map = Map::Create(isolate, inobject_properties); 547 Handle<JSObject> object = factory->NewJSObjectFromMap(map); 548 AddProperties(object, names, arraysize(names)); 549 CHECK_EQ(JS_OBJECT_TYPE, object->map()->instance_type()); 550 CHECK_EQ(inobject_properties, object->map()->GetInObjectProperties()); 551 CHECK(!object->map()->is_dictionary_map()); 552 objects.push_back(object); 553 } 554 555 { 556 // Fast object, half inobject properties. 557 int inobject_properties = arraysize(names) / 2; 558 Handle<Map> map = Map::Create(isolate, inobject_properties); 559 Handle<JSObject> object = factory->NewJSObjectFromMap(map); 560 AddProperties(object, names, arraysize(names)); 561 CHECK_EQ(JS_OBJECT_TYPE, object->map()->instance_type()); 562 CHECK_EQ(inobject_properties, object->map()->GetInObjectProperties()); 563 CHECK(!object->map()->is_dictionary_map()); 564 objects.push_back(object); 565 } 566 567 { 568 // Dictionary mode object. 569 Handle<JSFunction> function = factory->NewFunction(factory->empty_string()); 570 Handle<JSObject> object = factory->NewJSObject(function); 571 AddProperties(object, names, arraysize(names)); 572 JSObject::NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0, "test"); 573 574 JSObject::AddProperty(object, deleted_property_name, object, NONE); 575 CHECK(JSObject::DeleteProperty(object, deleted_property_name, SLOPPY) 576 .FromJust()); 577 578 CHECK_EQ(JS_OBJECT_TYPE, object->map()->instance_type()); 579 CHECK(object->map()->is_dictionary_map()); 580 objects.push_back(object); 581 } 582 583 { 584 // Global object. 585 Handle<JSFunction> function = factory->NewFunction(factory->empty_string()); 586 JSFunction::EnsureHasInitialMap(function); 587 function->initial_map()->set_instance_type(JS_GLOBAL_OBJECT_TYPE); 588 function->initial_map()->set_is_prototype_map(true); 589 function->initial_map()->set_dictionary_map(true); 590 Handle<JSObject> object = factory->NewJSGlobalObject(function); 591 AddProperties(object, names, arraysize(names)); 592 593 JSObject::AddProperty(object, deleted_property_name, object, NONE); 594 CHECK(JSObject::DeleteProperty(object, deleted_property_name, SLOPPY) 595 .FromJust()); 596 597 CHECK_EQ(JS_GLOBAL_OBJECT_TYPE, object->map()->instance_type()); 598 CHECK(object->map()->is_dictionary_map()); 599 objects.push_back(object); 600 } 601 602 { 603 for (Handle<JSObject> object : objects) { 604 for (size_t name_index = 0; name_index < arraysize(names); name_index++) { 605 Handle<Name> name = names[name_index]; 606 CHECK(JSReceiver::HasProperty(object, name).FromJust()); 607 ft.CheckTrue(object, name, expect_found); 608 } 609 } 610 } 611 612 { 613 Handle<Name> non_existing_names[] = { 614 factory->NewSymbol(), 615 factory->InternalizeUtf8String("ne_a"), 616 factory->InternalizeUtf8String("ne_bb"), 617 factory->NewPrivateSymbol(), 618 factory->InternalizeUtf8String("ne_ccc"), 619 factory->InternalizeUtf8String("ne_dddd"), 620 deleted_property_name, 621 }; 622 for (Handle<JSObject> object : objects) { 623 for (size_t key_index = 0; key_index < arraysize(non_existing_names); 624 key_index++) { 625 Handle<Name> name = non_existing_names[key_index]; 626 CHECK(!JSReceiver::HasProperty(object, name).FromJust()); 627 ft.CheckTrue(object, name, expect_not_found); 628 } 629 } 630 } 631 632 { 633 Handle<JSFunction> function = factory->NewFunction(factory->empty_string()); 634 Handle<JSProxy> object = factory->NewJSProxy(function, objects[0]); 635 CHECK_EQ(JS_PROXY_TYPE, object->map()->instance_type()); 636 ft.CheckTrue(object, names[0], expect_bailout); 637 } 638 639 { 640 Handle<JSObject> object = isolate->global_proxy(); 641 CHECK_EQ(JS_GLOBAL_PROXY_TYPE, object->map()->instance_type()); 642 ft.CheckTrue(object, names[0], expect_bailout); 643 } 644 } 645 646 TEST(TryGetOwnProperty) { 647 typedef CodeStubAssembler::Label Label; 648 typedef CodeStubAssembler::Variable Variable; 649 Isolate* isolate(CcTest::InitIsolateOnce()); 650 Factory* factory = isolate->factory(); 651 652 const int kNumParams = 2; 653 CodeStubAssemblerTester m(isolate, kNumParams); 654 655 Handle<Symbol> not_found_symbol = factory->NewSymbol(); 656 Handle<Symbol> bailout_symbol = factory->NewSymbol(); 657 { 658 Node* object = m.Parameter(0); 659 Node* unique_name = m.Parameter(1); 660 Node* context = m.Parameter(kNumParams + 2); 661 662 Variable var_value(&m, MachineRepresentation::kTagged); 663 Label if_found(&m), if_not_found(&m), if_bailout(&m); 664 665 Node* map = m.LoadMap(object); 666 Node* instance_type = m.LoadMapInstanceType(map); 667 668 m.TryGetOwnProperty(context, object, object, map, instance_type, 669 unique_name, &if_found, &var_value, &if_not_found, 670 &if_bailout); 671 672 m.Bind(&if_found); 673 m.Return(var_value.value()); 674 675 m.Bind(&if_not_found); 676 m.Return(m.HeapConstant(not_found_symbol)); 677 678 m.Bind(&if_bailout); 679 m.Return(m.HeapConstant(bailout_symbol)); 680 } 681 682 Handle<Code> code = m.GenerateCode(); 683 FunctionTester ft(code, kNumParams); 684 685 Handle<Name> deleted_property_name = 686 factory->InternalizeUtf8String("deleted"); 687 688 Handle<Name> names[] = { 689 factory->InternalizeUtf8String("bb"), 690 factory->NewSymbol(), 691 factory->InternalizeUtf8String("a"), 692 factory->InternalizeUtf8String("ccc"), 693 factory->InternalizeUtf8String("esajefe"), 694 factory->NewPrivateSymbol(), 695 factory->InternalizeUtf8String("eeeee"), 696 factory->InternalizeUtf8String("p1"), 697 factory->InternalizeUtf8String("acshw23e"), 698 factory->InternalizeUtf8String(""), 699 factory->InternalizeUtf8String("dddd"), 700 factory->NewPrivateSymbol(), 701 factory->InternalizeUtf8String("name"), 702 factory->InternalizeUtf8String("p2"), 703 factory->InternalizeUtf8String("p3"), 704 factory->InternalizeUtf8String("p4"), 705 factory->NewPrivateSymbol(), 706 }; 707 Handle<Object> values[] = { 708 factory->NewFunction(factory->empty_string()), 709 factory->NewSymbol(), 710 factory->InternalizeUtf8String("a"), 711 CreateAccessorPair(&ft, "() => 188;", "() => 199;"), 712 factory->NewFunction(factory->InternalizeUtf8String("bb")), 713 factory->InternalizeUtf8String("ccc"), 714 CreateAccessorPair(&ft, "() => 88;", nullptr), 715 handle(Smi::FromInt(1), isolate), 716 factory->InternalizeUtf8String(""), 717 CreateAccessorPair(&ft, nullptr, "() => 99;"), 718 factory->NewHeapNumber(4.2), 719 handle(Smi::FromInt(153), isolate), 720 factory->NewJSObject(factory->NewFunction(factory->empty_string())), 721 factory->NewPrivateSymbol(), 722 }; 723 STATIC_ASSERT(arraysize(values) < arraysize(names)); 724 725 base::RandomNumberGenerator rand_gen(FLAG_random_seed); 726 727 std::vector<Handle<JSObject>> objects; 728 729 { 730 // Fast object, no inobject properties. 731 int inobject_properties = 0; 732 Handle<Map> map = Map::Create(isolate, inobject_properties); 733 Handle<JSObject> object = factory->NewJSObjectFromMap(map); 734 AddProperties(object, names, arraysize(names), values, arraysize(values), 735 rand_gen.NextInt()); 736 CHECK_EQ(JS_OBJECT_TYPE, object->map()->instance_type()); 737 CHECK_EQ(inobject_properties, object->map()->GetInObjectProperties()); 738 CHECK(!object->map()->is_dictionary_map()); 739 objects.push_back(object); 740 } 741 742 { 743 // Fast object, all inobject properties. 744 int inobject_properties = arraysize(names) * 2; 745 Handle<Map> map = Map::Create(isolate, inobject_properties); 746 Handle<JSObject> object = factory->NewJSObjectFromMap(map); 747 AddProperties(object, names, arraysize(names), values, arraysize(values), 748 rand_gen.NextInt()); 749 CHECK_EQ(JS_OBJECT_TYPE, object->map()->instance_type()); 750 CHECK_EQ(inobject_properties, object->map()->GetInObjectProperties()); 751 CHECK(!object->map()->is_dictionary_map()); 752 objects.push_back(object); 753 } 754 755 { 756 // Fast object, half inobject properties. 757 int inobject_properties = arraysize(names) / 2; 758 Handle<Map> map = Map::Create(isolate, inobject_properties); 759 Handle<JSObject> object = factory->NewJSObjectFromMap(map); 760 AddProperties(object, names, arraysize(names), values, arraysize(values), 761 rand_gen.NextInt()); 762 CHECK_EQ(JS_OBJECT_TYPE, object->map()->instance_type()); 763 CHECK_EQ(inobject_properties, object->map()->GetInObjectProperties()); 764 CHECK(!object->map()->is_dictionary_map()); 765 objects.push_back(object); 766 } 767 768 { 769 // Dictionary mode object. 770 Handle<JSFunction> function = factory->NewFunction(factory->empty_string()); 771 Handle<JSObject> object = factory->NewJSObject(function); 772 AddProperties(object, names, arraysize(names), values, arraysize(values), 773 rand_gen.NextInt()); 774 JSObject::NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0, "test"); 775 776 JSObject::AddProperty(object, deleted_property_name, object, NONE); 777 CHECK(JSObject::DeleteProperty(object, deleted_property_name, SLOPPY) 778 .FromJust()); 779 780 CHECK_EQ(JS_OBJECT_TYPE, object->map()->instance_type()); 781 CHECK(object->map()->is_dictionary_map()); 782 objects.push_back(object); 783 } 784 785 { 786 // Global object. 787 Handle<JSGlobalObject> object = isolate->global_object(); 788 AddProperties(object, names, arraysize(names), values, arraysize(values), 789 rand_gen.NextInt()); 790 791 JSObject::AddProperty(object, deleted_property_name, object, NONE); 792 CHECK(JSObject::DeleteProperty(object, deleted_property_name, SLOPPY) 793 .FromJust()); 794 795 CHECK_EQ(JS_GLOBAL_OBJECT_TYPE, object->map()->instance_type()); 796 CHECK(object->map()->is_dictionary_map()); 797 objects.push_back(object); 798 } 799 800 // TODO(ishell): test proxy and interceptors when they are supported. 801 802 { 803 for (Handle<JSObject> object : objects) { 804 for (size_t name_index = 0; name_index < arraysize(names); name_index++) { 805 Handle<Name> name = names[name_index]; 806 Handle<Object> expected_value = 807 JSReceiver::GetProperty(object, name).ToHandleChecked(); 808 Handle<Object> value = ft.Call(object, name).ToHandleChecked(); 809 CHECK(expected_value->SameValue(*value)); 810 } 811 } 812 } 813 814 { 815 Handle<Name> non_existing_names[] = { 816 factory->NewSymbol(), 817 factory->InternalizeUtf8String("ne_a"), 818 factory->InternalizeUtf8String("ne_bb"), 819 factory->NewPrivateSymbol(), 820 factory->InternalizeUtf8String("ne_ccc"), 821 factory->InternalizeUtf8String("ne_dddd"), 822 deleted_property_name, 823 }; 824 for (Handle<JSObject> object : objects) { 825 for (size_t key_index = 0; key_index < arraysize(non_existing_names); 826 key_index++) { 827 Handle<Name> name = non_existing_names[key_index]; 828 Handle<Object> expected_value = 829 JSReceiver::GetProperty(object, name).ToHandleChecked(); 830 CHECK(expected_value->IsUndefined(isolate)); 831 Handle<Object> value = ft.Call(object, name).ToHandleChecked(); 832 CHECK_EQ(*not_found_symbol, *value); 833 } 834 } 835 } 836 837 { 838 Handle<JSFunction> function = factory->NewFunction(factory->empty_string()); 839 Handle<JSProxy> object = factory->NewJSProxy(function, objects[0]); 840 CHECK_EQ(JS_PROXY_TYPE, object->map()->instance_type()); 841 Handle<Object> value = ft.Call(object, names[0]).ToHandleChecked(); 842 // Proxies are not supported yet. 843 CHECK_EQ(*bailout_symbol, *value); 844 } 845 846 { 847 Handle<JSObject> object = isolate->global_proxy(); 848 CHECK_EQ(JS_GLOBAL_PROXY_TYPE, object->map()->instance_type()); 849 // Global proxies are not supported yet. 850 Handle<Object> value = ft.Call(object, names[0]).ToHandleChecked(); 851 CHECK_EQ(*bailout_symbol, *value); 852 } 853 } 854 855 namespace { 856 857 void AddElement(Handle<JSObject> object, uint32_t index, Handle<Object> value, 858 PropertyAttributes attributes = NONE) { 859 JSObject::AddDataElement(object, index, value, attributes).ToHandleChecked(); 860 } 861 862 } // namespace 863 864 TEST(TryLookupElement) { 865 typedef CodeStubAssembler::Label Label; 866 Isolate* isolate(CcTest::InitIsolateOnce()); 867 868 const int kNumParams = 3; 869 CodeStubAssemblerTester m(isolate, kNumParams); 870 871 enum Result { kFound, kNotFound, kBailout }; 872 { 873 Node* object = m.Parameter(0); 874 Node* index = m.SmiToWord32(m.Parameter(1)); 875 Node* expected_result = m.Parameter(2); 876 877 Label passed(&m), failed(&m); 878 Label if_found(&m), if_not_found(&m), if_bailout(&m); 879 880 Node* map = m.LoadMap(object); 881 Node* instance_type = m.LoadMapInstanceType(map); 882 883 m.TryLookupElement(object, map, instance_type, index, &if_found, 884 &if_not_found, &if_bailout); 885 886 m.Bind(&if_found); 887 m.Branch(m.WordEqual(expected_result, m.SmiConstant(Smi::FromInt(kFound))), 888 &passed, &failed); 889 890 m.Bind(&if_not_found); 891 m.Branch( 892 m.WordEqual(expected_result, m.SmiConstant(Smi::FromInt(kNotFound))), 893 &passed, &failed); 894 895 m.Bind(&if_bailout); 896 m.Branch( 897 m.WordEqual(expected_result, m.SmiConstant(Smi::FromInt(kBailout))), 898 &passed, &failed); 899 900 m.Bind(&passed); 901 m.Return(m.BooleanConstant(true)); 902 903 m.Bind(&failed); 904 m.Return(m.BooleanConstant(false)); 905 } 906 907 Handle<Code> code = m.GenerateCode(); 908 FunctionTester ft(code, kNumParams); 909 910 Factory* factory = isolate->factory(); 911 Handle<Object> smi0(Smi::FromInt(0), isolate); 912 Handle<Object> smi1(Smi::FromInt(1), isolate); 913 Handle<Object> smi7(Smi::FromInt(7), isolate); 914 Handle<Object> smi13(Smi::FromInt(13), isolate); 915 Handle<Object> smi42(Smi::FromInt(42), isolate); 916 917 Handle<Object> expect_found(Smi::FromInt(kFound), isolate); 918 Handle<Object> expect_not_found(Smi::FromInt(kNotFound), isolate); 919 Handle<Object> expect_bailout(Smi::FromInt(kBailout), isolate); 920 921 #define CHECK_FOUND(object, index) \ 922 CHECK(JSReceiver::HasElement(object, index).FromJust()); \ 923 ft.CheckTrue(object, smi##index, expect_found); 924 925 #define CHECK_NOT_FOUND(object, index) \ 926 CHECK(!JSReceiver::HasElement(object, index).FromJust()); \ 927 ft.CheckTrue(object, smi##index, expect_not_found); 928 929 { 930 Handle<JSArray> object = factory->NewJSArray(0, FAST_SMI_ELEMENTS); 931 AddElement(object, 0, smi0); 932 AddElement(object, 1, smi0); 933 CHECK_EQ(FAST_SMI_ELEMENTS, object->map()->elements_kind()); 934 935 CHECK_FOUND(object, 0); 936 CHECK_FOUND(object, 1); 937 CHECK_NOT_FOUND(object, 7); 938 CHECK_NOT_FOUND(object, 13); 939 CHECK_NOT_FOUND(object, 42); 940 } 941 942 { 943 Handle<JSArray> object = factory->NewJSArray(0, FAST_HOLEY_SMI_ELEMENTS); 944 AddElement(object, 0, smi0); 945 AddElement(object, 13, smi0); 946 CHECK_EQ(FAST_HOLEY_SMI_ELEMENTS, object->map()->elements_kind()); 947 948 CHECK_FOUND(object, 0); 949 CHECK_NOT_FOUND(object, 1); 950 CHECK_NOT_FOUND(object, 7); 951 CHECK_FOUND(object, 13); 952 CHECK_NOT_FOUND(object, 42); 953 } 954 955 { 956 Handle<JSArray> object = factory->NewJSArray(0, FAST_ELEMENTS); 957 AddElement(object, 0, smi0); 958 AddElement(object, 1, smi0); 959 CHECK_EQ(FAST_ELEMENTS, object->map()->elements_kind()); 960 961 CHECK_FOUND(object, 0); 962 CHECK_FOUND(object, 1); 963 CHECK_NOT_FOUND(object, 7); 964 CHECK_NOT_FOUND(object, 13); 965 CHECK_NOT_FOUND(object, 42); 966 } 967 968 { 969 Handle<JSArray> object = factory->NewJSArray(0, FAST_HOLEY_ELEMENTS); 970 AddElement(object, 0, smi0); 971 AddElement(object, 13, smi0); 972 CHECK_EQ(FAST_HOLEY_ELEMENTS, object->map()->elements_kind()); 973 974 CHECK_FOUND(object, 0); 975 CHECK_NOT_FOUND(object, 1); 976 CHECK_NOT_FOUND(object, 7); 977 CHECK_FOUND(object, 13); 978 CHECK_NOT_FOUND(object, 42); 979 } 980 981 { 982 Handle<JSFunction> constructor = isolate->string_function(); 983 Handle<JSObject> object = factory->NewJSObject(constructor); 984 Handle<String> str = factory->InternalizeUtf8String("ab"); 985 Handle<JSValue>::cast(object)->set_value(*str); 986 AddElement(object, 13, smi0); 987 CHECK_EQ(FAST_STRING_WRAPPER_ELEMENTS, object->map()->elements_kind()); 988 989 CHECK_FOUND(object, 0); 990 CHECK_FOUND(object, 1); 991 CHECK_NOT_FOUND(object, 7); 992 CHECK_FOUND(object, 13); 993 CHECK_NOT_FOUND(object, 42); 994 } 995 996 { 997 Handle<JSFunction> constructor = isolate->string_function(); 998 Handle<JSObject> object = factory->NewJSObject(constructor); 999 Handle<String> str = factory->InternalizeUtf8String("ab"); 1000 Handle<JSValue>::cast(object)->set_value(*str); 1001 AddElement(object, 13, smi0); 1002 JSObject::NormalizeElements(object); 1003 CHECK_EQ(SLOW_STRING_WRAPPER_ELEMENTS, object->map()->elements_kind()); 1004 1005 CHECK_FOUND(object, 0); 1006 CHECK_FOUND(object, 1); 1007 CHECK_NOT_FOUND(object, 7); 1008 CHECK_FOUND(object, 13); 1009 CHECK_NOT_FOUND(object, 42); 1010 } 1011 1012 // TODO(ishell): uncomment once NO_ELEMENTS kind is supported. 1013 // { 1014 // Handle<Map> map = Map::Create(isolate, 0); 1015 // map->set_elements_kind(NO_ELEMENTS); 1016 // Handle<JSObject> object = factory->NewJSObjectFromMap(map); 1017 // CHECK_EQ(NO_ELEMENTS, object->map()->elements_kind()); 1018 // 1019 // CHECK_NOT_FOUND(object, 0); 1020 // CHECK_NOT_FOUND(object, 1); 1021 // CHECK_NOT_FOUND(object, 7); 1022 // CHECK_NOT_FOUND(object, 13); 1023 // CHECK_NOT_FOUND(object, 42); 1024 // } 1025 1026 #undef CHECK_FOUND 1027 #undef CHECK_NOT_FOUND 1028 1029 { 1030 Handle<JSArray> handler = factory->NewJSArray(0); 1031 Handle<JSFunction> function = factory->NewFunction(factory->empty_string()); 1032 Handle<JSProxy> object = factory->NewJSProxy(function, handler); 1033 CHECK_EQ(JS_PROXY_TYPE, object->map()->instance_type()); 1034 ft.CheckTrue(object, smi0, expect_bailout); 1035 } 1036 1037 { 1038 Handle<JSObject> object = isolate->global_object(); 1039 CHECK_EQ(JS_GLOBAL_OBJECT_TYPE, object->map()->instance_type()); 1040 ft.CheckTrue(object, smi0, expect_bailout); 1041 } 1042 1043 { 1044 Handle<JSObject> object = isolate->global_proxy(); 1045 CHECK_EQ(JS_GLOBAL_PROXY_TYPE, object->map()->instance_type()); 1046 ft.CheckTrue(object, smi0, expect_bailout); 1047 } 1048 } 1049 1050 TEST(DeferredCodePhiHints) { 1051 typedef compiler::Node Node; 1052 typedef CodeStubAssembler::Label Label; 1053 typedef CodeStubAssembler::Variable Variable; 1054 Isolate* isolate(CcTest::InitIsolateOnce()); 1055 VoidDescriptor descriptor(isolate); 1056 CodeStubAssemblerTester m(isolate, descriptor); 1057 Label block1(&m, Label::kDeferred); 1058 m.Goto(&block1); 1059 m.Bind(&block1); 1060 { 1061 Variable var_object(&m, MachineRepresentation::kTagged); 1062 Label loop(&m, &var_object); 1063 var_object.Bind(m.IntPtrConstant(0)); 1064 m.Goto(&loop); 1065 m.Bind(&loop); 1066 { 1067 Node* map = m.LoadMap(var_object.value()); 1068 var_object.Bind(map); 1069 m.Goto(&loop); 1070 } 1071 } 1072 CHECK(!m.GenerateCode().is_null()); 1073 } 1074 1075 TEST(TestOutOfScopeVariable) { 1076 typedef CodeStubAssembler::Label Label; 1077 typedef CodeStubAssembler::Variable Variable; 1078 Isolate* isolate(CcTest::InitIsolateOnce()); 1079 VoidDescriptor descriptor(isolate); 1080 CodeStubAssemblerTester m(isolate, descriptor); 1081 Label block1(&m); 1082 Label block2(&m); 1083 Label block3(&m); 1084 Label block4(&m); 1085 m.Branch(m.WordEqual(m.Parameter(0), m.IntPtrConstant(0)), &block1, &block4); 1086 m.Bind(&block4); 1087 { 1088 Variable var_object(&m, MachineRepresentation::kTagged); 1089 m.Branch(m.WordEqual(m.Parameter(0), m.IntPtrConstant(0)), &block2, 1090 &block3); 1091 1092 m.Bind(&block2); 1093 var_object.Bind(m.IntPtrConstant(55)); 1094 m.Goto(&block1); 1095 1096 m.Bind(&block3); 1097 var_object.Bind(m.IntPtrConstant(66)); 1098 m.Goto(&block1); 1099 } 1100 m.Bind(&block1); 1101 CHECK(!m.GenerateCode().is_null()); 1102 } 1103 1104 namespace { 1105 1106 void TestStubCacheOffsetCalculation(StubCache::Table table, 1107 Code::Kind handler_kind) { 1108 Isolate* isolate(CcTest::InitIsolateOnce()); 1109 const int kNumParams = 2; 1110 CodeStubAssemblerTester m(isolate, kNumParams); 1111 1112 Code::Flags code_flags = 1113 Code::RemoveHolderFromFlags(Code::ComputeHandlerFlags(handler_kind)); 1114 { 1115 Node* name = m.Parameter(0); 1116 Node* map = m.Parameter(1); 1117 Node* primary_offset = m.StubCachePrimaryOffset(name, code_flags, map); 1118 Node* result; 1119 if (table == StubCache::kPrimary) { 1120 result = primary_offset; 1121 } else { 1122 CHECK_EQ(StubCache::kSecondary, table); 1123 result = m.StubCacheSecondaryOffset(name, code_flags, primary_offset); 1124 } 1125 m.Return(m.SmiFromWord32(result)); 1126 } 1127 1128 Handle<Code> code = m.GenerateCode(); 1129 FunctionTester ft(code, kNumParams); 1130 1131 Factory* factory = isolate->factory(); 1132 Handle<Name> names[] = { 1133 factory->NewSymbol(), 1134 factory->InternalizeUtf8String("a"), 1135 factory->InternalizeUtf8String("bb"), 1136 factory->InternalizeUtf8String("ccc"), 1137 factory->NewPrivateSymbol(), 1138 factory->InternalizeUtf8String("dddd"), 1139 factory->InternalizeUtf8String("eeeee"), 1140 factory->InternalizeUtf8String("name"), 1141 factory->NewSymbol(), 1142 factory->NewPrivateSymbol(), 1143 }; 1144 1145 Handle<Map> maps[] = { 1146 Handle<Map>(nullptr, isolate), 1147 factory->cell_map(), 1148 Map::Create(isolate, 0), 1149 factory->meta_map(), 1150 factory->code_map(), 1151 Map::Create(isolate, 0), 1152 factory->hash_table_map(), 1153 factory->symbol_map(), 1154 factory->string_map(), 1155 Map::Create(isolate, 0), 1156 factory->sloppy_arguments_elements_map(), 1157 }; 1158 1159 for (int name_index = 0; name_index < arraysize(names); name_index++) { 1160 Handle<Name> name = names[name_index]; 1161 for (int map_index = 0; map_index < arraysize(maps); map_index++) { 1162 Handle<Map> map = maps[map_index]; 1163 1164 int expected_result; 1165 { 1166 int primary_offset = 1167 StubCache::PrimaryOffsetForTesting(*name, code_flags, *map); 1168 if (table == StubCache::kPrimary) { 1169 expected_result = primary_offset; 1170 } else { 1171 expected_result = StubCache::SecondaryOffsetForTesting( 1172 *name, code_flags, primary_offset); 1173 } 1174 } 1175 Handle<Object> result = ft.Call(name, map).ToHandleChecked(); 1176 1177 Smi* expected = Smi::FromInt(expected_result & Smi::kMaxValue); 1178 CHECK_EQ(expected, Smi::cast(*result)); 1179 } 1180 } 1181 } 1182 1183 } // namespace 1184 1185 TEST(StubCachePrimaryOffsetLoadIC) { 1186 TestStubCacheOffsetCalculation(StubCache::kPrimary, Code::LOAD_IC); 1187 } 1188 1189 TEST(StubCachePrimaryOffsetStoreIC) { 1190 TestStubCacheOffsetCalculation(StubCache::kPrimary, Code::STORE_IC); 1191 } 1192 1193 TEST(StubCacheSecondaryOffsetLoadIC) { 1194 TestStubCacheOffsetCalculation(StubCache::kSecondary, Code::LOAD_IC); 1195 } 1196 1197 TEST(StubCacheSecondaryOffsetStoreIC) { 1198 TestStubCacheOffsetCalculation(StubCache::kSecondary, Code::STORE_IC); 1199 } 1200 1201 namespace { 1202 1203 Handle<Code> CreateCodeWithFlags(Code::Flags flags) { 1204 Isolate* isolate(CcTest::InitIsolateOnce()); 1205 CodeStubAssemblerTester m(isolate, flags); 1206 m.Return(m.UndefinedConstant()); 1207 return m.GenerateCodeCloseAndEscape(); 1208 } 1209 1210 } // namespace 1211 1212 TEST(TryProbeStubCache) { 1213 typedef CodeStubAssembler::Label Label; 1214 typedef CodeStubAssembler::Variable Variable; 1215 Isolate* isolate(CcTest::InitIsolateOnce()); 1216 const int kNumParams = 3; 1217 CodeStubAssemblerTester m(isolate, kNumParams); 1218 1219 Code::Flags flags_to_query = 1220 Code::RemoveHolderFromFlags(Code::ComputeHandlerFlags(Code::LOAD_IC)); 1221 1222 StubCache stub_cache(isolate); 1223 stub_cache.Clear(); 1224 1225 { 1226 Node* receiver = m.Parameter(0); 1227 Node* name = m.Parameter(1); 1228 Node* expected_handler = m.Parameter(2); 1229 1230 Label passed(&m), failed(&m); 1231 1232 Variable var_handler(&m, MachineRepresentation::kTagged); 1233 Label if_handler(&m), if_miss(&m); 1234 1235 m.TryProbeStubCache(&stub_cache, flags_to_query, receiver, name, 1236 &if_handler, &var_handler, &if_miss); 1237 m.Bind(&if_handler); 1238 m.BranchIfWordEqual(expected_handler, var_handler.value(), &passed, 1239 &failed); 1240 1241 m.Bind(&if_miss); 1242 m.BranchIfWordEqual(expected_handler, m.IntPtrConstant(0), &passed, 1243 &failed); 1244 1245 m.Bind(&passed); 1246 m.Return(m.BooleanConstant(true)); 1247 1248 m.Bind(&failed); 1249 m.Return(m.BooleanConstant(false)); 1250 } 1251 1252 Handle<Code> code = m.GenerateCode(); 1253 FunctionTester ft(code, kNumParams); 1254 1255 std::vector<Handle<Name>> names; 1256 std::vector<Handle<JSObject>> receivers; 1257 std::vector<Handle<Code>> handlers; 1258 1259 base::RandomNumberGenerator rand_gen(FLAG_random_seed); 1260 1261 Factory* factory = isolate->factory(); 1262 1263 // Generate some number of names. 1264 for (int i = 0; i < StubCache::kPrimaryTableSize / 7; i++) { 1265 Handle<Name> name; 1266 switch (rand_gen.NextInt(3)) { 1267 case 0: { 1268 // Generate string. 1269 std::stringstream ss; 1270 ss << "s" << std::hex 1271 << (rand_gen.NextInt(Smi::kMaxValue) % StubCache::kPrimaryTableSize); 1272 name = factory->InternalizeUtf8String(ss.str().c_str()); 1273 break; 1274 } 1275 case 1: { 1276 // Generate number string. 1277 std::stringstream ss; 1278 ss << (rand_gen.NextInt(Smi::kMaxValue) % StubCache::kPrimaryTableSize); 1279 name = factory->InternalizeUtf8String(ss.str().c_str()); 1280 break; 1281 } 1282 case 2: { 1283 // Generate symbol. 1284 name = factory->NewSymbol(); 1285 break; 1286 } 1287 default: 1288 UNREACHABLE(); 1289 } 1290 names.push_back(name); 1291 } 1292 1293 // Generate some number of receiver maps and receivers. 1294 for (int i = 0; i < StubCache::kSecondaryTableSize / 2; i++) { 1295 Handle<Map> map = Map::Create(isolate, 0); 1296 receivers.push_back(factory->NewJSObjectFromMap(map)); 1297 } 1298 1299 // Generate some number of handlers. 1300 for (int i = 0; i < 30; i++) { 1301 Code::Kind code_kind; 1302 switch (rand_gen.NextInt(4)) { 1303 case 0: 1304 code_kind = Code::LOAD_IC; 1305 break; 1306 case 1: 1307 code_kind = Code::KEYED_LOAD_IC; 1308 break; 1309 case 2: 1310 code_kind = Code::STORE_IC; 1311 break; 1312 case 3: 1313 code_kind = Code::KEYED_STORE_IC; 1314 break; 1315 default: 1316 UNREACHABLE(); 1317 } 1318 Code::Flags flags = 1319 Code::RemoveHolderFromFlags(Code::ComputeHandlerFlags(code_kind)); 1320 handlers.push_back(CreateCodeWithFlags(flags)); 1321 } 1322 1323 // Ensure that GC does happen because from now on we are going to fill our 1324 // own stub cache instance with raw values. 1325 DisallowHeapAllocation no_gc; 1326 1327 // Populate {stub_cache}. 1328 const int N = StubCache::kPrimaryTableSize + StubCache::kSecondaryTableSize; 1329 for (int i = 0; i < N; i++) { 1330 int index = rand_gen.NextInt(); 1331 Handle<Name> name = names[index % names.size()]; 1332 Handle<JSObject> receiver = receivers[index % receivers.size()]; 1333 Handle<Code> handler = handlers[index % handlers.size()]; 1334 stub_cache.Set(*name, receiver->map(), *handler); 1335 } 1336 1337 // Perform some queries. 1338 bool queried_existing = false; 1339 bool queried_non_existing = false; 1340 for (int i = 0; i < N; i++) { 1341 int index = rand_gen.NextInt(); 1342 Handle<Name> name = names[index % names.size()]; 1343 Handle<JSObject> receiver = receivers[index % receivers.size()]; 1344 Code* handler = stub_cache.Get(*name, receiver->map(), flags_to_query); 1345 if (handler == nullptr) { 1346 queried_non_existing = true; 1347 } else { 1348 queried_existing = true; 1349 } 1350 1351 Handle<Code> expected_handler(handler, isolate); 1352 ft.CheckTrue(receiver, name, expected_handler); 1353 } 1354 1355 for (int i = 0; i < N; i++) { 1356 int index1 = rand_gen.NextInt(); 1357 int index2 = rand_gen.NextInt(); 1358 Handle<Name> name = names[index1 % names.size()]; 1359 Handle<JSObject> receiver = receivers[index2 % receivers.size()]; 1360 Code* handler = stub_cache.Get(*name, receiver->map(), flags_to_query); 1361 if (handler == nullptr) { 1362 queried_non_existing = true; 1363 } else { 1364 queried_existing = true; 1365 } 1366 1367 Handle<Code> expected_handler(handler, isolate); 1368 ft.CheckTrue(receiver, name, expected_handler); 1369 } 1370 // Ensure we performed both kind of queries. 1371 CHECK(queried_existing && queried_non_existing); 1372 } 1373 1374 } // namespace internal 1375 } // namespace v8 1376