Commit ef94aab2 authored by jbroman's avatar jbroman Committed by Commit bot

ValueSerializer: Take advantage of fast elements in dense array serialization.

This yields a ~5% serialization time improvement on typical JSON-esque data.
The approach taken matches json-stringifier fairly closely.

BUG=chromium:148757

Review-Url: https://codereview.chromium.org/2311063004
Cr-Commit-Position: refs/heads/master@{#39254}
parent b7625e70
......@@ -459,7 +459,46 @@ Maybe<bool> ValueSerializer::WriteJSArray(Handle<JSArray> array) {
// format changes.
WriteTag(SerializationTag::kBeginDenseJSArray);
WriteVarint<uint32_t>(length);
for (uint32_t i = 0; i < length; i++) {
uint32_t i = 0;
// Fast paths. Note that FAST_ELEMENTS in particular can bail due to the
// structure of the elements changing.
switch (array->GetElementsKind()) {
case FAST_SMI_ELEMENTS: {
Handle<FixedArray> elements(FixedArray::cast(array->elements()),
isolate_);
for (; i < length; i++) WriteSmi(Smi::cast(elements->get(i)));
break;
}
case FAST_DOUBLE_ELEMENTS: {
Handle<FixedDoubleArray> elements(
FixedDoubleArray::cast(array->elements()), isolate_);
for (; i < length; i++) {
WriteTag(SerializationTag::kDouble);
WriteDouble(elements->get_scalar(i));
}
break;
}
case FAST_ELEMENTS: {
Handle<Object> old_length(array->length(), isolate_);
for (; i < length; i++) {
if (array->length() != *old_length ||
array->GetElementsKind() != FAST_ELEMENTS) {
// Fall back to slow path.
break;
}
Handle<Object> element(FixedArray::cast(array->elements())->get(i),
isolate_);
if (!WriteObject(element).FromMaybe(false)) return Nothing<bool>();
}
break;
}
default:
break;
}
// If there are elements remaining, serialize them slowly.
for (; i < length; i++) {
// Serializing the array's elements can have arbitrary side effects, so we
// cannot rely on still having fast elements, even if it did to begin
// with.
......@@ -470,6 +509,7 @@ Maybe<bool> ValueSerializer::WriteJSArray(Handle<JSArray> array) {
return Nothing<bool>();
}
}
KeyAccumulator accumulator(isolate_, KeyCollectionMode::kOwnOnly,
ENUMERABLE_STRINGS);
if (!accumulator.CollectOwnPropertyNames(array, array).FromMaybe(false)) {
......
......@@ -947,6 +947,19 @@ TEST_F(ValueSerializerTest, RoundTripArrayWithTrickyGetters) {
EXPECT_TRUE(EvaluateScriptForResultBool("result[0] === 1"));
EXPECT_TRUE(EvaluateScriptForResultBool("!result.hasOwnProperty(2)"));
});
// The same is true if the length is shortened, but there are still items
// remaining.
RoundTripTest(
"(() => {"
" var x = [1, { get a() { x.length = 3; }}, 3, 4];"
" return x;"
"})()",
[this](Local<Value> value) {
ASSERT_TRUE(value->IsArray());
ASSERT_EQ(4, Array::Cast(*value)->Length());
EXPECT_TRUE(EvaluateScriptForResultBool("result[2] === 3"));
EXPECT_TRUE(EvaluateScriptForResultBool("!result.hasOwnProperty(3)"));
});
// Same for sparse arrays.
RoundTripTest(
"(() => {"
......@@ -960,6 +973,18 @@ TEST_F(ValueSerializerTest, RoundTripArrayWithTrickyGetters) {
EXPECT_TRUE(EvaluateScriptForResultBool("result[0] === 1"));
EXPECT_TRUE(EvaluateScriptForResultBool("!result.hasOwnProperty(2)"));
});
RoundTripTest(
"(() => {"
" var x = [1, { get a() { x.length = 3; }}, 3, 4];"
" x.length = 1000;"
" return x;"
"})()",
[this](Local<Value> value) {
ASSERT_TRUE(value->IsArray());
ASSERT_EQ(1000, Array::Cast(*value)->Length());
EXPECT_TRUE(EvaluateScriptForResultBool("result[2] === 3"));
EXPECT_TRUE(EvaluateScriptForResultBool("!result.hasOwnProperty(3)"));
});
// If a getter makes a property non-enumerable, it should still be enumerated
// as enumeration happens once before getters are invoked.
RoundTripTest(
......
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