Flatten property_kind into state. Add UNKNOWN as a state for dict-mode receivers

BUG=
R=yangguo@chromium.org

Review URL: https://codereview.chromium.org/540903002

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@23692 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
verwaest@chromium.org 2014-09-04 12:28:13 +00:00
parent 0baf275e20
commit 3ef094402e
18 changed files with 276 additions and 329 deletions

View File

@ -181,7 +181,7 @@ void Accessors::ArgumentsIteratorSetter(
if (SetPropertyOnInstanceIfInherited(isolate, info, name, value)) return;
LookupIterator it(object, Utils::OpenHandle(*name));
CHECK(it.HasProperty());
CHECK_EQ(LookupIterator::ACCESSOR, it.state());
DCHECK(it.HolderIsReceiverOrHiddenPrototype());
Object::SetDataProperty(&it, value);
}

View File

@ -2488,7 +2488,7 @@ void Genesis::TransferNamedProperties(Handle<JSObject> from,
LookupIterator it(to, key, LookupIterator::OWN_SKIP_INTERCEPTOR);
CHECK_NE(LookupIterator::ACCESS_CHECK, it.state());
// If the property is already there we skip it
if (it.IsFound() && it.HasProperty()) continue;
if (it.IsFound()) continue;
HandleScope inner(isolate());
DCHECK(!to->HasFastProperties());
// Add to dictionary.
@ -2516,7 +2516,7 @@ void Genesis::TransferNamedProperties(Handle<JSObject> from,
Handle<Name> key(Name::cast(raw_key));
LookupIterator it(to, key, LookupIterator::OWN_SKIP_INTERCEPTOR);
CHECK_NE(LookupIterator::ACCESS_CHECK, it.state());
if (it.IsFound() && it.HasProperty()) continue;
if (it.IsFound()) continue;
// Set the property.
Handle<Object> value = Handle<Object>(properties->ValueAt(i),
isolate());

View File

@ -5297,21 +5297,17 @@ HOptimizedGraphBuilder::LookupGlobalProperty(Variable* var, LookupIterator* it,
}
switch (it->state()) {
case LookupIterator::ACCESSOR:
case LookupIterator::ACCESS_CHECK:
case LookupIterator::INTERCEPTOR:
case LookupIterator::NOT_FOUND:
return kUseGeneric;
case LookupIterator::PROPERTY:
if (!it->HasProperty()) return kUseGeneric;
switch (it->property_kind()) {
case LookupIterator::DATA:
if (access_type == STORE && it->IsReadOnly()) return kUseGeneric;
return kUseCell;
case LookupIterator::ACCESSOR:
return kUseGeneric;
}
case LookupIterator::DATA:
if (access_type == STORE && it->IsReadOnly()) return kUseGeneric;
return kUseCell;
case LookupIterator::JSPROXY:
case LookupIterator::TRANSITION:
case LookupIterator::UNKNOWN:
UNREACHABLE();
}
UNREACHABLE();

View File

@ -709,8 +709,7 @@ void NamedLoadHandlerCompiler::GenerateLoadInterceptorWithFollowup(
!holder().is_identical_to(it->GetHolder<JSObject>());
bool must_preserve_receiver_reg =
!receiver().is(holder_reg) &&
(it->property_kind() == LookupIterator::ACCESSOR ||
must_perform_prototype_check);
(it->state() == LookupIterator::ACCESSOR || must_perform_prototype_check);
// Save necessary data before invoking an interceptor.
// Requires a frame to make GC aware of pushed pointers.

View File

@ -760,8 +760,7 @@ void NamedLoadHandlerCompiler::GenerateLoadInterceptorWithFollowup(
!holder().is_identical_to(it->GetHolder<JSObject>());
bool must_preserve_receiver_reg =
!receiver().is(holder_reg) &&
(it->property_kind() == LookupIterator::ACCESSOR ||
must_perform_prototype_check);
(it->state() == LookupIterator::ACCESSOR || must_perform_prototype_check);
// Save necessary data before invoking an interceptor.
// Requires a frame to make GC aware of pushed pointers.

View File

@ -225,22 +225,28 @@ Handle<Code> NamedLoadHandlerCompiler::CompileLoadInterceptor(
// So far the most popular follow ups for interceptor loads are FIELD and
// ExecutableAccessorInfo, so inline only them. Other cases may be added
// later.
bool inline_followup = it->state() == LookupIterator::PROPERTY;
if (inline_followup) {
switch (it->property_kind()) {
case LookupIterator::DATA:
inline_followup = it->property_details().type() == FIELD;
break;
case LookupIterator::ACCESSOR: {
Handle<Object> accessors = it->GetAccessors();
inline_followup = accessors->IsExecutableAccessorInfo();
if (!inline_followup) break;
Handle<ExecutableAccessorInfo> info =
Handle<ExecutableAccessorInfo>::cast(accessors);
inline_followup = info->getter() != NULL &&
ExecutableAccessorInfo::IsCompatibleReceiverType(
isolate(), info, type());
}
bool inline_followup = false;
switch (it->state()) {
case LookupIterator::TRANSITION:
case LookupIterator::UNKNOWN:
UNREACHABLE();
case LookupIterator::ACCESS_CHECK:
case LookupIterator::INTERCEPTOR:
case LookupIterator::JSPROXY:
case LookupIterator::NOT_FOUND:
break;
case LookupIterator::DATA:
inline_followup = it->property_details().type() == FIELD;
break;
case LookupIterator::ACCESSOR: {
Handle<Object> accessors = it->GetAccessors();
inline_followup = accessors->IsExecutableAccessorInfo();
if (!inline_followup) break;
Handle<ExecutableAccessorInfo> info =
Handle<ExecutableAccessorInfo>::cast(accessors);
inline_followup = info->getter() != NULL &&
ExecutableAccessorInfo::IsCompatibleReceiverType(
isolate(), info, type());
}
}
@ -264,7 +270,14 @@ void NamedLoadHandlerCompiler::GenerateLoadPostInterceptor(
set_holder(real_named_property_holder);
Register reg = Frontend(interceptor_reg, it->name());
switch (it->property_kind()) {
switch (it->state()) {
case LookupIterator::ACCESS_CHECK:
case LookupIterator::INTERCEPTOR:
case LookupIterator::JSPROXY:
case LookupIterator::NOT_FOUND:
case LookupIterator::TRANSITION:
case LookupIterator::UNKNOWN:
UNREACHABLE();
case LookupIterator::DATA: {
DCHECK_EQ(FIELD, it->property_details().type());
__ Move(receiver(), reg);

View File

@ -699,8 +699,7 @@ void NamedLoadHandlerCompiler::GenerateLoadInterceptorWithFollowup(
!holder().is_identical_to(it->GetHolder<JSObject>());
bool must_preserve_receiver_reg =
!receiver().is(holder_reg) &&
(it->property_kind() == LookupIterator::ACCESSOR ||
must_perform_prototype_check);
(it->state() == LookupIterator::ACCESSOR || must_perform_prototype_check);
// Save necessary data before invoking an interceptor.
// Requires a frame to make GC aware of pushed pointers.

View File

@ -215,6 +215,7 @@ static void LookupForRead(LookupIterator* it) {
switch (it->state()) {
case LookupIterator::NOT_FOUND:
case LookupIterator::TRANSITION:
case LookupIterator::UNKNOWN:
UNREACHABLE();
case LookupIterator::JSPROXY:
return;
@ -234,9 +235,9 @@ static void LookupForRead(LookupIterator* it) {
break;
}
return;
case LookupIterator::PROPERTY:
if (it->HasProperty()) return; // Yay!
break;
case LookupIterator::ACCESSOR:
case LookupIterator::DATA:
return;
}
}
}
@ -285,7 +286,7 @@ bool IC::TryRemoveInvalidPrototypeDependentStub(Handle<Object> receiver,
Handle<GlobalObject> global = Handle<GlobalObject>::cast(receiver);
LookupIterator it(global, name, LookupIterator::OWN_SKIP_INTERCEPTOR);
if (it.state() == LookupIterator::ACCESS_CHECK) return false;
if (!it.IsFound() || !it.HasProperty()) return false;
if (!it.IsFound()) return false;
Handle<PropertyCell> cell = it.GetPropertyCell();
return cell->type()->IsConstant();
}
@ -976,8 +977,7 @@ Handle<Code> LoadIC::CompileHandler(LookupIterator* lookup,
}
// -------------- Accessors --------------
DCHECK(lookup->state() == LookupIterator::PROPERTY);
if (lookup->property_kind() == LookupIterator::ACCESSOR) {
if (lookup->state() == LookupIterator::ACCESSOR) {
// Use simple field loads for some well-known callback properties.
if (receiver_is_holder) {
DCHECK(receiver->IsJSObject());
@ -1032,7 +1032,7 @@ Handle<Code> LoadIC::CompileHandler(LookupIterator* lookup,
}
// -------------- Dictionary properties --------------
DCHECK(lookup->property_kind() == LookupIterator::DATA);
DCHECK(lookup->state() == LookupIterator::DATA);
if (lookup->property_encoding() == LookupIterator::DICTIONARY) {
if (kind() != Code::LOAD_IC) return slow_stub();
if (holder->IsGlobalObject()) {
@ -1223,6 +1223,7 @@ bool StoreIC::LookupForWrite(LookupIterator* it, Handle<Object> value,
switch (it->state()) {
case LookupIterator::NOT_FOUND:
case LookupIterator::TRANSITION:
case LookupIterator::UNKNOWN:
UNREACHABLE();
case LookupIterator::JSPROXY:
return false;
@ -1240,11 +1241,12 @@ bool StoreIC::LookupForWrite(LookupIterator* it, Handle<Object> value,
case LookupIterator::ACCESS_CHECK:
if (it->GetHolder<JSObject>()->IsAccessCheckNeeded()) return false;
break;
case LookupIterator::PROPERTY:
if (!it->HasProperty()) break;
case LookupIterator::ACCESSOR:
return !it->IsReadOnly();
case LookupIterator::DATA: {
if (it->IsReadOnly()) return false;
if (it->property_kind() == LookupIterator::ACCESSOR) return true;
if (it->GetHolder<Object>().is_identical_to(receiver)) {
Handle<JSObject> holder = it->GetHolder<JSObject>();
if (receiver.is_identical_to(holder)) {
it->PrepareForDataProperty(value);
// The previous receiver map might just have been deprecated,
// so reload it.
@ -1253,14 +1255,15 @@ bool StoreIC::LookupForWrite(LookupIterator* it, Handle<Object> value,
}
// Receiver != holder.
PrototypeIterator iter(it->isolate(), receiver);
if (receiver->IsJSGlobalProxy()) {
PrototypeIterator iter(it->isolate(), receiver);
return it->GetHolder<Object>().is_identical_to(
PrototypeIterator::GetCurrent(iter));
}
it->PrepareTransitionToDataProperty(value, NONE, store_mode);
return it->IsCacheableTransition();
}
}
}
@ -1417,8 +1420,7 @@ Handle<Code> StoreIC::CompileHandler(LookupIterator* lookup,
}
// -------------- Accessors --------------
DCHECK(lookup->state() == LookupIterator::PROPERTY);
if (lookup->property_kind() == LookupIterator::ACCESSOR) {
if (lookup->state() == LookupIterator::ACCESSOR) {
if (!holder->HasFastProperties()) return slow_stub();
Handle<Object> accessors = lookup->GetAccessors();
if (accessors->IsExecutableAccessorInfo()) {
@ -1452,7 +1454,7 @@ Handle<Code> StoreIC::CompileHandler(LookupIterator* lookup,
}
// -------------- Dictionary properties --------------
DCHECK(lookup->property_kind() == LookupIterator::DATA);
DCHECK(lookup->state() == LookupIterator::DATA);
if (lookup->property_encoding() == LookupIterator::DICTIONARY) {
if (holder->IsGlobalObject()) {
Handle<PropertyCell> cell = lookup->GetPropertyCell();

View File

@ -710,8 +710,7 @@ void NamedLoadHandlerCompiler::GenerateLoadInterceptorWithFollowup(
!holder().is_identical_to(it->GetHolder<JSObject>());
bool must_preserve_receiver_reg =
!receiver().is(holder_reg) &&
(it->property_kind() == LookupIterator::ACCESSOR ||
must_perform_prototype_check);
(it->state() == LookupIterator::ACCESSOR || must_perform_prototype_check);
// Save necessary data before invoking an interceptor.
// Requires a frame to make GC aware of pushed pointers.

View File

@ -710,8 +710,7 @@ void NamedLoadHandlerCompiler::GenerateLoadInterceptorWithFollowup(
!holder().is_identical_to(it->GetHolder<JSObject>());
bool must_preserve_receiver_reg =
!receiver().is(holder_reg) &&
(it->property_kind() == LookupIterator::ACCESSOR ||
must_perform_prototype_check);
(it->state() == LookupIterator::ACCESSOR || must_perform_prototype_check);
// Save necessary data before invoking an interceptor.
// Requires a frame to make GC aware of pushed pointers.

View File

@ -694,8 +694,7 @@ void NamedLoadHandlerCompiler::GenerateLoadInterceptorWithFollowup(
!holder().is_identical_to(it->GetHolder<JSObject>());
bool must_preserve_receiver_reg =
!receiver().is(holder_reg) &&
(it->property_kind() == LookupIterator::ACCESSOR ||
must_perform_prototype_check);
(it->state() == LookupIterator::ACCESSOR || must_perform_prototype_check);
// Save necessary data before invoking an interceptor.
// Requires a frame to make GC aware of pushed pointers.

View File

@ -747,8 +747,7 @@ void NamedLoadHandlerCompiler::GenerateLoadInterceptorWithFollowup(
!holder().is_identical_to(it->GetHolder<JSObject>());
bool must_preserve_receiver_reg =
!receiver().is(holder_reg) &&
(it->property_kind() == LookupIterator::ACCESSOR ||
must_perform_prototype_check);
(it->state() == LookupIterator::ACCESSOR || must_perform_prototype_check);
// Save necessary data before invoking an interceptor.
// Requires a frame to make GC aware of pushed pointers.

View File

@ -31,7 +31,8 @@ JSReceiver* LookupIterator::NextHolder(Map* map) {
}
LookupIterator::State LookupIterator::LookupInHolder(Map* map) {
LookupIterator::State LookupIterator::LookupInHolder(Map* map,
JSReceiver* holder) {
STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY);
DisallowHeapAllocation no_gc;
switch (state_) {
@ -47,14 +48,35 @@ LookupIterator::State LookupIterator::LookupInHolder(Map* map) {
case INTERCEPTOR:
if (map->is_dictionary_map()) {
property_encoding_ = DICTIONARY;
if (holder == NULL) return UNKNOWN;
NameDictionary* dict = JSObject::cast(holder)->property_dictionary();
number_ = dict->FindEntry(name_);
if (number_ == NameDictionary::kNotFound) return NOT_FOUND;
property_details_ = dict->DetailsAt(number_);
if (holder->IsGlobalObject()) {
if (property_details_.IsDeleted()) return NOT_FOUND;
PropertyCell* cell = PropertyCell::cast(dict->ValueAt(number_));
if (cell->value()->IsTheHole()) return NOT_FOUND;
}
} else {
DescriptorArray* descriptors = map->instance_descriptors();
number_ = descriptors->SearchWithCache(*name_, map);
if (number_ == DescriptorArray::kNotFound) return NOT_FOUND;
property_encoding_ = DESCRIPTOR;
property_details_ = descriptors->GetDetails(number_);
}
return PROPERTY;
case PROPERTY:
has_property_ = true;
switch (property_details_.type()) {
case v8::internal::CONSTANT:
case v8::internal::FIELD:
case v8::internal::NORMAL:
return DATA;
case v8::internal::CALLBACKS:
return ACCESSOR;
}
case ACCESSOR:
case DATA:
case UNKNOWN:
return NOT_FOUND;
case JSPROXY:
case TRANSITION:

View File

@ -14,25 +14,28 @@ namespace internal {
void LookupIterator::Next() {
DCHECK_NE(JSPROXY, state_);
DCHECK_NE(TRANSITION, state_);
DisallowHeapAllocation no_gc;
has_property_ = false;
JSReceiver* holder = NULL;
JSReceiver* holder =
maybe_holder_.is_null() ? NULL : *maybe_holder_.ToHandleChecked();
Map* map = *holder_map_;
// Perform lookup on current holder.
state_ = LookupInHolder(map);
state_ = LookupInHolder(map, holder);
if (IsFound()) return;
// Continue lookup if lookup on current holder failed.
while (!IsFound()) {
do {
JSReceiver* maybe_holder = NextHolder(map);
if (maybe_holder == NULL) break;
holder = maybe_holder;
map = holder->map();
state_ = LookupInHolder(map);
}
state_ = LookupInHolder(map, holder);
} while (!IsFound());
// Either was found in the receiver, or the receiver has no prototype.
if (holder == NULL) return;
maybe_holder_ = handle(holder, isolate_);
@ -81,58 +84,15 @@ bool LookupIterator::HasAccess(v8::AccessType access_type) const {
}
bool LookupIterator::HasProperty() {
DCHECK_EQ(PROPERTY, state_);
DCHECK(is_guaranteed_to_have_holder());
if (property_encoding_ == DICTIONARY) {
Handle<JSObject> holder = GetHolder<JSObject>();
number_ = holder->property_dictionary()->FindEntry(name_);
if (number_ == NameDictionary::kNotFound) return false;
property_details_ = holder->property_dictionary()->DetailsAt(number_);
// Holes in dictionary cells are absent values.
if (holder->IsGlobalObject() &&
(property_details_.IsDeleted() || FetchValue()->IsTheHole())) {
return false;
}
} else {
// Can't use descriptor_number() yet because has_property_ is still false.
property_details_ =
holder_map_->instance_descriptors()->GetDetails(number_);
}
LoadPropertyKind();
has_property_ = true;
return true;
}
void LookupIterator::LoadPropertyKind() {
switch (property_details_.type()) {
case v8::internal::FIELD:
case v8::internal::NORMAL:
case v8::internal::CONSTANT:
property_kind_ = DATA;
break;
case v8::internal::CALLBACKS:
property_kind_ = ACCESSOR;
break;
}
}
void LookupIterator::ReloadPropertyInformation() {
state_ = BEFORE_PROPERTY;
state_ = LookupInHolder(*holder_map_);
DCHECK(IsFound());
HasProperty();
state_ = LookupInHolder(*holder_map_, *maybe_holder_.ToHandleChecked());
DCHECK(IsFound() || holder_map_->is_dictionary_map());
}
void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
DCHECK(has_property_);
DCHECK(state_ == DATA || state_ == ACCESSOR);
DCHECK(HolderIsReceiverOrHiddenPrototype());
if (property_encoding_ == DICTIONARY) return;
holder_map_ =
@ -144,7 +104,7 @@ void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
PropertyAttributes attributes) {
DCHECK(has_property_);
DCHECK(state_ == DATA || state_ == ACCESSOR);
DCHECK(HolderIsReceiverOrHiddenPrototype());
Handle<JSObject> holder = GetHolder<JSObject>();
if (property_encoding_ != DICTIONARY) {
@ -166,9 +126,9 @@ void LookupIterator::PrepareTransitionToDataProperty(
Handle<Object> value, PropertyAttributes attributes,
Object::StoreFromKeyed store_mode) {
if (state_ == TRANSITION) return;
DCHECK(!has_property_ || property_kind_ != ACCESSOR);
DCHECK(!(has_property_ || state_ == JSPROXY) ||
!HolderIsReceiverOrHiddenPrototype());
DCHECK(state_ != LookupIterator::ACCESSOR ||
GetAccessors()->IsDeclaredAccessorInfo());
DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
// 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
@ -224,8 +184,7 @@ void LookupIterator::TransitionToAccessorProperty(
// Install the accessor into the dictionary-mode object.
PropertyDetails details(attributes, CALLBACKS, 0);
Handle<AccessorPair> pair;
if (IsFound() && HasProperty() && property_kind() == ACCESSOR &&
GetAccessors()->IsAccessorPair()) {
if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
pair = Handle<AccessorPair>::cast(GetAccessors());
// If the component and attributes are identical, nothing has to be done.
if (pair->get(component) == *accessor) {
@ -331,15 +290,13 @@ Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
Handle<Object> LookupIterator::GetAccessors() const {
DCHECK(has_property_);
DCHECK_EQ(ACCESSOR, property_kind_);
DCHECK_EQ(ACCESSOR, state_);
return FetchValue();
}
Handle<Object> LookupIterator::GetDataValue() const {
DCHECK(has_property_);
DCHECK_EQ(DATA, property_kind_);
DCHECK_EQ(DATA, state_);
Handle<Object> value = FetchValue();
return value;
}
@ -347,7 +304,7 @@ Handle<Object> LookupIterator::GetDataValue() const {
void LookupIterator::WriteDataValue(Handle<Object> value) {
DCHECK(is_guaranteed_to_have_holder());
DCHECK(has_property_);
DCHECK_EQ(DATA, state_);
Handle<JSObject> holder = GetHolder<JSObject>();
if (property_encoding_ == DICTIONARY) {
NameDictionary* property_dictionary = holder->property_dictionary();

View File

@ -34,18 +34,15 @@ class LookupIterator FINAL BASE_EMBEDDED {
INTERCEPTOR,
JSPROXY,
NOT_FOUND,
PROPERTY,
UNKNOWN, // Dictionary-mode holder map without a holder.
ACCESSOR,
DATA,
TRANSITION,
// Set state_ to BEFORE_PROPERTY to ensure that the next lookup will be a
// PROPERTY lookup.
BEFORE_PROPERTY = INTERCEPTOR
};
enum PropertyKind {
DATA,
ACCESSOR
};
enum PropertyEncoding {
DICTIONARY,
DESCRIPTOR
@ -55,7 +52,6 @@ class LookupIterator FINAL BASE_EMBEDDED {
Configuration configuration = PROTOTYPE_CHAIN)
: configuration_(ComputeConfiguration(configuration, name)),
state_(NOT_FOUND),
property_kind_(DATA),
property_encoding_(DESCRIPTOR),
property_details_(NONE, NORMAL, Representation::None()),
isolate_(name->GetIsolate()),
@ -73,7 +69,6 @@ class LookupIterator FINAL BASE_EMBEDDED {
Configuration configuration = PROTOTYPE_CHAIN)
: configuration_(ComputeConfiguration(configuration, name)),
state_(NOT_FOUND),
property_kind_(DATA),
property_encoding_(DESCRIPTOR),
property_details_(NONE, NORMAL, Representation::None()),
isolate_(name->GetIsolate()),
@ -118,10 +113,6 @@ class LookupIterator FINAL BASE_EMBEDDED {
bool HasAccess(v8::AccessType access_type) const;
/* PROPERTY */
// HasProperty needs to be called before any of the other PROPERTY methods
// below can be used. It ensures that we are able to provide a definite
// answer, and loads extra information about the property.
bool HasProperty();
void PrepareForDataProperty(Handle<Object> value);
void PrepareTransitionToDataProperty(Handle<Object> value,
PropertyAttributes attributes,
@ -131,7 +122,6 @@ class LookupIterator FINAL BASE_EMBEDDED {
state_ == TRANSITION && transition_map()->GetBackPointer()->IsMap();
if (cacheable) {
property_details_ = transition_map_->GetLastDescriptorDetails();
LoadPropertyKind();
has_property_ = true;
}
return cacheable;
@ -142,10 +132,6 @@ class LookupIterator FINAL BASE_EMBEDDED {
void TransitionToAccessorProperty(AccessorComponent component,
Handle<Object> accessor,
PropertyAttributes attributes);
PropertyKind property_kind() const {
DCHECK(has_property_);
return property_kind_;
}
PropertyEncoding property_encoding() const {
DCHECK(has_property_);
return property_encoding_;
@ -173,10 +159,9 @@ class LookupIterator FINAL BASE_EMBEDDED {
Handle<Map> GetReceiverMap() const;
MUST_USE_RESULT inline JSReceiver* NextHolder(Map* map);
inline State LookupInHolder(Map* map);
inline State LookupInHolder(Map* map, JSReceiver* holder);
Handle<Object> FetchValue() const;
void ReloadPropertyInformation();
void LoadPropertyKind();
bool IsBootstrapping() const;
@ -219,7 +204,6 @@ class LookupIterator FINAL BASE_EMBEDDED {
Configuration configuration_;
State state_;
bool has_property_;
PropertyKind property_kind_;
PropertyEncoding property_encoding_;
PropertyDetails property_details_;
Isolate* isolate_;

View File

@ -110,6 +110,7 @@ MaybeHandle<Object> Object::GetProperty(LookupIterator* it) {
switch (it->state()) {
case LookupIterator::NOT_FOUND:
case LookupIterator::TRANSITION:
case LookupIterator::UNKNOWN:
UNREACHABLE();
case LookupIterator::JSPROXY:
return JSProxy::GetPropertyWithHandler(it->GetHolder<JSProxy>(),
@ -124,18 +125,12 @@ MaybeHandle<Object> Object::GetProperty(LookupIterator* it) {
case LookupIterator::ACCESS_CHECK:
if (it->HasAccess(v8::ACCESS_GET)) break;
return JSObject::GetPropertyWithFailedAccessCheck(it);
case LookupIterator::PROPERTY:
if (it->HasProperty()) {
switch (it->property_kind()) {
case LookupIterator::ACCESSOR:
return GetPropertyWithAccessor(it->GetReceiver(), it->name(),
it->GetHolder<JSObject>(),
it->GetAccessors());
case LookupIterator::DATA:
return it->GetDataValue();
}
}
break;
case LookupIterator::ACCESSOR:
return GetPropertyWithAccessor(it->GetReceiver(), it->name(),
it->GetHolder<JSObject>(),
it->GetAccessors());
case LookupIterator::DATA:
return it->GetDataValue();
}
}
return it->factory()->undefined_value();
@ -156,6 +151,7 @@ Handle<Object> JSObject::GetDataProperty(LookupIterator* it) {
case LookupIterator::INTERCEPTOR:
case LookupIterator::NOT_FOUND:
case LookupIterator::TRANSITION:
case LookupIterator::UNKNOWN:
UNREACHABLE();
case LookupIterator::ACCESS_CHECK:
if (it->HasAccess(v8::ACCESS_GET)) continue;
@ -163,18 +159,14 @@ Handle<Object> JSObject::GetDataProperty(LookupIterator* it) {
case LookupIterator::JSPROXY:
it->NotFound();
return it->isolate()->factory()->undefined_value();
case LookupIterator::PROPERTY:
if (!it->HasProperty()) continue;
switch (it->property_kind()) {
case LookupIterator::DATA:
return it->GetDataValue();
case LookupIterator::ACCESSOR:
// TODO(verwaest): For now this doesn't call into
// ExecutableAccessorInfo, since clients don't need it. Update once
// relevant.
it->NotFound();
return it->isolate()->factory()->undefined_value();
}
case LookupIterator::ACCESSOR:
// TODO(verwaest): For now this doesn't call into
// ExecutableAccessorInfo, since clients don't need it. Update once
// relevant.
it->NotFound();
return it->isolate()->factory()->undefined_value();
case LookupIterator::DATA:
return it->GetDataValue();
}
}
return it->isolate()->factory()->undefined_value();
@ -582,9 +574,7 @@ MaybeHandle<Object> Object::SetPropertyWithDefinedSetter(
static bool FindAllCanReadHolder(LookupIterator* it) {
for (; it->IsFound(); it->Next()) {
if (it->state() == LookupIterator::PROPERTY &&
it->HasProperty() &&
it->property_kind() == LookupIterator::ACCESSOR) {
if (it->state() == LookupIterator::ACCESSOR) {
Handle<Object> accessors = it->GetAccessors();
if (accessors->IsAccessorInfo()) {
if (AccessorInfo::cast(*accessors)->all_can_read()) return true;
@ -623,8 +613,7 @@ Maybe<PropertyAttributes> JSObject::GetPropertyAttributesWithFailedAccessCheck(
static bool FindAllCanWriteHolder(LookupIterator* it) {
for (; it->IsFound(); it->Next()) {
if (it->state() == LookupIterator::PROPERTY && it->HasProperty() &&
it->property_kind() == LookupIterator::ACCESSOR) {
if (it->state() == LookupIterator::ACCESSOR) {
Handle<Object> accessors = it->GetAccessors();
if (accessors->IsAccessorInfo()) {
if (AccessorInfo::cast(*accessors)->all_can_write()) return true;
@ -2830,6 +2819,7 @@ MaybeHandle<Object> Object::SetProperty(LookupIterator* it,
for (; it->IsFound(); it->Next()) {
switch (it->state()) {
case LookupIterator::NOT_FOUND:
case LookupIterator::UNKNOWN:
UNREACHABLE();
case LookupIterator::ACCESS_CHECK:
@ -2875,24 +2865,25 @@ MaybeHandle<Object> Object::SetProperty(LookupIterator* it,
}
break;
case LookupIterator::PROPERTY:
if (!it->HasProperty()) break;
case LookupIterator::ACCESSOR:
if (it->property_details().IsReadOnly()) {
return WriteToReadOnlyProperty(it, value, strict_mode);
}
switch (it->property_kind()) {
case LookupIterator::ACCESSOR:
if (it->HolderIsReceiverOrHiddenPrototype() ||
!it->GetAccessors()->IsDeclaredAccessorInfo()) {
return SetPropertyWithAccessor(it->GetReceiver(), it->name(),
value, it->GetHolder<JSObject>(),
it->GetAccessors(), strict_mode);
}
break;
case LookupIterator::DATA:
if (it->HolderIsReceiverOrHiddenPrototype()) {
return SetDataProperty(it, value);
}
if (it->HolderIsReceiverOrHiddenPrototype() ||
!it->GetAccessors()->IsDeclaredAccessorInfo()) {
return SetPropertyWithAccessor(it->GetReceiver(), it->name(), value,
it->GetHolder<JSObject>(),
it->GetAccessors(), strict_mode);
}
done = true;
break;
case LookupIterator::DATA:
if (it->property_details().IsReadOnly()) {
return WriteToReadOnlyProperty(it, value, strict_mode);
}
if (it->HolderIsReceiverOrHiddenPrototype()) {
return SetDataProperty(it, value);
}
done = true;
break;
@ -3825,6 +3816,7 @@ MaybeHandle<Object> JSObject::SetOwnPropertyIgnoreAttributes(
case LookupIterator::JSPROXY:
case LookupIterator::NOT_FOUND:
case LookupIterator::TRANSITION:
case LookupIterator::UNKNOWN:
UNREACHABLE();
case LookupIterator::ACCESS_CHECK:
@ -3833,86 +3825,67 @@ MaybeHandle<Object> JSObject::SetOwnPropertyIgnoreAttributes(
}
break;
case LookupIterator::PROPERTY: {
if (!it.HasProperty()) break;
case LookupIterator::ACCESSOR: {
PropertyDetails details = it.property_details();
Handle<Object> old_value = it.isolate()->factory()->the_hole_value();
switch (it.property_kind()) {
case LookupIterator::ACCESSOR: {
// Ensure the context isn't changed after calling into accessors.
AssertNoContextChange ncc(it.isolate());
// Ensure the context isn't changed after calling into accessors.
AssertNoContextChange ncc(it.isolate());
Handle<Object> accessors = it.GetAccessors();
Handle<Object> accessors = it.GetAccessors();
if (is_observed && accessors->IsAccessorInfo()) {
ASSIGN_RETURN_ON_EXCEPTION(
it.isolate(), old_value,
GetPropertyWithAccessor(it.GetReceiver(), it.name(),
it.GetHolder<JSObject>(), accessors),
Object);
}
if (is_observed && accessors->IsAccessorInfo()) {
ASSIGN_RETURN_ON_EXCEPTION(
it.isolate(), old_value,
GetPropertyWithAccessor(it.GetReceiver(), it.name(),
it.GetHolder<JSObject>(), accessors),
Object);
}
// Special handling for ExecutableAccessorInfo, which behaves like a
// data property.
if (handling == DONT_FORCE_FIELD &&
accessors->IsExecutableAccessorInfo()) {
Handle<Object> result;
ASSIGN_RETURN_ON_EXCEPTION(
it.isolate(), result,
JSObject::SetPropertyWithAccessor(
it.GetReceiver(), it.name(), value,
it.GetHolder<JSObject>(), accessors, STRICT),
Object);
DCHECK(result->SameValue(*value));
// Special handling for ExecutableAccessorInfo, which behaves like a
// data property.
if (handling == DONT_FORCE_FIELD &&
accessors->IsExecutableAccessorInfo()) {
Handle<Object> result;
ASSIGN_RETURN_ON_EXCEPTION(
it.isolate(), result,
JSObject::SetPropertyWithAccessor(it.GetReceiver(), it.name(),
value, it.GetHolder<JSObject>(),
accessors, STRICT),
Object);
DCHECK(result->SameValue(*value));
if (details.attributes() == attributes) {
// Regular property update if the attributes match.
if (is_observed && !old_value->SameValue(*value)) {
// If we are setting the prototype of a function and are
// observed, don't send change records because the prototype
// handles that itself.
if (!object->IsJSFunction() ||
!Name::Equals(it.isolate()->factory()->prototype_string(),
name) ||
!Handle<JSFunction>::cast(object)
->should_have_prototype()) {
EnqueueChangeRecord(object, "update", name, old_value);
}
}
return value;
if (details.attributes() == attributes) {
// Regular property update if the attributes match.
if (is_observed && !old_value->SameValue(*value)) {
// If we are setting the prototype of a function and are
// observed, don't send change records because the prototype
// handles that itself.
if (!object->IsJSFunction() ||
!Name::Equals(it.isolate()->factory()->prototype_string(),
name) ||
!Handle<JSFunction>::cast(object)->should_have_prototype()) {
EnqueueChangeRecord(object, "update", name, old_value);
}
// Reconfigure the accessor if attributes mismatch.
Handle<ExecutableAccessorInfo> new_data =
Accessors::CloneAccessor(
it.isolate(),
Handle<ExecutableAccessorInfo>::cast(accessors));
new_data->set_property_attributes(attributes);
// By clearing the setter we don't have to introduce a lookup to
// the setter, simply make it unavailable to reflect the
// attributes.
if (attributes & READ_ONLY) new_data->clear_setter();
SetPropertyCallback(object, name, new_data, attributes);
if (is_observed) {
if (old_value->SameValue(*value)) {
old_value = it.isolate()->factory()->the_hole_value();
}
EnqueueChangeRecord(object, "reconfigure", name, old_value);
}
return value;
}
// Regular accessor. Reconfigure to data property.
break;
return value;
}
case LookupIterator::DATA:
// Regular property update if the attributes match.
if (details.attributes() == attributes) {
return SetDataProperty(&it, value);
// Reconfigure the accessor if attributes mismatch.
Handle<ExecutableAccessorInfo> new_data = Accessors::CloneAccessor(
it.isolate(), Handle<ExecutableAccessorInfo>::cast(accessors));
new_data->set_property_attributes(attributes);
// By clearing the setter we don't have to introduce a lookup to
// the setter, simply make it unavailable to reflect the
// attributes.
if (attributes & READ_ONLY) new_data->clear_setter();
SetPropertyCallback(object, name, new_data, attributes);
if (is_observed) {
if (old_value->SameValue(*value)) {
old_value = it.isolate()->factory()->the_hole_value();
}
// Reconfigure the data property if the attributes mismatch.
if (is_observed) old_value = it.GetDataValue();
EnqueueChangeRecord(object, "reconfigure", name, old_value);
}
return value;
}
it.ReconfigureDataProperty(value, attributes);
@ -3928,6 +3901,30 @@ MaybeHandle<Object> JSObject::SetOwnPropertyIgnoreAttributes(
return value;
}
case LookupIterator::DATA: {
PropertyDetails details = it.property_details();
Handle<Object> old_value = it.isolate()->factory()->the_hole_value();
// Regular property update if the attributes match.
if (details.attributes() == attributes) {
return SetDataProperty(&it, value);
}
// Reconfigure the data property if the attributes mismatch.
if (is_observed) old_value = it.GetDataValue();
it.ReconfigureDataProperty(value, attributes);
it.PrepareForDataProperty(value);
it.WriteDataValue(value);
if (is_observed) {
if (old_value->SameValue(*value)) {
old_value = it.isolate()->factory()->the_hole_value();
}
EnqueueChangeRecord(object, "reconfigure", name, old_value);
}
return value;
}
}
}
@ -3996,6 +3993,7 @@ Maybe<PropertyAttributes> JSReceiver::GetPropertyAttributes(
for (; it->IsFound(); it->Next()) {
switch (it->state()) {
case LookupIterator::NOT_FOUND:
case LookupIterator::UNKNOWN:
case LookupIterator::TRANSITION:
UNREACHABLE();
case LookupIterator::JSPROXY:
@ -4012,11 +4010,9 @@ Maybe<PropertyAttributes> JSReceiver::GetPropertyAttributes(
case LookupIterator::ACCESS_CHECK:
if (it->HasAccess(v8::ACCESS_HAS)) break;
return JSObject::GetPropertyAttributesWithFailedAccessCheck(it);
case LookupIterator::PROPERTY:
if (it->HasProperty()) {
return maybe(it->property_details().attributes());
}
break;
case LookupIterator::ACCESSOR:
case LookupIterator::DATA:
return maybe(it->property_details().attributes());
}
}
return maybe(ABSENT);
@ -4693,7 +4689,7 @@ bool JSObject::HasHiddenProperties(Handle<JSObject> object) {
Handle<Name> hidden = object->GetIsolate()->factory()->hidden_string();
LookupIterator it(object, hidden, LookupIterator::OWN_SKIP_INTERCEPTOR);
CHECK_NE(LookupIterator::ACCESS_CHECK, it.state());
return it.IsFound() && it.HasProperty();
return it.IsFound();
}
@ -4726,10 +4722,10 @@ Object* JSObject::GetHiddenPropertiesHashTable() {
LookupIterator it(handle(this), isolate->factory()->hidden_string(),
LookupIterator::OWN_SKIP_INTERCEPTOR);
CHECK_NE(LookupIterator::ACCESS_CHECK, it.state());
if (it.IsFound() && it.HasProperty()) {
DCHECK_EQ(LookupIterator::DATA, it.property_kind());
if (it.state() == LookupIterator::DATA) {
return *it.GetDataValue();
}
DCHECK(!it.IsFound());
return GetHeap()->undefined_value();
}
}
@ -4926,12 +4922,14 @@ MaybeHandle<Object> JSObject::DeleteProperty(Handle<JSObject> object,
bool is_observed = object->map()->is_observed() &&
*name != it.isolate()->heap()->hidden_string();
Handle<Object> old_value = it.isolate()->factory()->the_hole_value();
for (; it.IsFound(); it.Next()) {
switch (it.state()) {
case LookupIterator::JSPROXY:
case LookupIterator::NOT_FOUND:
case LookupIterator::TRANSITION:
case LookupIterator::UNKNOWN:
UNREACHABLE();
case LookupIterator::ACCESS_CHECK:
if (it.HasAccess(v8::ACCESS_DELETE)) break;
@ -4949,8 +4947,12 @@ MaybeHandle<Object> JSObject::DeleteProperty(Handle<JSObject> object,
if (it.isolate()->has_pending_exception()) return maybe_result;
break;
}
case LookupIterator::PROPERTY: {
if (!it.HasProperty()) continue;
case LookupIterator::DATA:
if (is_observed) {
old_value = it.GetDataValue();
}
// Fall through.
case LookupIterator::ACCESSOR: {
if (delete_mode != FORCE_DELETION && !it.IsConfigurable()) {
// Fail if the property is not configurable.
if (delete_mode == STRICT_DELETION) {
@ -4963,17 +4965,6 @@ MaybeHandle<Object> JSObject::DeleteProperty(Handle<JSObject> object,
return it.isolate()->factory()->false_value();
}
Handle<Object> old_value;
if (is_observed) {
switch (it.property_kind()) {
case LookupIterator::ACCESSOR:
old_value = it.isolate()->factory()->the_hole_value();
break;
case LookupIterator::DATA:
old_value = it.GetDataValue();
}
}
PropertyNormalizationMode mode = object->map()->is_prototype_map()
? KEEP_INOBJECT_PROPERTIES
: CLEAR_INOBJECT_PROPERTIES;
@ -6150,7 +6141,7 @@ MaybeHandle<Object> JSObject::DefineAccessor(Handle<JSObject> object,
LookupIterator it(object, name, LookupIterator::HIDDEN_SKIP_INTERCEPTOR);
CHECK(GetPropertyAttributes(&it).has_value);
preexists = it.IsFound();
if (preexists && (it.property_kind() == LookupIterator::DATA ||
if (preexists && (it.state() == LookupIterator::DATA ||
it.GetAccessors()->IsAccessorInfo())) {
old_value = GetProperty(&it).ToHandleChecked();
}
@ -6314,6 +6305,7 @@ MaybeHandle<Object> JSObject::GetAccessor(Handle<JSObject> object,
case LookupIterator::INTERCEPTOR:
case LookupIterator::NOT_FOUND:
case LookupIterator::TRANSITION:
case LookupIterator::UNKNOWN:
UNREACHABLE();
case LookupIterator::ACCESS_CHECK:
@ -6326,20 +6318,16 @@ MaybeHandle<Object> JSObject::GetAccessor(Handle<JSObject> object,
case LookupIterator::JSPROXY:
return isolate->factory()->undefined_value();
case LookupIterator::PROPERTY:
if (!it.HasProperty()) continue;
switch (it.property_kind()) {
case LookupIterator::DATA:
continue;
case LookupIterator::ACCESSOR: {
Handle<Object> maybe_pair = it.GetAccessors();
if (maybe_pair->IsAccessorPair()) {
return handle(
AccessorPair::cast(*maybe_pair)->GetComponent(component),
isolate);
}
}
case LookupIterator::DATA:
continue;
case LookupIterator::ACCESSOR: {
Handle<Object> maybe_pair = it.GetAccessors();
if (maybe_pair->IsAccessorPair()) {
return handle(
AccessorPair::cast(*maybe_pair)->GetComponent(component),
isolate);
}
}
}
}
}
@ -12849,7 +12837,7 @@ bool JSArray::WouldChangeReadOnlyLength(Handle<JSArray> array,
LookupIterator::OWN_SKIP_INTERCEPTOR);
CHECK_NE(LookupIterator::ACCESS_CHECK, it.state());
CHECK(it.IsFound());
CHECK(it.HasProperty());
CHECK_EQ(LookupIterator::ACCESSOR, it.state());
return it.IsReadOnly();
}
return false;
@ -13275,7 +13263,7 @@ Maybe<bool> JSObject::HasRealNamedCallbackProperty(Handle<JSObject> object,
LookupIterator it(object, key, LookupIterator::OWN_SKIP_INTERCEPTOR);
Maybe<PropertyAttributes> maybe_result = GetPropertyAttributes(&it);
if (!maybe_result.has_value) return Maybe<bool>();
return maybe(it.IsFound() && it.property_kind() == LookupIterator::ACCESSOR);
return maybe(it.state() == LookupIterator::ACCESSOR);
}

View File

@ -1990,8 +1990,7 @@ MUST_USE_RESULT static MaybeHandle<Object> GetOwnProperty(Isolate* isolate,
if (attrs == ABSENT) return factory->undefined_value();
// Get AccessorPair if present.
if (it.state() == LookupIterator::PROPERTY &&
it.property_kind() == LookupIterator::ACCESSOR &&
if (it.state() == LookupIterator::ACCESSOR &&
it.GetAccessors()->IsAccessorPair()) {
maybe_accessors = Handle<AccessorPair>::cast(it.GetAccessors());
}
@ -2323,7 +2322,7 @@ RUNTIME_FUNCTION(Runtime_InitializeConstGlobal) {
// Ignore if we can't reconfigure the value.
if ((old_attributes & DONT_DELETE) != 0) {
if ((old_attributes & READ_ONLY) != 0 ||
it.property_kind() == LookupIterator::ACCESSOR) {
it.state() == LookupIterator::ACCESSOR) {
return *value;
}
attr = static_cast<PropertyAttributes>(old_attributes | READ_ONLY);
@ -2468,7 +2467,7 @@ RUNTIME_FUNCTION(Runtime_InitializeLegacyConstLookupSlot) {
// Ignore if we can't reconfigure the value.
if ((old_attributes & DONT_DELETE) != 0) {
if ((old_attributes & READ_ONLY) != 0 ||
it.property_kind() == LookupIterator::ACCESSOR) {
it.state() == LookupIterator::ACCESSOR) {
return *value;
}
attr = static_cast<PropertyAttributes>(old_attributes | READ_ONLY);
@ -4891,8 +4890,8 @@ RUNTIME_FUNCTION(Runtime_KeyedGetProperty) {
// Lookup cache miss. Perform lookup and update the cache if
// appropriate.
LookupIterator it(receiver, key, LookupIterator::OWN);
if (it.IsFound() && it.state() == LookupIterator::PROPERTY &&
it.HasProperty() && it.property_details().type() == FIELD) {
if (it.state() == LookupIterator::DATA &&
it.property_details().type() == FIELD) {
FieldIndex field_index = it.GetFieldIndex();
// Do not track double fields in the keyed lookup cache. Reading
// double values requires boxing.
@ -5050,8 +5049,7 @@ RUNTIME_FUNCTION(Runtime_DefineDataPropertyUnchecked) {
// Take special care when attributes are different and there is already
// a property.
if (it.IsFound() && it.HasProperty() &&
it.property_kind() == LookupIterator::ACCESSOR) {
if (it.state() == LookupIterator::ACCESSOR) {
// Use IgnoreAttributes version since a readonly property may be
// overridden and SetProperty does not allow this.
Handle<Object> result;
@ -10898,6 +10896,7 @@ static Handle<Object> DebugGetProperty(LookupIterator* it,
switch (it->state()) {
case LookupIterator::NOT_FOUND:
case LookupIterator::TRANSITION:
case LookupIterator::UNKNOWN:
UNREACHABLE();
case LookupIterator::ACCESS_CHECK:
// Ignore access checks.
@ -10905,30 +10904,25 @@ static Handle<Object> DebugGetProperty(LookupIterator* it,
case LookupIterator::INTERCEPTOR:
case LookupIterator::JSPROXY:
return it->isolate()->factory()->undefined_value();
case LookupIterator::PROPERTY:
if (!it->HasProperty()) continue;
switch (it->property_kind()) {
case LookupIterator::ACCESSOR: {
Handle<Object> accessors = it->GetAccessors();
if (!accessors->IsAccessorInfo()) {
return it->isolate()->factory()->undefined_value();
}
MaybeHandle<Object> maybe_result =
JSObject::GetPropertyWithAccessor(it->GetReceiver(), it->name(),
it->GetHolder<JSObject>(),
accessors);
Handle<Object> result;
if (!maybe_result.ToHandle(&result)) {
result =
handle(it->isolate()->pending_exception(), it->isolate());
it->isolate()->clear_pending_exception();
if (has_caught != NULL) *has_caught = true;
}
return result;
}
case LookupIterator::DATA:
return it->GetDataValue();
case LookupIterator::ACCESSOR: {
Handle<Object> accessors = it->GetAccessors();
if (!accessors->IsAccessorInfo()) {
return it->isolate()->factory()->undefined_value();
}
MaybeHandle<Object> maybe_result = JSObject::GetPropertyWithAccessor(
it->GetReceiver(), it->name(), it->GetHolder<JSObject>(),
accessors);
Handle<Object> result;
if (!maybe_result.ToHandle(&result)) {
result = handle(it->isolate()->pending_exception(), it->isolate());
it->isolate()->clear_pending_exception();
if (has_caught != NULL) *has_caught = true;
}
return result;
}
case LookupIterator::DATA:
return it->GetDataValue();
}
}
@ -10983,8 +10977,7 @@ RUNTIME_FUNCTION(Runtime_DebugGetPropertyDetails) {
if (!it.IsFound()) return isolate->heap()->undefined_value();
Handle<Object> maybe_pair;
if (it.state() == LookupIterator::PROPERTY &&
it.property_kind() == LookupIterator::ACCESSOR) {
if (it.state() == LookupIterator::ACCESSOR) {
maybe_pair = it.GetAccessors();
}

View File

@ -2045,8 +2045,7 @@ THREADED_TEST(ExecutableAccessorIsPreservedOnAttributeChange) {
i::LookupResult lookup(i_isolate);
i::Handle<i::String> name(v8::Utils::OpenHandle(*v8_str("length")));
i::LookupIterator it(a, name, i::LookupIterator::OWN_SKIP_INTERCEPTOR);
CHECK_NE(i::LookupIterator::ACCESS_CHECK, it.state());
CHECK(it.HasProperty());
CHECK_EQ(i::LookupIterator::ACCESSOR, it.state());
CHECK(it.GetAccessors()->IsExecutableAccessorInfo());
}