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,
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);
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(
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);
}
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);
}
-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()) {
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;
}
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);
break;
case LookupIterator::ACCESSOR: {
- if (it->property_details().IsReadOnly()) {
+ if (it->IsReadOnly()) {
return WriteToReadOnlyProperty(it, value, language_mode);
}
Handle<Object> accessors = it->GetAccessors();
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()) {
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);
}
// 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);
}
}
+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,
// 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;
}
}
-// 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.
}
+// 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.
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) {
// 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(
}
-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;
}
-// 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());
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;
}
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.
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()) {
smi_mode);
new_elements->set(index, *value);
JSObject::ValidateElements(object);
- return value;
+ return;
}
// Finally, set the new element and length.
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));
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,
}
#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();
// 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;
Handle<JSArray>::cast(object)->set_length(Smi::FromInt(index + 1));
}
}
- return value;
+ return;
}
// Allow gap in fast case.
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;
}
}
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;
}
}
+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) {