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.
|
|
|
|
|
2015-08-20 07:44:00 +00:00
|
|
|
#include "src/lookup.h"
|
2014-06-11 09:59:14 +00:00
|
|
|
|
|
|
|
#include "src/bootstrapper.h"
|
2014-08-19 17:02:04 +00:00
|
|
|
#include "src/deoptimizer.h"
|
2015-10-05 16:23:37 +00:00
|
|
|
#include "src/elements.h"
|
2016-02-16 13:28:12 +00:00
|
|
|
#include "src/field-type.h"
|
2015-09-01 09:25:19 +00:00
|
|
|
#include "src/isolate-inl.h"
|
2014-06-11 09:59:14 +00:00
|
|
|
|
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
|
|
|
|
2015-09-30 14:51:45 +00:00
|
|
|
// static
|
|
|
|
LookupIterator LookupIterator::PropertyOrElement(Isolate* isolate,
|
|
|
|
Handle<Object> receiver,
|
|
|
|
Handle<Object> key,
|
|
|
|
bool* success,
|
|
|
|
Configuration configuration) {
|
|
|
|
uint32_t index = 0;
|
|
|
|
if (key->ToArrayIndex(&index)) {
|
|
|
|
*success = true;
|
|
|
|
return LookupIterator(isolate, receiver, index, configuration);
|
|
|
|
}
|
|
|
|
|
|
|
|
Handle<Name> name;
|
|
|
|
*success = Object::ToName(isolate, key).ToHandle(&name);
|
|
|
|
if (!*success) {
|
|
|
|
DCHECK(isolate->has_pending_exception());
|
|
|
|
// Return an unusable dummy.
|
|
|
|
return LookupIterator(receiver, isolate->factory()->empty_string());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (name->AsArrayIndex(&index)) {
|
|
|
|
LookupIterator it(isolate, receiver, index, configuration);
|
|
|
|
// Here we try to avoid having to rebuild the string later
|
|
|
|
// by storing it on the indexed LookupIterator.
|
2016-02-19 08:45:12 +00:00
|
|
|
it.name_ = name;
|
2015-09-30 14:51:45 +00:00
|
|
|
return it;
|
|
|
|
}
|
|
|
|
|
|
|
|
return LookupIterator(receiver, name, configuration);
|
|
|
|
}
|
|
|
|
|
2016-03-04 11:51:55 +00:00
|
|
|
template <bool is_element>
|
|
|
|
void LookupIterator::Start() {
|
|
|
|
DisallowHeapAllocation no_gc;
|
|
|
|
|
|
|
|
has_property_ = false;
|
|
|
|
state_ = NOT_FOUND;
|
|
|
|
holder_ = initial_holder_;
|
|
|
|
|
|
|
|
JSReceiver* holder = *holder_;
|
|
|
|
Map* map = holder->map();
|
|
|
|
|
|
|
|
state_ = LookupInHolder<is_element>(map, holder);
|
|
|
|
if (IsFound()) return;
|
|
|
|
|
|
|
|
NextInternal<is_element>(map, holder);
|
|
|
|
}
|
|
|
|
|
|
|
|
template void LookupIterator::Start<true>();
|
|
|
|
template void LookupIterator::Start<false>();
|
2016-03-02 14:18:55 +00:00
|
|
|
|
2014-06-11 09:59:14 +00:00
|
|
|
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_;
|
2016-02-17 09:46:38 +00:00
|
|
|
Map* map = holder->map();
|
2014-08-05 09:32:55 +00:00
|
|
|
|
2016-03-04 11:51:55 +00:00
|
|
|
if (map->instance_type() <= LAST_SPECIAL_RECEIVER_TYPE) {
|
|
|
|
state_ = IsElement() ? LookupInSpecialHolder<true>(map, holder)
|
|
|
|
: LookupInSpecialHolder<false>(map, holder);
|
|
|
|
if (IsFound()) return;
|
|
|
|
}
|
|
|
|
|
|
|
|
IsElement() ? NextInternal<true>(map, holder)
|
|
|
|
: NextInternal<false>(map, holder);
|
|
|
|
}
|
2014-08-05 09:32:55 +00:00
|
|
|
|
2016-03-04 11:51:55 +00:00
|
|
|
template <bool is_element>
|
|
|
|
void LookupIterator::NextInternal(Map* map, JSReceiver* holder) {
|
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) {
|
2016-03-04 11:51:55 +00:00
|
|
|
RestartLookupForNonMaskingInterceptors<is_element>();
|
2015-03-18 12:50:41 +00:00
|
|
|
return;
|
|
|
|
}
|
2016-03-04 11:51:55 +00:00
|
|
|
state_ = NOT_FOUND;
|
|
|
|
if (holder != *holder_) holder_ = handle(holder, isolate_);
|
|
|
|
return;
|
2015-03-18 12:50:41 +00:00
|
|
|
}
|
2014-08-05 09:32:55 +00:00
|
|
|
holder = maybe_holder;
|
|
|
|
map = holder->map();
|
2016-03-04 11:51:55 +00:00
|
|
|
state_ = LookupInHolder<is_element>(map, holder);
|
2014-09-04 12:28:13 +00:00
|
|
|
} while (!IsFound());
|
2014-08-05 09:32:55 +00:00
|
|
|
|
2016-03-04 11:51:55 +00:00
|
|
|
holder_ = handle(holder, isolate_);
|
2014-06-11 09:59:14 +00:00
|
|
|
}
|
|
|
|
|
2016-03-04 11:51:55 +00:00
|
|
|
template <bool is_element>
|
2015-10-26 16:38:53 +00:00
|
|
|
void LookupIterator::RestartInternal(InterceptorState interceptor_state) {
|
|
|
|
interceptor_state_ = interceptor_state;
|
2015-03-18 12:50:41 +00:00
|
|
|
property_details_ = PropertyDetails::Empty();
|
2016-03-07 16:43:12 +00:00
|
|
|
number_ = DescriptorArray::kNotFound;
|
2016-03-04 11:51:55 +00:00
|
|
|
Start<is_element>();
|
2015-03-18 12:50:41 +00:00
|
|
|
}
|
|
|
|
|
2016-03-04 11:51:55 +00:00
|
|
|
template void LookupIterator::RestartInternal<true>(InterceptorState);
|
|
|
|
template void LookupIterator::RestartInternal<false>(InterceptorState);
|
2015-03-18 12:50:41 +00:00
|
|
|
|
2015-07-10 16:11:00 +00:00
|
|
|
// static
|
2015-11-27 02:51:31 +00:00
|
|
|
Handle<JSReceiver> LookupIterator::GetRootForNonJSReceiver(
|
|
|
|
Isolate* isolate, Handle<Object> receiver, uint32_t index) {
|
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);
|
2016-06-14 10:08:44 +00:00
|
|
|
if (root->IsNull(isolate)) {
|
2015-03-25 13:05:06 +00:00
|
|
|
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 {
|
2015-07-14 10:53:06 +00:00
|
|
|
if (receiver_->IsNumber()) return factory()->heap_number_map();
|
2014-09-16 12:42:04 +00:00
|
|
|
return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_);
|
2014-06-11 09:59:14 +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-10-09 13:25:39 +00:00
|
|
|
return isolate_->MayAccess(handle(isolate_->context()),
|
|
|
|
GetHolder<JSObject>());
|
2014-06-11 09:59:14 +00:00
|
|
|
}
|
|
|
|
|
2016-03-04 11:51:55 +00:00
|
|
|
template <bool is_element>
|
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;
|
2016-03-04 11:51:55 +00:00
|
|
|
state_ = LookupInHolder<is_element>(holder_->map(), *holder_);
|
2016-02-17 09:46:38 +00:00
|
|
|
DCHECK(IsFound() || !holder_->HasFastProperties());
|
2015-07-10 12:49:29 +00:00
|
|
|
}
|
|
|
|
|
2016-03-04 11:51:55 +00:00
|
|
|
void LookupIterator::InternalUpdateProtector() {
|
Optimize @@species based on a global 'protector' cell
This patch makes ArraySpeciesCreate fast in V8 by avoiding two property reads
when the following conditions are met:
- No Array instance has had its __proto__ reset
- No Array instance has had a constructor property defined
- Array.prototype has not had its constructor changed
- Array[Symbol.species] has not been reset
For subclasses of Array, or for conditions where one of these assumptions is
violated, the full lookup of species is done according to the ArraySpeciesCreate
algorithm. Although this is a "performance cliff", it does not come up in the
expected typical use case of @@species (Array subclassing), so it is hoped that
this can form a good start. Array subclasses will incur the slowness of looking
up @@species, but their use won't slow down invocations of, for example,
Array.prototype.slice on Array base class instances.
Possible future optimizations:
- For the fallback case where the assumptions don't hold, optimize the two
property lookups.
- For Array.prototype.slice and Array.prototype.splice, even if the full lookup
of @@species needs to take place, we still could take the rest of the C++
fastpath. However, to do this correctly requires changing the calling convention
from C++ to JS to pass the @@species out, so it is not attempted in this patch.
With this patch, microbenchmarks of Array.prototype.slice do not suffer a
noticeable performance regression, unlike their previous 2.5x penalty.
TBR=hpayer@chromium.org
Review URL: https://codereview.chromium.org/1689733002
Cr-Commit-Position: refs/heads/master@{#34199}
2016-02-22 21:01:29 +00:00
|
|
|
if (isolate_->bootstrapper()->IsActive()) return;
|
|
|
|
|
2016-03-04 11:51:55 +00:00
|
|
|
if (*name_ == heap()->constructor_string()) {
|
2016-05-12 08:50:18 +00:00
|
|
|
if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
|
Optimize @@species based on a global 'protector' cell
This patch makes ArraySpeciesCreate fast in V8 by avoiding two property reads
when the following conditions are met:
- No Array instance has had its __proto__ reset
- No Array instance has had a constructor property defined
- Array.prototype has not had its constructor changed
- Array[Symbol.species] has not been reset
For subclasses of Array, or for conditions where one of these assumptions is
violated, the full lookup of species is done according to the ArraySpeciesCreate
algorithm. Although this is a "performance cliff", it does not come up in the
expected typical use case of @@species (Array subclassing), so it is hoped that
this can form a good start. Array subclasses will incur the slowness of looking
up @@species, but their use won't slow down invocations of, for example,
Array.prototype.slice on Array base class instances.
Possible future optimizations:
- For the fallback case where the assumptions don't hold, optimize the two
property lookups.
- For Array.prototype.slice and Array.prototype.splice, even if the full lookup
of @@species needs to take place, we still could take the rest of the C++
fastpath. However, to do this correctly requires changing the calling convention
from C++ to JS to pass the @@species out, so it is not attempted in this patch.
With this patch, microbenchmarks of Array.prototype.slice do not suffer a
noticeable performance regression, unlike their previous 2.5x penalty.
TBR=hpayer@chromium.org
Review URL: https://codereview.chromium.org/1689733002
Cr-Commit-Position: refs/heads/master@{#34199}
2016-02-22 21:01:29 +00:00
|
|
|
// Setting the constructor property could change an instance's @@species
|
|
|
|
if (holder_->IsJSArray()) {
|
|
|
|
isolate_->CountUsage(
|
|
|
|
v8::Isolate::UseCounterFeature::kArrayInstanceConstructorModified);
|
|
|
|
isolate_->InvalidateArraySpeciesProtector();
|
|
|
|
} else if (holder_->map()->is_prototype_map()) {
|
2016-05-12 08:50:18 +00:00
|
|
|
DisallowHeapAllocation no_gc;
|
Optimize @@species based on a global 'protector' cell
This patch makes ArraySpeciesCreate fast in V8 by avoiding two property reads
when the following conditions are met:
- No Array instance has had its __proto__ reset
- No Array instance has had a constructor property defined
- Array.prototype has not had its constructor changed
- Array[Symbol.species] has not been reset
For subclasses of Array, or for conditions where one of these assumptions is
violated, the full lookup of species is done according to the ArraySpeciesCreate
algorithm. Although this is a "performance cliff", it does not come up in the
expected typical use case of @@species (Array subclassing), so it is hoped that
this can form a good start. Array subclasses will incur the slowness of looking
up @@species, but their use won't slow down invocations of, for example,
Array.prototype.slice on Array base class instances.
Possible future optimizations:
- For the fallback case where the assumptions don't hold, optimize the two
property lookups.
- For Array.prototype.slice and Array.prototype.splice, even if the full lookup
of @@species needs to take place, we still could take the rest of the C++
fastpath. However, to do this correctly requires changing the calling convention
from C++ to JS to pass the @@species out, so it is not attempted in this patch.
With this patch, microbenchmarks of Array.prototype.slice do not suffer a
noticeable performance regression, unlike their previous 2.5x penalty.
TBR=hpayer@chromium.org
Review URL: https://codereview.chromium.org/1689733002
Cr-Commit-Position: refs/heads/master@{#34199}
2016-02-22 21:01:29 +00:00
|
|
|
// Setting the constructor of Array.prototype of any realm also needs
|
|
|
|
// to invalidate the species protector
|
2016-05-12 08:50:18 +00:00
|
|
|
if (isolate_->IsInAnyContext(*holder_,
|
|
|
|
Context::INITIAL_ARRAY_PROTOTYPE_INDEX)) {
|
Optimize @@species based on a global 'protector' cell
This patch makes ArraySpeciesCreate fast in V8 by avoiding two property reads
when the following conditions are met:
- No Array instance has had its __proto__ reset
- No Array instance has had a constructor property defined
- Array.prototype has not had its constructor changed
- Array[Symbol.species] has not been reset
For subclasses of Array, or for conditions where one of these assumptions is
violated, the full lookup of species is done according to the ArraySpeciesCreate
algorithm. Although this is a "performance cliff", it does not come up in the
expected typical use case of @@species (Array subclassing), so it is hoped that
this can form a good start. Array subclasses will incur the slowness of looking
up @@species, but their use won't slow down invocations of, for example,
Array.prototype.slice on Array base class instances.
Possible future optimizations:
- For the fallback case where the assumptions don't hold, optimize the two
property lookups.
- For Array.prototype.slice and Array.prototype.splice, even if the full lookup
of @@species needs to take place, we still could take the rest of the C++
fastpath. However, to do this correctly requires changing the calling convention
from C++ to JS to pass the @@species out, so it is not attempted in this patch.
With this patch, microbenchmarks of Array.prototype.slice do not suffer a
noticeable performance regression, unlike their previous 2.5x penalty.
TBR=hpayer@chromium.org
Review URL: https://codereview.chromium.org/1689733002
Cr-Commit-Position: refs/heads/master@{#34199}
2016-02-22 21:01:29 +00:00
|
|
|
isolate_->CountUsage(v8::Isolate::UseCounterFeature::
|
|
|
|
kArrayPrototypeConstructorModified);
|
|
|
|
isolate_->InvalidateArraySpeciesProtector();
|
|
|
|
}
|
|
|
|
}
|
2016-03-04 11:51:55 +00:00
|
|
|
} else if (*name_ == heap()->species_symbol()) {
|
2016-05-12 08:50:18 +00:00
|
|
|
if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
|
Optimize @@species based on a global 'protector' cell
This patch makes ArraySpeciesCreate fast in V8 by avoiding two property reads
when the following conditions are met:
- No Array instance has had its __proto__ reset
- No Array instance has had a constructor property defined
- Array.prototype has not had its constructor changed
- Array[Symbol.species] has not been reset
For subclasses of Array, or for conditions where one of these assumptions is
violated, the full lookup of species is done according to the ArraySpeciesCreate
algorithm. Although this is a "performance cliff", it does not come up in the
expected typical use case of @@species (Array subclassing), so it is hoped that
this can form a good start. Array subclasses will incur the slowness of looking
up @@species, but their use won't slow down invocations of, for example,
Array.prototype.slice on Array base class instances.
Possible future optimizations:
- For the fallback case where the assumptions don't hold, optimize the two
property lookups.
- For Array.prototype.slice and Array.prototype.splice, even if the full lookup
of @@species needs to take place, we still could take the rest of the C++
fastpath. However, to do this correctly requires changing the calling convention
from C++ to JS to pass the @@species out, so it is not attempted in this patch.
With this patch, microbenchmarks of Array.prototype.slice do not suffer a
noticeable performance regression, unlike their previous 2.5x penalty.
TBR=hpayer@chromium.org
Review URL: https://codereview.chromium.org/1689733002
Cr-Commit-Position: refs/heads/master@{#34199}
2016-02-22 21:01:29 +00:00
|
|
|
// Setting the Symbol.species property of any Array constructor invalidates
|
|
|
|
// the species protector
|
2016-05-12 08:50:18 +00:00
|
|
|
if (isolate_->IsInAnyContext(*holder_, Context::ARRAY_FUNCTION_INDEX)) {
|
Optimize @@species based on a global 'protector' cell
This patch makes ArraySpeciesCreate fast in V8 by avoiding two property reads
when the following conditions are met:
- No Array instance has had its __proto__ reset
- No Array instance has had a constructor property defined
- Array.prototype has not had its constructor changed
- Array[Symbol.species] has not been reset
For subclasses of Array, or for conditions where one of these assumptions is
violated, the full lookup of species is done according to the ArraySpeciesCreate
algorithm. Although this is a "performance cliff", it does not come up in the
expected typical use case of @@species (Array subclassing), so it is hoped that
this can form a good start. Array subclasses will incur the slowness of looking
up @@species, but their use won't slow down invocations of, for example,
Array.prototype.slice on Array base class instances.
Possible future optimizations:
- For the fallback case where the assumptions don't hold, optimize the two
property lookups.
- For Array.prototype.slice and Array.prototype.splice, even if the full lookup
of @@species needs to take place, we still could take the rest of the C++
fastpath. However, to do this correctly requires changing the calling convention
from C++ to JS to pass the @@species out, so it is not attempted in this patch.
With this patch, microbenchmarks of Array.prototype.slice do not suffer a
noticeable performance regression, unlike their previous 2.5x penalty.
TBR=hpayer@chromium.org
Review URL: https://codereview.chromium.org/1689733002
Cr-Commit-Position: refs/heads/master@{#34199}
2016-02-22 21:01:29 +00:00
|
|
|
isolate_->CountUsage(
|
|
|
|
v8::Isolate::UseCounterFeature::kArraySpeciesModified);
|
|
|
|
isolate_->InvalidateArraySpeciesProtector();
|
|
|
|
}
|
2016-05-12 08:50:18 +00:00
|
|
|
} else if (*name_ == heap()->is_concat_spreadable_symbol()) {
|
|
|
|
if (!isolate_->IsIsConcatSpreadableLookupChainIntact()) return;
|
|
|
|
isolate_->InvalidateIsConcatSpreadableProtector();
|
2016-05-17 11:23:59 +00:00
|
|
|
} else if (*name_ == heap()->has_instance_symbol()) {
|
|
|
|
if (!isolate_->IsHasInstanceLookupChainIntact()) return;
|
|
|
|
isolate_->InvalidateHasInstanceProtector();
|
Optimize @@species based on a global 'protector' cell
This patch makes ArraySpeciesCreate fast in V8 by avoiding two property reads
when the following conditions are met:
- No Array instance has had its __proto__ reset
- No Array instance has had a constructor property defined
- Array.prototype has not had its constructor changed
- Array[Symbol.species] has not been reset
For subclasses of Array, or for conditions where one of these assumptions is
violated, the full lookup of species is done according to the ArraySpeciesCreate
algorithm. Although this is a "performance cliff", it does not come up in the
expected typical use case of @@species (Array subclassing), so it is hoped that
this can form a good start. Array subclasses will incur the slowness of looking
up @@species, but their use won't slow down invocations of, for example,
Array.prototype.slice on Array base class instances.
Possible future optimizations:
- For the fallback case where the assumptions don't hold, optimize the two
property lookups.
- For Array.prototype.slice and Array.prototype.splice, even if the full lookup
of @@species needs to take place, we still could take the rest of the C++
fastpath. However, to do this correctly requires changing the calling convention
from C++ to JS to pass the @@species out, so it is not attempted in this patch.
With this patch, microbenchmarks of Array.prototype.slice do not suffer a
noticeable performance regression, unlike their previous 2.5x penalty.
TBR=hpayer@chromium.org
Review URL: https://codereview.chromium.org/1689733002
Cr-Commit-Position: refs/heads/master@{#34199}
2016-02-22 21:01:29 +00:00
|
|
|
}
|
|
|
|
}
|
2015-07-10 12:49:29 +00:00
|
|
|
|
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()) {
|
2016-02-17 09:46:38 +00:00
|
|
|
ElementsKind kind = holder->GetElementsKind();
|
2015-07-15 12:13:04 +00:00
|
|
|
ElementsKind to = value->OptimalElementsKind();
|
|
|
|
if (IsHoleyElementsKind(kind)) to = GetHoleyElementsKind(to);
|
2015-09-07 08:37:18 +00:00
|
|
|
to = GetMoreGeneralElementsKind(kind, to);
|
2016-02-15 14:13:53 +00:00
|
|
|
|
|
|
|
if (kind != to) {
|
|
|
|
JSObject::TransitionElementsKind(holder, to);
|
|
|
|
}
|
2015-06-11 15:07:00 +00:00
|
|
|
|
|
|
|
// Copy the backing store if it is copy-on-write.
|
2015-07-15 12:13:04 +00:00
|
|
|
if (IsFastSmiOrObjectElementsKind(to)) {
|
2015-06-11 15:07:00 +00:00
|
|
|
JSObject::EnsureWritableFastElements(holder);
|
|
|
|
}
|
2016-02-17 09:46:38 +00:00
|
|
|
return;
|
|
|
|
}
|
2015-06-11 15:07:00 +00:00
|
|
|
|
2016-07-07 12:32:15 +00:00
|
|
|
if (holder->IsJSGlobalObject()) {
|
|
|
|
Handle<GlobalDictionary> dictionary(holder->global_dictionary());
|
|
|
|
Handle<PropertyCell> cell(
|
|
|
|
PropertyCell::cast(dictionary->ValueAt(dictionary_entry())));
|
|
|
|
DCHECK(!cell->IsTheHole(isolate_));
|
|
|
|
property_details_ = cell->property_details();
|
|
|
|
PropertyCell::PrepareForValue(dictionary, dictionary_entry(), value,
|
|
|
|
property_details_);
|
|
|
|
return;
|
|
|
|
}
|
2016-02-17 09:46:38 +00:00
|
|
|
if (!holder->HasFastProperties()) return;
|
2016-02-15 14:13:53 +00:00
|
|
|
|
2016-02-17 09:46:38 +00:00
|
|
|
Handle<Map> old_map(holder->map(), isolate_);
|
|
|
|
Handle<Map> new_map =
|
|
|
|
Map::PrepareForDataProperty(old_map, descriptor_number(), value);
|
|
|
|
|
|
|
|
if (old_map.is_identical_to(new_map)) {
|
|
|
|
// Update the property details if the representation was None.
|
|
|
|
if (representation().IsNone()) {
|
|
|
|
property_details_ =
|
|
|
|
new_map->instance_descriptors()->GetDetails(descriptor_number());
|
2016-02-15 14:13:53 +00:00
|
|
|
}
|
2016-02-17 09:46:38 +00:00
|
|
|
return;
|
2015-06-11 15:07:00 +00:00
|
|
|
}
|
|
|
|
|
2016-02-17 09:46:38 +00:00
|
|
|
JSObject::MigrateToMap(holder, new_map);
|
2016-03-04 11:51:55 +00:00
|
|
|
ReloadPropertyInformation<false>();
|
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->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);
|
2016-03-04 11:51:55 +00:00
|
|
|
ReloadPropertyInformation<true>();
|
2016-07-07 12:32:15 +00:00
|
|
|
} else if (holder->HasFastProperties()) {
|
|
|
|
Handle<Map> old_map(holder->map(), isolate_);
|
|
|
|
Handle<Map> new_map = Map::ReconfigureExistingProperty(
|
|
|
|
old_map, descriptor_number(), i::kData, attributes);
|
|
|
|
new_map = Map::PrepareForDataProperty(new_map, descriptor_number(), value);
|
|
|
|
JSObject::MigrateToMap(holder, new_map);
|
|
|
|
ReloadPropertyInformation<false>();
|
2014-09-04 13:17:04 +00:00
|
|
|
} else {
|
2016-07-07 12:32:15 +00:00
|
|
|
PropertyDetails details(attributes, v8::internal::DATA, 0,
|
|
|
|
PropertyCellType::kMutable);
|
|
|
|
if (holder->IsJSGlobalObject()) {
|
|
|
|
Handle<GlobalDictionary> dictionary(holder->global_dictionary());
|
|
|
|
|
|
|
|
Handle<PropertyCell> cell = PropertyCell::PrepareForValue(
|
|
|
|
dictionary, dictionary_entry(), value, details);
|
|
|
|
cell->set_value(*value);
|
|
|
|
property_details_ = cell->property_details();
|
2016-03-04 11:51:55 +00:00
|
|
|
} else {
|
2016-07-07 12:32:15 +00:00
|
|
|
Handle<NameDictionary> dictionary(holder->property_dictionary());
|
|
|
|
PropertyDetails original_details =
|
|
|
|
dictionary->DetailsAt(dictionary_entry());
|
|
|
|
int enumeration_index = original_details.dictionary_index();
|
|
|
|
DCHECK(enumeration_index > 0);
|
|
|
|
details = details.set_index(enumeration_index);
|
|
|
|
dictionary->SetEntry(dictionary_entry(), name(), value, details);
|
|
|
|
property_details_ = details;
|
2016-03-04 11:51:55 +00:00
|
|
|
}
|
2016-07-07 12:32:15 +00:00
|
|
|
state_ = DATA;
|
2014-08-18 14:26:30 +00:00
|
|
|
}
|
|
|
|
|
2015-09-23 12:35:21 +00:00
|
|
|
WriteDataValue(value);
|
|
|
|
|
|
|
|
#if VERIFY_HEAP
|
|
|
|
if (FLAG_verify_heap) {
|
|
|
|
holder->JSObjectVerify();
|
|
|
|
}
|
|
|
|
#endif
|
2014-08-18 14:26:30 +00:00
|
|
|
}
|
|
|
|
|
2016-02-16 09:52:44 +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
|
|
|
void LookupIterator::PrepareTransitionToDataProperty(
|
2016-02-16 09:52:44 +00:00
|
|
|
Handle<JSObject> receiver, Handle<Object> value,
|
|
|
|
PropertyAttributes attributes, Object::StoreFromKeyed store_mode) {
|
|
|
|
DCHECK(receiver.is_identical_to(GetStoreTarget()));
|
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
|
|
|
|
2016-02-17 09:06:45 +00:00
|
|
|
Handle<Map> map(receiver->map(), isolate_);
|
|
|
|
|
|
|
|
// Dictionary maps can always have additional data properties.
|
|
|
|
if (map->is_dictionary_map()) {
|
|
|
|
state_ = TRANSITION;
|
|
|
|
if (map->IsJSGlobalObjectMap()) {
|
|
|
|
// Install a property cell.
|
2016-07-07 12:32:15 +00:00
|
|
|
Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(receiver);
|
|
|
|
int entry;
|
|
|
|
Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell(
|
|
|
|
global, name(), PropertyCellType::kUninitialized, &entry);
|
|
|
|
Handle<GlobalDictionary> dictionary(global->global_dictionary(),
|
|
|
|
isolate_);
|
2016-06-06 12:58:10 +00:00
|
|
|
DCHECK(cell->value()->IsTheHole(isolate_));
|
2016-07-07 12:32:15 +00:00
|
|
|
DCHECK(!value->IsTheHole(isolate_));
|
2016-02-17 09:06:45 +00:00
|
|
|
transition_ = cell;
|
2016-07-07 12:32:15 +00:00
|
|
|
// Assign an enumeration index to the property and update
|
|
|
|
// SetNextEnumerationIndex.
|
|
|
|
int index = dictionary->NextEnumerationIndex();
|
|
|
|
dictionary->SetNextEnumerationIndex(index + 1);
|
|
|
|
property_details_ = PropertyDetails(attributes, i::DATA, index,
|
|
|
|
PropertyCellType::kUninitialized);
|
|
|
|
PropertyCellType new_type =
|
|
|
|
PropertyCell::UpdatedType(cell, value, property_details_);
|
|
|
|
property_details_ = property_details_.set_cell_type(new_type);
|
|
|
|
cell->set_property_details(property_details_);
|
|
|
|
number_ = entry;
|
|
|
|
has_property_ = true;
|
2016-02-17 09:06:45 +00:00
|
|
|
} else {
|
2016-07-07 12:32:15 +00:00
|
|
|
// Don't set enumeration index (it will be set during value store).
|
|
|
|
property_details_ =
|
|
|
|
PropertyDetails(attributes, i::DATA, 0, PropertyCellType::kNoCell);
|
2016-02-17 09:06:45 +00:00
|
|
|
transition_ = map;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Handle<Map> transition =
|
|
|
|
Map::TransitionToDataProperty(map, 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;
|
|
|
|
|
2016-07-11 15:48:54 +00:00
|
|
|
if (transition->is_dictionary_map()) {
|
|
|
|
// Don't set enumeration index (it will be set during value store).
|
|
|
|
property_details_ =
|
|
|
|
PropertyDetails(attributes, i::DATA, 0, PropertyCellType::kNoCell);
|
|
|
|
} else {
|
2015-02-11 09:15:19 +00:00
|
|
|
property_details_ = transition->GetLastDescriptorDetails();
|
|
|
|
has_property_ = true;
|
|
|
|
}
|
2014-08-22 11:38:21 +00:00
|
|
|
}
|
|
|
|
|
2016-02-16 09:52:44 +00:00
|
|
|
void LookupIterator::ApplyTransitionToDataProperty(Handle<JSObject> receiver) {
|
2014-08-22 11:38:21 +00:00
|
|
|
DCHECK_EQ(TRANSITION, state_);
|
|
|
|
|
2016-02-16 09:52:44 +00:00
|
|
|
DCHECK(receiver.is_identical_to(GetStoreTarget()));
|
2014-09-16 12:42:04 +00:00
|
|
|
holder_ = receiver;
|
2016-07-07 12:32:15 +00:00
|
|
|
if (receiver->IsJSGlobalObject()) {
|
|
|
|
state_ = DATA;
|
|
|
|
return;
|
|
|
|
}
|
2016-02-15 14:13:53 +00:00
|
|
|
Handle<Map> transition = transition_map();
|
|
|
|
bool simple_transition = transition->GetBackPointer() == receiver->map();
|
2016-02-17 09:46:38 +00:00
|
|
|
JSObject::MigrateToMap(receiver, transition);
|
2016-02-15 14:13:53 +00:00
|
|
|
|
|
|
|
if (simple_transition) {
|
|
|
|
int number = transition->LastAdded();
|
|
|
|
number_ = static_cast<uint32_t>(number);
|
|
|
|
property_details_ = transition->GetLastDescriptorDetails();
|
|
|
|
state_ = DATA;
|
2016-07-07 12:32:15 +00:00
|
|
|
} else if (receiver->map()->is_dictionary_map()) {
|
|
|
|
Handle<NameDictionary> dictionary(receiver->property_dictionary(),
|
|
|
|
isolate_);
|
|
|
|
int entry;
|
|
|
|
dictionary = NameDictionary::Add(dictionary, name(),
|
|
|
|
isolate_->factory()->uninitialized_value(),
|
|
|
|
property_details_, &entry);
|
|
|
|
receiver->set_properties(*dictionary);
|
|
|
|
// Reload details containing proper enumeration index value.
|
|
|
|
property_details_ = dictionary->DetailsAt(entry);
|
|
|
|
number_ = entry;
|
|
|
|
has_property_ = true;
|
|
|
|
state_ = DATA;
|
|
|
|
|
2016-02-15 14:13:53 +00:00
|
|
|
} else {
|
2016-03-04 11:51:55 +00:00
|
|
|
ReloadPropertyInformation<false>();
|
2016-02-15 14:13:53 +00:00
|
|
|
}
|
2014-08-19 17:02:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-06 08:53:41 +00:00
|
|
|
void LookupIterator::Delete() {
|
2016-01-18 12:41:09 +00:00
|
|
|
Handle<JSReceiver> holder = Handle<JSReceiver>::cast(holder_);
|
2015-07-06 08:53:41 +00:00
|
|
|
if (IsElement()) {
|
2016-01-18 12:41:09 +00:00
|
|
|
Handle<JSObject> object = Handle<JSObject>::cast(holder);
|
|
|
|
ElementsAccessor* accessor = object->GetElementsAccessor();
|
|
|
|
accessor->Delete(object, number_);
|
2015-07-06 08:53:41 +00:00
|
|
|
} else {
|
2016-05-11 12:59:46 +00:00
|
|
|
bool is_prototype_map = holder->map()->is_prototype_map();
|
|
|
|
RuntimeCallTimerScope stats_scope(
|
|
|
|
isolate_, is_prototype_map
|
|
|
|
? &RuntimeCallStats::PrototypeObject_DeleteProperty
|
|
|
|
: &RuntimeCallStats::Object_DeleteProperty);
|
|
|
|
|
|
|
|
PropertyNormalizationMode mode =
|
|
|
|
is_prototype_map ? KEEP_INOBJECT_PROPERTIES : CLEAR_INOBJECT_PROPERTIES;
|
2015-07-06 08:53:41 +00:00
|
|
|
|
|
|
|
if (holder->HasFastProperties()) {
|
2016-01-18 12:41:09 +00:00
|
|
|
JSObject::NormalizeProperties(Handle<JSObject>::cast(holder), mode, 0,
|
|
|
|
"DeletingProperty");
|
2016-03-04 11:51:55 +00:00
|
|
|
ReloadPropertyInformation<false>();
|
2015-07-06 08:53:41 +00:00
|
|
|
}
|
|
|
|
// TODO(verwaest): Get rid of the name_ argument.
|
2016-01-18 12:41:09 +00:00
|
|
|
JSReceiver::DeleteNormalizedProperty(holder, name_, number_);
|
|
|
|
if (holder->IsJSObject()) {
|
|
|
|
JSObject::ReoptimizeIfPrototype(Handle<JSObject>::cast(holder));
|
|
|
|
}
|
2015-07-06 08:53:41 +00:00
|
|
|
}
|
2016-02-04 14:47:11 +00:00
|
|
|
state_ = NOT_FOUND;
|
2015-07-06 08:53:41 +00:00
|
|
|
}
|
|
|
|
|
2014-08-19 17:02:04 +00:00
|
|
|
void LookupIterator::TransitionToAccessorProperty(
|
2016-05-04 14:14:17 +00:00
|
|
|
Handle<Object> getter, Handle<Object> setter,
|
2014-08-19 17:02:04 +00:00
|
|
|
PropertyAttributes attributes) {
|
2016-06-14 10:08:44 +00:00
|
|
|
DCHECK(!getter->IsNull(isolate_) || !setter->IsNull(isolate_));
|
2014-08-19 17:02:04 +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-08-19 17:02:04 +00:00
|
|
|
|
2015-07-14 10:53:06 +00:00
|
|
|
if (!IsElement() && !receiver->map()->is_dictionary_map()) {
|
2016-02-17 09:46:38 +00:00
|
|
|
Handle<Map> old_map(receiver->map(), isolate_);
|
2016-03-04 12:18:28 +00:00
|
|
|
|
|
|
|
if (!holder_.is_identical_to(receiver)) {
|
|
|
|
holder_ = receiver;
|
|
|
|
state_ = NOT_FOUND;
|
|
|
|
} else if (state_ == INTERCEPTOR) {
|
|
|
|
LookupInRegularHolder<false>(*old_map, *holder_);
|
|
|
|
}
|
|
|
|
int descriptor =
|
|
|
|
IsFound() ? static_cast<int>(number_) : DescriptorArray::kNotFound;
|
|
|
|
|
2016-02-17 09:46:38 +00:00
|
|
|
Handle<Map> new_map = Map::TransitionToAccessorProperty(
|
2016-05-04 14:14:17 +00:00
|
|
|
isolate_, old_map, name_, descriptor, getter, setter, attributes);
|
2016-03-04 10:39:53 +00:00
|
|
|
bool simple_transition = new_map->GetBackPointer() == receiver->map();
|
2016-02-17 09:46:38 +00:00
|
|
|
JSObject::MigrateToMap(receiver, new_map);
|
2014-08-19 17:02:04 +00:00
|
|
|
|
2016-03-04 10:39:53 +00:00
|
|
|
if (simple_transition) {
|
|
|
|
int number = new_map->LastAdded();
|
|
|
|
number_ = static_cast<uint32_t>(number);
|
|
|
|
property_details_ = new_map->GetLastDescriptorDetails();
|
|
|
|
state_ = ACCESSOR;
|
|
|
|
return;
|
|
|
|
}
|
2014-08-19 17:02:04 +00:00
|
|
|
|
2016-03-04 11:51:55 +00:00
|
|
|
ReloadPropertyInformation<false>();
|
2016-02-17 09:46:38 +00:00
|
|
|
if (!new_map->is_dictionary_map()) return;
|
2015-07-14 10:53:06 +00:00
|
|
|
}
|
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.
|
2016-05-04 14:14:17 +00:00
|
|
|
if (pair->Equals(*getter, *setter)) {
|
2016-04-21 11:16:42 +00:00
|
|
|
if (property_details().attributes() == attributes) {
|
|
|
|
if (!IsElement()) JSObject::ReoptimizeIfPrototype(receiver);
|
|
|
|
return;
|
|
|
|
}
|
2014-08-19 17:02:04 +00:00
|
|
|
} else {
|
|
|
|
pair = AccessorPair::Copy(pair);
|
2016-05-04 14:14:17 +00:00
|
|
|
pair->SetComponents(*getter, *setter);
|
2014-08-19 17:02:04 +00:00
|
|
|
}
|
|
|
|
} else {
|
2015-07-14 10:53:06 +00:00
|
|
|
pair = factory()->NewAccessorPair();
|
2016-05-04 14:14:17 +00:00
|
|
|
pair->SetComponents(*getter, *setter);
|
2014-08-19 17:02:04 +00:00
|
|
|
}
|
|
|
|
|
2015-07-14 11:58:32 +00:00
|
|
|
TransitionToAccessorPair(pair, attributes);
|
2015-09-23 12:35:21 +00:00
|
|
|
|
|
|
|
#if VERIFY_HEAP
|
|
|
|
if (FLAG_verify_heap) {
|
|
|
|
receiver->JSObjectVerify();
|
|
|
|
}
|
|
|
|
#endif
|
2015-07-14 11:58:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void LookupIterator::TransitionToAccessorPair(Handle<Object> pair,
|
|
|
|
PropertyAttributes attributes) {
|
|
|
|
Handle<JSObject> receiver = GetStoreTarget();
|
|
|
|
holder_ = receiver;
|
|
|
|
|
|
|
|
PropertyDetails details(attributes, ACCESSOR_CONSTANT, 0,
|
|
|
|
PropertyCellType::kMutable);
|
|
|
|
|
2015-07-14 10:53:06 +00:00
|
|
|
if (IsElement()) {
|
|
|
|
// TODO(verwaest): Move code into the element accessor.
|
|
|
|
Handle<SeededNumberDictionary> dictionary =
|
|
|
|
JSObject::NormalizeElements(receiver);
|
|
|
|
|
2015-08-07 22:44:55 +00:00
|
|
|
// We unconditionally pass used_as_prototype=false here because the call
|
|
|
|
// to RequireSlowElements takes care of the required IC clearing and
|
|
|
|
// we don't want to walk the heap twice.
|
|
|
|
dictionary =
|
|
|
|
SeededNumberDictionary::Set(dictionary, index_, pair, details, false);
|
2015-07-15 12:06:20 +00:00
|
|
|
receiver->RequireSlowElements(*dictionary);
|
2015-07-14 10:53:06 +00:00
|
|
|
|
|
|
|
if (receiver->HasSlowArgumentsElements()) {
|
|
|
|
FixedArray* parameter_map = FixedArray::cast(receiver->elements());
|
|
|
|
uint32_t length = parameter_map->length() - 2;
|
|
|
|
if (number_ < length) {
|
|
|
|
parameter_map->set(number_ + 2, heap()->the_hole_value());
|
|
|
|
}
|
|
|
|
FixedArray::cast(receiver->elements())->set(1, *dictionary);
|
|
|
|
} else {
|
|
|
|
receiver->set_elements(*dictionary);
|
|
|
|
}
|
2016-03-04 11:51:55 +00:00
|
|
|
|
|
|
|
ReloadPropertyInformation<true>();
|
2015-07-14 10:53:06 +00:00
|
|
|
} else {
|
2015-07-14 11:58:32 +00:00
|
|
|
PropertyNormalizationMode mode = receiver->map()->is_prototype_map()
|
|
|
|
? KEEP_INOBJECT_PROPERTIES
|
|
|
|
: CLEAR_INOBJECT_PROPERTIES;
|
|
|
|
// Normalize object to make this operation simple.
|
|
|
|
JSObject::NormalizeProperties(receiver, mode, 0,
|
|
|
|
"TransitionToAccessorPair");
|
|
|
|
|
2015-07-14 10:53:06 +00:00
|
|
|
JSObject::SetNormalizedProperty(receiver, name_, pair, details);
|
|
|
|
JSObject::ReoptimizeIfPrototype(receiver);
|
2016-03-03 08:10:42 +00:00
|
|
|
|
2016-03-04 11:51:55 +00:00
|
|
|
ReloadPropertyInformation<false>();
|
|
|
|
}
|
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.
|
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;
|
2016-03-02 15:43:41 +00:00
|
|
|
if (*receiver_ == *holder_) return true;
|
2014-09-16 12:42:04 +00:00
|
|
|
if (!receiver_->IsJSReceiver()) return false;
|
2016-01-29 18:57:26 +00:00
|
|
|
JSReceiver* current = JSReceiver::cast(*receiver_);
|
2016-02-02 17:01:49 +00:00
|
|
|
JSReceiver* object = *holder_;
|
2016-02-08 13:49:47 +00:00
|
|
|
if (!current->map()->has_hidden_prototype()) return false;
|
2014-07-18 13:47:25 +00:00
|
|
|
// JSProxy do not occur as hidden prototypes.
|
2016-03-02 15:43:41 +00:00
|
|
|
if (object->IsJSProxy()) return false;
|
2016-06-08 14:43:22 +00:00
|
|
|
PrototypeIterator iter(isolate(), current, kStartAtPrototype,
|
2016-02-08 13:49:47 +00:00
|
|
|
PrototypeIterator::END_AT_NON_HIDDEN);
|
|
|
|
while (!iter.IsAtEnd()) {
|
2016-02-02 17:01:49 +00:00
|
|
|
if (iter.GetCurrent<JSReceiver>() == object) return true;
|
2014-07-18 13:47:25 +00:00
|
|
|
iter.Advance();
|
2016-01-29 18:57:26 +00:00
|
|
|
}
|
2014-07-18 13:47:25 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-11 09:59:14 +00:00
|
|
|
Handle<Object> LookupIterator::FetchValue() const {
|
|
|
|
Object* result = NULL;
|
2015-06-02 10:42:16 +00:00
|
|
|
if (IsElement()) {
|
2016-01-18 12:41:09 +00:00
|
|
|
Handle<JSObject> holder = GetHolder<JSObject>();
|
2015-06-02 10:42:16 +00:00
|
|
|
ElementsAccessor* accessor = holder->GetElementsAccessor();
|
2016-01-29 18:57:26 +00:00
|
|
|
return accessor->Get(holder, number_);
|
2016-02-17 09:46:38 +00:00
|
|
|
} else if (holder_->IsJSGlobalObject()) {
|
2016-01-18 12:41:09 +00:00
|
|
|
Handle<JSObject> holder = GetHolder<JSObject>();
|
2015-06-01 16:24:59 +00:00
|
|
|
result = holder->global_dictionary()->ValueAt(number_);
|
|
|
|
DCHECK(result->IsPropertyCell());
|
|
|
|
result = PropertyCell::cast(result)->value();
|
2016-02-17 09:46:38 +00:00
|
|
|
} else if (!holder_->HasFastProperties()) {
|
2016-01-18 12:41:09 +00:00
|
|
|
result = holder_->property_dictionary()->ValueAt(number_);
|
2015-01-19 17:49:13 +00:00
|
|
|
} else if (property_details_.type() == v8::internal::DATA) {
|
2016-01-18 12:41:09 +00:00
|
|
|
Handle<JSObject> holder = GetHolder<JSObject>();
|
2016-02-17 09:46:38 +00:00
|
|
|
FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_);
|
2014-09-04 13:17:04 +00:00
|
|
|
return JSObject::FastPropertyAt(holder, property_details_.representation(),
|
|
|
|
field_index);
|
|
|
|
} else {
|
2016-02-17 09:46:38 +00:00
|
|
|
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_);
|
2016-02-17 09:46:38 +00:00
|
|
|
DCHECK(holder_->HasFastProperties());
|
2015-01-26 15:42:06 +00:00
|
|
|
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_);
|
2016-02-17 09:46:38 +00:00
|
|
|
DCHECK(holder_->HasFastProperties());
|
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_);
|
2016-02-17 09:46:38 +00:00
|
|
|
DCHECK(holder_->HasFastProperties());
|
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());
|
2016-02-17 09:46:38 +00:00
|
|
|
Map* holder_map = holder_->map();
|
2014-08-04 08:34:56 +00:00
|
|
|
int index =
|
2016-02-17 09:46:38 +00:00
|
|
|
holder_map->instance_descriptors()->GetFieldIndex(descriptor_number());
|
2014-08-04 08:34:56 +00:00
|
|
|
bool is_double = representation().IsDouble();
|
2016-02-17 09:46:38 +00:00
|
|
|
return FieldIndex::ForPropertyIndex(holder_map, index, is_double);
|
2014-08-04 08:34:56 +00:00
|
|
|
}
|
|
|
|
|
2016-01-26 15:03:40 +00:00
|
|
|
Handle<FieldType> LookupIterator::GetFieldType() const {
|
2014-08-22 11:38:21 +00:00
|
|
|
DCHECK(has_property_);
|
2016-02-17 09:46:38 +00:00
|
|
|
DCHECK(holder_->HasFastProperties());
|
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(
|
2016-02-17 09:46:38 +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());
|
2016-07-07 12:32:15 +00:00
|
|
|
Handle<JSGlobalObject> holder = GetHolder<JSGlobalObject>();
|
|
|
|
Object* value = holder->global_dictionary()->ValueAt(dictionary_entry());
|
2015-03-19 13:59:12 +00:00
|
|
|
DCHECK(value->IsPropertyCell());
|
2016-07-07 12:32:15 +00:00
|
|
|
return handle(PropertyCell::cast(value), isolate_);
|
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_);
|
2016-01-18 12:41:09 +00:00
|
|
|
Handle<JSReceiver> holder = GetHolder<JSReceiver>();
|
2015-06-11 15:07:00 +00:00
|
|
|
if (IsElement()) {
|
2016-01-18 12:41:09 +00:00
|
|
|
Handle<JSObject> object = Handle<JSObject>::cast(holder);
|
|
|
|
ElementsAccessor* accessor = object->GetElementsAccessor();
|
2016-01-29 18:57:26 +00:00
|
|
|
accessor->Set(object, number_, *value);
|
2016-02-19 10:41:22 +00:00
|
|
|
} else if (holder->HasFastProperties()) {
|
|
|
|
if (property_details_.type() == v8::internal::DATA) {
|
|
|
|
JSObject::cast(*holder)->WriteToField(descriptor_number(),
|
|
|
|
property_details_, *value);
|
|
|
|
} else {
|
|
|
|
DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
|
|
|
|
}
|
2015-11-02 14:57:59 +00:00
|
|
|
} else if (holder->IsJSGlobalObject()) {
|
2016-07-07 12:32:15 +00:00
|
|
|
GlobalDictionary* dictionary = JSObject::cast(*holder)->global_dictionary();
|
|
|
|
Object* cell = dictionary->ValueAt(dictionary_entry());
|
|
|
|
DCHECK(cell->IsPropertyCell());
|
|
|
|
PropertyCell::cast(cell)->set_value(*value);
|
2016-02-19 10:41:22 +00:00
|
|
|
} else {
|
2016-07-07 12:32:15 +00:00
|
|
|
NameDictionary* dictionary = holder->property_dictionary();
|
|
|
|
dictionary->ValueAtPut(dictionary_entry(), *value);
|
2014-07-18 13:47:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-07 16:43:12 +00:00
|
|
|
template <bool is_element>
|
2015-03-18 12:50:41 +00:00
|
|
|
bool LookupIterator::SkipInterceptor(JSObject* holder) {
|
2016-03-07 16:43:12 +00:00
|
|
|
auto info = GetInterceptor<is_element>(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-10-05 16:23:37 +00:00
|
|
|
|
|
|
|
JSReceiver* LookupIterator::NextHolder(Map* map) {
|
|
|
|
DisallowHeapAllocation no_gc;
|
2016-03-04 11:51:55 +00:00
|
|
|
if (map->prototype() == heap()->null_value()) return NULL;
|
2016-05-13 11:33:17 +00:00
|
|
|
if (!check_prototype_chain() && !map->has_hidden_prototype()) return NULL;
|
2016-02-08 13:49:47 +00:00
|
|
|
return JSReceiver::cast(map->prototype());
|
2015-10-05 16:23:37 +00:00
|
|
|
}
|
|
|
|
|
2016-02-03 10:30:05 +00:00
|
|
|
LookupIterator::State LookupIterator::NotFound(JSReceiver* const holder) const {
|
|
|
|
DCHECK(!IsElement());
|
|
|
|
if (!holder->IsJSTypedArray() || !name_->IsString()) return NOT_FOUND;
|
|
|
|
|
|
|
|
Handle<String> name_string = Handle<String>::cast(name_);
|
|
|
|
if (name_string->length() == 0) return NOT_FOUND;
|
|
|
|
|
|
|
|
return IsSpecialIndex(isolate_->unicode_cache(), *name_string)
|
|
|
|
? INTEGER_INDEXED_EXOTIC
|
|
|
|
: NOT_FOUND;
|
|
|
|
}
|
2015-10-05 16:23:37 +00:00
|
|
|
|
2016-03-04 11:51:55 +00:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
template <bool is_element>
|
|
|
|
bool HasInterceptor(Map* map) {
|
|
|
|
return is_element ? map->has_indexed_interceptor()
|
|
|
|
: map->has_named_interceptor();
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
template <bool is_element>
|
|
|
|
LookupIterator::State LookupIterator::LookupInSpecialHolder(
|
|
|
|
Map* const map, JSReceiver* const holder) {
|
2015-10-05 16:23:37 +00:00
|
|
|
STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY);
|
|
|
|
switch (state_) {
|
|
|
|
case NOT_FOUND:
|
2015-12-03 15:04:25 +00:00
|
|
|
if (map->IsJSProxyMap()) {
|
2016-03-04 11:51:55 +00:00
|
|
|
if (is_element || !name_->IsPrivate()) return JSPROXY;
|
2015-12-03 15:04:25 +00:00
|
|
|
}
|
2016-02-17 11:10:04 +00:00
|
|
|
if (map->is_access_check_needed()) {
|
2016-03-04 11:51:55 +00:00
|
|
|
if (is_element || !name_->IsPrivate()) return ACCESS_CHECK;
|
2015-10-05 16:23:37 +00:00
|
|
|
}
|
|
|
|
// Fall through.
|
|
|
|
case ACCESS_CHECK:
|
2016-03-04 11:51:55 +00:00
|
|
|
if (check_interceptor() && HasInterceptor<is_element>(map) &&
|
2016-03-07 16:43:12 +00:00
|
|
|
!SkipInterceptor<is_element>(JSObject::cast(holder))) {
|
2016-03-04 11:51:55 +00:00
|
|
|
if (is_element || !name_->IsPrivate()) return INTERCEPTOR;
|
2015-10-05 16:23:37 +00:00
|
|
|
}
|
|
|
|
// Fall through.
|
|
|
|
case INTERCEPTOR:
|
2016-03-04 11:51:55 +00:00
|
|
|
if (!is_element && map->IsJSGlobalObjectMap()) {
|
2015-10-05 16:23:37 +00:00
|
|
|
GlobalDictionary* dict = JSObject::cast(holder)->global_dictionary();
|
|
|
|
int number = dict->FindEntry(name_);
|
|
|
|
if (number == GlobalDictionary::kNotFound) return NOT_FOUND;
|
|
|
|
number_ = static_cast<uint32_t>(number);
|
|
|
|
DCHECK(dict->ValueAt(number_)->IsPropertyCell());
|
|
|
|
PropertyCell* cell = PropertyCell::cast(dict->ValueAt(number_));
|
2016-06-06 12:58:10 +00:00
|
|
|
if (cell->value()->IsTheHole(isolate_)) return NOT_FOUND;
|
2015-10-05 16:23:37 +00:00
|
|
|
property_details_ = cell->property_details();
|
2016-03-04 11:51:55 +00:00
|
|
|
has_property_ = true;
|
|
|
|
switch (property_details_.kind()) {
|
|
|
|
case v8::internal::kData:
|
|
|
|
return DATA;
|
|
|
|
case v8::internal::kAccessor:
|
|
|
|
return ACCESSOR;
|
|
|
|
}
|
2015-10-05 16:23:37 +00:00
|
|
|
}
|
2016-03-04 11:51:55 +00:00
|
|
|
return LookupInRegularHolder<is_element>(map, holder);
|
2015-10-05 16:23:37 +00:00
|
|
|
case ACCESSOR:
|
|
|
|
case DATA:
|
|
|
|
return NOT_FOUND;
|
|
|
|
case INTEGER_INDEXED_EXOTIC:
|
|
|
|
case JSPROXY:
|
|
|
|
case TRANSITION:
|
|
|
|
UNREACHABLE();
|
|
|
|
}
|
|
|
|
UNREACHABLE();
|
2016-03-04 11:51:55 +00:00
|
|
|
return NOT_FOUND;
|
2015-10-05 16:23:37 +00:00
|
|
|
}
|
|
|
|
|
2016-03-04 11:51:55 +00:00
|
|
|
template <bool is_element>
|
|
|
|
LookupIterator::State LookupIterator::LookupInRegularHolder(
|
2016-03-03 08:10:42 +00:00
|
|
|
Map* const map, JSReceiver* const holder) {
|
2016-03-04 11:51:55 +00:00
|
|
|
DisallowHeapAllocation no_gc;
|
|
|
|
if (interceptor_state_ == InterceptorState::kProcessNonMasking) {
|
|
|
|
return NOT_FOUND;
|
2016-03-02 13:48:48 +00:00
|
|
|
}
|
2016-03-04 11:51:55 +00:00
|
|
|
|
|
|
|
if (is_element) {
|
|
|
|
JSObject* js_object = JSObject::cast(holder);
|
|
|
|
ElementsAccessor* accessor = js_object->GetElementsAccessor();
|
|
|
|
FixedArrayBase* backing_store = js_object->elements();
|
|
|
|
number_ = accessor->GetEntryForIndex(js_object, backing_store, index_);
|
|
|
|
if (number_ == kMaxUInt32) {
|
|
|
|
return holder->IsJSTypedArray() ? INTEGER_INDEXED_EXOTIC : NOT_FOUND;
|
|
|
|
}
|
|
|
|
property_details_ = accessor->GetDetails(js_object, number_);
|
|
|
|
} else if (!map->is_dictionary_map()) {
|
|
|
|
DescriptorArray* descriptors = map->instance_descriptors();
|
|
|
|
int number = descriptors->SearchWithCache(isolate_, *name_, map);
|
|
|
|
if (number == DescriptorArray::kNotFound) return NotFound(holder);
|
|
|
|
number_ = static_cast<uint32_t>(number);
|
|
|
|
property_details_ = descriptors->GetDetails(number_);
|
|
|
|
} else {
|
|
|
|
NameDictionary* dict = holder->property_dictionary();
|
|
|
|
int number = dict->FindEntry(name_);
|
|
|
|
if (number == NameDictionary::kNotFound) return NotFound(holder);
|
|
|
|
number_ = static_cast<uint32_t>(number);
|
|
|
|
property_details_ = dict->DetailsAt(number_);
|
|
|
|
}
|
|
|
|
has_property_ = true;
|
|
|
|
switch (property_details_.kind()) {
|
|
|
|
case v8::internal::kData:
|
|
|
|
return DATA;
|
|
|
|
case v8::internal::kAccessor:
|
|
|
|
return ACCESSOR;
|
|
|
|
}
|
|
|
|
|
2015-10-05 16:23:37 +00:00
|
|
|
UNREACHABLE();
|
|
|
|
return state_;
|
|
|
|
}
|
|
|
|
|
2016-06-27 11:48:04 +00:00
|
|
|
Handle<InterceptorInfo> LookupIterator::GetInterceptorForFailedAccessCheck()
|
|
|
|
const {
|
|
|
|
DCHECK_EQ(ACCESS_CHECK, state_);
|
|
|
|
DisallowHeapAllocation no_gc;
|
|
|
|
AccessCheckInfo* access_check_info =
|
|
|
|
AccessCheckInfo::Get(isolate_, Handle<JSObject>::cast(holder_));
|
|
|
|
if (access_check_info) {
|
|
|
|
Object* interceptor = IsElement() ? access_check_info->indexed_interceptor()
|
|
|
|
: access_check_info->named_interceptor();
|
|
|
|
if (interceptor) {
|
|
|
|
return handle(InterceptorInfo::cast(interceptor), isolate_);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Handle<InterceptorInfo>();
|
|
|
|
}
|
|
|
|
|
2015-06-01 22:46:54 +00:00
|
|
|
} // namespace internal
|
|
|
|
} // namespace v8
|