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, ...@@ -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<JSArrayBufferView> Factory::NewJSArrayBufferView(
Handle<Map> map, Handle<FixedArrayBase> elements, Handle<Map> map, Handle<FixedArrayBase> elements,
Handle<JSArrayBuffer> buffer, size_t byte_offset, size_t byte_length) { Handle<JSArrayBuffer> buffer, size_t byte_offset, size_t byte_length) {
CHECK_LE(byte_length, buffer->byte_length()); if (!IsRabGsabTypedArrayElementsKind(map->elements_kind())) {
CHECK_LE(byte_offset, buffer->byte_length()); CHECK_LE(byte_length, buffer->GetByteLength());
CHECK_LE(byte_offset + byte_length, buffer->byte_length()); CHECK_LE(byte_offset, buffer->GetByteLength());
CHECK_LE(byte_offset + byte_length, buffer->GetByteLength());
}
Handle<JSArrayBufferView> array_buffer_view = Handle<JSArrayBufferView>::cast( Handle<JSArrayBufferView> array_buffer_view = Handle<JSArrayBufferView>::cast(
NewJSObjectFromMap(map, AllocationType::kYoung)); NewJSObjectFromMap(map, AllocationType::kYoung));
DisallowGarbageCollection no_gc; DisallowGarbageCollection no_gc;
...@@ -3035,28 +3019,17 @@ Handle<JSTypedArray> Factory::NewJSTypedArray(ExternalArrayType type, ...@@ -3035,28 +3019,17 @@ Handle<JSTypedArray> Factory::NewJSTypedArray(ExternalArrayType type,
size_t length) { size_t length) {
size_t element_size; size_t element_size;
ElementsKind elements_kind; ElementsKind elements_kind;
ForFixedTypedArray(type, &element_size, &elements_kind); JSTypedArray::ForFixedTypedArray(type, &element_size, &elements_kind);
size_t byte_length = length * element_size; size_t byte_length = length * element_size;
CHECK_LE(length, JSTypedArray::kMaxLength); CHECK_LE(length, JSTypedArray::kMaxLength);
CHECK_EQ(length, byte_length / element_size); CHECK_EQ(length, byte_length / element_size);
CHECK_EQ(0, byte_offset % ElementsKindToByteSize(elements_kind)); CHECK_EQ(0, byte_offset % ElementsKindToByteSize(elements_kind));
Handle<Map> map; Handle<Map> map(
switch (elements_kind) { isolate()->raw_native_context().TypedArrayElementsKindToCtorMap(
#define TYPED_ARRAY_FUN(Type, type, TYPE, ctype) \ elements_kind),
case TYPE##_ELEMENTS: \ isolate());
map = \
handle(isolate()->native_context()->type##_array_fun().initial_map(), \
isolate()); \
break;
TYPED_ARRAYS(TYPED_ARRAY_FUN)
#undef TYPED_ARRAY_FUN
default:
UNREACHABLE();
}
Handle<JSTypedArray> typed_array = Handle<JSTypedArray> typed_array =
Handle<JSTypedArray>::cast(NewJSArrayBufferView( Handle<JSTypedArray>::cast(NewJSArrayBufferView(
map, empty_byte_array(), buffer, byte_offset, byte_length)); map, empty_byte_array(), buffer, byte_offset, byte_length));
......
...@@ -1036,6 +1036,7 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> { ...@@ -1036,6 +1036,7 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> {
private: private:
friend class FactoryBase<Factory>; friend class FactoryBase<Factory>;
friend class WebSnapshotDeserializer;
// ------ // ------
// Customization points for FactoryBase // Customization points for FactoryBase
......
...@@ -295,6 +295,28 @@ ScriptContextTable NativeContext::synchronized_script_context_table() const { ...@@ -295,6 +295,28 @@ ScriptContextTable NativeContext::synchronized_script_context_table() const {
get(SCRIPT_CONTEXT_TABLE_INDEX, kAcquireLoad)); 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) { void NativeContext::SetOptimizedCodeListHead(Object head) {
set(OPTIMIZED_CODE_LIST, head, UPDATE_WRITE_BARRIER, kReleaseStore); set(OPTIMIZED_CODE_LIST, head, UPDATE_WRITE_BARRIER, kReleaseStore);
} }
......
...@@ -740,6 +740,11 @@ class NativeContext : public Context { ...@@ -740,6 +740,11 @@ class NativeContext : public Context {
return Context::global_object(); return Context::global_object();
} }
inline Map TypedArrayElementsKindToCtorMap(ElementsKind element_kind) const;
inline Map TypedArrayElementsKindToRabGsabCtorMap(
ElementsKind element_kind) const;
// Dispatched behavior. // Dispatched behavior.
DECL_PRINTER(NativeContext) DECL_PRINTER(NativeContext)
DECL_VERIFIER(NativeContext) DECL_VERIFIER(NativeContext)
......
...@@ -230,6 +230,23 @@ bool JSTypedArray::IsDetachedOrOutOfBounds() const { ...@@ -230,6 +230,23 @@ bool JSTypedArray::IsDetachedOrOutOfBounds() const {
return out_of_bounds; 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 { size_t JSTypedArray::length() const {
DCHECK(!is_length_tracking()); DCHECK(!is_length_tracking());
DCHECK(!is_backed_by_rab()); DCHECK(!is_backed_by_rab());
......
...@@ -310,6 +310,10 @@ class JSTypedArray ...@@ -310,6 +310,10 @@ class JSTypedArray
inline bool IsOutOfBounds() const; inline bool IsOutOfBounds() const;
inline bool IsDetachedOrOutOfBounds() 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, static size_t LengthTrackingGsabBackedTypedArrayLength(Isolate* isolate,
Address raw_array); Address raw_array);
...@@ -372,6 +376,7 @@ class JSTypedArray ...@@ -372,6 +376,7 @@ class JSTypedArray
template <typename IsolateT> template <typename IsolateT>
friend class Deserializer; friend class Deserializer;
friend class Factory; friend class Factory;
friend class WebSnapshotDeserializer;
DECL_PRIMITIVE_SETTER(length, size_t) DECL_PRIMITIVE_SETTER(length, size_t)
// Reads the "length" field, doesn't assert the TypedArray is not RAB / GSAB // Reads the "length" field, doesn't assert the TypedArray is not RAB / GSAB
......
This diff is collapsed.
...@@ -85,6 +85,11 @@ class WebSnapshotSerializerDeserializer { ...@@ -85,6 +85,11 @@ class WebSnapshotSerializerDeserializer {
kBigUint64Array, kBigUint64Array,
}; };
static inline ExternalArrayType TypedArrayTypeToExternalArrayType(
TypedArrayType type);
static inline TypedArrayType ExternalArrayTypeToTypedArrayType(
ExternalArrayType type);
static constexpr uint8_t kMagicNumber[4] = {'+', '+', '+', ';'}; static constexpr uint8_t kMagicNumber[4] = {'+', '+', '+', ';'};
enum ContextType : uint8_t { FUNCTION, BLOCK }; enum ContextType : uint8_t { FUNCTION, BLOCK };
...@@ -100,6 +105,11 @@ class WebSnapshotSerializerDeserializer { ...@@ -100,6 +105,11 @@ class WebSnapshotSerializerDeserializer {
uint32_t AttributesToFlags(PropertyDetails details); uint32_t AttributesToFlags(PropertyDetails details);
PropertyAttributes FlagsToAttributes(uint32_t flags); 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.) // The maximum count of items for each value type (strings, objects etc.)
static constexpr uint32_t kMaxItemCount = static constexpr uint32_t kMaxItemCount =
static_cast<uint32_t>(FixedArray::kMaxLength - 1); static_cast<uint32_t>(FixedArray::kMaxLength - 1);
...@@ -123,6 +133,22 @@ class WebSnapshotSerializerDeserializer { ...@@ -123,6 +133,22 @@ class WebSnapshotSerializerDeserializer {
Isolate* isolate_; Isolate* isolate_;
const char* error_message_ = nullptr; 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: private:
WebSnapshotSerializerDeserializer(const WebSnapshotSerializerDeserializer&) = WebSnapshotSerializerDeserializer(const WebSnapshotSerializerDeserializer&) =
delete; 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