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-09-04 12:28:13 +00:00
|
|
|
DCHECK_NE(JSPROXY, state_);
|
|
|
|
DCHECK_NE(TRANSITION, state_);
|
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
|
|
|
|
2014-09-16 12:42:04 +00:00
|
|
|
JSReceiver* holder = *holder_;
|
2014-08-05 09:32:55 +00:00
|
|
|
Map* map = *holder_map_;
|
|
|
|
|
|
|
|
// Perform lookup on current holder.
|
2014-09-04 12:28:13 +00:00
|
|
|
state_ = LookupInHolder(map, holder);
|
|
|
|
if (IsFound()) return;
|
2014-08-05 09:32:55 +00:00
|
|
|
|
|
|
|
// Continue lookup if lookup on current holder failed.
|
2014-09-04 12:28:13 +00:00
|
|
|
do {
|
2014-08-05 09:32:55 +00:00
|
|
|
JSReceiver* maybe_holder = NextHolder(map);
|
2015-03-18 12:50:41 +00:00
|
|
|
if (maybe_holder == nullptr) {
|
|
|
|
if (interceptor_state_ == InterceptorState::kSkipNonMasking) {
|
|
|
|
RestartLookupForNonMaskingInterceptors();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2014-08-05 09:32:55 +00:00
|
|
|
holder = maybe_holder;
|
|
|
|
map = holder->map();
|
2014-09-04 12:28:13 +00:00
|
|
|
state_ = LookupInHolder(map, holder);
|
|
|
|
} while (!IsFound());
|
2014-08-05 09:32:55 +00:00
|
|
|
|
2014-09-16 12:42:04 +00:00
|
|
|
if (holder != *holder_) {
|
|
|
|
holder_ = handle(holder, isolate_);
|
|
|
|
holder_map_ = handle(map, isolate_);
|
|
|
|
}
|
2014-06-11 09:59:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-03-18 12:50:41 +00:00
|
|
|
void LookupIterator::RestartLookupForNonMaskingInterceptors() {
|
|
|
|
interceptor_state_ = InterceptorState::kProcessNonMasking;
|
|
|
|
state_ = NOT_FOUND;
|
|
|
|
property_details_ = PropertyDetails::Empty();
|
|
|
|
number_ = DescriptorArray::kNotFound;
|
|
|
|
holder_ = initial_holder_;
|
|
|
|
holder_map_ = handle(holder_->map(), isolate_);
|
|
|
|
Next();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-10 16:11:00 +00:00
|
|
|
// static
|
|
|
|
Handle<JSReceiver> LookupIterator::GetRoot(Isolate* isolate,
|
|
|
|
Handle<Object> receiver,
|
|
|
|
uint32_t index) {
|
2015-03-18 12:50:41 +00:00
|
|
|
if (receiver->IsJSReceiver()) return Handle<JSReceiver>::cast(receiver);
|
2015-07-10 16:11:00 +00:00
|
|
|
// 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;
|
|
|
|
}
|
2015-03-18 12:50:41 +00:00
|
|
|
auto root = handle(receiver->GetRootMap(isolate)->prototype(), isolate);
|
2015-03-25 13:05:06 +00:00
|
|
|
if (root->IsNull()) {
|
|
|
|
unsigned int magic = 0xbbbbbbbb;
|
|
|
|
isolate->PushStackTraceAndDie(magic, *receiver, NULL, magic);
|
|
|
|
}
|
2014-07-14 10:54:24 +00:00
|
|
|
return Handle<JSReceiver>::cast(root);
|
2014-06-11 09:59:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Handle<Map> LookupIterator::GetReceiverMap() const {
|
2014-09-16 12:42:04 +00:00
|
|
|
if (receiver_->IsNumber()) return isolate_->factory()->heap_number_map();
|
|
|
|
return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_);
|
2014-06-11 09:59:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-08-22 11:38:21 +00:00
|
|
|
Handle<JSObject> LookupIterator::GetStoreTarget() const {
|
2014-09-16 12:42:04 +00:00
|
|
|
if (receiver_->IsJSGlobalProxy()) {
|
|
|
|
PrototypeIterator iter(isolate(), receiver_);
|
|
|
|
if (iter.IsAtEnd()) return Handle<JSGlobalProxy>::cast(receiver_);
|
2014-08-22 11:38:21 +00:00
|
|
|
return Handle<JSGlobalObject>::cast(PrototypeIterator::GetCurrent(iter));
|
|
|
|
}
|
2014-09-16 12:42:04 +00:00
|
|
|
return Handle<JSObject>::cast(receiver_);
|
2014-08-22 11:38:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-26 10:34:44 +00:00
|
|
|
bool LookupIterator::HasAccess() const {
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK_EQ(ACCESS_CHECK, state_);
|
2015-02-26 10:34:44 +00:00
|
|
|
return isolate_->MayAccess(GetHolder<JSObject>());
|
2014-06-11 09:59:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-08-19 17:02:04 +00:00
|
|
|
void LookupIterator::ReloadPropertyInformation() {
|
|
|
|
state_ = BEFORE_PROPERTY;
|
2015-03-18 12:50:41 +00:00
|
|
|
interceptor_state_ = InterceptorState::kUninitialized;
|
2014-09-16 12:42:04 +00:00
|
|
|
state_ = LookupInHolder(*holder_map_, *holder_);
|
2014-09-04 12:28:13 +00:00
|
|
|
DCHECK(IsFound() || holder_map_->is_dictionary_map());
|
2014-08-19 17:02:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-10 12:49:29 +00:00
|
|
|
void LookupIterator::ReloadHolderMap() {
|
|
|
|
DCHECK_EQ(DATA, state_);
|
|
|
|
DCHECK(IsElement());
|
|
|
|
DCHECK(JSObject::cast(*holder_)->HasExternalArrayElements() ||
|
|
|
|
JSObject::cast(*holder_)->HasFixedTypedArrayElements());
|
|
|
|
if (*holder_map_ != holder_->map()) {
|
|
|
|
holder_map_ = handle(holder_->map(), isolate_);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-18 13:47:25 +00:00
|
|
|
void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
|
2014-09-04 12:28:13 +00:00
|
|
|
DCHECK(state_ == DATA || state_ == ACCESSOR);
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(HolderIsReceiverOrHiddenPrototype());
|
2015-06-11 15:07:00 +00:00
|
|
|
|
|
|
|
Handle<JSObject> holder = GetHolder<JSObject>();
|
|
|
|
|
|
|
|
if (IsElement()) {
|
|
|
|
ElementsKind old_kind = holder_map_->elements_kind();
|
|
|
|
holder_map_ = Map::PrepareForDataElement(holder_map_, value);
|
|
|
|
ElementsKind new_kind = holder_map_->elements_kind();
|
|
|
|
if (new_kind != old_kind) {
|
|
|
|
// TODO(verwaest): Handle element migration in MigrateToMap.
|
|
|
|
JSObject::UpdateAllocationSite(holder, new_kind);
|
|
|
|
if (IsFastDoubleElementsKind(old_kind) !=
|
|
|
|
IsFastDoubleElementsKind(new_kind)) {
|
2015-06-22 11:24:03 +00:00
|
|
|
uint32_t capacity = holder->elements()->length();
|
2015-06-11 15:07:00 +00:00
|
|
|
ElementsAccessor* accessor = ElementsAccessor::ForKind(new_kind);
|
2015-06-22 11:24:03 +00:00
|
|
|
accessor->GrowCapacityAndConvert(holder, capacity);
|
|
|
|
// GrowCapacityAndConvert migrated the object. No reloading of property
|
2015-06-11 15:07:00 +00:00
|
|
|
// infomation is necessary for elements.
|
|
|
|
return;
|
|
|
|
} else if (FLAG_trace_elements_transitions) {
|
|
|
|
Handle<FixedArrayBase> elements(holder->elements());
|
|
|
|
JSObject::PrintElementsTransition(stdout, holder, old_kind, elements,
|
|
|
|
new_kind, elements);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy the backing store if it is copy-on-write.
|
|
|
|
if (IsFastSmiOrObjectElementsKind(new_kind)) {
|
|
|
|
JSObject::EnsureWritableFastElements(holder);
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
if (holder_map_->is_dictionary_map()) return;
|
|
|
|
holder_map_ =
|
|
|
|
Map::PrepareForDataProperty(holder_map_, descriptor_number(), value);
|
|
|
|
}
|
|
|
|
|
|
|
|
JSObject::MigrateToMap(holder, 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) {
|
2014-09-04 12:28:13 +00:00
|
|
|
DCHECK(state_ == DATA || state_ == ACCESSOR);
|
2014-08-18 14:26:30 +00:00
|
|
|
DCHECK(HolderIsReceiverOrHiddenPrototype());
|
|
|
|
Handle<JSObject> holder = GetHolder<JSObject>();
|
2015-06-11 15:07:00 +00:00
|
|
|
if (IsElement()) {
|
2015-06-25 11:25:59 +00:00
|
|
|
DCHECK(!holder->HasExternalArrayElements());
|
|
|
|
DCHECK(!holder->HasFixedTypedArrayElements());
|
2015-06-18 12:20:54 +00:00
|
|
|
DCHECK(attributes != NONE || !holder->HasFastElements());
|
2015-06-25 11:25:59 +00:00
|
|
|
Handle<FixedArrayBase> elements(holder->elements());
|
|
|
|
holder->GetElementsAccessor()->Reconfigure(holder, elements, number_, value,
|
|
|
|
attributes);
|
2015-06-11 15:07:00 +00:00
|
|
|
} else if (holder_map_->is_dictionary_map()) {
|
2015-03-17 13:27:25 +00:00
|
|
|
PropertyDetails details(attributes, v8::internal::DATA, 0,
|
|
|
|
PropertyCellType::kMutable);
|
2014-08-18 14:26:30 +00:00
|
|
|
JSObject::SetNormalizedProperty(holder, name(), value, details);
|
2014-09-04 13:17:04 +00:00
|
|
|
} else {
|
2015-02-16 15:25:33 +00:00
|
|
|
holder_map_ = Map::ReconfigureExistingProperty(
|
|
|
|
holder_map_, descriptor_number(), i::kData, attributes);
|
|
|
|
holder_map_ =
|
|
|
|
Map::PrepareForDataProperty(holder_map_, descriptor_number(), value);
|
2014-09-04 13:17:04 +00:00
|
|
|
JSObject::MigrateToMap(holder, holder_map_);
|
2014-08-18 14:26:30 +00:00
|
|
|
}
|
|
|
|
|
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;
|
2015-05-18 12:36:49 +00:00
|
|
|
DCHECK(state_ != LookupIterator::ACCESSOR ||
|
|
|
|
(GetAccessors()->IsAccessorInfo() &&
|
|
|
|
AccessorInfo::cast(*GetAccessors())->is_special_data_property()));
|
2015-05-21 12:19:39 +00:00
|
|
|
DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_);
|
2014-09-04 12:28:13 +00:00
|
|
|
DCHECK(state_ == NOT_FOUND || !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-12-15 19:57:37 +00:00
|
|
|
if (!isolate()->IsInternallyUsedPropertyName(name()) &&
|
2014-08-22 11:38:21 +00:00
|
|
|
!receiver->map()->is_extensible()) {
|
|
|
|
return;
|
2014-07-18 13:47:25 +00:00
|
|
|
}
|
|
|
|
|
2015-02-11 09:15:19 +00:00
|
|
|
auto transition = Map::TransitionToDataProperty(
|
2014-08-25 11:31:38 +00:00
|
|
|
handle(receiver->map(), isolate_), name_, value, attributes, store_mode);
|
2014-08-22 11:38:21 +00:00
|
|
|
state_ = TRANSITION;
|
2015-02-11 09:15:19 +00:00
|
|
|
transition_ = transition;
|
|
|
|
|
|
|
|
if (receiver->IsGlobalObject()) {
|
|
|
|
// Install a property cell.
|
|
|
|
InternalizeName();
|
|
|
|
auto cell = GlobalObject::EnsurePropertyCell(
|
|
|
|
Handle<GlobalObject>::cast(receiver), name());
|
|
|
|
DCHECK(cell->value()->IsTheHole());
|
|
|
|
transition_ = cell;
|
2015-06-17 10:13:48 +00:00
|
|
|
} else if (!transition->is_dictionary_map()) {
|
2015-02-11 09:15:19 +00:00
|
|
|
property_details_ = transition->GetLastDescriptorDetails();
|
|
|
|
has_property_ = true;
|
|
|
|
}
|
2014-08-22 11:38:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void LookupIterator::ApplyTransitionToDataProperty() {
|
|
|
|
DCHECK_EQ(TRANSITION, state_);
|
|
|
|
|
|
|
|
Handle<JSObject> receiver = GetStoreTarget();
|
2015-02-11 09:15:19 +00:00
|
|
|
if (receiver->IsGlobalObject()) return;
|
2014-09-16 12:42:04 +00:00
|
|
|
holder_ = receiver;
|
2015-02-11 09:15:19 +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();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-06 08:53:41 +00:00
|
|
|
void LookupIterator::Delete() {
|
|
|
|
Handle<JSObject> holder = Handle<JSObject>::cast(holder_);
|
|
|
|
if (IsElement()) {
|
|
|
|
ElementsAccessor* accessor = holder->GetElementsAccessor();
|
|
|
|
accessor->Delete(holder, number_);
|
|
|
|
} else {
|
|
|
|
PropertyNormalizationMode mode = holder->map()->is_prototype_map()
|
|
|
|
? KEEP_INOBJECT_PROPERTIES
|
|
|
|
: CLEAR_INOBJECT_PROPERTIES;
|
|
|
|
|
|
|
|
if (holder->HasFastProperties()) {
|
|
|
|
JSObject::NormalizeProperties(holder, mode, 0, "DeletingProperty");
|
|
|
|
holder_map_ = handle(holder->map(), isolate_);
|
|
|
|
ReloadPropertyInformation();
|
|
|
|
}
|
|
|
|
// TODO(verwaest): Get rid of the name_ argument.
|
|
|
|
JSObject::DeleteNormalizedProperty(holder, name_, number_);
|
|
|
|
JSObject::ReoptimizeIfPrototype(holder);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-08-19 17:02:04 +00:00
|
|
|
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-09-16 12:42:04 +00:00
|
|
|
holder_ = receiver;
|
2014-08-25 11:31:38 +00:00
|
|
|
holder_map_ =
|
|
|
|
Map::TransitionToAccessorProperty(handle(receiver->map(), isolate_),
|
|
|
|
name_, component, accessor, attributes);
|
2014-08-19 17:02:04 +00:00
|
|
|
JSObject::MigrateToMap(receiver, holder_map_);
|
|
|
|
|
|
|
|
ReloadPropertyInformation();
|
|
|
|
|
|
|
|
if (!holder_map_->is_dictionary_map()) return;
|
|
|
|
|
|
|
|
|
|
|
|
// Install the accessor into the dictionary-mode object.
|
2015-03-17 13:27:25 +00:00
|
|
|
PropertyDetails details(attributes, ACCESSOR_CONSTANT, 0,
|
|
|
|
PropertyCellType::kMutable);
|
2014-08-19 17:02:04 +00:00
|
|
|
Handle<AccessorPair> pair;
|
2014-09-04 12:28:13 +00:00
|
|
|
if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
|
2014-08-19 17:02:04 +00:00
|
|
|
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);
|
2014-08-25 11:31:38 +00:00
|
|
|
holder_map_ = handle(receiver->map(), isolate_);
|
2014-08-19 17:02:04 +00:00
|
|
|
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);
|
2015-06-02 10:42:16 +00:00
|
|
|
return InternalHolderIsReceiverOrHiddenPrototype();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool LookupIterator::InternalHolderIsReceiverOrHiddenPrototype() const {
|
2014-08-18 14:26:30 +00:00
|
|
|
// Optimization that only works if configuration_ is not mutable.
|
2014-08-25 11:34:43 +00:00
|
|
|
if (!check_prototype_chain()) return true;
|
2014-07-18 13:47:25 +00:00
|
|
|
DisallowHeapAllocation no_gc;
|
2014-09-16 12:42:04 +00:00
|
|
|
if (!receiver_->IsJSReceiver()) return false;
|
|
|
|
Object* current = *receiver_;
|
|
|
|
JSReceiver* holder = *holder_;
|
2014-07-18 13:47:25 +00:00
|
|
|
// 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-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>();
|
2015-06-02 10:42:16 +00:00
|
|
|
if (IsElement()) {
|
|
|
|
// TODO(verwaest): Optimize.
|
2015-06-02 11:10:50 +00:00
|
|
|
if (holder->IsStringObjectWithCharacterAt(index_)) {
|
|
|
|
Handle<JSValue> js_value = Handle<JSValue>::cast(holder);
|
|
|
|
Handle<String> string(String::cast(js_value->value()));
|
|
|
|
return factory()->LookupSingleCharacterStringFromCode(
|
|
|
|
String::Flatten(string)->Get(index_));
|
|
|
|
}
|
|
|
|
|
2015-06-02 10:42:16 +00:00
|
|
|
ElementsAccessor* accessor = holder->GetElementsAccessor();
|
2015-07-10 14:13:27 +00:00
|
|
|
return accessor->Get(handle(holder->elements()), number_);
|
2015-06-02 10:42:16 +00:00
|
|
|
} else if (holder_map_->IsGlobalObjectMap()) {
|
2015-06-01 16:24:59 +00:00
|
|
|
result = holder->global_dictionary()->ValueAt(number_);
|
|
|
|
DCHECK(result->IsPropertyCell());
|
|
|
|
result = PropertyCell::cast(result)->value();
|
|
|
|
} else if (holder_map_->is_dictionary_map()) {
|
2014-09-04 13:17:04 +00:00
|
|
|
result = holder->property_dictionary()->ValueAt(number_);
|
2015-01-19 17:49:13 +00:00
|
|
|
} else if (property_details_.type() == v8::internal::DATA) {
|
2014-09-04 13:17:04 +00:00
|
|
|
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_);
|
2014-06-11 09:59:14 +00:00
|
|
|
}
|
|
|
|
return handle(result, isolate_);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-01-26 15:42:06 +00:00
|
|
|
int LookupIterator::GetAccessorIndex() const {
|
|
|
|
DCHECK(has_property_);
|
|
|
|
DCHECK(!holder_map_->is_dictionary_map());
|
|
|
|
DCHECK_EQ(v8::internal::ACCESSOR_CONSTANT, property_details_.type());
|
|
|
|
return descriptor_number();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-08-06 08:02:21 +00:00
|
|
|
int LookupIterator::GetConstantIndex() const {
|
|
|
|
DCHECK(has_property_);
|
2014-09-04 13:17:04 +00:00
|
|
|
DCHECK(!holder_map_->is_dictionary_map());
|
2015-01-19 17:49:13 +00:00
|
|
|
DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
|
2015-06-02 10:42:16 +00:00
|
|
|
DCHECK(!IsElement());
|
2014-08-06 08:02:21 +00:00
|
|
|
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_);
|
2014-09-04 13:17:04 +00:00
|
|
|
DCHECK(!holder_map_->is_dictionary_map());
|
2015-01-19 17:49:13 +00:00
|
|
|
DCHECK_EQ(v8::internal::DATA, property_details_.type());
|
2015-06-02 10:42:16 +00:00
|
|
|
DCHECK(!IsElement());
|
2014-08-04 08:34:56 +00:00
|
|
|
int index =
|
2014-09-04 13:17:04 +00:00
|
|
|
holder_map_->instance_descriptors()->GetFieldIndex(descriptor_number());
|
2014-08-04 08:34:56 +00:00
|
|
|
bool is_double = representation().IsDouble();
|
2014-09-04 13:17:04 +00:00
|
|
|
return FieldIndex::ForPropertyIndex(*holder_map_, index, is_double);
|
2014-08-04 08:34:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-08-22 11:38:21 +00:00
|
|
|
Handle<HeapType> LookupIterator::GetFieldType() const {
|
|
|
|
DCHECK(has_property_);
|
2014-09-04 13:17:04 +00:00
|
|
|
DCHECK(!holder_map_->is_dictionary_map());
|
2015-01-19 17:49:13 +00:00
|
|
|
DCHECK_EQ(v8::internal::DATA, property_details_.type());
|
2014-08-22 11:38:21 +00:00
|
|
|
return handle(
|
2014-09-04 13:17:04 +00:00
|
|
|
holder_map_->instance_descriptors()->GetFieldType(descriptor_number()),
|
2014-08-22 11:38:21 +00:00
|
|
|
isolate_);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-08-04 08:34:56 +00:00
|
|
|
Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
|
2015-06-02 10:42:16 +00:00
|
|
|
DCHECK(!IsElement());
|
2014-08-04 08:34:56 +00:00
|
|
|
Handle<JSObject> holder = GetHolder<JSObject>();
|
|
|
|
Handle<GlobalObject> global = Handle<GlobalObject>::cast(holder);
|
2015-06-01 16:24:59 +00:00
|
|
|
Object* value = global->global_dictionary()->ValueAt(dictionary_entry());
|
2015-03-19 13:59:12 +00:00
|
|
|
DCHECK(value->IsPropertyCell());
|
2015-03-19 23:54:04 +00:00
|
|
|
return handle(PropertyCell::cast(value));
|
2014-08-04 08:34:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-11 09:59:14 +00:00
|
|
|
Handle<Object> LookupIterator::GetAccessors() const {
|
2014-09-04 12:28:13 +00:00
|
|
|
DCHECK_EQ(ACCESSOR, state_);
|
2014-06-11 09:59:14 +00:00
|
|
|
return FetchValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Handle<Object> LookupIterator::GetDataValue() const {
|
2014-09-04 12:28:13 +00:00
|
|
|
DCHECK_EQ(DATA, state_);
|
2014-06-11 09:59:14 +00:00
|
|
|
Handle<Object> value = FetchValue();
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-06-12 12:39:32 +00:00
|
|
|
void LookupIterator::WriteDataValue(Handle<Object> value) {
|
2014-09-04 12:28:13 +00:00
|
|
|
DCHECK_EQ(DATA, state_);
|
2014-07-24 11:33:46 +00:00
|
|
|
Handle<JSObject> holder = GetHolder<JSObject>();
|
2015-06-11 15:07:00 +00:00
|
|
|
if (IsElement()) {
|
|
|
|
ElementsAccessor* accessor = holder->GetElementsAccessor();
|
2015-07-10 12:56:36 +00:00
|
|
|
accessor->Set(holder->elements(), number_, *value);
|
2015-06-11 15:07:00 +00:00
|
|
|
} else if (holder->IsGlobalObject()) {
|
2015-06-01 16:24:59 +00:00
|
|
|
Handle<GlobalDictionary> property_dictionary =
|
|
|
|
handle(holder->global_dictionary());
|
|
|
|
PropertyCell::UpdateCell(property_dictionary, dictionary_entry(), value,
|
|
|
|
property_details_);
|
|
|
|
} else if (holder_map_->is_dictionary_map()) {
|
2015-06-19 09:25:16 +00:00
|
|
|
NameDictionary* property_dictionary = holder->property_dictionary();
|
2015-06-01 16:24:59 +00:00
|
|
|
property_dictionary->ValueAtPut(dictionary_entry(), *value);
|
2015-01-19 17:49:13 +00:00
|
|
|
} else if (property_details_.type() == v8::internal::DATA) {
|
2014-08-04 08:34:56 +00:00
|
|
|
holder->WriteToField(descriptor_number(), *value);
|
2014-07-18 13:47:25 +00:00
|
|
|
} else {
|
2015-01-19 17:49:13 +00:00
|
|
|
DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
|
2014-07-18 13:47:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-03-10 19:11:11 +00:00
|
|
|
bool LookupIterator::IsIntegerIndexedExotic(JSReceiver* holder) {
|
2015-05-21 14:34:52 +00:00
|
|
|
DCHECK(exotic_index_state_ != ExoticIndexState::kNotExotic);
|
2015-03-10 19:11:11 +00:00
|
|
|
// Currently typed arrays are the only such objects.
|
|
|
|
if (!holder->IsJSTypedArray()) return false;
|
2015-05-21 12:19:39 +00:00
|
|
|
if (exotic_index_state_ == ExoticIndexState::kExotic) return true;
|
2015-06-02 10:42:16 +00:00
|
|
|
if (!InternalHolderIsReceiverOrHiddenPrototype()) {
|
|
|
|
exotic_index_state_ = ExoticIndexState::kNotExotic;
|
|
|
|
return false;
|
|
|
|
}
|
2015-03-10 19:11:11 +00:00
|
|
|
DCHECK(exotic_index_state_ == ExoticIndexState::kUninitialized);
|
|
|
|
bool result = false;
|
|
|
|
// Compute and cache result.
|
2015-06-02 10:42:16 +00:00
|
|
|
if (IsElement()) {
|
|
|
|
result = index_ >= JSTypedArray::cast(holder)->length_value();
|
|
|
|
} else if (name()->IsString()) {
|
2014-10-17 16:33:38 +00:00
|
|
|
Handle<String> name_string = Handle<String>::cast(name());
|
2015-03-10 19:11:11 +00:00
|
|
|
if (name_string->length() != 0) {
|
2015-03-30 11:40:57 +00:00
|
|
|
result = IsSpecialIndex(isolate_->unicode_cache(), *name_string);
|
2014-10-17 16:33:38 +00:00
|
|
|
}
|
|
|
|
}
|
2015-03-10 19:11:11 +00:00
|
|
|
exotic_index_state_ =
|
2015-05-21 12:19:39 +00:00
|
|
|
result ? ExoticIndexState::kExotic : ExoticIndexState::kNotExotic;
|
2015-03-10 19:11:11 +00:00
|
|
|
return result;
|
2014-10-17 16:33:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-18 13:47:25 +00:00
|
|
|
void LookupIterator::InternalizeName() {
|
|
|
|
if (name_->IsUniqueName()) return;
|
|
|
|
name_ = factory()->InternalizeString(Handle<String>::cast(name_));
|
|
|
|
}
|
2015-03-18 12:50:41 +00:00
|
|
|
|
|
|
|
|
2015-05-21 12:19:39 +00:00
|
|
|
bool LookupIterator::HasInterceptor(Map* map) const {
|
|
|
|
if (IsElement()) return map->has_indexed_interceptor();
|
|
|
|
return map->has_named_interceptor();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-05-21 14:34:52 +00:00
|
|
|
Handle<InterceptorInfo> LookupIterator::GetInterceptor() const {
|
|
|
|
DCHECK_EQ(INTERCEPTOR, state_);
|
2015-06-02 10:42:16 +00:00
|
|
|
return handle(GetInterceptor(JSObject::cast(*holder_)), isolate_);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
InterceptorInfo* LookupIterator::GetInterceptor(JSObject* holder) const {
|
|
|
|
if (IsElement()) return holder->GetIndexedInterceptor();
|
|
|
|
return holder->GetNamedInterceptor();
|
2015-05-21 14:34:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-03-18 12:50:41 +00:00
|
|
|
bool LookupIterator::SkipInterceptor(JSObject* holder) {
|
2015-06-02 10:42:16 +00:00
|
|
|
auto info = GetInterceptor(holder);
|
2015-03-18 12:50:41 +00:00
|
|
|
// 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;
|
|
|
|
}
|
2015-06-01 22:46:54 +00:00
|
|
|
} // namespace internal
|
|
|
|
} // namespace v8
|