Index: src/objects.cc |
diff --git a/src/objects.cc b/src/objects.cc |
index fa98881bb5e9eba0cd11a3f4a23dff9079a2fa71..91abf11a01beb2e9cd683b8ccaa97f30a9d6f55d 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()) { |
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; |
} |
@@ -3088,10 +3032,9 @@ MaybeHandle<Object> Object::SetPropertyInternal(LookupIterator* it, |
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()) break; |
+ // Check whether it makes sense to reuse the lookup iterator. Here it |
+ // might still call into setters up the prototype chain. |
return JSObject::SetPropertyWithFailedAccessCheck(it, value, |
language_mode); |
@@ -3130,7 +3073,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 +3083,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 +3143,72 @@ 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; |
+ break; |
+ } |
- 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,66 @@ 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; |
-} |
- |
+ if (it->IsElement()) { |
+ if (receiver->IsJSArray()) { |
+ Handle<JSArray> array = Handle<JSArray>::cast(receiver); |
+ if (JSArray::WouldChangeReadOnlyLength(array, it->index())) { |
+ if (is_sloppy(language_mode)) return value; |
+ return JSArray::ReadOnlyLengthError(array); |
+ } |
-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 (FLAG_trace_external_array_abuse && |
+ (array->HasExternalArrayElements() || |
+ array->HasFixedTypedArrayElements())) { |
+ CheckArrayAbuse(array, "typed elements write", it->index(), true); |
+ } |
- 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 (FLAG_trace_js_array_abuse && !array->HasExternalArrayElements() && |
+ !array->HasFixedTypedArrayElements()) { |
+ CheckArrayAbuse(array, "elements write", it->index(), false); |
} |
} |
- 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,129 +4125,174 @@ 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) { |
+ 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)) { |
+ 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)) { |
+ 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. |
@@ -7154,6 +7180,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. |
@@ -11896,48 +11959,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) { |
@@ -12022,16 +12043,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( |
@@ -12456,84 +12474,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; |
@@ -12558,14 +12498,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()); |
@@ -12577,22 +12511,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; |
@@ -12633,8 +12558,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. |
@@ -12649,7 +12574,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()) { |
@@ -12673,7 +12598,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. |
@@ -12682,22 +12607,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::SetSloppyArgumentsElement(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)); |
@@ -12706,71 +12672,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, |
@@ -12821,58 +12744,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(); |
@@ -12884,7 +12783,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; |
@@ -12894,7 +12793,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. |
@@ -12904,9 +12803,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; |
} |
} |
@@ -12918,277 +12817,111 @@ 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); |
} |
+// 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: |
+ SetSloppyArgumentsElement(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())) { |
+ // |old_length_handle| is kept null above unless the receiver is observed. |
+ DCHECK(receiver->map()->is_observed()); |
+ 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; |
} |
@@ -14979,6 +14712,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) { |