Index: src/objects.cc |
diff --git a/src/objects.cc b/src/objects.cc |
index b423560fdff5c394cdc81487709dd8c628d2e3a4..caae61444dd067ae3d379542c14d739d6d29600f 100644 |
--- a/src/objects.cc |
+++ b/src/objects.cc |
@@ -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); |
- } |
- |
- 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); |
+ 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); |
} |
- 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) { |