| Index: src/elements.cc
|
| diff --git a/src/elements.cc b/src/elements.cc
|
| index c1b4d799c1555587185c1b9ead0243ed107ef865..45ab2d9aa8a30e9b1ef2f7dfed104e1c19633d2a 100644
|
| --- a/src/elements.cc
|
| +++ b/src/elements.cc
|
| @@ -69,6 +69,10 @@ class ElementsAccessorBase : public ElementsAccessor {
|
| return obj->GetHeap()->the_hole_value();
|
| }
|
|
|
| + virtual MaybeObject* Delete(JSObject* obj,
|
| + uint32_t index,
|
| + JSReceiver::DeleteMode mode) = 0;
|
| +
|
| protected:
|
| static BackingStoreClass* GetBackingStore(JSObject* obj) {
|
| return BackingStoreClass::cast(obj->elements());
|
| @@ -85,12 +89,73 @@ class ElementsAccessorBase : public ElementsAccessor {
|
|
|
| class FastElementsAccessor
|
| : public ElementsAccessorBase<FastElementsAccessor, FixedArray> {
|
| + public:
|
| + static MaybeObject* DeleteCommon(JSObject* obj,
|
| + uint32_t index) {
|
| + ASSERT(obj->HasFastElements() || obj->HasFastArgumentsElements());
|
| + Heap* heap = obj->GetHeap();
|
| + FixedArray* backing_store = FixedArray::cast(obj->elements());
|
| + if (backing_store->map() == heap->non_strict_arguments_elements_map()) {
|
| + backing_store = FixedArray::cast(backing_store->get(1));
|
| + } else {
|
| + Object* writable;
|
| + MaybeObject* maybe = obj->EnsureWritableFastElements();
|
| + if (!maybe->ToObject(&writable)) return maybe;
|
| + backing_store = FixedArray::cast(writable);
|
| + }
|
| + uint32_t length = static_cast<uint32_t>(
|
| + obj->IsJSArray()
|
| + ? Smi::cast(JSArray::cast(obj)->length())->value()
|
| + : backing_store->length());
|
| + if (index < length) {
|
| + backing_store->set_the_hole(index);
|
| + // 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.
|
| + Object* hole = heap->the_hole_value();
|
| + const int kMinLengthForSparsenessCheck = 64;
|
| + if (backing_store->length() >= kMinLengthForSparsenessCheck &&
|
| + !heap->InNewSpace(backing_store) &&
|
| + ((index > 0 && backing_store->get(index - 1) == hole) ||
|
| + (index + 1 < length && backing_store->get(index + 1) == hole))) {
|
| + int num_used = 0;
|
| + for (int i = 0; i < backing_store->length(); ++i) {
|
| + if (backing_store->get(i) != hole) ++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()) {
|
| + MaybeObject* result = obj->NormalizeElements();
|
| + if (result->IsFailure()) return result;
|
| + }
|
| + }
|
| + }
|
| + return heap->true_value();
|
| + }
|
| +
|
| + virtual MaybeObject* Delete(JSObject* obj,
|
| + uint32_t index,
|
| + JSReceiver::DeleteMode mode) {
|
| + return DeleteCommon(obj, index);
|
| + }
|
| };
|
|
|
|
|
| class FastDoubleElementsAccessor
|
| : public ElementsAccessorBase<FastDoubleElementsAccessor,
|
| FixedDoubleArray> {
|
| + virtual MaybeObject* Delete(JSObject* obj,
|
| + uint32_t index,
|
| + JSReceiver::DeleteMode mode) {
|
| + int length = obj->IsJSArray()
|
| + ? Smi::cast(JSArray::cast(obj)->length())->value()
|
| + : FixedDoubleArray::cast(obj->elements())->length();
|
| + if (index < static_cast<uint32_t>(length)) {
|
| + FixedDoubleArray::cast(obj->elements())->set_the_hole(index);
|
| + }
|
| + return obj->GetHeap()->true_value();
|
| + }
|
| };
|
|
|
|
|
| @@ -112,6 +177,13 @@ class ExternalElementsAccessor
|
| return obj->GetHeap()->undefined_value();
|
| }
|
| }
|
| +
|
| + virtual MaybeObject* Delete(JSObject* obj,
|
| + uint32_t index,
|
| + JSReceiver::DeleteMode mode) {
|
| + // External arrays always ignore deletes.
|
| + return obj->GetHeap()->true_value();
|
| + }
|
| };
|
|
|
|
|
| @@ -194,6 +266,57 @@ class DictionaryElementsAccessor
|
| return obj->GetHeap()->the_hole_value();
|
| }
|
|
|
| +
|
| + static MaybeObject* DeleteCommon(JSObject* obj,
|
| + uint32_t index,
|
| + JSReceiver::DeleteMode mode) {
|
| + Isolate* isolate = obj->GetIsolate();
|
| + Heap* heap = isolate->heap();
|
| + FixedArray* backing_store = FixedArray::cast(obj->elements());
|
| + bool is_arguments =
|
| + (obj->GetElementsKind() == JSObject::NON_STRICT_ARGUMENTS_ELEMENTS);
|
| + if (is_arguments) {
|
| + backing_store = FixedArray::cast(backing_store->get(1));
|
| + }
|
| + NumberDictionary* dictionary = NumberDictionary::cast(backing_store);
|
| + int entry = dictionary->FindEntry(index);
|
| + if (entry != NumberDictionary::kNotFound) {
|
| + Object* result = dictionary->DeleteProperty(entry, mode);
|
| + if (result == heap->true_value()) {
|
| + MaybeObject* maybe_elements = dictionary->Shrink(index);
|
| + FixedArray* new_elements = NULL;
|
| + if (!maybe_elements->To(&new_elements)) {
|
| + return maybe_elements;
|
| + }
|
| + if (is_arguments) {
|
| + FixedArray::cast(obj->elements())->set(1, new_elements);
|
| + } else {
|
| + obj->set_elements(new_elements);
|
| + }
|
| + }
|
| + if (mode == JSObject::STRICT_DELETION &&
|
| + result == heap->false_value()) {
|
| + // In strict mode, attempting to delete a non-configurable property
|
| + // throws an exception.
|
| + HandleScope scope(isolate);
|
| + Handle<Object> holder(obj);
|
| + Handle<Object> name = isolate->factory()->NewNumberFromUint(index);
|
| + Handle<Object> args[2] = { name, holder };
|
| + Handle<Object> error =
|
| + isolate->factory()->NewTypeError("strict_delete_property",
|
| + HandleVector(args, 2));
|
| + return isolate->Throw(*error);
|
| + }
|
| + }
|
| + return heap->true_value();
|
| + }
|
| +
|
| + virtual MaybeObject* Delete(JSObject* obj,
|
| + uint32_t index,
|
| + JSReceiver::DeleteMode mode) {
|
| + return DeleteCommon(obj, index, mode);
|
| + }
|
| +
|
| virtual MaybeObject* GetWithReceiver(JSObject* obj,
|
| Object* receiver,
|
| uint32_t index) {
|
| @@ -236,6 +359,29 @@ class NonStrictArgumentsElementsAccessor
|
| }
|
| return obj->GetHeap()->the_hole_value();
|
| }
|
| +
|
| + virtual MaybeObject* Delete(JSObject* obj,
|
| + uint32_t index,
|
| + JSReceiver::DeleteMode mode) {
|
| + FixedArray* parameter_map = FixedArray::cast(obj->elements());
|
| + uint32_t length = parameter_map->length();
|
| + Object* probe =
|
| + index < (length - 2) ? parameter_map->get(index + 2) : NULL;
|
| + if (probe != NULL && !probe->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(index + 2);
|
| + } else {
|
| + FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
|
| + if (arguments->IsDictionary()) {
|
| + return DictionaryElementsAccessor::DeleteCommon(obj, index, mode);
|
| + } else {
|
| + return FastElementsAccessor::DeleteCommon(obj, index);
|
| + }
|
| + }
|
| + return obj->GetHeap()->true_value();
|
| + }
|
| };
|
|
|
|
|
|
|