Chromium Code Reviews| Index: src/objects.cc |
| diff --git a/src/objects.cc b/src/objects.cc |
| index 249597bc1ccf0fc30556043634c6ce48927de3c4..6c189b428a095798ffa22a3391e7880e11f445d6 100644 |
| --- a/src/objects.cc |
| +++ b/src/objects.cc |
| @@ -307,12 +307,16 @@ MaybeHandle<Object> Object::GetPropertyWithAccessor(LookupIterator* it) { |
| Handle<Object> structure = it->GetAccessors(); |
| Handle<Object> receiver = it->GetReceiver(); |
| + // We should never get here to initialize a const with the hole value since a |
| + // const declaration would conflict with the getter. |
| DCHECK(!structure->IsForeign()); |
| - // api style callbacks. |
| + |
| + // API style callbacks. |
| if (structure->IsAccessorInfo()) { |
|
Igor Sheludko
2015/06/11 13:44:25
IsExecutableAccessorInfo()?
Toon Verwaest
2015/06/11 14:26:50
It's in a weird state since it's the only subclass
|
| Handle<JSObject> holder = it->GetHolder<JSObject>(); |
| Handle<Name> name = it->GetName(); |
| - Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(structure); |
| + Handle<ExecutableAccessorInfo> info = |
| + Handle<ExecutableAccessorInfo>::cast(structure); |
| if (!info->IsCompatibleReceiver(*receiver)) { |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, |
| @@ -320,14 +324,12 @@ MaybeHandle<Object> Object::GetPropertyWithAccessor(LookupIterator* it) { |
| Object); |
| } |
| - Handle<ExecutableAccessorInfo> data = |
| - Handle<ExecutableAccessorInfo>::cast(structure); |
| v8::AccessorNameGetterCallback call_fun = |
| - v8::ToCData<v8::AccessorNameGetterCallback>(data->getter()); |
| - if (call_fun == NULL) return isolate->factory()->undefined_value(); |
| + v8::ToCData<v8::AccessorNameGetterCallback>(info->getter()); |
| + if (call_fun == nullptr) return isolate->factory()->undefined_value(); |
| LOG(isolate, ApiNamedPropertyAccess("load", *holder, *name)); |
| - PropertyCallbackArguments args(isolate, data->data(), *receiver, *holder); |
| + PropertyCallbackArguments args(isolate, info->data(), *receiver, *holder); |
| v8::Handle<v8::Value> result = |
| args.Call(call_fun, v8::Utils::ToLocal(name)); |
| RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); |
| @@ -340,9 +342,8 @@ MaybeHandle<Object> Object::GetPropertyWithAccessor(LookupIterator* it) { |
| return handle(*return_value, isolate); |
| } |
| - // __defineGetter__ callback |
| - Handle<Object> getter(Handle<AccessorPair>::cast(structure)->getter(), |
| - isolate); |
| + // Regular accessor. |
| + Handle<Object> getter(AccessorPair::cast(*structure)->getter(), isolate); |
| if (getter->IsSpecFunction()) { |
| // TODO(rossberg): nicer would be to cast to some JSCallable here... |
| return Object::GetPropertyWithDefinedGetter( |
| @@ -364,55 +365,53 @@ bool AccessorInfo::IsCompatibleReceiverMap(Isolate* isolate, |
| MaybeHandle<Object> Object::SetPropertyWithAccessor( |
| - Handle<Object> receiver, Handle<Name> name, Handle<Object> value, |
| - Handle<JSObject> holder, Handle<Object> structure, |
| - LanguageMode language_mode) { |
| - Isolate* isolate = name->GetIsolate(); |
| + LookupIterator* it, Handle<Object> value, LanguageMode language_mode) { |
| + Isolate* isolate = it->isolate(); |
| + Handle<Object> structure = it->GetAccessors(); |
| + Handle<Object> receiver = it->GetReceiver(); |
| - // We should never get here to initialize a const with the hole |
| - // value since a const declaration would conflict with the setter. |
| + // We should never get here to initialize a const with the hole value since a |
| + // const declaration would conflict with the setter. |
| DCHECK(!structure->IsForeign()); |
| + |
| + // API style callbacks. |
| if (structure->IsExecutableAccessorInfo()) { |
| - // Don't call executable accessor setters with non-JSObject receivers. |
| - if (!receiver->IsJSObject()) return value; |
| - // api style callbacks |
| - ExecutableAccessorInfo* info = ExecutableAccessorInfo::cast(*structure); |
| + Handle<JSObject> holder = it->GetHolder<JSObject>(); |
| + Handle<Name> name = it->GetName(); |
| + Handle<ExecutableAccessorInfo> info = |
| + Handle<ExecutableAccessorInfo>::cast(structure); |
| if (!info->IsCompatibleReceiver(*receiver)) { |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, |
| name, receiver), |
| Object); |
| } |
| - Object* call_obj = info->setter(); |
| + |
| v8::AccessorNameSetterCallback call_fun = |
| - v8::ToCData<v8::AccessorNameSetterCallback>(call_obj); |
| - if (call_fun == NULL) return value; |
| + v8::ToCData<v8::AccessorNameSetterCallback>(info->setter()); |
| + if (call_fun == nullptr) return value; |
| + |
| LOG(isolate, ApiNamedPropertyAccess("store", *holder, *name)); |
| PropertyCallbackArguments args(isolate, info->data(), *receiver, *holder); |
| - args.Call(call_fun, |
| - v8::Utils::ToLocal(name), |
| - v8::Utils::ToLocal(value)); |
| + args.Call(call_fun, v8::Utils::ToLocal(name), v8::Utils::ToLocal(value)); |
| RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); |
| return value; |
| } |
| - if (structure->IsAccessorPair()) { |
| - Handle<Object> setter(AccessorPair::cast(*structure)->setter(), isolate); |
| - if (setter->IsSpecFunction()) { |
| - // TODO(rossberg): nicer would be to cast to some JSCallable here... |
| - return SetPropertyWithDefinedSetter( |
| - receiver, Handle<JSReceiver>::cast(setter), value); |
| - } else { |
| - if (is_sloppy(language_mode)) return value; |
| - THROW_NEW_ERROR( |
| - isolate, |
| - NewTypeError(MessageTemplate::kNoSetterInCallback, name, holder), |
| - Object); |
| - } |
| + // Regular accessor. |
| + Handle<Object> setter(AccessorPair::cast(*structure)->setter(), isolate); |
| + if (setter->IsSpecFunction()) { |
| + // TODO(rossberg): nicer would be to cast to some JSCallable here... |
| + return SetPropertyWithDefinedSetter( |
| + receiver, Handle<JSReceiver>::cast(setter), value); |
| } |
| - UNREACHABLE(); |
| - return MaybeHandle<Object>(); |
| + if (is_sloppy(language_mode)) return value; |
| + |
| + THROW_NEW_ERROR(isolate, |
| + NewTypeError(MessageTemplate::kNoSetterInCallback, |
| + it->GetName(), it->GetHolder<JSObject>()), |
| + Object); |
| } |
| @@ -540,9 +539,7 @@ MaybeHandle<Object> JSObject::SetPropertyWithFailedAccessCheck( |
| LookupIterator* it, Handle<Object> value, LanguageMode language_mode) { |
| Handle<JSObject> checked = it->GetHolder<JSObject>(); |
| if (FindAllCanWriteHolder(it)) { |
| - return SetPropertyWithAccessor(it->GetReceiver(), it->GetName(), value, |
| - it->GetHolder<JSObject>(), |
| - it->GetAccessors(), language_mode); |
| + return SetPropertyWithAccessor(it, value, language_mode); |
| } |
| it->isolate()->ReportFailedAccessCheck(checked); |
| @@ -597,82 +594,6 @@ void JSObject::SetNormalizedProperty(Handle<JSObject> object, |
| } |
| -MaybeHandle<Object> Object::SetElementWithReceiver( |
| - Isolate* isolate, Handle<Object> object, Handle<Object> receiver, |
| - uint32_t index, Handle<Object> value, LanguageMode language_mode) { |
| - // Iterate up the prototype chain until an element is found or the null |
| - // prototype is encountered. |
| - bool done = false; |
| - for (PrototypeIterator iter(isolate, object, |
| - object->IsJSProxy() || object->IsJSObject() |
| - ? PrototypeIterator::START_AT_RECEIVER |
| - : PrototypeIterator::START_AT_PROTOTYPE); |
| - !iter.IsAtEnd() && !done; iter.Advance()) { |
| - if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) { |
| - // TODO(dslomov): implement. |
| - isolate->ThrowIllegalOperation(); |
| - return MaybeHandle<Object>(); |
| - } |
| - |
| - Handle<JSObject> js_object = |
| - Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)); |
| - |
| - // Check access rights if needed. |
| - if (js_object->IsAccessCheckNeeded()) { |
| - if (!isolate->MayAccess(js_object)) { |
| - isolate->ReportFailedAccessCheck(js_object); |
| - RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); |
| - return isolate->factory()->undefined_value(); |
| - } |
| - } |
| - |
| - if (js_object->HasIndexedInterceptor()) { |
| - LookupIterator it(isolate, receiver, index, js_object, |
| - LookupIterator::OWN); |
| - Maybe<PropertyAttributes> from_interceptor = |
| - JSObject::GetPropertyAttributes(&it); |
| - if (!from_interceptor.IsJust()) return MaybeHandle<Object>(); |
| - if ((from_interceptor.FromJust() & READ_ONLY) != 0) { |
| - return WriteToReadOnlyElement(isolate, receiver, index, value, |
| - language_mode); |
| - } |
| - done = from_interceptor.FromJust() != ABSENT; |
| - } |
| - |
| - if (!done && |
| - js_object->elements() != isolate->heap()->empty_fixed_array()) { |
| - ElementsAccessor* accessor = js_object->GetElementsAccessor(); |
| - PropertyAttributes attrs = accessor->GetAttributes(js_object, index); |
| - if ((attrs & READ_ONLY) != 0) { |
| - return WriteToReadOnlyElement(isolate, receiver, index, value, |
| - language_mode); |
| - } |
| - Handle<AccessorPair> pair; |
| - if (accessor->GetAccessorPair(js_object, index).ToHandle(&pair)) { |
| - return JSObject::SetElementWithCallback(receiver, pair, index, value, |
| - js_object, language_mode); |
| - } else { |
| - done = attrs != ABSENT; |
| - } |
| - } |
| - } |
| - |
| - if (!receiver->IsJSObject()) { |
| - return WriteToReadOnlyElement(isolate, receiver, index, value, |
| - language_mode); |
| - } |
| - Handle<JSObject> target = Handle<JSObject>::cast(receiver); |
| - ElementsAccessor* accessor = target->GetElementsAccessor(); |
| - PropertyAttributes attrs = accessor->GetAttributes(target, index); |
| - if (attrs == ABSENT) { |
| - return JSObject::SetElement(target, index, value, NONE, language_mode, |
| - false); |
| - } |
| - return JSObject::SetElement(target, index, value, attrs, language_mode, false, |
| - DEFINE_PROPERTY); |
| -} |
| - |
| - |
| Map* Object::GetRootMap(Isolate* isolate) { |
| DisallowHeapAllocation no_alloc; |
| if (IsSmi()) { |
| @@ -3037,27 +2958,50 @@ Handle<Map> Map::Update(Handle<Map> map) { |
| MaybeHandle<Object> JSObject::SetPropertyWithInterceptor(LookupIterator* it, |
| Handle<Object> value) { |
| - Handle<Name> name = it->name(); |
| + Isolate* isolate = it->isolate(); |
| + // Make sure that the top context does not change when doing callbacks or |
| + // interceptor calls. |
| + AssertNoContextChange ncc(isolate); |
| + |
| + DCHECK_EQ(LookupIterator::INTERCEPTOR, it->state()); |
| + Handle<InterceptorInfo> interceptor(it->GetInterceptor()); |
| + if (interceptor->setter()->IsUndefined()) return MaybeHandle<Object>(); |
| + |
| Handle<JSObject> holder = it->GetHolder<JSObject>(); |
| - Handle<InterceptorInfo> interceptor(holder->GetNamedInterceptor()); |
| - if (interceptor->setter()->IsUndefined() || |
| - (name->IsSymbol() && !interceptor->can_intercept_symbols())) { |
| - return MaybeHandle<Object>(); |
| + v8::Handle<v8::Value> result; |
| + PropertyCallbackArguments args(isolate, interceptor->data(), |
| + *it->GetReceiver(), *holder); |
| + |
| + if (it->IsElement()) { |
| + uint32_t index = it->index(); |
| + v8::IndexedPropertySetterCallback setter = |
| + v8::ToCData<v8::IndexedPropertySetterCallback>(interceptor->setter()); |
| + LOG(isolate, |
| + ApiIndexedPropertyAccess("interceptor-indexed-set", *holder, index)); |
| + result = args.Call(setter, index, v8::Utils::ToLocal(value)); |
| + } else { |
| + Handle<Name> name = it->name(); |
| + |
| + if (name->IsSymbol() && !interceptor->can_intercept_symbols()) { |
| + return MaybeHandle<Object>(); |
| + } |
| + |
| + v8::GenericNamedPropertySetterCallback setter = |
| + v8::ToCData<v8::GenericNamedPropertySetterCallback>( |
| + interceptor->setter()); |
| + LOG(it->isolate(), |
| + ApiNamedPropertyAccess("interceptor-named-set", *holder, *name)); |
| + result = |
| + args.Call(setter, v8::Utils::ToLocal(name), v8::Utils::ToLocal(value)); |
| } |
| - LOG(it->isolate(), |
| - ApiNamedPropertyAccess("interceptor-named-set", *holder, *name)); |
| - PropertyCallbackArguments args(it->isolate(), interceptor->data(), *holder, |
| - *holder); |
| - v8::GenericNamedPropertySetterCallback setter = |
| - v8::ToCData<v8::GenericNamedPropertySetterCallback>( |
| - interceptor->setter()); |
| - v8::Handle<v8::Value> result = |
| - args.Call(setter, v8::Utils::ToLocal(name), v8::Utils::ToLocal(value)); |
| RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(it->isolate(), Object); |
| - if (!result.IsEmpty()) return value; |
| - |
| - return MaybeHandle<Object>(); |
| + if (result.IsEmpty()) return MaybeHandle<Object>(); |
| +#ifdef DEBUG |
| + Handle<Object> result_internal = v8::Utils::OpenHandle(*result); |
| + result_internal->VerifyApiCallResultType(); |
| +#endif |
| + return value; |
| } |
| @@ -3130,7 +3074,7 @@ MaybeHandle<Object> Object::SetPropertyInternal(LookupIterator* it, |
| break; |
| case LookupIterator::ACCESSOR: { |
| - if (it->property_details().IsReadOnly()) { |
| + if (it->IsReadOnly()) { |
| return WriteToReadOnlyProperty(it, value, language_mode); |
| } |
| Handle<Object> accessors = it->GetAccessors(); |
| @@ -3140,16 +3084,14 @@ MaybeHandle<Object> Object::SetPropertyInternal(LookupIterator* it, |
| done = true; |
| break; |
| } |
| - return SetPropertyWithAccessor(it->GetReceiver(), it->GetName(), value, |
| - it->GetHolder<JSObject>(), accessors, |
| - language_mode); |
| + return SetPropertyWithAccessor(it, value, language_mode); |
| } |
| case LookupIterator::INTEGER_INDEXED_EXOTIC: |
| done = true; |
| break; |
| case LookupIterator::DATA: |
| - if (it->property_details().IsReadOnly()) { |
| + if (it->IsReadOnly()) { |
| return WriteToReadOnlyProperty(it, value, language_mode); |
| } |
| if (it->HolderIsReceiverOrHiddenPrototype()) { |
| @@ -3202,66 +3144,71 @@ MaybeHandle<Object> Object::SetSuperProperty(LookupIterator* it, |
| if (found) return result; |
| if (!it->GetReceiver()->IsJSReceiver()) { |
| - return WriteToReadOnlyProperty(it->isolate(), it->GetReceiver(), it->name(), |
| - value, language_mode); |
| + return WriteToReadOnlyProperty(it->isolate(), it->GetReceiver(), |
| + it->GetName(), value, language_mode); |
| } |
| - LookupIterator own_lookup(it->GetReceiver(), it->name(), LookupIterator::OWN); |
| + LookupIterator::Configuration c = LookupIterator::OWN; |
| + LookupIterator own_lookup = |
| + it->IsElement() |
| + ? LookupIterator(it->isolate(), it->GetReceiver(), it->index(), c) |
| + : LookupIterator(it->GetReceiver(), it->name(), c); |
| - switch (own_lookup.state()) { |
| - case LookupIterator::NOT_FOUND: |
| - return JSObject::AddDataProperty(&own_lookup, value, NONE, language_mode, |
| - store_mode); |
| + for (; own_lookup.IsFound(); own_lookup.Next()) { |
| + switch (own_lookup.state()) { |
| + case LookupIterator::ACCESS_CHECK: |
| + if (!it->isolate()->MayAccess(own_lookup.GetHolder<JSObject>())) { |
| + return JSObject::SetPropertyWithFailedAccessCheck(&own_lookup, value, |
| + language_mode); |
| + } |
| + break; |
| - case LookupIterator::INTEGER_INDEXED_EXOTIC: |
| - return result; |
| + case LookupIterator::INTEGER_INDEXED_EXOTIC: |
| + return result; |
| - case LookupIterator::DATA: { |
| - PropertyDetails details = own_lookup.property_details(); |
| - if (details.IsConfigurable() || !details.IsReadOnly()) { |
| - return JSObject::SetOwnPropertyIgnoreAttributes( |
| - Handle<JSObject>::cast(it->GetReceiver()), it->name(), value, |
| - details.attributes()); |
| + case LookupIterator::DATA: { |
| + PropertyDetails details = own_lookup.property_details(); |
| + if (details.IsConfigurable() || !details.IsReadOnly()) { |
| + return JSObject::ReconfigureAsDataProperty(&own_lookup, value, |
| + details.attributes()); |
| + } |
| + return WriteToReadOnlyProperty(&own_lookup, value, language_mode); |
| } |
| - return WriteToReadOnlyProperty(&own_lookup, value, language_mode); |
| - } |
| - case LookupIterator::ACCESSOR: { |
| - PropertyDetails details = own_lookup.property_details(); |
| - if (details.IsConfigurable()) { |
| - return JSObject::SetOwnPropertyIgnoreAttributes( |
| - Handle<JSObject>::cast(it->GetReceiver()), it->name(), value, |
| - details.attributes()); |
| - } |
| + case LookupIterator::ACCESSOR: { |
| + PropertyDetails details = own_lookup.property_details(); |
| + if (details.IsConfigurable()) { |
| + return JSObject::ReconfigureAsDataProperty(&own_lookup, value, |
| + details.attributes()); |
| + } |
| - return RedefineNonconfigurableProperty(it->isolate(), it->name(), value, |
| - language_mode); |
| - } |
| + return RedefineNonconfigurableProperty(it->isolate(), it->GetName(), |
| + value, language_mode); |
| + } |
| - case LookupIterator::TRANSITION: |
| - UNREACHABLE(); |
| - break; |
| + case LookupIterator::INTERCEPTOR: |
| + case LookupIterator::JSPROXY: { |
| + bool found = false; |
| + MaybeHandle<Object> result = SetPropertyInternal( |
| + &own_lookup, value, language_mode, store_mode, &found); |
| + if (found) return result; |
|
Igor Sheludko
2015/06/11 13:44:25
Missing break; ?
Toon Verwaest
2015/06/11 14:26:50
Done.
|
| + } |
| - case LookupIterator::INTERCEPTOR: |
| - case LookupIterator::JSPROXY: |
| - case LookupIterator::ACCESS_CHECK: { |
| - bool found = false; |
| - MaybeHandle<Object> result = SetPropertyInternal( |
| - &own_lookup, value, language_mode, store_mode, &found); |
| - if (found) return result; |
| - return SetDataProperty(&own_lookup, value); |
| + case LookupIterator::NOT_FOUND: |
| + case LookupIterator::TRANSITION: |
| + UNREACHABLE(); |
| } |
| } |
| - UNREACHABLE(); |
| - return MaybeHandle<Object>(); |
| + return JSObject::AddDataProperty(&own_lookup, value, NONE, language_mode, |
| + store_mode); |
| } |
| MaybeHandle<Object> Object::WriteToReadOnlyProperty( |
| LookupIterator* it, Handle<Object> value, LanguageMode language_mode) { |
| - return WriteToReadOnlyProperty(it->isolate(), it->GetReceiver(), it->name(), |
| - value, language_mode); |
| + return WriteToReadOnlyProperty(it->isolate(), it->GetReceiver(), |
| + it->GetName(), value, language_mode); |
| } |
| @@ -3310,21 +3257,32 @@ MaybeHandle<Object> Object::SetDataProperty(LookupIterator* it, |
| // 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->isolate()->IsInternallyUsedPropertyName(it->name()); |
| + (it->IsElement() || |
| + !it->isolate()->IsInternallyUsedPropertyName(it->name())); |
| MaybeHandle<Object> maybe_old; |
| if (is_observed) maybe_old = it->GetDataValue(); |
| + // Convert the incoming value to a number for storing into typed arrays. |
| + if (it->IsElement() && (receiver->HasExternalArrayElements() || |
| + receiver->HasFixedTypedArrayElements())) { |
| + if (!value->IsNumber() && !value->IsUndefined()) { |
| + ASSIGN_RETURN_ON_EXCEPTION(it->isolate(), value, |
| + Execution::ToNumber(it->isolate(), value), |
| + Object); |
| + } |
| + } |
| + |
| // 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); |
| + value = it->WriteDataValue(value); |
| // Send the change record if there are observers. |
| if (is_observed && !value->SameValue(*maybe_old.ToHandleChecked())) { |
| RETURN_ON_EXCEPTION(it->isolate(), JSObject::EnqueueChangeRecord( |
| - receiver, "update", it->name(), |
| + receiver, "update", it->GetName(), |
| maybe_old.ToHandleChecked()), |
| Object); |
| } |
| @@ -3333,6 +3291,47 @@ MaybeHandle<Object> Object::SetDataProperty(LookupIterator* it, |
| } |
| +MUST_USE_RESULT static MaybeHandle<Object> BeginPerformSplice( |
| + Handle<JSArray> object) { |
| + Isolate* isolate = object->GetIsolate(); |
| + HandleScope scope(isolate); |
| + Handle<Object> args[] = {object}; |
| + |
| + return Execution::Call( |
| + isolate, Handle<JSFunction>(isolate->observers_begin_perform_splice()), |
| + isolate->factory()->undefined_value(), arraysize(args), args); |
| +} |
| + |
| + |
| +MUST_USE_RESULT static MaybeHandle<Object> EndPerformSplice( |
| + Handle<JSArray> object) { |
| + Isolate* isolate = object->GetIsolate(); |
| + HandleScope scope(isolate); |
| + Handle<Object> args[] = {object}; |
| + |
| + return Execution::Call( |
| + isolate, Handle<JSFunction>(isolate->observers_end_perform_splice()), |
| + isolate->factory()->undefined_value(), arraysize(args), args); |
| +} |
| + |
| + |
| +MUST_USE_RESULT static MaybeHandle<Object> EnqueueSpliceRecord( |
| + Handle<JSArray> object, uint32_t index, Handle<JSArray> deleted, |
| + uint32_t add_count) { |
| + Isolate* isolate = object->GetIsolate(); |
| + HandleScope scope(isolate); |
| + Handle<Object> index_object = isolate->factory()->NewNumberFromUint(index); |
| + Handle<Object> add_count_object = |
| + isolate->factory()->NewNumberFromUint(add_count); |
| + |
| + Handle<Object> args[] = {object, index_object, deleted, add_count_object}; |
| + |
| + return Execution::Call( |
| + isolate, Handle<JSFunction>(isolate->observers_enqueue_splice()), |
| + isolate->factory()->undefined_value(), arraysize(args), args); |
| +} |
| + |
| + |
| MaybeHandle<Object> Object::AddDataProperty(LookupIterator* it, |
| Handle<Object> value, |
| PropertyAttributes attributes, |
| @@ -3352,84 +3351,55 @@ MaybeHandle<Object> Object::AddDataProperty(LookupIterator* it, |
| // instead. If the prototype is Null, the proxy is detached. |
| if (receiver->IsJSGlobalProxy()) return value; |
| - // Possibly migrate to the most up-to-date map that will be able to store |
| - // |value| under it->name() with |attributes|. |
| - it->PrepareTransitionToDataProperty(value, attributes, store_mode); |
| - if (it->state() != LookupIterator::TRANSITION) { |
| - if (is_sloppy(language_mode)) return value; |
| - THROW_NEW_ERROR( |
| - it->isolate(), |
| - NewTypeError(MessageTemplate::kObjectNotExtensible, it->name()), |
| - Object); |
| - } |
| - it->ApplyTransitionToDataProperty(); |
| - |
| - // TODO(verwaest): Encapsulate dictionary handling better. |
| - if (receiver->map()->is_dictionary_map()) { |
| - // TODO(verwaest): Probably should ensure this is done beforehand. |
| - it->InternalizeName(); |
| - // TODO(dcarney): just populate TransitionPropertyCell here? |
| - JSObject::AddSlowProperty(receiver, it->name(), value, attributes); |
| - } else { |
| - // Write the property value. |
| - it->WriteDataValue(value); |
| - } |
| + Isolate* isolate = it->isolate(); |
| - // Send the change record if there are observers. |
| - if (receiver->map()->is_observed() && |
| - !it->isolate()->IsInternallyUsedPropertyName(it->name())) { |
| - RETURN_ON_EXCEPTION(it->isolate(), JSObject::EnqueueChangeRecord( |
| - receiver, "add", it->name(), |
| - it->factory()->the_hole_value()), |
| - Object); |
| + if (!receiver->map()->is_extensible() && |
| + (it->IsElement() || !isolate->IsInternallyUsedPropertyName(it->name()))) { |
| + if (is_sloppy(language_mode)) return value; |
| + THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kObjectNotExtensible, |
| + it->GetName()), |
| + Object); |
| } |
| - return value; |
| -} |
| - |
| - |
| -MaybeHandle<Object> JSObject::SetElementWithCallbackSetterInPrototypes( |
| - Handle<JSObject> object, uint32_t index, Handle<Object> value, bool* found, |
| - LanguageMode language_mode) { |
| - Isolate* isolate = object->GetIsolate(); |
| - for (PrototypeIterator iter(isolate, object); !iter.IsAtEnd(); |
| - iter.Advance()) { |
| - if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) { |
| - return JSProxy::SetPropertyViaPrototypesWithHandler( |
| - Handle<JSProxy>::cast(PrototypeIterator::GetCurrent(iter)), object, |
| - isolate->factory()->Uint32ToString(index), // name |
| - value, language_mode, found); |
| - } |
| - Handle<JSObject> js_proto = |
| - Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)); |
| - |
| - if (js_proto->IsAccessCheckNeeded()) { |
| - if (!isolate->MayAccess(js_proto)) { |
| - *found = true; |
| - isolate->ReportFailedAccessCheck(js_proto); |
| - RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); |
| - return MaybeHandle<Object>(); |
| + if (it->IsElement()) { |
| + if (receiver->IsJSArray()) { |
|
Igor Sheludko
2015/06/11 13:44:25
You dropped CheckArrayAbuse() calls. Was it done i
Toon Verwaest
2015/06/11 14:26:50
Done.
|
| + Handle<JSArray> array = Handle<JSArray>::cast(receiver); |
| + if (JSArray::WouldChangeReadOnlyLength(array, it->index())) { |
| + if (is_sloppy(language_mode)) return value; |
| + return JSArray::ReadOnlyLengthError(array); |
| } |
| } |
| - if (!js_proto->HasDictionaryElements()) { |
| - continue; |
| + return JSObject::AddDataElement(receiver, it->index(), value, attributes); |
| + } else { |
| + // Migrate to the most up-to-date map that will be able to store |value| |
| + // under it->name() with |attributes|. |
| + it->PrepareTransitionToDataProperty(value, attributes, store_mode); |
| + DCHECK_EQ(LookupIterator::TRANSITION, it->state()); |
| + it->ApplyTransitionToDataProperty(); |
| + |
| + // TODO(verwaest): Encapsulate dictionary handling better. |
| + if (receiver->map()->is_dictionary_map()) { |
| + // TODO(verwaest): Probably should ensure this is done beforehand. |
| + it->InternalizeName(); |
| + // TODO(dcarney): just populate TransitionPropertyCell here? |
| + JSObject::AddSlowProperty(receiver, it->name(), value, attributes); |
| + } else { |
| + // Write the property value. |
| + it->WriteDataValue(value); |
| } |
| - Handle<SeededNumberDictionary> dictionary(js_proto->element_dictionary()); |
| - int entry = dictionary->FindEntry(index); |
| - if (entry != SeededNumberDictionary::kNotFound) { |
| - PropertyDetails details = dictionary->DetailsAt(entry); |
| - if (details.type() == ACCESSOR_CONSTANT) { |
| - *found = true; |
| - Handle<Object> structure(dictionary->ValueAt(entry), isolate); |
| - return SetElementWithCallback(object, structure, index, value, js_proto, |
| - language_mode); |
| - } |
| + // Send the change record if there are observers. |
| + if (receiver->map()->is_observed() && |
| + !isolate->IsInternallyUsedPropertyName(it->name())) { |
| + RETURN_ON_EXCEPTION(isolate, JSObject::EnqueueChangeRecord( |
| + receiver, "add", it->name(), |
| + it->factory()->the_hole_value()), |
| + Object); |
| } |
| } |
| - *found = false; |
| - return isolate->factory()->the_hole_value(); |
| + |
| + return value; |
| } |
| @@ -4144,130 +4114,177 @@ void JSObject::AddProperty(Handle<JSObject> object, Handle<Name> name, |
| } |
| -// Reconfigures a property to a data property with attributes, even if it is not |
| -// reconfigurable. |
| -MaybeHandle<Object> JSObject::SetOwnPropertyIgnoreAttributes( |
| - Handle<JSObject> object, |
| - Handle<Name> name, |
| - Handle<Object> value, |
| - PropertyAttributes attributes, |
| - ExecutableAccessorInfoHandling handling) { |
| - DCHECK(!value->IsTheHole()); |
| - LookupIterator it(object, name, LookupIterator::OWN_SKIP_INTERCEPTOR); |
| - bool is_observed = object->map()->is_observed() && |
| - !it.isolate()->IsInternallyUsedPropertyName(name); |
| - for (; it.IsFound(); it.Next()) { |
| - switch (it.state()) { |
| - case LookupIterator::INTEGER_INDEXED_EXOTIC: |
| - return value; |
| +// static |
| +void ExecutableAccessorInfo::ClearSetter(Handle<ExecutableAccessorInfo> info) { |
| + info->set_setter(*v8::FromCData(info->GetIsolate(), nullptr)); |
| +} |
| - case LookupIterator::INTERCEPTOR: |
| - case LookupIterator::JSPROXY: |
| - case LookupIterator::NOT_FOUND: |
| - case LookupIterator::TRANSITION: |
| - UNREACHABLE(); |
| - case LookupIterator::ACCESS_CHECK: |
| - if (!it.isolate()->MayAccess(object)) { |
| - return SetPropertyWithFailedAccessCheck(&it, value, SLOPPY); |
| - } |
| - break; |
| +MaybeHandle<Object> JSObject::ReconfigureAsDataProperty( |
| + LookupIterator* it, Handle<Object> value, PropertyAttributes attributes) { |
| + Handle<JSObject> object = Handle<JSObject>::cast(it->GetReceiver()); |
| + bool is_observed = object->map()->is_observed() && |
| + (it->IsElement() || |
| + !it->isolate()->IsInternallyUsedPropertyName(it->name())); |
| - case LookupIterator::ACCESSOR: { |
| - PropertyDetails details = it.property_details(); |
| - // Ensure the context isn't changed after calling into accessors. |
| - AssertNoContextChange ncc(it.isolate()); |
| + switch (it->state()) { |
| + case LookupIterator::INTERCEPTOR: |
| + case LookupIterator::JSPROXY: |
| + case LookupIterator::NOT_FOUND: |
| + case LookupIterator::TRANSITION: |
| + case LookupIterator::ACCESS_CHECK: |
| + UNREACHABLE(); |
| - Handle<Object> accessors = it.GetAccessors(); |
| + case LookupIterator::INTEGER_INDEXED_EXOTIC: |
| + return value; |
| - // Special handling for ExecutableAccessorInfo, which behaves like a |
| - // data property. |
| - if (handling == DONT_FORCE_FIELD && |
| - accessors->IsExecutableAccessorInfo()) { |
| - Handle<Object> result; |
| - ASSIGN_RETURN_ON_EXCEPTION( |
| - it.isolate(), result, |
| - JSObject::SetPropertyWithAccessor(it.GetReceiver(), it.name(), |
| - value, it.GetHolder<JSObject>(), |
| - accessors, STRICT), |
| - Object); |
| - DCHECK(result->SameValue(*value)); |
| + case LookupIterator::ACCESSOR: { |
| + PropertyDetails details = it->property_details(); |
| + // Ensure the context isn't changed after calling into accessors. |
| + AssertNoContextChange ncc(it->isolate()); |
| - if (details.attributes() == attributes) { |
| - return value; |
| - } |
| + Handle<Object> accessors = it->GetAccessors(); |
| - // Reconfigure the accessor if attributes mismatch. |
| - Handle<ExecutableAccessorInfo> new_data = Accessors::CloneAccessor( |
| - it.isolate(), Handle<ExecutableAccessorInfo>::cast(accessors)); |
| - new_data->set_property_attributes(attributes); |
| - // By clearing the setter we don't have to introduce a lookup to |
| - // the setter, simply make it unavailable to reflect the |
| - // attributes. |
| - if (attributes & READ_ONLY) { |
| - ExecutableAccessorInfo::ClearSetter(new_data); |
| - } |
| - SetPropertyCallback(object, name, new_data, attributes); |
| - if (is_observed) { |
| - RETURN_ON_EXCEPTION( |
| - it.isolate(), |
| - EnqueueChangeRecord(object, "reconfigure", name, |
| - it.isolate()->factory()->the_hole_value()), |
| - Object); |
| - } |
| - return value; |
| + // Special handling for ExecutableAccessorInfo, which behaves like a |
| + // data property. |
| + if (accessors->IsExecutableAccessorInfo()) { |
| + Handle<Object> result; |
| + ASSIGN_RETURN_ON_EXCEPTION( |
| + it->isolate(), result, |
| + JSObject::SetPropertyWithAccessor(it, value, STRICT), Object); |
| + DCHECK(result->SameValue(*value)); |
| + |
| + if (details.attributes() == attributes) return value; |
| + |
| + // Reconfigure the accessor if attributes mismatch. |
| + Handle<ExecutableAccessorInfo> new_data = Accessors::CloneAccessor( |
| + it->isolate(), Handle<ExecutableAccessorInfo>::cast(accessors)); |
| + new_data->set_property_attributes(attributes); |
| + // By clearing the setter we don't have to introduce a lookup to |
| + // the setter, simply make it unavailable to reflect the |
| + // attributes. |
| + if (attributes & READ_ONLY) { |
| + ExecutableAccessorInfo::ClearSetter(new_data); |
| } |
| - it.ReconfigureDataProperty(value, attributes); |
| - it.WriteDataValue(value); |
| - |
| + if (it->IsElement()) { |
| + SetElementCallback(object, it->index(), new_data, attributes); |
| + } else { |
| + SetPropertyCallback(object, it->name(), new_data, attributes); |
| + } |
| if (is_observed) { |
| RETURN_ON_EXCEPTION( |
| - it.isolate(), |
| - EnqueueChangeRecord(object, "reconfigure", name, |
| - it.isolate()->factory()->the_hole_value()), |
| + it->isolate(), |
| + EnqueueChangeRecord(object, "reconfigure", it->GetName(), |
| + it->factory()->the_hole_value()), |
| Object); |
| } |
| - |
| return value; |
| } |
| - case LookupIterator::DATA: { |
| - PropertyDetails details = it.property_details(); |
| - Handle<Object> old_value = it.isolate()->factory()->the_hole_value(); |
| - // Regular property update if the attributes match. |
| - if (details.attributes() == attributes) { |
| - return SetDataProperty(&it, value); |
| - } |
| - // Reconfigure the data property if the attributes mismatch. |
| - if (is_observed) old_value = it.GetDataValue(); |
| + it->ReconfigureDataProperty(value, attributes); |
| + it->WriteDataValue(value); |
| - it.ReconfigureDataProperty(value, attributes); |
| - it.WriteDataValue(value); |
| + if (is_observed) { |
| + RETURN_ON_EXCEPTION( |
| + it->isolate(), |
| + EnqueueChangeRecord(object, "reconfigure", it->GetName(), |
| + it->factory()->the_hole_value()), |
| + Object); |
| + } |
| - if (is_observed) { |
| - if (old_value->SameValue(*value)) { |
| - old_value = it.isolate()->factory()->the_hole_value(); |
| - } |
| - RETURN_ON_EXCEPTION( |
| - it.isolate(), |
| - EnqueueChangeRecord(object, "reconfigure", name, old_value), |
| - Object); |
| - } |
| + return value; |
| + } |
| - return value; |
| + case LookupIterator::DATA: { |
| + PropertyDetails details = it->property_details(); |
| + Handle<Object> old_value = it->factory()->the_hole_value(); |
| + // Regular property update if the attributes match. |
| + if (details.attributes() == attributes) { |
| + return SetDataProperty(it, value); |
| } |
| - } |
| - } |
| - return AddDataProperty(&it, value, attributes, STRICT, |
| - CERTAINLY_NOT_STORE_FROM_KEYED); |
| -} |
| + // Special case: properties of typed arrays cannot be reconfigured to |
| + // non-writable nor to non-enumerable. |
| + if (it->IsElement() && (object->HasExternalArrayElements() || |
| + object->HasFixedTypedArrayElements())) { |
| + return RedefineNonconfigurableProperty(it->isolate(), it->GetName(), |
| + value, STRICT); |
| + } |
| + // Reconfigure the data property if the attributes mismatch. |
| + if (is_observed) old_value = it->GetDataValue(); |
| -Maybe<PropertyAttributes> JSObject::GetPropertyAttributesWithInterceptor( |
| - LookupIterator* it) { |
| - Isolate* isolate = it->isolate(); |
| + it->ReconfigureDataProperty(value, attributes); |
| + it->WriteDataValue(value); |
| + |
| + if (is_observed) { |
| + if (old_value->SameValue(*value)) { |
| + old_value = it->factory()->the_hole_value(); |
| + } |
| + RETURN_ON_EXCEPTION(it->isolate(), |
| + EnqueueChangeRecord(object, "reconfigure", |
| + it->GetName(), old_value), |
| + Object); |
| + } |
| + } |
| + } |
| + |
| + return value; |
| +} |
| + |
| + |
| +// Reconfigures a property to a data property with attributes, even if it is not |
| +// reconfigurable. |
| +MaybeHandle<Object> JSObject::SetOwnPropertyIgnoreAttributes( |
| + Handle<JSObject> object, Handle<Name> name, Handle<Object> value, |
| + PropertyAttributes attributes) { |
| + DCHECK(!value->IsTheHole()); |
| + LookupIterator it(object, name, LookupIterator::OWN_SKIP_INTERCEPTOR); |
| + if (it.state() == LookupIterator::ACCESS_CHECK) { |
| + if (!it.isolate()->MayAccess(object)) { |
| + // TODO(verwaest): Check whether this makes sense. |
| + return SetPropertyWithFailedAccessCheck(&it, value, SLOPPY); |
| + } |
| + it.Next(); |
| + } |
| + |
| + if (it.IsFound()) { |
| + return ReconfigureAsDataProperty(&it, value, attributes); |
| + } |
| + |
| + return AddDataProperty(&it, value, attributes, STRICT, |
| + CERTAINLY_NOT_STORE_FROM_KEYED); |
| +} |
| + |
| + |
| +MaybeHandle<Object> JSObject::SetOwnElementIgnoreAttributes( |
| + Handle<JSObject> object, uint32_t index, Handle<Object> value, |
| + PropertyAttributes attributes) { |
| + DCHECK(!object->HasExternalArrayElements()); |
| + Isolate* isolate = object->GetIsolate(); |
| + LookupIterator it(isolate, object, index, |
| + LookupIterator::OWN_SKIP_INTERCEPTOR); |
| + if (it.state() == LookupIterator::ACCESS_CHECK) { |
| + if (!isolate->MayAccess(object)) { |
| + // TODO(verwaest): Check whether this makes sense. |
| + return SetPropertyWithFailedAccessCheck(&it, value, STRICT); |
| + } |
| + it.Next(); |
| + } |
| + |
| + if (it.IsFound()) { |
| + return ReconfigureAsDataProperty(&it, value, attributes); |
| + } |
| + |
| + return AddDataProperty(&it, value, attributes, STRICT, |
| + MAY_BE_STORE_FROM_KEYED); |
| +} |
| + |
| + |
| +Maybe<PropertyAttributes> JSObject::GetPropertyAttributesWithInterceptor( |
| + LookupIterator* it) { |
| + Isolate* isolate = it->isolate(); |
| // Make sure that the top context does not change when doing |
| // callbacks or interceptor calls. |
| AssertNoContextChange ncc(isolate); |
| @@ -7154,6 +7171,43 @@ bool DescriptorArray::CanHoldValue(int descriptor, Object* value) { |
| } |
| +// static |
| +Handle<Map> Map::PrepareForDataElement(Handle<Map> map, Handle<Object> value) { |
| + ElementsKind kind = map->elements_kind(); |
| + bool holey = IsHoleyElementsKind(kind); |
| + |
| + switch (kind) { |
| + case FAST_SMI_ELEMENTS: |
| + case FAST_HOLEY_SMI_ELEMENTS: |
| + if (value->IsSmi()) return map; |
| + kind = value->IsNumber() ? FAST_DOUBLE_ELEMENTS : FAST_ELEMENTS; |
| + break; |
| + |
| + case FAST_DOUBLE_ELEMENTS: |
| + case FAST_HOLEY_DOUBLE_ELEMENTS: |
| + if (value->IsNumber()) return map; |
| + kind = FAST_ELEMENTS; |
| + break; |
| + |
| + case FAST_ELEMENTS: |
| + case FAST_HOLEY_ELEMENTS: |
| + case DICTIONARY_ELEMENTS: |
| + case SLOPPY_ARGUMENTS_ELEMENTS: |
| +#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ |
| + case EXTERNAL_##TYPE##_ELEMENTS: \ |
| + case TYPE##_ELEMENTS: |
| + |
| + TYPED_ARRAYS(TYPED_ARRAY_CASE) |
| +#undef TYPED_ARRAY_CASE |
| + return map; |
| + } |
| + |
| + if (holey) kind = GetHoleyElementsKind(kind); |
| + return Map::AsElementsKind(map, kind); |
| +} |
| + |
| + |
| +// static |
| Handle<Map> Map::PrepareForDataProperty(Handle<Map> map, int descriptor, |
| Handle<Object> value) { |
| // Dictionaries can store any property value. |
| @@ -11895,48 +11949,6 @@ static bool GetOldValue(Isolate* isolate, |
| return true; |
| } |
| -MUST_USE_RESULT static MaybeHandle<Object> EnqueueSpliceRecord( |
| - Handle<JSArray> object, uint32_t index, Handle<JSArray> deleted, |
| - uint32_t add_count) { |
| - Isolate* isolate = object->GetIsolate(); |
| - HandleScope scope(isolate); |
| - Handle<Object> index_object = isolate->factory()->NewNumberFromUint(index); |
| - Handle<Object> add_count_object = |
| - isolate->factory()->NewNumberFromUint(add_count); |
| - |
| - Handle<Object> args[] = |
| - { object, index_object, deleted, add_count_object }; |
| - |
| - return Execution::Call( |
| - isolate, Handle<JSFunction>(isolate->observers_enqueue_splice()), |
| - isolate->factory()->undefined_value(), arraysize(args), args); |
| -} |
| - |
| - |
| -MUST_USE_RESULT static MaybeHandle<Object> BeginPerformSplice( |
| - Handle<JSArray> object) { |
| - Isolate* isolate = object->GetIsolate(); |
| - HandleScope scope(isolate); |
| - Handle<Object> args[] = { object }; |
| - |
| - return Execution::Call( |
| - isolate, Handle<JSFunction>(isolate->observers_begin_perform_splice()), |
| - isolate->factory()->undefined_value(), arraysize(args), args); |
| -} |
| - |
| - |
| -MUST_USE_RESULT static MaybeHandle<Object> EndPerformSplice( |
| - Handle<JSArray> object) { |
| - Isolate* isolate = object->GetIsolate(); |
| - HandleScope scope(isolate); |
| - Handle<Object> args[] = { object }; |
| - |
| - return Execution::Call( |
| - isolate, Handle<JSFunction>(isolate->observers_end_perform_splice()), |
| - isolate->factory()->undefined_value(), arraysize(args), args); |
| -} |
| - |
| - |
| MaybeHandle<Object> JSArray::SetElementsLength( |
| Handle<JSArray> array, |
| Handle<Object> new_length_handle) { |
| @@ -12021,16 +12033,13 @@ MaybeHandle<Object> JSArray::SetElementsLength( |
| // Skip deletions where the property was an accessor, leaving holes |
| // in the array of old values. |
| if (old_values[i]->IsTheHole()) continue; |
| - JSObject::SetOwnElement(deleted, indices[i] - index, old_values[i], |
| - SLOPPY).Assert(); |
| + JSObject::AddDataElement(deleted, indices[i] - index, old_values[i], NONE) |
| + .Assert(); |
| } |
| - RETURN_ON_EXCEPTION( |
| - isolate, |
| - SetProperty(deleted, isolate->factory()->length_string(), |
| - isolate->factory()->NewNumberFromUint(delete_count), |
| - STRICT), |
| - Object); |
| + SetProperty(deleted, isolate->factory()->length_string(), |
| + isolate->factory()->NewNumberFromUint(delete_count), |
| + STRICT).Assert(); |
| } |
| RETURN_ON_EXCEPTION( |
| @@ -12455,84 +12464,6 @@ MaybeHandle<AccessorPair> JSObject::GetOwnElementAccessorPair( |
| } |
| -MaybeHandle<Object> JSObject::SetElementWithInterceptor( |
| - Handle<JSObject> object, uint32_t index, Handle<Object> value, |
| - PropertyAttributes attributes, LanguageMode language_mode, |
| - bool check_prototype, SetPropertyMode set_mode) { |
| - Isolate* isolate = object->GetIsolate(); |
| - |
| - // Make sure that the top context does not change when doing |
| - // callbacks or interceptor calls. |
| - AssertNoContextChange ncc(isolate); |
| - |
| - Handle<InterceptorInfo> interceptor(object->GetIndexedInterceptor()); |
| - if (!interceptor->setter()->IsUndefined()) { |
| - v8::IndexedPropertySetterCallback setter = |
| - v8::ToCData<v8::IndexedPropertySetterCallback>(interceptor->setter()); |
| - LOG(isolate, |
| - ApiIndexedPropertyAccess("interceptor-indexed-set", *object, index)); |
| - PropertyCallbackArguments args(isolate, interceptor->data(), *object, |
| - *object); |
| - v8::Handle<v8::Value> result = |
| - args.Call(setter, index, v8::Utils::ToLocal(value)); |
| - RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); |
| - if (!result.IsEmpty()) return value; |
| - } |
| - |
| - return SetElementWithoutInterceptor(object, index, value, attributes, |
| - language_mode, check_prototype, set_mode); |
| -} |
| - |
| - |
| -MaybeHandle<Object> JSObject::SetElementWithCallback( |
| - Handle<Object> object, Handle<Object> structure, uint32_t index, |
| - Handle<Object> value, Handle<JSObject> holder, LanguageMode language_mode) { |
| - Isolate* isolate = holder->GetIsolate(); |
| - |
| - // We should never get here to initialize a const with the hole |
| - // value since a const declaration would conflict with the setter. |
| - DCHECK(!value->IsTheHole()); |
| - DCHECK(!structure->IsForeign()); |
| - if (structure->IsExecutableAccessorInfo()) { |
| - // api style callbacks |
| - Handle<ExecutableAccessorInfo> data = |
| - Handle<ExecutableAccessorInfo>::cast(structure); |
| - Object* call_obj = data->setter(); |
| - v8::AccessorNameSetterCallback call_fun = |
| - v8::ToCData<v8::AccessorNameSetterCallback>(call_obj); |
| - if (call_fun == NULL) return value; |
| - Handle<String> key(isolate->factory()->Uint32ToString(index)); |
| - LOG(isolate, ApiNamedPropertyAccess("store", *holder, *key)); |
| - PropertyCallbackArguments |
| - args(isolate, data->data(), *object, *holder); |
| - args.Call(call_fun, |
| - v8::Utils::ToLocal(key), |
| - v8::Utils::ToLocal(value)); |
| - RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); |
| - return value; |
| - } |
| - |
| - if (structure->IsAccessorPair()) { |
| - Handle<Object> setter(AccessorPair::cast(*structure)->setter(), isolate); |
| - if (setter->IsSpecFunction()) { |
| - // TODO(rossberg): nicer would be to cast to some JSCallable here... |
| - return SetPropertyWithDefinedSetter( |
| - object, Handle<JSReceiver>::cast(setter), value); |
| - } else { |
| - if (is_sloppy(language_mode)) return value; |
| - Handle<Object> key(isolate->factory()->NewNumberFromUint(index)); |
| - THROW_NEW_ERROR( |
| - isolate, |
| - NewTypeError(MessageTemplate::kNoSetterInCallback, key, holder), |
| - Object); |
| - } |
| - } |
| - |
| - UNREACHABLE(); |
| - return MaybeHandle<Object>(); |
| -} |
| - |
| - |
| bool JSObject::HasFastArgumentsElements() { |
| Heap* heap = GetHeap(); |
| if (!elements()->IsFixedArray()) return false; |
| @@ -12557,14 +12488,8 @@ bool JSObject::HasDictionaryArgumentsElements() { |
| } |
| -// Adding n elements in fast case is O(n*n). |
| -// Note: revisit design to have dual undefined values to capture absent |
| -// elements. |
| -MaybeHandle<Object> JSObject::SetFastElement(Handle<JSObject> object, |
| - uint32_t index, |
| - Handle<Object> value, |
| - LanguageMode language_mode, |
| - bool check_prototype) { |
| +void JSObject::SetFastElement(Handle<JSObject> object, uint32_t index, |
| + Handle<Object> value) { |
| DCHECK(object->HasFastSmiOrObjectElements() || |
| object->HasFastArgumentsElements()); |
| @@ -12576,22 +12501,13 @@ MaybeHandle<Object> JSObject::SetFastElement(Handle<JSObject> object, |
| isolate->UpdateArrayProtectorOnSetElement(object); |
| Handle<FixedArray> backing_store(FixedArray::cast(object->elements())); |
| - if (backing_store->map() == |
| - isolate->heap()->sloppy_arguments_elements_map()) { |
| + if (object->HasSloppyArgumentsElements()) { |
| backing_store = handle(FixedArray::cast(backing_store->get(1))); |
| } else { |
| backing_store = EnsureWritableFastElements(object); |
| } |
| uint32_t capacity = static_cast<uint32_t>(backing_store->length()); |
| - if (check_prototype && |
| - (index >= capacity || backing_store->get(index)->IsTheHole())) { |
| - bool found; |
| - MaybeHandle<Object> result = SetElementWithCallbackSetterInPrototypes( |
| - object, index, value, &found, language_mode); |
| - if (found) return result; |
| - } |
| - |
| uint32_t new_capacity = capacity; |
| // Check if the length property of this object needs to be updated. |
| uint32_t array_length = 0; |
| @@ -12632,8 +12548,8 @@ MaybeHandle<Object> JSObject::SetFastElement(Handle<JSObject> object, |
| } |
| if (convert_to_slow) { |
| NormalizeElements(object); |
| - return SetDictionaryElement(object, index, value, NONE, language_mode, |
| - check_prototype); |
| + SetDictionaryElement(object, index, value, NONE); |
| + return; |
| } |
| } |
| // Convert to fast double elements if appropriate. |
| @@ -12648,7 +12564,7 @@ MaybeHandle<Object> JSObject::SetFastElement(Handle<JSObject> object, |
| SetFastDoubleElementsCapacityAndLength(object, new_capacity, array_length); |
| FixedDoubleArray::cast(object->elements())->set(index, value->Number()); |
| JSObject::ValidateElements(object); |
| - return value; |
| + return; |
| } |
| // Change elements kind from Smi-only to generic FAST if necessary. |
| if (object->HasFastSmiElements() && !value->IsSmi()) { |
| @@ -12672,7 +12588,7 @@ MaybeHandle<Object> JSObject::SetFastElement(Handle<JSObject> object, |
| smi_mode); |
| new_elements->set(index, *value); |
| JSObject::ValidateElements(object); |
| - return value; |
| + return; |
| } |
| // Finally, set the new element and length. |
| @@ -12681,22 +12597,63 @@ MaybeHandle<Object> JSObject::SetFastElement(Handle<JSObject> object, |
| if (must_update_array_length) { |
| Handle<JSArray>::cast(object)->set_length(Smi::FromInt(array_length)); |
| } |
| - return value; |
| } |
| -MaybeHandle<Object> JSObject::SetDictionaryElement( |
| - Handle<JSObject> object, uint32_t index, Handle<Object> value, |
| - PropertyAttributes attributes, LanguageMode language_mode, |
| - bool check_prototype, SetPropertyMode set_mode) { |
| - DCHECK(object->HasDictionaryElements() || |
| - object->HasDictionaryArgumentsElements()); |
| +void JSObject::SetSloppyArgumentsElements(Handle<JSObject> object, |
| + uint32_t index, Handle<Object> value, |
| + PropertyAttributes attributes) { |
| + // TODO(verwaest): Handle with the elements accessor. |
| + Isolate* isolate = object->GetIsolate(); |
| + |
| + DCHECK(object->HasSloppyArgumentsElements()); |
| + |
| + Handle<FixedArray> parameter_map(FixedArray::cast(object->elements())); |
| + uint32_t length = parameter_map->length(); |
| + Handle<Object> probe = |
| + index < length - 2 |
| + ? Handle<Object>(parameter_map->get(index + 2), isolate) |
| + : Handle<Object>(); |
| + if (!probe.is_null() && !probe->IsTheHole()) { |
| + Handle<Context> context(Context::cast(parameter_map->get(0))); |
| + int context_index = Handle<Smi>::cast(probe)->value(); |
| + DCHECK(!context->get(context_index)->IsTheHole()); |
| + context->set(context_index, *value); |
| + |
| + if (attributes == NONE) return; |
| + |
| + // Redefining attributes of an aliased element destroys fast aliasing. |
| + parameter_map->set_the_hole(index + 2); |
| + // For elements that are still writable we re-establish slow aliasing. |
| + if ((attributes & READ_ONLY) == 0) { |
| + value = Handle<Object>::cast( |
| + isolate->factory()->NewAliasedArgumentsEntry(context_index)); |
| + } |
| + } |
| + |
| + Handle<FixedArray> arguments(FixedArray::cast(parameter_map->get(1))); |
| + if (arguments->IsDictionary()) { |
| + SetDictionaryElement(object, index, value, attributes); |
| + } else { |
| + SetFastElement(object, index, value); |
| + } |
| +} |
| + |
| + |
| +void JSObject::SetDictionaryElement(Handle<JSObject> object, uint32_t index, |
| + Handle<Object> value, |
| + PropertyAttributes attributes) { |
| + // TODO(verwaest): Handle with the elements accessor. |
| Isolate* isolate = object->GetIsolate(); |
| // Insert element in the dictionary. |
| Handle<FixedArray> elements(FixedArray::cast(object->elements())); |
| bool is_arguments = |
| (elements->map() == isolate->heap()->sloppy_arguments_elements_map()); |
| + |
| + DCHECK(object->HasDictionaryElements() || |
| + object->HasDictionaryArgumentsElements()); |
| + |
| Handle<SeededNumberDictionary> dictionary(is_arguments |
| ? SeededNumberDictionary::cast(elements->get(1)) |
| : SeededNumberDictionary::cast(*elements)); |
| @@ -12705,71 +12662,28 @@ MaybeHandle<Object> JSObject::SetDictionaryElement( |
| if (entry != SeededNumberDictionary::kNotFound) { |
| Handle<Object> element(dictionary->ValueAt(entry), isolate); |
| PropertyDetails details = dictionary->DetailsAt(entry); |
| - if (details.type() == ACCESSOR_CONSTANT && set_mode == SET_PROPERTY) { |
| - return SetElementWithCallback(object, element, index, value, object, |
| - language_mode); |
| - } else if (set_mode == DEFINE_PROPERTY && !details.IsConfigurable() && |
| - details.kind() == kAccessor) { |
| - return RedefineNonconfigurableProperty( |
| - isolate, isolate->factory()->NewNumberFromUint(index), |
| - isolate->factory()->undefined_value(), language_mode); |
| - |
| - } else if ((set_mode == DEFINE_PROPERTY && !details.IsConfigurable() && |
| - details.IsReadOnly()) || |
| - (set_mode == SET_PROPERTY && details.IsReadOnly() && |
| - !element->IsTheHole())) { |
| - // If a value has not been initialized we allow writing to it even if it |
| - // is read-only (a declared const that has not been initialized). |
| - return WriteToReadOnlyProperty( |
| - isolate, object, isolate->factory()->NewNumberFromUint(index), |
| - isolate->factory()->undefined_value(), language_mode); |
| - } else { |
| - DCHECK(details.IsConfigurable() || !details.IsReadOnly() || |
| - element->IsTheHole()); |
| - dictionary->UpdateMaxNumberKey(index); |
| - if (set_mode == DEFINE_PROPERTY) { |
| - details = PropertyDetails(attributes, DATA, details.dictionary_index(), |
| - PropertyCellType::kNoCell); |
| - dictionary->DetailsAtPut(entry, details); |
| - } |
| - |
| - // Elements of the arguments object in slow mode might be slow aliases. |
| - if (is_arguments && element->IsAliasedArgumentsEntry()) { |
| - Handle<AliasedArgumentsEntry> entry = |
| - Handle<AliasedArgumentsEntry>::cast(element); |
| - Handle<Context> context(Context::cast(elements->get(0))); |
| - int context_index = entry->aliased_context_slot(); |
| - DCHECK(!context->get(context_index)->IsTheHole()); |
| - context->set(context_index, *value); |
| - // For elements that are still writable we keep slow aliasing. |
| - if (!details.IsReadOnly()) value = element; |
| - } |
| - dictionary->ValueAtPut(entry, *value); |
| + DCHECK(details.IsConfigurable() || !details.IsReadOnly() || |
| + element->IsTheHole()); |
| + dictionary->UpdateMaxNumberKey(index); |
| + |
| + details = PropertyDetails(attributes, DATA, details.dictionary_index(), |
| + PropertyCellType::kNoCell); |
| + dictionary->DetailsAtPut(entry, details); |
| + |
| + // Elements of the arguments object in slow mode might be slow aliases. |
| + if (is_arguments && element->IsAliasedArgumentsEntry()) { |
| + Handle<AliasedArgumentsEntry> entry = |
| + Handle<AliasedArgumentsEntry>::cast(element); |
| + Handle<Context> context(Context::cast(elements->get(0))); |
| + int context_index = entry->aliased_context_slot(); |
| + DCHECK(!context->get(context_index)->IsTheHole()); |
| + context->set(context_index, *value); |
| + // For elements that are still writable we keep slow aliasing. |
| + if (!details.IsReadOnly()) value = element; |
| } |
| + dictionary->ValueAtPut(entry, *value); |
| } else { |
| - // Index not already used. Look for an accessor in the prototype chain. |
| - // Can cause GC! |
| - if (check_prototype) { |
| - bool found; |
| - MaybeHandle<Object> result = SetElementWithCallbackSetterInPrototypes( |
| - object, index, value, &found, language_mode); |
| - if (found) return result; |
| - } |
| - |
| - // When we set the is_extensible flag to false we always force the |
| - // element into dictionary mode (and force them to stay there). |
| - if (!object->map()->is_extensible()) { |
| - if (is_sloppy(language_mode)) { |
| - return isolate->factory()->undefined_value(); |
| - } else { |
| - Handle<Object> number = isolate->factory()->NewNumberFromUint(index); |
| - Handle<String> name = isolate->factory()->NumberToString(number); |
| - THROW_NEW_ERROR( |
| - isolate, NewTypeError(MessageTemplate::kObjectNotExtensible, name), |
| - Object); |
| - } |
| - } |
| - |
| + DCHECK(object->map()->is_extensible()); |
| PropertyDetails details(attributes, DATA, 0, PropertyCellType::kNoCell); |
| Handle<SeededNumberDictionary> new_dictionary = |
| SeededNumberDictionary::AddNumberEntry(dictionary, index, value, |
| @@ -12820,58 +12734,34 @@ MaybeHandle<Object> JSObject::SetDictionaryElement( |
| } |
| #endif |
| } |
| - return value; |
| } |
| -MaybeHandle<Object> JSObject::SetFastDoubleElement(Handle<JSObject> object, |
| - uint32_t index, |
| - Handle<Object> value, |
| - LanguageMode language_mode, |
| - bool check_prototype) { |
| +void JSObject::SetFastDoubleElement(Handle<JSObject> object, uint32_t index, |
| + Handle<Object> value) { |
| DCHECK(object->HasFastDoubleElements()); |
| Handle<FixedArrayBase> base_elms(FixedArrayBase::cast(object->elements())); |
| uint32_t elms_length = static_cast<uint32_t>(base_elms->length()); |
| + uint32_t length = elms_length; |
| - // If storing to an element that isn't in the array, pass the store request |
| - // up the prototype chain before storing in the receiver's elements. |
| - if (check_prototype && |
| - (index >= elms_length || |
| - Handle<FixedDoubleArray>::cast(base_elms)->is_the_hole(index))) { |
| - bool found; |
| - MaybeHandle<Object> result = SetElementWithCallbackSetterInPrototypes( |
| - object, index, value, &found, language_mode); |
| - if (found) return result; |
| - } |
| - |
| - // If the value object is not a heap number, switch to fast elements and try |
| - // again. |
| - bool value_is_smi = value->IsSmi(); |
| bool introduces_holes = true; |
| - uint32_t length = elms_length; |
| if (object->IsJSArray()) { |
| + // In case of JSArray, the length does not equal the capacity. |
| CHECK(Handle<JSArray>::cast(object)->length()->ToArrayLength(&length)); |
| introduces_holes = index > length; |
| } else { |
| introduces_holes = index >= elms_length; |
| } |
| + // If the value object is not a heap number, switch to fast elements and try |
| + // again. |
| if (!value->IsNumber()) { |
| SetFastElementsCapacityAndLength(object, elms_length, length, |
| kDontAllowSmiElements); |
| - Handle<Object> result; |
| - ASSIGN_RETURN_ON_EXCEPTION( |
| - object->GetIsolate(), result, |
| - SetFastElement(object, index, value, language_mode, check_prototype), |
| - Object); |
| - JSObject::ValidateElements(object); |
| - return result; |
| + SetFastElement(object, index, value); |
| + return; |
| } |
| - double double_value = value_is_smi |
| - ? static_cast<double>(Handle<Smi>::cast(value)->value()) |
| - : Handle<HeapNumber>::cast(value)->value(); |
| - |
| // If the array is growing, and it's not growth by a single element at the |
| // end, make sure that the ElementsKind is HOLEY. |
| ElementsKind elements_kind = object->GetElementsKind(); |
| @@ -12883,7 +12773,7 @@ MaybeHandle<Object> JSObject::SetFastDoubleElement(Handle<JSObject> object, |
| // Check whether there is extra space in the fixed array. |
| if (index < elms_length) { |
| Handle<FixedDoubleArray> elms(FixedDoubleArray::cast(object->elements())); |
| - elms->set(index, double_value); |
| + elms->set(index, value->Number()); |
| if (object->IsJSArray()) { |
| // Update the length of the array if needed. |
| uint32_t array_length = 0; |
| @@ -12893,7 +12783,7 @@ MaybeHandle<Object> JSObject::SetFastDoubleElement(Handle<JSObject> object, |
| Handle<JSArray>::cast(object)->set_length(Smi::FromInt(index + 1)); |
| } |
| } |
| - return value; |
| + return; |
| } |
| // Allow gap in fast case. |
| @@ -12903,9 +12793,9 @@ MaybeHandle<Object> JSObject::SetFastDoubleElement(Handle<JSObject> object, |
| if (!object->ShouldConvertToSlowElements(new_capacity)) { |
| DCHECK(static_cast<uint32_t>(new_capacity) > index); |
| SetFastDoubleElementsCapacityAndLength(object, new_capacity, index + 1); |
| - FixedDoubleArray::cast(object->elements())->set(index, double_value); |
| + FixedDoubleArray::cast(object->elements())->set(index, value->Number()); |
| JSObject::ValidateElements(object); |
| - return value; |
| + return; |
| } |
| } |
| @@ -12917,277 +12807,110 @@ MaybeHandle<Object> JSObject::SetFastDoubleElement(Handle<JSObject> object, |
| NormalizeElements(object); |
| DCHECK(object->HasDictionaryElements()); |
| - return SetElement(object, index, value, NONE, language_mode, check_prototype); |
| + SetDictionaryElement(object, index, value, NONE); |
| + return; |
|
Igor Sheludko
2015/06/11 13:44:25
nit: Do we need return; here?
Toon Verwaest
2015/06/11 14:26:50
Done.
|
| } |
| +// static |
| MaybeHandle<Object> JSReceiver::SetElement(Handle<JSReceiver> object, |
| uint32_t index, Handle<Object> value, |
| - PropertyAttributes attributes, |
| LanguageMode language_mode) { |
| - if (object->IsJSProxy()) { |
| - return JSProxy::SetElementWithHandler(Handle<JSProxy>::cast(object), object, |
| - index, value, language_mode); |
| - } |
| - return JSObject::SetElement(Handle<JSObject>::cast(object), index, value, |
| - attributes, language_mode); |
| -} |
| - |
| - |
| -MaybeHandle<Object> JSObject::SetOwnElement(Handle<JSObject> object, |
| - uint32_t index, |
| - Handle<Object> value, |
| - PropertyAttributes attributes, |
| - LanguageMode language_mode) { |
| - DCHECK(!object->HasExternalArrayElements()); |
| - return JSObject::SetElement(object, index, value, attributes, language_mode, |
| - false); |
| -} |
| - |
| - |
| -MaybeHandle<Object> JSObject::SetElement(Handle<JSObject> object, |
| - uint32_t index, Handle<Object> value, |
| - PropertyAttributes attributes, |
| - LanguageMode language_mode, |
| - bool check_prototype, |
| - SetPropertyMode set_mode) { |
| Isolate* isolate = object->GetIsolate(); |
| + LookupIterator it(isolate, object, index); |
| + return SetProperty(&it, value, language_mode, MAY_BE_STORE_FROM_KEYED); |
| +} |
| - if (object->HasExternalArrayElements() || |
| - object->HasFixedTypedArrayElements()) { |
| - if (!value->IsNumber() && !value->IsUndefined()) { |
| - ASSIGN_RETURN_ON_EXCEPTION( |
| - isolate, value, |
| - Execution::ToNumber(isolate, value), Object); |
| - } |
| - } |
| - |
| - // Check access rights if needed. |
| - if (object->IsAccessCheckNeeded()) { |
| - if (!isolate->MayAccess(object)) { |
| - isolate->ReportFailedAccessCheck(object); |
| - RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); |
| - return value; |
| - } |
| - } |
| - |
| - if (object->IsJSGlobalProxy()) { |
| - PrototypeIterator iter(isolate, object); |
| - if (iter.IsAtEnd()) return value; |
| - DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject()); |
| - return SetElement( |
| - Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)), index, |
| - value, attributes, language_mode, check_prototype, set_mode); |
| - } |
| - |
| - // Don't allow element properties to be redefined for external arrays. |
| - if ((object->HasExternalArrayElements() || |
| - object->HasFixedTypedArrayElements()) && |
| - set_mode == DEFINE_PROPERTY) { |
| - THROW_NEW_ERROR( |
| - isolate, NewTypeError(MessageTemplate::kRedefineExternalArray), Object); |
| - } |
| - |
| - // Normalize the elements to enable attributes on the property. |
| - if ((attributes & (DONT_DELETE | DONT_ENUM | READ_ONLY)) != 0) { |
| - Handle<SeededNumberDictionary> dictionary = NormalizeElements(object); |
| - // Make sure that we never go back to fast case. |
| - dictionary->set_requires_slow_elements(); |
| - } |
| - if (!object->map()->is_observed()) { |
| - return object->HasIndexedInterceptor() |
| - ? SetElementWithInterceptor(object, index, value, attributes, |
| - language_mode, check_prototype, |
| - set_mode) |
| - : SetElementWithoutInterceptor(object, index, value, attributes, |
| - language_mode, check_prototype, |
| - set_mode); |
| - } |
| +// static |
| +MaybeHandle<Object> JSObject::AddDataElement(Handle<JSObject> receiver, |
| + uint32_t index, |
| + Handle<Object> value, |
| + PropertyAttributes attributes) { |
| + DCHECK(receiver->map()->is_extensible()); |
| - Maybe<PropertyAttributes> maybe = |
| - JSReceiver::GetOwnElementAttributes(object, index); |
| - if (!maybe.IsJust()) return MaybeHandle<Object>(); |
| - PropertyAttributes old_attributes = maybe.FromJust(); |
| + Isolate* isolate = receiver->GetIsolate(); |
| - Handle<Object> old_value = isolate->factory()->the_hole_value(); |
| + // TODO(verwaest): Use ElementAccessor. |
| Handle<Object> old_length_handle; |
| - Handle<Object> new_length_handle; |
| - |
| - if (old_attributes != ABSENT) { |
| - if (GetOwnElementAccessorPair(object, index).is_null()) { |
| - old_value = Object::GetElement(isolate, object, index).ToHandleChecked(); |
| - } |
| - } else if (object->IsJSArray()) { |
| - // Store old array length in case adding an element grows the array. |
| - old_length_handle = handle(Handle<JSArray>::cast(object)->length(), |
| - isolate); |
| + if (receiver->IsJSArray() && receiver->map()->is_observed()) { |
| + old_length_handle = handle(JSArray::cast(*receiver)->length(), isolate); |
| } |
| - // Check for lookup interceptor |
| - Handle<Object> result; |
| - ASSIGN_RETURN_ON_EXCEPTION( |
| - isolate, result, |
| - object->HasIndexedInterceptor() |
| - ? SetElementWithInterceptor(object, index, value, attributes, |
| - language_mode, check_prototype, set_mode) |
| - : SetElementWithoutInterceptor(object, index, value, attributes, |
| - language_mode, check_prototype, |
| - set_mode), |
| - Object); |
| - |
| - Handle<String> name = isolate->factory()->Uint32ToString(index); |
| - maybe = GetOwnElementAttributes(object, index); |
| - if (!maybe.IsJust()) return MaybeHandle<Object>(); |
| - PropertyAttributes new_attributes = maybe.FromJust(); |
| - |
| - if (old_attributes == ABSENT) { |
| - if (object->IsJSArray() && |
| - !old_length_handle->SameValue( |
| - Handle<JSArray>::cast(object)->length())) { |
| - new_length_handle = handle(Handle<JSArray>::cast(object)->length(), |
| - isolate); |
| - uint32_t old_length = 0; |
| - uint32_t new_length = 0; |
| - CHECK(old_length_handle->ToArrayLength(&old_length)); |
| - CHECK(new_length_handle->ToArrayLength(&new_length)); |
| - |
| - RETURN_ON_EXCEPTION( |
| - isolate, BeginPerformSplice(Handle<JSArray>::cast(object)), Object); |
| - RETURN_ON_EXCEPTION( |
| - isolate, EnqueueChangeRecord(object, "add", name, old_value), Object); |
| - RETURN_ON_EXCEPTION( |
| - isolate, EnqueueChangeRecord(object, "update", |
| - isolate->factory()->length_string(), |
| - old_length_handle), |
| - Object); |
| - RETURN_ON_EXCEPTION( |
| - isolate, EndPerformSplice(Handle<JSArray>::cast(object)), Object); |
| - Handle<JSArray> deleted = isolate->factory()->NewJSArray(0); |
| - RETURN_ON_EXCEPTION( |
| - isolate, |
| - EnqueueSpliceRecord(Handle<JSArray>::cast(object), old_length, |
| - deleted, new_length - old_length), |
| - Object); |
| - } else { |
| - RETURN_ON_EXCEPTION( |
| - isolate, EnqueueChangeRecord(object, "add", name, old_value), Object); |
| - } |
| - } else if (old_value->IsTheHole()) { |
| - RETURN_ON_EXCEPTION( |
| - isolate, EnqueueChangeRecord(object, "reconfigure", name, old_value), |
| - Object); |
| - } else { |
| - Handle<Object> new_value = |
| - Object::GetElement(isolate, object, index).ToHandleChecked(); |
| - bool value_changed = !old_value->SameValue(*new_value); |
| - if (old_attributes != new_attributes) { |
| - if (!value_changed) old_value = isolate->factory()->the_hole_value(); |
| - RETURN_ON_EXCEPTION( |
| - isolate, EnqueueChangeRecord(object, "reconfigure", name, old_value), |
| - Object); |
| - } else if (value_changed) { |
| - RETURN_ON_EXCEPTION( |
| - isolate, EnqueueChangeRecord(object, "update", name, old_value), |
| - Object); |
| - } |
| + if (attributes != NONE) { |
| + Handle<SeededNumberDictionary> d = JSObject::NormalizeElements(receiver); |
| + // TODO(verwaest): Move this into NormalizeElements. |
| + d->set_requires_slow_elements(); |
| } |
| - return result; |
| -} |
| - |
| + Handle<Object> result = value; |
| -MaybeHandle<Object> JSObject::SetElementWithoutInterceptor( |
| - Handle<JSObject> object, uint32_t index, Handle<Object> value, |
| - PropertyAttributes attributes, LanguageMode language_mode, |
| - bool check_prototype, SetPropertyMode set_mode) { |
| - DCHECK(object->HasDictionaryElements() || |
| - object->HasDictionaryArgumentsElements() || |
| - (attributes & (DONT_DELETE | DONT_ENUM | READ_ONLY)) == 0); |
| - Isolate* isolate = object->GetIsolate(); |
| - if (FLAG_trace_external_array_abuse && |
| - IsExternalArrayElementsKind(object->GetElementsKind())) { |
| - CheckArrayAbuse(object, "external elements write", index); |
| - } |
| - if (FLAG_trace_js_array_abuse && |
| - !IsExternalArrayElementsKind(object->GetElementsKind())) { |
| - if (object->IsJSArray()) { |
| - CheckArrayAbuse(object, "elements write", index, true); |
| - } |
| - } |
| - if (object->IsJSArray() && JSArray::WouldChangeReadOnlyLength( |
| - Handle<JSArray>::cast(object), index)) { |
| - if (is_sloppy(language_mode)) { |
| - return value; |
| - } else { |
| - return JSArray::ReadOnlyLengthError(Handle<JSArray>::cast(object)); |
| - } |
| - } |
| - switch (object->GetElementsKind()) { |
| + switch (receiver->GetElementsKind()) { |
| case FAST_SMI_ELEMENTS: |
| case FAST_ELEMENTS: |
| case FAST_HOLEY_SMI_ELEMENTS: |
| case FAST_HOLEY_ELEMENTS: |
| - return SetFastElement(object, index, value, language_mode, |
| - check_prototype); |
| + SetFastElement(receiver, index, value); |
| + break; |
| case FAST_DOUBLE_ELEMENTS: |
| case FAST_HOLEY_DOUBLE_ELEMENTS: |
| - return SetFastDoubleElement(object, index, value, language_mode, |
| - check_prototype); |
| + SetFastDoubleElement(receiver, index, value); |
| + break; |
| -#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ |
| - case EXTERNAL_##TYPE##_ELEMENTS: { \ |
| - Handle<External##Type##Array> array( \ |
| - External##Type##Array::cast(object->elements())); \ |
| - return External##Type##Array::SetValue(object, array, index, value); \ |
| - } \ |
| - case TYPE##_ELEMENTS: { \ |
| - Handle<Fixed##Type##Array> array( \ |
| - Fixed##Type##Array::cast(object->elements())); \ |
| - return Fixed##Type##Array::SetValue(object, array, index, value); \ |
| - } |
| + case DICTIONARY_ELEMENTS: |
| + SetDictionaryElement(receiver, index, value, attributes); |
| + break; |
| + case SLOPPY_ARGUMENTS_ELEMENTS: |
| + SetSloppyArgumentsElements(receiver, index, value, attributes); |
| + break; |
| - TYPED_ARRAYS(TYPED_ARRAY_CASE) |
| +// Elements cannot be added to typed arrays. |
| +#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ |
| + case EXTERNAL_##TYPE##_ELEMENTS: \ |
| + case TYPE##_ELEMENTS: |
| + |
| + TYPED_ARRAYS(TYPED_ARRAY_CASE) |
| #undef TYPED_ARRAY_CASE |
| + UNREACHABLE(); |
| + break; |
| + } |
| - case DICTIONARY_ELEMENTS: |
| - return SetDictionaryElement(object, index, value, attributes, |
| - language_mode, check_prototype, set_mode); |
| - case SLOPPY_ARGUMENTS_ELEMENTS: { |
| - Handle<FixedArray> parameter_map(FixedArray::cast(object->elements())); |
| - uint32_t length = parameter_map->length(); |
| - Handle<Object> probe = index < length - 2 ? |
| - Handle<Object>(parameter_map->get(index + 2), isolate) : |
| - Handle<Object>(); |
| - if (!probe.is_null() && !probe->IsTheHole()) { |
| - Handle<Context> context(Context::cast(parameter_map->get(0))); |
| - int context_index = Handle<Smi>::cast(probe)->value(); |
| - DCHECK(!context->get(context_index)->IsTheHole()); |
| - context->set(context_index, *value); |
| - // Redefining attributes of an aliased element destroys fast aliasing. |
| - if (set_mode == SET_PROPERTY || attributes == NONE) return value; |
| - parameter_map->set_the_hole(index + 2); |
| - // For elements that are still writable we re-establish slow aliasing. |
| - if ((attributes & READ_ONLY) == 0) { |
| - value = Handle<Object>::cast( |
| - isolate->factory()->NewAliasedArgumentsEntry(context_index)); |
| - } |
| - } |
| - Handle<FixedArray> arguments(FixedArray::cast(parameter_map->get(1))); |
| - if (arguments->IsDictionary()) { |
| - return SetDictionaryElement(object, index, value, attributes, |
| - language_mode, check_prototype, set_mode); |
| - } else { |
| - return SetFastElement(object, index, value, language_mode, |
| - check_prototype); |
| - } |
| - } |
| + if (!old_length_handle.is_null() && |
| + !old_length_handle->SameValue( |
| + Handle<JSArray>::cast(receiver)->length())) { |
|
Igor Sheludko
2015/06/11 13:44:25
What about adding DCHECK(receiver->map()->is_obser
Toon Verwaest
2015/06/11 14:26:50
Done.
|
| + Handle<JSArray> array = Handle<JSArray>::cast(receiver); |
| + Handle<String> name = isolate->factory()->Uint32ToString(index); |
| + Handle<Object> new_length_handle(array->length(), isolate); |
| + uint32_t old_length = 0; |
| + uint32_t new_length = 0; |
| + CHECK(old_length_handle->ToArrayLength(&old_length)); |
| + CHECK(new_length_handle->ToArrayLength(&new_length)); |
| + |
| + RETURN_ON_EXCEPTION(isolate, BeginPerformSplice(array), Object); |
| + RETURN_ON_EXCEPTION( |
| + isolate, JSObject::EnqueueChangeRecord( |
| + array, "add", name, isolate->factory()->the_hole_value()), |
| + Object); |
| + RETURN_ON_EXCEPTION( |
| + isolate, JSObject::EnqueueChangeRecord( |
| + array, "update", isolate->factory()->length_string(), |
| + old_length_handle), |
| + Object); |
| + RETURN_ON_EXCEPTION(isolate, EndPerformSplice(array), Object); |
| + Handle<JSArray> deleted = isolate->factory()->NewJSArray(0); |
| + RETURN_ON_EXCEPTION(isolate, EnqueueSpliceRecord(array, old_length, deleted, |
| + new_length - old_length), |
| + Object); |
| + } else if (receiver->map()->is_observed()) { |
| + Handle<String> name = isolate->factory()->Uint32ToString(index); |
| + RETURN_ON_EXCEPTION(isolate, JSObject::EnqueueChangeRecord( |
| + receiver, "add", name, |
| + isolate->factory()->the_hole_value()), |
| + Object); |
| } |
| - // All possible cases have been handled above. Add a return to avoid the |
| - // complaints from the compiler. |
| - UNREACHABLE(); |
| - return isolate->factory()->null_value(); |
| + |
| + return result; |
| } |
| @@ -14978,6 +14701,23 @@ size_t JSTypedArray::element_size() { |
| } |
| +Handle<Object> FixedArray::SetValue(Handle<JSObject> holder, |
| + Handle<FixedArray> array, uint32_t index, |
| + Handle<Object> value) { |
| + array->set(index, *value); |
| + return value; |
| +} |
| + |
| + |
| +Handle<Object> FixedDoubleArray::SetValue(Handle<JSObject> holder, |
| + Handle<FixedDoubleArray> array, |
| + uint32_t index, |
| + Handle<Object> value) { |
| + array->set(index, value->Number()); |
| + return value; |
| +} |
| + |
| + |
| Handle<Object> ExternalUint8ClampedArray::SetValue( |
| Handle<JSObject> holder, Handle<ExternalUint8ClampedArray> array, |
| uint32_t index, Handle<Object> value) { |