[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:
parent
38aca8f586
commit
293654ec6b
@ -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)
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
312
test/mjsunit/web-snapshot/web-snapshot-typed-array.js
Normal file
312
test/mjsunit/web-snapshot/web-snapshot-typed-array.js
Normal 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]);
|
||||
})();
|
Loading…
Reference in New Issue
Block a user