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 13 namespace v8 { 14 namespace internal { 15 16 17 // static 18 LookupIterator LookupIterator::PropertyOrElement(Isolate* isolate, 19 Handle<Object> receiver, 20 Handle<Object> key, 21 bool* success, 22 Configuration configuration) { 23 uint32_t index = 0; 24 if (key->ToArrayIndex(&index)) { 25 *success = true; 26 return LookupIterator(isolate, receiver, index, configuration); 27 } 28 29 Handle<Name> name; 30 *success = Object::ToName(isolate, key).ToHandle(&name); 31 if (!*success) { 32 DCHECK(isolate->has_pending_exception()); 33 // Return an unusable dummy. 34 return LookupIterator(receiver, isolate->factory()->empty_string()); 35 } 36 37 if (name->AsArrayIndex(&index)) { 38 LookupIterator it(isolate, receiver, index, configuration); 39 // Here we try to avoid having to rebuild the string later 40 // by storing it on the indexed LookupIterator. 41 it.name_ = name; 42 return it; 43 } 44 45 return LookupIterator(receiver, name, configuration); 46 } 47 48 template <bool is_element> 49 void LookupIterator::Start() { 50 DisallowHeapAllocation no_gc; 51 52 has_property_ = false; 53 state_ = NOT_FOUND; 54 holder_ = initial_holder_; 55 56 JSReceiver* holder = *holder_; 57 Map* map = holder->map(); 58 59 state_ = LookupInHolder<is_element>(map, holder); 60 if (IsFound()) return; 61 62 NextInternal<is_element>(map, holder); 63 } 64 65 template void LookupIterator::Start<true>(); 66 template void LookupIterator::Start<false>(); 67 68 void LookupIterator::Next() { 69 DCHECK_NE(JSPROXY, state_); 70 DCHECK_NE(TRANSITION, state_); 71 DisallowHeapAllocation no_gc; 72 has_property_ = false; 73 74 JSReceiver* holder = *holder_; 75 Map* map = holder->map(); 76 77 if (map->instance_type() <= LAST_SPECIAL_RECEIVER_TYPE) { 78 state_ = IsElement() ? LookupInSpecialHolder<true>(map, holder) 79 : LookupInSpecialHolder<false>(map, holder); 80 if (IsFound()) return; 81 } 82 83 IsElement() ? NextInternal<true>(map, holder) 84 : NextInternal<false>(map, holder); 85 } 86 87 template <bool is_element> 88 void LookupIterator::NextInternal(Map* map, JSReceiver* holder) { 89 do { 90 JSReceiver* maybe_holder = NextHolder(map); 91 if (maybe_holder == nullptr) { 92 if (interceptor_state_ == InterceptorState::kSkipNonMasking) { 93 RestartLookupForNonMaskingInterceptors<is_element>(); 94 return; 95 } 96 state_ = NOT_FOUND; 97 if (holder != *holder_) holder_ = handle(holder, isolate_); 98 return; 99 } 100 holder = maybe_holder; 101 map = holder->map(); 102 state_ = LookupInHolder<is_element>(map, holder); 103 } while (!IsFound()); 104 105 holder_ = handle(holder, isolate_); 106 } 107 108 template <bool is_element> 109 void LookupIterator::RestartInternal(InterceptorState interceptor_state) { 110 interceptor_state_ = interceptor_state; 111 property_details_ = PropertyDetails::Empty(); 112 number_ = DescriptorArray::kNotFound; 113 Start<is_element>(); 114 } 115 116 template void LookupIterator::RestartInternal<true>(InterceptorState); 117 template void LookupIterator::RestartInternal<false>(InterceptorState); 118 119 // static 120 Handle<JSReceiver> LookupIterator::GetRootForNonJSReceiver( 121 Isolate* isolate, Handle<Object> receiver, uint32_t index) { 122 // Strings are the only objects with properties (only elements) directly on 123 // the wrapper. Hence we can skip generating the wrapper for all other cases. 124 if (index != kMaxUInt32 && receiver->IsString() && 125 index < static_cast<uint32_t>(String::cast(*receiver)->length())) { 126 // TODO(verwaest): Speed this up. Perhaps use a cached wrapper on the native 127 // context, ensuring that we don't leak it into JS? 128 Handle<JSFunction> constructor = isolate->string_function(); 129 Handle<JSObject> result = isolate->factory()->NewJSObject(constructor); 130 Handle<JSValue>::cast(result)->set_value(*receiver); 131 return result; 132 } 133 auto root = handle(receiver->GetRootMap(isolate)->prototype(), isolate); 134 if (root->IsNull(isolate)) { 135 unsigned int magic = 0xbbbbbbbb; 136 isolate->PushStackTraceAndDie(magic, *receiver, NULL, magic); 137 } 138 return Handle<JSReceiver>::cast(root); 139 } 140 141 142 Handle<Map> LookupIterator::GetReceiverMap() const { 143 if (receiver_->IsNumber()) return factory()->heap_number_map(); 144 return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_); 145 } 146 147 bool LookupIterator::HasAccess() const { 148 DCHECK_EQ(ACCESS_CHECK, state_); 149 return isolate_->MayAccess(handle(isolate_->context()), 150 GetHolder<JSObject>()); 151 } 152 153 template <bool is_element> 154 void LookupIterator::ReloadPropertyInformation() { 155 state_ = BEFORE_PROPERTY; 156 interceptor_state_ = InterceptorState::kUninitialized; 157 state_ = LookupInHolder<is_element>(holder_->map(), *holder_); 158 DCHECK(IsFound() || !holder_->HasFastProperties()); 159 } 160 161 void LookupIterator::InternalUpdateProtector() { 162 if (isolate_->bootstrapper()->IsActive()) return; 163 164 if (*name_ == heap()->constructor_string()) { 165 if (!isolate_->IsArraySpeciesLookupChainIntact()) return; 166 // Setting the constructor property could change an instance's @@species 167 if (holder_->IsJSArray()) { 168 isolate_->CountUsage( 169 v8::Isolate::UseCounterFeature::kArrayInstanceConstructorModified); 170 isolate_->InvalidateArraySpeciesProtector(); 171 } else if (holder_->map()->is_prototype_map()) { 172 DisallowHeapAllocation no_gc; 173 // Setting the constructor of Array.prototype of any realm also needs 174 // to invalidate the species protector 175 if (isolate_->IsInAnyContext(*holder_, 176 Context::INITIAL_ARRAY_PROTOTYPE_INDEX)) { 177 isolate_->CountUsage(v8::Isolate::UseCounterFeature:: 178 kArrayPrototypeConstructorModified); 179 isolate_->InvalidateArraySpeciesProtector(); 180 } 181 } 182 } else if (*name_ == heap()->species_symbol()) { 183 if (!isolate_->IsArraySpeciesLookupChainIntact()) return; 184 // Setting the Symbol.species property of any Array constructor invalidates 185 // the species protector 186 if (isolate_->IsInAnyContext(*holder_, Context::ARRAY_FUNCTION_INDEX)) { 187 isolate_->CountUsage( 188 v8::Isolate::UseCounterFeature::kArraySpeciesModified); 189 isolate_->InvalidateArraySpeciesProtector(); 190 } 191 } else if (*name_ == heap()->is_concat_spreadable_symbol()) { 192 if (!isolate_->IsIsConcatSpreadableLookupChainIntact()) return; 193 isolate_->InvalidateIsConcatSpreadableProtector(); 194 } else if (*name_ == heap()->has_instance_symbol()) { 195 if (!isolate_->IsHasInstanceLookupChainIntact()) return; 196 isolate_->InvalidateHasInstanceProtector(); 197 } 198 } 199 200 void LookupIterator::PrepareForDataProperty(Handle<Object> value) { 201 DCHECK(state_ == DATA || state_ == ACCESSOR); 202 DCHECK(HolderIsReceiverOrHiddenPrototype()); 203 204 Handle<JSObject> holder = GetHolder<JSObject>(); 205 206 if (IsElement()) { 207 ElementsKind kind = holder->GetElementsKind(); 208 ElementsKind to = value->OptimalElementsKind(); 209 if (IsHoleyElementsKind(kind)) to = GetHoleyElementsKind(to); 210 to = GetMoreGeneralElementsKind(kind, to); 211 212 if (kind != to) { 213 JSObject::TransitionElementsKind(holder, to); 214 } 215 216 // Copy the backing store if it is copy-on-write. 217 if (IsFastSmiOrObjectElementsKind(to)) { 218 JSObject::EnsureWritableFastElements(holder); 219 } 220 return; 221 } 222 223 if (!holder->HasFastProperties()) return; 224 225 Handle<Map> old_map(holder->map(), isolate_); 226 Handle<Map> new_map = 227 Map::PrepareForDataProperty(old_map, descriptor_number(), value); 228 229 if (old_map.is_identical_to(new_map)) { 230 // Update the property details if the representation was None. 231 if (representation().IsNone()) { 232 property_details_ = 233 new_map->instance_descriptors()->GetDetails(descriptor_number()); 234 } 235 return; 236 } 237 238 JSObject::MigrateToMap(holder, new_map); 239 ReloadPropertyInformation<false>(); 240 } 241 242 243 void LookupIterator::ReconfigureDataProperty(Handle<Object> value, 244 PropertyAttributes attributes) { 245 DCHECK(state_ == DATA || state_ == ACCESSOR); 246 DCHECK(HolderIsReceiverOrHiddenPrototype()); 247 Handle<JSObject> holder = GetHolder<JSObject>(); 248 if (IsElement()) { 249 DCHECK(!holder->HasFixedTypedArrayElements()); 250 DCHECK(attributes != NONE || !holder->HasFastElements()); 251 Handle<FixedArrayBase> elements(holder->elements()); 252 holder->GetElementsAccessor()->Reconfigure(holder, elements, number_, value, 253 attributes); 254 ReloadPropertyInformation<true>(); 255 } else { 256 if (!holder->HasFastProperties()) { 257 PropertyDetails details(attributes, v8::internal::DATA, 0, 258 PropertyCellType::kMutable); 259 JSObject::SetNormalizedProperty(holder, name(), value, details); 260 } else { 261 Handle<Map> old_map(holder->map(), isolate_); 262 Handle<Map> new_map = Map::ReconfigureExistingProperty( 263 old_map, descriptor_number(), i::kData, attributes); 264 new_map = 265 Map::PrepareForDataProperty(new_map, descriptor_number(), value); 266 JSObject::MigrateToMap(holder, new_map); 267 } 268 ReloadPropertyInformation<false>(); 269 } 270 271 WriteDataValue(value); 272 273 #if VERIFY_HEAP 274 if (FLAG_verify_heap) { 275 holder->JSObjectVerify(); 276 } 277 #endif 278 } 279 280 // Can only be called when the receiver is a JSObject. JSProxy has to be handled 281 // via a trap. Adding properties to primitive values is not observable. 282 void LookupIterator::PrepareTransitionToDataProperty( 283 Handle<JSObject> receiver, Handle<Object> value, 284 PropertyAttributes attributes, Object::StoreFromKeyed store_mode) { 285 DCHECK(receiver.is_identical_to(GetStoreTarget())); 286 if (state_ == TRANSITION) return; 287 DCHECK(state_ != LookupIterator::ACCESSOR || 288 (GetAccessors()->IsAccessorInfo() && 289 AccessorInfo::cast(*GetAccessors())->is_special_data_property())); 290 DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_); 291 DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype()); 292 293 Handle<Map> map(receiver->map(), isolate_); 294 295 // Dictionary maps can always have additional data properties. 296 if (map->is_dictionary_map()) { 297 state_ = TRANSITION; 298 if (map->IsJSGlobalObjectMap()) { 299 // Install a property cell. 300 auto cell = JSGlobalObject::EnsurePropertyCell( 301 Handle<JSGlobalObject>::cast(receiver), name()); 302 DCHECK(cell->value()->IsTheHole(isolate_)); 303 transition_ = cell; 304 } else { 305 transition_ = map; 306 } 307 return; 308 } 309 310 Handle<Map> transition = 311 Map::TransitionToDataProperty(map, name_, value, attributes, store_mode); 312 state_ = TRANSITION; 313 transition_ = transition; 314 315 if (!transition->is_dictionary_map()) { 316 property_details_ = transition->GetLastDescriptorDetails(); 317 has_property_ = true; 318 } 319 } 320 321 void LookupIterator::ApplyTransitionToDataProperty(Handle<JSObject> receiver) { 322 DCHECK_EQ(TRANSITION, state_); 323 324 DCHECK(receiver.is_identical_to(GetStoreTarget())); 325 326 if (receiver->IsJSGlobalObject()) return; 327 holder_ = receiver; 328 Handle<Map> transition = transition_map(); 329 bool simple_transition = transition->GetBackPointer() == receiver->map(); 330 JSObject::MigrateToMap(receiver, transition); 331 332 if (simple_transition) { 333 int number = transition->LastAdded(); 334 number_ = static_cast<uint32_t>(number); 335 property_details_ = transition->GetLastDescriptorDetails(); 336 state_ = DATA; 337 } else { 338 ReloadPropertyInformation<false>(); 339 } 340 } 341 342 343 void LookupIterator::Delete() { 344 Handle<JSReceiver> holder = Handle<JSReceiver>::cast(holder_); 345 if (IsElement()) { 346 Handle<JSObject> object = Handle<JSObject>::cast(holder); 347 ElementsAccessor* accessor = object->GetElementsAccessor(); 348 accessor->Delete(object, number_); 349 } else { 350 bool is_prototype_map = holder->map()->is_prototype_map(); 351 RuntimeCallTimerScope stats_scope( 352 isolate_, is_prototype_map 353 ? &RuntimeCallStats::PrototypeObject_DeleteProperty 354 : &RuntimeCallStats::Object_DeleteProperty); 355 356 PropertyNormalizationMode mode = 357 is_prototype_map ? KEEP_INOBJECT_PROPERTIES : CLEAR_INOBJECT_PROPERTIES; 358 359 if (holder->HasFastProperties()) { 360 JSObject::NormalizeProperties(Handle<JSObject>::cast(holder), mode, 0, 361 "DeletingProperty"); 362 ReloadPropertyInformation<false>(); 363 } 364 // TODO(verwaest): Get rid of the name_ argument. 365 JSReceiver::DeleteNormalizedProperty(holder, name_, number_); 366 if (holder->IsJSObject()) { 367 JSObject::ReoptimizeIfPrototype(Handle<JSObject>::cast(holder)); 368 } 369 } 370 state_ = NOT_FOUND; 371 } 372 373 void LookupIterator::TransitionToAccessorProperty( 374 Handle<Object> getter, Handle<Object> setter, 375 PropertyAttributes attributes) { 376 DCHECK(!getter->IsNull(isolate_) || !setter->IsNull(isolate_)); 377 // Can only be called when the receiver is a JSObject. JSProxy has to be 378 // handled via a trap. Adding properties to primitive values is not 379 // observable. 380 Handle<JSObject> receiver = GetStoreTarget(); 381 382 if (!IsElement() && !receiver->map()->is_dictionary_map()) { 383 Handle<Map> old_map(receiver->map(), isolate_); 384 385 if (!holder_.is_identical_to(receiver)) { 386 holder_ = receiver; 387 state_ = NOT_FOUND; 388 } else if (state_ == INTERCEPTOR) { 389 LookupInRegularHolder<false>(*old_map, *holder_); 390 } 391 int descriptor = 392 IsFound() ? static_cast<int>(number_) : DescriptorArray::kNotFound; 393 394 Handle<Map> new_map = Map::TransitionToAccessorProperty( 395 isolate_, old_map, name_, descriptor, getter, setter, attributes); 396 bool simple_transition = new_map->GetBackPointer() == receiver->map(); 397 JSObject::MigrateToMap(receiver, new_map); 398 399 if (simple_transition) { 400 int number = new_map->LastAdded(); 401 number_ = static_cast<uint32_t>(number); 402 property_details_ = new_map->GetLastDescriptorDetails(); 403 state_ = ACCESSOR; 404 return; 405 } 406 407 ReloadPropertyInformation<false>(); 408 if (!new_map->is_dictionary_map()) return; 409 } 410 411 Handle<AccessorPair> pair; 412 if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) { 413 pair = Handle<AccessorPair>::cast(GetAccessors()); 414 // If the component and attributes are identical, nothing has to be done. 415 if (pair->Equals(*getter, *setter)) { 416 if (property_details().attributes() == attributes) { 417 if (!IsElement()) JSObject::ReoptimizeIfPrototype(receiver); 418 return; 419 } 420 } else { 421 pair = AccessorPair::Copy(pair); 422 pair->SetComponents(*getter, *setter); 423 } 424 } else { 425 pair = factory()->NewAccessorPair(); 426 pair->SetComponents(*getter, *setter); 427 } 428 429 TransitionToAccessorPair(pair, attributes); 430 431 #if VERIFY_HEAP 432 if (FLAG_verify_heap) { 433 receiver->JSObjectVerify(); 434 } 435 #endif 436 } 437 438 439 void LookupIterator::TransitionToAccessorPair(Handle<Object> pair, 440 PropertyAttributes attributes) { 441 Handle<JSObject> receiver = GetStoreTarget(); 442 holder_ = receiver; 443 444 PropertyDetails details(attributes, ACCESSOR_CONSTANT, 0, 445 PropertyCellType::kMutable); 446 447 if (IsElement()) { 448 // TODO(verwaest): Move code into the element accessor. 449 Handle<SeededNumberDictionary> dictionary = 450 JSObject::NormalizeElements(receiver); 451 452 // We unconditionally pass used_as_prototype=false here because the call 453 // to RequireSlowElements takes care of the required IC clearing and 454 // we don't want to walk the heap twice. 455 dictionary = 456 SeededNumberDictionary::Set(dictionary, index_, pair, details, false); 457 receiver->RequireSlowElements(*dictionary); 458 459 if (receiver->HasSlowArgumentsElements()) { 460 FixedArray* parameter_map = FixedArray::cast(receiver->elements()); 461 uint32_t length = parameter_map->length() - 2; 462 if (number_ < length) { 463 parameter_map->set(number_ + 2, heap()->the_hole_value()); 464 } 465 FixedArray::cast(receiver->elements())->set(1, *dictionary); 466 } else { 467 receiver->set_elements(*dictionary); 468 } 469 470 ReloadPropertyInformation<true>(); 471 } else { 472 PropertyNormalizationMode mode = receiver->map()->is_prototype_map() 473 ? KEEP_INOBJECT_PROPERTIES 474 : CLEAR_INOBJECT_PROPERTIES; 475 // Normalize object to make this operation simple. 476 JSObject::NormalizeProperties(receiver, mode, 0, 477 "TransitionToAccessorPair"); 478 479 JSObject::SetNormalizedProperty(receiver, name_, pair, details); 480 JSObject::ReoptimizeIfPrototype(receiver); 481 482 ReloadPropertyInformation<false>(); 483 } 484 } 485 486 487 bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const { 488 DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY); 489 // Optimization that only works if configuration_ is not mutable. 490 if (!check_prototype_chain()) return true; 491 DisallowHeapAllocation no_gc; 492 if (*receiver_ == *holder_) return true; 493 if (!receiver_->IsJSReceiver()) return false; 494 JSReceiver* current = JSReceiver::cast(*receiver_); 495 JSReceiver* object = *holder_; 496 if (!current->map()->has_hidden_prototype()) return false; 497 // JSProxy do not occur as hidden prototypes. 498 if (object->IsJSProxy()) return false; 499 PrototypeIterator iter(isolate(), current, kStartAtPrototype, 500 PrototypeIterator::END_AT_NON_HIDDEN); 501 while (!iter.IsAtEnd()) { 502 if (iter.GetCurrent<JSReceiver>() == object) return true; 503 iter.Advance(); 504 } 505 return false; 506 } 507 508 509 Handle<Object> LookupIterator::FetchValue() const { 510 Object* result = NULL; 511 if (IsElement()) { 512 Handle<JSObject> holder = GetHolder<JSObject>(); 513 ElementsAccessor* accessor = holder->GetElementsAccessor(); 514 return accessor->Get(holder, number_); 515 } else if (holder_->IsJSGlobalObject()) { 516 Handle<JSObject> holder = GetHolder<JSObject>(); 517 result = holder->global_dictionary()->ValueAt(number_); 518 DCHECK(result->IsPropertyCell()); 519 result = PropertyCell::cast(result)->value(); 520 } else if (!holder_->HasFastProperties()) { 521 result = holder_->property_dictionary()->ValueAt(number_); 522 } else if (property_details_.type() == v8::internal::DATA) { 523 Handle<JSObject> holder = GetHolder<JSObject>(); 524 FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_); 525 return JSObject::FastPropertyAt(holder, property_details_.representation(), 526 field_index); 527 } else { 528 result = holder_->map()->instance_descriptors()->GetValue(number_); 529 } 530 return handle(result, isolate_); 531 } 532 533 534 int LookupIterator::GetAccessorIndex() const { 535 DCHECK(has_property_); 536 DCHECK(holder_->HasFastProperties()); 537 DCHECK_EQ(v8::internal::ACCESSOR_CONSTANT, property_details_.type()); 538 return descriptor_number(); 539 } 540 541 542 int LookupIterator::GetConstantIndex() const { 543 DCHECK(has_property_); 544 DCHECK(holder_->HasFastProperties()); 545 DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type()); 546 DCHECK(!IsElement()); 547 return descriptor_number(); 548 } 549 550 551 FieldIndex LookupIterator::GetFieldIndex() const { 552 DCHECK(has_property_); 553 DCHECK(holder_->HasFastProperties()); 554 DCHECK_EQ(v8::internal::DATA, property_details_.type()); 555 DCHECK(!IsElement()); 556 Map* holder_map = holder_->map(); 557 int index = 558 holder_map->instance_descriptors()->GetFieldIndex(descriptor_number()); 559 bool is_double = representation().IsDouble(); 560 return FieldIndex::ForPropertyIndex(holder_map, index, is_double); 561 } 562 563 Handle<FieldType> LookupIterator::GetFieldType() const { 564 DCHECK(has_property_); 565 DCHECK(holder_->HasFastProperties()); 566 DCHECK_EQ(v8::internal::DATA, property_details_.type()); 567 return handle( 568 holder_->map()->instance_descriptors()->GetFieldType(descriptor_number()), 569 isolate_); 570 } 571 572 573 Handle<PropertyCell> LookupIterator::GetPropertyCell() const { 574 DCHECK(!IsElement()); 575 Handle<JSObject> holder = GetHolder<JSObject>(); 576 Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(holder); 577 Object* value = global->global_dictionary()->ValueAt(dictionary_entry()); 578 DCHECK(value->IsPropertyCell()); 579 return handle(PropertyCell::cast(value)); 580 } 581 582 583 Handle<Object> LookupIterator::GetAccessors() const { 584 DCHECK_EQ(ACCESSOR, state_); 585 return FetchValue(); 586 } 587 588 589 Handle<Object> LookupIterator::GetDataValue() const { 590 DCHECK_EQ(DATA, state_); 591 Handle<Object> value = FetchValue(); 592 return value; 593 } 594 595 596 void LookupIterator::WriteDataValue(Handle<Object> value) { 597 DCHECK_EQ(DATA, state_); 598 Handle<JSReceiver> holder = GetHolder<JSReceiver>(); 599 if (IsElement()) { 600 Handle<JSObject> object = Handle<JSObject>::cast(holder); 601 ElementsAccessor* accessor = object->GetElementsAccessor(); 602 accessor->Set(object, number_, *value); 603 } else if (holder->HasFastProperties()) { 604 if (property_details_.type() == v8::internal::DATA) { 605 JSObject::cast(*holder)->WriteToField(descriptor_number(), 606 property_details_, *value); 607 } else { 608 DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type()); 609 } 610 } else if (holder->IsJSGlobalObject()) { 611 Handle<GlobalDictionary> property_dictionary = 612 handle(JSObject::cast(*holder)->global_dictionary()); 613 PropertyCell::UpdateCell(property_dictionary, dictionary_entry(), value, 614 property_details_); 615 } else { 616 NameDictionary* property_dictionary = holder->property_dictionary(); 617 property_dictionary->ValueAtPut(dictionary_entry(), *value); 618 } 619 } 620 621 template <bool is_element> 622 bool LookupIterator::SkipInterceptor(JSObject* holder) { 623 auto info = GetInterceptor<is_element>(holder); 624 // TODO(dcarney): check for symbol/can_intercept_symbols here as well. 625 if (info->non_masking()) { 626 switch (interceptor_state_) { 627 case InterceptorState::kUninitialized: 628 interceptor_state_ = InterceptorState::kSkipNonMasking; 629 // Fall through. 630 case InterceptorState::kSkipNonMasking: 631 return true; 632 case InterceptorState::kProcessNonMasking: 633 return false; 634 } 635 } 636 return interceptor_state_ == InterceptorState::kProcessNonMasking; 637 } 638 639 JSReceiver* LookupIterator::NextHolder(Map* map) { 640 DisallowHeapAllocation no_gc; 641 if (map->prototype() == heap()->null_value()) return NULL; 642 if (!check_prototype_chain() && !map->has_hidden_prototype()) return NULL; 643 return JSReceiver::cast(map->prototype()); 644 } 645 646 LookupIterator::State LookupIterator::NotFound(JSReceiver* const holder) const { 647 DCHECK(!IsElement()); 648 if (!holder->IsJSTypedArray() || !name_->IsString()) return NOT_FOUND; 649 650 Handle<String> name_string = Handle<String>::cast(name_); 651 if (name_string->length() == 0) return NOT_FOUND; 652 653 return IsSpecialIndex(isolate_->unicode_cache(), *name_string) 654 ? INTEGER_INDEXED_EXOTIC 655 : NOT_FOUND; 656 } 657 658 namespace { 659 660 template <bool is_element> 661 bool HasInterceptor(Map* map) { 662 return is_element ? map->has_indexed_interceptor() 663 : map->has_named_interceptor(); 664 } 665 666 } // namespace 667 668 template <bool is_element> 669 LookupIterator::State LookupIterator::LookupInSpecialHolder( 670 Map* const map, JSReceiver* const holder) { 671 STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY); 672 switch (state_) { 673 case NOT_FOUND: 674 if (map->IsJSProxyMap()) { 675 if (is_element || !name_->IsPrivate()) return JSPROXY; 676 } 677 if (map->is_access_check_needed()) { 678 if (is_element || !name_->IsPrivate()) return ACCESS_CHECK; 679 } 680 // Fall through. 681 case ACCESS_CHECK: 682 if (check_interceptor() && HasInterceptor<is_element>(map) && 683 !SkipInterceptor<is_element>(JSObject::cast(holder))) { 684 if (is_element || !name_->IsPrivate()) return INTERCEPTOR; 685 } 686 // Fall through. 687 case INTERCEPTOR: 688 if (!is_element && map->IsJSGlobalObjectMap()) { 689 GlobalDictionary* dict = JSObject::cast(holder)->global_dictionary(); 690 int number = dict->FindEntry(name_); 691 if (number == GlobalDictionary::kNotFound) return NOT_FOUND; 692 number_ = static_cast<uint32_t>(number); 693 DCHECK(dict->ValueAt(number_)->IsPropertyCell()); 694 PropertyCell* cell = PropertyCell::cast(dict->ValueAt(number_)); 695 if (cell->value()->IsTheHole(isolate_)) return NOT_FOUND; 696 property_details_ = cell->property_details(); 697 has_property_ = true; 698 switch (property_details_.kind()) { 699 case v8::internal::kData: 700 return DATA; 701 case v8::internal::kAccessor: 702 return ACCESSOR; 703 } 704 } 705 return LookupInRegularHolder<is_element>(map, holder); 706 case ACCESSOR: 707 case DATA: 708 return NOT_FOUND; 709 case INTEGER_INDEXED_EXOTIC: 710 case JSPROXY: 711 case TRANSITION: 712 UNREACHABLE(); 713 } 714 UNREACHABLE(); 715 return NOT_FOUND; 716 } 717 718 template <bool is_element> 719 LookupIterator::State LookupIterator::LookupInRegularHolder( 720 Map* const map, JSReceiver* const holder) { 721 DisallowHeapAllocation no_gc; 722 if (interceptor_state_ == InterceptorState::kProcessNonMasking) { 723 return NOT_FOUND; 724 } 725 726 if (is_element) { 727 JSObject* js_object = JSObject::cast(holder); 728 ElementsAccessor* accessor = js_object->GetElementsAccessor(); 729 FixedArrayBase* backing_store = js_object->elements(); 730 number_ = accessor->GetEntryForIndex(js_object, backing_store, index_); 731 if (number_ == kMaxUInt32) { 732 return holder->IsJSTypedArray() ? INTEGER_INDEXED_EXOTIC : NOT_FOUND; 733 } 734 property_details_ = accessor->GetDetails(js_object, number_); 735 } else if (!map->is_dictionary_map()) { 736 DescriptorArray* descriptors = map->instance_descriptors(); 737 int number = descriptors->SearchWithCache(isolate_, *name_, map); 738 if (number == DescriptorArray::kNotFound) return NotFound(holder); 739 number_ = static_cast<uint32_t>(number); 740 property_details_ = descriptors->GetDetails(number_); 741 } else { 742 NameDictionary* dict = holder->property_dictionary(); 743 int number = dict->FindEntry(name_); 744 if (number == NameDictionary::kNotFound) return NotFound(holder); 745 number_ = static_cast<uint32_t>(number); 746 property_details_ = dict->DetailsAt(number_); 747 } 748 has_property_ = true; 749 switch (property_details_.kind()) { 750 case v8::internal::kData: 751 return DATA; 752 case v8::internal::kAccessor: 753 return ACCESSOR; 754 } 755 756 UNREACHABLE(); 757 return state_; 758 } 759 760 Handle<InterceptorInfo> LookupIterator::GetInterceptorForFailedAccessCheck() 761 const { 762 DCHECK_EQ(ACCESS_CHECK, state_); 763 DisallowHeapAllocation no_gc; 764 AccessCheckInfo* access_check_info = 765 AccessCheckInfo::Get(isolate_, Handle<JSObject>::cast(holder_)); 766 if (access_check_info) { 767 Object* interceptor = IsElement() ? access_check_info->indexed_interceptor() 768 : access_check_info->named_interceptor(); 769 if (interceptor) { 770 return handle(InterceptorInfo::cast(interceptor), isolate_); 771 } 772 } 773 return Handle<InterceptorInfo>(); 774 } 775 776 } // namespace internal 777 } // namespace v8 778