Commit 17eda5f9 authored by Peter Marshall's avatar Peter Marshall Committed by Commit Bot

[serializer] Fix serialization of TypedArrays with an offset.

We explicitly serialize the backing store when we see the TypedArray.
We then put the reference in the external_pointer. To recalculate the
backing_store pointer during deserialization, we have to keep track of
each TypedArray and then fix up the pointer by adding the offset again.

Bug: v8:6966
Change-Id: I105d44413cffe5766c23c2a3d32ca2b78b5f22e8
Reviewed-on: https://chromium-review.googlesource.com/751269Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Commit-Queue: Peter Marshall <petermarshall@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49219}
parent 96ffe928
......@@ -207,6 +207,21 @@ HeapObject* Deserializer<AllocatorT>::PostProcessNewObject(HeapObject* obj,
NativesExternalStringResource::DecodeForDeserialization(
string->resource()));
isolate_->heap()->RegisterExternalString(string);
} else if (obj->IsJSTypedArray()) {
JSTypedArray* typed_array = JSTypedArray::cast(obj);
CHECK(typed_array->byte_offset()->IsSmi());
int32_t byte_offset = NumberToInt32(typed_array->byte_offset());
if (byte_offset > 0) {
FixedTypedArrayBase* elements =
FixedTypedArrayBase::cast(typed_array->elements());
// Must be off-heap layout.
DCHECK_NULL(elements->base_pointer());
void* pointer_with_offset = reinterpret_cast<void*>(
reinterpret_cast<intptr_t>(elements->external_pointer()) +
byte_offset);
elements->set_external_pointer(pointer_with_offset);
}
} else if (obj->IsJSArrayBuffer()) {
JSArrayBuffer* buffer = JSArrayBuffer::cast(obj);
// Only fixup for the off-heap case.
......
......@@ -7,6 +7,7 @@
#include <vector>
#include "src/objects/js-array.h"
#include "src/snapshot/default-deserializer-allocator.h"
#include "src/snapshot/serializer-common.h"
#include "src/snapshot/snapshot-source-sink.h"
......
......@@ -106,7 +106,7 @@ void SerializerDeserializer::Iterate(Isolate* isolate, RootVisitor* visitor) {
}
bool SerializerDeserializer::CanBeDeferred(HeapObject* o) {
return !o->IsString() && !o->IsScript();
return !o->IsString() && !o->IsScript() && !o->IsJSTypedArray();
}
void SerializerDeserializer::RestoreExternalReferenceRedirectors(
......
......@@ -392,19 +392,44 @@ int32_t Serializer<AllocatorT>::ObjectSerializer::SerializeBackingStore(
return static_cast<int32_t>(reference.off_heap_backing_store_index());
}
// When a JSArrayBuffer is neutered, the FixedTypedArray that points to the
// same backing store does not know anything about it. This fixup step finds
// neutered TypedArrays and clears the values in the FixedTypedArray so that
// we don't try to serialize the now invalid backing store.
template <class AllocatorT>
void Serializer<AllocatorT>::ObjectSerializer::FixupIfNeutered() {
JSTypedArray* array = JSTypedArray::cast(object_);
if (!array->WasNeutered()) return;
FixedTypedArrayBase* fta = FixedTypedArrayBase::cast(array->elements());
DCHECK_NULL(fta->base_pointer());
fta->set_external_pointer(Smi::kZero);
fta->set_length(0);
void Serializer<AllocatorT>::ObjectSerializer::SerializeJSTypedArray() {
JSTypedArray* typed_array = JSTypedArray::cast(object_);
FixedTypedArrayBase* elements =
FixedTypedArrayBase::cast(typed_array->elements());
if (!typed_array->WasNeutered()) {
bool off_heap = elements->base_pointer() == nullptr;
if (off_heap) {
// Explicitly serialize the backing store now.
JSArrayBuffer* buffer = JSArrayBuffer::cast(typed_array->buffer());
CHECK(buffer->byte_length()->IsSmi());
CHECK(typed_array->byte_offset()->IsSmi());
int32_t byte_length = NumberToInt32(buffer->byte_length());
int32_t byte_offset = NumberToInt32(typed_array->byte_offset());
// We need to calculate the backing store from the external pointer
// because the ArrayBuffer may already have been serialized.
void* backing_store = reinterpret_cast<void*>(
reinterpret_cast<intptr_t>(elements->external_pointer()) -
byte_offset);
int32_t ref = SerializeBackingStore(backing_store, byte_length);
// The external_pointer is the backing_store + typed_array->byte_offset.
// To properly share the buffer, we set the backing store ref here. On
// deserialization we re-add the byte_offset to external_pointer.
elements->set_external_pointer(Smi::FromInt(ref));
}
} else {
// When a JSArrayBuffer is neutered, the FixedTypedArray that points to the
// same backing store does not know anything about it. This fixup step finds
// neutered TypedArrays and clears the values in the FixedTypedArray so that
// we don't try to serialize the now invalid backing store.
elements->set_external_pointer(Smi::kZero);
elements->set_length(0);
}
SerializeObject();
}
template <class AllocatorT>
......@@ -424,26 +449,6 @@ void Serializer<AllocatorT>::ObjectSerializer::SerializeJSArrayBuffer() {
SerializeObject();
}
template <class AllocatorT>
void Serializer<AllocatorT>::ObjectSerializer::SerializeFixedTypedArray() {
FixedTypedArrayBase* fta = FixedTypedArrayBase::cast(object_);
void* backing_store = fta->DataPtr();
// We cannot store byte_length larger than Smi range in the snapshot.
CHECK_LT(fta->ByteLength(), Smi::kMaxValue);
int32_t byte_length = static_cast<int32_t>(fta->ByteLength());
// The heap contains empty FixedTypedArrays for each type, with a byte_length
// of 0 (e.g. empty_fixed_uint8_array). These look like they are are 'on-heap'
// but have no data to copy, so we skip the backing store here.
// The embedder-allocated backing store only exists for the off-heap case.
if (byte_length > 0 && fta->base_pointer() == nullptr) {
int32_t ref = SerializeBackingStore(backing_store, byte_length);
fta->set_external_pointer(Smi::FromInt(ref));
}
SerializeObject();
}
template <class AllocatorT>
void Serializer<AllocatorT>::ObjectSerializer::SerializeExternalString() {
Heap* heap = serializer_->isolate()->heap();
......@@ -572,16 +577,13 @@ void Serializer<AllocatorT>::ObjectSerializer::Serialize() {
SeqTwoByteString::cast(object_)->clear_padding();
}
if (object_->IsJSTypedArray()) {
FixupIfNeutered();
SerializeJSTypedArray();
return;
}
if (object_->IsJSArrayBuffer()) {
SerializeJSArrayBuffer();
return;
}
if (object_->IsFixedTypedArrayBase()) {
SerializeFixedTypedArray();
return;
}
// We don't expect fillers.
DCHECK(!object_->IsFiller());
......
......@@ -307,9 +307,8 @@ class Serializer<AllocatorT>::ObjectSerializer : public ObjectVisitor {
void OutputCode(int size);
int SkipTo(Address to);
int32_t SerializeBackingStore(void* backing_store, int32_t byte_length);
void FixupIfNeutered();
void SerializeJSTypedArray();
void SerializeJSArrayBuffer();
void SerializeFixedTypedArray();
void SerializeExternalString();
void SerializeExternalStringAsSequentialString();
......
......@@ -685,8 +685,10 @@ void TestInt32Expectations(const Int32Expectations& expectations) {
}
}
void TypedArrayTestHelper(const char* code,
const Int32Expectations& expectations) {
void TypedArrayTestHelper(
const char* code, const Int32Expectations& expectations,
const char* code_to_run_after_restore = nullptr,
const Int32Expectations& after_restore_expectations = Int32Expectations()) {
DisableAlwaysOpt();
i::FLAG_allow_natives_syntax = true;
v8::StartupData blob;
......@@ -723,6 +725,10 @@ void TypedArrayTestHelper(const char* code,
CHECK(deserialized_data.empty()); // We do not expect any embedder data.
v8::Context::Scope c_scope(context);
TestInt32Expectations(expectations);
if (code_to_run_after_restore) {
CompileRun(code_to_run_after_restore);
}
TestInt32Expectations(after_restore_expectations);
}
isolate->Dispose();
delete[] blob.data; // We can dispose of the snapshot blob now.
......@@ -770,6 +776,25 @@ TEST(CustomSnapshotDataBlobSharedArrayBuffer) {
TypedArrayTestHelper(code, expectations);
}
TEST(CustomSnapshotDataBlobArrayBufferWithOffset) {
const char* code =
"var x = new Int32Array([12, 24, 48, 96]);"
"var y = new Int32Array(x.buffer, 4, 2)";
Int32Expectations expectations = {
std::make_tuple("x[1]", 24), std::make_tuple("x[2]", 48),
std::make_tuple("y[0]", 24), std::make_tuple("y[1]", 48),
};
// Verify that the typed arrays use the same buffer (not independent copies).
const char* code_to_run_after_restore = "x[2] = 57; y[0] = 42;";
Int32Expectations after_restore_expectations = {
std::make_tuple("x[1]", 42), std::make_tuple("y[1]", 57),
};
TypedArrayTestHelper(code, expectations, code_to_run_after_restore,
after_restore_expectations);
}
TEST(CustomSnapshotDataBlobDataView) {
const char* code =
"var x = new Int8Array([1, 2, 3, 4]);"
......
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