Reimplement SetProperty using the LookupIterator

BUG=
R=ishell@chromium.org

Review URL: https://codereview.chromium.org/392243002

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22482 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
verwaest@chromium.org 2014-07-18 13:47:25 +00:00
parent 9d2609fe98
commit 7e29b64e27
10 changed files with 508 additions and 410 deletions

View File

@ -1547,7 +1547,7 @@ void Debug::PrepareStep(StepAction step_action,
if (is_load_or_store) {
// Remember source position and frame to handle step in getter/setter. If
// there is a custom getter/setter it will be handled in
// Object::Get/SetPropertyWithCallback, otherwise the step action will be
// Object::Get/SetPropertyWithAccessor, otherwise the step action will be
// propagated on the next Debug::Break.
thread_local_.last_statement_position_ =
debug_info->code()->SourceStatementPosition(frame->pc());

View File

@ -1281,6 +1281,8 @@ MaybeHandle<Object> StoreIC::Store(Handle<Object> object,
Handle<String> name,
Handle<Object> value,
JSReceiver::StoreFromKeyed store_mode) {
// TODO(verwaest): Let SetProperty do the migration, since storing a property
// might deprecate the current map again, if value does not fit.
if (MigrateDeprecated(object) || object->IsJSProxy()) {
Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(object);
Handle<Object> result;
@ -1757,6 +1759,8 @@ KeyedAccessStoreMode KeyedStoreIC::GetStoreMode(Handle<JSObject> receiver,
MaybeHandle<Object> KeyedStoreIC::Store(Handle<Object> object,
Handle<Object> key,
Handle<Object> value) {
// TODO(verwaest): Let SetProperty do the migration, since storing a property
// might deprecate the current map again, if value does not fit.
if (MigrateDeprecated(object)) {
Handle<Object> result;
ASSIGN_RETURN_ON_EXCEPTION(

View File

@ -144,6 +144,78 @@ bool LookupIterator::HasProperty() {
}
void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
ASSERT(has_property_);
ASSERT(HolderIsReceiver());
if (property_encoding_ == DICTIONARY) return;
holder_map_ = Map::PrepareForDataProperty(holder_map_, number_, value);
JSObject::MigrateToMap(GetHolder(), holder_map_);
// Reload property information.
if (holder_map_->is_dictionary_map()) {
property_encoding_ = DICTIONARY;
} else {
property_encoding_ = DESCRIPTOR;
}
CHECK(HasProperty());
}
void LookupIterator::TransitionToDataProperty(
Handle<Object> value, PropertyAttributes attributes,
Object::StoreFromKeyed store_mode) {
ASSERT(!has_property_ || !HolderIsReceiver());
// Can only be called when the receiver is a JSObject. JSProxy has to be
// handled via a trap. Adding properties to primitive values is not
// observable.
Handle<JSObject> receiver = Handle<JSObject>::cast(GetReceiver());
// Properties have to be added to context extension objects through
// SetOwnPropertyIgnoreAttributes.
ASSERT(!receiver->IsJSContextExtensionObject());
if (receiver->IsJSGlobalProxy()) {
PrototypeIterator iter(isolate(), receiver);
receiver =
Handle<JSGlobalObject>::cast(PrototypeIterator::GetCurrent(iter));
}
maybe_holder_ = receiver;
holder_map_ = Map::TransitionToDataProperty(handle(receiver->map()), name_,
value, attributes, store_mode);
JSObject::MigrateToMap(receiver, holder_map_);
// Reload the information.
state_ = NOT_FOUND;
configuration_ = CHECK_OWN_REAL;
state_ = LookupInHolder();
ASSERT(IsFound());
HasProperty();
}
bool LookupIterator::HolderIsReceiver() const {
ASSERT(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
DisallowHeapAllocation no_gc;
Handle<Object> receiver = GetReceiver();
if (!receiver->IsJSReceiver()) return false;
Object* current = *receiver;
JSReceiver* holder = *maybe_holder_.ToHandleChecked();
// JSProxy do not occur as hidden prototypes.
if (current->IsJSProxy()) {
return JSReceiver::cast(current) == holder;
}
PrototypeIterator iter(isolate(), current,
PrototypeIterator::START_AT_RECEIVER);
do {
if (JSReceiver::cast(iter.GetCurrent()) == holder) return true;
ASSERT(!current->IsJSProxy());
iter.Advance();
} while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN));
return false;
}
Handle<Object> LookupIterator::FetchValue() const {
Object* result = NULL;
switch (property_encoding_) {
@ -181,4 +253,29 @@ Handle<Object> LookupIterator::GetDataValue() const {
}
void LookupIterator::WriteDataValue(Handle<Object> value) {
ASSERT(is_guaranteed_to_have_holder());
ASSERT(has_property_);
if (property_encoding_ == DICTIONARY) {
Handle<JSObject> holder = GetHolder();
NameDictionary* property_dictionary = holder->property_dictionary();
if (holder->IsGlobalObject()) {
Handle<PropertyCell> cell(
PropertyCell::cast(property_dictionary->ValueAt(number_)));
PropertyCell::SetValueInferType(cell, value);
} else {
property_dictionary->ValueAtPut(number_, *value);
}
} else if (property_details_.type() == v8::internal::FIELD) {
GetHolder()->WriteToField(number_, *value);
} else {
ASSERT_EQ(v8::internal::CONSTANT, property_details_.type());
}
}
void LookupIterator::InternalizeName() {
if (name_->IsUniqueName()) return;
name_ = factory()->InternalizeString(Handle<String>::cast(name_));
}
} } // namespace v8::internal

View File

@ -92,11 +92,13 @@ class LookupIterator V8_FINAL BASE_EMBEDDED {
Handle<Object> GetReceiver() const {
return Handle<Object>::cast(maybe_receiver_.ToHandleChecked());
}
Handle<Map> holder_map() const { return holder_map_; }
Handle<JSObject> GetHolder() const {
ASSERT(IsFound() && state_ != JSPROXY);
return Handle<JSObject>::cast(maybe_holder_.ToHandleChecked());
}
Handle<JSReceiver> GetRoot() const;
bool HolderIsReceiver() const;
/* Dynamically reduce the trapped types. */
void skip_interceptor() {
@ -116,6 +118,10 @@ class LookupIterator V8_FINAL BASE_EMBEDDED {
// below can be used. It ensures that we are able to provide a definite
// answer, and loads extra information about the property.
bool HasProperty();
void PrepareForDataProperty(Handle<Object> value);
void TransitionToDataProperty(Handle<Object> value,
PropertyAttributes attributes,
Object::StoreFromKeyed store_mode);
PropertyKind property_kind() const {
ASSERT(has_property_);
return property_kind_;
@ -124,11 +130,18 @@ class LookupIterator V8_FINAL BASE_EMBEDDED {
ASSERT(has_property_);
return property_details_;
}
int descriptor_number() const {
ASSERT(has_property_);
ASSERT_EQ(DESCRIPTOR, property_encoding_);
return number_;
}
Handle<Object> GetAccessors() const;
Handle<Object> GetDataValue() const;
void WriteDataValue(Handle<Object> value);
void InternalizeName();
/* JSPROXY */
Handle<JSProxy> GetJSProxy() const {
return Handle<JSProxy>::cast(maybe_holder_.ToHandleChecked());
}

View File

@ -2079,23 +2079,12 @@ bool JSObject::HasFastProperties() {
}
bool JSObject::TooManyFastProperties(StoreFromKeyed store_mode) {
// Allow extra fast properties if the object has more than
// kFastPropertiesSoftLimit in-object properties. When this is the case, it is
// very unlikely that the object is being used as a dictionary and there is a
// good chance that allowing more map transitions will be worth it.
Map* map = this->map();
if (map->unused_property_fields() != 0) return false;
int inobject = map->inobject_properties();
int limit;
if (store_mode == CERTAINLY_NOT_STORE_FROM_KEYED) {
limit = Max(inobject, kMaxFastProperties);
} else {
limit = Max(inobject, kFastPropertiesSoftLimit);
}
return properties()->length() > limit;
bool Map::TooManyFastProperties(StoreFromKeyed store_mode) {
if (unused_property_fields() != 0) return false;
int minimum = store_mode == CERTAINLY_NOT_STORE_FROM_KEYED ? 128 : 12;
int limit = Max(minimum, inobject_properties());
int external = NumberOfFields() - inobject_properties();
return external > limit;
}

View File

@ -462,12 +462,9 @@ MaybeHandle<Object> Object::GetPropertyWithAccessor(Handle<Object> receiver,
}
MaybeHandle<Object> Object::SetPropertyWithCallback(Handle<Object> receiver,
Handle<Name> name,
Handle<Object> value,
Handle<JSObject> holder,
Handle<Object> structure,
StrictMode strict_mode) {
MaybeHandle<Object> Object::SetPropertyWithAccessor(
Handle<Object> receiver, Handle<Name> name, Handle<Object> value,
Handle<JSObject> holder, Handle<Object> structure, StrictMode strict_mode) {
Isolate* isolate = name->GetIsolate();
// We should never get here to initialize a const with the hole
@ -605,48 +602,33 @@ PropertyAttributes JSObject::GetPropertyAttributesWithFailedAccessCheck(
}
static bool FindAllCanWriteHolder(LookupResult* result,
Handle<Name> name,
bool check_prototype) {
if (result->IsInterceptor()) {
result->holder()->LookupOwnRealNamedProperty(name, result);
}
while (result->IsProperty()) {
if (result->type() == CALLBACKS) {
Object* callback_obj = result->GetCallbackObject();
if (callback_obj->IsAccessorInfo()) {
if (AccessorInfo::cast(callback_obj)->all_can_write()) return true;
static bool FindAllCanWriteHolder(LookupIterator* it) {
it->skip_interceptor();
it->skip_access_check();
for (; it->IsFound(); it->Next()) {
if (it->state() == LookupIterator::PROPERTY && it->HasProperty() &&
it->property_kind() == LookupIterator::ACCESSOR) {
Handle<Object> accessors = it->GetAccessors();
if (accessors->IsAccessorInfo()) {
if (AccessorInfo::cast(*accessors)->all_can_write()) return true;
}
}
if (!check_prototype) break;
result->holder()->LookupRealNamedPropertyInPrototypes(name, result);
}
return false;
}
MaybeHandle<Object> JSObject::SetPropertyWithFailedAccessCheck(
Handle<JSObject> object,
LookupResult* result,
Handle<Name> name,
Handle<Object> value,
bool check_prototype,
StrictMode strict_mode) {
if (check_prototype && !result->IsProperty()) {
object->LookupRealNamedPropertyInPrototypes(name, result);
LookupIterator* it, Handle<Object> value, StrictMode strict_mode) {
Handle<JSObject> checked = Handle<JSObject>::cast(it->GetHolder());
if (FindAllCanWriteHolder(it)) {
return SetPropertyWithAccessor(it->GetReceiver(), it->name(), value,
it->GetHolder(), it->GetAccessors(),
strict_mode);
}
if (FindAllCanWriteHolder(result, name, check_prototype)) {
Handle<JSObject> holder(result->holder());
Handle<Object> callbacks(result->GetCallbackObject(), result->isolate());
return SetPropertyWithCallback(
object, name, value, holder, callbacks, strict_mode);
}
Isolate* isolate = object->GetIsolate();
isolate->ReportFailedAccessCheck(object, v8::ACCESS_SET);
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
it->isolate()->ReportFailedAccessCheck(checked, v8::ACCESS_SET);
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(it->isolate(), Object);
return value;
}
@ -1835,7 +1817,7 @@ void JSObject::AddFastProperty(Handle<JSObject> object,
if (value->IsJSFunction()) {
maybe_map = Map::CopyWithConstant(
handle(object->map()), name, value, attributes, flag);
} else if (!object->TooManyFastProperties(store_mode)) {
} else if (!object->map()->TooManyFastProperties(store_mode)) {
Isolate* isolate = object->GetIsolate();
Representation representation = value->OptimalRepresentation();
maybe_map = Map::CopyWithField(
@ -1969,23 +1951,6 @@ void JSObject::EnqueueChangeRecord(Handle<JSObject> object,
}
MaybeHandle<Object> JSObject::SetPropertyPostInterceptor(
Handle<JSObject> object,
Handle<Name> name,
Handle<Object> value,
StrictMode strict_mode) {
// Check own property, ignore interceptor.
Isolate* isolate = object->GetIsolate();
LookupResult result(isolate);
object->LookupOwnRealNamedProperty(name, &result);
if (!result.IsFound()) {
object->map()->LookupTransition(*object, *name, &result);
}
return SetPropertyForResult(object, &result, name, value, strict_mode,
MAY_BE_STORE_FROM_KEYED);
}
static void ReplaceSlowProperty(Handle<JSObject> object,
Handle<Name> name,
Handle<Object> value,
@ -2972,29 +2937,28 @@ MaybeHandle<Map> Map::CurrentMapForDeprecatedInternal(Handle<Map> old_map) {
}
MaybeHandle<Object> JSObject::SetPropertyWithInterceptor(
Handle<JSObject> object,
Handle<Name> name,
Handle<Object> value,
StrictMode strict_mode) {
MaybeHandle<Object> JSObject::SetPropertyWithInterceptor(LookupIterator* it,
Handle<Object> value) {
// TODO(rossberg): Support symbols in the API.
if (name->IsSymbol()) return value;
Isolate* isolate = object->GetIsolate();
Handle<String> name_string = Handle<String>::cast(name);
Handle<InterceptorInfo> interceptor(object->GetNamedInterceptor());
if (!interceptor->setter()->IsUndefined()) {
LOG(isolate,
ApiNamedPropertyAccess("interceptor-named-set", *object, *name));
PropertyCallbackArguments args(
isolate, interceptor->data(), *object, *object);
v8::NamedPropertySetterCallback setter =
v8::ToCData<v8::NamedPropertySetterCallback>(interceptor->setter());
v8::Handle<v8::Value> result = args.Call(
setter, v8::Utils::ToLocal(name_string), v8::Utils::ToLocal(value));
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
if (!result.IsEmpty()) return value;
}
return SetPropertyPostInterceptor(object, name, value, strict_mode);
if (it->name()->IsSymbol()) return value;
Handle<String> name_string = Handle<String>::cast(it->name());
Handle<JSObject> holder = it->GetHolder();
Handle<InterceptorInfo> interceptor(holder->GetNamedInterceptor());
if (interceptor->setter()->IsUndefined()) return MaybeHandle<Object>();
LOG(it->isolate(),
ApiNamedPropertyAccess("interceptor-named-set", *holder, *name_string));
PropertyCallbackArguments args(it->isolate(), interceptor->data(), *holder,
*holder);
v8::NamedPropertySetterCallback setter =
v8::ToCData<v8::NamedPropertySetterCallback>(interceptor->setter());
v8::Handle<v8::Value> result = args.Call(
setter, v8::Utils::ToLocal(name_string), v8::Utils::ToLocal(value));
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(it->isolate(), Object);
if (!result.IsEmpty()) return value;
return MaybeHandle<Object>();
}
@ -3003,12 +2967,194 @@ MaybeHandle<Object> JSReceiver::SetProperty(Handle<JSReceiver> object,
Handle<Object> value,
StrictMode strict_mode,
StoreFromKeyed store_mode) {
LookupResult result(object->GetIsolate());
object->LookupOwn(name, &result, true);
if (!result.IsFound()) {
object->map()->LookupTransition(JSObject::cast(*object), *name, &result);
LookupIterator it(object, name);
return Object::SetProperty(&it, value, strict_mode, store_mode);
}
MaybeHandle<Object> Object::SetProperty(LookupIterator* it,
Handle<Object> value,
StrictMode strict_mode,
StoreFromKeyed store_mode) {
// Make sure that the top context does not change when doing callbacks or
// interceptor calls.
AssertNoContextChange ncc(it->isolate());
bool done = false;
for (; it->IsFound(); it->Next()) {
switch (it->state()) {
case LookupIterator::NOT_FOUND:
UNREACHABLE();
case LookupIterator::ACCESS_CHECK:
// TODO(verwaest): Remove the distinction. This is mostly bogus since we
// don't know whether we'll want to fetch attributes or call a setter
// until we find the property.
if (it->HasAccess(v8::ACCESS_SET)) break;
return JSObject::SetPropertyWithFailedAccessCheck(it, value,
strict_mode);
case LookupIterator::JSPROXY:
if (it->HolderIsReceiver()) {
return JSProxy::SetPropertyWithHandler(it->GetJSProxy(),
it->GetReceiver(), it->name(),
value, strict_mode);
} else {
// TODO(verwaest): Use the MaybeHandle to indicate result.
bool has_result = false;
MaybeHandle<Object> maybe_result =
JSProxy::SetPropertyViaPrototypesWithHandler(
it->GetJSProxy(), it->GetReceiver(), it->name(), value,
strict_mode, &has_result);
if (has_result) return maybe_result;
done = true;
}
break;
case LookupIterator::INTERCEPTOR:
if (it->HolderIsReceiver()) {
MaybeHandle<Object> maybe_result =
JSObject::SetPropertyWithInterceptor(it, value);
if (!maybe_result.is_null()) return maybe_result;
if (it->isolate()->has_pending_exception()) return maybe_result;
} else {
Maybe<PropertyAttributes> maybe_attributes =
JSObject::GetPropertyAttributesWithInterceptor(
it->GetHolder(), it->GetReceiver(), it->name());
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(it->isolate(), Object);
done = maybe_attributes.has_value;
if (done && (maybe_attributes.value & READ_ONLY) != 0) {
return WriteToReadOnlyProperty(it, value, strict_mode);
}
}
break;
case LookupIterator::PROPERTY:
if (!it->HasProperty()) break;
if (it->property_details().IsReadOnly()) {
return WriteToReadOnlyProperty(it, value, strict_mode);
}
switch (it->property_kind()) {
case LookupIterator::ACCESSOR:
if (it->HolderIsReceiver() ||
!it->GetAccessors()->IsDeclaredAccessorInfo()) {
return SetPropertyWithAccessor(it->GetReceiver(), it->name(),
value, it->GetHolder(),
it->GetAccessors(), strict_mode);
}
break;
case LookupIterator::DATA:
if (it->HolderIsReceiver()) return SetDataProperty(it, value);
}
done = true;
break;
}
if (done) break;
}
return SetProperty(object, &result, name, value, strict_mode, store_mode);
return AddDataProperty(it, value, NONE, strict_mode, store_mode);
}
MaybeHandle<Object> Object::WriteToReadOnlyProperty(LookupIterator* it,
Handle<Object> value,
StrictMode strict_mode) {
if (strict_mode != STRICT) return value;
Handle<Object> args[] = {it->name(), it->GetReceiver()};
Handle<Object> error = it->factory()->NewTypeError(
"strict_read_only_property", HandleVector(args, ARRAY_SIZE(args)));
return it->isolate()->Throw<Object>(error);
}
MaybeHandle<Object> Object::SetDataProperty(LookupIterator* it,
Handle<Object> value) {
// Proxies are handled on the WithHandler path. Other non-JSObjects cannot
// have own properties.
Handle<JSObject> receiver = Handle<JSObject>::cast(it->GetReceiver());
// Store on the holder which may be hidden behind the receiver.
ASSERT(it->HolderIsReceiver());
// Old value for the observation change record.
// Fetch before transforming the object since the encoding may become
// incompatible with what's cached in |it|.
bool is_observed =
receiver->map()->is_observed() &&
!it->name().is_identical_to(it->factory()->hidden_string());
MaybeHandle<Object> maybe_old;
if (is_observed) maybe_old = it->GetDataValue();
// Possibly migrate to the most up-to-date map that will be able to store
// |value| under it->name().
it->PrepareForDataProperty(value);
// Write the property value.
it->WriteDataValue(value);
// Send the change record if there are observers.
if (is_observed && !value->SameValue(*maybe_old.ToHandleChecked())) {
JSObject::EnqueueChangeRecord(receiver, "update", it->name(),
maybe_old.ToHandleChecked());
}
return value;
}
MaybeHandle<Object> Object::AddDataProperty(LookupIterator* it,
Handle<Object> value,
PropertyAttributes attributes,
StrictMode strict_mode,
StoreFromKeyed store_mode) {
ASSERT(!it->GetReceiver()->IsJSProxy());
// Transitions to data properties of value wrappers are not observable.
if (!it->GetReceiver()->IsJSObject()) return value;
Handle<JSObject> receiver = Handle<JSObject>::cast(it->GetReceiver());
// If the receiver is a JSGlobalProxy, store on the prototype (JSGlobalObject)
// instead. If the prototype is Null, the proxy is detached.
if (receiver->IsJSGlobalProxy()) {
// Trying to assign to a detached proxy.
PrototypeIterator iter(it->isolate(), receiver);
if (iter.IsAtEnd()) return value;
receiver =
Handle<JSGlobalObject>::cast(PrototypeIterator::GetCurrent(iter));
}
if (!receiver->map()->is_extensible()) {
if (strict_mode == SLOPPY) return value;
Handle<Object> args[1] = {it->name()};
Handle<Object> error = it->factory()->NewTypeError(
"object_not_extensible", HandleVector(args, ARRAY_SIZE(args)));
return it->isolate()->Throw<Object>(error);
}
// Possibly migrate to the most up-to-date map that will be able to store
// |value| under it->name() with |attributes|.
it->TransitionToDataProperty(value, attributes, store_mode);
// TODO(verwaest): Encapsulate dictionary handling better.
if (receiver->map()->is_dictionary_map()) {
// TODO(verwaest): Probably should ensure this is done beforehand.
it->InternalizeName();
JSObject::AddSlowProperty(receiver, it->name(), value, attributes);
} else {
// Write the property value.
it->WriteDataValue(value);
}
// Send the change record if there are observers.
if (receiver->map()->is_observed() &&
!it->name().is_identical_to(it->factory()->hidden_string())) {
JSObject::EnqueueChangeRecord(receiver, "add", it->name(),
it->factory()->the_hole_value());
}
return value;
}
@ -3049,66 +3195,6 @@ MaybeHandle<Object> JSObject::SetElementWithCallbackSetterInPrototypes(
}
MaybeHandle<Object> JSObject::SetPropertyViaPrototypes(
Handle<JSObject> object,
Handle<Name> name,
Handle<Object> value,
StrictMode strict_mode,
bool* done) {
Isolate* isolate = object->GetIsolate();
*done = false;
// We could not find an own property, so let's check whether there is an
// accessor that wants to handle the property, or whether the property is
// read-only on the prototype chain.
LookupResult result(isolate);
object->LookupRealNamedPropertyInPrototypes(name, &result);
if (result.IsFound()) {
switch (result.type()) {
case NORMAL:
case FIELD:
case CONSTANT:
*done = result.IsReadOnly();
break;
case INTERCEPTOR: {
LookupIterator it(object, name, handle(result.holder()));
PropertyAttributes attr = GetPropertyAttributes(&it);
*done = !!(attr & READ_ONLY);
break;
}
case CALLBACKS: {
*done = true;
if (!result.IsReadOnly()) {
Handle<Object> callback_object(result.GetCallbackObject(), isolate);
return SetPropertyWithCallback(object, name, value,
handle(result.holder()),
callback_object, strict_mode);
}
break;
}
case HANDLER: {
Handle<JSProxy> proxy(result.proxy());
return JSProxy::SetPropertyViaPrototypesWithHandler(
proxy, object, name, value, strict_mode, done);
}
case NONEXISTENT:
UNREACHABLE();
break;
}
}
// If we get here with *done true, we have encountered a read-only property.
if (*done) {
if (strict_mode == SLOPPY) return value;
Handle<Object> args[] = { name, object };
Handle<Object> error = isolate->factory()->NewTypeError(
"strict_read_only_property", HandleVector(args, ARRAY_SIZE(args)));
return isolate->Throw<Object>(error);
}
return isolate->factory()->the_hole_value();
}
void Map::EnsureDescriptorSlack(Handle<Map> map, int slack) {
// Only supports adding slack to owned descriptors.
ASSERT(map->owns_descriptors());
@ -3508,23 +3594,6 @@ void JSObject::LookupRealNamedPropertyInPrototypes(Handle<Name> name,
}
MaybeHandle<Object> JSReceiver::SetProperty(Handle<JSReceiver> object,
LookupResult* result,
Handle<Name> key,
Handle<Object> value,
StrictMode strict_mode,
StoreFromKeyed store_mode) {
if (result->IsHandler()) {
return JSProxy::SetPropertyWithHandler(handle(result->proxy()), object, key,
value, strict_mode);
} else {
return JSObject::SetPropertyForResult(Handle<JSObject>::cast(object),
result, key, value, strict_mode,
store_mode);
}
}
bool JSProxy::HasPropertyWithHandler(Handle<JSProxy> proxy, Handle<Name> name) {
Isolate* isolate = proxy->GetIsolate();
@ -3546,12 +3615,11 @@ bool JSProxy::HasPropertyWithHandler(Handle<JSProxy> proxy, Handle<Name> name) {
}
MaybeHandle<Object> JSProxy::SetPropertyWithHandler(
Handle<JSProxy> proxy,
Handle<JSReceiver> receiver,
Handle<Name> name,
Handle<Object> value,
StrictMode strict_mode) {
MaybeHandle<Object> JSProxy::SetPropertyWithHandler(Handle<JSProxy> proxy,
Handle<Object> receiver,
Handle<Name> name,
Handle<Object> value,
StrictMode strict_mode) {
Isolate* isolate = proxy->GetIsolate();
// TODO(rossberg): adjust once there is a story for symbols vs proxies.
@ -3572,12 +3640,8 @@ MaybeHandle<Object> JSProxy::SetPropertyWithHandler(
MaybeHandle<Object> JSProxy::SetPropertyViaPrototypesWithHandler(
Handle<JSProxy> proxy,
Handle<JSReceiver> receiver,
Handle<Name> name,
Handle<Object> value,
StrictMode strict_mode,
bool* done) {
Handle<JSProxy> proxy, Handle<Object> receiver, Handle<Name> name,
Handle<Object> value, StrictMode strict_mode, bool* done) {
Isolate* isolate = proxy->GetIsolate();
Handle<Object> handler(proxy->handler(), isolate); // Trap might morph proxy.
@ -3996,7 +4060,7 @@ void JSObject::ConvertAndSetOwnProperty(LookupResult* lookup,
Handle<Object> value,
PropertyAttributes attributes) {
Handle<JSObject> object(lookup->holder());
if (object->TooManyFastProperties()) {
if (object->map()->TooManyFastProperties(Object::MAY_BE_STORE_FROM_KEYED)) {
JSObject::NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0);
}
@ -4034,140 +4098,6 @@ void JSObject::SetPropertyToFieldWithAttributes(LookupResult* lookup,
}
MaybeHandle<Object> JSObject::SetPropertyForResult(
Handle<JSObject> object,
LookupResult* lookup,
Handle<Name> name,
Handle<Object> value,
StrictMode strict_mode,
StoreFromKeyed store_mode) {
ASSERT(!value->IsTheHole());
Isolate* isolate = object->GetIsolate();
// Make sure that the top context does not change when doing callbacks or
// interceptor calls.
AssertNoContextChange ncc(isolate);
// Optimization for 2-byte strings often used as keys in a decompression
// dictionary. We internalize these short keys to avoid constantly
// reallocating them.
if (name->IsString() && !name->IsInternalizedString() &&
Handle<String>::cast(name)->length() <= 2) {
name = isolate->factory()->InternalizeString(Handle<String>::cast(name));
}
// Check access rights if needed.
if (object->IsAccessCheckNeeded()) {
if (!isolate->MayNamedAccess(object, name, v8::ACCESS_SET)) {
return SetPropertyWithFailedAccessCheck(object, lookup, name, value,
true, strict_mode);
}
}
if (object->IsJSGlobalProxy()) {
PrototypeIterator iter(isolate, object);
if (iter.IsAtEnd()) return value;
ASSERT(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject());
return SetPropertyForResult(
Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)), lookup,
name, value, strict_mode, store_mode);
}
ASSERT(!lookup->IsFound() || lookup->holder() == *object ||
lookup->holder()->map()->is_hidden_prototype());
if (!lookup->IsProperty() && !object->IsJSContextExtensionObject()) {
bool done = false;
Handle<Object> result_object;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, result_object,
SetPropertyViaPrototypes(object, name, value, strict_mode, &done),
Object);
if (done) return result_object;
}
if (!lookup->IsFound()) {
// Neither properties nor transitions found.
return AddPropertyInternal(object, name, value, NONE, strict_mode,
store_mode);
}
if (lookup->IsProperty() && lookup->IsReadOnly()) {
if (strict_mode == STRICT) {
Handle<Object> args[] = { name, object };
Handle<Object> error = isolate->factory()->NewTypeError(
"strict_read_only_property", HandleVector(args, ARRAY_SIZE(args)));
return isolate->Throw<Object>(error);
} else {
return value;
}
}
Handle<Object> old_value = isolate->factory()->the_hole_value();
bool is_observed = object->map()->is_observed() &&
*name != isolate->heap()->hidden_string();
if (is_observed && lookup->IsDataProperty()) {
old_value = Object::GetPropertyOrElement(object, name).ToHandleChecked();
}
// This is a real property that is not read-only, or it is a
// transition or null descriptor and there are no setters in the prototypes.
MaybeHandle<Object> maybe_result = value;
if (lookup->IsTransition()) {
maybe_result = SetPropertyUsingTransition(handle(lookup->holder()), lookup,
name, value, NONE);
} else {
switch (lookup->type()) {
case NORMAL:
SetNormalizedProperty(handle(lookup->holder()), lookup, value);
break;
case FIELD:
SetPropertyToField(lookup, value);
break;
case CONSTANT:
// Only replace the constant if necessary.
if (*value == lookup->GetConstant()) return value;
SetPropertyToField(lookup, value);
break;
case CALLBACKS: {
Handle<Object> callback_object(lookup->GetCallbackObject(), isolate);
return SetPropertyWithCallback(object, name, value,
handle(lookup->holder()),
callback_object, strict_mode);
}
case INTERCEPTOR:
maybe_result = SetPropertyWithInterceptor(handle(lookup->holder()),
name, value, strict_mode);
break;
case HANDLER:
case NONEXISTENT:
UNREACHABLE();
}
}
Handle<Object> result;
ASSIGN_RETURN_ON_EXCEPTION(isolate, result, maybe_result, Object);
if (is_observed) {
if (lookup->IsTransition()) {
EnqueueChangeRecord(object, "add", name, old_value);
} else {
LookupResult new_lookup(isolate);
object->LookupOwn(name, &new_lookup, true);
if (new_lookup.IsDataProperty()) {
Handle<Object> new_value =
Object::GetPropertyOrElement(object, name).ToHandleChecked();
if (!new_value->SameValue(*old_value)) {
EnqueueChangeRecord(object, "update", name, old_value);
}
}
}
}
return result;
}
void JSObject::AddProperty(
Handle<JSObject> object,
Handle<Name> name,
@ -4215,8 +4145,8 @@ MaybeHandle<Object> JSObject::SetOwnPropertyIgnoreAttributes(
// Check access rights if needed.
if (object->IsAccessCheckNeeded()) {
if (!isolate->MayNamedAccess(object, name, v8::ACCESS_SET)) {
return SetPropertyWithFailedAccessCheck(object, &lookup, name, value,
false, SLOPPY);
LookupIterator it(object, name, LookupIterator::CHECK_OWN);
return SetPropertyWithFailedAccessCheck(&it, value, SLOPPY);
}
}
@ -4288,13 +4218,9 @@ MaybeHandle<Object> JSObject::SetOwnPropertyIgnoreAttributes(
handling == DONT_FORCE_FIELD) {
Handle<Object> result;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, result,
JSObject::SetPropertyWithCallback(object,
name,
value,
handle(lookup.holder()),
callback,
STRICT),
isolate, result, JSObject::SetPropertyWithAccessor(
object, name, value, handle(lookup.holder()),
callback, STRICT),
Object);
if (attributes != lookup.GetAttributes()) {
@ -4363,7 +4289,7 @@ Maybe<PropertyAttributes> JSObject::GetPropertyAttributesWithInterceptor(
Handle<Object> receiver,
Handle<Name> name) {
// TODO(rossberg): Support symbols in the API.
if (name->IsSymbol()) return Maybe<PropertyAttributes>(ABSENT);
if (name->IsSymbol()) return Maybe<PropertyAttributes>();
Isolate* isolate = holder->GetIsolate();
HandleScope scope(isolate);
@ -7367,6 +7293,97 @@ Handle<Map> Map::CopyForFreeze(Handle<Map> map) {
}
bool DescriptorArray::CanHoldValue(int descriptor, Object* value) {
PropertyDetails details = GetDetails(descriptor);
switch (details.type()) {
case FIELD:
return value->FitsRepresentation(details.representation()) &&
GetFieldType(descriptor)->NowContains(value);
case CONSTANT:
ASSERT(GetConstant(descriptor) != value ||
value->FitsRepresentation(details.representation()));
return GetConstant(descriptor) == value;
case CALLBACKS:
return false;
case NORMAL:
case INTERCEPTOR:
case HANDLER:
case NONEXISTENT:
break;
}
UNREACHABLE();
return false;
}
Handle<Map> Map::PrepareForDataProperty(Handle<Map> map, int descriptor,
Handle<Object> value) {
// Dictionaries can store any property value.
if (map->is_dictionary_map()) return map;
Handle<DescriptorArray> descriptors(map->instance_descriptors());
if (descriptors->CanHoldValue(descriptor, *value)) return map;
Isolate* isolate = map->GetIsolate();
Representation representation = value->OptimalRepresentation();
Handle<HeapType> type = value->OptimalType(isolate, representation);
return GeneralizeRepresentation(map, descriptor, representation, type,
FORCE_FIELD);
}
Handle<Map> Map::TransitionToDataProperty(Handle<Map> map, Handle<Name> name,
Handle<Object> value,
PropertyAttributes attributes,
StoreFromKeyed store_mode) {
// Cannot currently handle deprecated maps.
ASSERT(!map->is_deprecated());
// Dictionary maps can always have additional data properties.
if (map->is_dictionary_map()) return map;
int index = map->SearchTransition(*name);
if (index != TransitionArray::kNotFound) {
Handle<Map> transition(map->GetTransition(index));
int descriptor = transition->LastAdded();
// TODO(verwaest): Handle attributes better.
DescriptorArray* descriptors = transition->instance_descriptors();
if (descriptors->GetDetails(descriptor).attributes() != attributes) {
return CopyGeneralizeAllRepresentations(transition, descriptor,
FORCE_FIELD, attributes,
"attributes mismatch");
}
return Map::PrepareForDataProperty(transition, descriptor, value);
}
TransitionFlag flag = INSERT_TRANSITION;
MaybeHandle<Map> maybe_map;
if (value->IsJSFunction()) {
maybe_map = Map::CopyWithConstant(map, name, value, attributes, flag);
} else if (!map->TooManyFastProperties(store_mode)) {
Isolate* isolate = name->GetIsolate();
Representation representation = value->OptimalRepresentation();
Handle<HeapType> type = value->OptimalType(isolate, representation);
maybe_map =
Map::CopyWithField(map, name, type, attributes, representation, flag);
}
Handle<Map> result;
if (!maybe_map.ToHandle(&result)) {
return Map::Normalize(map, CLEAR_INOBJECT_PROPERTIES);
}
return result;
}
Handle<Map> Map::CopyAddDescriptor(Handle<Map> map,
Descriptor* descriptor,
TransitionFlag flag) {

View File

@ -1375,6 +1375,13 @@ class Object {
HEAP_OBJECT_TYPE_LIST(IS_TYPE_FUNCTION_DECL)
#undef IS_TYPE_FUNCTION_DECL
// A non-keyed store is of the form a.x = foo or a["x"] = foo whereas
// a keyed store is of the form a[expression] = foo.
enum StoreFromKeyed {
MAY_BE_STORE_FROM_KEYED,
CERTAINLY_NOT_STORE_FROM_KEYED
};
INLINE(bool IsFixedArrayBase() const);
INLINE(bool IsExternal() const);
INLINE(bool IsAccessorInfo() const);
@ -1476,6 +1483,16 @@ class Object {
void Lookup(Handle<Name> name, LookupResult* result);
MUST_USE_RESULT static MaybeHandle<Object> GetProperty(LookupIterator* it);
MUST_USE_RESULT static MaybeHandle<Object> SetProperty(
LookupIterator* it, Handle<Object> value, StrictMode strict_mode,
StoreFromKeyed store_mode);
MUST_USE_RESULT static MaybeHandle<Object> WriteToReadOnlyProperty(
LookupIterator* it, Handle<Object> value, StrictMode strict_mode);
MUST_USE_RESULT static MaybeHandle<Object> SetDataProperty(
LookupIterator* it, Handle<Object> value);
MUST_USE_RESULT static MaybeHandle<Object> AddDataProperty(
LookupIterator* it, Handle<Object> value, PropertyAttributes attributes,
StrictMode strict_mode, StoreFromKeyed store_mode);
MUST_USE_RESULT static inline MaybeHandle<Object> GetPropertyOrElement(
Handle<Object> object,
Handle<Name> key);
@ -1492,12 +1509,9 @@ class Object {
Handle<Name> name,
Handle<JSObject> holder,
Handle<Object> structure);
MUST_USE_RESULT static MaybeHandle<Object> SetPropertyWithCallback(
Handle<Object> receiver,
Handle<Name> name,
Handle<Object> value,
Handle<JSObject> holder,
Handle<Object> structure,
MUST_USE_RESULT static MaybeHandle<Object> SetPropertyWithAccessor(
Handle<Object> receiver, Handle<Name> name, Handle<Object> value,
Handle<JSObject> holder, Handle<Object> structure,
StrictMode strict_mode);
MUST_USE_RESULT static MaybeHandle<Object> GetPropertyWithDefinedGetter(
@ -1918,13 +1932,6 @@ class JSReceiver: public HeapObject {
FORCE_DELETION
};
// A non-keyed store is of the form a.x = foo or a["x"] = foo whereas
// a keyed store is of the form a[expression] = foo.
enum StoreFromKeyed {
MAY_BE_STORE_FROM_KEYED,
CERTAINLY_NOT_STORE_FROM_KEYED
};
// Internal properties (e.g. the hidden properties dictionary) might
// be added even though the receiver is non-extensible.
enum ExtensibilityCheck {
@ -2136,18 +2143,7 @@ class JSObject: public JSReceiver {
uint32_t limit);
MUST_USE_RESULT static MaybeHandle<Object> SetPropertyWithInterceptor(
Handle<JSObject> object,
Handle<Name> name,
Handle<Object> value,
StrictMode strict_mode);
MUST_USE_RESULT static MaybeHandle<Object> SetPropertyForResult(
Handle<JSObject> object,
LookupResult* result,
Handle<Name> name,
Handle<Object> value,
StrictMode strict_mode,
StoreFromKeyed store_mode = MAY_BE_STORE_FROM_KEYED);
LookupIterator* it, Handle<Object> value);
// SetLocalPropertyIgnoreAttributes converts callbacks to fields. We need to
// grant an exemption to ExecutableAccessor callbacks in some cases.
@ -2573,12 +2569,6 @@ class JSObject: public JSReceiver {
Object* SlowReverseLookup(Object* value);
// Maximal number of fast properties for the JSObject. Used to
// restrict the number of map transitions to avoid an explosion in
// the number of maps for objects used as dictionaries.
inline bool TooManyFastProperties(
StoreFromKeyed store_mode = MAY_BE_STORE_FROM_KEYED);
// Maximal number of elements (numbered 0 .. kMaxElementCount - 1).
// Also maximal value of JSArray's length property.
static const uint32_t kMaxElementCount = 0xffffffffu;
@ -2606,8 +2596,6 @@ class JSObject: public JSReceiver {
// "global.Object" and not to arbitrary other JSObject maps.
static const int kInitialGlobalObjectUnusedPropertiesCount = 4;
static const int kFastPropertiesSoftLimit = 12;
static const int kMaxFastProperties = 128;
static const int kMaxInstanceSize = 255 * kPointerSize;
// When extending the backing storage for property values, we increase
// its size by more than the 1 entry necessary, so sequentially adding fields
@ -2734,21 +2722,6 @@ class JSObject: public JSReceiver {
StrictMode strict_mode,
bool check_prototype = true);
// Searches the prototype chain for property 'name'. If it is found and
// has a setter, invoke it and set '*done' to true. If it is found and is
// read-only, reject and set '*done' to true. Otherwise, set '*done' to
// false. Can throw and return an empty handle with '*done==true'.
MUST_USE_RESULT static MaybeHandle<Object> SetPropertyViaPrototypes(
Handle<JSObject> object,
Handle<Name> name,
Handle<Object> value,
StrictMode strict_mode,
bool* done);
MUST_USE_RESULT static MaybeHandle<Object> SetPropertyPostInterceptor(
Handle<JSObject> object,
Handle<Name> name,
Handle<Object> value,
StrictMode strict_mode);
MUST_USE_RESULT static MaybeHandle<Object> SetPropertyUsingTransition(
Handle<JSObject> object,
LookupResult* lookup,
@ -2756,12 +2729,7 @@ class JSObject: public JSReceiver {
Handle<Object> value,
PropertyAttributes attributes);
MUST_USE_RESULT static MaybeHandle<Object> SetPropertyWithFailedAccessCheck(
Handle<JSObject> object,
LookupResult* result,
Handle<Name> name,
Handle<Object> value,
bool check_prototype,
StrictMode strict_mode);
LookupIterator* it, Handle<Object> value, StrictMode strict_mode);
// Add a property to an object.
MUST_USE_RESULT static MaybeHandle<Object> AddPropertyInternal(
@ -3452,6 +3420,8 @@ class DescriptorArray: public FixedArray {
FixedArray* new_cache,
Object* new_index_cache);
bool CanHoldValue(int descriptor, Object* value);
// Accessors for fetching instance descriptor at descriptor number.
inline Name* GetKey(int descriptor_number);
inline Object** GetKeySlot(int descriptor_number);
@ -6334,6 +6304,10 @@ class Map: public HeapObject {
StoreMode store_mode,
const char* reason);
static Handle<Map> PrepareForDataProperty(Handle<Map> old_map,
int descriptor_number,
Handle<Object> value);
static Handle<Map> Normalize(Handle<Map> map, PropertyNormalizationMode mode);
// Returns the constructor name (the name (possibly, inferred name) of the
@ -6526,6 +6500,15 @@ class Map: public HeapObject {
static Handle<Map> CopyForObserved(Handle<Map> map);
static Handle<Map> CopyForFreeze(Handle<Map> map);
// Maximal number of fast properties. Used to restrict the number of map
// transitions to avoid an explosion in the number of maps for objects used as
// dictionaries.
inline bool TooManyFastProperties(StoreFromKeyed store_mode);
static Handle<Map> TransitionToDataProperty(Handle<Map> map,
Handle<Name> name,
Handle<Object> value,
PropertyAttributes attributes,
StoreFromKeyed store_mode);
inline void AppendDescriptor(Descriptor* desc);
@ -6834,6 +6817,9 @@ class Map: public HeapObject {
Handle<Object> prototype,
Handle<Map> target_map);
static const int kFastPropertiesSoftLimit = 12;
static const int kMaxFastProperties = 128;
DISALLOW_IMPLICIT_CONSTRUCTORS(Map);
};
@ -9968,12 +9954,8 @@ class JSProxy: public JSReceiver {
// otherwise set it to false.
MUST_USE_RESULT
static MaybeHandle<Object> SetPropertyViaPrototypesWithHandler(
Handle<JSProxy> proxy,
Handle<JSReceiver> receiver,
Handle<Name> name,
Handle<Object> value,
StrictMode strict_mode,
bool* done);
Handle<JSProxy> proxy, Handle<Object> receiver, Handle<Name> name,
Handle<Object> value, StrictMode strict_mode, bool* done);
static PropertyAttributes GetPropertyAttributesWithHandler(
Handle<JSProxy> proxy,
@ -9983,6 +9965,9 @@ class JSProxy: public JSReceiver {
Handle<JSProxy> proxy,
Handle<JSReceiver> receiver,
uint32_t index);
MUST_USE_RESULT static MaybeHandle<Object> SetPropertyWithHandler(
Handle<JSProxy> proxy, Handle<Object> receiver, Handle<Name> name,
Handle<Object> value, StrictMode strict_mode);
// Turn the proxy into an (empty) JSObject.
static void Fix(Handle<JSProxy> proxy);
@ -10022,12 +10007,6 @@ class JSProxy: public JSReceiver {
private:
friend class JSReceiver;
MUST_USE_RESULT static MaybeHandle<Object> SetPropertyWithHandler(
Handle<JSProxy> proxy,
Handle<JSReceiver> receiver,
Handle<Name> name,
Handle<Object> value,
StrictMode strict_mode);
MUST_USE_RESULT static inline MaybeHandle<Object> SetElementWithHandler(
Handle<JSProxy> proxy,
Handle<JSReceiver> receiver,

View File

@ -581,8 +581,8 @@ RUNTIME_FUNCTION(StoreInterceptorProperty) {
ASSERT(receiver->HasNamedInterceptor());
Handle<Object> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result, JSObject::SetPropertyWithInterceptor(
receiver, name, value, ic.strict_mode()));
isolate, result,
JSObject::SetProperty(receiver, name, value, ic.strict_mode()));
return *result;
}

View File

@ -1322,7 +1322,9 @@ function ObjectIsSealed(obj) {
for (var i = 0; i < names.length; i++) {
var name = names[i];
var desc = GetOwnPropertyJS(obj, name);
if (desc.isConfigurable()) return false;
if (desc.isConfigurable()) {
return false;
}
}
return true;
}

View File

@ -423,11 +423,8 @@ TEST(ExistsInPrototype) {
// Sanity check to make sure that the holder of the interceptor
// really is the prototype object.
{ ExistsInPrototypeContext context;
context.Check("this.x = 87; this.x",
0,
0,
0,
EXPECT_RESULT, Number::New(CcTest::isolate(), 87));
context.Check("this.x = 87; this.x", 0, 0, 1, EXPECT_RESULT,
Number::New(CcTest::isolate(), 87));
}
{ ExistsInPrototypeContext context;