[web snapshot] Support resizable ArrayBuffer, detached

... ArrayBuffer and shared ArrayBuffer.

Bug: v8:11525
Change-Id: I6b3f78d5cf6528123b40c49f2767ade2b6bfbed1
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3706279
Commit-Queue: 王澳 <wangao.james@bytedance.com>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/main@{#81189}
This commit is contained in:
jameslahm 2022-06-15 21:14:31 +08:00 committed by V8 LUCI CQ
parent 70289dd72a
commit cc709727e7
10 changed files with 558 additions and 151 deletions

View File

@ -2988,31 +2988,15 @@ void Factory::TypeAndSizeForElementsKind(ElementsKind kind,
}
}
namespace {
void ForFixedTypedArray(ExternalArrayType array_type, size_t* element_size,
ElementsKind* element_kind) {
switch (array_type) {
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
case kExternal##Type##Array: \
*element_size = sizeof(ctype); \
*element_kind = TYPE##_ELEMENTS; \
return;
TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
}
UNREACHABLE();
}
} // namespace
Handle<JSArrayBufferView> Factory::NewJSArrayBufferView(
Handle<Map> map, Handle<FixedArrayBase> elements,
Handle<JSArrayBuffer> buffer, size_t byte_offset, size_t byte_length) {
CHECK_LE(byte_length, buffer->byte_length());
CHECK_LE(byte_offset, buffer->byte_length());
CHECK_LE(byte_offset + byte_length, buffer->byte_length());
if (!IsRabGsabTypedArrayElementsKind(map->elements_kind())) {
CHECK_LE(byte_length, buffer->GetByteLength());
CHECK_LE(byte_offset, buffer->GetByteLength());
CHECK_LE(byte_offset + byte_length, buffer->GetByteLength());
}
Handle<JSArrayBufferView> array_buffer_view = Handle<JSArrayBufferView>::cast(
NewJSObjectFromMap(map, AllocationType::kYoung));
DisallowGarbageCollection no_gc;
@ -3035,28 +3019,17 @@ Handle<JSTypedArray> Factory::NewJSTypedArray(ExternalArrayType type,
size_t length) {
size_t element_size;
ElementsKind elements_kind;
ForFixedTypedArray(type, &element_size, &elements_kind);
JSTypedArray::ForFixedTypedArray(type, &element_size, &elements_kind);
size_t byte_length = length * element_size;
CHECK_LE(length, JSTypedArray::kMaxLength);
CHECK_EQ(length, byte_length / element_size);
CHECK_EQ(0, byte_offset % ElementsKindToByteSize(elements_kind));
Handle<Map> map;
switch (elements_kind) {
#define TYPED_ARRAY_FUN(Type, type, TYPE, ctype) \
case TYPE##_ELEMENTS: \
map = \
handle(isolate()->native_context()->type##_array_fun().initial_map(), \
isolate()); \
break;
TYPED_ARRAYS(TYPED_ARRAY_FUN)
#undef TYPED_ARRAY_FUN
default:
UNREACHABLE();
}
Handle<Map> map(
isolate()->raw_native_context().TypedArrayElementsKindToCtorMap(
elements_kind),
isolate());
Handle<JSTypedArray> typed_array =
Handle<JSTypedArray>::cast(NewJSArrayBufferView(
map, empty_byte_array(), buffer, byte_offset, byte_length));

View File

@ -1036,6 +1036,7 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> {
private:
friend class FactoryBase<Factory>;
friend class WebSnapshotDeserializer;
// ------
// Customization points for FactoryBase

View File

@ -295,6 +295,28 @@ ScriptContextTable NativeContext::synchronized_script_context_table() const {
get(SCRIPT_CONTEXT_TABLE_INDEX, kAcquireLoad));
}
Map NativeContext::TypedArrayElementsKindToCtorMap(
ElementsKind element_kind) const {
int ctor_index = Context::FIRST_FIXED_TYPED_ARRAY_FUN_INDEX + element_kind -
ElementsKind::FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND;
Map map = Map::cast(JSFunction::cast(get(ctor_index)).initial_map());
DCHECK_EQ(map.elements_kind(), element_kind);
DCHECK(InstanceTypeChecker::IsJSTypedArray(map.instance_type()));
return map;
}
Map NativeContext::TypedArrayElementsKindToRabGsabCtorMap(
ElementsKind element_kind) const {
int ctor_index = Context::FIRST_RAB_GSAB_TYPED_ARRAY_MAP_INDEX +
element_kind -
ElementsKind::FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND;
Map map = Map::cast(get(ctor_index));
DCHECK_EQ(map.elements_kind(),
GetCorrespondingRabGsabElementsKind(element_kind));
DCHECK(InstanceTypeChecker::IsJSTypedArray(map.instance_type()));
return map;
}
void NativeContext::SetOptimizedCodeListHead(Object head) {
set(OPTIMIZED_CODE_LIST, head, UPDATE_WRITE_BARRIER, kReleaseStore);
}

View File

@ -740,6 +740,11 @@ class NativeContext : public Context {
return Context::global_object();
}
inline Map TypedArrayElementsKindToCtorMap(ElementsKind element_kind) const;
inline Map TypedArrayElementsKindToRabGsabCtorMap(
ElementsKind element_kind) const;
// Dispatched behavior.
DECL_PRINTER(NativeContext)
DECL_VERIFIER(NativeContext)

View File

@ -230,6 +230,23 @@ bool JSTypedArray::IsDetachedOrOutOfBounds() const {
return out_of_bounds;
}
// static
inline void JSTypedArray::ForFixedTypedArray(ExternalArrayType array_type,
size_t* element_size,
ElementsKind* element_kind) {
switch (array_type) {
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
case kExternal##Type##Array: \
*element_size = sizeof(ctype); \
*element_kind = TYPE##_ELEMENTS; \
return;
TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
}
UNREACHABLE();
}
size_t JSTypedArray::length() const {
DCHECK(!is_length_tracking());
DCHECK(!is_backed_by_rab());

View File

@ -310,6 +310,10 @@ class JSTypedArray
inline bool IsOutOfBounds() const;
inline bool IsDetachedOrOutOfBounds() const;
static inline void ForFixedTypedArray(ExternalArrayType array_type,
size_t* element_size,
ElementsKind* element_kind);
static size_t LengthTrackingGsabBackedTypedArrayLength(Isolate* isolate,
Address raw_array);
@ -372,6 +376,7 @@ class JSTypedArray
template <typename IsolateT>
friend class Deserializer;
friend class Factory;
friend class WebSnapshotDeserializer;
DECL_PRIMITIVE_SETTER(length, size_t)
// Reads the "length" field, doesn't assert the TypedArray is not RAB / GSAB

View File

@ -15,7 +15,7 @@
#include "src/base/platform/wrappers.h"
#include "src/handles/handles.h"
#include "src/logging/runtime-call-stats-scope.h"
#include "src/objects/contexts.h"
#include "src/objects/contexts-inl.h"
#include "src/objects/js-array-buffer-inl.h"
#include "src/objects/js-regexp-inl.h"
#include "src/objects/script.h"
@ -1211,20 +1211,8 @@ 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;
CHECK_EQ(array_buffer->GetByteLength(), 0);
}
uint32_t id;
if (InsertIntoIndexMap(array_buffer_ids_, *array_buffer, id)) {
@ -1639,40 +1627,91 @@ void WebSnapshotSerializer::SerializeElements(Handle<JSObject> object,
}
}
}
uint32_t WebSnapshotSerializerDeserializer::ArrayBufferKindToFlags(
Handle<JSArrayBuffer> array_buffer) {
return DetachedBitField::encode(array_buffer->was_detached()) |
SharedBitField::encode(array_buffer->is_shared()) |
ResizableBitField::encode(array_buffer->is_resizable());
}
// Format (serialized array buffer):
// - Length
// - ArrayBufferFlags, including was_detached, is_shared and is_resizable.
// - Byte length
// - if is_resizable
// - Max byte length
// - Raw bytes
void WebSnapshotSerializer::SerializeArrayBuffer(
Handle<JSArrayBuffer> array_buffer) {
size_t byte_length = array_buffer->byte_length();
size_t byte_length = array_buffer->GetByteLength();
if (byte_length > std::numeric_limits<uint32_t>::max()) {
Throw("Too large array buffer");
return;
}
array_buffer_serializer_.WriteUint32(ArrayBufferKindToFlags(array_buffer));
array_buffer_serializer_.WriteUint32(static_cast<uint32_t>(byte_length));
if (array_buffer->is_resizable()) {
size_t max_byte_length = array_buffer->max_byte_length();
if (max_byte_length > std::numeric_limits<uint32_t>::max()) {
Throw("Too large resizable array buffer");
return;
}
array_buffer_serializer_.WriteUint32(
static_cast<uint32_t>(array_buffer->max_byte_length()));
}
array_buffer_serializer_.WriteRawBytes(array_buffer->backing_store(),
byte_length);
}
uint32_t WebSnapshotSerializerDeserializer::ArrayBufferViewKindToFlags(
Handle<JSArrayBufferView> array_buffer_view) {
return LengthTrackingBitField::encode(
array_buffer_view->is_length_tracking());
}
// static
ExternalArrayType
WebSnapshotSerializerDeserializer::TypedArrayTypeToExternalArrayType(
TypedArrayType type) {
switch (type) {
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
case k##Type##Array: \
return kExternal##Type##Array;
TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
}
UNREACHABLE();
}
// static
WebSnapshotSerializerDeserializer::TypedArrayType
WebSnapshotSerializerDeserializer::ExternalArrayTypeToTypedArrayType(
ExternalArrayType type) {
switch (type) {
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
case kExternal##Type##Array: \
return TypedArrayType::k##Type##Array;
TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
}
UNREACHABLE();
}
// Format (serialized typed array):
// - TypedArrayType
// - Serialized ArrayBuffer
// - ArrayBufferViewFlags, including is_length_tracking
// - Byte offset
// - Byte length
// - If not is_length_tracking
// - 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
}
TypedArrayType typed_array_type =
ExternalArrayTypeToTypedArrayType(typed_array->type());
typed_array_serializer_.WriteUint32(typed_array_type);
WriteValue(typed_array->GetBuffer(), typed_array_serializer_);
// TODO(v8:11525): Implement WriteByte.
typed_array_serializer_.WriteUint32(ArrayBufferViewKindToFlags(typed_array));
if (typed_array->byte_offset() > std::numeric_limits<uint32_t>::max()) {
Throw("Too large byte offset in TypedArray");
return;
@ -1683,9 +1722,10 @@ void WebSnapshotSerializer::SerializeTypedArray(
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.
if (!typed_array->is_length_tracking()) {
typed_array_serializer_.WriteUint32(
static_cast<uint32_t>(typed_array->byte_length()));
}
}
// Format (serialized export):
@ -3245,21 +3285,83 @@ void WebSnapshotDeserializer::DeserializeArrayBuffers() {
array_buffers_ = *array_buffers_handle_;
for (; current_array_buffer_count_ < array_buffer_count_;
++current_array_buffer_count_) {
uint32_t flags;
uint32_t byte_length;
if (!deserializer_->ReadUint32(&byte_length) ||
if (!deserializer_->ReadUint32(&flags) ||
!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");
uint32_t mask = DetachedBitField::kMask | SharedBitField::kMask |
ResizableBitField::kMask;
if ((flags | mask) != mask) {
Throw("Malformed array buffer");
return;
}
bool was_detached = DetachedBitField::decode(flags);
CHECK_IMPLIES(was_detached, (byte_length == 0));
SharedFlag shared = SharedBitField::decode(flags) ? SharedFlag::kShared
: SharedFlag::kNotShared;
CHECK_IMPLIES(was_detached, (shared == SharedFlag::kNotShared));
ResizableFlag resizable = ResizableBitField::decode(flags)
? ResizableFlag::kResizable
: ResizableFlag::kNotResizable;
uint32_t max_byte_length = byte_length;
if (resizable == ResizableFlag::kResizable) {
if (!deserializer_->ReadUint32(&max_byte_length)) {
Throw("Malformed array buffer");
return;
}
CHECK_GE(max_byte_length, byte_length);
}
Handle<Map> map;
if (shared == SharedFlag::kNotShared) {
map = handle(
isolate_->raw_native_context().array_buffer_fun().initial_map(),
isolate_);
} else {
map = handle(isolate_->raw_native_context()
.shared_array_buffer_fun()
.initial_map(),
isolate_);
}
Handle<JSArrayBuffer> array_buffer = Handle<JSArrayBuffer>::cast(
isolate_->factory()->NewJSObjectFromMap(map, AllocationType::kYoung));
array_buffer->Setup(shared, resizable, nullptr);
std::unique_ptr<BackingStore> backing_store;
if (was_detached) {
array_buffer->set_was_detached(true);
} else {
if (resizable == ResizableFlag::kNotResizable) {
backing_store = BackingStore::Allocate(isolate_, byte_length, shared,
InitializedFlag::kUninitialized);
} else {
size_t page_size, initial_pages, max_pages;
if (JSArrayBuffer::GetResizableBackingStorePageConfiguration(
isolate_, byte_length, max_byte_length, kThrowOnError,
&page_size, &initial_pages, &max_pages)
.IsNothing()) {
Throw("Create array buffer failed");
return;
}
backing_store = BackingStore::TryAllocateAndPartiallyCommitMemory(
isolate_, byte_length, max_byte_length, page_size, initial_pages,
max_pages, false, shared);
}
if (!backing_store) {
Throw("Create array buffer failed");
return;
}
array_buffer->Attach(std::move(backing_store));
}
array_buffer->set_max_byte_length(max_byte_length);
if (byte_length > 0) {
memcpy(array_buffer->backing_store(), deserializer_->position_,
byte_length);
@ -3291,36 +3393,80 @@ void WebSnapshotDeserializer::DeserializeTypedArrays() {
Handle<JSArrayBuffer> array_buffer(
JSArrayBuffer::cast(std::get<0>(ReadValue())), isolate_);
uint32_t byte_offset = 0;
uint32_t flags = 0;
if (!deserializer_->ReadUint32(&flags) ||
!deserializer_->ReadUint32(&byte_offset)) {
Throw("Malformed typed array");
return;
}
size_t element_size = 0;
ElementsKind element_kind = UINT8_ELEMENTS;
JSTypedArray::ForFixedTypedArray(
TypedArrayTypeToExternalArrayType(
static_cast<TypedArrayType>(typed_array_type)),
&element_size, &element_kind);
Handle<Map> map(
isolate_->raw_native_context().TypedArrayElementsKindToCtorMap(
element_kind),
isolate_);
uint32_t mask = LengthTrackingBitField::kMask;
if ((flags | mask) != mask) {
Throw("Malformed typed array");
return;
}
if (byte_offset % element_size != 0) {
Throw("Malformed typed array");
return;
}
uint32_t byte_length = 0;
if (!deserializer_->ReadUint32(&byte_offset) ||
!deserializer_->ReadUint32(&byte_length)) {
Throw("Malformed typed array");
return;
size_t length = 0;
bool is_length_tracking = LengthTrackingBitField::decode(flags);
if (is_length_tracking) {
CHECK(array_buffer->is_resizable());
} else {
if (!deserializer_->ReadUint32(&byte_length)) {
Throw("Malformed typed array");
return;
}
if (byte_length % element_size != 0) {
Throw("Malformed typed array");
return;
}
length = byte_length / element_size;
if (length > JSTypedArray::kMaxLength) {
Throw("Too large 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
bool rabGsab = array_buffer->is_resizable() &&
(!array_buffer->is_shared() || is_length_tracking);
if (rabGsab) {
map = handle(
isolate_->raw_native_context().TypedArrayElementsKindToRabGsabCtorMap(
element_kind),
isolate_);
}
if (element_size == 0 || byte_offset % element_size != 0 ||
byte_length % element_size != 0) {
Throw("Malformed typed array");
return;
Handle<JSTypedArray> typed_array =
Handle<JSTypedArray>::cast(factory()->NewJSArrayBufferView(
map, factory()->empty_byte_array(), array_buffer, byte_offset,
byte_length));
{
DisallowGarbageCollection no_gc;
JSTypedArray raw = *typed_array;
raw.set_length(length);
raw.SetOffHeapDataPtr(isolate_, array_buffer->backing_store(),
byte_offset);
raw.set_is_length_tracking(is_length_tracking);
raw.set_is_backed_by_rab(array_buffer->is_resizable() &&
!array_buffer->is_shared());
}
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);

View File

@ -85,6 +85,11 @@ class WebSnapshotSerializerDeserializer {
kBigUint64Array,
};
static inline ExternalArrayType TypedArrayTypeToExternalArrayType(
TypedArrayType type);
static inline TypedArrayType ExternalArrayTypeToTypedArrayType(
ExternalArrayType type);
static constexpr uint8_t kMagicNumber[4] = {'+', '+', '+', ';'};
enum ContextType : uint8_t { FUNCTION, BLOCK };
@ -100,6 +105,11 @@ class WebSnapshotSerializerDeserializer {
uint32_t AttributesToFlags(PropertyDetails details);
PropertyAttributes FlagsToAttributes(uint32_t flags);
uint32_t ArrayBufferViewKindToFlags(
Handle<JSArrayBufferView> array_buffer_view);
uint32_t ArrayBufferKindToFlags(Handle<JSArrayBuffer> array_buffer);
// The maximum count of items for each value type (strings, objects etc.)
static constexpr uint32_t kMaxItemCount =
static_cast<uint32_t>(FixedArray::kMaxLength - 1);
@ -123,6 +133,22 @@ class WebSnapshotSerializerDeserializer {
Isolate* isolate_;
const char* error_message_ = nullptr;
// Encode JSArrayBufferFlags, including was_detached, is_shared, is_resizable.
// DetachedBitField indicates whether the ArrayBuffer was detached.
using DetachedBitField = base::BitField<bool, 0, 1>;
// SharedBitField indicates whether the ArrayBuffer is SharedArrayBuffer.
using SharedBitField = DetachedBitField::Next<bool, 1>;
// ResizableBitField indicates whether the ArrayBuffer is ResizableArrayBuffer
// or GrowableSharedArrayBuffer.
using ResizableBitField = SharedBitField::Next<bool, 1>;
// Encode JSArrayBufferViewFlags, including is_length_tracking, see
// https://github.com/tc39/proposal-resizablearraybuffer.
// LengthTrackingBitField indicates whether the ArrayBufferView should track
// the length of the backing buffer, that is whether the ArrayBufferView is
// constructed without the specified length argument.
using LengthTrackingBitField = base::BitField<bool, 0, 1>;
private:
WebSnapshotSerializerDeserializer(const WebSnapshotSerializerDeserializer&) =
delete;

View File

@ -0,0 +1,85 @@
// 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 --harmony-rab-gsab
'use strict';
d8.file.execute('test/mjsunit/web-snapshot/web-snapshot-helpers.js');
(function TestSharedArrayBuffer() {
function createObjects() {
const growableArrayBuffer = new SharedArrayBuffer(5, { maxByteLength: 10 });
globalThis.growableArrayBuffer = growableArrayBuffer;
const array1 = new Uint8Array(growableArrayBuffer);
for (let i = 0; i < 5; i++) {
array1[i] = i;
}
const arrayBuffer = new SharedArrayBuffer(5);
globalThis.arrayBuffer = arrayBuffer;
const array2 = new Uint8Array(arrayBuffer);
for (let i = 0; i < 5; i++) {
array2[i] = i;
}
}
const { growableArrayBuffer, arrayBuffer } = takeAndUseWebSnapshot(createObjects, ['growableArrayBuffer', 'arrayBuffer']);
assertEquals(growableArrayBuffer.byteLength, 5);
assertEquals(growableArrayBuffer.maxByteLength, 10);
assertTrue(growableArrayBuffer.growable);
const array1 = new Uint8Array(growableArrayBuffer);
for (let i = 0; i < 5; i++) {
assertEquals(array1[i], i);
}
assertEquals(arrayBuffer.byteLength, 5);
assertEquals(arrayBuffer.maxByteLength, 5);
assertFalse(arrayBuffer.growable, false);
const array2 = new Uint8Array(arrayBuffer);
for (let i = 0; i < 5; i++) {
assertEquals(array2[i], i);
}
})();
(function TestArrayBuffer() {
function createObjects() {
const resizableArrayBuffer = new ArrayBuffer(5, {maxByteLength: 10});
globalThis.resizableArrayBuffer = resizableArrayBuffer;
const array1 = new Uint8Array(resizableArrayBuffer);
for (let i = 0; i < 5; i++) {
array1[i] = i;
}
const arrayBuffer = new ArrayBuffer(5);
globalThis.arrayBuffer = arrayBuffer;
const array2 = new Uint8Array(arrayBuffer);
for (let i = 0; i < 5; i++) {
array2[i] = i;
}
const detachedArrayBuffer = new ArrayBuffer(5);
%ArrayBufferDetach(detachedArrayBuffer);
globalThis.detachedArrayBuffer = detachedArrayBuffer;
}
const { resizableArrayBuffer, arrayBuffer, detachedArrayBuffer } = takeAndUseWebSnapshot(createObjects, ['resizableArrayBuffer', 'arrayBuffer', 'detachedArrayBuffer']);
assertEquals(resizableArrayBuffer.byteLength, 5);
assertEquals(resizableArrayBuffer.maxByteLength, 10);
assertTrue(resizableArrayBuffer.resizable)
const array1 = new Uint8Array(resizableArrayBuffer);
for (let i = 0; i < 5; i++) {
assertEquals(array1[i], i);
}
assertEquals(arrayBuffer.byteLength, 5);
assertEquals(arrayBuffer.maxByteLength, 5);
assertFalse(arrayBuffer.resizable)
const array2 = new Uint8Array(arrayBuffer);
for (let i = 0; i < 5; i++) {
assertEquals(array2[i], i);
}
assertEquals(detachedArrayBuffer.byteLength, 0);
assertEquals(detachedArrayBuffer.maxByteLength, 0);
})()

View File

@ -2,7 +2,7 @@
// 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
// Flags: --experimental-d8-web-snapshot-api --allow-natives-syntax --harmony-rab-gsab
'use strict';
@ -11,47 +11,47 @@ 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++){
for (let i = 0; i < 3; i++) {
int8Array[i] = i;
}
const uint8Array = new Uint8Array(3);
for (let i = 0; i < 3; i++){
for (let i = 0; i < 3; i++) {
uint8Array[i] = i;
}
const uint8ClampedArray = new Uint8ClampedArray(3);
for (let i = 0; i < 3; i++){
for (let i = 0; i < 3; i++) {
uint8ClampedArray[i] = i;
}
const int16Array = new Int16Array(3);
for (let i = 0; i < 3; i++){
for (let i = 0; i < 3; i++) {
int16Array[i] = i;
}
const uint16Array = new Uint16Array(3);
for (let i = 0; i < 3; i++){
for (let i = 0; i < 3; i++) {
uint16Array[i] = i;
}
const int32Array = new Int32Array(3);
for (let i = 0; i < 3; i++){
for (let i = 0; i < 3; i++) {
int32Array[i] = i;
}
const uint32Array = new Uint32Array(3);
for (let i = 0; i < 3; i++){
for (let i = 0; i < 3; i++) {
uint32Array[i] = i;
}
const float32Array = new Float32Array(3);
for (let i = 0; i < 3; i++){
for (let i = 0; i < 3; i++) {
float32Array[i] = i + 0.2;
}
const float64Array = new Float64Array(3);
for (let i = 0; i < 3; i++){
for (let i = 0; i < 3; i++) {
float64Array[i] = i + 0.2;
}
const bigInt64Array = new BigInt64Array(3);
for (let i = 0; i < 3; i++){
for (let i = 0; i < 3; i++) {
bigInt64Array[i] = BigInt(i);
}
const bigUint64Array = new BigUint64Array(3);
for (let i = 0; i < 3; i++){
for (let i = 0; i < 3; i++) {
bigUint64Array[i] = BigInt(i);
}
globalThis.int8Array = int8Array;
@ -65,72 +65,90 @@ d8.file.execute('test/mjsunit/web-snapshot/web-snapshot-helpers.js');
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'
]);
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++){
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++){
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++){
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++){
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++){
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++){
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++){
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++){
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++){
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++){
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++){
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]);
@ -138,7 +156,10 @@ d8.file.execute('test/mjsunit/web-snapshot/web-snapshot-helpers.js');
const array2 = new Int8Array(array.buffer, 1, 2);
globalThis.array2 = array2;
}
const { array, array2 } = takeAndUseWebSnapshot(createObjects, ['array', 'array2']);
const {array, array2} = takeAndUseWebSnapshot(createObjects, [
'array',
'array2',
]);
assertEquals(array.length, 5);
assertEquals(array[0], 127);
assertEquals(array[1], -128);
@ -158,7 +179,10 @@ d8.file.execute('test/mjsunit/web-snapshot/web-snapshot-helpers.js');
const array2 = new Uint8Array(array.buffer, 1, 2);
globalThis.array2 = array2;
}
const { array, array2 } = takeAndUseWebSnapshot(createObjects, ['array', 'array2']);
const {array, array2} = takeAndUseWebSnapshot(createObjects, [
'array',
'array2',
]);
assertEquals(array.length, 5);
assertEquals(array[0], 255);
assertEquals(array[1], 0);
@ -173,12 +197,15 @@ d8.file.execute('test/mjsunit/web-snapshot/web-snapshot-helpers.js');
(function TestUint8ClampedArray() {
function createObjects() {
const array = new Uint8ClampedArray([-1,0, 2,255, 256]);
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']);
const {array, array2} = takeAndUseWebSnapshot(createObjects, [
'array',
'array2',
]);
assertEquals(array.length, 5);
assertEquals(array[0], 0);
assertEquals(array[1], 0);
@ -198,7 +225,10 @@ d8.file.execute('test/mjsunit/web-snapshot/web-snapshot-helpers.js');
const array2 = new Int16Array(array.buffer, 2, 2);
globalThis.array2 = array2;
}
const { array, array2 } = takeAndUseWebSnapshot(createObjects, ['array', 'array2']);
const {array, array2} = takeAndUseWebSnapshot(createObjects, [
'array',
'array2',
]);
assertEquals(array.length, 5);
assertEquals(array[0], 32767);
assertEquals(array[1], -32768);
@ -213,12 +243,15 @@ d8.file.execute('test/mjsunit/web-snapshot/web-snapshot-helpers.js');
(function TestUint16Array() {
function createObjects() {
const array = new Uint16Array([-1,0, 2,65535, 65536]);
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']);
const {array, array2} = takeAndUseWebSnapshot(createObjects, [
'array',
'array2',
]);
assertEquals(array.length, 5);
assertEquals(array[0], 65535);
assertEquals(array[1], 0);
@ -233,12 +266,21 @@ d8.file.execute('test/mjsunit/web-snapshot/web-snapshot-helpers.js');
(function TestInt32Array() {
function createObjects() {
const array = new Int32Array([-2147483649, -2147483648, 1, 2147483647, 2147483648]);
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']);
const {array, array2} = takeAndUseWebSnapshot(createObjects, [
'array',
'array2',
]);
assertEquals(array.length, 5);
assertEquals(array[0], 2147483647);
assertEquals(array[1], -2147483648);
@ -253,12 +295,15 @@ d8.file.execute('test/mjsunit/web-snapshot/web-snapshot-helpers.js');
(function TestUint32Array() {
function createObjects() {
const array = new Uint32Array([-1,0, 2,4294967295, 4294967296]);
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']);
const {array, array2} = takeAndUseWebSnapshot(createObjects, [
'array',
'array2',
]);
assertEquals(array.length, 5);
assertEquals(array[0], 4294967295);
assertEquals(array[1], 0);
@ -273,17 +318,26 @@ d8.file.execute('test/mjsunit/web-snapshot/web-snapshot-helpers.js');
(function TestBigInt64Array() {
function createObjects() {
const array = new BigInt64Array([BigInt(-(2**63))-1n, BigInt(-(2**63)), 1n, BigInt((2**63)) - 1n, BigInt((2**63))]);
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']);
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[0], BigInt(2 ** 63) - 1n);
assertEquals(array[1], BigInt(-(2 ** 63)));
assertEquals(array[2], 1n);
assertEquals(array[3], BigInt((2**63)) - 1n);
assertEquals(array[3], BigInt(2 ** 63) - 1n);
assertEquals(array[4], BigInt(-(2 ** 63)));
assertSame(array.buffer, array2.buffer);
assertEquals(array2.length, 2);
@ -293,20 +347,93 @@ d8.file.execute('test/mjsunit/web-snapshot/web-snapshot-helpers.js');
(function TestBigUint32Array() {
function createObjects() {
const array = new BigUint64Array([-1n, 0n, 2n,BigInt(2**64)-1n, BigInt(2**64)]);
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']);
const {array, array2} = takeAndUseWebSnapshot(createObjects, [
'array',
'array2',
]);
assertEquals(array.length, 5);
assertEquals(array[0], BigInt(2**64)-1n);
assertEquals(array[0], BigInt(2 ** 64) - 1n);
assertEquals(array[1], 0n);
assertEquals(array[2], 2n);
assertEquals(array[3], BigInt(2**64)-1n);
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]);
})();
(function TestResizableTypedArray() {
function createObjects() {
let resizableArrayBuffer = new ArrayBuffer(1024, {
maxByteLength: 1024 * 2,
});
// 0 offset, auto length
let array = new Uint32Array(resizableArrayBuffer);
globalThis.array = array;
// Non-0 offset, auto length
let array2 = new Uint32Array(resizableArrayBuffer, 256);
globalThis.array2 = array2;
// Non-0 offset, fixed length
let array3 = new Uint32Array(resizableArrayBuffer, 128, 4);
globalThis.array3 = array3;
}
const {array, array2, array3} = takeAndUseWebSnapshot(createObjects, [
'array',
'array2',
'array3',
]);
assertTrue(array.buffer.resizable);
assertEquals(array.length, 256); // (1024 - 0) / 4
assertEquals(array2.length, 192); // (1024 - 256) / 4
assertEquals(array3.length, 4);
array.buffer.resize(1024 * 2);
assertEquals(array.length, 512); // (2048 - 0) / 4
assertEquals(array2.length, 448); // (2048 - 256) / 4
assertEquals(array3.length, 4);
})();
(function TestGrowableTypedArray() {
function createObjects() {
let resizableArrayBuffer = new SharedArrayBuffer(1024, {
maxByteLength: 1024 * 2,
});
// 0 offset, auto length
let array = new Uint32Array(resizableArrayBuffer);
globalThis.array = array;
// Non-0 offset, auto length
let array2 = new Uint32Array(resizableArrayBuffer, 256);
globalThis.array2 = array2;
// Non-0 offset, fixed length
let array3 = new Uint32Array(resizableArrayBuffer, 128, 4);
globalThis.array3 = array3;
}
const {array, array2, array3} = takeAndUseWebSnapshot(createObjects, [
'array',
'array2',
'array3',
]);
assertTrue(array.buffer.growable);
assertEquals(array.length, 256); // (1024 - 0) / 4
assertEquals(array2.length, 192); // (1024 - 256) / 4
assertEquals(array3.length, 4);
array.buffer.grow(1024 * 2);
assertEquals(array.length, 512); // (2048 - 0) / 4
assertEquals(array2.length, 448); // (2048 - 256) / 4
assertEquals(array3.length, 4);
})();