a63eeb485a
Avoid using the iterator for arrays with fast elements where the iterator has not been modified. Only deals with the case where there is a single spread argument. Improves the six-speed "spread" benchmark to 1.5x slower than baseline es5 implementation, compared to 19x slower previously. BUG=v8:5511 Review-Url: https://codereview.chromium.org/2465253011 Cr-Commit-Position: refs/heads/master@{#40998}
371 lines
12 KiB
C++
371 lines
12 KiB
C++
// Copyright 2014 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#ifndef V8_LOOKUP_H_
|
|
#define V8_LOOKUP_H_
|
|
|
|
#include "src/factory.h"
|
|
#include "src/globals.h"
|
|
#include "src/isolate.h"
|
|
#include "src/objects.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
|
|
class V8_EXPORT_PRIVATE LookupIterator final BASE_EMBEDDED {
|
|
public:
|
|
enum Configuration {
|
|
// Configuration bits.
|
|
kInterceptor = 1 << 0,
|
|
kPrototypeChain = 1 << 1,
|
|
|
|
// Convience combinations of bits.
|
|
OWN_SKIP_INTERCEPTOR = 0,
|
|
OWN = kInterceptor,
|
|
PROTOTYPE_CHAIN_SKIP_INTERCEPTOR = kPrototypeChain,
|
|
PROTOTYPE_CHAIN = kPrototypeChain | kInterceptor,
|
|
DEFAULT = PROTOTYPE_CHAIN
|
|
};
|
|
|
|
enum State {
|
|
ACCESS_CHECK,
|
|
INTEGER_INDEXED_EXOTIC,
|
|
INTERCEPTOR,
|
|
JSPROXY,
|
|
NOT_FOUND,
|
|
ACCESSOR,
|
|
DATA,
|
|
TRANSITION,
|
|
// Set state_ to BEFORE_PROPERTY to ensure that the next lookup will be a
|
|
// PROPERTY lookup.
|
|
BEFORE_PROPERTY = INTERCEPTOR
|
|
};
|
|
|
|
LookupIterator(Handle<Object> receiver, Handle<Name> name,
|
|
Configuration configuration = DEFAULT)
|
|
: LookupIterator(name->GetIsolate(), receiver, name, configuration) {}
|
|
|
|
LookupIterator(Isolate* isolate, Handle<Object> receiver, Handle<Name> name,
|
|
Configuration configuration = DEFAULT)
|
|
: LookupIterator(isolate, receiver, name, GetRoot(isolate, receiver),
|
|
configuration) {}
|
|
|
|
LookupIterator(Handle<Object> receiver, Handle<Name> name,
|
|
Handle<JSReceiver> holder,
|
|
Configuration configuration = DEFAULT)
|
|
: LookupIterator(name->GetIsolate(), receiver, name, holder,
|
|
configuration) {}
|
|
|
|
LookupIterator(Isolate* isolate, Handle<Object> receiver, Handle<Name> name,
|
|
Handle<JSReceiver> holder,
|
|
Configuration configuration = DEFAULT)
|
|
: configuration_(ComputeConfiguration(configuration, name)),
|
|
interceptor_state_(InterceptorState::kUninitialized),
|
|
property_details_(PropertyDetails::Empty()),
|
|
isolate_(isolate),
|
|
name_(isolate_->factory()->InternalizeName(name)),
|
|
receiver_(receiver),
|
|
initial_holder_(holder),
|
|
// kMaxUInt32 isn't a valid index.
|
|
index_(kMaxUInt32),
|
|
number_(DescriptorArray::kNotFound) {
|
|
#ifdef DEBUG
|
|
uint32_t index; // Assert that the name is not an array index.
|
|
DCHECK(!name->AsArrayIndex(&index));
|
|
#endif // DEBUG
|
|
Start<false>();
|
|
}
|
|
|
|
LookupIterator(Isolate* isolate, Handle<Object> receiver, uint32_t index,
|
|
Configuration configuration = DEFAULT)
|
|
: LookupIterator(isolate, receiver, index,
|
|
GetRoot(isolate, receiver, index), configuration) {}
|
|
|
|
LookupIterator(Isolate* isolate, Handle<Object> receiver, uint32_t index,
|
|
Handle<JSReceiver> holder,
|
|
Configuration configuration = DEFAULT)
|
|
: configuration_(configuration),
|
|
interceptor_state_(InterceptorState::kUninitialized),
|
|
property_details_(PropertyDetails::Empty()),
|
|
isolate_(isolate),
|
|
receiver_(receiver),
|
|
initial_holder_(holder),
|
|
index_(index),
|
|
number_(DescriptorArray::kNotFound) {
|
|
// kMaxUInt32 isn't a valid index.
|
|
DCHECK_NE(kMaxUInt32, index_);
|
|
Start<true>();
|
|
}
|
|
|
|
static LookupIterator PropertyOrElement(
|
|
Isolate* isolate, Handle<Object> receiver, Handle<Name> name,
|
|
Configuration configuration = DEFAULT) {
|
|
uint32_t index;
|
|
if (name->AsArrayIndex(&index)) {
|
|
LookupIterator it =
|
|
LookupIterator(isolate, receiver, index, configuration);
|
|
it.name_ = name;
|
|
return it;
|
|
}
|
|
return LookupIterator(receiver, name, configuration);
|
|
}
|
|
|
|
static LookupIterator PropertyOrElement(
|
|
Isolate* isolate, Handle<Object> receiver, Handle<Name> name,
|
|
Handle<JSReceiver> holder, Configuration configuration = DEFAULT) {
|
|
uint32_t index;
|
|
if (name->AsArrayIndex(&index)) {
|
|
LookupIterator it =
|
|
LookupIterator(isolate, receiver, index, holder, configuration);
|
|
it.name_ = name;
|
|
return it;
|
|
}
|
|
return LookupIterator(receiver, name, holder, configuration);
|
|
}
|
|
|
|
static LookupIterator PropertyOrElement(
|
|
Isolate* isolate, Handle<Object> receiver, Handle<Object> key,
|
|
bool* success, Configuration configuration = DEFAULT);
|
|
|
|
void Restart() {
|
|
InterceptorState state = InterceptorState::kUninitialized;
|
|
IsElement() ? RestartInternal<true>(state) : RestartInternal<false>(state);
|
|
}
|
|
|
|
Isolate* isolate() const { return isolate_; }
|
|
State state() const { return state_; }
|
|
|
|
Handle<Name> name() const {
|
|
DCHECK(!IsElement());
|
|
return name_;
|
|
}
|
|
Handle<Name> GetName() {
|
|
if (name_.is_null()) {
|
|
DCHECK(IsElement());
|
|
name_ = factory()->Uint32ToString(index_);
|
|
}
|
|
return name_;
|
|
}
|
|
uint32_t index() const { return index_; }
|
|
|
|
bool IsElement() const { return index_ != kMaxUInt32; }
|
|
|
|
bool IsFound() const { return state_ != NOT_FOUND; }
|
|
void Next();
|
|
void NotFound() {
|
|
has_property_ = false;
|
|
state_ = NOT_FOUND;
|
|
}
|
|
|
|
Heap* heap() const { return isolate_->heap(); }
|
|
Factory* factory() const { return isolate_->factory(); }
|
|
Handle<Object> GetReceiver() const { return receiver_; }
|
|
|
|
Handle<JSObject> GetStoreTarget() const {
|
|
DCHECK(receiver_->IsJSObject());
|
|
if (receiver_->IsJSGlobalProxy()) {
|
|
Map* map = JSGlobalProxy::cast(*receiver_)->map();
|
|
if (map->has_hidden_prototype()) {
|
|
return handle(JSGlobalObject::cast(map->prototype()), isolate_);
|
|
}
|
|
}
|
|
return Handle<JSObject>::cast(receiver_);
|
|
}
|
|
|
|
bool is_dictionary_holder() const { return !holder_->HasFastProperties(); }
|
|
Handle<Map> transition_map() const {
|
|
DCHECK_EQ(TRANSITION, state_);
|
|
return Handle<Map>::cast(transition_);
|
|
}
|
|
Handle<PropertyCell> transition_cell() const {
|
|
DCHECK_EQ(TRANSITION, state_);
|
|
return Handle<PropertyCell>::cast(transition_);
|
|
}
|
|
template <class T>
|
|
Handle<T> GetHolder() const {
|
|
DCHECK(IsFound());
|
|
return Handle<T>::cast(holder_);
|
|
}
|
|
|
|
bool HolderIsReceiverOrHiddenPrototype() const;
|
|
|
|
bool check_prototype_chain() const {
|
|
return (configuration_ & kPrototypeChain) != 0;
|
|
}
|
|
|
|
/* ACCESS_CHECK */
|
|
bool HasAccess() const;
|
|
|
|
/* PROPERTY */
|
|
bool ExtendingNonExtensible(Handle<JSObject> receiver) {
|
|
DCHECK(receiver.is_identical_to(GetStoreTarget()));
|
|
return !receiver->map()->is_extensible() &&
|
|
(IsElement() || !name_->IsPrivate());
|
|
}
|
|
void PrepareForDataProperty(Handle<Object> value);
|
|
void PrepareTransitionToDataProperty(Handle<JSObject> receiver,
|
|
Handle<Object> value,
|
|
PropertyAttributes attributes,
|
|
Object::StoreFromKeyed store_mode);
|
|
bool IsCacheableTransition() {
|
|
DCHECK_EQ(TRANSITION, state_);
|
|
return transition_->IsPropertyCell() ||
|
|
(!transition_map()->is_dictionary_map() &&
|
|
transition_map()->GetBackPointer()->IsMap());
|
|
}
|
|
void ApplyTransitionToDataProperty(Handle<JSObject> receiver);
|
|
void ReconfigureDataProperty(Handle<Object> value,
|
|
PropertyAttributes attributes);
|
|
void Delete();
|
|
void TransitionToAccessorProperty(Handle<Object> getter,
|
|
Handle<Object> setter,
|
|
PropertyAttributes attributes);
|
|
void TransitionToAccessorPair(Handle<Object> pair,
|
|
PropertyAttributes attributes);
|
|
PropertyDetails property_details() const {
|
|
DCHECK(has_property_);
|
|
return property_details_;
|
|
}
|
|
PropertyAttributes property_attributes() const {
|
|
return property_details().attributes();
|
|
}
|
|
bool IsConfigurable() const { return property_details().IsConfigurable(); }
|
|
bool IsReadOnly() const { return property_details().IsReadOnly(); }
|
|
bool IsEnumerable() const { return property_details().IsEnumerable(); }
|
|
Representation representation() const {
|
|
return property_details().representation();
|
|
}
|
|
FieldIndex GetFieldIndex() const;
|
|
Handle<FieldType> GetFieldType() const;
|
|
int GetFieldDescriptorIndex() const;
|
|
int GetAccessorIndex() const;
|
|
int GetConstantIndex() const;
|
|
Handle<PropertyCell> GetPropertyCell() const;
|
|
Handle<Object> GetAccessors() const;
|
|
inline Handle<InterceptorInfo> GetInterceptor() const {
|
|
DCHECK_EQ(INTERCEPTOR, state_);
|
|
InterceptorInfo* result =
|
|
IsElement() ? GetInterceptor<true>(JSObject::cast(*holder_))
|
|
: GetInterceptor<false>(JSObject::cast(*holder_));
|
|
return handle(result, isolate_);
|
|
}
|
|
Handle<InterceptorInfo> GetInterceptorForFailedAccessCheck() const;
|
|
Handle<Object> GetDataValue() const;
|
|
void WriteDataValue(Handle<Object> value);
|
|
inline void UpdateProtector() {
|
|
if (IsElement()) return;
|
|
if (*name_ == heap()->is_concat_spreadable_symbol() ||
|
|
*name_ == heap()->constructor_string() ||
|
|
*name_ == heap()->species_symbol() ||
|
|
*name_ == heap()->has_instance_symbol() ||
|
|
*name_ == heap()->iterator_symbol()) {
|
|
InternalUpdateProtector();
|
|
}
|
|
}
|
|
|
|
// Lookup a 'cached' private property for an accessor.
|
|
// If not found returns false and leaves the LookupIterator unmodified.
|
|
bool TryLookupCachedProperty();
|
|
bool LookupCachedProperty();
|
|
|
|
private:
|
|
void InternalUpdateProtector();
|
|
|
|
enum class InterceptorState {
|
|
kUninitialized,
|
|
kSkipNonMasking,
|
|
kProcessNonMasking
|
|
};
|
|
|
|
Handle<Map> GetReceiverMap() const;
|
|
|
|
MUST_USE_RESULT inline JSReceiver* NextHolder(Map* map);
|
|
|
|
template <bool is_element>
|
|
V8_EXPORT_PRIVATE void Start();
|
|
template <bool is_element>
|
|
void NextInternal(Map* map, JSReceiver* holder);
|
|
template <bool is_element>
|
|
inline State LookupInHolder(Map* map, JSReceiver* holder) {
|
|
return map->instance_type() <= LAST_SPECIAL_RECEIVER_TYPE
|
|
? LookupInSpecialHolder<is_element>(map, holder)
|
|
: LookupInRegularHolder<is_element>(map, holder);
|
|
}
|
|
template <bool is_element>
|
|
State LookupInRegularHolder(Map* map, JSReceiver* holder);
|
|
template <bool is_element>
|
|
State LookupInSpecialHolder(Map* map, JSReceiver* holder);
|
|
template <bool is_element>
|
|
void RestartLookupForNonMaskingInterceptors() {
|
|
RestartInternal<is_element>(InterceptorState::kProcessNonMasking);
|
|
}
|
|
template <bool is_element>
|
|
void RestartInternal(InterceptorState interceptor_state);
|
|
Handle<Object> FetchValue() const;
|
|
template <bool is_element>
|
|
void ReloadPropertyInformation();
|
|
|
|
template <bool is_element>
|
|
bool SkipInterceptor(JSObject* holder);
|
|
template <bool is_element>
|
|
inline InterceptorInfo* GetInterceptor(JSObject* holder) const {
|
|
return is_element ? holder->GetIndexedInterceptor()
|
|
: holder->GetNamedInterceptor();
|
|
}
|
|
|
|
bool check_interceptor() const {
|
|
return (configuration_ & kInterceptor) != 0;
|
|
}
|
|
int descriptor_number() const {
|
|
DCHECK(!IsElement());
|
|
DCHECK(has_property_);
|
|
DCHECK(holder_->HasFastProperties());
|
|
return number_;
|
|
}
|
|
int dictionary_entry() const {
|
|
DCHECK(!IsElement());
|
|
DCHECK(has_property_);
|
|
DCHECK(!holder_->HasFastProperties());
|
|
return number_;
|
|
}
|
|
|
|
static Configuration ComputeConfiguration(
|
|
Configuration configuration, Handle<Name> name) {
|
|
return name->IsPrivate() ? OWN_SKIP_INTERCEPTOR : configuration;
|
|
}
|
|
|
|
static Handle<JSReceiver> GetRootForNonJSReceiver(
|
|
Isolate* isolate, Handle<Object> receiver, uint32_t index = kMaxUInt32);
|
|
inline static Handle<JSReceiver> GetRoot(Isolate* isolate,
|
|
Handle<Object> receiver,
|
|
uint32_t index = kMaxUInt32) {
|
|
if (receiver->IsJSReceiver()) return Handle<JSReceiver>::cast(receiver);
|
|
return GetRootForNonJSReceiver(isolate, receiver, index);
|
|
}
|
|
|
|
State NotFound(JSReceiver* const holder) const;
|
|
|
|
// If configuration_ becomes mutable, update
|
|
// HolderIsReceiverOrHiddenPrototype.
|
|
const Configuration configuration_;
|
|
State state_;
|
|
bool has_property_;
|
|
InterceptorState interceptor_state_;
|
|
PropertyDetails property_details_;
|
|
Isolate* const isolate_;
|
|
Handle<Name> name_;
|
|
Handle<Object> transition_;
|
|
const Handle<Object> receiver_;
|
|
Handle<JSReceiver> holder_;
|
|
const Handle<JSReceiver> initial_holder_;
|
|
const uint32_t index_;
|
|
uint32_t number_;
|
|
};
|
|
|
|
|
|
} // namespace internal
|
|
} // namespace v8
|
|
|
|
#endif // V8_LOOKUP_H_
|