| 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) {
|
|
|