v8/src/lookup.cc
ishell 3fbb45216d [runtime] Better encapsulation of dictionary objects handling in lookup iterator.
Now LookupIterator follows the same pattern of prepare transition, apply transition
and write value when adding new properties to dictionary objects.

JSGlobalObject case:
* Prepare transition phase ensures that there is a "transition" property cell
  prepared for receiving a value.
* Apply transition phase does nothing.
* Prepare for data property phase ensures that the existing property cell can
  receive the value.
* Write value phase writes value directly to the current property cell.

JSObject case:
* Prepare transition phase prepares the object for receiving a data value (which
  could switch an object to dictionary mode).
* Apply transition phase migrates object to a transition map. If the map happened
  to be a dictionary mode object's map then an uninitialized entry added to the
  properties dictionary.
* Prepare for data property phase does nothing.
* Write value phase just puts value to the properties dictionary.

BUG=chromium:576312

Review-Url: https://codereview.chromium.org/2127583002
Cr-Commit-Position: refs/heads/master@{#37585}
2016-07-07 12:35:06 +00:00

837 lines
29 KiB
C++

// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/lookup.h"
#include "src/bootstrapper.h"
#include "src/deoptimizer.h"
#include "src/elements.h"
#include "src/field-type.h"
#include "src/isolate-inl.h"
namespace v8 {
namespace internal {
// static
LookupIterator LookupIterator::PropertyOrElement(Isolate* isolate,
Handle<Object> receiver,
Handle<Object> key,
bool* success,
Configuration configuration) {
uint32_t index = 0;
if (key->ToArrayIndex(&index)) {
*success = true;
return LookupIterator(isolate, receiver, index, configuration);
}
Handle<Name> name;
*success = Object::ToName(isolate, key).ToHandle(&name);
if (!*success) {
DCHECK(isolate->has_pending_exception());
// Return an unusable dummy.
return LookupIterator(receiver, isolate->factory()->empty_string());
}
if (name->AsArrayIndex(&index)) {
LookupIterator it(isolate, receiver, index, configuration);
// Here we try to avoid having to rebuild the string later
// by storing it on the indexed LookupIterator.
it.name_ = name;
return it;
}
return LookupIterator(receiver, name, configuration);
}
template <bool is_element>
void LookupIterator::Start() {
DisallowHeapAllocation no_gc;
has_property_ = false;
state_ = NOT_FOUND;
holder_ = initial_holder_;
JSReceiver* holder = *holder_;
Map* map = holder->map();
state_ = LookupInHolder<is_element>(map, holder);
if (IsFound()) return;
NextInternal<is_element>(map, holder);
}
template void LookupIterator::Start<true>();
template void LookupIterator::Start<false>();
void LookupIterator::Next() {
DCHECK_NE(JSPROXY, state_);
DCHECK_NE(TRANSITION, state_);
DisallowHeapAllocation no_gc;
has_property_ = false;
JSReceiver* holder = *holder_;
Map* map = holder->map();
if (map->instance_type() <= LAST_SPECIAL_RECEIVER_TYPE) {
state_ = IsElement() ? LookupInSpecialHolder<true>(map, holder)
: LookupInSpecialHolder<false>(map, holder);
if (IsFound()) return;
}
IsElement() ? NextInternal<true>(map, holder)
: NextInternal<false>(map, holder);
}
template <bool is_element>
void LookupIterator::NextInternal(Map* map, JSReceiver* holder) {
do {
JSReceiver* maybe_holder = NextHolder(map);
if (maybe_holder == nullptr) {
if (interceptor_state_ == InterceptorState::kSkipNonMasking) {
RestartLookupForNonMaskingInterceptors<is_element>();
return;
}
state_ = NOT_FOUND;
if (holder != *holder_) holder_ = handle(holder, isolate_);
return;
}
holder = maybe_holder;
map = holder->map();
state_ = LookupInHolder<is_element>(map, holder);
} while (!IsFound());
holder_ = handle(holder, isolate_);
}
template <bool is_element>
void LookupIterator::RestartInternal(InterceptorState interceptor_state) {
interceptor_state_ = interceptor_state;
property_details_ = PropertyDetails::Empty();
number_ = DescriptorArray::kNotFound;
Start<is_element>();
}
template void LookupIterator::RestartInternal<true>(InterceptorState);
template void LookupIterator::RestartInternal<false>(InterceptorState);
// static
Handle<JSReceiver> LookupIterator::GetRootForNonJSReceiver(
Isolate* isolate, Handle<Object> receiver, uint32_t index) {
// Strings are the only objects with properties (only elements) directly on
// the wrapper. Hence we can skip generating the wrapper for all other cases.
if (index != kMaxUInt32 && receiver->IsString() &&
index < static_cast<uint32_t>(String::cast(*receiver)->length())) {
// TODO(verwaest): Speed this up. Perhaps use a cached wrapper on the native
// context, ensuring that we don't leak it into JS?
Handle<JSFunction> constructor = isolate->string_function();
Handle<JSObject> result = isolate->factory()->NewJSObject(constructor);
Handle<JSValue>::cast(result)->set_value(*receiver);
return result;
}
auto root = handle(receiver->GetRootMap(isolate)->prototype(), isolate);
if (root->IsNull(isolate)) {
unsigned int magic = 0xbbbbbbbb;
isolate->PushStackTraceAndDie(magic, *receiver, NULL, magic);
}
return Handle<JSReceiver>::cast(root);
}
Handle<Map> LookupIterator::GetReceiverMap() const {
if (receiver_->IsNumber()) return factory()->heap_number_map();
return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_);
}
bool LookupIterator::HasAccess() const {
DCHECK_EQ(ACCESS_CHECK, state_);
return isolate_->MayAccess(handle(isolate_->context()),
GetHolder<JSObject>());
}
template <bool is_element>
void LookupIterator::ReloadPropertyInformation() {
state_ = BEFORE_PROPERTY;
interceptor_state_ = InterceptorState::kUninitialized;
state_ = LookupInHolder<is_element>(holder_->map(), *holder_);
DCHECK(IsFound() || !holder_->HasFastProperties());
}
void LookupIterator::InternalUpdateProtector() {
if (isolate_->bootstrapper()->IsActive()) return;
if (*name_ == heap()->constructor_string()) {
if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
// Setting the constructor property could change an instance's @@species
if (holder_->IsJSArray()) {
isolate_->CountUsage(
v8::Isolate::UseCounterFeature::kArrayInstanceConstructorModified);
isolate_->InvalidateArraySpeciesProtector();
} else if (holder_->map()->is_prototype_map()) {
DisallowHeapAllocation no_gc;
// Setting the constructor of Array.prototype of any realm also needs
// to invalidate the species protector
if (isolate_->IsInAnyContext(*holder_,
Context::INITIAL_ARRAY_PROTOTYPE_INDEX)) {
isolate_->CountUsage(v8::Isolate::UseCounterFeature::
kArrayPrototypeConstructorModified);
isolate_->InvalidateArraySpeciesProtector();
}
}
} else if (*name_ == heap()->species_symbol()) {
if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
// Setting the Symbol.species property of any Array constructor invalidates
// the species protector
if (isolate_->IsInAnyContext(*holder_, Context::ARRAY_FUNCTION_INDEX)) {
isolate_->CountUsage(
v8::Isolate::UseCounterFeature::kArraySpeciesModified);
isolate_->InvalidateArraySpeciesProtector();
}
} else if (*name_ == heap()->is_concat_spreadable_symbol()) {
if (!isolate_->IsIsConcatSpreadableLookupChainIntact()) return;
isolate_->InvalidateIsConcatSpreadableProtector();
} else if (*name_ == heap()->has_instance_symbol()) {
if (!isolate_->IsHasInstanceLookupChainIntact()) return;
isolate_->InvalidateHasInstanceProtector();
}
}
void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
DCHECK(state_ == DATA || state_ == ACCESSOR);
DCHECK(HolderIsReceiverOrHiddenPrototype());
Handle<JSObject> holder = GetHolder<JSObject>();
if (IsElement()) {
ElementsKind kind = holder->GetElementsKind();
ElementsKind to = value->OptimalElementsKind();
if (IsHoleyElementsKind(kind)) to = GetHoleyElementsKind(to);
to = GetMoreGeneralElementsKind(kind, to);
if (kind != to) {
JSObject::TransitionElementsKind(holder, to);
}
// Copy the backing store if it is copy-on-write.
if (IsFastSmiOrObjectElementsKind(to)) {
JSObject::EnsureWritableFastElements(holder);
}
return;
}
if (holder->IsJSGlobalObject()) {
Handle<GlobalDictionary> dictionary(holder->global_dictionary());
Handle<PropertyCell> cell(
PropertyCell::cast(dictionary->ValueAt(dictionary_entry())));
DCHECK(!cell->IsTheHole(isolate_));
property_details_ = cell->property_details();
PropertyCell::PrepareForValue(dictionary, dictionary_entry(), value,
property_details_);
return;
}
if (!holder->HasFastProperties()) return;
Handle<Map> old_map(holder->map(), isolate_);
Handle<Map> new_map =
Map::PrepareForDataProperty(old_map, descriptor_number(), value);
if (old_map.is_identical_to(new_map)) {
// Update the property details if the representation was None.
if (representation().IsNone()) {
property_details_ =
new_map->instance_descriptors()->GetDetails(descriptor_number());
}
return;
}
JSObject::MigrateToMap(holder, new_map);
ReloadPropertyInformation<false>();
}
void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
PropertyAttributes attributes) {
DCHECK(state_ == DATA || state_ == ACCESSOR);
DCHECK(HolderIsReceiverOrHiddenPrototype());
Handle<JSObject> holder = GetHolder<JSObject>();
if (IsElement()) {
DCHECK(!holder->HasFixedTypedArrayElements());
DCHECK(attributes != NONE || !holder->HasFastElements());
Handle<FixedArrayBase> elements(holder->elements());
holder->GetElementsAccessor()->Reconfigure(holder, elements, number_, value,
attributes);
ReloadPropertyInformation<true>();
} else if (holder->HasFastProperties()) {
Handle<Map> old_map(holder->map(), isolate_);
Handle<Map> new_map = Map::ReconfigureExistingProperty(
old_map, descriptor_number(), i::kData, attributes);
new_map = Map::PrepareForDataProperty(new_map, descriptor_number(), value);
JSObject::MigrateToMap(holder, new_map);
ReloadPropertyInformation<false>();
} else {
PropertyDetails details(attributes, v8::internal::DATA, 0,
PropertyCellType::kMutable);
if (holder->IsJSGlobalObject()) {
Handle<GlobalDictionary> dictionary(holder->global_dictionary());
Handle<PropertyCell> cell = PropertyCell::PrepareForValue(
dictionary, dictionary_entry(), value, details);
cell->set_value(*value);
property_details_ = cell->property_details();
} else {
Handle<NameDictionary> dictionary(holder->property_dictionary());
PropertyDetails original_details =
dictionary->DetailsAt(dictionary_entry());
int enumeration_index = original_details.dictionary_index();
DCHECK(enumeration_index > 0);
details = details.set_index(enumeration_index);
dictionary->SetEntry(dictionary_entry(), name(), value, details);
property_details_ = details;
}
state_ = DATA;
}
WriteDataValue(value);
#if VERIFY_HEAP
if (FLAG_verify_heap) {
holder->JSObjectVerify();
}
#endif
}
// Can only be called when the receiver is a JSObject. JSProxy has to be handled
// via a trap. Adding properties to primitive values is not observable.
void LookupIterator::PrepareTransitionToDataProperty(
Handle<JSObject> receiver, Handle<Object> value,
PropertyAttributes attributes, Object::StoreFromKeyed store_mode) {
DCHECK(receiver.is_identical_to(GetStoreTarget()));
if (state_ == TRANSITION) return;
DCHECK(state_ != LookupIterator::ACCESSOR ||
(GetAccessors()->IsAccessorInfo() &&
AccessorInfo::cast(*GetAccessors())->is_special_data_property()));
DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_);
DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
Handle<Map> map(receiver->map(), isolate_);
// Dictionary maps can always have additional data properties.
if (map->is_dictionary_map()) {
state_ = TRANSITION;
if (map->IsJSGlobalObjectMap()) {
// Install a property cell.
Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(receiver);
int entry;
Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell(
global, name(), PropertyCellType::kUninitialized, &entry);
Handle<GlobalDictionary> dictionary(global->global_dictionary(),
isolate_);
DCHECK(cell->value()->IsTheHole(isolate_));
DCHECK(!value->IsTheHole(isolate_));
transition_ = cell;
// Assign an enumeration index to the property and update
// SetNextEnumerationIndex.
int index = dictionary->NextEnumerationIndex();
dictionary->SetNextEnumerationIndex(index + 1);
property_details_ = PropertyDetails(attributes, i::DATA, index,
PropertyCellType::kUninitialized);
PropertyCellType new_type =
PropertyCell::UpdatedType(cell, value, property_details_);
property_details_ = property_details_.set_cell_type(new_type);
cell->set_property_details(property_details_);
number_ = entry;
has_property_ = true;
} else {
// Don't set enumeration index (it will be set during value store).
property_details_ =
PropertyDetails(attributes, i::DATA, 0, PropertyCellType::kNoCell);
transition_ = map;
}
return;
}
Handle<Map> transition =
Map::TransitionToDataProperty(map, name_, value, attributes, store_mode);
state_ = TRANSITION;
transition_ = transition;
if (!transition->is_dictionary_map()) {
property_details_ = transition->GetLastDescriptorDetails();
has_property_ = true;
}
}
void LookupIterator::ApplyTransitionToDataProperty(Handle<JSObject> receiver) {
DCHECK_EQ(TRANSITION, state_);
DCHECK(receiver.is_identical_to(GetStoreTarget()));
holder_ = receiver;
if (receiver->IsJSGlobalObject()) {
state_ = DATA;
return;
}
Handle<Map> transition = transition_map();
bool simple_transition = transition->GetBackPointer() == receiver->map();
JSObject::MigrateToMap(receiver, transition);
if (simple_transition) {
int number = transition->LastAdded();
number_ = static_cast<uint32_t>(number);
property_details_ = transition->GetLastDescriptorDetails();
state_ = DATA;
} else if (receiver->map()->is_dictionary_map()) {
Handle<NameDictionary> dictionary(receiver->property_dictionary(),
isolate_);
int entry;
dictionary = NameDictionary::Add(dictionary, name(),
isolate_->factory()->uninitialized_value(),
property_details_, &entry);
receiver->set_properties(*dictionary);
// Reload details containing proper enumeration index value.
property_details_ = dictionary->DetailsAt(entry);
number_ = entry;
has_property_ = true;
state_ = DATA;
} else {
ReloadPropertyInformation<false>();
}
}
void LookupIterator::Delete() {
Handle<JSReceiver> holder = Handle<JSReceiver>::cast(holder_);
if (IsElement()) {
Handle<JSObject> object = Handle<JSObject>::cast(holder);
ElementsAccessor* accessor = object->GetElementsAccessor();
accessor->Delete(object, number_);
} else {
bool is_prototype_map = holder->map()->is_prototype_map();
RuntimeCallTimerScope stats_scope(
isolate_, is_prototype_map
? &RuntimeCallStats::PrototypeObject_DeleteProperty
: &RuntimeCallStats::Object_DeleteProperty);
PropertyNormalizationMode mode =
is_prototype_map ? KEEP_INOBJECT_PROPERTIES : CLEAR_INOBJECT_PROPERTIES;
if (holder->HasFastProperties()) {
JSObject::NormalizeProperties(Handle<JSObject>::cast(holder), mode, 0,
"DeletingProperty");
ReloadPropertyInformation<false>();
}
// TODO(verwaest): Get rid of the name_ argument.
JSReceiver::DeleteNormalizedProperty(holder, name_, number_);
if (holder->IsJSObject()) {
JSObject::ReoptimizeIfPrototype(Handle<JSObject>::cast(holder));
}
}
state_ = NOT_FOUND;
}
void LookupIterator::TransitionToAccessorProperty(
Handle<Object> getter, Handle<Object> setter,
PropertyAttributes attributes) {
DCHECK(!getter->IsNull(isolate_) || !setter->IsNull(isolate_));
// Can only be called when the receiver is a JSObject. JSProxy has to be
// handled via a trap. Adding properties to primitive values is not
// observable.
Handle<JSObject> receiver = GetStoreTarget();
if (!IsElement() && !receiver->map()->is_dictionary_map()) {
Handle<Map> old_map(receiver->map(), isolate_);
if (!holder_.is_identical_to(receiver)) {
holder_ = receiver;
state_ = NOT_FOUND;
} else if (state_ == INTERCEPTOR) {
LookupInRegularHolder<false>(*old_map, *holder_);
}
int descriptor =
IsFound() ? static_cast<int>(number_) : DescriptorArray::kNotFound;
Handle<Map> new_map = Map::TransitionToAccessorProperty(
isolate_, old_map, name_, descriptor, getter, setter, attributes);
bool simple_transition = new_map->GetBackPointer() == receiver->map();
JSObject::MigrateToMap(receiver, new_map);
if (simple_transition) {
int number = new_map->LastAdded();
number_ = static_cast<uint32_t>(number);
property_details_ = new_map->GetLastDescriptorDetails();
state_ = ACCESSOR;
return;
}
ReloadPropertyInformation<false>();
if (!new_map->is_dictionary_map()) return;
}
Handle<AccessorPair> pair;
if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
pair = Handle<AccessorPair>::cast(GetAccessors());
// If the component and attributes are identical, nothing has to be done.
if (pair->Equals(*getter, *setter)) {
if (property_details().attributes() == attributes) {
if (!IsElement()) JSObject::ReoptimizeIfPrototype(receiver);
return;
}
} else {
pair = AccessorPair::Copy(pair);
pair->SetComponents(*getter, *setter);
}
} else {
pair = factory()->NewAccessorPair();
pair->SetComponents(*getter, *setter);
}
TransitionToAccessorPair(pair, attributes);
#if VERIFY_HEAP
if (FLAG_verify_heap) {
receiver->JSObjectVerify();
}
#endif
}
void LookupIterator::TransitionToAccessorPair(Handle<Object> pair,
PropertyAttributes attributes) {
Handle<JSObject> receiver = GetStoreTarget();
holder_ = receiver;
PropertyDetails details(attributes, ACCESSOR_CONSTANT, 0,
PropertyCellType::kMutable);
if (IsElement()) {
// TODO(verwaest): Move code into the element accessor.
Handle<SeededNumberDictionary> dictionary =
JSObject::NormalizeElements(receiver);
// We unconditionally pass used_as_prototype=false here because the call
// to RequireSlowElements takes care of the required IC clearing and
// we don't want to walk the heap twice.
dictionary =
SeededNumberDictionary::Set(dictionary, index_, pair, details, false);
receiver->RequireSlowElements(*dictionary);
if (receiver->HasSlowArgumentsElements()) {
FixedArray* parameter_map = FixedArray::cast(receiver->elements());
uint32_t length = parameter_map->length() - 2;
if (number_ < length) {
parameter_map->set(number_ + 2, heap()->the_hole_value());
}
FixedArray::cast(receiver->elements())->set(1, *dictionary);
} else {
receiver->set_elements(*dictionary);
}
ReloadPropertyInformation<true>();
} else {
PropertyNormalizationMode mode = receiver->map()->is_prototype_map()
? KEEP_INOBJECT_PROPERTIES
: CLEAR_INOBJECT_PROPERTIES;
// Normalize object to make this operation simple.
JSObject::NormalizeProperties(receiver, mode, 0,
"TransitionToAccessorPair");
JSObject::SetNormalizedProperty(receiver, name_, pair, details);
JSObject::ReoptimizeIfPrototype(receiver);
ReloadPropertyInformation<false>();
}
}
bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
// Optimization that only works if configuration_ is not mutable.
if (!check_prototype_chain()) return true;
DisallowHeapAllocation no_gc;
if (*receiver_ == *holder_) return true;
if (!receiver_->IsJSReceiver()) return false;
JSReceiver* current = JSReceiver::cast(*receiver_);
JSReceiver* object = *holder_;
if (!current->map()->has_hidden_prototype()) return false;
// JSProxy do not occur as hidden prototypes.
if (object->IsJSProxy()) return false;
PrototypeIterator iter(isolate(), current, kStartAtPrototype,
PrototypeIterator::END_AT_NON_HIDDEN);
while (!iter.IsAtEnd()) {
if (iter.GetCurrent<JSReceiver>() == object) return true;
iter.Advance();
}
return false;
}
Handle<Object> LookupIterator::FetchValue() const {
Object* result = NULL;
if (IsElement()) {
Handle<JSObject> holder = GetHolder<JSObject>();
ElementsAccessor* accessor = holder->GetElementsAccessor();
return accessor->Get(holder, number_);
} else if (holder_->IsJSGlobalObject()) {
Handle<JSObject> holder = GetHolder<JSObject>();
result = holder->global_dictionary()->ValueAt(number_);
DCHECK(result->IsPropertyCell());
result = PropertyCell::cast(result)->value();
} else if (!holder_->HasFastProperties()) {
result = holder_->property_dictionary()->ValueAt(number_);
} else if (property_details_.type() == v8::internal::DATA) {
Handle<JSObject> holder = GetHolder<JSObject>();
FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_);
return JSObject::FastPropertyAt(holder, property_details_.representation(),
field_index);
} else {
result = holder_->map()->instance_descriptors()->GetValue(number_);
}
return handle(result, isolate_);
}
int LookupIterator::GetAccessorIndex() const {
DCHECK(has_property_);
DCHECK(holder_->HasFastProperties());
DCHECK_EQ(v8::internal::ACCESSOR_CONSTANT, property_details_.type());
return descriptor_number();
}
int LookupIterator::GetConstantIndex() const {
DCHECK(has_property_);
DCHECK(holder_->HasFastProperties());
DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
DCHECK(!IsElement());
return descriptor_number();
}
FieldIndex LookupIterator::GetFieldIndex() const {
DCHECK(has_property_);
DCHECK(holder_->HasFastProperties());
DCHECK_EQ(v8::internal::DATA, property_details_.type());
DCHECK(!IsElement());
Map* holder_map = holder_->map();
int index =
holder_map->instance_descriptors()->GetFieldIndex(descriptor_number());
bool is_double = representation().IsDouble();
return FieldIndex::ForPropertyIndex(holder_map, index, is_double);
}
Handle<FieldType> LookupIterator::GetFieldType() const {
DCHECK(has_property_);
DCHECK(holder_->HasFastProperties());
DCHECK_EQ(v8::internal::DATA, property_details_.type());
return handle(
holder_->map()->instance_descriptors()->GetFieldType(descriptor_number()),
isolate_);
}
Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
DCHECK(!IsElement());
Handle<JSGlobalObject> holder = GetHolder<JSGlobalObject>();
Object* value = holder->global_dictionary()->ValueAt(dictionary_entry());
DCHECK(value->IsPropertyCell());
return handle(PropertyCell::cast(value), isolate_);
}
Handle<Object> LookupIterator::GetAccessors() const {
DCHECK_EQ(ACCESSOR, state_);
return FetchValue();
}
Handle<Object> LookupIterator::GetDataValue() const {
DCHECK_EQ(DATA, state_);
Handle<Object> value = FetchValue();
return value;
}
void LookupIterator::WriteDataValue(Handle<Object> value) {
DCHECK_EQ(DATA, state_);
Handle<JSReceiver> holder = GetHolder<JSReceiver>();
if (IsElement()) {
Handle<JSObject> object = Handle<JSObject>::cast(holder);
ElementsAccessor* accessor = object->GetElementsAccessor();
accessor->Set(object, number_, *value);
} else if (holder->HasFastProperties()) {
if (property_details_.type() == v8::internal::DATA) {
JSObject::cast(*holder)->WriteToField(descriptor_number(),
property_details_, *value);
} else {
DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
}
} else if (holder->IsJSGlobalObject()) {
GlobalDictionary* dictionary = JSObject::cast(*holder)->global_dictionary();
Object* cell = dictionary->ValueAt(dictionary_entry());
DCHECK(cell->IsPropertyCell());
PropertyCell::cast(cell)->set_value(*value);
} else {
NameDictionary* dictionary = holder->property_dictionary();
dictionary->ValueAtPut(dictionary_entry(), *value);
}
}
template <bool is_element>
bool LookupIterator::SkipInterceptor(JSObject* holder) {
auto info = GetInterceptor<is_element>(holder);
// TODO(dcarney): check for symbol/can_intercept_symbols here as well.
if (info->non_masking()) {
switch (interceptor_state_) {
case InterceptorState::kUninitialized:
interceptor_state_ = InterceptorState::kSkipNonMasking;
// Fall through.
case InterceptorState::kSkipNonMasking:
return true;
case InterceptorState::kProcessNonMasking:
return false;
}
}
return interceptor_state_ == InterceptorState::kProcessNonMasking;
}
JSReceiver* LookupIterator::NextHolder(Map* map) {
DisallowHeapAllocation no_gc;
if (map->prototype() == heap()->null_value()) return NULL;
if (!check_prototype_chain() && !map->has_hidden_prototype()) return NULL;
return JSReceiver::cast(map->prototype());
}
LookupIterator::State LookupIterator::NotFound(JSReceiver* const holder) const {
DCHECK(!IsElement());
if (!holder->IsJSTypedArray() || !name_->IsString()) return NOT_FOUND;
Handle<String> name_string = Handle<String>::cast(name_);
if (name_string->length() == 0) return NOT_FOUND;
return IsSpecialIndex(isolate_->unicode_cache(), *name_string)
? INTEGER_INDEXED_EXOTIC
: NOT_FOUND;
}
namespace {
template <bool is_element>
bool HasInterceptor(Map* map) {
return is_element ? map->has_indexed_interceptor()
: map->has_named_interceptor();
}
} // namespace
template <bool is_element>
LookupIterator::State LookupIterator::LookupInSpecialHolder(
Map* const map, JSReceiver* const holder) {
STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY);
switch (state_) {
case NOT_FOUND:
if (map->IsJSProxyMap()) {
if (is_element || !name_->IsPrivate()) return JSPROXY;
}
if (map->is_access_check_needed()) {
if (is_element || !name_->IsPrivate()) return ACCESS_CHECK;
}
// Fall through.
case ACCESS_CHECK:
if (check_interceptor() && HasInterceptor<is_element>(map) &&
!SkipInterceptor<is_element>(JSObject::cast(holder))) {
if (is_element || !name_->IsPrivate()) return INTERCEPTOR;
}
// Fall through.
case INTERCEPTOR:
if (!is_element && map->IsJSGlobalObjectMap()) {
GlobalDictionary* dict = JSObject::cast(holder)->global_dictionary();
int number = dict->FindEntry(name_);
if (number == GlobalDictionary::kNotFound) return NOT_FOUND;
number_ = static_cast<uint32_t>(number);
DCHECK(dict->ValueAt(number_)->IsPropertyCell());
PropertyCell* cell = PropertyCell::cast(dict->ValueAt(number_));
if (cell->value()->IsTheHole(isolate_)) return NOT_FOUND;
property_details_ = cell->property_details();
has_property_ = true;
switch (property_details_.kind()) {
case v8::internal::kData:
return DATA;
case v8::internal::kAccessor:
return ACCESSOR;
}
}
return LookupInRegularHolder<is_element>(map, holder);
case ACCESSOR:
case DATA:
return NOT_FOUND;
case INTEGER_INDEXED_EXOTIC:
case JSPROXY:
case TRANSITION:
UNREACHABLE();
}
UNREACHABLE();
return NOT_FOUND;
}
template <bool is_element>
LookupIterator::State LookupIterator::LookupInRegularHolder(
Map* const map, JSReceiver* const holder) {
DisallowHeapAllocation no_gc;
if (interceptor_state_ == InterceptorState::kProcessNonMasking) {
return NOT_FOUND;
}
if (is_element) {
JSObject* js_object = JSObject::cast(holder);
ElementsAccessor* accessor = js_object->GetElementsAccessor();
FixedArrayBase* backing_store = js_object->elements();
number_ = accessor->GetEntryForIndex(js_object, backing_store, index_);
if (number_ == kMaxUInt32) {
return holder->IsJSTypedArray() ? INTEGER_INDEXED_EXOTIC : NOT_FOUND;
}
property_details_ = accessor->GetDetails(js_object, number_);
} else if (!map->is_dictionary_map()) {
DescriptorArray* descriptors = map->instance_descriptors();
int number = descriptors->SearchWithCache(isolate_, *name_, map);
if (number == DescriptorArray::kNotFound) return NotFound(holder);
number_ = static_cast<uint32_t>(number);
property_details_ = descriptors->GetDetails(number_);
} else {
NameDictionary* dict = holder->property_dictionary();
int number = dict->FindEntry(name_);
if (number == NameDictionary::kNotFound) return NotFound(holder);
number_ = static_cast<uint32_t>(number);
property_details_ = dict->DetailsAt(number_);
}
has_property_ = true;
switch (property_details_.kind()) {
case v8::internal::kData:
return DATA;
case v8::internal::kAccessor:
return ACCESSOR;
}
UNREACHABLE();
return state_;
}
Handle<InterceptorInfo> LookupIterator::GetInterceptorForFailedAccessCheck()
const {
DCHECK_EQ(ACCESS_CHECK, state_);
DisallowHeapAllocation no_gc;
AccessCheckInfo* access_check_info =
AccessCheckInfo::Get(isolate_, Handle<JSObject>::cast(holder_));
if (access_check_info) {
Object* interceptor = IsElement() ? access_check_info->indexed_interceptor()
: access_check_info->named_interceptor();
if (interceptor) {
return handle(InterceptorInfo::cast(interceptor), isolate_);
}
}
return Handle<InterceptorInfo>();
}
} // namespace internal
} // namespace v8