[web snapshot] Support TypedArray

This CL adds serialization and deserialization
support for ArrayBuffer and TypedArray.

TODOs:
- Support resizable ArrayBuffer.
- Support detached ArrayBuffer.
- Support shared ArrayBuffer.

Bug: v8:11525
Change-Id: Ic9267a78e427ee20d55f2f0483b677eeee5c214b
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3688896
Commit-Queue: 王澳 <wangao.james@bytedance.com>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/main@{#81136}
This commit is contained in:
jameslahm 2022-06-09 19:33:56 +08:00 committed by V8 LUCI CQ
parent 38aca8f586
commit 293654ec6b
4 changed files with 669 additions and 3 deletions

View File

@ -489,6 +489,7 @@ class RuntimeCallTimer final {
V(UpdateProtector) \
V(WebSnapshotDeserialize) \
V(WebSnapshotDeserialize_Arrays) \
V(WebSnapshotDeserialize_ArrayBuffers) \
V(WebSnapshotDeserialize_BuiltinObjects) \
V(WebSnapshotDeserialize_Classes) \
V(WebSnapshotDeserialize_Contexts) \
@ -498,6 +499,7 @@ class RuntimeCallTimer final {
V(WebSnapshotDeserialize_Objects) \
V(WebSnapshotDeserialize_Strings) \
V(WebSnapshotDeserialize_Symbols) \
V(WebSnapshotDeserialize_TypedArrays) \
V(WrappedFunctionLengthGetter) \
V(WrappedFunctionNameGetter)

View File

@ -16,6 +16,7 @@
#include "src/handles/handles.h"
#include "src/logging/runtime-call-stats-scope.h"
#include "src/objects/contexts.h"
#include "src/objects/js-array-buffer-inl.h"
#include "src/objects/js-regexp-inl.h"
#include "src/objects/script.h"
@ -266,6 +267,8 @@ WebSnapshotSerializer::WebSnapshotSerializer(Isolate* isolate)
function_serializer_(isolate_, nullptr),
class_serializer_(isolate_, nullptr),
array_serializer_(isolate_, nullptr),
typed_array_serializer_(isolate_, nullptr),
array_buffer_serializer_(isolate_, nullptr),
object_serializer_(isolate_, nullptr),
export_serializer_(isolate_, nullptr),
external_object_ids_(isolate_->heap()),
@ -276,6 +279,8 @@ WebSnapshotSerializer::WebSnapshotSerializer(Isolate* isolate)
function_ids_(isolate_->heap()),
class_ids_(isolate_->heap()),
array_ids_(isolate_->heap()),
typed_array_ids_(isolate->heap()),
array_buffer_ids_(isolate->heap()),
object_ids_(isolate_->heap()),
builtin_object_to_name_(isolate_->heap()),
builtin_object_ids_(isolate_->heap()),
@ -288,6 +293,8 @@ WebSnapshotSerializer::WebSnapshotSerializer(Isolate* isolate)
functions_ = empty_array_list;
classes_ = empty_array_list;
arrays_ = empty_array_list;
array_buffers_ = empty_array_list;
typed_arrays_ = empty_array_list;
objects_ = empty_array_list;
}
@ -433,6 +440,16 @@ void WebSnapshotSerializer::SerializePendingItems() {
Handle<JSArray> array = handle(JSArray::cast(arrays_->Get(i)), isolate_);
SerializeArray(array);
}
for (int i = array_buffers_->Length() - 1; i >= 0; --i) {
Handle<JSArrayBuffer> array_buffer =
handle(JSArrayBuffer::cast(array_buffers_->Get(i)), isolate_);
SerializeArrayBuffer(array_buffer);
}
for (int i = typed_arrays_->Length() - 1; i >= 0; --i) {
Handle<JSTypedArray> typed_array =
handle(JSTypedArray::cast(typed_arrays_->Get(i)), isolate_);
SerializeTypedArray(typed_array);
}
for (int i = objects_->Length() - 1; i >= 0; --i) {
Handle<JSObject> object =
handle(JSObject::cast(objects_->Get(i)), isolate_);
@ -480,8 +497,9 @@ void WebSnapshotSerializer::WriteSnapshot(uint8_t*& buffer,
builtin_object_serializer_.buffer_size_ + map_serializer_.buffer_size_ +
context_serializer_.buffer_size_ + function_serializer_.buffer_size_ +
class_serializer_.buffer_size_ + array_serializer_.buffer_size_ +
object_serializer_.buffer_size_ + export_serializer_.buffer_size_ +
10 * sizeof(uint32_t);
array_buffer_serializer_.buffer_size_ +
typed_array_serializer_.buffer_size_ + object_serializer_.buffer_size_ +
export_serializer_.buffer_size_ + 12 * sizeof(uint32_t);
if (total_serializer.ExpandBuffer(needed_size).IsNothing()) {
Throw("Out of memory");
return;
@ -498,6 +516,10 @@ void WebSnapshotSerializer::WriteSnapshot(uint8_t*& buffer,
WriteObjects(total_serializer, function_count(), function_serializer_,
"functions");
WriteObjects(total_serializer, array_count(), array_serializer_, "arrays");
WriteObjects(total_serializer, array_buffer_count(), array_buffer_serializer_,
"array buffers");
WriteObjects(total_serializer, typed_array_count(), typed_array_serializer_,
"typed arrays");
WriteObjects(total_serializer, object_count(), object_serializer_, "objects");
WriteObjects(total_serializer, class_count(), class_serializer_, "classes");
WriteObjects(total_serializer, export_count_, export_serializer_, "exports");
@ -855,6 +877,17 @@ void WebSnapshotSerializer::Discover(Handle<HeapObject> start_object) {
DiscoverString(flags_string);
break;
}
case JS_ARRAY_BUFFER_TYPE: {
Handle<JSArrayBuffer> array_buffer =
Handle<JSArrayBuffer>::cast(object);
DiscoverArrayBuffer(array_buffer);
break;
}
case JS_TYPED_ARRAY_TYPE: {
Handle<JSTypedArray> typed_array = Handle<JSTypedArray>::cast(object);
DiscoverTypedArray(typed_array);
break;
}
default:
if (object->IsString()) {
// These are array elements / object properties -> allow in place
@ -1176,6 +1209,42 @@ void WebSnapshotSerializer::DiscoverElements(Handle<JSObject> object) {
}
}
void WebSnapshotSerializer::DiscoverArrayBuffer(
Handle<JSArrayBuffer> array_buffer) {
// TODO(v8:11525): Support SharedArrayBuffer.
if (array_buffer->is_shared()) {
Throw("Unsupported SharedArrayBuffer");
return;
}
// TODO(v8:11525): Support detached ArrayBuffer.
if (array_buffer->was_detached()) {
Throw("Unsupported detached ArrayBuffer");
return;
}
// TODO(v8:11525): Support resizable ArrayBuffer.
if (array_buffer->is_resizable()) {
Throw("Unsupported resizable ArrayBuffer");
return;
}
uint32_t id;
if (InsertIntoIndexMap(array_buffer_ids_, *array_buffer, id)) {
return;
}
DCHECK_EQ(id, array_buffers_->Length());
array_buffers_ = ArrayList::Add(isolate_, array_buffers_, array_buffer);
}
void WebSnapshotSerializer::DiscoverTypedArray(
Handle<JSTypedArray> typed_array) {
uint32_t id;
if (InsertIntoIndexMap(typed_array_ids_, *typed_array, id)) {
return;
}
DCHECK_EQ(id, typed_arrays_->Length());
typed_arrays_ = ArrayList::Add(isolate_, typed_arrays_, typed_array);
discovery_queue_.push(typed_array->GetBuffer());
}
template <typename T>
void WebSnapshotSerializer::DiscoverObjectPropertiesWithDictionaryMap(T dict) {
DisallowGarbageCollection no_gc;
@ -1571,6 +1640,54 @@ void WebSnapshotSerializer::SerializeElements(Handle<JSObject> object,
}
}
// Format (serialized array buffer):
// - Length
// - Raw bytes
void WebSnapshotSerializer::SerializeArrayBuffer(
Handle<JSArrayBuffer> array_buffer) {
size_t byte_length = array_buffer->byte_length();
if (byte_length > std::numeric_limits<uint32_t>::max()) {
Throw("Too large array buffer");
return;
}
array_buffer_serializer_.WriteUint32(static_cast<uint32_t>(byte_length));
array_buffer_serializer_.WriteRawBytes(array_buffer->backing_store(),
byte_length);
}
// Format (serialized typed array):
// - TypedArrayType
// - Serialized ArrayBuffer
// - Byte offset
// - Byte length
void WebSnapshotSerializer::SerializeTypedArray(
Handle<JSTypedArray> typed_array) {
TypedArrayType typed_array_type = TypedArrayType::kUint8Array;
switch (typed_array->type()) {
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
case kExternal##Type##Array: \
typed_array_type = TypedArrayType::k##Type##Array; \
break;
TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
}
typed_array_serializer_.WriteUint32(typed_array_type);
WriteValue(typed_array->GetBuffer(), typed_array_serializer_);
if (typed_array->byte_offset() > std::numeric_limits<uint32_t>::max()) {
Throw("Too large byte offset in TypedArray");
return;
}
typed_array_serializer_.WriteUint32(
static_cast<uint32_t>(typed_array->byte_offset()));
if (typed_array->byte_length() > std::numeric_limits<uint32_t>::max()) {
Throw("Too large byte length in TypedArray");
return;
}
typed_array_serializer_.WriteUint32(
static_cast<uint32_t>(typed_array->byte_length()));
// TODO(v8:11525): Serialize the bit field in a browser-independent way.
}
// Format (serialized export):
// - String id (export name)
// - Serialized value (export value)
@ -1674,6 +1791,20 @@ void WebSnapshotSerializer::WriteValue(Handle<Object> object,
WriteStringId(flags_string, serializer);
break;
}
case JS_ARRAY_BUFFER_TYPE: {
Handle<JSArrayBuffer> array_buffer =
Handle<JSArrayBuffer>::cast(heap_object);
serializer.WriteUint32(ValueType::ARRAY_BUFFER_ID);
serializer.WriteUint32(GetArrayBufferId(*array_buffer));
break;
}
case JS_TYPED_ARRAY_TYPE: {
Handle<JSTypedArray> typed_array =
Handle<JSTypedArray>::cast(heap_object);
serializer.WriteUint32(ValueType::TYPED_ARRAY_ID);
serializer.WriteUint32(GetTypedArrayId(*typed_array));
break;
}
default:
if (heap_object->IsString()) {
// Write strings which are referred to only once as in-place strings.
@ -1771,6 +1902,22 @@ uint32_t WebSnapshotSerializer::GetArrayId(JSArray array) {
return static_cast<uint32_t>(array_ids_.size() - 1 - id);
}
uint32_t WebSnapshotSerializer::GetTypedArrayId(JSTypedArray typed_array) {
int id;
bool return_value = typed_array_ids_.Lookup(typed_array, &id);
DCHECK(return_value);
USE(return_value);
return static_cast<uint32_t>(typed_array_ids_.size() - 1 - id);
}
uint32_t WebSnapshotSerializer::GetArrayBufferId(JSArrayBuffer array_buffer) {
int id;
bool return_value = array_buffer_ids_.Lookup(array_buffer, &id);
DCHECK(return_value);
USE(return_value);
return static_cast<uint32_t>(array_buffer_ids_.size() - 1 - id);
}
uint32_t WebSnapshotSerializer::GetObjectId(JSObject object) {
int id;
bool return_value = object_ids_.Lookup(object, &id);
@ -1844,6 +1991,8 @@ WebSnapshotDeserializer::WebSnapshotDeserializer(
functions_handle_ = empty_array;
classes_handle_ = empty_array;
arrays_handle_ = empty_array;
array_buffers_handle_ = empty_array;
typed_arrays_handle_ = empty_array;
objects_handle_ = empty_array;
external_references_handle_ = empty_array;
isolate_->heap()->AddGCEpilogueCallback(UpdatePointersCallback,
@ -1863,6 +2012,8 @@ void WebSnapshotDeserializer::UpdatePointers() {
functions_ = *functions_handle_;
classes_ = *classes_handle_;
arrays_ = *arrays_handle_;
array_buffers_ = *array_buffers_handle_;
typed_arrays_ = *typed_arrays_handle_;
objects_ = *objects_handle_;
external_references_ = *external_references_handle_;
}
@ -1992,6 +2143,8 @@ bool WebSnapshotDeserializer::DeserializeSnapshot(bool skip_exports) {
DeserializeContexts();
DeserializeFunctions();
DeserializeArrays();
DeserializeArrayBuffers();
DeserializeTypedArrays();
DeserializeObjects();
DeserializeClasses();
ProcessDeferredReferences();
@ -3079,6 +3232,101 @@ void WebSnapshotDeserializer::DeserializeArrays() {
}
}
void WebSnapshotDeserializer::DeserializeArrayBuffers() {
RCS_SCOPE(isolate_,
RuntimeCallCounterId::kWebSnapshotDeserialize_ArrayBuffers);
if (!deserializer_->ReadUint32(&array_buffer_count_) ||
object_count_ > kMaxItemCount) {
Throw("Malformed array buffer table");
return;
}
static_assert(kMaxItemCount <= FixedArray::kMaxLength);
array_buffers_handle_ = factory()->NewFixedArray(array_buffer_count_);
array_buffers_ = *array_buffers_handle_;
for (; current_array_buffer_count_ < array_buffer_count_;
++current_array_buffer_count_) {
uint32_t byte_length;
if (!deserializer_->ReadUint32(&byte_length) ||
byte_length > static_cast<size_t>(deserializer_->end_ -
deserializer_->position_)) {
Throw("Malformed array buffer");
return;
}
MaybeHandle<JSArrayBuffer> result =
isolate_->factory()->NewJSArrayBufferAndBackingStore(
byte_length, InitializedFlag::kUninitialized);
Handle<JSArrayBuffer> array_buffer;
if (!result.ToHandle(&array_buffer)) {
Throw("Create array buffer failed");
return;
}
if (byte_length > 0) {
memcpy(array_buffer->backing_store(), deserializer_->position_,
byte_length);
}
deserializer_->position_ += byte_length;
array_buffers_.set(static_cast<int>(current_array_buffer_count_),
*array_buffer);
}
}
void WebSnapshotDeserializer::DeserializeTypedArrays() {
RCS_SCOPE(isolate_,
RuntimeCallCounterId::kWebSnapshotDeserialize_TypedArrays);
if (!deserializer_->ReadUint32(&typed_array_count_) ||
object_count_ > kMaxItemCount) {
Throw("Malformed typed array table");
return;
}
static_assert(kMaxItemCount <= FixedArray::kMaxLength);
typed_arrays_handle_ = factory()->NewFixedArray(typed_array_count_);
typed_arrays_ = *typed_arrays_handle_;
for (; current_typed_array_count_ < typed_array_count_;
++current_typed_array_count_) {
uint32_t typed_array_type;
if (!deserializer_->ReadUint32(&typed_array_type)) {
Throw("Malformed array buffer");
return;
}
Handle<JSArrayBuffer> array_buffer(
JSArrayBuffer::cast(std::get<0>(ReadValue())), isolate_);
uint32_t byte_offset = 0;
uint32_t byte_length = 0;
if (!deserializer_->ReadUint32(&byte_offset) ||
!deserializer_->ReadUint32(&byte_length)) {
Throw("Malformed typed array");
return;
}
ExternalArrayType external_array_type = kExternalInt8Array;
unsigned element_size = 0;
switch (static_cast<TypedArrayType>(typed_array_type)) {
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
case TypedArrayType::k##Type##Array: \
external_array_type = kExternal##Type##Array; \
element_size = sizeof(ctype); \
break;
TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
}
if (element_size == 0 || byte_offset % element_size != 0 ||
byte_length % element_size != 0) {
Throw("Malformed typed array");
return;
}
size_t length = byte_length / element_size;
if (length > JSTypedArray::kMaxLength) {
Throw("Too large TypedArray");
return;
}
// TODO(v8:11525): Set the bit_field for typed_array correctly.
Handle<JSTypedArray> typed_array = isolate_->factory()->NewJSTypedArray(
external_array_type, array_buffer, byte_offset, length);
typed_arrays_.set(static_cast<int>(current_typed_array_count_),
*typed_array);
}
}
void WebSnapshotDeserializer::DeserializeExports(bool skip_exports) {
RCS_SCOPE(isolate_, RuntimeCallCounterId::kWebSnapshotDeserialize_Exports);
uint32_t count;
@ -3204,6 +3452,10 @@ std::tuple<Object, bool> WebSnapshotDeserializer::ReadValue(
return std::make_tuple(ReadBuiltinObjectReference(), false);
case ValueType::IN_PLACE_STRING_ID:
return std::make_tuple(ReadInPlaceString(internalize_strings), false);
case ValueType::ARRAY_BUFFER_ID:
return ReadArrayBuffer(container, container_index);
case ValueType::TYPED_ARRAY_ID:
return ReadTypedArray(container, container_index);
default:
// TODO(v8:11525): Handle other value types.
Throw("Unsupported value type");
@ -3244,6 +3496,38 @@ std::tuple<Object, bool> WebSnapshotDeserializer::ReadArray(
AddDeferredReference(container, index, ARRAY_ID, array_id), true);
}
std::tuple<Object, bool> WebSnapshotDeserializer::ReadArrayBuffer(
Handle<HeapObject> container, uint32_t index) {
uint32_t array_buffer_id;
if (!deserializer_->ReadUint32(&array_buffer_id) ||
array_buffer_id >= kMaxItemCount) {
Throw("Malformed variable");
return std::make_tuple(Smi::zero(), false);
}
if (array_buffer_id < current_array_buffer_count_) {
return std::make_tuple(array_buffers_.get(array_buffer_id), false);
}
// The array buffer hasn't been deserialized yet.
return std::make_tuple(
AddDeferredReference(container, index, ARRAY_BUFFER_ID, array_buffer_id),
true);
}
std::tuple<Object, bool> WebSnapshotDeserializer::ReadTypedArray(
Handle<HeapObject> container, uint32_t index) {
uint32_t typed_array_id;
if (!deserializer_->ReadUint32(&typed_array_id) ||
typed_array_id >= kMaxItemCount) {
Throw("Malformed variable");
return std::make_tuple(Smi::zero(), false);
}
if (typed_array_id < current_typed_array_count_) {
return std::make_tuple(typed_arrays_.get(typed_array_id), false);
}
// The typed array hasn't been deserialized yet.
return std::make_tuple(
AddDeferredReference(container, index, TYPED_ARRAY_ID, typed_array_id),
true);
}
std::tuple<Object, bool> WebSnapshotDeserializer::ReadObject(
Handle<HeapObject> container, uint32_t index) {
uint32_t object_id;
@ -3445,6 +3729,22 @@ void WebSnapshotDeserializer::ProcessDeferredReferences() {
}
target = arrays_.get(target_index);
break;
case ARRAY_BUFFER_ID:
if (static_cast<uint32_t>(target_index) >= array_buffer_count_) {
AllowGarbageCollection allow_gc;
Throw("Invalid array buffer reference");
return;
}
target = array_buffers_.get(target_index);
break;
case TYPED_ARRAY_ID:
if (static_cast<uint32_t>(target_index) >= typed_array_count_) {
AllowGarbageCollection allow_gc;
Throw("Invalid typed array reference");
return;
}
target = typed_arrays_.get(target_index);
break;
case OBJECT_ID:
if (static_cast<uint32_t>(target_index) >= object_count_) {
AllowGarbageCollection allow_gc;

View File

@ -58,7 +58,9 @@ class WebSnapshotSerializerDeserializer {
SYMBOL_ID,
EXTERNAL_ID,
BUILTIN_OBJECT_ID,
IN_PLACE_STRING_ID
IN_PLACE_STRING_ID,
ARRAY_BUFFER_ID,
TYPED_ARRAY_ID
};
enum SymbolType : uint8_t {
@ -69,6 +71,20 @@ class WebSnapshotSerializerDeserializer {
enum ElementsType : uint8_t { kDense = 0, kSparse = 1 };
enum TypedArrayType : uint8_t {
kInt8Array,
kUint8Array,
kUint8ClampedArray,
kInt16Array,
kUint16Array,
kInt32Array,
kUint32Array,
kFloat32Array,
kFloat64Array,
kBigInt64Array,
kBigUint64Array,
};
static constexpr uint8_t kMagicNumber[4] = {'+', '+', '+', ';'};
enum ContextType : uint8_t { FUNCTION, BLOCK };
@ -174,6 +190,14 @@ class V8_EXPORT WebSnapshotSerializer
return static_cast<uint32_t>(array_ids_.size());
}
uint32_t array_buffer_count() const {
return static_cast<uint32_t>(array_buffer_ids_.size());
}
uint32_t typed_array_count() const {
return static_cast<uint32_t>(typed_array_ids_.size());
}
uint32_t object_count() const {
return static_cast<uint32_t>(object_ids_.size());
}
@ -216,6 +240,8 @@ class V8_EXPORT WebSnapshotSerializer
void DiscoverContextAndPrototype(Handle<JSFunction> function);
void DiscoverContext(Handle<Context> context);
void DiscoverArray(Handle<JSArray> array);
void DiscoverTypedArray(Handle<JSTypedArray> typed_array);
void DiscoverArrayBuffer(Handle<JSArrayBuffer> array_buffer);
void DiscoverElements(Handle<JSObject> object);
void DiscoverObject(Handle<JSObject> object);
bool DiscoverIfBuiltinObject(Handle<HeapObject> object);
@ -243,6 +269,8 @@ class V8_EXPORT WebSnapshotSerializer
void SerializeArray(Handle<JSArray> array);
void SerializeElements(Handle<JSObject> object, ValueSerializer& serializer);
void SerializeObject(Handle<JSObject> object);
void SerializeArrayBuffer(Handle<JSArrayBuffer> array_buffer);
void SerializeTypedArray(Handle<JSTypedArray> typed_array);
void SerializeExport(Handle<Object> object, Handle<String> export_name);
void WriteValue(Handle<Object> object, ValueSerializer& serializer);
@ -257,6 +285,8 @@ class V8_EXPORT WebSnapshotSerializer
uint32_t GetClassId(JSFunction function);
uint32_t GetContextId(Context context);
uint32_t GetArrayId(JSArray array);
uint32_t GetTypedArrayId(JSTypedArray typed_array);
uint32_t GetArrayBufferId(JSArrayBuffer array_buffer);
uint32_t GetObjectId(JSObject object);
bool GetExternalId(HeapObject object, uint32_t* id = nullptr);
// Returns index into builtin_object_name_strings_.
@ -271,6 +301,8 @@ class V8_EXPORT WebSnapshotSerializer
ValueSerializer function_serializer_;
ValueSerializer class_serializer_;
ValueSerializer array_serializer_;
ValueSerializer typed_array_serializer_;
ValueSerializer array_buffer_serializer_;
ValueSerializer object_serializer_;
ValueSerializer export_serializer_;
@ -282,6 +314,8 @@ class V8_EXPORT WebSnapshotSerializer
Handle<ArrayList> functions_;
Handle<ArrayList> classes_;
Handle<ArrayList> arrays_;
Handle<ArrayList> typed_arrays_;
Handle<ArrayList> array_buffers_;
Handle<ArrayList> objects_;
// IndexMap to keep track of explicitly blocked external objects and
@ -299,6 +333,8 @@ class V8_EXPORT WebSnapshotSerializer
ObjectCacheIndexMap function_ids_;
ObjectCacheIndexMap class_ids_;
ObjectCacheIndexMap array_ids_;
ObjectCacheIndexMap typed_array_ids_;
ObjectCacheIndexMap array_buffer_ids_;
ObjectCacheIndexMap object_ids_;
uint32_t export_count_ = 0;
@ -407,6 +443,8 @@ class V8_EXPORT WebSnapshotDeserializer
void DeserializeFunctions();
void DeserializeClasses();
void DeserializeArrays();
void DeserializeArrayBuffers();
void DeserializeTypedArrays();
void DeserializeObjects();
void DeserializeObjectElements(Handle<JSObject> object,
bool map_from_snapshot);
@ -441,6 +479,10 @@ class V8_EXPORT WebSnapshotDeserializer
Object ReadSymbol();
std::tuple<Object, bool> ReadArray(Handle<HeapObject> container,
uint32_t container_index);
std::tuple<Object, bool> ReadArrayBuffer(Handle<HeapObject> container,
uint32_t container_index);
std::tuple<Object, bool> ReadTypedArray(Handle<HeapObject> container,
uint32_t container_index);
std::tuple<Object, bool> ReadObject(Handle<HeapObject> container,
uint32_t container_index);
std::tuple<Object, bool> ReadFunction(Handle<HeapObject> container,
@ -494,6 +536,12 @@ class V8_EXPORT WebSnapshotDeserializer
Handle<FixedArray> arrays_handle_;
FixedArray arrays_;
Handle<FixedArray> array_buffers_handle_;
FixedArray array_buffers_;
Handle<FixedArray> typed_arrays_handle_;
FixedArray typed_arrays_;
Handle<FixedArray> objects_handle_;
FixedArray objects_;
@ -526,6 +574,10 @@ class V8_EXPORT WebSnapshotDeserializer
uint32_t current_class_count_ = 0;
uint32_t array_count_ = 0;
uint32_t current_array_count_ = 0;
uint32_t array_buffer_count_ = 0;
uint32_t current_array_buffer_count_ = 0;
uint32_t typed_array_count_ = 0;
uint32_t current_typed_array_count_ = 0;
uint32_t object_count_ = 0;
uint32_t current_object_count_ = 0;

View File

@ -0,0 +1,312 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --experimental-d8-web-snapshot-api --allow-natives-syntax
'use strict';
d8.file.execute('test/mjsunit/web-snapshot/web-snapshot-helpers.js');
(function TestTypedArray() {
function createObjects() {
const int8Array = new Int8Array(3);
for (let i = 0; i < 3; i++){
int8Array[i] = i;
}
const uint8Array = new Uint8Array(3);
for (let i = 0; i < 3; i++){
uint8Array[i] = i;
}
const uint8ClampedArray = new Uint8ClampedArray(3);
for (let i = 0; i < 3; i++){
uint8ClampedArray[i] = i;
}
const int16Array = new Int16Array(3);
for (let i = 0; i < 3; i++){
int16Array[i] = i;
}
const uint16Array = new Uint16Array(3);
for (let i = 0; i < 3; i++){
uint16Array[i] = i;
}
const int32Array = new Int32Array(3);
for (let i = 0; i < 3; i++){
int32Array[i] = i;
}
const uint32Array = new Uint32Array(3);
for (let i = 0; i < 3; i++){
uint32Array[i] = i;
}
const float32Array = new Float32Array(3);
for (let i = 0; i < 3; i++){
float32Array[i] = i + 0.2;
}
const float64Array = new Float64Array(3);
for (let i = 0; i < 3; i++){
float64Array[i] = i + 0.2;
}
const bigInt64Array = new BigInt64Array(3);
for (let i = 0; i < 3; i++){
bigInt64Array[i] = BigInt(i);
}
const bigUint64Array = new BigUint64Array(3);
for (let i = 0; i < 3; i++){
bigUint64Array[i] = BigInt(i);
}
globalThis.int8Array = int8Array;
globalThis.uint8Array = uint8Array;
globalThis.uint8ClampedArray = uint8ClampedArray;
globalThis.int16Array = int16Array;
globalThis.uint16Array = uint16Array;
globalThis.int32Array = int32Array;
globalThis.uint32Array = uint32Array;
globalThis.float32Array = float32Array;
globalThis.float64Array = float64Array;
globalThis.bigInt64Array = bigInt64Array;
globalThis.bigUint64Array = bigUint64Array;
}
const { int8Array, uint8Array, uint8ClampedArray, int16Array, uint16Array,
int32Array, uint32Array, float32Array, float64Array, bigInt64Array, bigUint64Array } = takeAndUseWebSnapshot(createObjects, [
'int8Array', 'uint8Array', 'uint8ClampedArray', 'int16Array', 'uint16Array',
'int32Array', 'uint32Array', 'float32Array', 'float64Array', 'bigInt64Array',
'bigUint64Array'
]);
assertNotSame(globalThis.int8Array, int8Array);
assertEquals(int8Array.length, 3);
for (let i = 0; i < 3; i++){
assertEquals(int8Array[i], i);
}
assertNotSame(globalThis.uint8Array, uint8Array);
assertEquals(uint8Array.length, 3);
for (let i = 0; i < 3; i++){
assertEquals(uint8Array[i], i);
}
assertNotSame(globalThis.uint8ClampedArray, uint8ClampedArray);
assertEquals(uint8ClampedArray.length, 3);
for (let i = 0; i < 3; i++){
assertEquals(uint8ClampedArray[i], i);
}
assertNotSame(globalThis.int16Array, int16Array);
assertEquals(int16Array.length, 3);
for (let i = 0; i < 3; i++){
assertEquals(int16Array[i], i);
}
assertNotSame(globalThis.uint16Array, uint16Array);
assertEquals(uint16Array.length, 3);
for (let i = 0; i < 3; i++){
assertEquals(uint16Array[i], i);
}
assertNotSame(globalThis.int32Array, int32Array);
assertEquals(int32Array.length, 3);
for (let i = 0; i < 3; i++){
assertEquals(int32Array[i], i);
}
assertNotSame(globalThis.uint32Array, uint32Array);
assertEquals(uint32Array.length, 3);
for (let i = 0; i < 3; i++){
assertEquals(uint32Array[i], i);
}
assertNotSame(globalThis.float32Array, float32Array);
assertEquals(float32Array.length, 3);
for (let i = 0; i < 3; i++){
assertEqualsDelta(float32Array[i], i + 0.2);
}
assertNotSame(globalThis.float64Array, float64Array);
assertEquals(float64Array.length, 3);
for (let i = 0; i < 3; i++){
assertEqualsDelta(float64Array[i], i + 0.2);
}
assertNotSame(globalThis.bigInt64Array, bigInt64Array);
assertEquals(bigInt64Array.length, 3);
for (let i = 0; i < 3; i++){
assertEquals(bigInt64Array[i], BigInt(i));
}
assertNotSame(globalThis.bigUint64Array, bigUint64Array);
assertEquals(bigUint64Array.length, 3);
for (let i = 0; i < 3; i++){
assertEquals(bigUint64Array[i], BigInt(i));
}
})();
(function TestInt8Array() {
function createObjects() {
const array = new Int8Array([-129, -128, 1, 127, 128]);
globalThis.array = array;
const array2 = new Int8Array(array.buffer, 1, 2);
globalThis.array2 = array2;
}
const { array, array2 } = takeAndUseWebSnapshot(createObjects, ['array', 'array2']);
assertEquals(array.length, 5);
assertEquals(array[0], 127);
assertEquals(array[1], -128);
assertEquals(array[2], 1);
assertEquals(array[3], 127);
assertEquals(array[4], -128);
assertSame(array.buffer, array2.buffer);
assertEquals(array2.length, 2);
assertEquals(array2[0], array[1]);
assertEquals(array2[1], array[2]);
})();
(function TestUint8Array() {
function createObjects() {
const array = new Uint8Array([-1, 0, 2, 255, 256]);
globalThis.array = array;
const array2 = new Uint8Array(array.buffer, 1, 2);
globalThis.array2 = array2;
}
const { array, array2 } = takeAndUseWebSnapshot(createObjects, ['array', 'array2']);
assertEquals(array.length, 5);
assertEquals(array[0], 255);
assertEquals(array[1], 0);
assertEquals(array[2], 2);
assertEquals(array[3], 255);
assertEquals(array[4], 0);
assertSame(array.buffer, array2.buffer);
assertEquals(array2.length, 2);
assertEquals(array2[0], array[1]);
assertEquals(array2[1], array[2]);
})();
(function TestUint8ClampedArray() {
function createObjects() {
const array = new Uint8ClampedArray([-1,0, 2,255, 256]);
globalThis.array = array;
const array2 = new Uint8ClampedArray(array.buffer, 1, 2);
globalThis.array2 = array2;
}
const { array, array2 } = takeAndUseWebSnapshot(createObjects, ['array', 'array2']);
assertEquals(array.length, 5);
assertEquals(array[0], 0);
assertEquals(array[1], 0);
assertEquals(array[2], 2);
assertEquals(array[3], 255);
assertEquals(array[4], 255);
assertSame(array.buffer, array2.buffer);
assertEquals(array2.length, 2);
assertEquals(array2[0], array[1]);
assertEquals(array2[1], array[2]);
})();
(function TestInt16Array() {
function createObjects() {
const array = new Int16Array([-32769, -32768, 1, 32767, 32768]);
globalThis.array = array;
const array2 = new Int16Array(array.buffer, 2, 2);
globalThis.array2 = array2;
}
const { array, array2 } = takeAndUseWebSnapshot(createObjects, ['array', 'array2']);
assertEquals(array.length, 5);
assertEquals(array[0], 32767);
assertEquals(array[1], -32768);
assertEquals(array[2], 1);
assertEquals(array[3], 32767);
assertEquals(array[4], -32768);
assertSame(array.buffer, array2.buffer);
assertEquals(array2.length, 2);
assertEquals(array2[0], array[1]);
assertEquals(array2[1], array[2]);
})();
(function TestUint16Array() {
function createObjects() {
const array = new Uint16Array([-1,0, 2,65535, 65536]);
globalThis.array = array;
const array2 = new Uint16Array(array.buffer, 2, 2);
globalThis.array2 = array2;
}
const { array, array2 } = takeAndUseWebSnapshot(createObjects, ['array', 'array2']);
assertEquals(array.length, 5);
assertEquals(array[0], 65535);
assertEquals(array[1], 0);
assertEquals(array[2], 2);
assertEquals(array[3], 65535);
assertEquals(array[4], 0);
assertSame(array.buffer, array2.buffer);
assertEquals(array2.length, 2);
assertEquals(array2[0], array[1]);
assertEquals(array2[1], array[2]);
})();
(function TestInt32Array() {
function createObjects() {
const array = new Int32Array([-2147483649, -2147483648, 1, 2147483647, 2147483648]);
globalThis.array = array;
const array2 = new Int32Array(array.buffer, 4, 2);
globalThis.array2 = array2;
}
const { array, array2 } = takeAndUseWebSnapshot(createObjects, ['array', 'array2']);
assertEquals(array.length, 5);
assertEquals(array[0], 2147483647);
assertEquals(array[1], -2147483648);
assertEquals(array[2], 1);
assertEquals(array[3], 2147483647);
assertEquals(array[4], -2147483648);
assertSame(array.buffer, array2.buffer);
assertEquals(array2.length, 2);
assertEquals(array2[0], array[1]);
assertEquals(array2[1], array[2]);
})();
(function TestUint32Array() {
function createObjects() {
const array = new Uint32Array([-1,0, 2,4294967295, 4294967296]);
globalThis.array = array;
const array2 = new Uint32Array(array.buffer, 4, 2);
globalThis.array2 = array2;
}
const { array, array2 } = takeAndUseWebSnapshot(createObjects, ['array', 'array2']);
assertEquals(array.length, 5);
assertEquals(array[0], 4294967295);
assertEquals(array[1], 0);
assertEquals(array[2], 2);
assertEquals(array[3], 4294967295);
assertEquals(array[4], 0);
assertSame(array.buffer, array2.buffer);
assertEquals(array2.length, 2);
assertEquals(array2[0], array[1]);
assertEquals(array2[1], array[2]);
})();
(function TestBigInt64Array() {
function createObjects() {
const array = new BigInt64Array([BigInt(-(2**63))-1n, BigInt(-(2**63)), 1n, BigInt((2**63)) - 1n, BigInt((2**63))]);
globalThis.array = array;
const array2 = new BigInt64Array(array.buffer, 8, 2);
globalThis.array2 = array2;
}
const { array, array2 } = takeAndUseWebSnapshot(createObjects, ['array', 'array2']);
assertEquals(array.length, 5);
assertEquals(array[0], BigInt((2**63)) - 1n);
assertEquals(array[1], BigInt(-(2**63)));
assertEquals(array[2], 1n);
assertEquals(array[3], BigInt((2**63)) - 1n);
assertEquals(array[4], BigInt(-(2 ** 63)));
assertSame(array.buffer, array2.buffer);
assertEquals(array2.length, 2);
assertEquals(array2[0], array[1]);
assertEquals(array2[1], array[2]);
})();
(function TestBigUint32Array() {
function createObjects() {
const array = new BigUint64Array([-1n, 0n, 2n,BigInt(2**64)-1n, BigInt(2**64)]);
globalThis.array = array;
const array2 = new BigUint64Array(array.buffer, 8, 2);
globalThis.array2 = array2;
}
const { array, array2 } = takeAndUseWebSnapshot(createObjects, ['array', 'array2']);
assertEquals(array.length, 5);
assertEquals(array[0], BigInt(2**64)-1n);
assertEquals(array[1], 0n);
assertEquals(array[2], 2n);
assertEquals(array[3], BigInt(2**64)-1n);
assertEquals(array[4], 0n);
assertSame(array.buffer, array2.buffer);
assertEquals(array2.length, 2);
assertEquals(array2[0], array[1]);
assertEquals(array2[1], array[2]);
})();