Cleanup Delete backend implementation.

BUG=v8:4137
LOG=n

Review URL: https://codereview.chromium.org/1218813012

Cr-Commit-Position: refs/heads/master@{#29477}
This commit is contained in:
verwaest 2015-07-06 01:53:41 -07:00 committed by Commit bot
parent b0a852e8c2
commit dba715ec6a
7 changed files with 120 additions and 130 deletions

View File

@ -718,8 +718,9 @@ class ElementsAccessorBase : public ElementsAccessor {
ElementsAccessorSubclass::GrowCapacityAndConvertImpl(object, capacity);
}
virtual void Delete(Handle<JSObject> obj, uint32_t key,
LanguageMode language_mode) override = 0;
virtual void Delete(Handle<JSObject> obj, uint32_t index) final {
ElementsAccessorSubclass::DeleteImpl(obj, index);
}
static void CopyElementsImpl(FixedArrayBase* from, uint32_t from_start,
FixedArrayBase* to, ElementsKind from_kind,
@ -976,37 +977,16 @@ class DictionaryElementsAccessor
}
static void DeleteCommon(Handle<JSObject> obj, uint32_t key,
LanguageMode language_mode) {
Isolate* isolate = obj->GetIsolate();
Handle<FixedArray> backing_store(FixedArray::cast(obj->elements()),
isolate);
bool is_arguments = obj->HasSloppyArgumentsElements();
if (is_arguments) {
backing_store = handle(FixedArray::cast(backing_store->get(1)), isolate);
}
Handle<SeededNumberDictionary> dictionary =
Handle<SeededNumberDictionary>::cast(backing_store);
int entry = dictionary->FindEntry(key);
if (entry != SeededNumberDictionary::kNotFound) {
Handle<Object> result =
SeededNumberDictionary::DeleteProperty(dictionary, entry);
USE(result);
DCHECK(result->IsTrue());
Handle<FixedArray> new_elements =
SeededNumberDictionary::Shrink(dictionary, key);
if (is_arguments) {
FixedArray::cast(obj->elements())->set(1, *new_elements);
} else {
obj->set_elements(*new_elements);
}
}
}
virtual void Delete(Handle<JSObject> obj, uint32_t key,
LanguageMode language_mode) final {
DeleteCommon(obj, key, language_mode);
static void DeleteImpl(Handle<JSObject> obj, uint32_t index) {
// TODO(verwaest): Remove reliance on key in Shrink.
Handle<SeededNumberDictionary> dict(
SeededNumberDictionary::cast(obj->elements()));
uint32_t key = GetKeyForIndexImpl(*dict, index);
Handle<Object> result = SeededNumberDictionary::DeleteProperty(dict, index);
USE(result);
DCHECK(result->IsTrue());
Handle<FixedArray> new_elements = SeededNumberDictionary::Shrink(dict, key);
obj->set_elements(*new_elements);
}
static Handle<Object> GetImpl(Handle<JSObject> obj, uint32_t key,
@ -1101,58 +1081,38 @@ class FastElementsAccessor
typedef typename KindTraits::BackingStore BackingStore;
static void DeleteCommon(Handle<JSObject> obj, uint32_t key,
LanguageMode language_mode) {
static void DeleteCommon(Handle<JSObject> obj, uint32_t index,
Handle<FixedArrayBase> store) {
DCHECK(obj->HasFastSmiOrObjectElements() ||
obj->HasFastDoubleElements() ||
obj->HasFastArgumentsElements());
Isolate* isolate = obj->GetIsolate();
Heap* heap = obj->GetHeap();
Handle<FixedArrayBase> elements(obj->elements());
if (*elements == heap->empty_fixed_array()) return;
Handle<BackingStore> backing_store = Handle<BackingStore>::cast(store);
backing_store->set_the_hole(index);
Handle<BackingStore> backing_store = Handle<BackingStore>::cast(elements);
bool is_sloppy_arguments_elements_map =
backing_store->map() == heap->sloppy_arguments_elements_map();
if (is_sloppy_arguments_elements_map) {
backing_store = handle(
BackingStore::cast(Handle<FixedArray>::cast(backing_store)->get(1)),
isolate);
// TODO(verwaest): Move this out of elements.cc.
// If an old space backing store is larger than a certain size and
// has too few used values, normalize it.
// To avoid doing the check on every delete we require at least
// one adjacent hole to the value being deleted.
const int kMinLengthForSparsenessCheck = 64;
if (backing_store->length() < kMinLengthForSparsenessCheck) return;
if (backing_store->GetHeap()->InNewSpace(*backing_store)) return;
uint32_t length = 0;
if (obj->IsJSArray()) {
JSArray::cast(*obj)->length()->ToArrayLength(&length);
} else {
length = static_cast<uint32_t>(store->length());
}
uint32_t length = static_cast<uint32_t>(
obj->IsJSArray()
? Smi::cast(Handle<JSArray>::cast(obj)->length())->value()
: backing_store->length());
if (key < length) {
if (!is_sloppy_arguments_elements_map) {
ElementsKind kind = KindTraits::Kind;
if (IsFastPackedElementsKind(kind)) {
JSObject::TransitionElementsKind(obj, GetHoleyElementsKind(kind));
}
if (IsFastSmiOrObjectElementsKind(KindTraits::Kind)) {
Handle<Object> writable = JSObject::EnsureWritableFastElements(obj);
backing_store = Handle<BackingStore>::cast(writable);
}
if ((index > 0 && backing_store->is_the_hole(index - 1)) ||
(index + 1 < length && backing_store->is_the_hole(index + 1))) {
int num_used = 0;
for (int i = 0; i < backing_store->length(); ++i) {
if (!backing_store->is_the_hole(i)) ++num_used;
// Bail out early if more than 1/4 is used.
if (4 * num_used > backing_store->length()) break;
}
backing_store->set_the_hole(key);
// If an old space backing store is larger than a certain size and
// has too few used values, normalize it.
// To avoid doing the check on every delete we require at least
// one adjacent hole to the value being deleted.
const int kMinLengthForSparsenessCheck = 64;
if (backing_store->length() >= kMinLengthForSparsenessCheck &&
!heap->InNewSpace(*backing_store) &&
((key > 0 && backing_store->is_the_hole(key - 1)) ||
(key + 1 < length && backing_store->is_the_hole(key + 1)))) {
int num_used = 0;
for (int i = 0; i < backing_store->length(); ++i) {
if (!backing_store->is_the_hole(i)) ++num_used;
// Bail out early if more than 1/4 is used.
if (4 * num_used > backing_store->length()) break;
}
if (4 * num_used <= backing_store->length()) {
JSObject::NormalizeElements(obj);
}
if (4 * num_used <= backing_store->length()) {
JSObject::NormalizeElements(obj);
}
}
}
@ -1193,9 +1153,15 @@ class FastElementsAccessor
FastElementsAccessorSubclass::SetImpl(object->elements(), index, *value);
}
virtual void Delete(Handle<JSObject> obj, uint32_t key,
LanguageMode language_mode) final {
DeleteCommon(obj, key, language_mode);
static void DeleteImpl(Handle<JSObject> obj, uint32_t index) {
ElementsKind kind = KindTraits::Kind;
if (IsFastPackedElementsKind(kind)) {
JSObject::TransitionElementsKind(obj, GetHoleyElementsKind(kind));
}
if (IsFastSmiOrObjectElementsKind(KindTraits::Kind)) {
JSObject::EnsureWritableFastElements(obj);
}
DeleteCommon(obj, index, handle(obj->elements()));
}
static bool HasIndexImpl(FixedArrayBase* backing_store, uint32_t index) {
@ -1436,9 +1402,8 @@ class TypedElementsAccessor
UNREACHABLE();
}
virtual void Delete(Handle<JSObject> obj, uint32_t key,
LanguageMode language_mode) final {
// External arrays always ignore deletes.
static void DeleteImpl(Handle<JSObject> obj, uint32_t index) {
UNREACHABLE();
}
static uint32_t GetIndexForKeyImpl(JSObject* holder,
@ -1514,19 +1479,6 @@ class SloppyArgumentsElementsAccessor
}
}
virtual void Delete(Handle<JSObject> obj, uint32_t key,
LanguageMode language_mode) final {
FixedArray* parameter_map = FixedArray::cast(obj->elements());
if (!GetParameterMapArg(parameter_map, key)->IsTheHole()) {
// TODO(kmillikin): We could check if this was the last aliased
// parameter, and revert to normal elements in that case. That
// would enable GC of the context.
parameter_map->set_the_hole(key + 2);
} else {
ArgumentsAccessor::DeleteCommon(obj, key, language_mode);
}
}
static void GrowCapacityAndConvertImpl(Handle<JSObject> object,
uint32_t capacity) {
UNREACHABLE();
@ -1612,6 +1564,20 @@ class SloppyArgumentsElementsAccessor
? parameter_map->get(key + 2)
: Object::cast(parameter_map->GetHeap()->the_hole_value());
}
static void DeleteImpl(Handle<JSObject> obj, uint32_t index) {
FixedArray* parameter_map = FixedArray::cast(obj->elements());
uint32_t length = static_cast<uint32_t>(parameter_map->length()) - 2;
if (index < length) {
// TODO(kmillikin): We could check if this was the last aliased
// parameter, and revert to normal elements in that case. That
// would enable GC of the context.
parameter_map->set_the_hole(index + 2);
} else {
SloppyArgumentsElementsAccessorSubclass::DeleteFromArguments(
obj, index - length);
}
}
};
@ -1625,6 +1591,19 @@ class SlowSloppyArgumentsElementsAccessor
SlowSloppyArgumentsElementsAccessor, DictionaryElementsAccessor,
ElementsKindTraits<SLOW_SLOPPY_ARGUMENTS_ELEMENTS> >(name) {}
static void DeleteFromArguments(Handle<JSObject> obj, uint32_t index) {
Handle<FixedArray> parameter_map(FixedArray::cast(obj->elements()));
Handle<SeededNumberDictionary> dict(
SeededNumberDictionary::cast(parameter_map->get(1)));
// TODO(verwaest): Remove reliance on key in Shrink.
uint32_t key = GetKeyForIndexImpl(*dict, index);
Handle<Object> result = SeededNumberDictionary::DeleteProperty(dict, index);
USE(result);
DCHECK(result->IsTrue());
Handle<FixedArray> new_elements = SeededNumberDictionary::Shrink(dict, key);
parameter_map->set(1, *new_elements);
}
static void AddImpl(Handle<JSObject> object, uint32_t key,
Handle<Object> value, PropertyAttributes attributes,
uint32_t new_capacity) {
@ -1693,6 +1672,12 @@ class FastSloppyArgumentsElementsAccessor
FastHoleyObjectElementsAccessor,
ElementsKindTraits<FAST_SLOPPY_ARGUMENTS_ELEMENTS> >(name) {}
static void DeleteFromArguments(Handle<JSObject> obj, uint32_t index) {
FixedArray* parameter_map = FixedArray::cast(obj->elements());
Handle<FixedArray> arguments(FixedArray::cast(parameter_map->get(1)));
FastHoleyObjectElementsAccessor::DeleteCommon(obj, index, arguments);
}
static void AddImpl(Handle<JSObject> object, uint32_t key,
Handle<Object> value, PropertyAttributes attributes,
uint32_t new_capacity) {

View File

@ -62,8 +62,7 @@ class ElementsAccessor {
virtual void SetLength(Handle<JSArray> holder, uint32_t new_length) = 0;
// Deletes an element in an object.
virtual void Delete(Handle<JSObject> holder, uint32_t key,
LanguageMode language_mode) = 0;
virtual void Delete(Handle<JSObject> holder, uint32_t index) = 0;
// If kCopyToEnd is specified as the copy_size to CopyElements, it copies all
// of elements from source after source_start to the destination array.

View File

@ -223,6 +223,28 @@ void LookupIterator::ApplyTransitionToDataProperty() {
}
void LookupIterator::Delete() {
Handle<JSObject> holder = Handle<JSObject>::cast(holder_);
if (IsElement()) {
ElementsAccessor* accessor = holder->GetElementsAccessor();
accessor->Delete(holder, number_);
} else {
PropertyNormalizationMode mode = holder->map()->is_prototype_map()
? KEEP_INOBJECT_PROPERTIES
: CLEAR_INOBJECT_PROPERTIES;
if (holder->HasFastProperties()) {
JSObject::NormalizeProperties(holder, mode, 0, "DeletingProperty");
holder_map_ = handle(holder->map(), isolate_);
ReloadPropertyInformation();
}
// TODO(verwaest): Get rid of the name_ argument.
JSObject::DeleteNormalizedProperty(holder, name_, number_);
JSObject::ReoptimizeIfPrototype(holder);
}
}
void LookupIterator::TransitionToAccessorProperty(
AccessorComponent component, Handle<Object> accessor,
PropertyAttributes attributes) {

View File

@ -216,6 +216,7 @@ class LookupIterator final BASE_EMBEDDED {
void ApplyTransitionToDataProperty();
void ReconfigureDataProperty(Handle<Object> value,
PropertyAttributes attributes);
void Delete();
void TransitionToAccessorProperty(AccessorComponent component,
Handle<Object> accessor,
PropertyAttributes attributes);

View File

@ -229,7 +229,7 @@ class CallSite {
T(StrongArity, \
"In strong mode, calling a function with too few arguments is deprecated") \
T(StrongDeleteProperty, \
"On strong object %, deletion of property % is deprecated") \
"Deleting property '%' of strong object '%' is deprecated") \
T(StrongImplicitConversion, \
"In strong mode, implicit conversions are deprecated") \
T(StrongRedefineDisallowed, \

View File

@ -5145,14 +5145,13 @@ MaybeHandle<Object> JSObject::DeletePropertyWithInterceptor(
void JSObject::DeleteNormalizedProperty(Handle<JSObject> object,
Handle<Name> name) {
Handle<Name> name, int entry) {
DCHECK(!object->HasFastProperties());
Isolate* isolate = object->GetIsolate();
if (object->IsGlobalObject()) {
// If we have a global object, invalidate the cell and swap in a new one.
Handle<GlobalDictionary> dictionary(object->global_dictionary());
int entry = dictionary->FindEntry(name);
DCHECK_NE(GlobalDictionary::kNotFound, entry);
auto cell = PropertyCell::InvalidateEntry(dictionary, entry);
@ -5162,7 +5161,6 @@ void JSObject::DeleteNormalizedProperty(Handle<JSObject> object,
cell->property_details().set_cell_type(PropertyCellType::kInvalidated));
} else {
Handle<NameDictionary> dictionary(object->property_dictionary());
int entry = dictionary->FindEntry(name);
DCHECK_NE(NameDictionary::kNotFound, entry);
NameDictionary::DeleteProperty(dictionary, entry);
@ -5221,16 +5219,12 @@ MaybeHandle<Object> JSReceiver::DeleteProperty(LookupIterator* it,
if (!it->IsConfigurable() || receiver->map()->is_strong()) {
// Fail if the property is not configurable, or on a strong object.
if (is_strict(language_mode)) {
if (receiver->map()->is_strong()) {
THROW_NEW_ERROR(
isolate, NewTypeError(MessageTemplate::kStrongDeleteProperty,
receiver, it->GetName()),
Object);
}
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kStrictDeleteProperty,
it->GetName(), receiver),
Object);
MessageTemplate::Template templ =
receiver->map()->is_strong()
? MessageTemplate::kStrongDeleteProperty
: MessageTemplate::kStrictDeleteProperty;
THROW_NEW_ERROR(
isolate, NewTypeError(templ, it->GetName(), receiver), Object);
}
return it->factory()->false_value();
}
@ -5243,18 +5237,7 @@ MaybeHandle<Object> JSReceiver::DeleteProperty(LookupIterator* it,
return it->factory()->true_value();
}
if (it->IsElement()) {
ElementsAccessor* accessor = holder->GetElementsAccessor();
accessor->Delete(holder, it->index(), language_mode);
} else {
PropertyNormalizationMode mode = holder->map()->is_prototype_map()
? KEEP_INOBJECT_PROPERTIES
: CLEAR_INOBJECT_PROPERTIES;
JSObject::NormalizeProperties(holder, mode, 0, "DeletingProperty");
JSObject::DeleteNormalizedProperty(holder, it->name());
JSObject::ReoptimizeIfPrototype(holder);
}
it->Delete();
if (is_observed) {
RETURN_ON_EXCEPTION(isolate,

View File

@ -2292,6 +2292,10 @@ class JSObject: public JSReceiver {
// Gets the current elements capacity and the number of used elements.
void GetElementsCapacityAndUsage(int* capacity, int* used);
// Deletes an existing named property in a normalized object.
static void DeleteNormalizedProperty(Handle<JSObject> object,
Handle<Name> name, int entry);
private:
friend class DictionaryElementsAccessor;
friend class JSReceiver;
@ -2318,10 +2322,6 @@ class JSObject: public JSReceiver {
MUST_USE_RESULT static MaybeHandle<Object> DeletePropertyWithInterceptor(
LookupIterator* it);
// Deletes an existing named property in a normalized object.
static void DeleteNormalizedProperty(Handle<JSObject> object,
Handle<Name> name);
bool ReferencesObjectFromElements(FixedArray* elements,
ElementsKind kind,
Object* object);