2014-06-11 09:59:14 +00:00
|
|
|
// 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"
|
2014-08-19 17:02:04 +00:00
|
|
|
#include "src/deoptimizer.h"
|
2014-06-11 09:59:14 +00:00
|
|
|
#include "src/lookup.h"
|
2014-08-05 09:32:55 +00:00
|
|
|
#include "src/lookup-inl.h"
|
2014-06-11 09:59:14 +00:00
|
|
|
|
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
|
|
|
|
|
|
|
|
|
|
|
void LookupIterator::Next() {
|
2014-08-05 09:32:55 +00:00
|
|
|
DisallowHeapAllocation no_gc;
|
2014-06-11 09:59:14 +00:00
|
|
|
has_property_ = false;
|
2014-08-05 09:32:55 +00:00
|
|
|
|
|
|
|
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);
|
2014-06-11 09:59:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-11 18:02:38 +00:00
|
|
|
Handle<JSReceiver> LookupIterator::GetRoot() const {
|
2014-06-11 09:59:14 +00:00
|
|
|
Handle<Object> receiver = GetReceiver();
|
|
|
|
if (receiver->IsJSReceiver()) return Handle<JSReceiver>::cast(receiver);
|
2014-07-14 10:54:24 +00:00
|
|
|
Handle<Object> root =
|
|
|
|
handle(receiver->GetRootMap(isolate_)->prototype(), isolate_);
|
|
|
|
CHECK(!root->IsNull());
|
|
|
|
return Handle<JSReceiver>::cast(root);
|
2014-06-11 09:59:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-08-22 11:38:21 +00:00
|
|
|
Handle<JSObject> LookupIterator::GetStoreTarget() const {
|
|
|
|
Handle<JSObject> receiver = Handle<JSObject>::cast(GetReceiver());
|
|
|
|
|
|
|
|
if (receiver->IsJSGlobalProxy()) {
|
|
|
|
PrototypeIterator iter(isolate(), receiver);
|
|
|
|
if (iter.IsAtEnd()) return receiver;
|
|
|
|
return Handle<JSGlobalObject>::cast(PrototypeIterator::GetCurrent(iter));
|
|
|
|
}
|
|
|
|
return receiver;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-11 09:59:14 +00:00
|
|
|
bool LookupIterator::IsBootstrapping() const {
|
|
|
|
return isolate_->bootstrapper()->IsActive();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool LookupIterator::HasAccess(v8::AccessType access_type) const {
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK_EQ(ACCESS_CHECK, state_);
|
|
|
|
DCHECK(is_guaranteed_to_have_holder());
|
2014-07-24 11:33:46 +00:00
|
|
|
return isolate_->MayNamedAccess(GetHolder<JSObject>(), name_, access_type);
|
2014-06-11 09:59:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool LookupIterator::HasProperty() {
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK_EQ(PROPERTY, state_);
|
|
|
|
DCHECK(is_guaranteed_to_have_holder());
|
2014-06-11 09:59:14 +00:00
|
|
|
|
|
|
|
if (property_encoding_ == DICTIONARY) {
|
2014-07-24 11:33:46 +00:00
|
|
|
Handle<JSObject> holder = GetHolder<JSObject>();
|
2014-06-11 09:59:14 +00:00
|
|
|
number_ = holder->property_dictionary()->FindEntry(name_);
|
|
|
|
if (number_ == NameDictionary::kNotFound) return false;
|
|
|
|
|
2014-07-24 11:33:46 +00:00
|
|
|
property_details_ = holder->property_dictionary()->DetailsAt(number_);
|
2014-07-17 09:06:49 +00:00
|
|
|
// Holes in dictionary cells are absent values.
|
2014-06-11 09:59:14 +00:00
|
|
|
if (holder->IsGlobalObject() &&
|
2014-07-17 09:06:49 +00:00
|
|
|
(property_details_.IsDeleted() || FetchValue()->IsTheHole())) {
|
2014-06-11 09:59:14 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
2014-08-04 08:34:56 +00:00
|
|
|
// Can't use descriptor_number() yet because has_property_ is still false.
|
|
|
|
property_details_ =
|
|
|
|
holder_map_->instance_descriptors()->GetDetails(number_);
|
2014-06-11 09:59:14 +00:00
|
|
|
}
|
|
|
|
|
2014-08-22 11:38:21 +00:00
|
|
|
LoadPropertyKind();
|
|
|
|
|
|
|
|
has_property_ = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void LookupIterator::LoadPropertyKind() {
|
2014-06-11 09:59:14 +00:00
|
|
|
switch (property_details_.type()) {
|
|
|
|
case v8::internal::FIELD:
|
|
|
|
case v8::internal::NORMAL:
|
|
|
|
case v8::internal::CONSTANT:
|
2014-06-11 18:02:38 +00:00
|
|
|
property_kind_ = DATA;
|
2014-06-11 09:59:14 +00:00
|
|
|
break;
|
|
|
|
case v8::internal::CALLBACKS:
|
2014-06-11 18:02:38 +00:00
|
|
|
property_kind_ = ACCESSOR;
|
2014-06-11 09:59:14 +00:00
|
|
|
break;
|
|
|
|
case v8::internal::HANDLER:
|
|
|
|
case v8::internal::INTERCEPTOR:
|
|
|
|
UNREACHABLE();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-08-19 17:02:04 +00:00
|
|
|
void LookupIterator::ReloadPropertyInformation() {
|
|
|
|
state_ = BEFORE_PROPERTY;
|
|
|
|
state_ = LookupInHolder(*holder_map_);
|
|
|
|
DCHECK(IsFound());
|
|
|
|
HasProperty();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-18 13:47:25 +00:00
|
|
|
void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(has_property_);
|
|
|
|
DCHECK(HolderIsReceiverOrHiddenPrototype());
|
2014-07-18 13:47:25 +00:00
|
|
|
if (property_encoding_ == DICTIONARY) return;
|
2014-08-04 08:34:56 +00:00
|
|
|
holder_map_ =
|
|
|
|
Map::PrepareForDataProperty(holder_map_, descriptor_number(), value);
|
2014-07-24 11:33:46 +00:00
|
|
|
JSObject::MigrateToMap(GetHolder<JSObject>(), holder_map_);
|
2014-08-19 17:02:04 +00:00
|
|
|
ReloadPropertyInformation();
|
2014-07-18 13:47:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-08-18 14:26:30 +00:00
|
|
|
void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
|
|
|
|
PropertyAttributes attributes) {
|
|
|
|
DCHECK(has_property_);
|
|
|
|
DCHECK(HolderIsReceiverOrHiddenPrototype());
|
|
|
|
Handle<JSObject> holder = GetHolder<JSObject>();
|
|
|
|
if (property_encoding_ != DICTIONARY) {
|
|
|
|
holder_map_ = Map::ReconfigureDataProperty(holder_map_, descriptor_number(),
|
|
|
|
attributes);
|
|
|
|
JSObject::MigrateToMap(holder, holder_map_);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (holder_map_->is_dictionary_map()) {
|
|
|
|
PropertyDetails details(attributes, NORMAL, 0);
|
|
|
|
JSObject::SetNormalizedProperty(holder, name(), value, details);
|
|
|
|
}
|
|
|
|
|
2014-08-19 17:02:04 +00:00
|
|
|
ReloadPropertyInformation();
|
2014-08-18 14:26:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-08-22 11:38:21 +00:00
|
|
|
void LookupIterator::PrepareTransitionToDataProperty(
|
2014-07-18 13:47:25 +00:00
|
|
|
Handle<Object> value, PropertyAttributes attributes,
|
|
|
|
Object::StoreFromKeyed store_mode) {
|
2014-08-22 11:38:21 +00:00
|
|
|
if (state_ == TRANSITION) return;
|
|
|
|
DCHECK(!has_property_ || property_kind_ != ACCESSOR);
|
|
|
|
DCHECK(!(has_property_ || state_ == JSPROXY) ||
|
|
|
|
!HolderIsReceiverOrHiddenPrototype());
|
2014-07-18 13:47:25 +00:00
|
|
|
|
|
|
|
// 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.
|
2014-08-22 11:38:21 +00:00
|
|
|
Handle<JSObject> receiver = GetStoreTarget();
|
2014-07-18 13:47:25 +00:00
|
|
|
|
2014-08-22 11:38:21 +00:00
|
|
|
if (!name().is_identical_to(isolate()->factory()->hidden_string()) &&
|
|
|
|
!receiver->map()->is_extensible()) {
|
|
|
|
return;
|
2014-07-18 13:47:25 +00:00
|
|
|
}
|
|
|
|
|
2014-08-22 11:38:21 +00:00
|
|
|
transition_map_ = Map::TransitionToDataProperty(
|
|
|
|
handle(receiver->map()), name_, value, attributes, store_mode);
|
|
|
|
state_ = TRANSITION;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void LookupIterator::ApplyTransitionToDataProperty() {
|
|
|
|
DCHECK_EQ(TRANSITION, state_);
|
|
|
|
|
|
|
|
Handle<JSObject> receiver = GetStoreTarget();
|
2014-07-18 13:47:25 +00:00
|
|
|
maybe_holder_ = receiver;
|
2014-08-22 11:38:21 +00:00
|
|
|
holder_map_ = transition_map_;
|
2014-07-18 13:47:25 +00:00
|
|
|
JSObject::MigrateToMap(receiver, holder_map_);
|
2014-08-19 17:02:04 +00:00
|
|
|
ReloadPropertyInformation();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void LookupIterator::TransitionToAccessorProperty(
|
|
|
|
AccessorComponent component, Handle<Object> accessor,
|
|
|
|
PropertyAttributes attributes) {
|
|
|
|
DCHECK(!accessor->IsNull());
|
|
|
|
// 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.
|
2014-08-22 11:38:21 +00:00
|
|
|
Handle<JSObject> receiver = GetStoreTarget();
|
2014-08-19 17:02:04 +00:00
|
|
|
maybe_holder_ = receiver;
|
|
|
|
holder_map_ = Map::TransitionToAccessorProperty(
|
|
|
|
handle(receiver->map()), name_, component, accessor, attributes);
|
|
|
|
JSObject::MigrateToMap(receiver, holder_map_);
|
|
|
|
|
|
|
|
ReloadPropertyInformation();
|
|
|
|
|
|
|
|
if (!holder_map_->is_dictionary_map()) return;
|
|
|
|
|
|
|
|
// We have to deoptimize since accesses to data properties may have been
|
|
|
|
// inlined without a corresponding map-check.
|
|
|
|
if (holder_map_->IsGlobalObjectMap()) {
|
|
|
|
Deoptimizer::DeoptimizeGlobalObject(*receiver);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Install the accessor into the dictionary-mode object.
|
|
|
|
PropertyDetails details(attributes, CALLBACKS, 0);
|
|
|
|
Handle<AccessorPair> pair;
|
|
|
|
if (IsFound() && HasProperty() && property_kind() == 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) {
|
|
|
|
if (property_details().attributes() == attributes) return;
|
|
|
|
} else {
|
|
|
|
pair = AccessorPair::Copy(pair);
|
|
|
|
pair->set(component, *accessor);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
pair = isolate()->factory()->NewAccessorPair();
|
|
|
|
pair->set(component, *accessor);
|
|
|
|
}
|
|
|
|
JSObject::SetNormalizedProperty(receiver, name_, pair, details);
|
|
|
|
|
|
|
|
JSObject::ReoptimizeIfPrototype(receiver);
|
|
|
|
holder_map_ = handle(receiver->map());
|
|
|
|
ReloadPropertyInformation();
|
2014-07-18 13:47:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-08-04 08:34:56 +00:00
|
|
|
bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
|
2014-08-18 14:26:30 +00:00
|
|
|
// Optimization that only works if configuration_ is not mutable.
|
|
|
|
if (!check_derived()) return true;
|
2014-07-18 13:47:25 +00:00
|
|
|
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;
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(!current->IsJSProxy());
|
2014-07-18 13:47:25 +00:00
|
|
|
iter.Advance();
|
|
|
|
} while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-08-18 14:26:30 +00:00
|
|
|
bool LookupIterator::HolderIsNonGlobalHiddenPrototype() const {
|
|
|
|
if (!HolderIsReceiverOrHiddenPrototype()) return false;
|
|
|
|
Handle<Object> receiver = GetReceiver();
|
|
|
|
Handle<JSReceiver> holder = GetHolder<JSReceiver>();
|
|
|
|
if (receiver.is_identical_to(holder)) return false;
|
|
|
|
if (receiver->IsJSGlobalProxy()) return !holder->IsJSGlobalObject();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-11 09:59:14 +00:00
|
|
|
Handle<Object> LookupIterator::FetchValue() const {
|
|
|
|
Object* result = NULL;
|
2014-07-24 11:33:46 +00:00
|
|
|
Handle<JSObject> holder = GetHolder<JSObject>();
|
2014-06-11 09:59:14 +00:00
|
|
|
switch (property_encoding_) {
|
|
|
|
case DICTIONARY:
|
2014-07-24 11:33:46 +00:00
|
|
|
result = holder->property_dictionary()->ValueAt(number_);
|
|
|
|
if (holder->IsGlobalObject()) {
|
2014-06-11 09:59:14 +00:00
|
|
|
result = PropertyCell::cast(result)->value();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case DESCRIPTOR:
|
|
|
|
if (property_details_.type() == v8::internal::FIELD) {
|
2014-08-04 08:34:56 +00:00
|
|
|
FieldIndex field_index =
|
|
|
|
FieldIndex::ForDescriptor(*holder_map_, number_);
|
2014-06-11 09:59:14 +00:00
|
|
|
return JSObject::FastPropertyAt(
|
2014-07-24 11:33:46 +00:00
|
|
|
holder, property_details_.representation(), field_index);
|
2014-06-11 09:59:14 +00:00
|
|
|
}
|
|
|
|
result = holder_map_->instance_descriptors()->GetValue(number_);
|
|
|
|
}
|
|
|
|
return handle(result, isolate_);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-08-06 08:02:21 +00:00
|
|
|
int LookupIterator::GetConstantIndex() const {
|
|
|
|
DCHECK(has_property_);
|
|
|
|
DCHECK_EQ(DESCRIPTOR, property_encoding_);
|
|
|
|
DCHECK_EQ(v8::internal::CONSTANT, property_details_.type());
|
|
|
|
return descriptor_number();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-08-04 08:34:56 +00:00
|
|
|
FieldIndex LookupIterator::GetFieldIndex() const {
|
2014-08-06 08:02:21 +00:00
|
|
|
DCHECK(has_property_);
|
|
|
|
DCHECK_EQ(DESCRIPTOR, property_encoding_);
|
|
|
|
DCHECK_EQ(v8::internal::FIELD, property_details_.type());
|
2014-08-04 08:34:56 +00:00
|
|
|
int index =
|
|
|
|
holder_map()->instance_descriptors()->GetFieldIndex(descriptor_number());
|
|
|
|
bool is_double = representation().IsDouble();
|
|
|
|
return FieldIndex::ForPropertyIndex(*holder_map(), index, is_double);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-08-22 11:38:21 +00:00
|
|
|
Handle<HeapType> LookupIterator::GetFieldType() const {
|
|
|
|
DCHECK(has_property_);
|
|
|
|
DCHECK_EQ(DESCRIPTOR, property_encoding_);
|
|
|
|
DCHECK_EQ(v8::internal::FIELD, property_details_.type());
|
|
|
|
return handle(
|
|
|
|
holder_map()->instance_descriptors()->GetFieldType(descriptor_number()),
|
|
|
|
isolate_);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-08-04 08:34:56 +00:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-11 09:59:14 +00:00
|
|
|
Handle<Object> LookupIterator::GetAccessors() const {
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(has_property_);
|
|
|
|
DCHECK_EQ(ACCESSOR, property_kind_);
|
2014-06-11 09:59:14 +00:00
|
|
|
return FetchValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Handle<Object> LookupIterator::GetDataValue() const {
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(has_property_);
|
|
|
|
DCHECK_EQ(DATA, property_kind_);
|
2014-06-11 09:59:14 +00:00
|
|
|
Handle<Object> value = FetchValue();
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-18 13:47:25 +00:00
|
|
|
void LookupIterator::WriteDataValue(Handle<Object> value) {
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(is_guaranteed_to_have_holder());
|
|
|
|
DCHECK(has_property_);
|
2014-07-24 11:33:46 +00:00
|
|
|
Handle<JSObject> holder = GetHolder<JSObject>();
|
2014-07-18 13:47:25 +00:00
|
|
|
if (property_encoding_ == DICTIONARY) {
|
|
|
|
NameDictionary* property_dictionary = holder->property_dictionary();
|
|
|
|
if (holder->IsGlobalObject()) {
|
|
|
|
Handle<PropertyCell> cell(
|
2014-08-04 08:34:56 +00:00
|
|
|
PropertyCell::cast(property_dictionary->ValueAt(dictionary_entry())));
|
2014-07-18 13:47:25 +00:00
|
|
|
PropertyCell::SetValueInferType(cell, value);
|
|
|
|
} else {
|
2014-08-04 08:34:56 +00:00
|
|
|
property_dictionary->ValueAtPut(dictionary_entry(), *value);
|
2014-07-18 13:47:25 +00:00
|
|
|
}
|
|
|
|
} else if (property_details_.type() == v8::internal::FIELD) {
|
2014-08-04 08:34:56 +00:00
|
|
|
holder->WriteToField(descriptor_number(), *value);
|
2014-07-18 13:47:25 +00:00
|
|
|
} else {
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK_EQ(v8::internal::CONSTANT, property_details_.type());
|
2014-07-18 13:47:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void LookupIterator::InternalizeName() {
|
|
|
|
if (name_->IsUniqueName()) return;
|
|
|
|
name_ = factory()->InternalizeString(Handle<String>::cast(name_));
|
|
|
|
}
|
2014-06-11 09:59:14 +00:00
|
|
|
} } // namespace v8::internal
|