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:
parent
b0a852e8c2
commit
dba715ec6a
181
src/elements.cc
181
src/elements.cc
@ -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) {
|
||||
|
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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, \
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user