blob: 967ce498ac1310487c524208a31f9f7df7515456 [file] [log] [blame]
// 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/v8.h"
#include "src/bootstrapper.h"
#include "src/lookup.h"
#include "src/lookup-inl.h"
namespace v8 {
namespace internal {
void LookupIterator::Next() {
DisallowHeapAllocation no_gc;
has_property_ = false;
JSReceiver* holder = NULL;
Map* map = *holder_map_;
// Perform lookup on current holder.
state_ = LookupInHolder(map);
// Continue lookup if lookup on current holder failed.
while (!IsFound()) {
JSReceiver* maybe_holder = NextHolder(map);
if (maybe_holder == NULL) break;
holder = maybe_holder;
map = holder->map();
state_ = LookupInHolder(map);
}
// Either was found in the receiver, or the receiver has no prototype.
if (holder == NULL) return;
maybe_holder_ = handle(holder);
holder_map_ = handle(map);
}
Handle<JSReceiver> LookupIterator::GetRoot() const {
Handle<Object> receiver = GetReceiver();
if (receiver->IsJSReceiver()) return Handle<JSReceiver>::cast(receiver);
Handle<Object> root =
handle(receiver->GetRootMap(isolate_)->prototype(), isolate_);
CHECK(!root->IsNull());
return Handle<JSReceiver>::cast(root);
}
Handle<Map> LookupIterator::GetReceiverMap() const {
Handle<Object> receiver = GetReceiver();
if (receiver->IsNumber()) return isolate_->factory()->heap_number_map();
return handle(Handle<HeapObject>::cast(receiver)->map());
}
bool LookupIterator::IsBootstrapping() const {
return isolate_->bootstrapper()->IsActive();
}
bool LookupIterator::HasAccess(v8::AccessType access_type) const {
DCHECK_EQ(ACCESS_CHECK, state_);
DCHECK(is_guaranteed_to_have_holder());
return isolate_->MayNamedAccess(GetHolder<JSObject>(), name_, access_type);
}
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_);
}
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;
case v8::internal::HANDLER:
case v8::internal::NONEXISTENT:
case v8::internal::INTERCEPTOR:
UNREACHABLE();
}
has_property_ = true;
return true;
}
void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
DCHECK(has_property_);
DCHECK(HolderIsReceiverOrHiddenPrototype());
if (property_encoding_ == DICTIONARY) return;
holder_map_ =
Map::PrepareForDataProperty(holder_map_, descriptor_number(), value);
JSObject::MigrateToMap(GetHolder<JSObject>(), holder_map_);
// Reload property information.
if (holder_map_->is_dictionary_map()) {
property_encoding_ = DICTIONARY;
} else {
property_encoding_ = DESCRIPTOR;
}
CHECK(HasProperty());
}
void LookupIterator::TransitionToDataProperty(
Handle<Object> value, PropertyAttributes attributes,
Object::StoreFromKeyed store_mode) {
DCHECK(!has_property_ || !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
// observable.
Handle<JSObject> receiver = Handle<JSObject>::cast(GetReceiver());
// Properties have to be added to context extension objects through
// SetOwnPropertyIgnoreAttributes.
DCHECK(!receiver->IsJSContextExtensionObject());
if (receiver->IsJSGlobalProxy()) {
PrototypeIterator iter(isolate(), receiver);
receiver =
Handle<JSGlobalObject>::cast(PrototypeIterator::GetCurrent(iter));
}
maybe_holder_ = receiver;
holder_map_ = Map::TransitionToDataProperty(handle(receiver->map()), name_,
value, attributes, store_mode);
JSObject::MigrateToMap(receiver, holder_map_);
// Reload the information.
state_ = NOT_FOUND;
configuration_ = CHECK_OWN_REAL;
state_ = LookupInHolder(*holder_map_);
DCHECK(IsFound());
HasProperty();
}
bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
DisallowHeapAllocation no_gc;
Handle<Object> receiver = GetReceiver();
if (!receiver->IsJSReceiver()) return false;
Object* current = *receiver;
JSReceiver* holder = *maybe_holder_.ToHandleChecked();
// JSProxy do not occur as hidden prototypes.
if (current->IsJSProxy()) {
return JSReceiver::cast(current) == holder;
}
PrototypeIterator iter(isolate(), current,
PrototypeIterator::START_AT_RECEIVER);
do {
if (JSReceiver::cast(iter.GetCurrent()) == holder) return true;
DCHECK(!current->IsJSProxy());
iter.Advance();
} while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN));
return false;
}
Handle<Object> LookupIterator::FetchValue() const {
Object* result = NULL;
Handle<JSObject> holder = GetHolder<JSObject>();
switch (property_encoding_) {
case DICTIONARY:
result = holder->property_dictionary()->ValueAt(number_);
if (holder->IsGlobalObject()) {
result = PropertyCell::cast(result)->value();
}
break;
case DESCRIPTOR:
if (property_details_.type() == v8::internal::FIELD) {
FieldIndex field_index =
FieldIndex::ForDescriptor(*holder_map_, number_);
return JSObject::FastPropertyAt(
holder, property_details_.representation(), field_index);
}
result = holder_map_->instance_descriptors()->GetValue(number_);
}
return handle(result, isolate_);
}
int LookupIterator::GetConstantIndex() const {
DCHECK(has_property_);
DCHECK_EQ(DESCRIPTOR, property_encoding_);
DCHECK_EQ(v8::internal::CONSTANT, property_details_.type());
return descriptor_number();
}
FieldIndex LookupIterator::GetFieldIndex() const {
DCHECK(has_property_);
DCHECK_EQ(DESCRIPTOR, property_encoding_);
DCHECK_EQ(v8::internal::FIELD, property_details_.type());
int index =
holder_map()->instance_descriptors()->GetFieldIndex(descriptor_number());
bool is_double = representation().IsDouble();
return FieldIndex::ForPropertyIndex(*holder_map(), index, is_double);
}
Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
Handle<JSObject> holder = GetHolder<JSObject>();
Handle<GlobalObject> global = Handle<GlobalObject>::cast(holder);
Object* value = global->property_dictionary()->ValueAt(dictionary_entry());
return Handle<PropertyCell>(PropertyCell::cast(value));
}
Handle<Object> LookupIterator::GetAccessors() const {
DCHECK(has_property_);
DCHECK_EQ(ACCESSOR, property_kind_);
return FetchValue();
}
Handle<Object> LookupIterator::GetDataValue() const {
DCHECK(has_property_);
DCHECK_EQ(DATA, property_kind_);
Handle<Object> value = FetchValue();
return value;
}
void LookupIterator::WriteDataValue(Handle<Object> value) {
DCHECK(is_guaranteed_to_have_holder());
DCHECK(has_property_);
Handle<JSObject> holder = GetHolder<JSObject>();
if (property_encoding_ == DICTIONARY) {
NameDictionary* property_dictionary = holder->property_dictionary();
if (holder->IsGlobalObject()) {
Handle<PropertyCell> cell(
PropertyCell::cast(property_dictionary->ValueAt(dictionary_entry())));
PropertyCell::SetValueInferType(cell, value);
} else {
property_dictionary->ValueAtPut(dictionary_entry(), *value);
}
} else if (property_details_.type() == v8::internal::FIELD) {
holder->WriteToField(descriptor_number(), *value);
} else {
DCHECK_EQ(v8::internal::CONSTANT, property_details_.type());
}
}
void LookupIterator::InternalizeName() {
if (name_->IsUniqueName()) return;
name_ = factory()->InternalizeString(Handle<String>::cast(name_));
}
} } // namespace v8::internal