Chromium Code Reviews| Index: src/objects.cc |
| diff --git a/src/objects.cc b/src/objects.cc |
| index d5f7f75c18ccb2ea6a9e5139c3b38a2c17a31139..19f877100408bc64f125feba91c5e49af52a596c 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 |
| @@ -606,48 +603,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,32 +2937,33 @@ 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()); |
| - Handle<Object> value_unhole = value->IsTheHole() |
| - ? Handle<Object>(isolate->factory()->undefined_value()) : value; |
| - v8::Handle<v8::Value> result = args.Call(setter, |
| - v8::Utils::ToLocal(name_string), |
| - v8::Utils::ToLocal(value_unhole)); |
| - 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()); |
| + Handle<Object> value_unhole = |
| + value->IsTheHole() |
| + ? Handle<Object>::cast(it->factory()->undefined_value()) |
| + : value; |
| + v8::Handle<v8::Value> result = |
| + args.Call(setter, v8::Utils::ToLocal(name_string), |
| + v8::Utils::ToLocal(value_unhole)); |
| + RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(it->isolate(), Object); |
| + if (!result.IsEmpty()) return value; |
| + |
| + return MaybeHandle<Object>(); |
| } |
| @@ -3006,12 +2972,189 @@ 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() || |
|
Igor Sheludko
2014/07/18 13:08:31
&&
Toon Verwaest
2014/07/18 13:45:22
The original was actually correct. If the holder-i
|
| + !it->GetAccessors()->IsDeclaredAccessorInfo()) { |
| + return SetPropertyWithAccessor(it->GetReceiver(), it->name(), |
| + value, it->GetHolder(), |
| + it->GetAccessors(), strict_mode); |
| + } |
| + // Fall through. |
|
Igor Sheludko
2014/07/18 13:08:31
Wrong identation.
|
| + 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(); |
|
Igor Sheludko
2014/07/18 13:08:31
Should we observe modifications of "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. |
| + if (receiver->GetPrototype()->IsNull()) return value; |
| + receiver = handle(JSGlobalObject::cast(receiver->GetPrototype())); |
| + } |
| + |
| + 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()) { |
|
Igor Sheludko
2014/07/18 13:08:31
Same here.
|
| + JSObject::EnqueueChangeRecord(receiver, "add", it->name(), |
| + it->factory()->the_hole_value()); |
| + } |
| + |
| + return value; |
| } |
| @@ -3052,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()); |
| @@ -3520,23 +3603,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(); |
| @@ -3558,12 +3624,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. |
| @@ -3584,12 +3649,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. |
| @@ -4008,7 +4069,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); |
| } |
| @@ -4046,138 +4107,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) { |
| - 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()) { |
| - Handle<Object> proto(object->GetPrototype(), isolate); |
| - if (proto->IsNull()) return value; |
| - ASSERT(proto->IsJSGlobalObject()); |
| - return SetPropertyForResult(Handle<JSObject>::cast(proto), 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, |
| @@ -4224,8 +4153,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); |
| } |
| } |
| @@ -4297,13 +4226,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()) { |
| @@ -4372,7 +4297,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 +7292,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) { |