Commit cc709727 authored by jameslahm's avatar jameslahm Committed by V8 LUCI CQ

[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: 's avatarCamillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/main@{#81189}
parent 70289dd7
......@@ -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));
......
......@@ -1036,6 +1036,7 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> {
private:
friend class FactoryBase<Factory>;
friend class WebSnapshotDeserializer;
// ------
// Customization points for FactoryBase
......
......@@ -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);
}
......
......@@ -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)
......
......@@ -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());
......
......@@ -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
......
This diff is collapsed.
......@@ -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;
......
// 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);
})()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment