1 // Copyright 2012 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/transitions.h" 6 7 #include "src/objects-inl.h" 8 #include "src/transitions-inl.h" 9 #include "src/utils.h" 10 11 namespace v8 { 12 namespace internal { 13 14 void TransitionsAccessor::Initialize() { 15 raw_transitions_ = map_->raw_transitions(); 16 HeapObject* heap_object; 17 if (raw_transitions_->IsSmi() || 18 raw_transitions_->IsClearedWeakHeapObject()) { 19 encoding_ = kUninitialized; 20 } else if (raw_transitions_->IsWeakHeapObject()) { 21 encoding_ = kWeakRef; 22 } else if (raw_transitions_->ToStrongHeapObject(&heap_object)) { 23 if (heap_object->IsTransitionArray()) { 24 encoding_ = kFullTransitionArray; 25 } else { 26 DCHECK(heap_object->IsPrototypeInfo()); 27 encoding_ = kPrototypeInfo; 28 } 29 } else { 30 UNREACHABLE(); 31 } 32 #if DEBUG 33 needs_reload_ = false; 34 #endif 35 } 36 37 Map* TransitionsAccessor::GetSimpleTransition() { 38 switch (encoding()) { 39 case kWeakRef: 40 return Map::cast(raw_transitions_->ToWeakHeapObject()); 41 default: 42 return nullptr; 43 } 44 } 45 46 bool TransitionsAccessor::HasSimpleTransitionTo(Map* map) { 47 switch (encoding()) { 48 case kWeakRef: 49 return raw_transitions_->ToWeakHeapObject() == map; 50 case kPrototypeInfo: 51 case kUninitialized: 52 case kFullTransitionArray: 53 return false; 54 } 55 UNREACHABLE(); 56 } 57 58 void TransitionsAccessor::Insert(Handle<Name> name, Handle<Map> target, 59 SimpleTransitionFlag flag) { 60 DCHECK(!map_handle_.is_null()); 61 target->SetBackPointer(map_); 62 63 // If the map doesn't have any transitions at all yet, install the new one. 64 if (encoding() == kUninitialized) { 65 if (flag == SIMPLE_PROPERTY_TRANSITION) { 66 ReplaceTransitions(HeapObjectReference::Weak(*target)); 67 return; 68 } 69 // If the flag requires a full TransitionArray, allocate one. 70 Handle<TransitionArray> result = 71 isolate_->factory()->NewTransitionArray(0, 1); 72 ReplaceTransitions(MaybeObject::FromObject(*result)); 73 Reload(); 74 } 75 76 bool is_special_transition = flag == SPECIAL_TRANSITION; 77 // If the map has a simple transition, check if it should be overwritten. 78 Map* simple_transition = GetSimpleTransition(); 79 if (simple_transition != nullptr) { 80 Name* key = GetSimpleTransitionKey(simple_transition); 81 PropertyDetails old_details = GetSimpleTargetDetails(simple_transition); 82 PropertyDetails new_details = is_special_transition 83 ? PropertyDetails::Empty() 84 : GetTargetDetails(*name, *target); 85 if (flag == SIMPLE_PROPERTY_TRANSITION && key->Equals(*name) && 86 old_details.kind() == new_details.kind() && 87 old_details.attributes() == new_details.attributes()) { 88 ReplaceTransitions(HeapObjectReference::Weak(*target)); 89 return; 90 } 91 // Otherwise allocate a full TransitionArray with slack for a new entry. 92 Handle<Map> map(simple_transition, isolate_); 93 Handle<TransitionArray> result = 94 isolate_->factory()->NewTransitionArray(1, 1); 95 // Reload state; allocations might have caused it to be cleared. 96 Reload(); 97 simple_transition = GetSimpleTransition(); 98 if (simple_transition != nullptr) { 99 DCHECK_EQ(*map, simple_transition); 100 if (encoding_ == kWeakRef) { 101 result->Set(0, GetSimpleTransitionKey(simple_transition), 102 HeapObjectReference::Weak(simple_transition)); 103 } else { 104 UNREACHABLE(); 105 } 106 } else { 107 result->SetNumberOfTransitions(0); 108 } 109 ReplaceTransitions(MaybeObject::FromObject(*result)); 110 Reload(); 111 } 112 113 // At this point, we know that the map has a full TransitionArray. 114 DCHECK_EQ(kFullTransitionArray, encoding()); 115 116 int number_of_transitions = 0; 117 int new_nof = 0; 118 int insertion_index = kNotFound; 119 DCHECK_EQ(is_special_transition, 120 IsSpecialTransition(ReadOnlyRoots(isolate_), *name)); 121 PropertyDetails details = is_special_transition 122 ? PropertyDetails::Empty() 123 : GetTargetDetails(*name, *target); 124 125 { 126 DisallowHeapAllocation no_gc; 127 TransitionArray* array = transitions(); 128 number_of_transitions = array->number_of_transitions(); 129 new_nof = number_of_transitions; 130 131 int index = 132 is_special_transition 133 ? array->SearchSpecial(Symbol::cast(*name), &insertion_index) 134 : array->Search(details.kind(), *name, details.attributes(), 135 &insertion_index); 136 // If an existing entry was found, overwrite it and return. 137 if (index != kNotFound) { 138 array->SetRawTarget(index, HeapObjectReference::Weak(*target)); 139 return; 140 } 141 142 ++new_nof; 143 CHECK_LE(new_nof, kMaxNumberOfTransitions); 144 DCHECK(insertion_index >= 0 && insertion_index <= number_of_transitions); 145 146 // If there is enough capacity, insert new entry into the existing array. 147 if (new_nof <= array->Capacity()) { 148 array->SetNumberOfTransitions(new_nof); 149 for (index = number_of_transitions; index > insertion_index; --index) { 150 array->SetKey(index, array->GetKey(index - 1)); 151 array->SetRawTarget(index, array->GetRawTarget(index - 1)); 152 } 153 array->SetKey(index, *name); 154 array->SetRawTarget(index, HeapObjectReference::Weak(*target)); 155 SLOW_DCHECK(array->IsSortedNoDuplicates()); 156 return; 157 } 158 } 159 160 // We're gonna need a bigger TransitionArray. 161 Handle<TransitionArray> result = isolate_->factory()->NewTransitionArray( 162 new_nof, 163 Map::SlackForArraySize(number_of_transitions, kMaxNumberOfTransitions)); 164 165 // The map's transition array may have shrunk during the allocation above as 166 // it was weakly traversed, though it is guaranteed not to disappear. Trim the 167 // result copy if needed, and recompute variables. 168 Reload(); 169 DisallowHeapAllocation no_gc; 170 TransitionArray* array = transitions(); 171 if (array->number_of_transitions() != number_of_transitions) { 172 DCHECK(array->number_of_transitions() < number_of_transitions); 173 174 number_of_transitions = array->number_of_transitions(); 175 new_nof = number_of_transitions; 176 177 insertion_index = kNotFound; 178 int index = 179 is_special_transition 180 ? array->SearchSpecial(Symbol::cast(*name), &insertion_index) 181 : array->Search(details.kind(), *name, details.attributes(), 182 &insertion_index); 183 if (index == kNotFound) { 184 ++new_nof; 185 } else { 186 insertion_index = index; 187 } 188 DCHECK(insertion_index >= 0 && insertion_index <= number_of_transitions); 189 190 result->SetNumberOfTransitions(new_nof); 191 } 192 193 if (array->HasPrototypeTransitions()) { 194 result->SetPrototypeTransitions(array->GetPrototypeTransitions()); 195 } 196 197 DCHECK_NE(kNotFound, insertion_index); 198 for (int i = 0; i < insertion_index; ++i) { 199 result->Set(i, array->GetKey(i), array->GetRawTarget(i)); 200 } 201 result->Set(insertion_index, *name, HeapObjectReference::Weak(*target)); 202 for (int i = insertion_index; i < number_of_transitions; ++i) { 203 result->Set(i + 1, array->GetKey(i), array->GetRawTarget(i)); 204 } 205 206 SLOW_DCHECK(result->IsSortedNoDuplicates()); 207 ReplaceTransitions(MaybeObject::FromObject(*result)); 208 } 209 210 Map* TransitionsAccessor::SearchTransition(Name* name, PropertyKind kind, 211 PropertyAttributes attributes) { 212 DCHECK(name->IsUniqueName()); 213 switch (encoding()) { 214 case kPrototypeInfo: 215 case kUninitialized: 216 return nullptr; 217 case kWeakRef: { 218 Map* map = Map::cast(raw_transitions_->ToWeakHeapObject()); 219 if (!IsMatchingMap(map, name, kind, attributes)) return nullptr; 220 return map; 221 } 222 case kFullTransitionArray: { 223 int transition = transitions()->Search(kind, name, attributes); 224 if (transition == kNotFound) return nullptr; 225 return transitions()->GetTarget(transition); 226 } 227 } 228 UNREACHABLE(); 229 } 230 231 Map* TransitionsAccessor::SearchSpecial(Symbol* name) { 232 if (encoding() != kFullTransitionArray) return nullptr; 233 int transition = transitions()->SearchSpecial(name); 234 if (transition == kNotFound) return nullptr; 235 return transitions()->GetTarget(transition); 236 } 237 238 // static 239 bool TransitionsAccessor::IsSpecialTransition(ReadOnlyRoots roots, Name* name) { 240 if (!name->IsSymbol()) return false; 241 return name == roots.nonextensible_symbol() || 242 name == roots.sealed_symbol() || name == roots.frozen_symbol() || 243 name == roots.elements_transition_symbol() || 244 name == roots.strict_function_transition_symbol(); 245 } 246 247 MaybeHandle<Map> TransitionsAccessor::FindTransitionToDataProperty( 248 Handle<Name> name, RequestedLocation requested_location) { 249 DCHECK(name->IsUniqueName()); 250 DisallowHeapAllocation no_gc; 251 PropertyAttributes attributes = name->IsPrivate() ? DONT_ENUM : NONE; 252 Map* target = SearchTransition(*name, kData, attributes); 253 if (target == nullptr) return MaybeHandle<Map>(); 254 PropertyDetails details = target->GetLastDescriptorDetails(); 255 DCHECK_EQ(attributes, details.attributes()); 256 DCHECK_EQ(kData, details.kind()); 257 if (requested_location == kFieldOnly && details.location() != kField) { 258 return MaybeHandle<Map>(); 259 } 260 return Handle<Map>(target, isolate_); 261 } 262 263 Handle<String> TransitionsAccessor::ExpectedTransitionKey() { 264 DisallowHeapAllocation no_gc; 265 switch (encoding()) { 266 case kPrototypeInfo: 267 case kUninitialized: 268 case kFullTransitionArray: 269 return Handle<String>::null(); 270 case kWeakRef: { 271 Map* target = Map::cast(raw_transitions_->ToWeakHeapObject()); 272 PropertyDetails details = GetSimpleTargetDetails(target); 273 if (details.location() != kField) return Handle<String>::null(); 274 DCHECK_EQ(kData, details.kind()); 275 if (details.attributes() != NONE) return Handle<String>::null(); 276 Name* name = GetSimpleTransitionKey(target); 277 if (!name->IsString()) return Handle<String>::null(); 278 return handle(String::cast(name), isolate_); 279 } 280 } 281 UNREACHABLE(); 282 } 283 284 Handle<Map> TransitionsAccessor::ExpectedTransitionTarget() { 285 DCHECK(!ExpectedTransitionKey().is_null()); 286 return handle(GetTarget(0), isolate_); 287 } 288 289 bool TransitionsAccessor::CanHaveMoreTransitions() { 290 if (map_->is_dictionary_map()) return false; 291 if (encoding() == kFullTransitionArray) { 292 return transitions()->number_of_transitions() < kMaxNumberOfTransitions; 293 } 294 return true; 295 } 296 297 // static 298 bool TransitionsAccessor::IsMatchingMap(Map* target, Name* name, 299 PropertyKind kind, 300 PropertyAttributes attributes) { 301 int descriptor = target->LastAdded(); 302 DescriptorArray* descriptors = target->instance_descriptors(); 303 Name* key = descriptors->GetKey(descriptor); 304 if (key != name) return false; 305 PropertyDetails details = descriptors->GetDetails(descriptor); 306 return (details.kind() == kind && details.attributes() == attributes); 307 } 308 309 // static 310 bool TransitionArray::CompactPrototypeTransitionArray(Isolate* isolate, 311 WeakFixedArray* array) { 312 const int header = kProtoTransitionHeaderSize; 313 int number_of_transitions = NumberOfPrototypeTransitions(array); 314 if (number_of_transitions == 0) { 315 // Empty array cannot be compacted. 316 return false; 317 } 318 int new_number_of_transitions = 0; 319 for (int i = 0; i < number_of_transitions; i++) { 320 MaybeObject* target = array->Get(header + i); 321 DCHECK(target->IsClearedWeakHeapObject() || 322 (target->IsWeakHeapObject() && target->ToWeakHeapObject()->IsMap())); 323 if (!target->IsClearedWeakHeapObject()) { 324 if (new_number_of_transitions != i) { 325 array->Set(header + new_number_of_transitions, target); 326 } 327 new_number_of_transitions++; 328 } 329 } 330 // Fill slots that became free with undefined value. 331 MaybeObject* undefined = 332 MaybeObject::FromObject(*isolate->factory()->undefined_value()); 333 for (int i = new_number_of_transitions; i < number_of_transitions; i++) { 334 array->Set(header + i, undefined); 335 } 336 if (number_of_transitions != new_number_of_transitions) { 337 SetNumberOfPrototypeTransitions(array, new_number_of_transitions); 338 } 339 return new_number_of_transitions < number_of_transitions; 340 } 341 342 343 // static 344 Handle<WeakFixedArray> TransitionArray::GrowPrototypeTransitionArray( 345 Handle<WeakFixedArray> array, int new_capacity, Isolate* isolate) { 346 // Grow array by factor 2 up to MaxCachedPrototypeTransitions. 347 int capacity = array->length() - kProtoTransitionHeaderSize; 348 new_capacity = Min(kMaxCachedPrototypeTransitions, new_capacity); 349 DCHECK_GT(new_capacity, capacity); 350 int grow_by = new_capacity - capacity; 351 array = 352 isolate->factory()->CopyWeakFixedArrayAndGrow(array, grow_by, TENURED); 353 if (capacity < 0) { 354 // There was no prototype transitions array before, so the size 355 // couldn't be copied. Initialize it explicitly. 356 SetNumberOfPrototypeTransitions(*array, 0); 357 } 358 return array; 359 } 360 361 void TransitionsAccessor::PutPrototypeTransition(Handle<Object> prototype, 362 Handle<Map> target_map) { 363 DCHECK(HeapObject::cast(*prototype)->map()->IsMap()); 364 // Don't cache prototype transition if this map is either shared, or a map of 365 // a prototype. 366 if (map_->is_prototype_map()) return; 367 if (map_->is_dictionary_map() || !FLAG_cache_prototype_transitions) return; 368 369 const int header = TransitionArray::kProtoTransitionHeaderSize; 370 371 Handle<WeakFixedArray> cache(GetPrototypeTransitions(), isolate_); 372 int capacity = cache->length() - header; 373 int transitions = TransitionArray::NumberOfPrototypeTransitions(*cache) + 1; 374 375 if (transitions > capacity) { 376 // Grow the array if compacting it doesn't free space. 377 if (!TransitionArray::CompactPrototypeTransitionArray(isolate_, *cache)) { 378 if (capacity == TransitionArray::kMaxCachedPrototypeTransitions) return; 379 cache = TransitionArray::GrowPrototypeTransitionArray( 380 cache, 2 * transitions, isolate_); 381 Reload(); 382 SetPrototypeTransitions(cache); 383 } 384 } 385 386 // Reload number of transitions as they might have been compacted. 387 int last = TransitionArray::NumberOfPrototypeTransitions(*cache); 388 int entry = header + last; 389 390 cache->Set(entry, HeapObjectReference::Weak(*target_map)); 391 TransitionArray::SetNumberOfPrototypeTransitions(*cache, last + 1); 392 } 393 394 Handle<Map> TransitionsAccessor::GetPrototypeTransition( 395 Handle<Object> prototype) { 396 DisallowHeapAllocation no_gc; 397 WeakFixedArray* cache = GetPrototypeTransitions(); 398 int length = TransitionArray::NumberOfPrototypeTransitions(cache); 399 for (int i = 0; i < length; i++) { 400 MaybeObject* target = 401 cache->Get(TransitionArray::kProtoTransitionHeaderSize + i); 402 DCHECK(target->IsClearedWeakHeapObject() || target->IsWeakHeapObject()); 403 if (!target->IsClearedWeakHeapObject()) { 404 Map* map = Map::cast(target->ToWeakHeapObject()); 405 if (map->prototype() == *prototype) { 406 return handle(map, isolate_); 407 } 408 } 409 } 410 return Handle<Map>(); 411 } 412 413 WeakFixedArray* TransitionsAccessor::GetPrototypeTransitions() { 414 if (encoding() != kFullTransitionArray || 415 !transitions()->HasPrototypeTransitions()) { 416 return ReadOnlyRoots(isolate_).empty_weak_fixed_array(); 417 } 418 return transitions()->GetPrototypeTransitions(); 419 } 420 421 // static 422 void TransitionArray::SetNumberOfPrototypeTransitions( 423 WeakFixedArray* proto_transitions, int value) { 424 DCHECK_NE(proto_transitions->length(), 0); 425 proto_transitions->Set(kProtoTransitionNumberOfEntriesOffset, 426 MaybeObject::FromSmi(Smi::FromInt(value))); 427 } 428 429 int TransitionsAccessor::NumberOfTransitions() { 430 switch (encoding()) { 431 case kPrototypeInfo: 432 case kUninitialized: 433 return 0; 434 case kWeakRef: 435 return 1; 436 case kFullTransitionArray: 437 return transitions()->number_of_transitions(); 438 } 439 UNREACHABLE(); 440 return 0; // Make GCC happy. 441 } 442 443 void TransitionArray::Zap(Isolate* isolate) { 444 MemsetPointer( 445 data_start() + kPrototypeTransitionsIndex, 446 MaybeObject::FromObject(ReadOnlyRoots(isolate).the_hole_value()), 447 length() - kPrototypeTransitionsIndex); 448 SetNumberOfTransitions(0); 449 } 450 451 void TransitionsAccessor::ReplaceTransitions(MaybeObject* new_transitions) { 452 if (encoding() == kFullTransitionArray) { 453 TransitionArray* old_transitions = transitions(); 454 #if DEBUG 455 CheckNewTransitionsAreConsistent(old_transitions, 456 new_transitions->ToStrongHeapObject()); 457 DCHECK(old_transitions != new_transitions->ToStrongHeapObject()); 458 #endif 459 // Transition arrays are not shared. When one is replaced, it should not 460 // keep referenced objects alive, so we zap it. 461 // When there is another reference to the array somewhere (e.g. a handle), 462 // not zapping turns from a waste of memory into a source of crashes. 463 old_transitions->Zap(isolate_); 464 } 465 map_->set_raw_transitions(new_transitions); 466 MarkNeedsReload(); 467 } 468 469 void TransitionsAccessor::SetPrototypeTransitions( 470 Handle<WeakFixedArray> proto_transitions) { 471 EnsureHasFullTransitionArray(); 472 transitions()->SetPrototypeTransitions(*proto_transitions); 473 } 474 475 void TransitionsAccessor::EnsureHasFullTransitionArray() { 476 if (encoding() == kFullTransitionArray) return; 477 int nof = encoding() == kUninitialized ? 0 : 1; 478 Handle<TransitionArray> result = isolate_->factory()->NewTransitionArray(nof); 479 Reload(); // Reload after possible GC. 480 if (nof == 1) { 481 if (encoding() == kUninitialized) { 482 // If allocation caused GC and cleared the target, trim the new array. 483 result->SetNumberOfTransitions(0); 484 } else { 485 // Otherwise populate the new array. 486 Handle<Map> target(GetSimpleTransition(), isolate_); 487 Name* key = GetSimpleTransitionKey(*target); 488 result->Set(0, key, HeapObjectReference::Weak(*target)); 489 } 490 } 491 ReplaceTransitions(MaybeObject::FromObject(*result)); 492 Reload(); // Reload after replacing transitions. 493 } 494 495 void TransitionsAccessor::TraverseTransitionTreeInternal( 496 TraverseCallback callback, void* data, DisallowHeapAllocation* no_gc) { 497 switch (encoding()) { 498 case kPrototypeInfo: 499 case kUninitialized: 500 break; 501 case kWeakRef: { 502 Map* simple_target = Map::cast(raw_transitions_->ToWeakHeapObject()); 503 TransitionsAccessor(isolate_, simple_target, no_gc) 504 .TraverseTransitionTreeInternal(callback, data, no_gc); 505 break; 506 } 507 case kFullTransitionArray: { 508 if (transitions()->HasPrototypeTransitions()) { 509 WeakFixedArray* proto_trans = transitions()->GetPrototypeTransitions(); 510 int length = TransitionArray::NumberOfPrototypeTransitions(proto_trans); 511 for (int i = 0; i < length; ++i) { 512 int index = TransitionArray::kProtoTransitionHeaderSize + i; 513 MaybeObject* target = proto_trans->Get(index); 514 DCHECK(target->IsClearedWeakHeapObject() || 515 target->IsWeakHeapObject()); 516 if (target->IsClearedWeakHeapObject()) continue; 517 TransitionsAccessor(isolate_, Map::cast(target->ToWeakHeapObject()), 518 no_gc) 519 .TraverseTransitionTreeInternal(callback, data, no_gc); 520 } 521 } 522 for (int i = 0; i < transitions()->number_of_transitions(); ++i) { 523 TransitionsAccessor(isolate_, transitions()->GetTarget(i), no_gc) 524 .TraverseTransitionTreeInternal(callback, data, no_gc); 525 } 526 break; 527 } 528 } 529 callback(map_, data); 530 } 531 532 #ifdef DEBUG 533 void TransitionsAccessor::CheckNewTransitionsAreConsistent( 534 TransitionArray* old_transitions, Object* transitions) { 535 // This function only handles full transition arrays. 536 DCHECK_EQ(kFullTransitionArray, encoding()); 537 TransitionArray* new_transitions = TransitionArray::cast(transitions); 538 for (int i = 0; i < old_transitions->number_of_transitions(); i++) { 539 Map* target = old_transitions->GetTarget(i); 540 if (target->instance_descriptors() == map_->instance_descriptors()) { 541 Name* key = old_transitions->GetKey(i); 542 int new_target_index; 543 if (IsSpecialTransition(ReadOnlyRoots(isolate_), key)) { 544 new_target_index = new_transitions->SearchSpecial(Symbol::cast(key)); 545 } else { 546 PropertyDetails details = GetTargetDetails(key, target); 547 new_target_index = 548 new_transitions->Search(details.kind(), key, details.attributes()); 549 } 550 DCHECK_NE(TransitionArray::kNotFound, new_target_index); 551 DCHECK_EQ(target, new_transitions->GetTarget(new_target_index)); 552 } 553 } 554 } 555 #endif 556 557 // Private non-static helper functions (operating on full transition arrays). 558 559 int TransitionArray::SearchDetails(int transition, PropertyKind kind, 560 PropertyAttributes attributes, 561 int* out_insertion_index) { 562 int nof_transitions = number_of_transitions(); 563 DCHECK(transition < nof_transitions); 564 Name* key = GetKey(transition); 565 for (; transition < nof_transitions && GetKey(transition) == key; 566 transition++) { 567 Map* target = GetTarget(transition); 568 PropertyDetails target_details = 569 TransitionsAccessor::GetTargetDetails(key, target); 570 571 int cmp = CompareDetails(kind, attributes, target_details.kind(), 572 target_details.attributes()); 573 if (cmp == 0) { 574 return transition; 575 } else if (cmp < 0) { 576 break; 577 } 578 } 579 if (out_insertion_index != nullptr) *out_insertion_index = transition; 580 return kNotFound; 581 } 582 583 int TransitionArray::Search(PropertyKind kind, Name* name, 584 PropertyAttributes attributes, 585 int* out_insertion_index) { 586 int transition = SearchName(name, out_insertion_index); 587 if (transition == kNotFound) return kNotFound; 588 return SearchDetails(transition, kind, attributes, out_insertion_index); 589 } 590 591 void TransitionArray::Sort() { 592 DisallowHeapAllocation no_gc; 593 // In-place insertion sort. 594 int length = number_of_transitions(); 595 ReadOnlyRoots roots = GetReadOnlyRoots(); 596 for (int i = 1; i < length; i++) { 597 Name* key = GetKey(i); 598 MaybeObject* target = GetRawTarget(i); 599 PropertyKind kind = kData; 600 PropertyAttributes attributes = NONE; 601 if (!TransitionsAccessor::IsSpecialTransition(roots, key)) { 602 Map* target_map = TransitionsAccessor::GetTargetFromRaw(target); 603 PropertyDetails details = 604 TransitionsAccessor::GetTargetDetails(key, target_map); 605 kind = details.kind(); 606 attributes = details.attributes(); 607 } 608 int j; 609 for (j = i - 1; j >= 0; j--) { 610 Name* temp_key = GetKey(j); 611 MaybeObject* temp_target = GetRawTarget(j); 612 PropertyKind temp_kind = kData; 613 PropertyAttributes temp_attributes = NONE; 614 if (!TransitionsAccessor::IsSpecialTransition(roots, temp_key)) { 615 Map* temp_target_map = 616 TransitionsAccessor::GetTargetFromRaw(temp_target); 617 PropertyDetails details = 618 TransitionsAccessor::GetTargetDetails(temp_key, temp_target_map); 619 temp_kind = details.kind(); 620 temp_attributes = details.attributes(); 621 } 622 int cmp = 623 CompareKeys(temp_key, temp_key->Hash(), temp_kind, temp_attributes, 624 key, key->Hash(), kind, attributes); 625 if (cmp > 0) { 626 SetKey(j + 1, temp_key); 627 SetRawTarget(j + 1, temp_target); 628 } else { 629 break; 630 } 631 } 632 SetKey(j + 1, key); 633 SetRawTarget(j + 1, target); 634 } 635 DCHECK(IsSortedNoDuplicates()); 636 } 637 638 } // namespace internal 639 } // namespace v8 640