Commit f5b85cb7 authored by caitpotter88's avatar caitpotter88 Committed by Commit bot

[esnext] handle elements in FastObjectValuesOrEntries()

Add support for optimizing objects with elements, which do not invoke JS and
cannot change the shape of the Object.

BUG=v8:4663
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#35081}
parent 0af32aaf
......@@ -7,6 +7,7 @@
#include "src/arguments.h"
#include "src/conversions.h"
#include "src/factory.h"
#include "src/isolate-inl.h"
#include "src/messages.h"
#include "src/objects-inl.h"
#include "src/utils.h"
......@@ -856,6 +857,56 @@ class ElementsAccessorBase : public ElementsAccessor {
return Handle<SeededNumberDictionary>();
}
Maybe<bool> CollectValuesOrEntries(Isolate* isolate, Handle<JSObject> object,
Handle<FixedArray> values_or_entries,
bool get_entries, int* nof_items,
PropertyFilter filter) {
return ElementsAccessorSubclass::CollectValuesOrEntriesImpl(
isolate, object, values_or_entries, get_entries, nof_items, filter);
}
static Maybe<bool> CollectValuesOrEntriesImpl(
Isolate* isolate, Handle<JSObject> object,
Handle<FixedArray> values_or_entries, bool get_entries, int* nof_items,
PropertyFilter filter) {
int count = 0;
KeyAccumulator accumulator(isolate, OWN_ONLY, ALL_PROPERTIES);
accumulator.NextPrototype();
ElementsAccessorSubclass::CollectElementIndicesImpl(
object, handle(object->elements(), isolate), &accumulator, kMaxUInt32,
ALL_PROPERTIES, 0);
Handle<FixedArray> keys = accumulator.GetKeys();
for (int i = 0; i < keys->length(); ++i) {
Handle<Object> key(keys->get(i), isolate);
Handle<Object> value;
uint32_t index;
if (!key->ToUint32(&index)) continue;
uint32_t entry = ElementsAccessorSubclass::GetEntryForIndexImpl(
*object, object->elements(), index, filter);
if (entry == kMaxUInt32) continue;
PropertyDetails details =
ElementsAccessorSubclass::GetDetailsImpl(*object, entry);
if (details.kind() == kData) {
value = ElementsAccessorSubclass::GetImpl(object, entry);
} else {
LookupIterator it(isolate, object, index, LookupIterator::OWN);
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, value, Object::GetProperty(&it), Nothing<bool>());
}
if (get_entries) {
value = MakeEntryPair(isolate, index, value);
}
values_or_entries->set(count++, *value);
}
*nof_items = count;
return Just(true);
}
void CollectElementIndices(Handle<JSObject> object,
Handle<FixedArrayBase> backing_store,
KeyAccumulator* keys, uint32_t range,
......@@ -1630,6 +1681,25 @@ class FastElementsAccessor
return deleted_elements;
}
static Maybe<bool> CollectValuesOrEntriesImpl(
Isolate* isolate, Handle<JSObject> object,
Handle<FixedArray> values_or_entries, bool get_entries, int* nof_items,
PropertyFilter filter) {
int count = 0;
uint32_t length = object->elements()->length();
for (uint32_t index = 0; index < length; ++index) {
if (!HasEntryImpl(object->elements(), index)) continue;
Handle<Object> value =
FastElementsAccessorSubclass::GetImpl(object->elements(), index);
if (get_entries) {
value = MakeEntryPair(isolate, index, value);
}
values_or_entries->set(count++, *value);
}
*nof_items = count;
return Just(true);
}
private:
// SpliceShrinkStep might modify the backing_store.
static void SpliceShrinkStep(Isolate* isolate, Handle<JSArray> receiver,
......@@ -2130,6 +2200,26 @@ class TypedElementsAccessor
accumulator->AddKey(value, convert);
}
}
static Maybe<bool> CollectValuesOrEntriesImpl(
Isolate* isolate, Handle<JSObject> object,
Handle<FixedArray> values_or_entries, bool get_entries, int* nof_items,
PropertyFilter filter) {
int count = 0;
if ((filter & ONLY_CONFIGURABLE) == 0) {
Handle<FixedArrayBase> elements(object->elements());
uint32_t length = AccessorClass::GetCapacityImpl(*object, *elements);
for (uint32_t index = 0; index < length; ++index) {
Handle<Object> value = AccessorClass::GetImpl(*elements, index);
if (get_entries) {
value = MakeEntryPair(isolate, index, value);
}
values_or_entries->set(count++, *value);
}
}
*nof_items = count;
return Just(true);
}
};
......
......@@ -95,6 +95,11 @@ class ElementsAccessor {
filter, offset);
}
virtual Maybe<bool> CollectValuesOrEntries(
Isolate* isolate, Handle<JSObject> object,
Handle<FixedArray> values_or_entries, bool get_entries, int* nof_items,
PropertyFilter filter = ALL_PROPERTIES) = 0;
//
virtual Handle<FixedArray> PrependElementIndices(
Handle<JSObject> object, Handle<FixedArrayBase> backing_store,
......@@ -151,6 +156,9 @@ class ElementsAccessor {
virtual Handle<SeededNumberDictionary> Normalize(Handle<JSObject> object) = 0;
virtual uint32_t GetCapacity(JSObject* holder,
FixedArrayBase* backing_store) = 0;
protected:
friend class LookupIterator;
......@@ -176,8 +184,6 @@ class ElementsAccessor {
uint32_t destination_start, int copy_size) = 0;
private:
virtual uint32_t GetCapacity(JSObject* holder,
FixedArrayBase* backing_store) = 0;
static ElementsAccessor** elements_accessors_;
const char* name_;
......
......@@ -7756,6 +7756,30 @@ static inline uint32_t ObjectAddressForHashing(void* object) {
return value & MemoryChunk::kAlignmentMask;
}
static inline Handle<Object> MakeEntryPair(Isolate* isolate, uint32_t index,
Handle<Object> value) {
Handle<Object> key = isolate->factory()->Uint32ToString(index);
Handle<FixedArray> entry_storage =
isolate->factory()->NewUninitializedFixedArray(2);
{
entry_storage->set(0, *key, SKIP_WRITE_BARRIER);
entry_storage->set(1, *value, SKIP_WRITE_BARRIER);
}
return isolate->factory()->NewJSArrayWithElements(entry_storage,
FAST_ELEMENTS, 2);
}
static inline Handle<Object> MakeEntryPair(Isolate* isolate, Handle<Name> key,
Handle<Object> value) {
Handle<FixedArray> entry_storage =
isolate->factory()->NewUninitializedFixedArray(2);
{
entry_storage->set(0, *key, SKIP_WRITE_BARRIER);
entry_storage->set(1, *value, SKIP_WRITE_BARRIER);
}
return isolate->factory()->NewJSArrayWithElements(entry_storage,
FAST_ELEMENTS, 2);
}
#undef TYPE_CHECKER
#undef CAST_ACCESSOR
......
......@@ -8733,17 +8733,23 @@ MUST_USE_RESULT Maybe<bool> FastGetOwnValuesOrEntries(
if (!map->OnlyHasSimpleProperties()) return Just(false);
Handle<JSObject> object(JSObject::cast(*receiver));
if (object->elements() != isolate->heap()->empty_fixed_array()) {
return Just(false);
}
Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate);
int number_of_own_descriptors = map->NumberOfOwnDescriptors();
Handle<FixedArray> values_or_entries =
isolate->factory()->NewFixedArray(number_of_own_descriptors);
int number_of_own_elements =
object->GetElementsAccessor()->GetCapacity(*object, object->elements());
Handle<FixedArray> values_or_entries = isolate->factory()->NewFixedArray(
number_of_own_descriptors + number_of_own_elements);
int count = 0;
bool stable = true;
if (object->elements() != isolate->heap()->empty_fixed_array()) {
MAYBE_RETURN(object->GetElementsAccessor()->CollectValuesOrEntries(
isolate, object, values_or_entries, get_entries, &count,
ENUMERABLE_STRINGS),
Nothing<bool>());
}
bool stable = object->map() == *map;
for (int index = 0; index < number_of_own_descriptors; index++) {
Handle<Name> next_key(descriptors->GetKey(index), isolate);
......@@ -8782,12 +8788,7 @@ MUST_USE_RESULT Maybe<bool> FastGetOwnValuesOrEntries(
}
if (get_entries) {
Handle<FixedArray> entry_storage =
isolate->factory()->NewUninitializedFixedArray(2);
entry_storage->set(0, *next_key);
entry_storage->set(1, *prop_value);
prop_value = isolate->factory()->NewJSArrayWithElements(entry_storage,
FAST_ELEMENTS, 2);
prop_value = MakeEntryPair(isolate, next_key, prop_value);
}
values_or_entries->set(count, *prop_value);
......
......@@ -247,3 +247,69 @@ function TestMutateDuringEnumeration() {
assertEquals([ [ "a", 1 ], [ "b", 2 ] ], Object.entries(aMakesBEnumerable));
}
TestMutateDuringEnumeration();
(function TestElementKinds() {
var O1 = { name: "1" }, O2 = { name: "2" }, O3 = { name: "3" };
var PI = 3.141592653589793;
var E = 2.718281828459045;
function fastSloppyArguments(a, b, c) {
delete arguments[0];
arguments[0] = a;
return arguments;
}
function slowSloppyArguments(a, b, c) {
delete arguments[0];
arguments[0] = a;
Object.defineProperties(arguments, {
0: {
enumerable: true,
value: a
},
9999: {
enumerable: false,
value: "Y"
}
});
arguments[10000] = "X";
return arguments;
}
var element_kinds = {
FAST_SMI_ELEMENTS: [ [1, 2, 3], [ ["0", 1], ["1", 2], ["2", 3] ] ],
FAST_HOLEY_SMI_ELEMENTS: [ [, , 3], [ ["2", 3] ] ],
FAST_ELEMENTS: [ [O1, O2, O3], [ ["0", O1], ["1", O2], ["2", O3] ] ],
FAST_HOLEY_ELEMENTS: [ [, , O3], [ ["2", O3] ] ],
FAST_DOUBLE_ELEMENTS: [ [E, NaN, PI], [ ["0", E], ["1", NaN], ["2", PI] ] ],
FAST_HOLEY_DOUBLE_ELEMENTS: [ [, , NaN], [ ["2", NaN] ] ],
DICTIONARY_ELEMENTS: [ Object.defineProperties({ 10000: "world" }, {
100: { enumerable: true, value: "hello" },
99: { enumerable: false, value: "nope" }
}), [ ["100", "hello"], ["10000", "world" ] ] ],
FAST_SLOPPY_ARGUMENTS_ELEMENTS: [
fastSloppyArguments("a", "b", "c"),
[ ["0", "a"], ["1", "b"], ["2", "c"] ] ],
SLOW_SLOPPY_ARGUMENTS_ELEMENTS: [
slowSloppyArguments("a", "b", "c"),
[ ["0", "a"], ["1", "b"], ["2", "c"], ["10000", "X"] ] ],
FAST_STRING_WRAPPER_ELEMENTS: [ new String("str"),
[ ["0", "s"], ["1", "t"], ["2", "r"]] ],
SLOW_STRING_WRAPPER_ELEMENTS: [
Object.defineProperties(new String("str"), {
10000: { enumerable: false, value: "X" },
9999: { enumerable: true, value: "Y" }
}), [["0", "s"], ["1", "t"], ["2", "r"], ["9999", "Y"]] ],
};
for (let [kind, [object, expected]] of Object.entries(element_kinds)) {
let result1 = Object.entries(object);
assertEquals(expected, result1, `fast Object.entries() with ${kind}`);
let proxy = new Proxy(object, {});
let result2 = Object.entries(proxy);
assertEquals(result1, result2, `slow Object.entries() with ${kind}`);
}
})();
......@@ -227,3 +227,66 @@ function TestMutateDuringEnumeration() {
assertEquals([1, 2], Object.values(aMakesBEnumerable));
}
TestMutateDuringEnumeration();
(function TestElementKinds() {
var O1 = { name: "1" }, O2 = { name: "2" }, O3 = { name: "3" };
var PI = 3.141592653589793;
var E = 2.718281828459045;
function fastSloppyArguments(a, b, c) {
delete arguments[0];
arguments[0] = a;
return arguments;
}
function slowSloppyArguments(a, b, c) {
delete arguments[0];
arguments[0] = a;
Object.defineProperties(arguments, {
0: {
enumerable: true,
value: a
},
9999: {
enumerable: false,
value: "Y"
}
});
arguments[10000] = "X";
return arguments;
}
var element_kinds = {
FAST_SMI_ELEMENTS: [ [1, 2, 3], [1, 2, 3] ],
FAST_HOLEY_SMI_ELEMENTS: [ [, , 3], [ 3 ] ],
FAST_ELEMENTS: [ [O1, O2, O3], [O1, O2, O3] ],
FAST_HOLEY_ELEMENTS: [ [, , O3], [O3] ],
FAST_DOUBLE_ELEMENTS: [ [E, NaN, PI], [E, NaN, PI] ],
FAST_HOLEY_DOUBLE_ELEMENTS: [ [, , NaN], [NaN] ],
DICTIONARY_ELEMENTS: [ Object.defineProperties({ 10000: "world" }, {
100: { enumerable: true, value: "hello" },
99: { enumerable: false, value: "nope" }
}), [ "hello", "world" ] ],
FAST_SLOPPY_ARGUMENTS_ELEMENTS: [
fastSloppyArguments("a", "b", "c"), ["a", "b", "c"] ],
SLOW_SLOPPY_ARGUMENTS_ELEMENTS: [
slowSloppyArguments("a", "b", "c"), [ "a", "b", "c", "X"]],
FAST_STRING_WRAPPER_ELEMENTS: [ new String("str"), ["s", "t", "r"] ],
SLOW_STRING_WRAPPER_ELEMENTS: [
Object.defineProperties(new String("str"), {
10000: { enumerable: false, value: "X" },
9999: { enumerable: true, value: "Y" }
}), ["s", "t", "r", "Y"] ],
};
for (let [kind, [object, expected]] of Object.entries(element_kinds)) {
let result1 = Object.values(object);
assertEquals(expected, result1, `fast Object.values() with ${kind}`);
let proxy = new Proxy(object, {});
let result2 = Object.values(proxy);
assertEquals(result1, result2, `slow Object.values() with ${kind}`);
}
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment