1 // Copyright 2014 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/lookup.h" 6 7 #include "src/bootstrapper.h" 8 #include "src/deoptimizer.h" 9 #include "src/elements.h" 10 #include "src/field-type.h" 11 #include "src/isolate-inl.h" 12 #include "src/objects/hash-table-inl.h" 13 14 namespace v8 { 15 namespace internal { 16 17 // static 18 LookupIterator LookupIterator::PropertyOrElement( 19 Isolate* isolate, Handle<Object> receiver, Handle<Object> key, 20 bool* success, Handle<JSReceiver> holder, Configuration configuration) { 21 uint32_t index = 0; 22 if (key->ToArrayIndex(&index)) { 23 *success = true; 24 return LookupIterator(isolate, receiver, index, holder, configuration); 25 } 26 27 Handle<Name> name; 28 *success = Object::ToName(isolate, key).ToHandle(&name); 29 if (!*success) { 30 DCHECK(isolate->has_pending_exception()); 31 // Return an unusable dummy. 32 return LookupIterator(isolate, receiver, 33 isolate->factory()->empty_string()); 34 } 35 36 if (name->AsArrayIndex(&index)) { 37 LookupIterator it(isolate, receiver, index, holder, configuration); 38 // Here we try to avoid having to rebuild the string later 39 // by storing it on the indexed LookupIterator. 40 it.name_ = name; 41 return it; 42 } 43 44 return LookupIterator(receiver, name, holder, configuration); 45 } 46 47 // static 48 LookupIterator LookupIterator::PropertyOrElement(Isolate* isolate, 49 Handle<Object> receiver, 50 Handle<Object> key, 51 bool* success, 52 Configuration configuration) { 53 // TODO(mslekova): come up with better way to avoid duplication 54 uint32_t index = 0; 55 if (key->ToArrayIndex(&index)) { 56 *success = true; 57 return LookupIterator(isolate, receiver, index, configuration); 58 } 59 60 Handle<Name> name; 61 *success = Object::ToName(isolate, key).ToHandle(&name); 62 if (!*success) { 63 DCHECK(isolate->has_pending_exception()); 64 // Return an unusable dummy. 65 return LookupIterator(isolate, receiver, 66 isolate->factory()->empty_string()); 67 } 68 69 if (name->AsArrayIndex(&index)) { 70 LookupIterator it(isolate, receiver, index, configuration); 71 // Here we try to avoid having to rebuild the string later 72 // by storing it on the indexed LookupIterator. 73 it.name_ = name; 74 return it; 75 } 76 77 return LookupIterator(isolate, receiver, name, configuration); 78 } 79 80 // TODO(ishell): Consider removing this way of LookupIterator creation. 81 // static 82 LookupIterator LookupIterator::ForTransitionHandler( 83 Isolate* isolate, Handle<Object> receiver, Handle<Name> name, 84 Handle<Object> value, MaybeHandle<Map> maybe_transition_map) { 85 Handle<Map> transition_map; 86 if (!maybe_transition_map.ToHandle(&transition_map) || 87 !transition_map->IsPrototypeValidityCellValid()) { 88 // This map is not a valid transition handler, so full lookup is required. 89 return LookupIterator(isolate, receiver, name); 90 } 91 92 PropertyDetails details = PropertyDetails::Empty(); 93 bool has_property; 94 if (transition_map->is_dictionary_map()) { 95 details = PropertyDetails(kData, NONE, PropertyCellType::kNoCell); 96 has_property = false; 97 } else { 98 details = transition_map->GetLastDescriptorDetails(); 99 has_property = true; 100 } 101 #ifdef DEBUG 102 if (name->IsPrivate()) { 103 DCHECK_EQ(DONT_ENUM, details.attributes()); 104 } else { 105 DCHECK_EQ(NONE, details.attributes()); 106 } 107 #endif 108 LookupIterator it(isolate, receiver, name, transition_map, details, 109 has_property); 110 111 if (!transition_map->is_dictionary_map()) { 112 int descriptor_number = transition_map->LastAdded(); 113 Handle<Map> new_map = 114 Map::PrepareForDataProperty(isolate, transition_map, descriptor_number, 115 PropertyConstness::kConst, value); 116 // Reload information; this is no-op if nothing changed. 117 it.property_details_ = 118 new_map->instance_descriptors()->GetDetails(descriptor_number); 119 it.transition_ = new_map; 120 } 121 return it; 122 } 123 124 LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver, 125 Handle<Name> name, Handle<Map> transition_map, 126 PropertyDetails details, bool has_property) 127 : configuration_(DEFAULT), 128 state_(TRANSITION), 129 has_property_(has_property), 130 interceptor_state_(InterceptorState::kUninitialized), 131 property_details_(details), 132 isolate_(isolate), 133 name_(name), 134 transition_(transition_map), 135 receiver_(receiver), 136 initial_holder_(GetRoot(isolate, receiver)), 137 index_(kMaxUInt32), 138 number_(static_cast<uint32_t>(DescriptorArray::kNotFound)) { 139 holder_ = initial_holder_; 140 } 141 142 template <bool is_element> 143 void LookupIterator::Start() { 144 DisallowHeapAllocation no_gc; 145 146 has_property_ = false; 147 state_ = NOT_FOUND; 148 holder_ = initial_holder_; 149 150 JSReceiver* holder = *holder_; 151 Map* map = holder->map(); 152 153 state_ = LookupInHolder<is_element>(map, holder); 154 if (IsFound()) return; 155 156 NextInternal<is_element>(map, holder); 157 } 158 159 template void LookupIterator::Start<true>(); 160 template void LookupIterator::Start<false>(); 161 162 void LookupIterator::Next() { 163 DCHECK_NE(JSPROXY, state_); 164 DCHECK_NE(TRANSITION, state_); 165 DisallowHeapAllocation no_gc; 166 has_property_ = false; 167 168 JSReceiver* holder = *holder_; 169 Map* map = holder->map(); 170 171 if (map->IsSpecialReceiverMap()) { 172 state_ = IsElement() ? LookupInSpecialHolder<true>(map, holder) 173 : LookupInSpecialHolder<false>(map, holder); 174 if (IsFound()) return; 175 } 176 177 IsElement() ? NextInternal<true>(map, holder) 178 : NextInternal<false>(map, holder); 179 } 180 181 template <bool is_element> 182 void LookupIterator::NextInternal(Map* map, JSReceiver* holder) { 183 do { 184 JSReceiver* maybe_holder = NextHolder(map); 185 if (maybe_holder == nullptr) { 186 if (interceptor_state_ == InterceptorState::kSkipNonMasking) { 187 RestartLookupForNonMaskingInterceptors<is_element>(); 188 return; 189 } 190 state_ = NOT_FOUND; 191 if (holder != *holder_) holder_ = handle(holder, isolate_); 192 return; 193 } 194 holder = maybe_holder; 195 map = holder->map(); 196 state_ = LookupInHolder<is_element>(map, holder); 197 } while (!IsFound()); 198 199 holder_ = handle(holder, isolate_); 200 } 201 202 template <bool is_element> 203 void LookupIterator::RestartInternal(InterceptorState interceptor_state) { 204 interceptor_state_ = interceptor_state; 205 property_details_ = PropertyDetails::Empty(); 206 number_ = static_cast<uint32_t>(DescriptorArray::kNotFound); 207 Start<is_element>(); 208 } 209 210 template void LookupIterator::RestartInternal<true>(InterceptorState); 211 template void LookupIterator::RestartInternal<false>(InterceptorState); 212 213 // static 214 Handle<JSReceiver> LookupIterator::GetRootForNonJSReceiver( 215 Isolate* isolate, Handle<Object> receiver, uint32_t index) { 216 // Strings are the only objects with properties (only elements) directly on 217 // the wrapper. Hence we can skip generating the wrapper for all other cases. 218 if (index != kMaxUInt32 && receiver->IsString() && 219 index < static_cast<uint32_t>(String::cast(*receiver)->length())) { 220 // TODO(verwaest): Speed this up. Perhaps use a cached wrapper on the native 221 // context, ensuring that we don't leak it into JS? 222 Handle<JSFunction> constructor = isolate->string_function(); 223 Handle<JSObject> result = isolate->factory()->NewJSObject(constructor); 224 Handle<JSValue>::cast(result)->set_value(*receiver); 225 return result; 226 } 227 auto root = 228 handle(receiver->GetPrototypeChainRootMap(isolate)->prototype(), isolate); 229 if (root->IsNull(isolate)) { 230 isolate->PushStackTraceAndDie(*receiver); 231 } 232 return Handle<JSReceiver>::cast(root); 233 } 234 235 236 Handle<Map> LookupIterator::GetReceiverMap() const { 237 if (receiver_->IsNumber()) return factory()->heap_number_map(); 238 return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_); 239 } 240 241 bool LookupIterator::HasAccess() const { 242 DCHECK_EQ(ACCESS_CHECK, state_); 243 return isolate_->MayAccess(handle(isolate_->context(), isolate_), 244 GetHolder<JSObject>()); 245 } 246 247 template <bool is_element> 248 void LookupIterator::ReloadPropertyInformation() { 249 state_ = BEFORE_PROPERTY; 250 interceptor_state_ = InterceptorState::kUninitialized; 251 state_ = LookupInHolder<is_element>(holder_->map(), *holder_); 252 DCHECK(IsFound() || !holder_->HasFastProperties()); 253 } 254 255 namespace { 256 257 bool IsTypedArrayFunctionInAnyContext(Isolate* isolate, JSReceiver* holder) { 258 static uint32_t context_slots[] = { 259 #define TYPED_ARRAY_CONTEXT_SLOTS(Type, type, TYPE, ctype) \ 260 Context::TYPE##_ARRAY_FUN_INDEX, 261 262 TYPED_ARRAYS(TYPED_ARRAY_CONTEXT_SLOTS) 263 #undef TYPED_ARRAY_CONTEXT_SLOTS 264 }; 265 266 if (!holder->IsJSFunction()) return false; 267 268 return std::any_of( 269 std::begin(context_slots), std::end(context_slots), 270 [=](uint32_t slot) { return isolate->IsInAnyContext(holder, slot); }); 271 } 272 273 } // namespace 274 275 void LookupIterator::InternalUpdateProtector() { 276 if (isolate_->bootstrapper()->IsActive()) return; 277 278 ReadOnlyRoots roots(heap()); 279 if (*name_ == roots.constructor_string()) { 280 if (!isolate_->IsArraySpeciesLookupChainIntact() && 281 !isolate_->IsTypedArraySpeciesLookupChainIntact() && 282 !isolate_->IsPromiseSpeciesLookupChainIntact()) 283 return; 284 // Setting the constructor property could change an instance's @@species 285 if (holder_->IsJSArray()) { 286 if (!isolate_->IsArraySpeciesLookupChainIntact()) return; 287 isolate_->CountUsage( 288 v8::Isolate::UseCounterFeature::kArrayInstanceConstructorModified); 289 isolate_->InvalidateArraySpeciesProtector(); 290 return; 291 } else if (holder_->IsJSPromise()) { 292 if (!isolate_->IsPromiseSpeciesLookupChainIntact()) return; 293 isolate_->InvalidatePromiseSpeciesProtector(); 294 return; 295 } else if (holder_->IsJSTypedArray()) { 296 if (!isolate_->IsTypedArraySpeciesLookupChainIntact()) return; 297 isolate_->InvalidateTypedArraySpeciesProtector(); 298 return; 299 } 300 if (holder_->map()->is_prototype_map()) { 301 DisallowHeapAllocation no_gc; 302 // Setting the constructor of Array.prototype, Promise.prototype or 303 // %TypedArray%.prototype of any realm also needs to invalidate the 304 // @@species protector. 305 // For typed arrays, we check a prototype of this holder since TypedArrays 306 // have different prototypes for each type, and their parent prototype is 307 // pointing the same TYPED_ARRAY_PROTOTYPE. 308 if (isolate_->IsInAnyContext(*holder_, 309 Context::INITIAL_ARRAY_PROTOTYPE_INDEX)) { 310 if (!isolate_->IsArraySpeciesLookupChainIntact()) return; 311 isolate_->CountUsage( 312 v8::Isolate::UseCounterFeature::kArrayPrototypeConstructorModified); 313 isolate_->InvalidateArraySpeciesProtector(); 314 } else if (isolate_->IsInAnyContext(*holder_, 315 Context::PROMISE_PROTOTYPE_INDEX)) { 316 if (!isolate_->IsPromiseSpeciesLookupChainIntact()) return; 317 isolate_->InvalidatePromiseSpeciesProtector(); 318 } else if (isolate_->IsInAnyContext( 319 holder_->map()->prototype(), 320 Context::TYPED_ARRAY_PROTOTYPE_INDEX)) { 321 if (!isolate_->IsTypedArraySpeciesLookupChainIntact()) return; 322 isolate_->InvalidateTypedArraySpeciesProtector(); 323 } 324 } 325 } else if (*name_ == roots.next_string()) { 326 if (!isolate_->IsArrayIteratorLookupChainIntact()) return; 327 // Setting the next property of %ArrayIteratorPrototype% also needs to 328 // invalidate the array iterator protector. 329 if (isolate_->IsInAnyContext( 330 *holder_, Context::INITIAL_ARRAY_ITERATOR_PROTOTYPE_INDEX)) { 331 isolate_->InvalidateArrayIteratorProtector(); 332 } 333 } else if (*name_ == roots.species_symbol()) { 334 if (!isolate_->IsArraySpeciesLookupChainIntact() && 335 !isolate_->IsTypedArraySpeciesLookupChainIntact() && 336 !isolate_->IsPromiseSpeciesLookupChainIntact()) 337 return; 338 // Setting the Symbol.species property of any Array, Promise or TypedArray 339 // constructor invalidates the @@species protector 340 if (isolate_->IsInAnyContext(*holder_, Context::ARRAY_FUNCTION_INDEX)) { 341 if (!isolate_->IsArraySpeciesLookupChainIntact()) return; 342 isolate_->CountUsage( 343 v8::Isolate::UseCounterFeature::kArraySpeciesModified); 344 isolate_->InvalidateArraySpeciesProtector(); 345 } else if (isolate_->IsInAnyContext(*holder_, 346 Context::PROMISE_FUNCTION_INDEX)) { 347 if (!isolate_->IsPromiseSpeciesLookupChainIntact()) return; 348 isolate_->InvalidatePromiseSpeciesProtector(); 349 } else if (IsTypedArrayFunctionInAnyContext(isolate_, *holder_)) { 350 if (!isolate_->IsTypedArraySpeciesLookupChainIntact()) return; 351 isolate_->InvalidateTypedArraySpeciesProtector(); 352 } 353 } else if (*name_ == roots.is_concat_spreadable_symbol()) { 354 if (!isolate_->IsIsConcatSpreadableLookupChainIntact()) return; 355 isolate_->InvalidateIsConcatSpreadableProtector(); 356 } else if (*name_ == roots.iterator_symbol()) { 357 if (!isolate_->IsArrayIteratorLookupChainIntact()) return; 358 if (holder_->IsJSArray()) { 359 isolate_->InvalidateArrayIteratorProtector(); 360 } 361 } else if (*name_ == roots.resolve_string()) { 362 if (!isolate_->IsPromiseResolveLookupChainIntact()) return; 363 // Setting the "resolve" property on any %Promise% intrinsic object 364 // invalidates the Promise.resolve protector. 365 if (isolate_->IsInAnyContext(*holder_, Context::PROMISE_FUNCTION_INDEX)) { 366 isolate_->InvalidatePromiseResolveProtector(); 367 } 368 } else if (*name_ == roots.then_string()) { 369 if (!isolate_->IsPromiseThenLookupChainIntact()) return; 370 // Setting the "then" property on any JSPromise instance or on the 371 // initial %PromisePrototype% invalidates the Promise#then protector. 372 // Also setting the "then" property on the initial %ObjectPrototype% 373 // invalidates the Promise#then protector, since we use this protector 374 // to guard the fast-path in AsyncGeneratorResolve, where we can skip 375 // the ResolvePromise step and go directly to FulfillPromise if we 376 // know that the Object.prototype doesn't contain a "then" method. 377 if (holder_->IsJSPromise() || 378 isolate_->IsInAnyContext(*holder_, 379 Context::INITIAL_OBJECT_PROTOTYPE_INDEX) || 380 isolate_->IsInAnyContext(*holder_, Context::PROMISE_PROTOTYPE_INDEX)) { 381 isolate_->InvalidatePromiseThenProtector(); 382 } 383 } 384 } 385 386 void LookupIterator::PrepareForDataProperty(Handle<Object> value) { 387 DCHECK(state_ == DATA || state_ == ACCESSOR); 388 DCHECK(HolderIsReceiverOrHiddenPrototype()); 389 390 Handle<JSReceiver> holder = GetHolder<JSReceiver>(); 391 // JSProxy does not have fast properties so we do an early return. 392 DCHECK_IMPLIES(holder->IsJSProxy(), !holder->HasFastProperties()); 393 DCHECK_IMPLIES(holder->IsJSProxy(), name()->IsPrivate()); 394 if (holder->IsJSProxy()) return; 395 396 Handle<JSObject> holder_obj = Handle<JSObject>::cast(holder); 397 398 if (IsElement()) { 399 ElementsKind kind = holder_obj->GetElementsKind(); 400 ElementsKind to = value->OptimalElementsKind(); 401 if (IsHoleyElementsKind(kind)) to = GetHoleyElementsKind(to); 402 to = GetMoreGeneralElementsKind(kind, to); 403 404 if (kind != to) { 405 JSObject::TransitionElementsKind(holder_obj, to); 406 } 407 408 // Copy the backing store if it is copy-on-write. 409 if (IsSmiOrObjectElementsKind(to)) { 410 JSObject::EnsureWritableFastElements(holder_obj); 411 } 412 return; 413 } 414 415 if (holder_obj->IsJSGlobalObject()) { 416 Handle<GlobalDictionary> dictionary( 417 JSGlobalObject::cast(*holder_obj)->global_dictionary(), isolate()); 418 Handle<PropertyCell> cell(dictionary->CellAt(dictionary_entry()), 419 isolate()); 420 property_details_ = cell->property_details(); 421 PropertyCell::PrepareForValue(isolate(), dictionary, dictionary_entry(), 422 value, property_details_); 423 return; 424 } 425 if (!holder_obj->HasFastProperties()) return; 426 427 PropertyConstness new_constness = PropertyConstness::kConst; 428 if (FLAG_track_constant_fields) { 429 if (constness() == PropertyConstness::kConst) { 430 DCHECK_EQ(kData, property_details_.kind()); 431 // Check that current value matches new value otherwise we should make 432 // the property mutable. 433 if (!IsConstFieldValueEqualTo(*value)) 434 new_constness = PropertyConstness::kMutable; 435 } 436 } else { 437 new_constness = PropertyConstness::kMutable; 438 } 439 440 Handle<Map> old_map(holder_obj->map(), isolate_); 441 Handle<Map> new_map = Map::PrepareForDataProperty( 442 isolate(), old_map, descriptor_number(), new_constness, value); 443 444 if (old_map.is_identical_to(new_map)) { 445 // Update the property details if the representation was None. 446 if (constness() != new_constness || representation().IsNone()) { 447 property_details_ = 448 new_map->instance_descriptors()->GetDetails(descriptor_number()); 449 } 450 return; 451 } 452 453 JSObject::MigrateToMap(holder_obj, new_map); 454 ReloadPropertyInformation<false>(); 455 } 456 457 458 void LookupIterator::ReconfigureDataProperty(Handle<Object> value, 459 PropertyAttributes attributes) { 460 DCHECK(state_ == DATA || state_ == ACCESSOR); 461 DCHECK(HolderIsReceiverOrHiddenPrototype()); 462 463 Handle<JSReceiver> holder = GetHolder<JSReceiver>(); 464 465 // Property details can never change for private fields. 466 if (holder->IsJSProxy()) { 467 DCHECK(name()->IsPrivate()); 468 return; 469 } 470 471 Handle<JSObject> holder_obj = Handle<JSObject>::cast(holder); 472 if (IsElement()) { 473 DCHECK(!holder_obj->HasFixedTypedArrayElements()); 474 DCHECK(attributes != NONE || !holder_obj->HasFastElements()); 475 Handle<FixedArrayBase> elements(holder_obj->elements(), isolate()); 476 holder_obj->GetElementsAccessor()->Reconfigure(holder_obj, elements, 477 number_, value, attributes); 478 ReloadPropertyInformation<true>(); 479 } else if (holder_obj->HasFastProperties()) { 480 Handle<Map> old_map(holder_obj->map(), isolate_); 481 Handle<Map> new_map = Map::ReconfigureExistingProperty( 482 isolate_, old_map, descriptor_number(), i::kData, attributes); 483 // Force mutable to avoid changing constant value by reconfiguring 484 // kData -> kAccessor -> kData. 485 new_map = 486 Map::PrepareForDataProperty(isolate(), new_map, descriptor_number(), 487 PropertyConstness::kMutable, value); 488 JSObject::MigrateToMap(holder_obj, new_map); 489 ReloadPropertyInformation<false>(); 490 } 491 492 if (!IsElement() && !holder_obj->HasFastProperties()) { 493 PropertyDetails details(kData, attributes, PropertyCellType::kMutable); 494 if (holder_obj->map()->is_prototype_map() && 495 (property_details_.attributes() & READ_ONLY) == 0 && 496 (attributes & READ_ONLY) != 0) { 497 // Invalidate prototype validity cell when a property is reconfigured 498 // from writable to read-only as this may invalidate transitioning store 499 // IC handlers. 500 JSObject::InvalidatePrototypeChains(holder->map()); 501 } 502 if (holder_obj->IsJSGlobalObject()) { 503 Handle<GlobalDictionary> dictionary( 504 JSGlobalObject::cast(*holder_obj)->global_dictionary(), isolate()); 505 506 Handle<PropertyCell> cell = PropertyCell::PrepareForValue( 507 isolate(), dictionary, dictionary_entry(), value, details); 508 cell->set_value(*value); 509 property_details_ = cell->property_details(); 510 } else { 511 Handle<NameDictionary> dictionary(holder_obj->property_dictionary(), 512 isolate()); 513 PropertyDetails original_details = 514 dictionary->DetailsAt(dictionary_entry()); 515 int enumeration_index = original_details.dictionary_index(); 516 DCHECK_GT(enumeration_index, 0); 517 details = details.set_index(enumeration_index); 518 dictionary->SetEntry(isolate(), dictionary_entry(), *name(), *value, 519 details); 520 property_details_ = details; 521 } 522 state_ = DATA; 523 } 524 525 WriteDataValue(value, true); 526 527 #if VERIFY_HEAP 528 if (FLAG_verify_heap) { 529 holder->HeapObjectVerify(isolate()); 530 } 531 #endif 532 } 533 534 // Can only be called when the receiver is a JSObject. JSProxy has to be handled 535 // via a trap. Adding properties to primitive values is not observable. 536 void LookupIterator::PrepareTransitionToDataProperty( 537 Handle<JSReceiver> receiver, Handle<Object> value, 538 PropertyAttributes attributes, Object::StoreFromKeyed store_mode) { 539 DCHECK_IMPLIES(receiver->IsJSProxy(), name()->IsPrivate()); 540 DCHECK(receiver.is_identical_to(GetStoreTarget<JSReceiver>())); 541 if (state_ == TRANSITION) return; 542 543 if (!IsElement() && name()->IsPrivate()) { 544 attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM); 545 } 546 547 DCHECK(state_ != LookupIterator::ACCESSOR || 548 (GetAccessors()->IsAccessorInfo() && 549 AccessorInfo::cast(*GetAccessors())->is_special_data_property())); 550 DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_); 551 DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype()); 552 553 Handle<Map> map(receiver->map(), isolate_); 554 555 // Dictionary maps can always have additional data properties. 556 if (map->is_dictionary_map()) { 557 state_ = TRANSITION; 558 if (map->IsJSGlobalObjectMap()) { 559 // Install a property cell. 560 Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(receiver); 561 int entry; 562 Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell( 563 global, name(), PropertyCellType::kUninitialized, &entry); 564 Handle<GlobalDictionary> dictionary(global->global_dictionary(), 565 isolate_); 566 DCHECK(cell->value()->IsTheHole(isolate_)); 567 DCHECK(!value->IsTheHole(isolate_)); 568 transition_ = cell; 569 // Assign an enumeration index to the property and update 570 // SetNextEnumerationIndex. 571 int index = dictionary->NextEnumerationIndex(); 572 dictionary->SetNextEnumerationIndex(index + 1); 573 property_details_ = PropertyDetails( 574 kData, attributes, PropertyCellType::kUninitialized, index); 575 PropertyCellType new_type = 576 PropertyCell::UpdatedType(isolate(), cell, value, property_details_); 577 property_details_ = property_details_.set_cell_type(new_type); 578 cell->set_property_details(property_details_); 579 number_ = entry; 580 has_property_ = true; 581 } else { 582 // Don't set enumeration index (it will be set during value store). 583 property_details_ = 584 PropertyDetails(kData, attributes, PropertyCellType::kNoCell); 585 transition_ = map; 586 } 587 return; 588 } 589 590 Handle<Map> transition = 591 Map::TransitionToDataProperty(isolate_, map, name_, value, attributes, 592 kDefaultFieldConstness, store_mode); 593 state_ = TRANSITION; 594 transition_ = transition; 595 596 if (transition->is_dictionary_map()) { 597 // Don't set enumeration index (it will be set during value store). 598 property_details_ = 599 PropertyDetails(kData, attributes, PropertyCellType::kNoCell); 600 } else { 601 property_details_ = transition->GetLastDescriptorDetails(); 602 has_property_ = true; 603 } 604 } 605 606 void LookupIterator::ApplyTransitionToDataProperty( 607 Handle<JSReceiver> receiver) { 608 DCHECK_EQ(TRANSITION, state_); 609 610 DCHECK(receiver.is_identical_to(GetStoreTarget<JSReceiver>())); 611 holder_ = receiver; 612 if (receiver->IsJSGlobalObject()) { 613 JSObject::InvalidatePrototypeChains(receiver->map()); 614 state_ = DATA; 615 return; 616 } 617 Handle<Map> transition = transition_map(); 618 bool simple_transition = transition->GetBackPointer() == receiver->map(); 619 620 if (configuration_ == DEFAULT && !transition->is_dictionary_map() && 621 !transition->IsPrototypeValidityCellValid()) { 622 // Only LookupIterator instances with DEFAULT (full prototype chain) 623 // configuration can produce valid transition handler maps. 624 Handle<Object> validity_cell = 625 Map::GetOrCreatePrototypeChainValidityCell(transition, isolate()); 626 transition->set_prototype_validity_cell(*validity_cell); 627 } 628 629 if (!receiver->IsJSProxy()) { 630 JSObject::MigrateToMap(Handle<JSObject>::cast(receiver), transition); 631 } 632 633 if (simple_transition) { 634 int number = transition->LastAdded(); 635 number_ = static_cast<uint32_t>(number); 636 property_details_ = transition->GetLastDescriptorDetails(); 637 state_ = DATA; 638 } else if (receiver->map()->is_dictionary_map()) { 639 Handle<NameDictionary> dictionary(receiver->property_dictionary(), 640 isolate_); 641 int entry; 642 if (receiver->map()->is_prototype_map() && receiver->IsJSObject()) { 643 JSObject::InvalidatePrototypeChains(receiver->map()); 644 } 645 dictionary = NameDictionary::Add(isolate(), dictionary, name(), 646 isolate_->factory()->uninitialized_value(), 647 property_details_, &entry); 648 receiver->SetProperties(*dictionary); 649 // Reload details containing proper enumeration index value. 650 property_details_ = dictionary->DetailsAt(entry); 651 number_ = entry; 652 has_property_ = true; 653 state_ = DATA; 654 655 } else { 656 ReloadPropertyInformation<false>(); 657 } 658 } 659 660 661 void LookupIterator::Delete() { 662 Handle<JSReceiver> holder = Handle<JSReceiver>::cast(holder_); 663 if (IsElement()) { 664 Handle<JSObject> object = Handle<JSObject>::cast(holder); 665 ElementsAccessor* accessor = object->GetElementsAccessor(); 666 accessor->Delete(object, number_); 667 } else { 668 DCHECK(!name()->IsPrivateField()); 669 bool is_prototype_map = holder->map()->is_prototype_map(); 670 RuntimeCallTimerScope stats_scope( 671 isolate_, is_prototype_map 672 ? RuntimeCallCounterId::kPrototypeObject_DeleteProperty 673 : RuntimeCallCounterId::kObject_DeleteProperty); 674 675 PropertyNormalizationMode mode = 676 is_prototype_map ? KEEP_INOBJECT_PROPERTIES : CLEAR_INOBJECT_PROPERTIES; 677 678 if (holder->HasFastProperties()) { 679 JSObject::NormalizeProperties(Handle<JSObject>::cast(holder), mode, 0, 680 "DeletingProperty"); 681 ReloadPropertyInformation<false>(); 682 } 683 JSReceiver::DeleteNormalizedProperty(holder, number_); 684 if (holder->IsJSObject()) { 685 JSObject::ReoptimizeIfPrototype(Handle<JSObject>::cast(holder)); 686 } 687 } 688 state_ = NOT_FOUND; 689 } 690 691 void LookupIterator::TransitionToAccessorProperty( 692 Handle<Object> getter, Handle<Object> setter, 693 PropertyAttributes attributes) { 694 DCHECK(!getter->IsNull(isolate_) || !setter->IsNull(isolate_)); 695 // Can only be called when the receiver is a JSObject. JSProxy has to be 696 // handled via a trap. Adding properties to primitive values is not 697 // observable. 698 Handle<JSObject> receiver = GetStoreTarget<JSObject>(); 699 if (!IsElement() && name()->IsPrivate()) { 700 attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM); 701 } 702 703 if (!IsElement() && !receiver->map()->is_dictionary_map()) { 704 Handle<Map> old_map(receiver->map(), isolate_); 705 706 if (!holder_.is_identical_to(receiver)) { 707 holder_ = receiver; 708 state_ = NOT_FOUND; 709 } else if (state_ == INTERCEPTOR) { 710 LookupInRegularHolder<false>(*old_map, *holder_); 711 } 712 int descriptor = 713 IsFound() ? static_cast<int>(number_) : DescriptorArray::kNotFound; 714 715 Handle<Map> new_map = Map::TransitionToAccessorProperty( 716 isolate_, old_map, name_, descriptor, getter, setter, attributes); 717 bool simple_transition = new_map->GetBackPointer() == receiver->map(); 718 JSObject::MigrateToMap(receiver, new_map); 719 720 if (simple_transition) { 721 int number = new_map->LastAdded(); 722 number_ = static_cast<uint32_t>(number); 723 property_details_ = new_map->GetLastDescriptorDetails(); 724 state_ = ACCESSOR; 725 return; 726 } 727 728 ReloadPropertyInformation<false>(); 729 if (!new_map->is_dictionary_map()) return; 730 } 731 732 Handle<AccessorPair> pair; 733 if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) { 734 pair = Handle<AccessorPair>::cast(GetAccessors()); 735 // If the component and attributes are identical, nothing has to be done. 736 if (pair->Equals(*getter, *setter)) { 737 if (property_details().attributes() == attributes) { 738 if (!IsElement()) JSObject::ReoptimizeIfPrototype(receiver); 739 return; 740 } 741 } else { 742 pair = AccessorPair::Copy(isolate(), pair); 743 pair->SetComponents(*getter, *setter); 744 } 745 } else { 746 pair = factory()->NewAccessorPair(); 747 pair->SetComponents(*getter, *setter); 748 } 749 750 TransitionToAccessorPair(pair, attributes); 751 752 #if VERIFY_HEAP 753 if (FLAG_verify_heap) { 754 receiver->JSObjectVerify(isolate()); 755 } 756 #endif 757 } 758 759 760 void LookupIterator::TransitionToAccessorPair(Handle<Object> pair, 761 PropertyAttributes attributes) { 762 Handle<JSObject> receiver = GetStoreTarget<JSObject>(); 763 holder_ = receiver; 764 765 PropertyDetails details(kAccessor, attributes, PropertyCellType::kMutable); 766 767 if (IsElement()) { 768 // TODO(verwaest): Move code into the element accessor. 769 isolate_->CountUsage(v8::Isolate::kIndexAccessor); 770 Handle<NumberDictionary> dictionary = JSObject::NormalizeElements(receiver); 771 772 dictionary = NumberDictionary::Set(isolate_, dictionary, index_, pair, 773 receiver, details); 774 receiver->RequireSlowElements(*dictionary); 775 776 if (receiver->HasSlowArgumentsElements()) { 777 FixedArray* parameter_map = FixedArray::cast(receiver->elements()); 778 uint32_t length = parameter_map->length() - 2; 779 if (number_ < length) { 780 parameter_map->set(number_ + 2, ReadOnlyRoots(heap()).the_hole_value()); 781 } 782 FixedArray::cast(receiver->elements())->set(1, *dictionary); 783 } else { 784 receiver->set_elements(*dictionary); 785 } 786 787 ReloadPropertyInformation<true>(); 788 } else { 789 PropertyNormalizationMode mode = CLEAR_INOBJECT_PROPERTIES; 790 if (receiver->map()->is_prototype_map()) { 791 JSObject::InvalidatePrototypeChains(receiver->map()); 792 mode = KEEP_INOBJECT_PROPERTIES; 793 } 794 795 // Normalize object to make this operation simple. 796 JSObject::NormalizeProperties(receiver, mode, 0, 797 "TransitionToAccessorPair"); 798 799 JSObject::SetNormalizedProperty(receiver, name_, pair, details); 800 JSObject::ReoptimizeIfPrototype(receiver); 801 802 ReloadPropertyInformation<false>(); 803 } 804 } 805 806 bool LookupIterator::HolderIsReceiver() const { 807 DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY); 808 // Optimization that only works if configuration_ is not mutable. 809 if (!check_prototype_chain()) return true; 810 return *receiver_ == *holder_; 811 } 812 813 bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const { 814 DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY); 815 // Optimization that only works if configuration_ is not mutable. 816 if (!check_prototype_chain()) return true; 817 DisallowHeapAllocation no_gc; 818 if (*receiver_ == *holder_) return true; 819 if (!receiver_->IsJSReceiver()) return false; 820 JSReceiver* current = JSReceiver::cast(*receiver_); 821 JSReceiver* object = *holder_; 822 if (!current->map()->has_hidden_prototype()) return false; 823 // JSProxy do not occur as hidden prototypes. 824 if (object->IsJSProxy()) return false; 825 PrototypeIterator iter(isolate(), current, kStartAtPrototype, 826 PrototypeIterator::END_AT_NON_HIDDEN); 827 while (!iter.IsAtEnd()) { 828 if (iter.GetCurrent<JSReceiver>() == object) return true; 829 iter.Advance(); 830 } 831 return false; 832 } 833 834 835 Handle<Object> LookupIterator::FetchValue() const { 836 Object* result = nullptr; 837 if (IsElement()) { 838 Handle<JSObject> holder = GetHolder<JSObject>(); 839 ElementsAccessor* accessor = holder->GetElementsAccessor(); 840 return accessor->Get(holder, number_); 841 } else if (holder_->IsJSGlobalObject()) { 842 Handle<JSGlobalObject> holder = GetHolder<JSGlobalObject>(); 843 result = holder->global_dictionary()->ValueAt(number_); 844 } else if (!holder_->HasFastProperties()) { 845 result = holder_->property_dictionary()->ValueAt(number_); 846 } else if (property_details_.location() == kField) { 847 DCHECK_EQ(kData, property_details_.kind()); 848 Handle<JSObject> holder = GetHolder<JSObject>(); 849 FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_); 850 return JSObject::FastPropertyAt(holder, property_details_.representation(), 851 field_index); 852 } else { 853 result = holder_->map()->instance_descriptors()->GetStrongValue(number_); 854 } 855 return handle(result, isolate_); 856 } 857 858 bool LookupIterator::IsConstFieldValueEqualTo(Object* value) const { 859 DCHECK(!IsElement()); 860 DCHECK(holder_->HasFastProperties()); 861 DCHECK_EQ(kField, property_details_.location()); 862 DCHECK_EQ(PropertyConstness::kConst, property_details_.constness()); 863 Handle<JSObject> holder = GetHolder<JSObject>(); 864 FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_); 865 if (property_details_.representation().IsDouble()) { 866 if (!value->IsNumber()) return false; 867 uint64_t bits; 868 if (holder->IsUnboxedDoubleField(field_index)) { 869 bits = holder->RawFastDoublePropertyAsBitsAt(field_index); 870 } else { 871 Object* current_value = holder->RawFastPropertyAt(field_index); 872 DCHECK(current_value->IsMutableHeapNumber()); 873 bits = MutableHeapNumber::cast(current_value)->value_as_bits(); 874 } 875 // Use bit representation of double to to check for hole double, since 876 // manipulating the signaling NaN used for the hole in C++, e.g. with 877 // bit_cast or value(), will change its value on ia32 (the x87 stack is 878 // used to return values and stores to the stack silently clear the 879 // signalling bit). 880 if (bits == kHoleNanInt64) { 881 // Uninitialized double field. 882 return true; 883 } 884 return bit_cast<double>(bits) == value->Number(); 885 } else { 886 Object* current_value = holder->RawFastPropertyAt(field_index); 887 return current_value->IsUninitialized(isolate()) || current_value == value; 888 } 889 } 890 891 int LookupIterator::GetFieldDescriptorIndex() const { 892 DCHECK(has_property_); 893 DCHECK(holder_->HasFastProperties()); 894 DCHECK_EQ(kField, property_details_.location()); 895 DCHECK_EQ(kData, property_details_.kind()); 896 return descriptor_number(); 897 } 898 899 int LookupIterator::GetAccessorIndex() const { 900 DCHECK(has_property_); 901 DCHECK(holder_->HasFastProperties()); 902 DCHECK_EQ(kDescriptor, property_details_.location()); 903 DCHECK_EQ(kAccessor, property_details_.kind()); 904 return descriptor_number(); 905 } 906 907 908 int LookupIterator::GetConstantIndex() const { 909 DCHECK(has_property_); 910 DCHECK(holder_->HasFastProperties()); 911 DCHECK_EQ(kDescriptor, property_details_.location()); 912 DCHECK_EQ(kData, property_details_.kind()); 913 DCHECK(!FLAG_track_constant_fields); 914 DCHECK(!IsElement()); 915 return descriptor_number(); 916 } 917 918 Handle<Map> LookupIterator::GetFieldOwnerMap() const { 919 DCHECK(has_property_); 920 DCHECK(holder_->HasFastProperties()); 921 DCHECK_EQ(kField, property_details_.location()); 922 DCHECK(!IsElement()); 923 Map* holder_map = holder_->map(); 924 return handle(holder_map->FindFieldOwner(isolate(), descriptor_number()), 925 isolate_); 926 } 927 928 FieldIndex LookupIterator::GetFieldIndex() const { 929 DCHECK(has_property_); 930 DCHECK(holder_->HasFastProperties()); 931 DCHECK_EQ(kField, property_details_.location()); 932 DCHECK(!IsElement()); 933 return FieldIndex::ForDescriptor(holder_->map(), descriptor_number()); 934 } 935 936 Handle<FieldType> LookupIterator::GetFieldType() const { 937 DCHECK(has_property_); 938 DCHECK(holder_->HasFastProperties()); 939 DCHECK_EQ(kField, property_details_.location()); 940 return handle( 941 holder_->map()->instance_descriptors()->GetFieldType(descriptor_number()), 942 isolate_); 943 } 944 945 946 Handle<PropertyCell> LookupIterator::GetPropertyCell() const { 947 DCHECK(!IsElement()); 948 Handle<JSGlobalObject> holder = GetHolder<JSGlobalObject>(); 949 return handle(holder->global_dictionary()->CellAt(dictionary_entry()), 950 isolate_); 951 } 952 953 954 Handle<Object> LookupIterator::GetAccessors() const { 955 DCHECK_EQ(ACCESSOR, state_); 956 return FetchValue(); 957 } 958 959 960 Handle<Object> LookupIterator::GetDataValue() const { 961 DCHECK_EQ(DATA, state_); 962 Handle<Object> value = FetchValue(); 963 return value; 964 } 965 966 void LookupIterator::WriteDataValue(Handle<Object> value, 967 bool initializing_store) { 968 DCHECK_EQ(DATA, state_); 969 Handle<JSReceiver> holder = GetHolder<JSReceiver>(); 970 if (IsElement()) { 971 Handle<JSObject> object = Handle<JSObject>::cast(holder); 972 ElementsAccessor* accessor = object->GetElementsAccessor(); 973 accessor->Set(object, number_, *value); 974 } else if (holder->HasFastProperties()) { 975 if (property_details_.location() == kField) { 976 // Check that in case of VariableMode::kConst field the existing value is 977 // equal to |value|. 978 DCHECK_IMPLIES(!initializing_store && property_details_.constness() == 979 PropertyConstness::kConst, 980 IsConstFieldValueEqualTo(*value)); 981 JSObject::cast(*holder)->WriteToField(descriptor_number(), 982 property_details_, *value); 983 } else { 984 DCHECK_EQ(kDescriptor, property_details_.location()); 985 DCHECK_EQ(PropertyConstness::kConst, property_details_.constness()); 986 } 987 } else if (holder->IsJSGlobalObject()) { 988 GlobalDictionary* dictionary = 989 JSGlobalObject::cast(*holder)->global_dictionary(); 990 dictionary->CellAt(dictionary_entry())->set_value(*value); 991 } else { 992 DCHECK_IMPLIES(holder->IsJSProxy(), name()->IsPrivate()); 993 NameDictionary* dictionary = holder->property_dictionary(); 994 dictionary->ValueAtPut(dictionary_entry(), *value); 995 } 996 } 997 998 template <bool is_element> 999 bool LookupIterator::SkipInterceptor(JSObject* holder) { 1000 auto info = GetInterceptor<is_element>(holder); 1001 if (!is_element && name_->IsSymbol() && !info->can_intercept_symbols()) { 1002 return true; 1003 } 1004 if (info->non_masking()) { 1005 switch (interceptor_state_) { 1006 case InterceptorState::kUninitialized: 1007 interceptor_state_ = InterceptorState::kSkipNonMasking; 1008 V8_FALLTHROUGH; 1009 case InterceptorState::kSkipNonMasking: 1010 return true; 1011 case InterceptorState::kProcessNonMasking: 1012 return false; 1013 } 1014 } 1015 return interceptor_state_ == InterceptorState::kProcessNonMasking; 1016 } 1017 1018 JSReceiver* LookupIterator::NextHolder(Map* map) { 1019 DisallowHeapAllocation no_gc; 1020 if (map->prototype() == ReadOnlyRoots(heap()).null_value()) return nullptr; 1021 if (!check_prototype_chain() && !map->has_hidden_prototype()) return nullptr; 1022 return JSReceiver::cast(map->prototype()); 1023 } 1024 1025 LookupIterator::State LookupIterator::NotFound(JSReceiver* const holder) const { 1026 DCHECK(!IsElement()); 1027 if (!holder->IsJSTypedArray() || !name_->IsString()) return NOT_FOUND; 1028 1029 Handle<String> name_string = Handle<String>::cast(name_); 1030 if (name_string->length() == 0) return NOT_FOUND; 1031 1032 return IsSpecialIndex(isolate_->unicode_cache(), *name_string) 1033 ? INTEGER_INDEXED_EXOTIC 1034 : NOT_FOUND; 1035 } 1036 1037 namespace { 1038 1039 template <bool is_element> 1040 bool HasInterceptor(Map* map) { 1041 return is_element ? map->has_indexed_interceptor() 1042 : map->has_named_interceptor(); 1043 } 1044 1045 } // namespace 1046 1047 template <bool is_element> 1048 LookupIterator::State LookupIterator::LookupInSpecialHolder( 1049 Map* const map, JSReceiver* const holder) { 1050 STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY); 1051 switch (state_) { 1052 case NOT_FOUND: 1053 if (map->IsJSProxyMap()) { 1054 if (is_element || !name_->IsPrivate()) return JSPROXY; 1055 } 1056 if (map->is_access_check_needed()) { 1057 if (is_element || !name_->IsPrivate()) return ACCESS_CHECK; 1058 } 1059 V8_FALLTHROUGH; 1060 case ACCESS_CHECK: 1061 if (check_interceptor() && HasInterceptor<is_element>(map) && 1062 !SkipInterceptor<is_element>(JSObject::cast(holder))) { 1063 if (is_element || !name_->IsPrivate()) return INTERCEPTOR; 1064 } 1065 V8_FALLTHROUGH; 1066 case INTERCEPTOR: 1067 if (!is_element && map->IsJSGlobalObjectMap()) { 1068 GlobalDictionary* dict = 1069 JSGlobalObject::cast(holder)->global_dictionary(); 1070 int number = dict->FindEntry(isolate(), name_); 1071 if (number == GlobalDictionary::kNotFound) return NOT_FOUND; 1072 number_ = static_cast<uint32_t>(number); 1073 PropertyCell* cell = dict->CellAt(number_); 1074 if (cell->value()->IsTheHole(isolate_)) return NOT_FOUND; 1075 property_details_ = cell->property_details(); 1076 has_property_ = true; 1077 switch (property_details_.kind()) { 1078 case v8::internal::kData: 1079 return DATA; 1080 case v8::internal::kAccessor: 1081 return ACCESSOR; 1082 } 1083 } 1084 return LookupInRegularHolder<is_element>(map, holder); 1085 case ACCESSOR: 1086 case DATA: 1087 return NOT_FOUND; 1088 case INTEGER_INDEXED_EXOTIC: 1089 case JSPROXY: 1090 case TRANSITION: 1091 UNREACHABLE(); 1092 } 1093 UNREACHABLE(); 1094 } 1095 1096 template <bool is_element> 1097 LookupIterator::State LookupIterator::LookupInRegularHolder( 1098 Map* const map, JSReceiver* const holder) { 1099 DisallowHeapAllocation no_gc; 1100 if (interceptor_state_ == InterceptorState::kProcessNonMasking) { 1101 return NOT_FOUND; 1102 } 1103 1104 if (is_element) { 1105 JSObject* js_object = JSObject::cast(holder); 1106 ElementsAccessor* accessor = js_object->GetElementsAccessor(); 1107 FixedArrayBase* backing_store = js_object->elements(); 1108 number_ = 1109 accessor->GetEntryForIndex(isolate_, js_object, backing_store, index_); 1110 if (number_ == kMaxUInt32) { 1111 return holder->IsJSTypedArray() ? INTEGER_INDEXED_EXOTIC : NOT_FOUND; 1112 } 1113 property_details_ = accessor->GetDetails(js_object, number_); 1114 } else if (!map->is_dictionary_map()) { 1115 DescriptorArray* descriptors = map->instance_descriptors(); 1116 int number = descriptors->SearchWithCache(isolate_, *name_, map); 1117 if (number == DescriptorArray::kNotFound) return NotFound(holder); 1118 number_ = static_cast<uint32_t>(number); 1119 property_details_ = descriptors->GetDetails(number_); 1120 } else { 1121 DCHECK_IMPLIES(holder->IsJSProxy(), name()->IsPrivate()); 1122 NameDictionary* dict = holder->property_dictionary(); 1123 int number = dict->FindEntry(isolate(), name_); 1124 if (number == NameDictionary::kNotFound) return NotFound(holder); 1125 number_ = static_cast<uint32_t>(number); 1126 property_details_ = dict->DetailsAt(number_); 1127 } 1128 has_property_ = true; 1129 switch (property_details_.kind()) { 1130 case v8::internal::kData: 1131 return DATA; 1132 case v8::internal::kAccessor: 1133 return ACCESSOR; 1134 } 1135 1136 UNREACHABLE(); 1137 } 1138 1139 Handle<InterceptorInfo> LookupIterator::GetInterceptorForFailedAccessCheck() 1140 const { 1141 DCHECK_EQ(ACCESS_CHECK, state_); 1142 DisallowHeapAllocation no_gc; 1143 AccessCheckInfo* access_check_info = 1144 AccessCheckInfo::Get(isolate_, Handle<JSObject>::cast(holder_)); 1145 if (access_check_info) { 1146 Object* interceptor = IsElement() ? access_check_info->indexed_interceptor() 1147 : access_check_info->named_interceptor(); 1148 if (interceptor) { 1149 return handle(InterceptorInfo::cast(interceptor), isolate_); 1150 } 1151 } 1152 return Handle<InterceptorInfo>(); 1153 } 1154 1155 bool LookupIterator::TryLookupCachedProperty() { 1156 return state() == LookupIterator::ACCESSOR && 1157 GetAccessors()->IsAccessorPair() && LookupCachedProperty(); 1158 } 1159 1160 bool LookupIterator::LookupCachedProperty() { 1161 DCHECK_EQ(state(), LookupIterator::ACCESSOR); 1162 DCHECK(GetAccessors()->IsAccessorPair()); 1163 1164 AccessorPair* accessor_pair = AccessorPair::cast(*GetAccessors()); 1165 Handle<Object> getter(accessor_pair->getter(), isolate()); 1166 MaybeHandle<Name> maybe_name = 1167 FunctionTemplateInfo::TryGetCachedPropertyName(isolate(), getter); 1168 if (maybe_name.is_null()) return false; 1169 1170 // We have found a cached property! Modify the iterator accordingly. 1171 name_ = maybe_name.ToHandleChecked(); 1172 Restart(); 1173 CHECK_EQ(state(), LookupIterator::DATA); 1174 return true; 1175 } 1176 1177 } // namespace internal 1178 } // namespace v8 1179