[rab/gsab] ResizableArrayBuffer / GrowableSharedArrayBuffer part 1

Detailed list of changes:
https://docs.google.com/document/d/15i4-SZDzFDW7FfclIYuZEhFn-q-KpobCBy23x9zZZLc/edit?usp=sharing

Bug: v8:11111
Change-Id: I931003bd4552cf91d57de95af04a427a9e6d6ac9
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2814259
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: Shu-yu Guo <syg@chromium.org>
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Commit-Queue: Marja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/master@{#74459}
This commit is contained in:
Marja Hölttä 2021-05-06 12:43:37 +02:00 committed by V8 LUCI CQ
parent 4cd2b536e1
commit 3160edf011
58 changed files with 3506 additions and 578 deletions

View File

@ -9,21 +9,25 @@ transitioning javascript builtin ArrayBufferPrototypeGetByteLength(
js-implicit context: NativeContext, receiver: JSAny)(): Number {
// 1. Let O be the this value.
// 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).
const functionName = 'get ArrayBuffer.prototype.byteLength';
const o = Cast<JSArrayBuffer>(receiver) otherwise
ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver,
'get ArrayBuffer.prototype.byteLength', receiver);
MessageTemplate::kIncompatibleMethodReceiver, functionName, receiver);
// 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.
if (IsSharedArrayBuffer(o)) {
ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver,
'get ArrayBuffer.prototype.byteLength', receiver);
MessageTemplate::kIncompatibleMethodReceiver, functionName, receiver);
}
// 4. If IsDetachedBuffer(O) is true, throw a TypeError exception.
// 4. If IsResizableArrayBuffer(O) is true, throw a TypeError exception.
if (IsResizableArrayBuffer(o)) {
ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver, functionName, receiver);
}
// 5. If IsDetachedBuffer(O) is true, throw a TypeError exception.
// TODO(v8:4895): We don't actually throw here.
// 5. Let length be O.[[ArrayBufferByteLength]].
// 6. Let length be O.[[ArrayBufferByteLength]].
const length = o.byte_length;
// 6. Return length.
// 7. Return length.
return Convert<Number>(length);
}
@ -32,15 +36,43 @@ transitioning javascript builtin SharedArrayBufferPrototypeGetByteLength(
js-implicit context: NativeContext, receiver: JSAny)(): Number {
// 1. Let O be the this value.
// 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).
const functionName = 'get SharedArrayBuffer.prototype.byteLength';
const o = Cast<JSArrayBuffer>(receiver) otherwise
ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver,
'get SharedArrayBuffer.prototype.byteLength', receiver);
// 3. If IsSharedArrayBuffer(O) is false, throw a TypeError exception.
MessageTemplate::kIncompatibleMethodReceiver, functionName, receiver);
// 3. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).
if (!IsSharedArrayBuffer(o)) {
ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver,
'get SharedArrayBuffer.prototype.byteLength', receiver);
MessageTemplate::kIncompatibleMethodReceiver, functionName, receiver);
}
// 4. If IsResizableArrayBuffer(O) is true, throw a TypeError exception.
if (IsResizableArrayBuffer(o)) {
ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver, functionName, receiver);
}
// 5. Let length be O.[[ArrayBufferByteLength]].
const length = o.byte_length;
// 6. Return length.
return Convert<Number>(length);
}
// #sec-get-resizablearraybuffer.prototype.bytelength
transitioning javascript builtin ResizableArrayBufferPrototypeGetByteLength(
js-implicit context: NativeContext, receiver: JSAny)(): Number {
// 1. Let O be the this value.
// 2. Perform ? RequireInternalSlot(O, [[ArrayBufferMaxByteLength]]).
const functionName = 'get ResizableArrayBuffer.prototype.byteLength';
const o = Cast<JSArrayBuffer>(receiver) otherwise
ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver, functionName, receiver);
if (!IsResizableArrayBuffer(o)) {
ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver, functionName, receiver);
}
// 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.
if (IsSharedArrayBuffer(o)) {
ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver, functionName, receiver);
}
// 4. Let length be O.[[ArrayBufferByteLength]].
const length = o.byte_length;
@ -48,6 +80,55 @@ transitioning javascript builtin SharedArrayBufferPrototypeGetByteLength(
return Convert<Number>(length);
}
// #sec-get-resizablearraybuffer.prototype.maxbytelength
transitioning javascript builtin ResizableArrayBufferPrototypeGetMaxByteLength(
js-implicit context: NativeContext, receiver: JSAny)(): Number {
// 1. Let O be the this value.
// 2. Perform ? RequireInternalSlot(O, [[ArrayBufferMaxByteLength]]).
const functionName = 'get ResizableArrayBuffer.prototype.maxByteLength';
const o = Cast<JSArrayBuffer>(receiver) otherwise
ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver, functionName, receiver);
if (!IsResizableArrayBuffer(o)) {
ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver, functionName, receiver);
}
// 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.
if (IsSharedArrayBuffer(o)) {
ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver, functionName, receiver);
}
// 4. Let length be O.[[ArrayBufferMaxByteLength]].
const length = o.max_byte_length;
// 5. Return length.
return Convert<Number>(length);
}
// #sec-get-growablesharedarraybuffer.prototype.maxbytelength
transitioning javascript builtin
GrowableSharedArrayBufferPrototypeGetMaxByteLength(
js-implicit context: NativeContext, receiver: JSAny)(): Number {
// 1. Let O be the this value.
// 2. Perform ? RequireInternalSlot(O, [[ArrayBufferMaxByteLength]]).
const functionName = 'get GrowableSharedArrayBuffer.prototype.maxByteLength';
const o = Cast<JSArrayBuffer>(receiver) otherwise
ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver, functionName, receiver);
if (!IsResizableArrayBuffer(o)) {
ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver, functionName, receiver);
}
// 3. If IsSharedArrayBuffer(O) is false, throw a TypeError exception.
if (!IsSharedArrayBuffer(o)) {
ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver, functionName, receiver);
}
// 4. Let length be O.[[ArrayBufferMaxByteLength]].
const length = o.max_byte_length;
// 5. Return length.
return Convert<Number>(length);
}
// #sec-arraybuffer.isview
transitioning javascript builtin ArrayBufferIsView(arg: JSAny): Boolean {
// 1. If Type(arg) is not Object, return false.

View File

@ -1696,7 +1696,7 @@ extern transitioning runtime SetOwnPropertyIgnoreAttributes(
namespace runtime {
extern runtime
GetDerivedMap(Context, JSFunction, JSReceiver): Map;
GetDerivedMap(Context, JSFunction, JSReceiver, JSAny): Map;
}
extern macro IsDeprecatedMap(Map): bool;

View File

@ -889,9 +889,11 @@ uint32_t EstimateElementCount(Isolate* isolate, Handle<JSArray> array) {
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) case TYPE##_ELEMENTS:
TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
RAB_GSAB_TYPED_ARRAYS(TYPED_ARRAY_CASE)
// External arrays are always dense.
return length;
#undef TYPED_ARRAY_CASE
case NO_ELEMENTS:
return 0;
case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
@ -965,9 +967,7 @@ void CollectElementIndices(Isolate* isolate, Handle<JSObject> object,
}
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) case TYPE##_ELEMENTS:
TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
{
TYPED_ARRAYS(TYPED_ARRAY_CASE) {
size_t length = Handle<JSTypedArray>::cast(object)->length();
if (range <= length) {
length = range;
@ -983,6 +983,11 @@ void CollectElementIndices(Isolate* isolate, Handle<JSObject> object,
if (length == range) return; // All indices accounted for already.
break;
}
RAB_GSAB_TYPED_ARRAYS(TYPED_ARRAY_CASE)
// TODO(v8:11111): Support RAB / GSAB.
UNREACHABLE();
#undef TYPED_ARRAY_CASE
case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
case SLOW_SLOPPY_ARGUMENTS_ELEMENTS: {
DisallowGarbageCollection no_gc;
@ -1208,8 +1213,11 @@ bool IterateElements(Isolate* isolate, Handle<JSReceiver> receiver,
break;
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) case TYPE##_ELEMENTS:
TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
return IterateElementsSlow(isolate, receiver, length, visitor);
RAB_GSAB_TYPED_ARRAYS(TYPED_ARRAY_CASE)
// TODO(v8:11111): Support RAB / GSAB.
UNREACHABLE();
#undef TYPED_ARRAY_CASE
case FAST_STRING_WRAPPER_ELEMENTS:
case SLOW_STRING_WRAPPER_ELEMENTS:
// |array| is guaranteed to be an array or typed array.

View File

@ -23,17 +23,43 @@ namespace internal {
name)); \
}
#define CHECK_RESIZABLE(expected, name, method) \
if (name->is_resizable() != expected) { \
THROW_NEW_ERROR_RETURN_FAILURE( \
isolate, \
NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, \
isolate->factory()->NewStringFromAsciiChecked(method), \
name)); \
}
// -----------------------------------------------------------------------------
// ES#sec-arraybuffer-objects
namespace {
bool RoundUpToPageSize(size_t byte_length, size_t page_size,
size_t max_allowed_byte_length, size_t* pages) {
size_t bytes_wanted = RoundUp(byte_length, page_size);
if (bytes_wanted > max_allowed_byte_length) {
return false;
}
*pages = bytes_wanted / page_size;
return true;
}
Object ConstructBuffer(Isolate* isolate, Handle<JSFunction> target,
Handle<JSReceiver> new_target, Handle<Object> length,
InitializedFlag initialized) {
SharedFlag shared = (*target != target->native_context().array_buffer_fun())
Handle<Object> max_length, InitializedFlag initialized) {
SharedFlag shared =
(*target != target->native_context().array_buffer_fun() &&
*target != target->native_context().resizable_array_buffer_fun())
? SharedFlag::kShared
: SharedFlag::kNotShared;
ResizableFlag resizable =
(*target == target->native_context().resizable_array_buffer_fun() ||
*target == target->native_context().growable_shared_array_buffer_fun())
? ResizableFlag::kResizable
: ResizableFlag::kNotResizable;
Handle<JSObject> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result,
@ -42,9 +68,10 @@ Object ConstructBuffer(Isolate* isolate, Handle<JSFunction> target,
// Ensure that all fields are initialized because BackingStore::Allocate is
// allowed to GC. Note that we cannot move the allocation of the ArrayBuffer
// after BackingStore::Allocate because of the spec.
array_buffer->Setup(shared, nullptr);
array_buffer->Setup(shared, resizable, nullptr);
size_t byte_length;
size_t max_byte_length = 0;
if (!TryNumberToSize(*length, &byte_length) ||
byte_length > JSArrayBuffer::kMaxByteLength) {
// ToNumber failed.
@ -52,8 +79,46 @@ Object ConstructBuffer(Isolate* isolate, Handle<JSFunction> target,
isolate, NewRangeError(MessageTemplate::kInvalidArrayBufferLength));
}
auto backing_store =
std::unique_ptr<BackingStore> backing_store;
if (resizable == ResizableFlag::kNotResizable) {
backing_store =
BackingStore::Allocate(isolate, byte_length, shared, initialized);
} else {
Handle<Object> number_max_length;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, number_max_length,
Object::ToInteger(isolate, max_length));
if (!TryNumberToSize(*number_max_length, &max_byte_length)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewRangeError(MessageTemplate::kInvalidArrayBufferMaxLength));
}
if (byte_length > max_byte_length) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewRangeError(MessageTemplate::kInvalidArrayBufferMaxLength));
}
size_t page_size = AllocatePageSize();
size_t initial_pages;
if (!RoundUpToPageSize(byte_length, page_size,
JSArrayBuffer::kMaxByteLength, &initial_pages)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kInvalidArrayBufferLength));
}
size_t max_pages;
if (!RoundUpToPageSize(max_byte_length, page_size,
JSArrayBuffer::kMaxByteLength, &max_pages)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewRangeError(MessageTemplate::kInvalidArrayBufferMaxLength));
}
constexpr bool kIsWasmMemory = false;
backing_store = BackingStore::TryAllocateAndPartiallyCommitMemory(
isolate, byte_length, page_size, initial_pages, max_pages,
kIsWasmMemory, shared);
}
if (!backing_store) {
// Allocation of backing store failed.
THROW_NEW_ERROR_RETURN_FAILURE(
@ -61,6 +126,7 @@ Object ConstructBuffer(Isolate* isolate, Handle<JSFunction> target,
}
array_buffer->Attach(std::move(backing_store));
array_buffer->set_max_byte_length(max_byte_length);
return *array_buffer;
}
@ -71,7 +137,10 @@ BUILTIN(ArrayBufferConstructor) {
HandleScope scope(isolate);
Handle<JSFunction> target = args.target();
DCHECK(*target == target->native_context().array_buffer_fun() ||
*target == target->native_context().shared_array_buffer_fun());
*target == target->native_context().shared_array_buffer_fun() ||
*target == target->native_context().resizable_array_buffer_fun() ||
*target ==
target->native_context().growable_shared_array_buffer_fun());
if (args.new_target()->IsUndefined(isolate)) { // [[Call]]
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kConstructorNotFunction,
@ -89,7 +158,8 @@ BUILTIN(ArrayBufferConstructor) {
isolate, NewRangeError(MessageTemplate::kInvalidArrayBufferLength));
}
return ConstructBuffer(isolate, target, new_target, number_length,
Handle<Object> max_length = args.atOrUndefined(isolate, 2);
return ConstructBuffer(isolate, target, new_target, number_length, max_length,
InitializedFlag::kZeroInitialized);
}
@ -101,7 +171,7 @@ BUILTIN(ArrayBufferConstructor_DoNotInitialize) {
Handle<JSFunction> target(isolate->native_context()->array_buffer_fun(),
isolate);
Handle<Object> length = args.atOrUndefined(isolate, 1);
return ConstructBuffer(isolate, target, target, length,
return ConstructBuffer(isolate, target, target, length, Handle<Object>(),
InitializedFlag::kUninitialized);
}
@ -119,6 +189,8 @@ static Object SliceHelper(BuiltinArguments args, Isolate* isolate,
// * [SAB] If IsSharedArrayBuffer(O) is false, throw a TypeError exception.
CHECK_SHARED(is_shared, array_buffer, kMethodName);
CHECK_RESIZABLE(false, array_buffer, kMethodName);
// * [AB] If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
if (!is_shared && array_buffer->was_detached()) {
THROW_NEW_ERROR_RETURN_FAILURE(
@ -280,5 +352,140 @@ BUILTIN(ArrayBufferPrototypeSlice) {
return SliceHelper(args, isolate, kMethodName, false);
}
static Object ResizeHelper(BuiltinArguments args, Isolate* isolate,
const char* kMethodName, bool is_shared) {
HandleScope scope(isolate);
// 1 Let O be the this value.
// 2. Perform ? RequireInternalSlot(O, [[ArrayBufferMaxByteLength]]).
CHECK_RECEIVER(JSArrayBuffer, array_buffer, kMethodName);
CHECK_RESIZABLE(true, array_buffer, kMethodName);
// [RAB] 3. If IsSharedArrayBuffer(O) is true, throw a *TypeError* exception
// [GSAB] 3. If IsSharedArrayBuffer(O) is false, throw a *TypeError* exception
CHECK_SHARED(is_shared, array_buffer, kMethodName);
// Let newByteLength to ? ToIntegerOrInfinity(newLength).
Handle<Object> new_length = args.at(1);
Handle<Object> number_new_byte_length;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, number_new_byte_length,
Object::ToInteger(isolate, new_length));
// [RAB] If IsDetachedBuffer(O) is true, throw a TypeError exception.
if (!is_shared && array_buffer->was_detached()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kDetachedOperation,
isolate->factory()->NewStringFromAsciiChecked(
kMethodName)));
}
// [RAB] If newByteLength < 0 or newByteLength >
// O.[[ArrayBufferMaxByteLength]], throw a RangeError exception.
// [GSAB] If newByteLength < currentByteLength or newByteLength >
// O.[[ArrayBufferMaxByteLength]], throw a RangeError exception.
size_t new_byte_length;
if (!TryNumberToSize(*number_new_byte_length, &new_byte_length)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kInvalidArrayBufferResizeLength,
isolate->factory()->NewStringFromAsciiChecked(
kMethodName)));
}
if (is_shared && new_byte_length < array_buffer->byte_length()) {
// GrowableSharedArrayBuffer is only allowed to grow.
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kInvalidArrayBufferResizeLength,
isolate->factory()->NewStringFromAsciiChecked(
kMethodName)));
}
if (new_byte_length > array_buffer->max_byte_length()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kInvalidArrayBufferResizeLength,
isolate->factory()->NewStringFromAsciiChecked(
kMethodName)));
}
size_t page_size = AllocatePageSize();
size_t new_committed_pages;
bool round_return_value =
RoundUpToPageSize(new_byte_length, page_size,
JSArrayBuffer::kMaxByteLength, &new_committed_pages);
CHECK(round_return_value);
// [RAB] Let hostHandled be ? HostResizeArrayBuffer(O, newByteLength).
// [GSAB] Let hostHandled be ? HostGrowArrayBuffer(O, newByteLength).
// If hostHandled is handled, return undefined.
// TODO(v8:11111): Wasm integration.
// [RAB] Let oldBlock be O.[[ArrayBufferData]].
// [RAB] Let newBlock be ? CreateByteDataBlock(newByteLength).
// [RAB] Let copyLength be min(newByteLength, O.[[ArrayBufferByteLength]]).
// [RAB] Perform CopyDataBlockBytes(newBlock, 0, oldBlock, 0, copyLength).
// [RAB] NOTE: Neither creation of the new Data Block nor copying from the old
// Data Block are observable. Implementations reserve the right to implement
// this method as in-place growth or shrinkage.
if (!array_buffer->GetBackingStore()->ResizeInPlace(
isolate, new_byte_length, new_committed_pages * page_size,
!is_shared)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kInvalidArrayBufferLength));
}
// [RAB] Set O.[[ArrayBufferByteLength]] to newLength.
if (!is_shared) {
array_buffer->set_byte_length(new_byte_length);
} else {
// Invariant: byte_length for a GSAB is 0 (it needs to be read from the
// BackingStore).
CHECK_EQ(0, array_buffer->byte_length());
}
return ReadOnlyRoots(isolate).undefined_value();
}
// ES #sec-get-growablesharedarraybuffer.prototype.bytelength
// get GrowableSharedArrayBuffer.prototype.byteLength
BUILTIN(GrowableSharedArrayBufferPrototypeGetByteLength) {
const char* const kMethodName =
"get GrowableSharedArrayBuffer.prototype.byteLength";
HandleScope scope(isolate);
// 1. Let O be the this value.
// 2. Perform ? RequireInternalSlot(O, [[ArrayBufferMaxLength]]).
CHECK_RECEIVER(JSArrayBuffer, array_buffer, kMethodName);
CHECK_RESIZABLE(true, array_buffer, kMethodName);
// 3. If IsSharedArrayBuffer(O) is false, throw a TypeError exception.
CHECK_SHARED(true, array_buffer, kMethodName);
// 4. Let length be ArrayBufferByteLength(O, SeqCst).
// Invariant: byte_length for GSAB is 0 (it needs to be read from the
// BackingStore).
DCHECK_EQ(0, array_buffer->byte_length());
size_t byte_length =
array_buffer->GetBackingStore()->byte_length(std::memory_order_seq_cst);
// 5. Return length.
return *isolate->factory()->NewNumberFromSize(byte_length);
}
// ES #sec-resizablearraybuffer.prototype.resize
// ResizableArrayBuffer.prototype.resize(new_size))
BUILTIN(ResizableArrayBufferPrototypeResize) {
const char* const kMethodName = "ResizableArrayBuffer.prototype.resize";
constexpr bool kIsShared = false;
return ResizeHelper(args, isolate, kMethodName, kIsShared);
}
// ES #sec-growablesharedarraybuffer.prototype.grow
// GrowableSharedArrayBuffer.prototype.grow(new_size))
BUILTIN(GrowableSharedArrayBufferPrototypeGrow) {
const char* const kMethodName = "GrowableSharedArrayBuffer.prototype.grow";
constexpr bool kIsShared = true;
return ResizeHelper(args, isolate, kMethodName, kIsShared);
}
} // namespace internal
} // namespace v8

View File

@ -768,6 +768,11 @@ namespace internal {
ASM(RegExpInterpreterTrampoline, CCall) \
ASM(RegExpExperimentalTrampoline, CCall) \
\
/* ResizableArrayBuffer & GrowableSharedArrayBuffer */ \
CPP(ResizableArrayBufferPrototypeResize) \
CPP(GrowableSharedArrayBufferPrototypeGrow) \
CPP(GrowableSharedArrayBufferPrototypeGetByteLength) \
\
/* Set */ \
TFJ(SetConstructor, kDontAdaptArgumentsSentinel) \
TFJ(SetPrototypeHas, 1, kReceiver, kKey) \

View File

@ -204,7 +204,18 @@ TF_BUILTIN(ElementsTransitionAndStore_NoTransitionHandleCOW,
V(FLOAT64_ELEMENTS) \
V(UINT8_CLAMPED_ELEMENTS) \
V(BIGUINT64_ELEMENTS) \
V(BIGINT64_ELEMENTS)
V(BIGINT64_ELEMENTS) \
V(RAB_GSAB_UINT8_ELEMENTS) \
V(RAB_GSAB_INT8_ELEMENTS) \
V(RAB_GSAB_UINT16_ELEMENTS) \
V(RAB_GSAB_INT16_ELEMENTS) \
V(RAB_GSAB_UINT32_ELEMENTS) \
V(RAB_GSAB_INT32_ELEMENTS) \
V(RAB_GSAB_FLOAT32_ELEMENTS) \
V(RAB_GSAB_FLOAT64_ELEMENTS) \
V(RAB_GSAB_UINT8_CLAMPED_ELEMENTS) \
V(RAB_GSAB_BIGUINT64_ELEMENTS) \
V(RAB_GSAB_BIGINT64_ELEMENTS)
void HandlerBuiltinsAssembler::DispatchByElementsKind(
TNode<Int32T> elements_kind, const ElementsKindSwitchCase& case_function,

View File

@ -123,13 +123,26 @@ TF_BUILTIN(TypedArrayPrototypeByteLength, TypedArrayBuiltinsAssembler) {
// Check if the {receiver} is actually a JSTypedArray.
ThrowIfNotInstanceType(context, receiver, JS_TYPED_ARRAY_TYPE, kMethodName);
// Default to zero if the {receiver}s buffer was detached.
TNode<JSTypedArray> receiver_array = CAST(receiver);
TNode<JSArrayBuffer> receiver_buffer =
LoadJSArrayBufferViewBuffer(CAST(receiver));
LoadJSArrayBufferViewBuffer(receiver_array);
Label variable_length(this), normal(this);
Branch(IsVariableLengthTypedArray(receiver_array), &variable_length, &normal);
BIND(&variable_length);
{
Return(ChangeUintPtrToTagged(LoadVariableLengthJSTypedArrayByteLength(
context, receiver_array, receiver_buffer)));
}
BIND(&normal);
{
// Default to zero if the {receiver}s buffer was detached.
TNode<UintPtrT> byte_length = Select<UintPtrT>(
IsDetachedBuffer(receiver_buffer), [=] { return UintPtrConstant(0); },
[=] { return LoadJSArrayBufferViewByteLength(CAST(receiver)); });
[=] { return LoadJSArrayBufferViewByteLength(receiver_array); });
Return(ChangeUintPtrToTagged(byte_length));
}
}
// ES6 #sec-get-%typedarray%.prototype.byteoffset
@ -159,13 +172,29 @@ TF_BUILTIN(TypedArrayPrototypeLength, TypedArrayBuiltinsAssembler) {
// Check if the {receiver} is actually a JSTypedArray.
ThrowIfNotInstanceType(context, receiver, JS_TYPED_ARRAY_TYPE, kMethodName);
// Default to zero if the {receiver}s buffer was detached.
TNode<JSTypedArray> receiver_array = CAST(receiver);
TNode<JSArrayBuffer> receiver_buffer =
LoadJSArrayBufferViewBuffer(CAST(receiver));
LoadJSArrayBufferViewBuffer(receiver_array);
Label variable_length(this), normal(this);
Branch(IsVariableLengthTypedArray(receiver_array), &variable_length, &normal);
BIND(&variable_length);
{
Label miss(this);
Return(ChangeUintPtrToTagged(LoadVariableLengthJSTypedArrayLength(
receiver_array, receiver_buffer, &miss)));
BIND(&miss);
Return(ChangeUintPtrToTagged(UintPtrConstant(0)));
}
BIND(&normal);
{
// Default to zero if the {receiver}s buffer was detached.
TNode<UintPtrT> length = Select<UintPtrT>(
IsDetachedBuffer(receiver_buffer), [=] { return UintPtrConstant(0); },
[=] { return LoadJSTypedArrayLength(CAST(receiver)); });
[=] { return LoadJSTypedArrayLength(receiver_array); });
Return(ChangeUintPtrToTagged(length));
}
}
TNode<BoolT> TypedArrayBuiltinsAssembler::IsUint8ElementsKind(
@ -322,17 +351,18 @@ void TypedArrayBuiltinsAssembler::DispatchTypedArrayByElementsKind(
int32_t elements_kinds[] = {
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) TYPE##_ELEMENTS,
TYPED_ARRAYS(TYPED_ARRAY_CASE)
TYPED_ARRAYS(TYPED_ARRAY_CASE) RAB_GSAB_TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
};
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) Label if_##type##array(this);
TYPED_ARRAYS(TYPED_ARRAY_CASE)
RAB_GSAB_TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
Label* elements_kind_labels[] = {
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) &if_##type##array,
TYPED_ARRAYS(TYPED_ARRAY_CASE)
TYPED_ARRAYS(TYPED_ARRAY_CASE) RAB_GSAB_TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
};
STATIC_ASSERT(arraysize(elements_kinds) == arraysize(elements_kind_labels));
@ -350,6 +380,15 @@ void TypedArrayBuiltinsAssembler::DispatchTypedArrayByElementsKind(
TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
BIND(&if_##type##array); \
{ \
case_function(TYPE##_ELEMENTS, sizeof(ctype), 0); \
Goto(&next); \
}
RAB_GSAB_TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
BIND(&if_unknown_type);
Unreachable();

View File

@ -19,11 +19,17 @@ extern macro TypedArrayBuiltinsAssembler::SetupTypedArrayEmbedderFields(
extern runtime ThrowInvalidTypedArrayAlignment(implicit context: Context)(
Map, String): never;
extern runtime GrowableSharedArrayBufferByteLength(implicit context: Context)(
Object): JSAny;
transitioning macro AllocateTypedArray(implicit context: Context)(
isOnHeap: constexpr bool, map: Map, buffer: JSArrayBuffer,
byteOffset: uintptr, byteLength: uintptr, length: uintptr): JSTypedArray {
byteOffset: uintptr, byteLength: uintptr, length: uintptr,
isLengthTracking: bool): JSTypedArray {
let elements: ByteArray;
if constexpr (isOnHeap) {
assert(!IsResizableArrayBuffer(buffer));
assert(!isLengthTracking);
elements = AllocateByteArray(byteLength);
} else {
elements = kEmptyByteArray;
@ -53,6 +59,9 @@ transitioning macro AllocateTypedArray(implicit context: Context)(
typedArray.byte_offset = byteOffset;
typedArray.byte_length = byteLength;
typedArray.length = length;
typedArray.bit_field.is_length_tracking = isLengthTracking;
typedArray.bit_field.is_backed_by_rab =
IsResizableArrayBuffer(buffer) && !IsSharedArrayBuffer(buffer);
typed_array::AllocateJSTypedArrayExternalPointerEntry(typedArray);
if constexpr (isOnHeap) {
typed_array::SetJSTypedArrayOnHeapDataPtr(typedArray, elements, byteOffset);
@ -88,8 +97,10 @@ transitioning macro TypedArrayInitialize(implicit context: Context)(
const buffer = AllocateEmptyOnHeapBuffer(byteLength);
const isOnHeap: constexpr bool = true;
const isLengthTracking: constexpr bool = false;
const typedArray = AllocateTypedArray(
isOnHeap, map, buffer, byteOffset, byteLength, length);
isOnHeap, map, buffer, byteOffset, byteLength, length,
isLengthTracking);
if constexpr (initialize) {
const backingStore = typedArray.data_ptr;
@ -107,8 +118,10 @@ transitioning macro TypedArrayInitialize(implicit context: Context)(
} label AttachOffHeapBuffer(bufferObj: Object) {
const buffer = Cast<JSArrayBuffer>(bufferObj) otherwise unreachable;
const isOnHeap: constexpr bool = false;
const isLengthTracking: constexpr bool = false;
return AllocateTypedArray(
isOnHeap, map, buffer, byteOffset, byteLength, length);
isOnHeap, map, buffer, byteOffset, byteLength, length,
isLengthTracking);
}
}
@ -204,8 +217,26 @@ transitioning macro ConstructByTypedArray(implicit context: Context)(
// 22.2.4.5 TypedArray ( buffer, byteOffset, length )
// ES #sec-typedarray-buffer-byteoffset-length
transitioning macro ConstructByArrayBuffer(implicit context: Context)(
map: Map, buffer: JSArrayBuffer, byteOffset: JSAny, length: JSAny,
elementsInfo: typed_array::TypedArrayElementsInfo): JSTypedArray {
target: JSFunction, newTarget: JSReceiver, buffer: JSArrayBuffer,
byteOffset: JSAny, length: JSAny): JSTypedArray {
let map: Map;
const isLengthTracking: bool =
IsResizableArrayBuffer(buffer) && (length == Undefined);
// Pick the RAB / GSAB map (containing the corresponding RAB / GSAB
// ElementsKind). GSAB-backed non-length-tracking TypedArrays behave just like
// normal TypedArrays, so exclude them.
const rabGsab: bool = IsResizableArrayBuffer(buffer) &&
(!IsSharedArrayBuffer(buffer) || isLengthTracking);
if (rabGsab) {
map = GetDerivedRabGsabMap(target, newTarget);
} else {
map = GetDerivedMap(target, newTarget);
}
// 5. Let elementSize be the Number value of the Element Size value in Table
// 56 for constructorName.
const elementsInfo = GetTypedArrayElementsInfo(map);
try {
// 6. Let offset be ? ToIndex(byteOffset).
const offset: uintptr = ToIndex(byteOffset) otherwise IfInvalidOffset;
@ -226,7 +257,13 @@ transitioning macro ConstructByArrayBuffer(implicit context: Context)(
}
// 10. Let bufferByteLength be buffer.[[ArrayBufferByteLength]].
const bufferByteLength: uintptr = buffer.byte_length;
let bufferByteLength: uintptr;
if (IsResizableArrayBuffer(buffer) && IsSharedArrayBuffer(buffer)) {
bufferByteLength = ToIndex(GrowableSharedArrayBufferByteLength(buffer))
otherwise unreachable;
} else {
bufferByteLength = buffer.byte_length;
}
// 11. If length is either not present or undefined, then
if (length == Undefined) {
@ -261,7 +298,8 @@ transitioning macro ConstructByArrayBuffer(implicit context: Context)(
const isOnHeap: constexpr bool = false;
return AllocateTypedArray(
isOnHeap, map, buffer, offset, newByteLength, newLength);
isOnHeap, map, buffer, offset, newByteLength, newLength,
isLengthTracking);
} label IfInvalidAlignment(problemString: String) deferred {
ThrowInvalidTypedArrayAlignment(map, problemString);
} label IfInvalidLength deferred {
@ -286,6 +324,8 @@ transitioning macro TypedArrayCreateByLength(implicit context: Context)(
// ValidateTypedArray currently returns the array, not the ViewBuffer.
const newTypedArray: JSTypedArray =
ValidateTypedArray(context, newTypedArrayObj, methodName);
newTypedArray.bit_field.is_length_tracking = false;
newTypedArray.bit_field.is_backed_by_rab = false;
if (IsDetachedBuffer(newTypedArray.buffer)) deferred {
ThrowTypeError(MessageTemplate::kDetachedOperation, methodName);
@ -336,21 +376,16 @@ transitioning builtin CreateTypedArray(
assert(IsConstructor(target));
// 4. Let O be ? AllocateTypedArray(constructorName, NewTarget,
// "%TypedArrayPrototype%").
const map = GetDerivedMap(target, newTarget);
// 5. Let elementSize be the Number value of the Element Size value in Table
// 56 for constructorName.
const elementsInfo = GetTypedArrayElementsInfo(map);
try {
typeswitch (arg1) {
case (length: Smi): {
goto IfConstructByLength(length);
}
case (buffer: JSArrayBuffer): {
return ConstructByArrayBuffer(map, buffer, arg2, arg3, elementsInfo);
return ConstructByArrayBuffer(target, newTarget, buffer, arg2, arg3);
}
case (typedArray: JSTypedArray): {
// TODO(v8:11111): Support RAB / GSAB.
ConstructByTypedArray(typedArray) otherwise IfConstructByArrayLike;
}
case (obj: JSReceiver): {
@ -363,9 +398,18 @@ transitioning builtin CreateTypedArray(
}
}
} label IfConstructByLength(length: JSAny) {
const map = GetDerivedMap(target, newTarget);
// 5. Let elementSize be the Number value of the Element Size value in Table
// 56 for constructorName.
const elementsInfo = GetTypedArrayElementsInfo(map);
return ConstructByLength(map, length, elementsInfo);
} label IfConstructByArrayLike(
arrayLike: JSReceiver, length: uintptr, bufferConstructor: JSReceiver) {
const map = GetDerivedMap(target, newTarget);
// 5. Let elementSize be the Number value of the Element Size value in Table
// 56 for constructorName.
const elementsInfo = GetTypedArrayElementsInfo(map);
return ConstructByArrayLike(
map, arrayLike, length, elementsInfo, bufferConstructor);
}

View File

@ -9603,6 +9603,7 @@ void CodeStubAssembler::TryLookupElement(
BIGUINT64_ELEMENTS,
BIGINT64_ELEMENTS,
};
// TODO(v8:11111): Support RAB / GSAB.
Label* labels[] = {
&if_isobjectorsmi, &if_isobjectorsmi, &if_isobjectorsmi,
&if_isobjectorsmi, &if_isobjectorsmi, &if_isobjectorsmi,
@ -10860,6 +10861,12 @@ void CodeStubAssembler::EmitElementStore(
TNode<Context> context, TVariable<Object>* maybe_converted_value) {
CSA_ASSERT(this, Word32BinaryNot(IsJSProxy(object)));
// TODO(v8:11111): Fast path for RAB / GSAB backed TypedArrays.
if (IsRabGsabTypedArrayElementsKind(elements_kind)) {
GotoIf(Int32TrueConstant(), bailout);
return;
}
TNode<FixedArrayBase> elements = LoadElements(object);
if (!(IsSmiOrObjectElementsKind(elements_kind) ||
IsSealedElementsKind(elements_kind) ||
@ -13654,6 +13661,149 @@ TNode<UintPtrT> CodeStubAssembler::LoadJSTypedArrayLength(
return LoadObjectField<UintPtrT>(typed_array, JSTypedArray::kLengthOffset);
}
// ES #sec-integerindexedobjectlength
TNode<UintPtrT> CodeStubAssembler::LoadVariableLengthJSTypedArrayLength(
TNode<JSTypedArray> array, TNode<JSArrayBuffer> buffer, Label* miss) {
Label is_gsab(this), is_rab(this), end(this);
TVARIABLE(UintPtrT, result);
Branch(IsSharedArrayBuffer(buffer), &is_gsab, &is_rab);
BIND(&is_gsab);
{
// Non-length-tracking GSAB-backed TypedArrays shouldn't end up here.
CSA_ASSERT(this, IsLengthTrackingTypedArray(array));
// Read the byte length from the BackingStore.
const TNode<ExternalReference> length_function = ExternalConstant(
ExternalReference::length_tracking_gsab_backed_typed_array_length());
TNode<ExternalReference> isolate_ptr =
ExternalConstant(ExternalReference::isolate_address(isolate()));
result = UncheckedCast<UintPtrT>(
CallCFunction(length_function, MachineType::UintPtr(),
std::make_pair(MachineType::Pointer(), isolate_ptr),
std::make_pair(MachineType::AnyTagged(), array)));
Goto(&end);
}
BIND(&is_rab);
{
GotoIf(IsDetachedBuffer(buffer), miss);
TNode<UintPtrT> buffer_byte_length = LoadJSArrayBufferByteLength(buffer);
TNode<UintPtrT> array_byte_offset = LoadJSArrayBufferViewByteOffset(array);
Label is_length_tracking(this), not_length_tracking(this);
Branch(IsLengthTrackingTypedArray(array), &is_length_tracking,
&not_length_tracking);
BIND(&is_length_tracking);
{
// The backing RAB might have been shrunk so that the start of the
// TypedArray is already out of bounds.
GotoIfNot(UintPtrLessThanOrEqual(array_byte_offset, buffer_byte_length),
miss);
// length = (buffer_byte_length - byte_offset) / element_size
// Conversion to signed is OK since buffer_byte_length <
// JSArrayBuffer::kMaxByteLength.
TNode<IntPtrT> element_size =
RabGsabElementsKindToElementByteSize(LoadElementsKind(array));
TNode<IntPtrT> length =
IntPtrDiv(Signed(UintPtrSub(buffer_byte_length, array_byte_offset)),
element_size);
result = Unsigned(length);
Goto(&end);
}
BIND(&not_length_tracking);
{
// Check if the backing RAB has shrunk so that the buffer is out of
// bounds.
TNode<UintPtrT> array_byte_length =
LoadJSArrayBufferViewByteLength(array);
GotoIfNot(UintPtrGreaterThanOrEqual(
buffer_byte_length,
UintPtrAdd(array_byte_offset, array_byte_length)),
miss);
result = LoadJSTypedArrayLength(array);
Goto(&end);
}
}
BIND(&end);
return result.value();
}
// ES #sec-integerindexedobjectbytelength
TNode<UintPtrT> CodeStubAssembler::LoadVariableLengthJSTypedArrayByteLength(
TNode<Context> context, TNode<JSTypedArray> array,
TNode<JSArrayBuffer> buffer) {
Label miss(this), end(this);
TVARIABLE(UintPtrT, result);
TNode<UintPtrT> length =
LoadVariableLengthJSTypedArrayLength(array, buffer, &miss);
TNode<IntPtrT> element_size =
RabGsabElementsKindToElementByteSize(LoadElementsKind(array));
// Conversion to signed is OK since length < JSArrayBuffer::kMaxByteLength.
TNode<IntPtrT> byte_length = IntPtrMul(Signed(length), element_size);
result = Unsigned(byte_length);
Goto(&end);
BIND(&miss);
{
result = UintPtrConstant(0);
Goto(&end);
}
BIND(&end);
return result.value();
}
TNode<IntPtrT> CodeStubAssembler::RabGsabElementsKindToElementByteSize(
TNode<Int32T> elements_kind) {
TVARIABLE(IntPtrT, result);
Label elements_8(this), elements_16(this), elements_32(this),
elements_64(this), not_found(this), end(this);
int32_t elements_kinds[] = {
RAB_GSAB_UINT8_ELEMENTS, RAB_GSAB_UINT8_CLAMPED_ELEMENTS,
RAB_GSAB_INT8_ELEMENTS, RAB_GSAB_UINT16_ELEMENTS,
RAB_GSAB_INT16_ELEMENTS, RAB_GSAB_UINT32_ELEMENTS,
RAB_GSAB_INT32_ELEMENTS, RAB_GSAB_FLOAT32_ELEMENTS,
RAB_GSAB_FLOAT64_ELEMENTS, RAB_GSAB_BIGINT64_ELEMENTS,
RAB_GSAB_BIGUINT64_ELEMENTS};
Label* elements_kind_labels[] = {&elements_8, &elements_8, &elements_8,
&elements_16, &elements_16, &elements_32,
&elements_32, &elements_32, &elements_64,
&elements_64, &elements_64};
const size_t kTypedElementsKindCount =
LAST_RAB_GSAB_FIXED_TYPED_ARRAY_ELEMENTS_KIND -
FIRST_RAB_GSAB_FIXED_TYPED_ARRAY_ELEMENTS_KIND + 1;
DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kinds));
DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kind_labels));
Switch(elements_kind, &not_found, elements_kinds, elements_kind_labels,
kTypedElementsKindCount);
BIND(&elements_8);
{
result = IntPtrConstant(1);
Goto(&end);
}
BIND(&elements_16);
{
result = IntPtrConstant(2);
Goto(&end);
}
BIND(&elements_32);
{
result = IntPtrConstant(4);
Goto(&end);
}
BIND(&elements_64);
{
result = IntPtrConstant(8);
Goto(&end);
}
BIND(&not_found);
{ Unreachable(); }
BIND(&end);
return result.value();
}
TNode<JSArrayBuffer> CodeStubAssembler::GetTypedArrayBuffer(
TNode<Context> context, TNode<JSTypedArray> array) {
Label call_runtime(this), done(this);

View File

@ -3494,6 +3494,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<JSArrayBufferView> array_buffer_view);
TNode<UintPtrT> LoadJSArrayBufferViewByteLength(
TNode<JSArrayBufferView> array_buffer_view);
TNode<UintPtrT> LoadJSArrayBufferViewByteOffset(
TNode<JSArrayBufferView> array_buffer_view);
void ThrowIfArrayBufferViewBufferIsDetached(
@ -3502,6 +3503,17 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
// JSTypedArray helpers
TNode<UintPtrT> LoadJSTypedArrayLength(TNode<JSTypedArray> typed_array);
// Helper for length tracking JSTypedArrays and JSTypedArrays backed by
// ResizableArrayBuffer.
TNode<UintPtrT> LoadVariableLengthJSTypedArrayLength(
TNode<JSTypedArray> array, TNode<JSArrayBuffer> buffer, Label* miss);
// Helper for length tracking JSTypedArrays and JSTypedArrays backed by
// ResizableArrayBuffer.
TNode<UintPtrT> LoadVariableLengthJSTypedArrayByteLength(
TNode<Context> context, TNode<JSTypedArray> array,
TNode<JSArrayBuffer> buffer);
TNode<IntPtrT> RabGsabElementsKindToElementByteSize(
TNode<Int32T> elementsKind);
TNode<RawPtrT> LoadJSTypedArrayDataPtr(TNode<JSTypedArray> typed_array);
TNode<JSArrayBuffer> GetTypedArrayBuffer(TNode<Context> context,
TNode<JSTypedArray> array);

View File

@ -819,6 +819,9 @@ ExternalReference ExternalReference::search_string_raw() {
FUNCTION_REFERENCE(jsarray_array_join_concat_to_sequential_string,
JSArray::ArrayJoinConcatToSequentialString)
FUNCTION_REFERENCE(length_tracking_gsab_backed_typed_array_length,
JSTypedArray::LengthTrackingGsabBackedTypedArrayLength)
ExternalReference ExternalReference::search_string_raw_one_one() {
return search_string_raw<const uint8_t, const uint8_t>();
}

View File

@ -169,6 +169,8 @@ class StatsCounter;
V(jsarray_array_join_concat_to_sequential_string, \
"jsarray_array_join_concat_to_sequential_string") \
V(jsreceiver_create_identity_hash, "jsreceiver_create_identity_hash") \
V(length_tracking_gsab_backed_typed_array_length, \
"LengthTrackingGsabBackedTypedArrayLength") \
V(libc_memchr_function, "libc_memchr") \
V(libc_memcpy_function, "libc_memcpy") \
V(libc_memmove_function, "libc_memmove") \

View File

@ -330,6 +330,8 @@ namespace internal {
"Expected letters optionally connected with underscores or hyphens for " \
"a location, got %") \
T(InvalidArrayBufferLength, "Invalid array buffer length") \
T(InvalidArrayBufferMaxLength, "Invalid array buffer max length") \
T(InvalidArrayBufferResizeLength, "%: Invalid length parameter") \
T(ArrayBufferAllocationFailed, "Array buffer allocation failed") \
T(Invalid, "Invalid % : %") \
T(InvalidArrayLength, "Invalid array length") \

View File

@ -1437,7 +1437,7 @@ void JSArrayBufferView::JSArrayBufferViewVerify(Isolate* isolate) {
void JSTypedArray::JSTypedArrayVerify(Isolate* isolate) {
TorqueGeneratedClassVerifiers::JSTypedArrayVerify(*this, isolate);
CHECK_LE(length(), JSTypedArray::kMaxLength);
CHECK_LE(GetLength(), JSTypedArray::kMaxLength);
}
void JSDataView::JSDataViewVerify(Isolate* isolate) {
@ -1805,6 +1805,7 @@ void JSObject::IncrementSpillStatistics(Isolate* isolate,
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) case TYPE##_ELEMENTS:
TYPED_ARRAYS(TYPED_ARRAY_CASE)
RAB_GSAB_TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
{
info->number_of_objects_with_fast_elements_++;

View File

@ -508,7 +508,7 @@ void JSObject::PrintElements(std::ostream& os) {
#define PRINT_ELEMENTS(Type, type, TYPE, elementType) \
case TYPE##_ELEMENTS: { \
size_t length = JSTypedArray::cast(*this).length(); \
size_t length = JSTypedArray::cast(*this).GetLength(); \
bool is_on_heap = JSTypedArray::cast(*this).is_on_heap(); \
const elementType* data_ptr = \
static_cast<const elementType*>(JSTypedArray::cast(*this).DataPtr()); \
@ -516,6 +516,7 @@ void JSObject::PrintElements(std::ostream& os) {
break; \
}
TYPED_ARRAYS(PRINT_ELEMENTS)
RAB_GSAB_TYPED_ARRAYS(PRINT_ELEMENTS)
#undef PRINT_ELEMENTS
case DICTIONARY_ELEMENTS:
@ -572,7 +573,7 @@ static void JSObjectPrintBody(std::ostream& os, JSObject obj,
os << "}\n";
if (print_elements) {
size_t length = obj.IsJSTypedArray() ? JSTypedArray::cast(obj).length()
size_t length = obj.IsJSTypedArray() ? JSTypedArray::cast(obj).GetLength()
: obj.elements().length();
if (length > 0) obj.PrintElements(os);
}
@ -1391,6 +1392,7 @@ void JSArrayBuffer::JSArrayBufferPrint(std::ostream& os) {
if (is_detachable()) os << "\n - detachable";
if (was_detached()) os << "\n - detached";
if (is_shared()) os << "\n - shared";
if (is_resizable()) os << "\n - resizable";
JSObjectPrintBody(os, *this, !was_detached());
}
@ -1399,7 +1401,7 @@ void JSTypedArray::JSTypedArrayPrint(std::ostream& os) {
os << "\n - buffer: " << Brief(buffer());
os << "\n - byte_offset: " << byte_offset();
os << "\n - byte_length: " << byte_length();
os << "\n - length: " << length();
os << "\n - length: " << GetLength();
os << "\n - data_ptr: " << DataPtr();
Tagged_t base_ptr = static_cast<Tagged_t>(base_pointer().ptr());
os << "\n - base_pointer: "
@ -1411,6 +1413,8 @@ void JSTypedArray::JSTypedArrayPrint(std::ostream& os) {
return;
}
if (WasDetached()) os << "\n - detached";
if (is_length_tracking()) os << "\n - length-tracking";
if (is_backed_by_rab()) os << "\n - backed-by-rab";
JSObjectPrintBody(os, *this, !WasDetached());
}

View File

@ -273,7 +273,9 @@ DEFINE_BOOL(harmony_shipping, true, "enable all shipped harmony features")
V(harmony_regexp_sequence, "RegExp Unicode sequence properties") \
V(harmony_weak_refs_with_cleanup_some, \
"harmony weak references with FinalizationRegistry.prototype.cleanupSome") \
V(harmony_import_assertions, "harmony import assertions")
V(harmony_import_assertions, "harmony import assertions") \
V(harmony_rab_gsab, \
"harmony ResizableArrayBuffer / GrowableSharedArrayBuffer")
#ifdef V8_INTL_SUPPORT
#define HARMONY_INPROGRESS(V) \

View File

@ -2518,7 +2518,8 @@ Handle<JSArrayBuffer> Factory::NewJSArrayBuffer(
isolate());
auto result =
Handle<JSArrayBuffer>::cast(NewJSObjectFromMap(map, allocation));
result->Setup(SharedFlag::kNotShared, std::move(backing_store));
result->Setup(SharedFlag::kNotShared, ResizableFlag::kNotResizable,
std::move(backing_store));
return result;
}
@ -2536,18 +2537,32 @@ MaybeHandle<JSArrayBuffer> Factory::NewJSArrayBufferAndBackingStore(
isolate());
auto array_buffer =
Handle<JSArrayBuffer>::cast(NewJSObjectFromMap(map, allocation));
array_buffer->Setup(SharedFlag::kNotShared, std::move(backing_store));
array_buffer->Setup(SharedFlag::kNotShared, ResizableFlag::kNotResizable,
std::move(backing_store));
return array_buffer;
}
Handle<JSArrayBuffer> Factory::NewJSSharedArrayBuffer(
std::shared_ptr<BackingStore> backing_store) {
Handle<Map> map(
Handle<Map> map;
if (backing_store->is_resizable()) {
DCHECK(FLAG_harmony_rab_gsab);
map = Handle<Map>(isolate()
->native_context()
->growable_shared_array_buffer_fun()
.initial_map(),
isolate());
} else {
map = Handle<Map>(
isolate()->native_context()->shared_array_buffer_fun().initial_map(),
isolate());
}
auto result = Handle<JSArrayBuffer>::cast(
NewJSObjectFromMap(map, AllocationType::kYoung));
result->Setup(SharedFlag::kShared, std::move(backing_store));
ResizableFlag resizable = backing_store->is_resizable()
? ResizableFlag::kResizable
: ResizableFlag::kNotResizable;
result->Setup(SharedFlag::kShared, resizable, std::move(backing_store));
return result;
}
@ -2602,6 +2617,7 @@ void Factory::TypeAndSizeForElementsKind(ElementsKind kind,
*element_size = sizeof(ctype); \
break;
TYPED_ARRAYS(TYPED_ARRAY_CASE)
RAB_GSAB_TYPED_ARRAYS_WITH_TYPED_ARRAY_TYPE(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
default:
@ -2684,6 +2700,8 @@ Handle<JSTypedArray> Factory::NewJSTypedArray(ExternalArrayType type,
raw.AllocateExternalPointerEntries(isolate());
raw.set_length(length);
raw.SetOffHeapDataPtr(isolate(), buffer->backing_store(), byte_offset);
raw.set_is_length_tracking(false);
raw.set_is_backed_by_rab(!buffer->is_shared() && buffer->is_resizable());
return typed_array;
}

View File

@ -2086,9 +2086,9 @@ void AccessorAssembler::EmitElementLoad(
Label* if_hole, Label* rebox_double, TVariable<Float64T>* var_double_value,
Label* unimplemented_elements_kind, Label* out_of_bounds, Label* miss,
ExitPoint* exit_point, LoadAccessMode access_mode) {
Label if_typed_array(this), if_fast(this), if_fast_packed(this),
if_fast_holey(this), if_fast_double(this), if_fast_holey_double(this),
if_nonfast(this), if_dictionary(this);
Label if_rab_gsab_typed_array(this), if_typed_array(this), if_fast(this),
if_fast_packed(this), if_fast_holey(this), if_fast_double(this),
if_fast_holey_double(this), if_nonfast(this), if_dictionary(this);
Branch(Int32GreaterThan(elements_kind,
Int32Constant(LAST_ANY_NONEXTENSIBLE_ELEMENTS_KIND)),
&if_nonfast, &if_fast);
@ -2169,7 +2169,16 @@ void AccessorAssembler::EmitElementLoad(
BIND(&if_nonfast);
{
STATIC_ASSERT(LAST_ELEMENTS_KIND == LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND);
Label uint8_elements(this), int8_elements(this), uint16_elements(this),
int16_elements(this), uint32_elements(this), int32_elements(this),
float32_elements(this), float64_elements(this), bigint64_elements(this),
biguint64_elements(this);
STATIC_ASSERT(LAST_ELEMENTS_KIND ==
LAST_RAB_GSAB_FIXED_TYPED_ARRAY_ELEMENTS_KIND);
GotoIf(Int32GreaterThanOrEqual(
elements_kind,
Int32Constant(FIRST_RAB_GSAB_FIXED_TYPED_ARRAY_ELEMENTS_KIND)),
&if_rab_gsab_typed_array);
GotoIf(Int32GreaterThanOrEqual(
elements_kind,
Int32Constant(FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND)),
@ -2195,7 +2204,49 @@ void AccessorAssembler::EmitElementLoad(
exit_point->Return(access_mode == LoadAccessMode::kHas ? TrueConstant()
: value);
}
{
TVARIABLE(RawPtrT, data_ptr);
BIND(&if_rab_gsab_typed_array);
{
Comment("rab gsab typed elements");
Label variable_length(this), normal(this), length_check_ok(this);
TNode<JSTypedArray> array = CAST(object);
TNode<JSArrayBuffer> buffer = LoadJSArrayBufferViewBuffer(array);
// Bounds check (incl. detachedness check).
TNode<UintPtrT> length =
LoadVariableLengthJSTypedArrayLength(array, buffer, miss);
Branch(UintPtrLessThan(intptr_index, length), &length_check_ok,
out_of_bounds);
BIND(&length_check_ok);
{
if (access_mode == LoadAccessMode::kHas) {
exit_point->Return(TrueConstant());
} else {
data_ptr = LoadJSTypedArrayDataPtr(array);
Label* elements_kind_labels[] = {
&uint8_elements, &uint8_elements, &int8_elements,
&uint16_elements, &int16_elements, &uint32_elements,
&int32_elements, &float32_elements, &float64_elements,
&bigint64_elements, &biguint64_elements};
int32_t elements_kinds[] = {
RAB_GSAB_UINT8_ELEMENTS, RAB_GSAB_UINT8_CLAMPED_ELEMENTS,
RAB_GSAB_INT8_ELEMENTS, RAB_GSAB_UINT16_ELEMENTS,
RAB_GSAB_INT16_ELEMENTS, RAB_GSAB_UINT32_ELEMENTS,
RAB_GSAB_INT32_ELEMENTS, RAB_GSAB_FLOAT32_ELEMENTS,
RAB_GSAB_FLOAT64_ELEMENTS, RAB_GSAB_BIGINT64_ELEMENTS,
RAB_GSAB_BIGUINT64_ELEMENTS};
const size_t kTypedElementsKindCount =
LAST_RAB_GSAB_FIXED_TYPED_ARRAY_ELEMENTS_KIND -
FIRST_RAB_GSAB_FIXED_TYPED_ARRAY_ELEMENTS_KIND + 1;
DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kinds));
DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kind_labels));
Switch(elements_kind, miss, elements_kinds, elements_kind_labels,
kTypedElementsKindCount);
}
}
}
BIND(&if_typed_array);
{
Comment("typed elements");
@ -2209,12 +2260,8 @@ void AccessorAssembler::EmitElementLoad(
if (access_mode == LoadAccessMode::kHas) {
exit_point->Return(TrueConstant());
} else {
TNode<RawPtrT> data_ptr = LoadJSTypedArrayDataPtr(CAST(object));
data_ptr = LoadJSTypedArrayDataPtr(CAST(object));
Label uint8_elements(this), int8_elements(this), uint16_elements(this),
int16_elements(this), uint32_elements(this), int32_elements(this),
float32_elements(this), float64_elements(this),
bigint64_elements(this), biguint64_elements(this);
Label* elements_kind_labels[] = {
&uint8_elements, &uint8_elements, &int8_elements,
&uint16_elements, &int16_elements, &uint32_elements,
@ -2232,51 +2279,54 @@ void AccessorAssembler::EmitElementLoad(
DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kind_labels));
Switch(elements_kind, miss, elements_kinds, elements_kind_labels,
kTypedElementsKindCount);
}
}
if (access_mode != LoadAccessMode::kHas) {
BIND(&uint8_elements);
{
Comment("UINT8_ELEMENTS"); // Handles UINT8_CLAMPED_ELEMENTS too.
TNode<Int32T> element = Load<Uint8T>(data_ptr, intptr_index);
TNode<Int32T> element = Load<Uint8T>(data_ptr.value(), intptr_index);
exit_point->Return(SmiFromInt32(element));
}
BIND(&int8_elements);
{
Comment("INT8_ELEMENTS");
TNode<Int32T> element = Load<Int8T>(data_ptr, intptr_index);
TNode<Int32T> element = Load<Int8T>(data_ptr.value(), intptr_index);
exit_point->Return(SmiFromInt32(element));
}
BIND(&uint16_elements);
{
Comment("UINT16_ELEMENTS");
TNode<IntPtrT> index = WordShl(intptr_index, IntPtrConstant(1));
TNode<Int32T> element = Load<Uint16T>(data_ptr, index);
TNode<Int32T> element = Load<Uint16T>(data_ptr.value(), index);
exit_point->Return(SmiFromInt32(element));
}
BIND(&int16_elements);
{
Comment("INT16_ELEMENTS");
TNode<IntPtrT> index = WordShl(intptr_index, IntPtrConstant(1));
TNode<Int32T> element = Load<Int16T>(data_ptr, index);
TNode<Int32T> element = Load<Int16T>(data_ptr.value(), index);
exit_point->Return(SmiFromInt32(element));
}
BIND(&uint32_elements);
{
Comment("UINT32_ELEMENTS");
TNode<IntPtrT> index = WordShl(intptr_index, IntPtrConstant(2));
TNode<Uint32T> element = Load<Uint32T>(data_ptr, index);
TNode<Uint32T> element = Load<Uint32T>(data_ptr.value(), index);
exit_point->Return(ChangeUint32ToTagged(element));
}
BIND(&int32_elements);
{
Comment("INT32_ELEMENTS");
TNode<IntPtrT> index = WordShl(intptr_index, IntPtrConstant(2));
TNode<Int32T> element = Load<Int32T>(data_ptr, index);
TNode<Int32T> element = Load<Int32T>(data_ptr.value(), index);
exit_point->Return(ChangeInt32ToTagged(element));
}
BIND(&float32_elements);
{
Comment("FLOAT32_ELEMENTS");
TNode<IntPtrT> index = WordShl(intptr_index, IntPtrConstant(2));
TNode<Float32T> element = Load<Float32T>(data_ptr, index);
TNode<Float32T> element = Load<Float32T>(data_ptr.value(), index);
*var_double_value = ChangeFloat32ToFloat64(element);
Goto(rebox_double);
}
@ -2284,7 +2334,7 @@ void AccessorAssembler::EmitElementLoad(
{
Comment("FLOAT64_ELEMENTS");
TNode<IntPtrT> index = WordShl(intptr_index, IntPtrConstant(3));
TNode<Float64T> element = Load<Float64T>(data_ptr, index);
TNode<Float64T> element = Load<Float64T>(data_ptr.value(), index);
*var_double_value = element;
Goto(rebox_double);
}
@ -2292,13 +2342,13 @@ void AccessorAssembler::EmitElementLoad(
{
Comment("BIGINT64_ELEMENTS");
exit_point->Return(LoadFixedTypedArrayElementAsTagged(
data_ptr, Unsigned(intptr_index), BIGINT64_ELEMENTS));
data_ptr.value(), Unsigned(intptr_index), BIGINT64_ELEMENTS));
}
BIND(&biguint64_elements);
{
Comment("BIGUINT64_ELEMENTS");
exit_point->Return(LoadFixedTypedArrayElementAsTagged(
data_ptr, Unsigned(intptr_index), BIGUINT64_ELEMENTS));
data_ptr.value(), Unsigned(intptr_index), BIGUINT64_ELEMENTS));
}
}
}

View File

@ -1266,7 +1266,7 @@ Handle<Object> KeyedLoadIC::LoadElementHandler(Handle<Map> receiver_map,
}
DCHECK(IsFastElementsKind(elements_kind) ||
IsAnyNonextensibleElementsKind(elements_kind) ||
IsTypedArrayElementsKind(elements_kind));
IsTypedArrayOrRabGsabTypedArrayElementsKind(elements_kind));
bool convert_hole_to_undefined =
(elements_kind == HOLEY_SMI_ELEMENTS ||
elements_kind == HOLEY_ELEMENTS) &&
@ -1382,7 +1382,7 @@ bool IsOutOfBoundsAccess(Handle<Object> receiver, size_t index) {
if (receiver->IsJSArray()) {
length = JSArray::cast(*receiver).length().Number();
} else if (receiver->IsJSTypedArray()) {
length = JSTypedArray::cast(*receiver).length();
length = JSTypedArray::cast(*receiver).GetLength();
} else if (receiver->IsJSObject()) {
length = JSObject::cast(*receiver).elements().length();
} else if (receiver->IsString()) {
@ -2032,7 +2032,7 @@ void KeyedStoreIC::UpdateStoreElement(Handle<Map> receiver_map,
"unsupported combination of arrays (potentially read-only length)");
return;
} else if (map->has_typed_array_elements()) {
} else if (map->has_typed_array_or_rab_gsab_typed_array_elements()) {
DCHECK(!IsStoreInArrayLiteralICKind(kind()));
external_arrays++;
}
@ -2086,7 +2086,9 @@ Handle<Object> KeyedStoreIC::StoreElementHandler(
receiver_map->has_typed_array_elements()) {
TRACE_HANDLER_STATS(isolate(), KeyedStoreIC_StoreFastElementStub);
code = CodeFactory::StoreFastElementIC(isolate(), store_mode).code();
if (receiver_map->has_typed_array_elements()) return code;
if (receiver_map->has_typed_array_elements()) {
return code;
}
} else if (IsStoreInArrayLiteralICKind(kind())) {
// TODO(jgruber): Update counter name.
TRACE_HANDLER_STATS(isolate(), StoreInArrayLiteralIC_SlowStub);
@ -2095,7 +2097,9 @@ Handle<Object> KeyedStoreIC::StoreElementHandler(
// TODO(jgruber): Update counter name.
TRACE_HANDLER_STATS(isolate(), KeyedStoreIC_StoreElementStub);
DCHECK(DICTIONARY_ELEMENTS == receiver_map->elements_kind() ||
receiver_map->has_frozen_elements());
receiver_map->has_frozen_elements() ||
receiver_map->has_rab_gsab_typed_array_elements());
// TODO(v8:11111): Add fast paths for RAB / GSAB.
code = StoreHandler::StoreSlow(isolate(), store_mode);
}

View File

@ -568,7 +568,8 @@ void KeyedStoreGenericAssembler::EmitGenericElementStore(
// dispatch.
BIND(&if_nonfast);
{
STATIC_ASSERT(LAST_ELEMENTS_KIND == LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND);
STATIC_ASSERT(LAST_ELEMENTS_KIND ==
LAST_RAB_GSAB_FIXED_TYPED_ARRAY_ELEMENTS_KIND);
GotoIf(Int32GreaterThanOrEqual(
elements_kind,
Int32Constant(FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND)),
@ -588,7 +589,8 @@ void KeyedStoreGenericAssembler::EmitGenericElementStore(
BIND(&if_typed_array);
{
Comment("Typed array");
// TODO(jkummerow): Support typed arrays.
// TODO(jkummerow): Support typed arrays. Note: RAB / GSAB backed typed
// arrays end up here too.
Goto(slow);
}
}

View File

@ -235,6 +235,8 @@ class Genesis {
enum ArrayBufferKind {
ARRAY_BUFFER,
SHARED_ARRAY_BUFFER,
RESIZABLE_ARRAY_BUFFER,
GROWABLE_SHARED_ARRAY_BUFFER
};
Handle<JSFunction> CreateArrayBuffer(Handle<String> name,
ArrayBufferKind array_buffer_kind);
@ -244,7 +246,8 @@ class Genesis {
Handle<JSFunction> InstallTypedArray(const char* name,
ElementsKind elements_kind,
InstanceType type);
InstanceType type,
int rab_gsab_initial_map_index);
void InitializeNormalizedMapCaches();
enum ExtensionTraversalState { UNVISITED, VISITED, INSTALLED };
@ -3290,6 +3293,25 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
InstallSpeciesGetter(isolate_, shared_array_buffer_fun);
}
{ // R e s i z a b l e A r r a y B u f f e r
Handle<String> name = factory->ResizableArrayBuffer_string();
Handle<JSFunction> resizable_array_buffer_fun =
CreateArrayBuffer(name, RESIZABLE_ARRAY_BUFFER);
InstallWithIntrinsicDefaultProto(isolate_, resizable_array_buffer_fun,
Context::RESIZABLE_ARRAY_BUFFER_FUN_INDEX);
InstallSpeciesGetter(isolate_, resizable_array_buffer_fun);
}
{ // G r o w a b l e S h a r e d A r r a y B u f f e r
Handle<String> name = factory->GrowableSharedArrayBuffer_string();
Handle<JSFunction> growable_shared_array_buffer_fun =
CreateArrayBuffer(name, GROWABLE_SHARED_ARRAY_BUFFER);
InstallWithIntrinsicDefaultProto(
isolate_, growable_shared_array_buffer_fun,
Context::GROWABLE_SHARED_ARRAY_BUFFER_FUN_INDEX);
InstallSpeciesGetter(isolate_, growable_shared_array_buffer_fun);
}
{ // -- A t o m i c s
Handle<JSObject> atomics_object =
factory->NewJSObject(isolate_->object_function(), AllocationType::kOld);
@ -3422,7 +3444,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
#define INSTALL_TYPED_ARRAY(Type, type, TYPE, ctype) \
{ \
Handle<JSFunction> fun = InstallTypedArray( \
#Type "Array", TYPE##_ELEMENTS, TYPE##_TYPED_ARRAY_CONSTRUCTOR_TYPE); \
#Type "Array", TYPE##_ELEMENTS, TYPE##_TYPED_ARRAY_CONSTRUCTOR_TYPE, \
Context::RAB_GSAB_##TYPE##_ARRAY_MAP_INDEX); \
InstallWithIntrinsicDefaultProto(isolate_, fun, \
Context::TYPE##_ARRAY_FUN_INDEX); \
}
@ -4055,7 +4078,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Handle<JSFunction> Genesis::InstallTypedArray(const char* name,
ElementsKind elements_kind,
InstanceType type) {
InstanceType type,
int rab_gsab_initial_map_index) {
Handle<JSObject> global =
Handle<JSObject>(native_context()->global_object(), isolate());
@ -4092,6 +4116,15 @@ Handle<JSFunction> Genesis::InstallTypedArray(const char* name,
prototype->map().set_instance_type(JS_TYPED_ARRAY_PROTOTYPE_TYPE);
InstallConstant(isolate(), prototype, "BYTES_PER_ELEMENT", bytes_per_element);
// RAB / GSAB backed TypedArrays don't have separate constructors, but they
// have their own maps. Create the corresponding map here.
Handle<Map> rab_gsab_initial_map = factory()->NewMap(
JS_TYPED_ARRAY_TYPE, JSTypedArray::kSizeWithEmbedderFields,
GetCorrespondingRabGsabElementsKind(elements_kind), 0);
native_context()->set(rab_gsab_initial_map_index, *rab_gsab_initial_map);
Map::SetPrototype(isolate(), rab_gsab_initial_map, prototype);
return result;
}
@ -4553,6 +4586,19 @@ void Genesis::InitializeGlobal_harmony_intl_locale_info() {
#endif // V8_INTL_SUPPORT
void Genesis::InitializeGlobal_harmony_rab_gsab() {
if (!FLAG_harmony_rab_gsab) return;
Handle<JSGlobalObject> global(native_context()->global_object(), isolate());
JSObject::AddProperty(isolate_, global, "ResizableArrayBuffer",
isolate()->resizable_array_buffer_fun(), DONT_ENUM);
JSObject::AddProperty(isolate_, global, "GrowableSharedArrayBuffer",
isolate()->growable_shared_array_buffer_fun(),
DONT_ENUM);
}
Handle<JSFunction> Genesis::CreateArrayBuffer(
Handle<String> name, ArrayBufferKind array_buffer_kind) {
// Create the %ArrayBufferPrototype%
@ -4596,6 +4642,28 @@ Handle<JSFunction> Genesis::CreateArrayBuffer(
Builtins::kSharedArrayBufferPrototypeSlice, 2,
true);
break;
case RESIZABLE_ARRAY_BUFFER:
SimpleInstallGetter(isolate(), prototype, factory()->byte_length_string(),
Builtins::kResizableArrayBufferPrototypeGetByteLength,
false);
SimpleInstallGetter(
isolate(), prototype, factory()->max_byte_length_string(),
Builtins::kResizableArrayBufferPrototypeGetMaxByteLength, false);
SimpleInstallFunction(isolate(), prototype, "resize",
Builtins::kResizableArrayBufferPrototypeResize, 1,
true);
break;
case GROWABLE_SHARED_ARRAY_BUFFER:
SimpleInstallGetter(
isolate(), prototype, factory()->byte_length_string(),
Builtins::kGrowableSharedArrayBufferPrototypeGetByteLength, true);
SimpleInstallGetter(
isolate(), prototype, factory()->max_byte_length_string(),
Builtins::kGrowableSharedArrayBufferPrototypeGetMaxByteLength, false);
SimpleInstallFunction(isolate(), prototype, "grow",
Builtins::kGrowableSharedArrayBufferPrototypeGrow,
1, true);
break;
}
return array_buffer_fun;

View File

@ -222,6 +222,7 @@
V(_, global_string, "global") \
V(_, globalThis_string, "globalThis") \
V(_, groups_string, "groups") \
V(_, GrowableSharedArrayBuffer_string, "GrowableSharedArrayBuffer") \
V(_, has_string, "has") \
V(_, has_indices_string, "hasIndices") \
V(_, ignoreCase_string, "ignoreCase") \
@ -248,6 +249,7 @@
V(_, long_string, "long") \
V(_, Map_string, "Map") \
V(_, MapIterator_string, "Map Iterator") \
V(_, max_byte_length_string, "maxByteLength") \
V(_, medium_string, "medium") \
V(_, message_string, "message") \
V(_, meta_string, "meta") \
@ -295,6 +297,7 @@
V(_, ReflectHas_string, "Reflect.has") \
V(_, RegExp_string, "RegExp") \
V(_, regexp_to_string, "[object RegExp]") \
V(_, ResizableArrayBuffer_string, "ResizableArrayBuffer") \
V(_, resolve_string, "resolve") \
V(_, return_string, "return") \
V(_, revoke_string, "revoke") \

View File

@ -70,9 +70,9 @@ class AllocationSite : public Struct {
bool IsNested();
// transition_info bitfields, for constructed array transition info.
using ElementsKindBits = base::BitField<ElementsKind, 0, 5>;
using DoNotInlineBit = base::BitField<bool, 5, 1>;
// Unused bits 6-30.
using ElementsKindBits = base::BitField<ElementsKind, 0, 6>;
using DoNotInlineBit = base::BitField<bool, 6, 1>;
// Unused bits 7-30.
// Bitfields for pretenure_data
using MementoFoundCountBits = base::BitField<int, 0, 26>;

View File

@ -160,6 +160,8 @@ BackingStore::~BackingStore() {
#if V8_ENABLE_WEBASSEMBLY
if (is_wasm_memory_) {
// TODO(v8:11111): RAB / GSAB - Wasm integration.
DCHECK(!is_resizable_);
DCHECK(free_on_destruct_);
DCHECK(!custom_deleter_);
size_t reservation_size =
@ -189,6 +191,23 @@ BackingStore::~BackingStore() {
}
#endif // V8_ENABLE_WEBASSEMBLY
if (is_resizable_) {
DCHECK(free_on_destruct_);
DCHECK(!custom_deleter_);
size_t reservation_size =
GetReservationSize(has_guard_regions_, byte_capacity_);
auto region =
GetReservedRegion(has_guard_regions_, buffer_start_, byte_capacity_);
bool pages_were_freed =
region.size() == 0 /* no need to free any pages */ ||
FreePages(GetPlatformPageAllocator(),
reinterpret_cast<void*>(region.begin()), region.size());
CHECK(pages_were_freed);
BackingStore::ReleaseReservation(reservation_size);
Clear();
return;
}
if (custom_deleter_) {
DCHECK(free_on_destruct_);
TRACE_BS("BS:custom deleter bs=%p mem=%p (length=%zu, capacity=%zu)\n",
@ -256,6 +275,7 @@ std::unique_ptr<BackingStore> BackingStore::Allocate(
byte_length, // length
byte_length, // capacity
shared, // shared
ResizableFlag::kNotResizable, // resizable
false, // is_wasm_memory
true, // free_on_destruct
false, // has_guard_regions
@ -305,12 +325,37 @@ void BackingStore::ReleaseReservation(uint64_t num_bytes) {
std::unique_ptr<BackingStore> BackingStore::TryAllocateWasmMemory(
Isolate* isolate, size_t initial_pages, size_t maximum_pages,
SharedFlag shared) {
// Compute size of reserved memory.
size_t engine_max_pages = wasm::max_mem_pages();
maximum_pages = std::min(engine_max_pages, maximum_pages);
auto result = TryAllocateAndPartiallyCommitMemory(
isolate, initial_pages * wasm::kWasmPageSize, wasm::kWasmPageSize,
initial_pages, maximum_pages, true, shared);
// Shared Wasm memories need an anchor for the memory object list.
if (result && shared == SharedFlag::kShared) {
result->type_specific_data_.shared_wasm_memory_data =
new SharedWasmMemoryData();
}
return result;
}
#endif // V8_ENABLE_WEBASSEMBLY
std::unique_ptr<BackingStore> BackingStore::TryAllocateAndPartiallyCommitMemory(
Isolate* isolate, size_t byte_length, size_t page_size,
size_t initial_pages, size_t maximum_pages, bool is_wasm_memory,
SharedFlag shared) {
// Enforce engine limitation on the maximum number of pages.
if (maximum_pages > std::numeric_limits<size_t>::max() / page_size) {
return nullptr;
}
// Cannot reserve 0 pages on some OSes.
if (maximum_pages == 0) maximum_pages = 1;
TRACE_BS("BSw:try %zu pages, %zu max\n", initial_pages, maximum_pages);
bool guards = trap_handler::IsTrapHandlerEnabled();
bool guards = is_wasm_memory && trap_handler::IsTrapHandlerEnabled();
// For accounting purposes, whether a GC was necessary.
bool did_retry = false;
@ -329,16 +374,7 @@ std::unique_ptr<BackingStore> BackingStore::TryAllocateWasmMemory(
return false;
};
// Compute size of reserved memory.
size_t engine_max_pages = wasm::max_mem_pages();
maximum_pages = std::min(engine_max_pages, maximum_pages);
// If the platform doesn't support so many pages, attempting to allocate
// is guaranteed to fail, so we don't even try.
if (maximum_pages > kPlatformMaxPages) return {};
CHECK_LE(maximum_pages,
std::numeric_limits<size_t>::max() / wasm::kWasmPageSize);
size_t byte_capacity = maximum_pages * wasm::kWasmPageSize;
size_t byte_capacity = maximum_pages * page_size;
size_t reservation_size = GetReservationSize(guards, byte_capacity);
//--------------------------------------------------------------------------
@ -366,7 +402,7 @@ std::unique_ptr<BackingStore> BackingStore::TryAllocateWasmMemory(
auto allocate_pages = [&] {
allocation_base =
AllocatePages(GetPlatformPageAllocator(), nullptr, reservation_size,
wasm::kWasmPageSize, PageAllocator::kNoAccess);
page_size, PageAllocator::kNoAccess);
return allocation_base != nullptr;
};
if (!gc_retry(allocate_pages)) {
@ -385,17 +421,17 @@ std::unique_ptr<BackingStore> BackingStore::TryAllocateWasmMemory(
//--------------------------------------------------------------------------
// 3. Commit the initial pages (allow read/write).
//--------------------------------------------------------------------------
size_t byte_length = initial_pages * wasm::kWasmPageSize;
size_t committed_byte_length = initial_pages * page_size;
auto commit_memory = [&] {
return byte_length == 0 ||
SetPermissions(GetPlatformPageAllocator(), buffer_start, byte_length,
PageAllocator::kReadWrite);
return committed_byte_length == 0 ||
SetPermissions(GetPlatformPageAllocator(), buffer_start,
committed_byte_length, PageAllocator::kReadWrite);
};
if (!gc_retry(commit_memory)) {
TRACE_BS("BSw:try failed to set permissions (%p, %zu)\n", buffer_start,
byte_length);
committed_byte_length);
// SetPermissions put us over the process memory limit.
V8::FatalProcessOutOfMemory(nullptr, "BackingStore::AllocateWasmMemory()");
V8::FatalProcessOutOfMemory(nullptr, "BackingStore::AllocateMemory()");
}
DebugCheckZero(buffer_start, byte_length); // touch the bytes.
@ -403,11 +439,15 @@ std::unique_ptr<BackingStore> BackingStore::TryAllocateWasmMemory(
RecordStatus(isolate, did_retry ? AllocationStatus::kSuccessAfterRetry
: AllocationStatus::kSuccess);
ResizableFlag resizable =
is_wasm_memory ? ResizableFlag::kNotResizable : ResizableFlag::kResizable;
auto result = new BackingStore(buffer_start, // start
byte_length, // length
byte_capacity, // capacity
shared, // shared
true, // is_wasm_memory
resizable, // resizable
is_wasm_memory, // is_wasm_memory
true, // free_on_destruct
guards, // has_guard_regions
false, // custom_deleter
@ -418,15 +458,10 @@ std::unique_ptr<BackingStore> BackingStore::TryAllocateWasmMemory(
result, result->buffer_start(), byte_length, byte_capacity,
reservation_size);
// Shared Wasm memories need an anchor for the memory object list.
if (shared == SharedFlag::kShared) {
result->type_specific_data_.shared_wasm_memory_data =
new SharedWasmMemoryData();
}
return std::unique_ptr<BackingStore>(result);
}
#if V8_ENABLE_WEBASSEMBLY
// Allocate a backing store for a Wasm memory. Always use the page allocator
// and add guard regions.
std::unique_ptr<BackingStore> BackingStore::AllocateWasmMemory(
@ -583,6 +618,70 @@ void BackingStore::UpdateSharedWasmMemoryObjects(Isolate* isolate) {
}
#endif // V8_ENABLE_WEBASSEMBLY
// Commit already reserved memory.
bool BackingStore::ResizeInPlace(Isolate* isolate, size_t new_byte_length,
size_t new_committed_length,
bool allow_shrinking) {
DCHECK_LE(new_byte_length, new_committed_length);
// See comment in GrowWasmMemoryInPlace.
// GrowableSharedArrayBuffer.prototype.grow can be called from several
// threads. If two threads try to grow() in a racy way, the spec allows the
// larger grow to throw also if the smaller grow succeeds first. The
// implementation below doesn't throw in that case - instead, it retries and
// succeeds. If the larger grow finishes first though, the smaller grow must
// throw.
size_t old_byte_length = byte_length_.load(std::memory_order_seq_cst);
while (true) {
if (new_byte_length < old_byte_length) {
// TOOO(v8:11111): Figure out a strategy for shrinking - when do we
// un-commit the memory?
if (allow_shrinking) {
// This branch is only relevant for RABs and they are not shared between
// threads.
DCHECK(!is_shared());
// Zero the memory so that in case the buffer is grown later, we have
// zeroed the contents already.
memset(reinterpret_cast<byte*>(buffer_start_) + new_byte_length, 0,
old_byte_length - new_byte_length);
// Changing the byte length wouldn't strictly speaking be needed, since
// the JSArrayBuffer already stores the updated length. This is to keep
// the BackingStore and JSArrayBuffer in sync.
byte_length_ = new_byte_length;
return true;
}
return false;
}
if (new_byte_length == 0) {
DCHECK_EQ(0, old_byte_length);
// i::SetPermissions with size 0 fails on some platforms.
return true;
}
// Try to adjust the permissions on the memory.
if (!i::SetPermissions(GetPlatformPageAllocator(), buffer_start_,
new_committed_length, PageAllocator::kReadWrite)) {
return false;
}
// compare_exchange_weak updates old_byte_length.
if (byte_length_.compare_exchange_weak(old_byte_length, new_byte_length,
std::memory_order_seq_cst)) {
// Successfully updated both the length and permissions.
break;
}
}
DCHECK(free_on_destruct_);
if (!is_shared_) {
// Only do per-isolate accounting for non-shared backing stores.
reinterpret_cast<v8::Isolate*>(isolate)
->AdjustAmountOfExternalAllocatedMemory(new_byte_length -
old_byte_length);
}
return true;
}
std::unique_ptr<BackingStore> BackingStore::WrapAllocation(
Isolate* isolate, void* allocation_base, size_t allocation_length,
SharedFlag shared, bool free_on_destruct) {
@ -590,6 +689,7 @@ std::unique_ptr<BackingStore> BackingStore::WrapAllocation(
allocation_length, // length
allocation_length, // capacity
shared, // shared
ResizableFlag::kNotResizable, // resizable
false, // is_wasm_memory
free_on_destruct, // free_on_destruct
false, // has_guard_regions
@ -610,6 +710,7 @@ std::unique_ptr<BackingStore> BackingStore::WrapAllocation(
allocation_length, // length
allocation_length, // capacity
shared, // shared
ResizableFlag::kNotResizable, // resizable
false, // is_wasm_memory
true, // free_on_destruct
false, // has_guard_regions
@ -627,6 +728,7 @@ std::unique_ptr<BackingStore> BackingStore::EmptyBackingStore(
0, // length
0, // capacity
shared, // shared
ResizableFlag::kNotResizable, // resizable
false, // is_wasm_memory
true, // free_on_destruct
false, // has_guard_regions

View File

@ -21,6 +21,9 @@ class WasmMemoryObject;
// Whether the backing store is shared or not.
enum class SharedFlag : uint8_t { kNotShared, kShared };
// Whether the backing store is resizable or not.
enum class ResizableFlag : uint8_t { kNotResizable, kResizable };
// Whether the backing store memory is initialied to zero or not.
enum class InitializedFlag : uint8_t { kUninitialized, kZeroInitialized };
@ -56,6 +59,12 @@ class V8_EXPORT_PRIVATE BackingStore : public BackingStoreBase {
SharedFlag shared);
#endif // V8_ENABLE_WEBASSEMBLY
// Tries to allocate `maximum_pages` of memory and commit `initial_pages`.
static std::unique_ptr<BackingStore> TryAllocateAndPartiallyCommitMemory(
Isolate* isolate, size_t byte_length, size_t page_size,
size_t initial_pages, size_t maximum_pages, bool is_wasm_memory,
SharedFlag shared);
// Create a backing store that wraps existing allocated memory.
// If {free_on_destruct} is {true}, the memory will be freed using the
// ArrayBufferAllocator::Free() callback when this backing store is
@ -77,15 +86,20 @@ class V8_EXPORT_PRIVATE BackingStore : public BackingStoreBase {
// Accessors.
void* buffer_start() const { return buffer_start_; }
size_t byte_length() const {
return byte_length_.load(std::memory_order_relaxed);
size_t byte_length(
std::memory_order memory_order = std::memory_order_relaxed) const {
return byte_length_.load(memory_order);
}
size_t byte_capacity() const { return byte_capacity_; }
bool is_shared() const { return is_shared_; }
bool is_resizable() const { return is_resizable_; }
bool is_wasm_memory() const { return is_wasm_memory_; }
bool has_guard_regions() const { return has_guard_regions_; }
bool free_on_destruct() const { return free_on_destruct_; }
bool ResizeInPlace(Isolate* isolate, size_t new_byte_length,
size_t new_committed_length, bool allow_shrinking);
// Wrapper around ArrayBuffer::Allocator::Reallocate.
bool Reallocate(Isolate* isolate, size_t new_byte_length);
@ -148,19 +162,26 @@ class V8_EXPORT_PRIVATE BackingStore : public BackingStoreBase {
friend class GlobalBackingStoreRegistry;
BackingStore(void* buffer_start, size_t byte_length, size_t byte_capacity,
SharedFlag shared, bool is_wasm_memory, bool free_on_destruct,
bool has_guard_regions, bool custom_deleter, bool empty_deleter)
SharedFlag shared, ResizableFlag resizable, bool is_wasm_memory,
bool free_on_destruct, bool has_guard_regions,
bool custom_deleter, bool empty_deleter)
: buffer_start_(buffer_start),
byte_length_(byte_length),
byte_capacity_(byte_capacity),
is_shared_(shared == SharedFlag::kShared),
is_resizable_(resizable == ResizableFlag::kResizable),
is_wasm_memory_(is_wasm_memory),
holds_shared_ptr_to_allocator_(false),
free_on_destruct_(free_on_destruct),
has_guard_regions_(has_guard_regions),
globally_registered_(false),
custom_deleter_(custom_deleter),
empty_deleter_(empty_deleter) {}
empty_deleter_(empty_deleter) {
// TODO(v8:11111): RAB / GSAB - Wasm integration.
DCHECK_IMPLIES(is_wasm_memory_, !is_resizable_);
DCHECK_IMPLIES(is_resizable_, !custom_deleter_);
DCHECK_IMPLIES(is_resizable_, free_on_destruct_);
}
BackingStore(const BackingStore&) = delete;
BackingStore& operator=(const BackingStore&) = delete;
void SetAllocatorFromIsolate(Isolate* isolate);
@ -199,6 +220,8 @@ class V8_EXPORT_PRIVATE BackingStore : public BackingStoreBase {
} type_specific_data_;
bool is_shared_ : 1;
// Backing stores for (Resizable|GrowableShared)ArrayBuffer
bool is_resizable_ : 1;
bool is_wasm_memory_ : 1;
bool holds_shared_ptr_to_allocator_ : 1;
bool free_on_destruct_ : 1;

View File

@ -112,6 +112,8 @@ enum ContextLookupFlags {
V(GENERATOR_OBJECT_PROTOTYPE_MAP_INDEX, Map, generator_object_prototype_map) \
V(ASYNC_GENERATOR_OBJECT_PROTOTYPE_MAP_INDEX, Map, \
async_generator_object_prototype_map) \
V(GROWABLE_SHARED_ARRAY_BUFFER_FUN_INDEX, JSFunction, \
growable_shared_array_buffer_fun) \
V(INITIAL_ARRAY_ITERATOR_MAP_INDEX, Map, initial_array_iterator_map) \
V(INITIAL_ARRAY_ITERATOR_PROTOTYPE_INDEX, JSObject, \
initial_array_iterator_prototype) \
@ -210,6 +212,18 @@ enum ContextLookupFlags {
V(PROXY_MAP_INDEX, Map, proxy_map) \
V(PROXY_REVOCABLE_RESULT_MAP_INDEX, Map, proxy_revocable_result_map) \
V(PROMISE_PROTOTYPE_INDEX, JSObject, promise_prototype) \
V(RAB_GSAB_UINT8_ARRAY_MAP_INDEX, Map, rab_gsab_uint8_array_map) \
V(RAB_GSAB_INT8_ARRAY_MAP_INDEX, Map, rab_gsab_int8_array_map) \
V(RAB_GSAB_UINT16_ARRAY_MAP_INDEX, Map, rab_gsab_uint16_array_map) \
V(RAB_GSAB_INT16_ARRAY_MAP_INDEX, Map, rab_gsab_int16_array_map) \
V(RAB_GSAB_UINT32_ARRAY_MAP_INDEX, Map, rab_gsab_uint32_array_map) \
V(RAB_GSAB_INT32_ARRAY_MAP_INDEX, Map, rab_gsab_int32_array_map) \
V(RAB_GSAB_FLOAT32_ARRAY_MAP_INDEX, Map, rab_gsab_float32_array_map) \
V(RAB_GSAB_FLOAT64_ARRAY_MAP_INDEX, Map, rab_gsab_float64_array_map) \
V(RAB_GSAB_UINT8_CLAMPED_ARRAY_MAP_INDEX, Map, \
rab_gsab_uint8_clamped_array_map) \
V(RAB_GSAB_BIGUINT64_ARRAY_MAP_INDEX, Map, rab_gsab_biguint64_array_map) \
V(RAB_GSAB_BIGINT64_ARRAY_MAP_INDEX, Map, rab_gsab_bigint64_array_map) \
V(RECORDER_CONTEXT_ID, Object, recorder_context_id) \
V(REGEXP_EXEC_FUNCTION_INDEX, JSFunction, regexp_exec_function) \
V(REGEXP_FUNCTION_INDEX, JSFunction, regexp_function) \
@ -226,6 +240,7 @@ enum ContextLookupFlags {
V(REGEXP_SPLIT_FUNCTION_INDEX, JSFunction, regexp_split_function) \
V(INITIAL_REGEXP_STRING_ITERATOR_PROTOTYPE_MAP_INDEX, Map, \
initial_regexp_string_iterator_prototype_map) \
V(RESIZABLE_ARRAY_BUFFER_FUN_INDEX, JSFunction, resizable_array_buffer_fun) \
V(SCRIPT_CONTEXT_TABLE_INDEX, ScriptContextTable, script_context_table) \
V(SCRIPT_EXECUTION_CALLBACK_INDEX, Object, script_execution_callback) \
V(SECURITY_TOKEN_INDEX, Object, security_token) \

View File

@ -120,6 +120,30 @@ extern enum ContextSlot extends intptr constexpr 'Context::Field' {
FUNCTION_CONTEXT_MAP_INDEX: Slot<NativeContext, Map>,
FUNCTION_PROTOTYPE_APPLY_INDEX: Slot<NativeContext, JSFunction>,
UINT8_ARRAY_FUN_INDEX: Slot<NativeContext, JSFunction>,
INT8_ARRAY_FUN_INDEX: Slot<NativeContext, JSFunction>,
UINT16_ARRAY_FUN_INDEX: Slot<NativeContext, JSFunction>,
INT16_ARRAY_FUN_INDEX: Slot<NativeContext, JSFunction>,
UINT32_ARRAY_FUN_INDEX: Slot<NativeContext, JSFunction>,
INT32_ARRAY_FUN_INDEX: Slot<NativeContext, JSFunction>,
FLOAT32_ARRAY_FUN_INDEX: Slot<NativeContext, JSFunction>,
FLOAT64_ARRAY_FUN_INDEX: Slot<NativeContext, JSFunction>,
UINT8_CLAMPED_ARRAY_FUN_INDEX: Slot<NativeContext, JSFunction>,
BIGUINT64_ARRAY_FUN_INDEX: Slot<NativeContext, JSFunction>,
BIGINT64_ARRAY_FUN_INDEX: Slot<NativeContext, JSFunction>,
RAB_GSAB_UINT8_ARRAY_MAP_INDEX: Slot<NativeContext, Map>,
RAB_GSAB_INT8_ARRAY_MAP_INDEX: Slot<NativeContext, Map>,
RAB_GSAB_UINT16_ARRAY_MAP_INDEX: Slot<NativeContext, Map>,
RAB_GSAB_INT16_ARRAY_MAP_INDEX: Slot<NativeContext, Map>,
RAB_GSAB_UINT32_ARRAY_MAP_INDEX: Slot<NativeContext, Map>,
RAB_GSAB_INT32_ARRAY_MAP_INDEX: Slot<NativeContext, Map>,
RAB_GSAB_FLOAT32_ARRAY_MAP_INDEX: Slot<NativeContext, Map>,
RAB_GSAB_FLOAT64_ARRAY_MAP_INDEX: Slot<NativeContext, Map>,
RAB_GSAB_UINT8_CLAMPED_ARRAY_MAP_INDEX: Slot<NativeContext, Map>,
RAB_GSAB_BIGUINT64_ARRAY_MAP_INDEX: Slot<NativeContext, Map>,
RAB_GSAB_BIGINT64_ARRAY_MAP_INDEX: Slot<NativeContext, Map>,
PROMISE_FUNCTION_INDEX: Slot<NativeContext, JSFunction>,
PROMISE_THEN_INDEX: Slot<NativeContext, JSFunction>,
PROMISE_PROTOTYPE_INDEX: Slot<NativeContext, JSObject>,

View File

@ -17,19 +17,30 @@ int ElementsKindToShiftSize(ElementsKind elements_kind) {
case UINT8_ELEMENTS:
case INT8_ELEMENTS:
case UINT8_CLAMPED_ELEMENTS:
case RAB_GSAB_UINT8_ELEMENTS:
case RAB_GSAB_INT8_ELEMENTS:
case RAB_GSAB_UINT8_CLAMPED_ELEMENTS:
return 0;
case UINT16_ELEMENTS:
case INT16_ELEMENTS:
case RAB_GSAB_UINT16_ELEMENTS:
case RAB_GSAB_INT16_ELEMENTS:
return 1;
case UINT32_ELEMENTS:
case INT32_ELEMENTS:
case FLOAT32_ELEMENTS:
case RAB_GSAB_UINT32_ELEMENTS:
case RAB_GSAB_INT32_ELEMENTS:
case RAB_GSAB_FLOAT32_ELEMENTS:
return 2;
case PACKED_DOUBLE_ELEMENTS:
case HOLEY_DOUBLE_ELEMENTS:
case FLOAT64_ELEMENTS:
case BIGINT64_ELEMENTS:
case BIGUINT64_ELEMENTS:
case RAB_GSAB_FLOAT64_ELEMENTS:
case RAB_GSAB_BIGINT64_ELEMENTS:
case RAB_GSAB_BIGUINT64_ELEMENTS:
return 3;
case PACKED_SMI_ELEMENTS:
case PACKED_ELEMENTS:
@ -109,6 +120,7 @@ const char* ElementsKindToString(ElementsKind kind) {
return #TYPE "ELEMENTS";
TYPED_ARRAYS(PRINT_NAME);
RAB_GSAB_TYPED_ARRAYS(PRINT_NAME);
#undef PRINT_NAME
case NO_ELEMENTS:
return "NO_ELEMENTS";

View File

@ -28,6 +28,36 @@ namespace internal {
V(BigUint64, biguint64, BIGUINT64, uint64_t) \
V(BigInt64, bigint64, BIGINT64, int64_t)
#define RAB_GSAB_TYPED_ARRAYS(V) \
V(RabGsabUint8, rab_gsab_uint8, RAB_GSAB_UINT8, uint8_t) \
V(RabGsabInt8, rab_gsab_int8, RAB_GSAB_INT8, int8_t) \
V(RabGsabUint16, rab_gsab_uint16, RAB_GSAB_UINT16, uint16_t) \
V(RabGsabInt16, rab_gsab_int16, RAB_GSAB_INT16, int16_t) \
V(RabGsabUint32, rab_gsab_uint32, RAB_GSAB_UINT32, uint32_t) \
V(RabGsabInt32, rab_gsab_int32, RAB_GSAB_INT32, int32_t) \
V(RabGsabFloat32, rab_gsab_float32, RAB_GSAB_FLOAT32, float) \
V(RabGsabFloat64, rab_gsab_float64, RAB_GSAB_FLOAT64, double) \
V(RabGsabUint8Clamped, rab_gsab_uint8_clamped, RAB_GSAB_UINT8_CLAMPED, \
uint8_t) \
V(RabGsabBigUint64, rab_gsab_biguint64, RAB_GSAB_BIGUINT64, uint64_t) \
V(RabGsabBigInt64, rab_gsab_bigint64, RAB_GSAB_BIGINT64, int64_t)
// The TypedArrays backed by RAB / GSAB are called Uint8Array, Uint16Array etc,
// and not RabGsabUint8Array, RabGsabUint16Array etc. This macro is used for
// generating code which refers to the TypedArray type.
#define RAB_GSAB_TYPED_ARRAYS_WITH_TYPED_ARRAY_TYPE(V) \
V(Uint8, rab_gsab_uint8, RAB_GSAB_UINT8, uint8_t) \
V(Int8, rab_gsab_int8, RAB_GSAB_INT8, int8_t) \
V(Uint16, rab_gsab_uint16, RAB_GSAB_UINT16, uint16_t) \
V(Int16, rab_gsab_int16, RAB_GSAB_INT16, int16_t) \
V(Uint32, rab_gsab_uint32, RAB_GSAB_UINT32, uint32_t) \
V(Int32, rab_gsab_int32, RAB_GSAB_INT32, int32_t) \
V(Float32, rab_gsab_float32, RAB_GSAB_FLOAT32, float) \
V(Float64, rab_gsab_float64, RAB_GSAB_FLOAT64, double) \
V(Uint8Clamped, rab_gsab_uint8_clamped, RAB_GSAB_UINT8_CLAMPED, uint8_t) \
V(BigUint64, rab_gsab_biguint64, RAB_GSAB_BIGUINT64, uint64_t) \
V(BigInt64, rab_gsab_bigint64, RAB_GSAB_BIGINT64, int64_t)
enum ElementsKind : uint8_t {
// The "fast" kind for elements that only contain SMI values. Must be first
// to make it possible to efficiently check maps for this kind.
@ -71,6 +101,7 @@ enum ElementsKind : uint8_t {
// Fixed typed arrays.
#define TYPED_ARRAY_ELEMENTS_KIND(Type, type, TYPE, ctype) TYPE##_ELEMENTS,
TYPED_ARRAYS(TYPED_ARRAY_ELEMENTS_KIND)
RAB_GSAB_TYPED_ARRAYS(TYPED_ARRAY_ELEMENTS_KIND)
#undef TYPED_ARRAY_ELEMENTS_KIND
// Sentinel ElementsKind for objects with no elements.
@ -78,11 +109,13 @@ enum ElementsKind : uint8_t {
// Derived constants from ElementsKind.
FIRST_ELEMENTS_KIND = PACKED_SMI_ELEMENTS,
LAST_ELEMENTS_KIND = BIGINT64_ELEMENTS,
LAST_ELEMENTS_KIND = RAB_GSAB_BIGINT64_ELEMENTS,
FIRST_FAST_ELEMENTS_KIND = PACKED_SMI_ELEMENTS,
LAST_FAST_ELEMENTS_KIND = HOLEY_DOUBLE_ELEMENTS,
FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND = UINT8_ELEMENTS,
LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND = BIGINT64_ELEMENTS,
FIRST_RAB_GSAB_FIXED_TYPED_ARRAY_ELEMENTS_KIND = RAB_GSAB_UINT8_ELEMENTS,
LAST_RAB_GSAB_FIXED_TYPED_ARRAY_ELEMENTS_KIND = RAB_GSAB_BIGINT64_ELEMENTS,
TERMINAL_FAST_ELEMENTS_KIND = HOLEY_ELEMENTS,
FIRST_ANY_NONEXTENSIBLE_ELEMENTS_KIND = PACKED_NONEXTENSIBLE_ELEMENTS,
LAST_ANY_NONEXTENSIBLE_ELEMENTS_KIND = HOLEY_FROZEN_ELEMENTS,
@ -103,7 +136,7 @@ constexpr int kFastElementsKindCount =
constexpr int kFastElementsKindPackedToHoley =
HOLEY_SMI_ELEMENTS - PACKED_SMI_ELEMENTS;
constexpr int kElementsKindBits = 5;
constexpr int kElementsKindBits = 6;
STATIC_ASSERT((1 << kElementsKindBits) > LAST_ELEMENTS_KIND);
STATIC_ASSERT((1 << (kElementsKindBits - 1)) <= LAST_ELEMENTS_KIND);
@ -150,8 +183,20 @@ inline bool IsTypedArrayElementsKind(ElementsKind kind) {
LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND);
}
inline bool IsRabGsabTypedArrayElementsKind(ElementsKind kind) {
return base::IsInRange(kind, FIRST_RAB_GSAB_FIXED_TYPED_ARRAY_ELEMENTS_KIND,
LAST_RAB_GSAB_FIXED_TYPED_ARRAY_ELEMENTS_KIND);
}
inline bool IsTypedArrayOrRabGsabTypedArrayElementsKind(ElementsKind kind) {
return base::IsInRange(kind, FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND,
LAST_RAB_GSAB_FIXED_TYPED_ARRAY_ELEMENTS_KIND);
}
inline bool IsTerminalElementsKind(ElementsKind kind) {
return kind == TERMINAL_FAST_ELEMENTS_KIND || IsTypedArrayElementsKind(kind);
return kind == TERMINAL_FAST_ELEMENTS_KIND ||
IsTypedArrayElementsKind(kind) ||
IsRabGsabTypedArrayElementsKind(kind);
}
inline bool IsFastElementsKind(ElementsKind kind) {
@ -281,6 +326,13 @@ inline ElementsKind GetHoleyElementsKind(ElementsKind packed_kind) {
return packed_kind;
}
inline ElementsKind GetCorrespondingRabGsabElementsKind(
ElementsKind typed_array_kind) {
DCHECK(IsTypedArrayElementsKind(typed_array_kind));
return ElementsKind(typed_array_kind - FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND +
FIRST_RAB_GSAB_FIXED_TYPED_ARRAY_ELEMENTS_KIND);
}
inline bool UnionElementsKindUptoPackedness(ElementsKind* a_out,
ElementsKind b) {
// Assert that the union of two ElementKinds can be computed via std::max.

View File

@ -59,6 +59,17 @@
// - Uint8ClampedElementsAccessor
// - BigUint64ElementsAccessor
// - BigInt64ElementsAccessor
// - RabGsabUint8ElementsAccessor
// - RabGsabInt8ElementsAccessor
// - RabGsabUint16ElementsAccessor
// - RabGsabInt16ElementsAccessor
// - RabGsabUint32ElementsAccessor
// - RabGsabInt32ElementsAccessor
// - RabGsabFloat32ElementsAccessor
// - RabGsabFloat64ElementsAccessor
// - RabGsabUint8ClampedElementsAccessor
// - RabGsabBigUint64ElementsAccessor
// - RabGsabBigInt64ElementsAccessor
// - DictionaryElementsAccessor
// - SloppyArgumentsElementsAccessor
// - FastSloppyArgumentsElementsAccessor
@ -129,7 +140,19 @@ enum Where { AT_START, AT_END };
V(Float64ElementsAccessor, FLOAT64_ELEMENTS, ByteArray) \
V(Uint8ClampedElementsAccessor, UINT8_CLAMPED_ELEMENTS, ByteArray) \
V(BigUint64ElementsAccessor, BIGUINT64_ELEMENTS, ByteArray) \
V(BigInt64ElementsAccessor, BIGINT64_ELEMENTS, ByteArray)
V(BigInt64ElementsAccessor, BIGINT64_ELEMENTS, ByteArray) \
V(RabGsabUint8ElementsAccessor, RAB_GSAB_UINT8_ELEMENTS, ByteArray) \
V(RabGsabInt8ElementsAccessor, RAB_GSAB_INT8_ELEMENTS, ByteArray) \
V(RabGsabUint16ElementsAccessor, RAB_GSAB_UINT16_ELEMENTS, ByteArray) \
V(RabGsabInt16ElementsAccessor, RAB_GSAB_INT16_ELEMENTS, ByteArray) \
V(RabGsabUint32ElementsAccessor, RAB_GSAB_UINT32_ELEMENTS, ByteArray) \
V(RabGsabInt32ElementsAccessor, RAB_GSAB_INT32_ELEMENTS, ByteArray) \
V(RabGsabFloat32ElementsAccessor, RAB_GSAB_FLOAT32_ELEMENTS, ByteArray) \
V(RabGsabFloat64ElementsAccessor, RAB_GSAB_FLOAT64_ELEMENTS, ByteArray) \
V(RabGsabUint8ClampedElementsAccessor, RAB_GSAB_UINT8_CLAMPED_ELEMENTS, \
ByteArray) \
V(RabGsabBigUint64ElementsAccessor, RAB_GSAB_BIGUINT64_ELEMENTS, ByteArray) \
V(RabGsabBigInt64ElementsAccessor, RAB_GSAB_BIGINT64_ELEMENTS, ByteArray)
template <ElementsKind Kind>
class ElementsKindTraits {
@ -2520,6 +2543,7 @@ class FastSmiOrObjectElementsAccessor
case SLOW_STRING_WRAPPER_ELEMENTS:
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) case TYPE##_ELEMENTS:
TYPED_ARRAYS(TYPED_ARRAY_CASE)
RAB_GSAB_TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
// This function is currently only used for JSArrays with non-zero
// length.
@ -2935,6 +2959,7 @@ class FastDoubleElementsAccessor
case NO_ELEMENTS:
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) case TYPE##_ELEMENTS:
TYPED_ARRAYS(TYPED_ARRAY_CASE)
RAB_GSAB_TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
// This function is currently only used for JSArrays with non-zero
// length.
@ -3056,7 +3081,7 @@ class TypedElementsAccessor
static void SetImpl(Handle<JSObject> holder, InternalIndex entry,
Object value) {
Handle<JSTypedArray> typed_array = Handle<JSTypedArray>::cast(holder);
DCHECK_LE(entry.raw_value(), typed_array->length());
DCHECK_LE(entry.raw_value(), typed_array->GetLength());
SetImpl(static_cast<ElementType*>(typed_array->DataPtr()),
entry.raw_value(), FromObject(value));
}
@ -3108,7 +3133,7 @@ class TypedElementsAccessor
InternalIndex entry) {
Handle<JSTypedArray> typed_array = Handle<JSTypedArray>::cast(holder);
Isolate* isolate = typed_array->GetIsolate();
DCHECK_LE(entry.raw_value(), typed_array->length());
DCHECK_LT(entry.raw_value(), typed_array->GetLength());
DCHECK(!typed_array->WasDetached());
ElementType elem = GetImpl(
static_cast<ElementType*>(typed_array->DataPtr()), entry.raw_value());
@ -3208,8 +3233,7 @@ class TypedElementsAccessor
static size_t GetCapacityImpl(JSObject holder, FixedArrayBase backing_store) {
JSTypedArray typed_array = JSTypedArray::cast(holder);
if (typed_array.WasDetached()) return 0;
return typed_array.length();
return typed_array.GetLength();
}
static size_t NumberOfElementsImpl(JSObject receiver,
@ -3970,9 +3994,225 @@ Handle<Object> TypedElementsAccessor<BIGUINT64_ELEMENTS, uint64_t>::ToHandle(
return BigInt::FromUint64(isolate, value);
}
// static
template <>
Handle<Object> TypedElementsAccessor<RAB_GSAB_INT8_ELEMENTS, int8_t>::ToHandle(
Isolate* isolate, int8_t value) {
return handle(Smi::FromInt(value), isolate);
}
// static
template <>
Handle<Object> TypedElementsAccessor<RAB_GSAB_UINT8_ELEMENTS,
uint8_t>::ToHandle(Isolate* isolate,
uint8_t value) {
return handle(Smi::FromInt(value), isolate);
}
// static
template <>
Handle<Object> TypedElementsAccessor<RAB_GSAB_INT16_ELEMENTS,
int16_t>::ToHandle(Isolate* isolate,
int16_t value) {
return handle(Smi::FromInt(value), isolate);
}
// static
template <>
Handle<Object> TypedElementsAccessor<RAB_GSAB_UINT16_ELEMENTS,
uint16_t>::ToHandle(Isolate* isolate,
uint16_t value) {
return handle(Smi::FromInt(value), isolate);
}
// static
template <>
Handle<Object> TypedElementsAccessor<RAB_GSAB_INT32_ELEMENTS,
int32_t>::ToHandle(Isolate* isolate,
int32_t value) {
return isolate->factory()->NewNumberFromInt(value);
}
// static
template <>
Handle<Object> TypedElementsAccessor<RAB_GSAB_UINT32_ELEMENTS,
uint32_t>::ToHandle(Isolate* isolate,
uint32_t value) {
return isolate->factory()->NewNumberFromUint(value);
}
// static
template <>
float TypedElementsAccessor<RAB_GSAB_FLOAT32_ELEMENTS, float>::FromScalar(
double value) {
return DoubleToFloat32(value);
}
// static
template <>
Handle<Object> TypedElementsAccessor<RAB_GSAB_FLOAT32_ELEMENTS,
float>::ToHandle(Isolate* isolate,
float value) {
return isolate->factory()->NewNumber(value);
}
// static
template <>
double TypedElementsAccessor<RAB_GSAB_FLOAT64_ELEMENTS, double>::FromScalar(
double value) {
return value;
}
// static
template <>
Handle<Object> TypedElementsAccessor<RAB_GSAB_FLOAT64_ELEMENTS,
double>::ToHandle(Isolate* isolate,
double value) {
return isolate->factory()->NewNumber(value);
}
// static
template <>
uint8_t TypedElementsAccessor<RAB_GSAB_UINT8_CLAMPED_ELEMENTS,
uint8_t>::FromScalar(int value) {
if (value < 0x00) return 0x00;
if (value > 0xFF) return 0xFF;
return static_cast<uint8_t>(value);
}
// static
template <>
uint8_t TypedElementsAccessor<RAB_GSAB_UINT8_CLAMPED_ELEMENTS,
uint8_t>::FromScalar(uint32_t value) {
// We need this special case for Uint32 -> Uint8Clamped, because the highest
// Uint32 values will be negative as an int, clamping to 0, rather than 255.
if (value > 0xFF) return 0xFF;
return static_cast<uint8_t>(value);
}
// static
template <>
uint8_t TypedElementsAccessor<RAB_GSAB_UINT8_CLAMPED_ELEMENTS,
uint8_t>::FromScalar(double value) {
// Handle NaNs and less than zero values which clamp to zero.
if (!(value > 0)) return 0;
if (value > 0xFF) return 0xFF;
return static_cast<uint8_t>(lrint(value));
}
// static
template <>
Handle<Object> TypedElementsAccessor<RAB_GSAB_UINT8_CLAMPED_ELEMENTS,
uint8_t>::ToHandle(Isolate* isolate,
uint8_t value) {
return handle(Smi::FromInt(value), isolate);
}
// static
template <>
int64_t TypedElementsAccessor<RAB_GSAB_BIGINT64_ELEMENTS, int64_t>::FromScalar(
int value) {
UNREACHABLE();
}
// static
template <>
int64_t TypedElementsAccessor<RAB_GSAB_BIGINT64_ELEMENTS, int64_t>::FromScalar(
uint32_t value) {
UNREACHABLE();
}
// static
template <>
int64_t TypedElementsAccessor<RAB_GSAB_BIGINT64_ELEMENTS, int64_t>::FromScalar(
double value) {
UNREACHABLE();
}
// static
template <>
int64_t TypedElementsAccessor<RAB_GSAB_BIGINT64_ELEMENTS, int64_t>::FromScalar(
int64_t value) {
return value;
}
// static
template <>
int64_t TypedElementsAccessor<RAB_GSAB_BIGINT64_ELEMENTS, int64_t>::FromScalar(
uint64_t value) {
return static_cast<int64_t>(value);
}
// static
template <>
int64_t TypedElementsAccessor<RAB_GSAB_BIGINT64_ELEMENTS, int64_t>::FromObject(
Object value, bool* lossless) {
return BigInt::cast(value).AsInt64(lossless);
}
// static
template <>
Handle<Object> TypedElementsAccessor<RAB_GSAB_BIGINT64_ELEMENTS,
int64_t>::ToHandle(Isolate* isolate,
int64_t value) {
return BigInt::FromInt64(isolate, value);
}
// static
template <>
uint64_t TypedElementsAccessor<RAB_GSAB_BIGUINT64_ELEMENTS,
uint64_t>::FromScalar(int value) {
UNREACHABLE();
}
// static
template <>
uint64_t TypedElementsAccessor<RAB_GSAB_BIGUINT64_ELEMENTS,
uint64_t>::FromScalar(uint32_t value) {
UNREACHABLE();
}
// static
template <>
uint64_t TypedElementsAccessor<RAB_GSAB_BIGUINT64_ELEMENTS,
uint64_t>::FromScalar(double value) {
UNREACHABLE();
}
// static
template <>
uint64_t TypedElementsAccessor<RAB_GSAB_BIGUINT64_ELEMENTS,
uint64_t>::FromScalar(int64_t value) {
return static_cast<uint64_t>(value);
}
// static
template <>
uint64_t TypedElementsAccessor<RAB_GSAB_BIGUINT64_ELEMENTS,
uint64_t>::FromScalar(uint64_t value) {
return value;
}
// static
template <>
uint64_t TypedElementsAccessor<RAB_GSAB_BIGUINT64_ELEMENTS,
uint64_t>::FromObject(Object value,
bool* lossless) {
return BigInt::cast(value).AsUint64(lossless);
}
// static
template <>
Handle<Object> TypedElementsAccessor<RAB_GSAB_BIGUINT64_ELEMENTS,
uint64_t>::ToHandle(Isolate* isolate,
uint64_t value) {
return BigInt::FromUint64(isolate, value);
}
#define FIXED_ELEMENTS_ACCESSOR(Type, type, TYPE, ctype) \
using Type##ElementsAccessor = TypedElementsAccessor<TYPE##_ELEMENTS, ctype>;
TYPED_ARRAYS(FIXED_ELEMENTS_ACCESSOR)
RAB_GSAB_TYPED_ARRAYS(FIXED_ELEMENTS_ACCESSOR)
#undef FIXED_ELEMENTS_ACCESSOR
template <typename Subclass, typename ArgumentsAccessor, typename KindTraits>

View File

@ -168,6 +168,8 @@ BIT_FIELD_ACCESSORS(JSArrayBuffer, bit_field, is_asmjs_memory,
JSArrayBuffer::IsAsmJsMemoryBit)
BIT_FIELD_ACCESSORS(JSArrayBuffer, bit_field, is_shared,
JSArrayBuffer::IsSharedBit)
BIT_FIELD_ACCESSORS(JSArrayBuffer, bit_field, is_resizable,
JSArrayBuffer::IsResizableBit)
size_t JSArrayBufferView::byte_offset() const {
return ReadField<size_t>(kByteOffsetOffset);
@ -189,11 +191,49 @@ bool JSArrayBufferView::WasDetached() const {
return JSArrayBuffer::cast(buffer()).was_detached();
}
BIT_FIELD_ACCESSORS(JSTypedArray, bit_field, is_length_tracking,
JSTypedArray::IsLengthTrackingBit)
BIT_FIELD_ACCESSORS(JSTypedArray, bit_field, is_backed_by_rab,
JSTypedArray::IsBackedByRabBit)
bool JSTypedArray::IsVariableLength() const {
return is_length_tracking() || is_backed_by_rab();
}
size_t JSTypedArray::GetLength() const {
if (WasDetached()) return 0;
if (is_length_tracking()) {
if (is_backed_by_rab()) {
return buffer().byte_length() / element_size();
}
return buffer().GetBackingStore()->byte_length(std::memory_order_seq_cst) /
element_size();
}
size_t array_length = LengthUnchecked();
if (is_backed_by_rab()) {
// The sum can't overflow, since we have managed to allocate the
// JSTypedArray.
if (byte_offset() + array_length * element_size() >
buffer().byte_length()) {
return 0;
}
}
return array_length;
}
void JSTypedArray::AllocateExternalPointerEntries(Isolate* isolate) {
InitExternalPointerField(kExternalPointerOffset, isolate);
}
size_t JSTypedArray::length() const { return ReadField<size_t>(kLengthOffset); }
size_t JSTypedArray::length() const {
DCHECK(!is_length_tracking());
DCHECK(!is_backed_by_rab());
return ReadField<size_t>(kLengthOffset);
}
size_t JSTypedArray::LengthUnchecked() const {
return ReadField<size_t>(kLengthOffset);
}
void JSTypedArray::set_length(size_t value) {
WriteField<size_t>(kLengthOffset, value);

View File

@ -35,11 +35,12 @@ bool CanonicalNumericIndexString(Isolate* isolate, Handle<Object> s,
}
} // anonymous namespace
void JSArrayBuffer::Setup(SharedFlag shared,
void JSArrayBuffer::Setup(SharedFlag shared, ResizableFlag resizable,
std::shared_ptr<BackingStore> backing_store) {
clear_padding();
set_bit_field(0);
set_is_shared(shared == SharedFlag::kShared);
set_is_resizable(resizable == ResizableFlag::kResizable);
set_is_detachable(shared != SharedFlag::kShared);
for (int i = 0; i < v8::ArrayBuffer::kEmbedderFieldCount; i++) {
SetEmbedderField(i, Smi::zero());
@ -61,10 +62,17 @@ void JSArrayBuffer::Setup(SharedFlag shared,
void JSArrayBuffer::Attach(std::shared_ptr<BackingStore> backing_store) {
DCHECK_NOT_NULL(backing_store);
DCHECK_EQ(is_shared(), backing_store->is_shared());
DCHECK_EQ(is_resizable(), backing_store->is_resizable());
DCHECK(!was_detached());
Isolate* isolate = GetIsolate();
set_backing_store(isolate, backing_store->buffer_start());
if (is_shared() && is_resizable()) {
// GSABs need to read their byte_length from the BackingStore. Maintain the
// invariant that their byte_length field is always 0.
set_byte_length(0);
} else {
set_byte_length(backing_store->byte_length());
}
if (backing_store->is_wasm_memory()) set_is_detachable(false);
if (!backing_store->free_on_destruct()) set_is_external(true);
Heap* heap = isolate->heap();
@ -154,14 +162,14 @@ void JSArrayBuffer::YoungMarkExtensionPromoted() {
Handle<JSArrayBuffer> JSTypedArray::GetBuffer() {
Isolate* isolate = GetIsolate();
Handle<JSTypedArray> self(*this, isolate);
DCHECK(IsTypedArrayElementsKind(self->GetElementsKind()));
DCHECK(IsTypedArrayOrRabGsabTypedArrayElementsKind(self->GetElementsKind()));
Handle<JSArrayBuffer> array_buffer(JSArrayBuffer::cast(self->buffer()),
isolate);
if (!is_on_heap()) {
// Already is off heap, so return the existing buffer.
return array_buffer;
}
DCHECK(!array_buffer->is_resizable());
// The existing array buffer should be empty.
DCHECK_NULL(array_buffer->backing_store());
@ -182,7 +190,8 @@ Handle<JSArrayBuffer> JSTypedArray::GetBuffer() {
}
// Attach the backing store to the array buffer.
array_buffer->Setup(SharedFlag::kNotShared, std::move(backing_store));
array_buffer->Setup(SharedFlag::kNotShared, ResizableFlag::kNotResizable,
std::move(backing_store));
// Clear the elements of the typed array.
self->set_elements(ReadOnlyRoots(isolate).empty_byte_array());
@ -270,6 +279,7 @@ ExternalArrayType JSTypedArray::type() {
return kExternal##Type##Array;
TYPED_ARRAYS(ELEMENTS_KIND_TO_ARRAY_TYPE)
RAB_GSAB_TYPED_ARRAYS_WITH_TYPED_ARRAY_TYPE(ELEMENTS_KIND_TO_ARRAY_TYPE)
#undef ELEMENTS_KIND_TO_ARRAY_TYPE
default:
@ -277,13 +287,14 @@ ExternalArrayType JSTypedArray::type() {
}
}
size_t JSTypedArray::element_size() {
size_t JSTypedArray::element_size() const {
switch (map().elements_kind()) {
#define ELEMENTS_KIND_TO_ELEMENT_SIZE(Type, type, TYPE, ctype) \
case TYPE##_ELEMENTS: \
return sizeof(ctype);
TYPED_ARRAYS(ELEMENTS_KIND_TO_ELEMENT_SIZE)
RAB_GSAB_TYPED_ARRAYS(ELEMENTS_KIND_TO_ELEMENT_SIZE)
#undef ELEMENTS_KIND_TO_ELEMENT_SIZE
default:
@ -291,5 +302,24 @@ size_t JSTypedArray::element_size() {
}
}
size_t JSTypedArray::LengthTrackingGsabBackedTypedArrayLength(
Isolate* isolate, Address raw_array) {
// TODO(v8:11111): Cache the last seen length in JSArrayBuffer and use it
// in bounds checks to minimize the need for calling this function.
DCHECK(FLAG_harmony_rab_gsab);
DisallowGarbageCollection no_gc;
DisallowJavascriptExecution no_js(isolate);
JSTypedArray array = JSTypedArray::cast(Object(raw_array));
CHECK(array.is_length_tracking());
JSArrayBuffer buffer = array.buffer();
CHECK(buffer.is_resizable());
CHECK(buffer.is_shared());
size_t backing_byte_length =
buffer.GetBackingStore()->byte_length(std::memory_order_seq_cst);
CHECK_GE(backing_byte_length, array.byte_offset());
auto element_byte_size = ElementsKindToByteSize(array.GetElementsKind());
return (backing_byte_length - array.byte_offset()) / element_byte_size;
}
} // namespace internal
} // namespace v8

View File

@ -77,13 +77,18 @@ class JSArrayBuffer
// [is_asmjs_memory]: true => this buffer was once used as asm.js memory.
DECL_BOOLEAN_ACCESSORS(is_asmjs_memory)
// [is_shared]: tells whether this is an ArrayBuffer or a SharedArrayBuffer.
// [is_shared]: true if this is a SharedArrayBuffer or a
// GrowableSharedArrayBuffer.
DECL_BOOLEAN_ACCESSORS(is_shared)
// [is_resizable]: true if this is a ResizableArrayBuffer or a
// GrowableSharedArrayBuffer.
DECL_BOOLEAN_ACCESSORS(is_resizable)
// Initializes the fields of the ArrayBuffer. The provided backing_store can
// be nullptr. If it is not nullptr, then the function registers it with
// src/heap/array-buffer-tracker.h.
V8_EXPORT_PRIVATE void Setup(SharedFlag shared,
V8_EXPORT_PRIVATE void Setup(SharedFlag shared, ResizableFlag resizable,
std::shared_ptr<BackingStore> backing_store);
// Attaches the backing store to an already constructed empty ArrayBuffer.
@ -259,6 +264,9 @@ class JSTypedArray
// eventually.
static constexpr size_t kMaxLength = v8::TypedArray::kMaxLength;
// Bit positions for [bit_field].
DEFINE_TORQUE_GENERATED_JS_TYPED_ARRAY_FLAGS()
// [length]: length of typed array in elements.
DECL_PRIMITIVE_GETTER(length, size_t)
@ -271,7 +279,7 @@ class JSTypedArray
PropertyDescriptor* desc, Maybe<ShouldThrow> should_throw);
ExternalArrayType type();
V8_EXPORT_PRIVATE size_t element_size();
V8_EXPORT_PRIVATE size_t element_size() const;
V8_EXPORT_PRIVATE Handle<JSArrayBuffer> GetBuffer();
@ -296,6 +304,14 @@ class JSTypedArray
inline bool is_on_heap() const;
inline bool is_on_heap(AcquireLoadTag tag) const;
DECL_BOOLEAN_ACCESSORS(is_length_tracking)
DECL_BOOLEAN_ACCESSORS(is_backed_by_rab)
inline bool IsVariableLength() const;
inline size_t GetLength() const;
static size_t LengthTrackingGsabBackedTypedArrayLength(Isolate* isolate,
Address raw_array);
// Note: this is a pointer compression specific optimization.
// Normally, on-heap typed arrays contain HeapObject value in |base_pointer|
// field and an offset in |external_pointer|.
@ -352,6 +368,9 @@ class JSTypedArray
friend class Factory;
DECL_PRIMITIVE_SETTER(length, size_t)
// Reads the "length" field, doesn't assert the TypedArray is not RAB / GSAB
// backed.
inline size_t LengthUnchecked() const;
DECL_GETTER(external_pointer, Address)
DECL_GETTER(external_pointer_raw, ExternalPointer_t)

View File

@ -8,11 +8,13 @@ bitfield struct JSArrayBufferFlags extends uint32 {
was_detached: bool: 1 bit;
is_asm_js_memory: bool: 1 bit;
is_shared: bool: 1 bit;
is_resizable: bool: 1 bit;
}
@generateCppClass
extern class JSArrayBuffer extends JSObject {
byte_length: uintptr;
max_byte_length: uintptr;
backing_store: ExternalPointer;
extension: RawPtr;
bit_field: JSArrayBufferFlags;
@ -29,10 +31,16 @@ macro IsDetachedBuffer(buffer: JSArrayBuffer): bool {
return buffer.bit_field.was_detached;
}
@export
macro IsSharedArrayBuffer(buffer: JSArrayBuffer): bool {
return buffer.bit_field.is_shared;
}
@export
macro IsResizableArrayBuffer(buffer: JSArrayBuffer): bool {
return buffer.bit_field.is_resizable;
}
@abstract
@generateCppClass
extern class JSArrayBufferView extends JSObject {
@ -41,11 +49,35 @@ extern class JSArrayBufferView extends JSObject {
byte_length: uintptr;
}
// We have 4 different TypedArrays:
// 1) Normal (backed by AB / SAB) or non-length tracking backed by GSAB (can't
// go oob once constructed) 2) Non-length tracking backed by RAB (can go oob
// once constructed) 3) Length-tracking backed by RAB (JSArrayBuffer stores the
// length) 4) Length-tracking backed by GSAB (BackingStore stores the length)
bitfield struct JSTypedArrayFlags extends uint32 {
is_length_tracking: bool: 1 bit;
is_backed_by_rab: bool: 1 bit;
}
@generateCppClass
extern class JSTypedArray extends JSArrayBufferView {
length: uintptr;
external_pointer: ExternalPointer;
base_pointer: ByteArray|Smi;
bit_field: JSTypedArrayFlags;
// Pads header size to be a multiple of kTaggedSize.
@if(TAGGED_SIZE_8_BYTES) optional_padding: uint32;
@ifnot(TAGGED_SIZE_8_BYTES) optional_padding: void;
}
@export
macro IsVariableLengthTypedArray(array: JSTypedArray): bool {
return array.bit_field.is_length_tracking || array.bit_field.is_backed_by_rab;
}
@export
macro IsLengthTrackingTypedArray(array: JSTypedArray): bool {
return array.bit_field.is_length_tracking;
}
@generateCppClass

View File

@ -796,6 +796,55 @@ MaybeHandle<Map> JSFunction::GetDerivedMap(Isolate* isolate,
return map;
}
Handle<Map> JSFunction::GetDerivedRabGsabMap(Isolate* isolate,
Handle<JSFunction> constructor,
Handle<JSReceiver> new_target) {
{
DisallowHeapAllocation no_alloc;
NativeContext context = isolate->context().native_context();
if (*new_target == context.uint8_array_fun()) {
return handle(context.rab_gsab_uint8_array_map(), isolate);
}
if (*new_target == context.int8_array_fun()) {
return handle(context.rab_gsab_int8_array_map(), isolate);
}
if (*new_target == context.uint16_array_fun()) {
return handle(context.rab_gsab_uint16_array_map(), isolate);
}
if (*new_target == context.int16_array_fun()) {
return handle(context.rab_gsab_int16_array_map(), isolate);
}
if (*new_target == context.uint32_array_fun()) {
return handle(context.rab_gsab_uint32_array_map(), isolate);
}
if (*new_target == context.int32_array_fun()) {
return handle(context.rab_gsab_int32_array_map(), isolate);
}
if (*new_target == context.float32_array_fun()) {
return handle(context.rab_gsab_float32_array_map(), isolate);
}
if (*new_target == context.float64_array_fun()) {
return handle(context.rab_gsab_float64_array_map(), isolate);
}
if (*new_target == context.biguint64_array_fun()) {
return handle(context.rab_gsab_biguint64_array_map(), isolate);
}
if (*new_target == context.bigint64_array_fun()) {
return handle(context.rab_gsab_bigint64_array_map(), isolate);
}
}
// This only happens when subclassing TypedArrays. Create a new map with the
// corresponding RAB / GSAB ElementsKind. Note: the map is not cached and
// reused -> every array gets a unique map, making ICs slow.
Handle<Map> map =
GetDerivedMap(isolate, constructor, new_target).ToHandleChecked();
Handle<Map> rab_gsab_map = Map::Copy(isolate, map, "RAB / GSAB");
rab_gsab_map->set_elements_kind(
GetCorrespondingRabGsabElementsKind(map->elements_kind()));
return rab_gsab_map;
}
int JSFunction::ComputeInstanceSizeWithMinSlack(Isolate* isolate) {
CHECK(has_initial_map());
if (initial_map().IsInobjectSlackTrackingInProgress()) {

View File

@ -237,6 +237,11 @@ class JSFunction : public JSFunctionOrBoundFunction {
Isolate* isolate, Handle<JSFunction> constructor,
Handle<JSReceiver> new_target);
// Like GetDerivedMap, but returns a map with a RAB / GSAB ElementsKind.
static V8_WARN_UNUSED_RESULT Handle<Map> GetDerivedRabGsabMap(
Isolate* isolate, Handle<JSFunction> constructor,
Handle<JSReceiver> new_target);
// Get and set the prototype property on a JSFunction. If the
// function has an initial map the prototype is set on the initial
// map. Otherwise, the prototype is put in the initial map field

View File

@ -4220,10 +4220,15 @@ bool JSObject::HasEnumerableElements() {
}
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) case TYPE##_ELEMENTS:
TYPED_ARRAYS(TYPED_ARRAY_CASE)
TYPED_ARRAYS(TYPED_ARRAY_CASE) {
size_t length = JSTypedArray::cast(object).length();
return length > 0;
}
RAB_GSAB_TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
{
size_t length = JSTypedArray::cast(object).length();
size_t length = JSTypedArray::cast(object).GetLength();
return length > 0;
}
case DICTIONARY_ELEMENTS: {
@ -5025,6 +5030,7 @@ int JSObject::GetFastElementsUsage() {
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) case TYPE##_ELEMENTS:
TYPED_ARRAYS(TYPED_ARRAY_CASE)
RAB_GSAB_TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
UNREACHABLE();
}

View File

@ -67,10 +67,15 @@ macro GetDerivedMap(implicit context: Context)(
return map;
} label SlowPath {
return runtime::GetDerivedMap(context, target, newTarget);
return runtime::GetDerivedMap(context, target, newTarget, FalseConstant());
}
}
macro GetDerivedRabGsabMap(implicit context: Context)(
target: JSFunction, newTarget: JSReceiver): Map {
return runtime::GetDerivedMap(context, target, newTarget, TrueConstant());
}
macro AllocateFastOrSlowJSObjectFromMap(implicit context: Context)(map: Map):
JSObject {
let properties: EmptyFixedArray|NameDictionary|SwissNameDictionary =

View File

@ -169,7 +169,8 @@ Handle<Name> LookupIterator::GetName() {
bool LookupIterator::IsElement(JSReceiver object) const {
return index_ <= JSObject::kMaxElementIndex ||
(index_ != kInvalidIndex && object.map().has_typed_array_elements());
(index_ != kInvalidIndex &&
object.map().has_typed_array_or_rab_gsab_typed_array_elements());
}
bool LookupIterator::is_dictionary_holder() const {

View File

@ -239,7 +239,7 @@ FixedArrayBase Map::GetInitialElements() const {
if (has_fast_elements() || has_fast_string_wrapper_elements() ||
has_any_nonextensible_elements()) {
result = GetReadOnlyRoots().empty_fixed_array();
} else if (has_typed_array_elements()) {
} else if (has_typed_array_or_rab_gsab_typed_array_elements()) {
result = GetReadOnlyRoots().empty_byte_array();
} else if (has_dictionary_elements()) {
result = GetReadOnlyRoots().empty_slow_element_dictionary();
@ -593,6 +593,14 @@ bool Map::has_typed_array_elements() const {
return IsTypedArrayElementsKind(elements_kind());
}
bool Map::has_rab_gsab_typed_array_elements() const {
return IsRabGsabTypedArrayElementsKind(elements_kind());
}
bool Map::has_typed_array_or_rab_gsab_typed_array_elements() const {
return IsTypedArrayOrRabGsabTypedArrayElementsKind(elements_kind());
}
bool Map::has_dictionary_elements() const {
return IsDictionaryElementsKind(elements_kind());
}

View File

@ -151,8 +151,7 @@ using MapHandles = std::vector<Handle<Map>>;
// | Byte | [bit_field2] |
// | | - new_target_is_base (bit 0) |
// | | - is_immutable_proto (bit 1) |
// | | - unused bit (bit 2) |
// | | - elements_kind (bits 3..7) |
// | | - elements_kind (bits 2..7) |
// +----+----------+-------------------------------------------------+
// | Int | [bit_field3] |
// | | - enum_length (bit 0..9) |
@ -416,6 +415,8 @@ class Map : public HeapObject {
inline bool has_fast_sloppy_arguments_elements() const;
inline bool has_fast_string_wrapper_elements() const;
inline bool has_typed_array_elements() const;
inline bool has_rab_gsab_typed_array_elements() const;
inline bool has_typed_array_or_rab_gsab_typed_array_elements() const;
inline bool has_dictionary_elements() const;
inline bool has_any_nonextensible_elements() const;
inline bool has_nonextensible_elements() const;

View File

@ -16,8 +16,7 @@ bitfield struct MapBitFields1 extends uint8 {
bitfield struct MapBitFields2 extends uint8 {
new_target_is_base: bool: 1 bit;
is_immutable_prototype: bool: 1 bit;
unused: bool: 1 bit;
elements_kind: ElementsKind: 5 bit;
elements_kind: ElementsKind: 6 bit;
}
bitfield struct MapBitFields3 extends uint32 {

View File

@ -2812,6 +2812,7 @@ Maybe<bool> Object::SetDataProperty(LookupIterator* it, Handle<Object> value) {
Handle<Object> to_assign = value;
// Convert the incoming value to a number for storing into typed arrays.
// TODO(v8:11111): Support RAB / GSAB.
if (it->IsElement() && receiver->IsJSObject() &&
JSObject::cast(*receiver).HasTypedArrayElements()) {
ElementsKind elements_kind = JSObject::cast(*receiver).GetElementsKind();

View File

@ -140,6 +140,7 @@ const char* ElementsKindToType(ElementsKind fixed_elements_kind) {
return #Type "Array";
TYPED_ARRAYS(ELEMENTS_KIND_CASE)
RAB_GSAB_TYPED_ARRAYS_WITH_TYPED_ARRAY_TYPE(ELEMENTS_KIND_CASE)
#undef ELEMENTS_KIND_CASE
default:

View File

@ -218,6 +218,7 @@ MaybeHandle<JSObject> JSObjectWalkVisitor<ContextObject>::StructureWalk(
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) case TYPE##_ELEMENTS:
TYPED_ARRAYS(TYPED_ARRAY_CASE)
RAB_GSAB_TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
// Typed elements cannot be created using an object literal.
UNREACHABLE();

View File

@ -972,11 +972,16 @@ RUNTIME_FUNCTION(Runtime_NewObject) {
RUNTIME_FUNCTION(Runtime_GetDerivedMap) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
DCHECK_EQ(3, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSFunction, target, 0);
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, new_target, 1);
CONVERT_ARG_HANDLE_CHECKED(Object, rab_gsab, 2);
if (rab_gsab->IsTrue()) {
return *JSFunction::GetDerivedRabGsabMap(isolate, target, new_target);
} else {
RETURN_RESULT_OR_FAILURE(
isolate, JSFunction::GetDerivedMap(isolate, target, new_target));
}
}
RUNTIME_FUNCTION(Runtime_CompleteInobjectSlackTrackingForMap) {

View File

@ -53,6 +53,15 @@ RUNTIME_FUNCTION(Runtime_TypedArrayGetBuffer) {
return *holder->GetBuffer();
}
RUNTIME_FUNCTION(Runtime_GrowableSharedArrayBufferByteLength) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, array_buffer, 0);
CHECK_EQ(0, array_buffer->byte_length());
size_t byte_length = array_buffer->GetBackingStore()->byte_length();
return *isolate->factory()->NewNumberFromSize(byte_length);
}
namespace {

View File

@ -558,6 +558,7 @@ namespace internal {
#define FOR_EACH_INTRINSIC_TYPEDARRAY(F, I) \
F(ArrayBufferDetach, 1, 1) \
F(GrowableSharedArrayBufferByteLength, 1, 1) \
F(TypedArrayCopyElements, 3, 1) \
F(TypedArrayGetBuffer, 1, 1) \
F(TypedArraySet, 2, 1) \

View File

@ -62,9 +62,11 @@ void ContextDeserializer::SetupOffHeapArrayBufferBackingStores() {
uint32_t store_index = buffer->GetBackingStoreRefForDeserialization();
auto bs = backing_store(store_index);
buffer->AllocateExternalPointerEntries(isolate());
// TODO(v8:11111): Support RAB / GSAB.
CHECK(!buffer->is_resizable());
SharedFlag shared =
bs && bs->is_shared() ? SharedFlag::kShared : SharedFlag::kNotShared;
buffer->Setup(shared, bs);
buffer->Setup(shared, ResizableFlag::kNotResizable, bs);
}
}

View File

@ -66,7 +66,9 @@ void ObjectDeserializer::CommitPostProcessedObjects() {
auto bs = backing_store(store_index);
SharedFlag shared =
bs && bs->is_shared() ? SharedFlag::kShared : SharedFlag::kNotShared;
buffer->Setup(shared, bs);
// TODO(v8:11111): Support RAB / GSAB.
CHECK(!bs || !bs->is_resizable());
buffer->Setup(shared, ResizableFlag::kNotResizable, bs);
}
for (Handle<Script> script : new_scripts()) {

View File

@ -83,7 +83,7 @@ bytecodes: [
B(Mov), R(this), R(0),
B(Mov), R(context), R(2),
/* 48 E> */ B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(0), U8(3),
/* 53 S> */ B(Wide), B(LdaSmi), I16(276),
/* 53 S> */ B(Wide), B(LdaSmi), I16(278),
B(Star3),
B(LdaConstant), U8(0),
B(Star4),
@ -114,7 +114,7 @@ bytecodes: [
B(Mov), R(this), R(0),
B(Mov), R(context), R(2),
/* 41 E> */ B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(0), U8(3),
/* 46 S> */ B(Wide), B(LdaSmi), I16(275),
/* 46 S> */ B(Wide), B(LdaSmi), I16(277),
B(Star3),
B(LdaConstant), U8(0),
B(Star4),
@ -145,7 +145,7 @@ bytecodes: [
B(Mov), R(this), R(0),
B(Mov), R(context), R(2),
/* 48 E> */ B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(0), U8(3),
/* 53 S> */ B(Wide), B(LdaSmi), I16(276),
/* 53 S> */ B(Wide), B(LdaSmi), I16(278),
B(Star3),
B(LdaConstant), U8(0),
B(Star4),
@ -176,7 +176,7 @@ bytecodes: [
B(Mov), R(this), R(0),
B(Mov), R(context), R(2),
/* 41 E> */ B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(0), U8(3),
/* 46 S> */ B(Wide), B(LdaSmi), I16(275),
/* 46 S> */ B(Wide), B(LdaSmi), I16(277),
B(Star4),
B(LdaConstant), U8(0),
B(Star5),

View File

@ -56,7 +56,7 @@ bytecodes: [
B(Mov), R(this), R(0),
B(Mov), R(context), R(2),
/* 44 E> */ B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(0), U8(3),
/* 49 S> */ B(Wide), B(LdaSmi), I16(274),
/* 49 S> */ B(Wide), B(LdaSmi), I16(276),
B(Star3),
B(LdaConstant), U8(0),
B(Star4),
@ -88,7 +88,7 @@ bytecodes: [
B(Mov), R(this), R(0),
B(Mov), R(context), R(2),
/* 44 E> */ B(CallRuntime), U16(Runtime::kAddPrivateBrand), R(0), U8(3),
/* 49 S> */ B(Wide), B(LdaSmi), I16(274),
/* 49 S> */ B(Wide), B(LdaSmi), I16(276),
B(Star3),
B(LdaConstant), U8(0),
B(Star4),

View File

@ -24,7 +24,7 @@ bytecodes: [
B(TestReferenceEqual), R(this),
B(Mov), R(this), R(1),
B(JumpIfTrue), U8(16),
B(Wide), B(LdaSmi), I16(272),
B(Wide), B(LdaSmi), I16(274),
B(Star2),
B(LdaConstant), U8(0),
B(Star3),
@ -55,7 +55,7 @@ frame size: 2
parameter count: 1
bytecode array length: 14
bytecodes: [
/* 56 S> */ B(Wide), B(LdaSmi), I16(274),
/* 56 S> */ B(Wide), B(LdaSmi), I16(276),
B(Star0),
B(LdaConstant), U8(0),
B(Star1),
@ -82,7 +82,7 @@ frame size: 2
parameter count: 1
bytecode array length: 14
bytecodes: [
/* 56 S> */ B(Wide), B(LdaSmi), I16(274),
/* 56 S> */ B(Wide), B(LdaSmi), I16(276),
B(Star0),
B(LdaConstant), U8(0),
B(Star1),
@ -121,7 +121,7 @@ bytecodes: [
B(TestReferenceEqual), R(this),
B(Mov), R(this), R(0),
B(JumpIfTrue), U8(16),
B(Wide), B(LdaSmi), I16(272),
B(Wide), B(LdaSmi), I16(274),
B(Star2),
B(LdaConstant), U8(0),
B(Star3),
@ -143,7 +143,7 @@ bytecodes: [
B(TestReferenceEqual), R(this),
B(Mov), R(this), R(1),
B(JumpIfTrue), U8(16),
B(Wide), B(LdaSmi), I16(273),
B(Wide), B(LdaSmi), I16(275),
B(Star3),
B(LdaConstant), U8(0),
B(Star4),
@ -158,7 +158,7 @@ bytecodes: [
B(TestReferenceEqual), R(this),
B(Mov), R(this), R(0),
B(JumpIfTrue), U8(16),
B(Wide), B(LdaSmi), I16(272),
B(Wide), B(LdaSmi), I16(274),
B(Star2),
B(LdaConstant), U8(0),
B(Star3),
@ -188,7 +188,7 @@ frame size: 2
parameter count: 1
bytecode array length: 14
bytecodes: [
/* 60 S> */ B(Wide), B(LdaSmi), I16(276),
/* 60 S> */ B(Wide), B(LdaSmi), I16(278),
B(Star0),
B(LdaConstant), U8(0),
B(Star1),
@ -214,7 +214,7 @@ frame size: 2
parameter count: 1
bytecode array length: 14
bytecodes: [
/* 53 S> */ B(Wide), B(LdaSmi), I16(275),
/* 53 S> */ B(Wide), B(LdaSmi), I16(277),
B(Star0),
B(LdaConstant), U8(0),
B(Star1),
@ -240,7 +240,7 @@ frame size: 2
parameter count: 1
bytecode array length: 14
bytecodes: [
/* 60 S> */ B(Wide), B(LdaSmi), I16(276),
/* 60 S> */ B(Wide), B(LdaSmi), I16(278),
B(Star0),
B(LdaConstant), U8(0),
B(Star1),
@ -266,7 +266,7 @@ frame size: 3
parameter count: 1
bytecode array length: 14
bytecodes: [
/* 46 S> */ B(Wide), B(LdaSmi), I16(275),
/* 46 S> */ B(Wide), B(LdaSmi), I16(277),
B(Star1),
B(LdaConstant), U8(0),
B(Star2),

View File

@ -0,0 +1,430 @@
// Copyright 2021 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: --harmony-rab-gsab --allow-natives-syntax
"use strict";
function resizeHelper(ab, value) {
const return_value = ab.resize(value);
assertEquals(undefined, return_value);
assertEquals(value, ab.byteLength);
}
function growHelper(ab, value) {
const return_value = ab.grow(value);
assertEquals(undefined, return_value);
assertEquals(value, ab.byteLength);
}
(function TestRABBasics() {
const rab = new ResizableArrayBuffer(10, 20);
assertTrue(rab instanceof ResizableArrayBuffer);
assertFalse(rab instanceof GrowableSharedArrayBuffer);
assertFalse(rab instanceof ArrayBuffer);
assertFalse(rab instanceof SharedArrayBuffer);
assertEquals(10, rab.byteLength);
assertEquals(20, rab.maxByteLength);
})();
(function TestRABCtorByteLengthEqualsMax() {
const rab = new ResizableArrayBuffer(10, 10);
assertEquals(10, rab.byteLength);
assertEquals(10, rab.maxByteLength);
})();
(function TestRABCtorByteLengthZero() {
const rab = new ResizableArrayBuffer(0, 10);
assertEquals(0, rab.byteLength);
assertEquals(10, rab.maxByteLength);
})();
(function TestRABCtorByteLengthAndMaxZero() {
const rab = new ResizableArrayBuffer(0, 0);
assertEquals(0, rab.byteLength);
assertEquals(0, rab.maxByteLength);
})();
(function TestRABCtorNoMaxByteLength() {
assertThrows(() => { new ResizableArrayBuffer(10); }, RangeError);
// But this is fine; undefined is converted to 0.
const rab = new ResizableArrayBuffer(0);
assertEquals(0, rab.byteLength);
assertEquals(0, rab.maxByteLength);
})();
(function TestAllocatingOutrageouslyMuchThrows() {
assertThrows(() => { new ResizableArrayBuffer(0, 2 ** 100);}, RangeError);
})();
(function TestRABCtorOperationOrder() {
let log = '';
const mock_length = {valueOf: function() {
log += 'valueof length, '; return 10; }};
const mock_max_length = {valueOf: function() {
log += 'valueof max_length, '; return 10; }};
new ResizableArrayBuffer(mock_length, mock_max_length);
assertEquals('valueof length, valueof max_length, ', log);
})();
(function TestGSABCtorOperationOrder() {
let log = '';
const mock_length = {valueOf: function() {
log += 'valueof length, '; return 10; }};
const mock_max_length = {valueOf: function() {
log += 'valueof max_length, '; return 10; }};
new ResizableArrayBuffer(mock_length, mock_max_length);
assertEquals('valueof length, valueof max_length, ', log);
})();
(function TestByteLengthGetterReceiverChecks() {
const name = 'byteLength';
const ab_getter = Object.getOwnPropertyDescriptor(
ArrayBuffer.prototype, name).get;
const sab_getter = Object.getOwnPropertyDescriptor(
SharedArrayBuffer.prototype, name).get;
const rab_getter = Object.getOwnPropertyDescriptor(
ResizableArrayBuffer.prototype, name).get;
const gsab_getter = Object.getOwnPropertyDescriptor(
GrowableSharedArrayBuffer.prototype, name).get;
const ab = new ArrayBuffer(40);
const sab = new SharedArrayBuffer(40);
const rab = new ResizableArrayBuffer(40, 40);
const gsab = new GrowableSharedArrayBuffer(40, 40);
assertEquals(40, ab_getter.call(ab));
assertEquals(40, sab_getter.call(sab));
assertEquals(40, rab_getter.call(rab));
assertEquals(40, gsab_getter.call(gsab));
assertThrows(() => { ab_getter.call(sab);});
assertThrows(() => { ab_getter.call(rab);});
assertThrows(() => { ab_getter.call(gsab);});
assertThrows(() => { sab_getter.call(ab);});
assertThrows(() => { sab_getter.call(rab);});
assertThrows(() => { sab_getter.call(gsab);});
assertThrows(() => { rab_getter.call(ab);});
assertThrows(() => { rab_getter.call(sab);});
assertThrows(() => { rab_getter.call(gsab);});
assertThrows(() => { gsab_getter.call(ab);});
assertThrows(() => { gsab_getter.call(sab);});
assertThrows(() => { gsab_getter.call(rab);});
})();
(function TestMaxByteLengthGetterReceiverChecks() {
const name = 'maxByteLength';
const rab_getter = Object.getOwnPropertyDescriptor(
ResizableArrayBuffer.prototype, name).get;
const gsab_getter = Object.getOwnPropertyDescriptor(
GrowableSharedArrayBuffer.prototype, name).get;
const ab = new ArrayBuffer(40);
const sab = new SharedArrayBuffer(40);
const rab = new ResizableArrayBuffer(20, 40);
const gsab = new GrowableSharedArrayBuffer(20, 40);
assertEquals(40, rab_getter.call(rab));
assertEquals(40, gsab_getter.call(gsab));
assertThrows(() => { rab_getter.call(ab);});
assertThrows(() => { rab_getter.call(sab);});
assertThrows(() => { rab_getter.call(gsab);});
assertThrows(() => { gsab_getter.call(ab);});
assertThrows(() => { gsab_getter.call(sab);});
assertThrows(() => { gsab_getter.call(rab);});
})();
(function TestResizeAndGrowReceiverChecks() {
const rab_resize = ResizableArrayBuffer.prototype.resize;
const gsab_grow = GrowableSharedArrayBuffer.prototype.grow;
const ab = new ArrayBuffer(40);
const sab = new SharedArrayBuffer(40);
const rab = new ResizableArrayBuffer(10, 40);
const gsab = new GrowableSharedArrayBuffer(10, 40);
rab_resize.call(rab, 20);
gsab_grow.call(gsab, 20);
assertThrows(() => { rab_resize.call(ab, 30);});
assertThrows(() => { rab_resize.call(sab, 30);});
assertThrows(() => { rab_resize.call(gsab, 30);});
assertThrows(() => { gsab_grow.call(ab, 30);});
assertThrows(() => { gsab_grow.call(sab, 30);});
assertThrows(() => { gsab_grow.call(rab, 30);});
})();
(function TestRABResizeToMax() {
const rab = new ResizableArrayBuffer(10, 20);
resizeHelper(rab, 20);
})();
(function TestRABResizeToSameSize() {
const rab = new ResizableArrayBuffer(10, 20);
resizeHelper(rab, 10);
})();
(function TestRABResizeToSmaller() {
const rab = new ResizableArrayBuffer(10, 20);
resizeHelper(rab, 5);
})();
(function TestRABResizeToZero() {
const rab = new ResizableArrayBuffer(10, 20);
resizeHelper(rab, 0);
})();
(function TestRABResizeZeroToZero() {
const rab = new ResizableArrayBuffer(0, 20);
resizeHelper(rab, 0);
})();
(function TestRABGrowBeyondMaxThrows() {
const rab = new ResizableArrayBuffer(10, 20);
assertEquals(10, rab.byteLength);
assertThrows(() => {rab.grow(21)});
assertEquals(10, rab.byteLength);
})();
(function TestRABResizeMultipleTimes() {
const rab = new ResizableArrayBuffer(10, 20);
const sizes = [15, 7, 7, 0, 8, 20, 20, 10];
for (let s of sizes) {
resizeHelper(rab, s);
}
})();
(function TestRABResizeParameters() {
const rab = new ResizableArrayBuffer(10, 20);
rab.resize('15');
assertEquals(15, rab.byteLength);
rab.resize({valueOf: function() { return 16; }});
assertEquals(16, rab.byteLength);
rab.resize(17.9);
assertEquals(17, rab.byteLength);
})();
(function TestRABResizeInvalidParameters() {
const rab = new ResizableArrayBuffer(10, 20);
assertThrows(() => { rab.resize(-1) }, RangeError);
assertThrows(() => { rab.resize({valueOf: function() {
throw new Error('length param'); }})});
assertEquals(10, rab.byteLength);
// Various non-numbers are converted to NaN which is treated as 0.
rab.resize('string');
assertEquals(0, rab.byteLength);
rab.resize();
assertEquals(0, rab.byteLength);
})();
(function TestRABResizeDetached() {
const rab = new ResizableArrayBuffer(10, 20);
%ArrayBufferDetach(rab);
assertThrows(() => { rab.resize(15) }, TypeError);
})();
(function DetachInsideResizeParameterConversion() {
const rab = new ResizableArrayBuffer(40, 80);
const evil = {
valueOf: () => { %ArrayBufferDetach(rab); return 20; }
};
assertThrows(() => { rab.resize(evil); });
})();
(function ResizeInsideResizeParameterConversion() {
const rab = new ResizableArrayBuffer(40, 80);
const evil = {
valueOf: () => { rab.resize(10); return 20; }
};
rab.resize(evil);
assertEquals(20, rab.byteLength);
})();
(function TestRABNewMemoryAfterResizeInitializedToZero() {
const maybe_page_size = 4096;
const rab = new ResizableArrayBuffer(maybe_page_size, 2 * maybe_page_size);
const i8a = new Int8Array(rab);
rab.resize(2 * maybe_page_size);
for (let i = 0; i < 2 * maybe_page_size; ++i) {
assertEquals(0, i8a[i]);
}
})();
(function TestRABMemoryInitializedToZeroAfterShrinkAndGrow() {
const maybe_page_size = 4096;
const rab = new ResizableArrayBuffer(maybe_page_size, 2 * maybe_page_size);
const i8a = new Int8Array(rab);
for (let i = 0; i < maybe_page_size; ++i) {
i8a[i] = 1;
}
rab.resize(maybe_page_size / 2);
rab.resize(maybe_page_size);
for (let i = maybe_page_size / 2; i < maybe_page_size; ++i) {
assertEquals(0, i8a[i]);
}
})();
(function TestGSABBasics() {
const gsab = new GrowableSharedArrayBuffer(10, 20);
assertFalse(gsab instanceof ResizableArrayBuffer);
assertTrue(gsab instanceof GrowableSharedArrayBuffer);
assertFalse(gsab instanceof ArrayBuffer);
assertFalse(gsab instanceof SharedArrayBuffer);
assertEquals(10, gsab.byteLength);
assertEquals(20, gsab.maxByteLength);
})();
(function TestGSABCtorByteLengthEqualsMax() {
const gsab = new GrowableSharedArrayBuffer(10, 10);
assertEquals(10, gsab.byteLength);
assertEquals(10, gsab.maxByteLength);
})();
(function TestGSABCtorByteLengthZero() {
const gsab = new GrowableSharedArrayBuffer(0, 10);
assertEquals(0, gsab.byteLength);
assertEquals(10, gsab.maxByteLength);
})();
(function TestGSABCtorByteLengthAndMaxZero() {
const gsab = new GrowableSharedArrayBuffer(0, 0);
assertEquals(0, gsab.byteLength);
assertEquals(0, gsab.maxByteLength);
})();
(function TestGSABCtorNoMaxByteLength() {
assertThrows(() => { new GrowableSharedArrayBuffer(10); }, RangeError);
// But this is fine; undefined is converted to 0.
const gsab = new GrowableSharedArrayBuffer(0);
assertEquals(0, gsab.byteLength);
assertEquals(0, gsab.maxByteLength);
})();
(function TestAllocatingOutrageouslyMuchThrows() {
assertThrows(() => { new GrowableSharedArrayBuffer(0, 2 ** 100);},
RangeError);
})();
(function TestGSABGrowToMax() {
const gsab = new GrowableSharedArrayBuffer(10, 20);
assertEquals(10, gsab.byteLength);
growHelper(gsab, 20);
})();
(function TestGSABGrowToSameSize() {
const gsab = new GrowableSharedArrayBuffer(10, 20);
assertEquals(10, gsab.byteLength);
growHelper(gsab, 10);
})();
(function TestGSABGrowToSmallerThrows() {
const gsab = new GrowableSharedArrayBuffer(10, 20);
assertEquals(10, gsab.byteLength);
assertThrows(() => {gsab.grow(5)});
assertEquals(10, gsab.byteLength);
})();
(function TestGSABGrowToZeroThrows() {
const gsab = new GrowableSharedArrayBuffer(10, 20);
assertEquals(10, gsab.byteLength);
assertThrows(() => {gsab.grow(0)});
assertEquals(10, gsab.byteLength);
})();
(function TestGSABGrowBeyondMaxThrows() {
const gsab = new GrowableSharedArrayBuffer(10, 20);
assertEquals(10, gsab.byteLength);
assertThrows(() => {gsab.grow(21)});
assertEquals(10, gsab.byteLength);
})();
(function TestGSABGrowMultipleTimes() {
const gsab = new GrowableSharedArrayBuffer(10, 20);
assertEquals(10, gsab.byteLength);
const sizes = [15, 7, 7, 0, 8, 20, 20, 10];
for (let s of sizes) {
const current_size = gsab.byteLength;
if (s < gsab.byteLength) {
assertThrows(() => {gsab.grow(s)});
assertEquals(current_size, gsab.byteLength);
} else {
growHelper(gsab, s);
}
}
})();
(function TestGSABGrowParameters() {
const gsab = new GrowableSharedArrayBuffer(10, 20);
gsab.grow('15');
assertEquals(15, gsab.byteLength);
gsab.grow({valueOf: function() { return 16; }});
assertEquals(16, gsab.byteLength);
gsab.grow(17.9);
assertEquals(17, gsab.byteLength);
})();
(function TestGSABGrowInvalidParameters() {
const gsab = new GrowableSharedArrayBuffer(0, 20);
assertThrows(() => { gsab.grow(-1) }, RangeError);
assertThrows(() => { gsab.grow({valueOf: function() {
throw new Error('length param'); }})});
assertEquals(0, gsab.byteLength);
// Various non-numbers are converted to NaN which is treated as 0.
gsab.grow('string');
assertEquals(0, gsab.byteLength);
gsab.grow();
assertEquals(0, gsab.byteLength);
})();
(function TestGSABMemoryInitializedToZeroAfterGrow() {
const maybe_page_size = 4096;
const gsab = new GrowableSharedArrayBuffer(maybe_page_size,
2 * maybe_page_size);
const i8a = new Int8Array(gsab);
gsab.grow(2 * maybe_page_size);
assertEquals(2 * maybe_page_size, i8a.length);
for (let i = 0; i < 2 * maybe_page_size; ++i) {
assertEquals(0, i8a[i]);
}
})();
(function GrowGSABOnADifferentThread() {
const gsab = new GrowableSharedArrayBuffer(10, 20);
assertEquals(10, gsab.byteLength);
function workerCode() {
function assert(thing) {
if (!thing) {
postMessage('error');
}
}
onmessage = function(params) {
const gsab = params.gsab;
assert(!(gsab instanceof ResizableArrayBuffer));
assert(gsab instanceof GrowableSharedArrayBuffer);
assert(!(gsab instanceof ArrayBuffer));
assert(!(gsab instanceof SharedArrayBuffer));
assert(10 == gsab.byteLength);
gsab.grow(15);
postMessage('ok');
}
}
const w = new Worker(workerCode, {type: 'function'});
w.postMessage({gsab: gsab});
assertEquals('ok', w.getMessage());
assertEquals(15, gsab.byteLength);
})();

View File

@ -0,0 +1,354 @@
// Copyright 2021 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: --harmony-rab-gsab --allow-natives-syntax
"use strict";
class MyUint8Array extends Uint8Array {};
const ctors = [
Uint8Array,
Int8Array,
Uint16Array,
Int16Array,
Int32Array,
Float32Array,
Float64Array,
Uint8ClampedArray,
BigUint64Array,
BigInt64Array,
MyUint8Array
];
(function TypedArrayPrototype() {
const gsab = new GrowableSharedArrayBuffer(40, 80);
const sab = new SharedArrayBuffer(80);
for (let ctor of ctors) {
const ta_gsab = new ctor(gsab, 0, 3);
const ta_sab = new ctor(sab, 0, 3);
assertEquals(ta_gsab.__proto__, ta_sab.__proto__);
}
})();
(function TypedArrayLengthAndByteLength() {
const gsab = new GrowableSharedArrayBuffer(40, 80);
for (let ctor of ctors) {
const ta = new ctor(gsab, 0, 3);
assertEquals(gsab, ta.buffer);
assertEquals(3, ta.length);
assertEquals(3 * ctor.BYTES_PER_ELEMENT, ta.byteLength);
const empty_ta = new ctor(gsab, 0, 0);
assertEquals(gsab, empty_ta.buffer);
assertEquals(0, empty_ta.length);
assertEquals(0, empty_ta.byteLength);
const ta_with_offset = new ctor(gsab, 2 * ctor.BYTES_PER_ELEMENT, 3);
assertEquals(gsab, ta_with_offset.buffer);
assertEquals(3, ta_with_offset.length);
assertEquals(3 * ctor.BYTES_PER_ELEMENT, ta_with_offset.byteLength);
const empty_ta_with_offset = new ctor(gsab, 2 * ctor.BYTES_PER_ELEMENT, 0);
assertEquals(gsab, empty_ta_with_offset.buffer);
assertEquals(0, empty_ta_with_offset.length);
assertEquals(0, empty_ta_with_offset.byteLength);
const length_tracking_ta = new ctor(gsab);
assertEquals(gsab, length_tracking_ta.buffer);
assertEquals(40 / ctor.BYTES_PER_ELEMENT, length_tracking_ta.length);
assertEquals(40, length_tracking_ta.byteLength);
const offset = 8;
const length_tracking_ta_with_offset = new ctor(gsab, offset);
assertEquals(gsab, length_tracking_ta_with_offset.buffer);
assertEquals((40 - offset) / ctor.BYTES_PER_ELEMENT,
length_tracking_ta_with_offset.length);
assertEquals(40 - offset, length_tracking_ta_with_offset.byteLength);
const length_tracking_ta_zero = new ctor(gsab, 40);
assertEquals(gsab, length_tracking_ta_zero.buffer);
assertEquals(0, length_tracking_ta_zero.length);
assertEquals(0, length_tracking_ta_zero.byteLength);
}
})();
(function ConstructInvalid() {
const gsab = new GrowableSharedArrayBuffer(40, 80);
for (let ctor of ctors) {
// Length too big.
assertThrows(() => { new ctor(gsab, 0, 40 / ctor.BYTES_PER_ELEMENT + 1); },
RangeError);
// Offset too close to the end.
assertThrows(() => { new ctor(gsab, 40 - ctor.BYTES_PER_ELEMENT, 2); },
RangeError);
// Offset beyond end.
assertThrows(() => { new ctor(gsab, 40, 1); }, RangeError);
if (ctor.BYTES_PER_ELEMENT > 1) {
// Offset not a multiple of ctor.BYTES_PER_ELEMENT.
assertThrows(() => { new ctor(gsab, 1, 1); }, RangeError);
assertThrows(() => { new ctor(gsab, 1); }, RangeError);
}
}
// Verify the error messages.
assertThrows(() => { new Int16Array(gsab, 1, 1); }, RangeError,
/start offset of Int16Array should be a multiple of 2/);
assertThrows(() => { new Int16Array(gsab, 38, 2); }, RangeError,
/Invalid typed array length: 2/);
})();
(function TypedArrayLengthWhenGrown1() {
const gsab = new GrowableSharedArrayBuffer(16, 40);
// Create TAs which cover the bytes 0-7.
let tas_and_lengths = [];
for (let ctor of ctors) {
const length = 8 / ctor.BYTES_PER_ELEMENT;
tas_and_lengths.push([new ctor(gsab, 0, length), length]);
}
for (let [ta, length] of tas_and_lengths) {
assertEquals(length, ta.length);
assertEquals(length * ta.BYTES_PER_ELEMENT, ta.byteLength);
}
gsab.grow(20);
for (let [ta, length] of tas_and_lengths) {
assertEquals(length, ta.length);
assertEquals(length * ta.BYTES_PER_ELEMENT, ta.byteLength);
}
gsab.grow(40);
for (let [ta, length] of tas_and_lengths) {
assertEquals(length, ta.length);
assertEquals(length * ta.BYTES_PER_ELEMENT, ta.byteLength);
}
})();
// The previous test with offsets.
(function TypedArrayLengthWhenGrown2() {
const gsab = new GrowableSharedArrayBuffer(20, 40);
// Create TAs which cover the bytes 8-15.
let tas_and_lengths = [];
for (let ctor of ctors) {
const length = 8 / ctor.BYTES_PER_ELEMENT;
tas_and_lengths.push([new ctor(gsab, 8, length), length]);
}
for (let [ta, length] of tas_and_lengths) {
assertEquals(length, ta.length);
assertEquals(length * ta.BYTES_PER_ELEMENT, ta.byteLength);
}
gsab.grow(20);
for (let [ta, length] of tas_and_lengths) {
assertEquals(length, ta.length);
assertEquals(length * ta.BYTES_PER_ELEMENT, ta.byteLength);
}
gsab.grow(40);
for (let [ta, length] of tas_and_lengths) {
assertEquals(length, ta.length);
assertEquals(length * ta.BYTES_PER_ELEMENT, ta.byteLength);
}
})();
(function LengthTracking1() {
const gsab = new GrowableSharedArrayBuffer(16, 40);
let tas = [];
for (let ctor of ctors) {
tas.push(new ctor(gsab));
}
for (let ta of tas) {
assertEquals(16 / ta.BYTES_PER_ELEMENT, ta.length);
assertEquals(16, ta.byteLength);
}
gsab.grow(24);
for (let ta of tas) {
assertEquals(24 / ta.BYTES_PER_ELEMENT, ta.length);
assertEquals(24, ta.byteLength);
}
// Grow to a number which is not a multiple of all byte_lengths.
gsab.grow(26);
for (let ta of tas) {
const expected_length = Math.floor(26 / ta.BYTES_PER_ELEMENT);
assertEquals(expected_length, ta.length);
assertEquals(expected_length * ta.BYTES_PER_ELEMENT, ta.byteLength);
}
gsab.grow(40);
for (let ta of tas) {
assertEquals(40 / ta.BYTES_PER_ELEMENT, ta.length);
assertEquals(40, ta.byteLength);
}
})();
// The previous test with offsets.
(function LengthTracking2() {
const gsab = new GrowableSharedArrayBuffer(16, 40);
const offset = 8;
let tas = [];
for (let ctor of ctors) {
tas.push(new ctor(gsab, offset));
}
for (let ta of tas) {
assertEquals((16 - offset) / ta.BYTES_PER_ELEMENT, ta.length);
assertEquals(16 - offset, ta.byteLength);
}
gsab.grow(24);
for (let ta of tas) {
assertEquals((24 - offset) / ta.BYTES_PER_ELEMENT, ta.length);
assertEquals(24 - offset, ta.byteLength);
}
// Grow to a number which is not a multiple of all byte_lengths.
gsab.grow(26);
for (let ta of tas) {
const expected_length = Math.floor((26 - offset)/ ta.BYTES_PER_ELEMENT);
assertEquals(expected_length, ta.length);
assertEquals(expected_length * ta.BYTES_PER_ELEMENT, ta.byteLength);
}
gsab.grow(40);
for (let ta of tas) {
assertEquals((40 - offset) / ta.BYTES_PER_ELEMENT, ta.length);
assertEquals(40 - offset, ta.byteLength);
}
})();
(function LoadWithFeedback() {
function ReadElement2(ta) {
return ta[2];
}
%EnsureFeedbackVectorForFunction(ReadElement2);
const gsab = new GrowableSharedArrayBuffer(16, 40);
const i8a = new Int8Array(gsab, 0, 4);
for (let i = 0; i < 3; ++i) {
assertEquals(0, ReadElement2(i8a));
}
// Within-bounds write
for (let i = 0; i < 4; ++i) {
i8a[i] = i;
}
// Within-bounds read
for (let i = 0; i < 3; ++i) {
assertEquals(2, ReadElement2(i8a));
}
gsab.grow(20);
// Within-bounds read
for (let i = 0; i < 3; ++i) {
assertEquals(2, ReadElement2(i8a));
}
gsab.grow(40);
// Within-bounds read
for (let i = 0; i < 3; ++i) {
assertEquals(2, ReadElement2(i8a));
}
})();
(function LoadAndStoreWithFeedback() {
function ReadElement(ta, i) {
return ta[i];
}
function HasElement(ta, i) {
return i in ta;
}
function WriteElement(ta, i, v) {
ta[i] = v;
}
%EnsureFeedbackVectorForFunction(ReadElement);
%EnsureFeedbackVectorForFunction(HasElement);
%EnsureFeedbackVectorForFunction(WriteElement);
const gsab = new GrowableSharedArrayBuffer(16, 40);
const i8a = new Int8Array(gsab); // length-tracking
assertEquals(16, i8a.length);
// Within-bounds read
for (let i = 0; i < i8a.length; ++i) {
assertEquals(0, ReadElement(i8a, i));
assertTrue(HasElement(i8a, i));
}
assertFalse(HasElement(i8a, 17));
// Within-bounds write
for (let i = 0; i < i8a.length; ++i) {
WriteElement(i8a, i, i);
}
// Within-bounds read
for (let i = 0; i < i8a.length; ++i) {
assertEquals(i, ReadElement(i8a, i));
}
let old_length = i8a.length;
gsab.grow(20);
assertEquals(20, i8a.length);
for (let i = 0; i < i8a.length; ++i) {
if (i < old_length) {
assertEquals(i, ReadElement(i8a, i));
} else {
assertEquals(0, ReadElement(i8a, i));
}
assertTrue(HasElement(i8a, i));
}
assertFalse(HasElement(i8a, 21));
// Within-bounds write
for (let i = 0; i < i8a.length; ++i) {
WriteElement(i8a, i, i + 1);
}
// Within-bounds read
for (let i = 0; i < i8a.length; ++i) {
assertEquals(i + 1, ReadElement(i8a, i));
}
})();
(function EnumerateElements() {
let gsab = new GrowableSharedArrayBuffer(100, 200);
for (let ctor of ctors) {
const ta = new ctor(gsab, 0, 3);
let keys = '';
for (const key in ta) {
keys += key;
}
assertEquals('012', keys);
}
}());

View File

@ -0,0 +1,138 @@
// Copyright 2021 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: --harmony-rab-gsab --allow-natives-syntax
"use strict";
class MyUint8Array extends Uint8Array {};
const ctors = [
Uint8Array,
Int8Array,
Uint16Array,
Int16Array,
Int32Array,
Float32Array,
Float64Array,
Uint8ClampedArray,
BigUint64Array,
BigInt64Array,
MyUint8Array
];
(function ConstructorThrowsIfBufferDetached() {
const rab = new ResizableArrayBuffer(40, 80);
%ArrayBufferDetach(rab);
for (let ctor of ctors) {
assertThrows(() => { ctor(rab); }, TypeError);
assertThrows(() => { ctor(rab, 8); }, TypeError);
assertThrows(() => { ctor(rab, 8, 1); }, TypeError);
}
})();
(function TypedArrayLengthAndByteLength() {
const rab = new ResizableArrayBuffer(40, 80);
let tas = [];
for (let ctor of ctors) {
tas.push(new ctor(rab, 0, 3));
tas.push(new ctor(rab, 8, 3));
tas.push(new ctor(rab));
tas.push(new ctor(rab, 8));
}
%ArrayBufferDetach(rab);
for (let ta of tas) {
assertEquals(0, ta.length);
assertEquals(0, ta.byteLength);
}
})();
(function AccessDetachedTypedArray() {
const rab = new ResizableArrayBuffer(16, 40);
const i8a = new Int8Array(rab, 0, 4);
// Initial values
for (let i = 0; i < 4; ++i) {
assertEquals(0, i8a[i]);
}
// Within-bounds write
for (let i = 0; i < 4; ++i) {
i8a[i] = i;
}
%ArrayBufferDetach(rab);
// OOB read
for (let i = 0; i < 4; ++i) {
assertEquals(undefined, i8a[i]);
}
// OOB write (has no effect)
for (let i = 0; i < 4; ++i) {
i8a[i] = 10;
}
for (let i = 0; i < 4; ++i) {
assertEquals(undefined, i8a[i]);
}
})();
(function LoadFromOutOfScopeTypedArrayWithFeedback() {
function ReadElement2(ta) {
return ta[2];
}
%EnsureFeedbackVectorForFunction(ReadElement2);
const rab = new ResizableArrayBuffer(16, 40);
const i8a = new Int8Array(rab, 0, 4);
assertEquals(0, ReadElement2(i8a));
// Within-bounds write
for (let i = 0; i < 4; ++i) {
i8a[i] = i;
}
%ArrayBufferDetach(rab);
// OOB read
for (let i = 0; i < 3; ++i) {
assertEquals(undefined, ReadElement2(i8a));
}
})();
(function StoreToOutOfScopeTypedArrayWithFeedback() {
function WriteElement2(ta, i) {
ta[2] = i;
}
%EnsureFeedbackVectorForFunction(WriteElement2);
const rab = new ResizableArrayBuffer(16, 40);
const i8a = new Int8Array(rab, 0, 4);
assertEquals(0, i8a[2]);
// Within-bounds write
for (let i = 0; i < 3; ++i) {
WriteElement2(i8a, 3);
}
%ArrayBufferDetach(rab);
// OOB write (has no effect)
for (let i = 0; i < 3; ++i) {
WriteElement2(i8a, 4);
}
// OOB read
for (let i = 0; i < 3; ++i) {
assertEquals(undefined, i8a[2]);
}
})();

View File

@ -0,0 +1,605 @@
// Copyright 2021 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: --harmony-rab-gsab --allow-natives-syntax
"use strict";
class MyUint8Array extends Uint8Array {};
const ctors = [
Uint8Array,
Int8Array,
Uint16Array,
Int16Array,
Int32Array,
Float32Array,
Float64Array,
Uint8ClampedArray,
BigUint64Array,
BigInt64Array,
MyUint8Array
];
(function TypedArrayPrototype() {
const rab = new ResizableArrayBuffer(40, 80);
const ab = new ArrayBuffer(80);
for (let ctor of ctors) {
const ta_rab = new ctor(rab, 0, 3);
const ta_ab = new ctor(ab, 0, 3);
assertEquals(ta_rab.__proto__, ta_ab.__proto__);
}
})();
(function TypedArrayLengthAndByteLength() {
const rab = new ResizableArrayBuffer(40, 80);
for (let ctor of ctors) {
const ta = new ctor(rab, 0, 3);
assertEquals(rab, ta.buffer);
assertEquals(3, ta.length);
assertEquals(3 * ctor.BYTES_PER_ELEMENT, ta.byteLength);
const empty_ta = new ctor(rab, 0, 0);
assertEquals(rab, empty_ta.buffer);
assertEquals(0, empty_ta.length);
assertEquals(0, empty_ta.byteLength);
const ta_with_offset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 3);
assertEquals(rab, ta_with_offset.buffer);
assertEquals(3, ta_with_offset.length);
assertEquals(3 * ctor.BYTES_PER_ELEMENT, ta_with_offset.byteLength);
const empty_ta_with_offset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 0);
assertEquals(rab, empty_ta_with_offset.buffer);
assertEquals(0, empty_ta_with_offset.length);
assertEquals(0, empty_ta_with_offset.byteLength);
const length_tracking_ta = new ctor(rab);
assertEquals(rab, length_tracking_ta.buffer);
assertEquals(40 / ctor.BYTES_PER_ELEMENT, length_tracking_ta.length);
assertEquals(40, length_tracking_ta.byteLength);
const offset = 8;
const length_tracking_ta_with_offset = new ctor(rab, offset);
assertEquals(rab, length_tracking_ta_with_offset.buffer);
assertEquals((40 - offset) / ctor.BYTES_PER_ELEMENT,
length_tracking_ta_with_offset.length);
assertEquals(40 - offset, length_tracking_ta_with_offset.byteLength);
const empty_length_tracking_ta_with_offset = new ctor(rab, 40);
assertEquals(rab, empty_length_tracking_ta_with_offset.buffer);
assertEquals(0, empty_length_tracking_ta_with_offset.length);
assertEquals(0, empty_length_tracking_ta_with_offset.byteLength);
}
})();
(function ConstructInvalid() {
const rab = new ResizableArrayBuffer(40, 80);
for (let ctor of ctors) {
// Length too big.
assertThrows(() => { new ctor(rab, 0, 40 / ctor.BYTES_PER_ELEMENT + 1); },
RangeError);
// Offset too close to the end.
assertThrows(() => { new ctor(rab, 40 - ctor.BYTES_PER_ELEMENT, 2); },
RangeError);
// Offset beyond end.
assertThrows(() => { new ctor(rab, 40, 1); }, RangeError);
if (ctor.BYTES_PER_ELEMENT > 1) {
// Offset not a multiple of the byte size.
assertThrows(() => { new ctor(rab, 1, 1); }, RangeError);
assertThrows(() => { new ctor(rab, 1); }, RangeError);
}
}
// Verify the error messages.
assertThrows(() => { new Int16Array(rab, 1, 1); }, RangeError,
/start offset of Int16Array should be a multiple of 2/);
assertThrows(() => { new Int16Array(rab, 38, 2); }, RangeError,
/Invalid typed array length: 2/);
})();
(function TypedArrayLengthWhenResizedOutOfBounds1() {
const rab = new ResizableArrayBuffer(16, 40);
// Create TAs which cover the bytes 0-7.
let tas_and_lengths = [];
for (let ctor of ctors) {
const length = 8 / ctor.BYTES_PER_ELEMENT;
tas_and_lengths.push([new ctor(rab, 0, length), length]);
}
for (let [ta, length] of tas_and_lengths) {
assertEquals(length, ta.length);
assertEquals(length * ta.BYTES_PER_ELEMENT, ta.byteLength);
}
rab.resize(2);
for (let [ta, length] of tas_and_lengths) {
assertEquals(0, ta.length);
assertEquals(0, ta.byteLength);
}
// Resize the rab so that it just barely covers the needed 8 bytes.
rab.resize(8);
for (let [ta, length] of tas_and_lengths) {
assertEquals(length, ta.length);
assertEquals(length * ta.BYTES_PER_ELEMENT, ta.byteLength);
}
rab.resize(40);
for (let [ta, length] of tas_and_lengths) {
assertEquals(length, ta.length);
assertEquals(length * ta.BYTES_PER_ELEMENT, ta.byteLength);
}
})();
// The previous test with offsets.
(function TypedArrayLengthWhenResizedOutOfBounds2() {
const rab = new ResizableArrayBuffer(20, 40);
// Create TAs which cover the bytes 8-15.
let tas_and_lengths = [];
for (let ctor of ctors) {
const length = 8 / ctor.BYTES_PER_ELEMENT;
tas_and_lengths.push([new ctor(rab, 8, length), length]);
}
for (let [ta, length] of tas_and_lengths) {
assertEquals(length, ta.length);
assertEquals(length * ta.BYTES_PER_ELEMENT, ta.byteLength);
}
rab.resize(10);
for (let [ta, length] of tas_and_lengths) {
assertEquals(0, ta.length);
assertEquals(0, ta.byteLength);
}
// Resize the rab so that it just barely covers the needed 8 bytes.
rab.resize(16);
for (let [ta, length] of tas_and_lengths) {
assertEquals(length, ta.length);
assertEquals(length * ta.BYTES_PER_ELEMENT, ta.byteLength);
}
rab.resize(40);
for (let [ta, length] of tas_and_lengths) {
assertEquals(length, ta.length);
assertEquals(length * ta.BYTES_PER_ELEMENT, ta.byteLength);
}
})();
(function LengthTracking1() {
const rab = new ResizableArrayBuffer(16, 40);
let tas = [];
for (let ctor of ctors) {
tas.push(new ctor(rab));
}
for (let ta of tas) {
assertEquals(16 / ta.BYTES_PER_ELEMENT, ta.length);
assertEquals(16, ta.byteLength);
}
rab.resize(40);
for (let ta of tas) {
assertEquals(40 / ta.BYTES_PER_ELEMENT, ta.length);
assertEquals(40, ta.byteLength);
}
// Resize to a number which is not a multiple of all byte_lengths.
rab.resize(19);
for (let ta of tas) {
const expected_length = Math.floor(19 / ta.BYTES_PER_ELEMENT);
assertEquals(expected_length, ta.length);
assertEquals(expected_length * ta.BYTES_PER_ELEMENT, ta.byteLength);
}
rab.resize(1);
for (let ta of tas) {
if (ta.BYTES_PER_ELEMENT == 1) {
assertEquals(1, ta.length);
assertEquals(1, ta.byteLength);
} else {
assertEquals(0, ta.length);
assertEquals(0, ta.byteLength);
}
}
rab.resize(0);
for (let ta of tas) {
assertEquals(0, ta.length);
assertEquals(0, ta.byteLength);
}
rab.resize(8);
for (let ta of tas) {
assertEquals(8 / ta.BYTES_PER_ELEMENT, ta.length);
assertEquals(8, ta.byteLength);
}
rab.resize(40);
for (let ta of tas) {
assertEquals(40 / ta.BYTES_PER_ELEMENT, ta.length);
assertEquals(40, ta.byteLength);
}
})();
// The previous test with offsets.
(function LengthTracking2() {
const rab = new ResizableArrayBuffer(16, 40);
const offset = 8;
let tas = [];
for (let ctor of ctors) {
tas.push(new ctor(rab, offset));
}
for (let ta of tas) {
assertEquals((16 - offset) / ta.BYTES_PER_ELEMENT, ta.length);
assertEquals(16 - offset, ta.byteLength);
}
rab.resize(40);
for (let ta of tas) {
assertEquals((40 - offset) / ta.BYTES_PER_ELEMENT, ta.length);
assertEquals(40 - offset, ta.byteLength);
}
// Resize to a number which is not a multiple of all byte_lengths.
rab.resize(20);
for (let ta of tas) {
const expected_length = Math.floor((20 - offset)/ ta.BYTES_PER_ELEMENT);
assertEquals(expected_length, ta.length);
assertEquals(expected_length * ta.BYTES_PER_ELEMENT, ta.byteLength);
}
// Resize so that all TypedArrays go out of bounds (because of the offset).
rab.resize(7);
for (let ta of tas) {
assertEquals(0, ta.length);
assertEquals(0, ta.byteLength);
}
rab.resize(0);
for (let ta of tas) {
assertEquals(0, ta.length);
assertEquals(0, ta.byteLength);
}
rab.resize(8);
for (let ta of tas) {
assertEquals(0, ta.length);
assertEquals(0, ta.byteLength);
}
// Resize so that the TypedArrays which have element size > 1 go out of bounds
// (because less than 1 full element would fit).
rab.resize(offset + 1);
for (let ta of tas) {
if (ta.BYTES_PER_ELEMENT == 1) {
assertEquals(1, ta.length);
assertEquals(1, ta.byteLength);
} else {
assertEquals(0, ta.length);
assertEquals(0, ta.byteLength);
}
}
rab.resize(40);
for (let ta of tas) {
assertEquals((40 - offset) / ta.BYTES_PER_ELEMENT, ta.length);
assertEquals(40 - offset, ta.byteLength);
}
})();
(function AccessOutOfBoundsTypedArray() {
for (let ctor of ctors) {
if (ctor.BYTES_PER_ELEMENT != 1) {
continue;
}
const rab = new ResizableArrayBuffer(16, 40);
const array = new ctor(rab, 0, 4);
// Initial values
for (let i = 0; i < 4; ++i) {
assertEquals(0, array[i]);
}
// Within-bounds write
for (let i = 0; i < 4; ++i) {
array[i] = i;
}
// Within-bounds read
for (let i = 0; i < 4; ++i) {
assertEquals(i, array[i]);
}
rab.resize(2);
// OOB read. If the RAB isn't large enough to fit the entire TypedArray,
// the length of the TypedArray is treated as 0.
for (let i = 0; i < 4; ++i) {
assertEquals(undefined, array[i]);
}
// OOB write (has no effect)
for (let i = 0; i < 4; ++i) {
array[i] = 10;
}
rab.resize(4);
// Within-bounds read
for (let i = 0; i < 2; ++i) {
assertEquals(i, array[i]);
}
// The shrunk-and-regrown part got zeroed.
for (let i = 2; i < 4; ++i) {
assertEquals(0, array[i]);
}
rab.resize(40);
// Within-bounds read
for (let i = 0; i < 2; ++i) {
assertEquals(i, array[i]);
}
for (let i = 2; i < 4; ++i) {
assertEquals(0, array[i]);
}
}
})();
(function OutOfBoundsTypedArrayAndHas() {
for (let ctor of ctors) {
if (ctor.BYTES_PER_ELEMENT != 1) {
continue;
}
const rab = new ResizableArrayBuffer(16, 40);
const array = new ctor(rab, 0, 4);
// Within-bounds read
for (let i = 0; i < 4; ++i) {
assertTrue(i in array);
}
rab.resize(2);
// OOB read. If the RAB isn't large enough to fit the entire TypedArray,
// the length of the TypedArray is treated as 0.
for (let i = 0; i < 4; ++i) {
assertFalse(i in array);
}
rab.resize(4);
// Within-bounds read
for (let i = 0; i < 4; ++i) {
assertTrue(i in array);
}
rab.resize(40);
// Within-bounds read
for (let i = 0; i < 4; ++i) {
assertTrue(i in array);
}
}
})();
(function LoadFromOutOfBoundsTypedArrayWithFeedback() {
function ReadElement2(ta) {
return ta[2];
}
%EnsureFeedbackVectorForFunction(ReadElement2);
const rab = new ResizableArrayBuffer(16, 40);
const i8a = new Int8Array(rab, 0, 4);
for (let i = 0; i < 3; ++i) {
assertEquals(0, ReadElement2(i8a));
}
// Within-bounds write
for (let i = 0; i < 4; ++i) {
i8a[i] = i;
}
// Within-bounds read
for (let i = 0; i < 3; ++i) {
assertEquals(2, ReadElement2(i8a));
}
rab.resize(2);
// OOB read
for (let i = 0; i < 3; ++i) {
assertEquals(undefined, ReadElement2(i8a));
}
rab.resize(4);
// Within-bounds read (the memory got zeroed)
for (let i = 0; i < 3; ++i) {
assertEquals(0, ReadElement2(i8a));
}
i8a[2] = 3;
for (let i = 0; i < 3; ++i) {
assertEquals(3, ReadElement2(i8a));
}
rab.resize(40);
// Within-bounds read
for (let i = 0; i < 3; ++i) {
assertEquals(3, ReadElement2(i8a));
}
})();
(function HasAndOutOfBoundsTypedArrayWithFeedback() {
function HasElement2(ta) {
return 2 in ta;
}
%EnsureFeedbackVectorForFunction(HasElement2);
const rab = new ResizableArrayBuffer(16, 40);
const i8a = new Int8Array(rab, 0, 4);
// Within-bounds read
for (let i = 0; i < 3; ++i) {
assertTrue(HasElement2(i8a));
}
rab.resize(2);
// OOB read
for (let i = 0; i < 3; ++i) {
assertFalse(HasElement2(i8a));
}
rab.resize(4);
// Within-bounds read
for (let i = 0; i < 3; ++i) {
assertTrue(HasElement2(i8a));
}
rab.resize(40);
// Within-bounds read
for (let i = 0; i < 3; ++i) {
assertTrue(HasElement2(i8a));
}
})();
(function StoreToOutOfBoundsTypedArrayWithFeedback() {
function WriteElement2(ta, i) {
ta[2] = i;
}
%EnsureFeedbackVectorForFunction(WriteElement2);
const rab = new ResizableArrayBuffer(16, 40);
const i8a = new Int8Array(rab, 0, 4);
assertEquals(0, i8a[2]);
// Within-bounds write
for (let i = 0; i < 3; ++i) {
WriteElement2(i8a, 3);
}
// Within-bounds read
for (let i = 0; i < 3; ++i) {
assertEquals(3, i8a[2]);
}
rab.resize(2);
// OOB write (has no effect)
for (let i = 0; i < 3; ++i) {
WriteElement2(i8a, 4);
}
rab.resize(4);
// Within-bounds read (the memory got zeroed)
for (let i = 0; i < 3; ++i) {
assertEquals(0, i8a[2]);
}
// Within-bounds write
for (let i = 0; i < 3; ++i) {
WriteElement2(i8a, 5);
}
rab.resize(40);
// Within-bounds read
for (let i = 0; i < 3; ++i) {
assertEquals(5, i8a[2]);
}
})();
(function OOBBehavesLikeDetached() {
function ReadElement2(ta) {
return ta[2];
}
function HasElement2(ta) {
return 2 in ta;
}
const rab = new ResizableArrayBuffer(16, 40);
const i8a = new Int8Array(rab, 0, 4);
i8a.__proto__ = {2: 'wrong value'};
i8a[2] = 10;
assertEquals(10, ReadElement2(i8a));
assertTrue(HasElement2(i8a));
rab.resize(0);
assertEquals(undefined, ReadElement2(i8a));
assertFalse(HasElement2(i8a));
})();
(function OOBBehavesLikeDetachedWithFeedback() {
function ReadElement2(ta) {
return ta[2];
}
function HasElement2(ta) {
return 2 in ta;
}
%EnsureFeedbackVectorForFunction(ReadElement2);
%EnsureFeedbackVectorForFunction(HasElement2);
const rab = new ResizableArrayBuffer(16, 40);
const i8a = new Int8Array(rab, 0, 4);
i8a.__proto__ = {2: 'wrong value'};
i8a[2] = 10;
for (let i = 0; i < 3; ++i) {
assertEquals(10, ReadElement2(i8a));
assertTrue(HasElement2(i8a));
}
rab.resize(0);
for (let i = 0; i < 3; ++i) {
assertEquals(undefined, ReadElement2(i8a));
assertFalse(HasElement2(i8a));
}
})();
(function EnumerateElements() {
let rab = new ResizableArrayBuffer(100, 200);
for (let ctor of ctors) {
const ta = new ctor(rab, 0, 3);
let keys = '';
for (const key in ta) {
keys += key;
}
assertEquals('012', keys);
}
}());

View File

@ -328,67 +328,67 @@ KNOWN_MAPS = {
("read_only_space", 0x031ed): (67, "BasicBlockCountersMarkerMap"),
("read_only_space", 0x03231): (90, "ArrayBoilerplateDescriptionMap"),
("read_only_space", 0x03331): (103, "InterceptorInfoMap"),
("read_only_space", 0x055d5): (75, "PromiseFulfillReactionJobTaskMap"),
("read_only_space", 0x055fd): (76, "PromiseRejectReactionJobTaskMap"),
("read_only_space", 0x05625): (77, "CallableTaskMap"),
("read_only_space", 0x0564d): (78, "CallbackTaskMap"),
("read_only_space", 0x05675): (79, "PromiseResolveThenableJobTaskMap"),
("read_only_space", 0x0569d): (82, "FunctionTemplateInfoMap"),
("read_only_space", 0x056c5): (83, "ObjectTemplateInfoMap"),
("read_only_space", 0x056ed): (84, "AccessCheckInfoMap"),
("read_only_space", 0x05715): (85, "AccessorInfoMap"),
("read_only_space", 0x0573d): (86, "AccessorPairMap"),
("read_only_space", 0x05765): (87, "AliasedArgumentsEntryMap"),
("read_only_space", 0x0578d): (88, "AllocationMementoMap"),
("read_only_space", 0x057b5): (91, "AsmWasmDataMap"),
("read_only_space", 0x057dd): (92, "AsyncGeneratorRequestMap"),
("read_only_space", 0x05805): (93, "BaselineDataMap"),
("read_only_space", 0x0582d): (94, "BreakPointMap"),
("read_only_space", 0x05855): (95, "BreakPointInfoMap"),
("read_only_space", 0x0587d): (96, "CachedTemplateObjectMap"),
("read_only_space", 0x058a5): (98, "ClassPositionsMap"),
("read_only_space", 0x058cd): (99, "DebugInfoMap"),
("read_only_space", 0x058f5): (102, "FunctionTemplateRareDataMap"),
("read_only_space", 0x0591d): (104, "InterpreterDataMap"),
("read_only_space", 0x05945): (105, "ModuleRequestMap"),
("read_only_space", 0x0596d): (106, "PromiseCapabilityMap"),
("read_only_space", 0x05995): (107, "PromiseReactionMap"),
("read_only_space", 0x059bd): (108, "PropertyDescriptorObjectMap"),
("read_only_space", 0x059e5): (109, "PrototypeInfoMap"),
("read_only_space", 0x05a0d): (110, "RegExpBoilerplateDescriptionMap"),
("read_only_space", 0x05a35): (111, "ScriptMap"),
("read_only_space", 0x05a5d): (112, "SourceTextModuleInfoEntryMap"),
("read_only_space", 0x05a85): (113, "StackFrameInfoMap"),
("read_only_space", 0x05aad): (114, "TemplateObjectDescriptionMap"),
("read_only_space", 0x05ad5): (115, "Tuple2Map"),
("read_only_space", 0x05afd): (116, "WasmExceptionTagMap"),
("read_only_space", 0x05b25): (117, "WasmIndirectFunctionTableMap"),
("read_only_space", 0x05b4d): (135, "SloppyArgumentsElementsMap"),
("read_only_space", 0x05b75): (152, "DescriptorArrayMap"),
("read_only_space", 0x05b9d): (157, "UncompiledDataWithoutPreparseDataMap"),
("read_only_space", 0x05bc5): (156, "UncompiledDataWithPreparseDataMap"),
("read_only_space", 0x05bed): (173, "OnHeapBasicBlockProfilerDataMap"),
("read_only_space", 0x05c15): (169, "InternalClassMap"),
("read_only_space", 0x05c3d): (180, "SmiPairMap"),
("read_only_space", 0x05c65): (179, "SmiBoxMap"),
("read_only_space", 0x05c8d): (146, "ExportedSubClassBaseMap"),
("read_only_space", 0x05cb5): (147, "ExportedSubClassMap"),
("read_only_space", 0x05cdd): (68, "AbstractInternalClassSubclass1Map"),
("read_only_space", 0x05d05): (69, "AbstractInternalClassSubclass2Map"),
("read_only_space", 0x05d2d): (134, "InternalClassWithSmiElementsMap"),
("read_only_space", 0x05d55): (170, "InternalClassWithStructElementsMap"),
("read_only_space", 0x05d7d): (148, "ExportedSubClass2Map"),
("read_only_space", 0x05da5): (181, "SortStateMap"),
("read_only_space", 0x05dcd): (184, "WasmCapiFunctionDataMap"),
("read_only_space", 0x05df5): (89, "AllocationSiteWithWeakNextMap"),
("read_only_space", 0x05e1d): (89, "AllocationSiteWithoutWeakNextMap"),
("read_only_space", 0x05e45): (80, "LoadHandler1Map"),
("read_only_space", 0x05e6d): (80, "LoadHandler2Map"),
("read_only_space", 0x05e95): (80, "LoadHandler3Map"),
("read_only_space", 0x05ebd): (81, "StoreHandler0Map"),
("read_only_space", 0x05ee5): (81, "StoreHandler1Map"),
("read_only_space", 0x05f0d): (81, "StoreHandler2Map"),
("read_only_space", 0x05f35): (81, "StoreHandler3Map"),
("read_only_space", 0x05639): (75, "PromiseFulfillReactionJobTaskMap"),
("read_only_space", 0x05661): (76, "PromiseRejectReactionJobTaskMap"),
("read_only_space", 0x05689): (77, "CallableTaskMap"),
("read_only_space", 0x056b1): (78, "CallbackTaskMap"),
("read_only_space", 0x056d9): (79, "PromiseResolveThenableJobTaskMap"),
("read_only_space", 0x05701): (82, "FunctionTemplateInfoMap"),
("read_only_space", 0x05729): (83, "ObjectTemplateInfoMap"),
("read_only_space", 0x05751): (84, "AccessCheckInfoMap"),
("read_only_space", 0x05779): (85, "AccessorInfoMap"),
("read_only_space", 0x057a1): (86, "AccessorPairMap"),
("read_only_space", 0x057c9): (87, "AliasedArgumentsEntryMap"),
("read_only_space", 0x057f1): (88, "AllocationMementoMap"),
("read_only_space", 0x05819): (91, "AsmWasmDataMap"),
("read_only_space", 0x05841): (92, "AsyncGeneratorRequestMap"),
("read_only_space", 0x05869): (93, "BaselineDataMap"),
("read_only_space", 0x05891): (94, "BreakPointMap"),
("read_only_space", 0x058b9): (95, "BreakPointInfoMap"),
("read_only_space", 0x058e1): (96, "CachedTemplateObjectMap"),
("read_only_space", 0x05909): (98, "ClassPositionsMap"),
("read_only_space", 0x05931): (99, "DebugInfoMap"),
("read_only_space", 0x05959): (102, "FunctionTemplateRareDataMap"),
("read_only_space", 0x05981): (104, "InterpreterDataMap"),
("read_only_space", 0x059a9): (105, "ModuleRequestMap"),
("read_only_space", 0x059d1): (106, "PromiseCapabilityMap"),
("read_only_space", 0x059f9): (107, "PromiseReactionMap"),
("read_only_space", 0x05a21): (108, "PropertyDescriptorObjectMap"),
("read_only_space", 0x05a49): (109, "PrototypeInfoMap"),
("read_only_space", 0x05a71): (110, "RegExpBoilerplateDescriptionMap"),
("read_only_space", 0x05a99): (111, "ScriptMap"),
("read_only_space", 0x05ac1): (112, "SourceTextModuleInfoEntryMap"),
("read_only_space", 0x05ae9): (113, "StackFrameInfoMap"),
("read_only_space", 0x05b11): (114, "TemplateObjectDescriptionMap"),
("read_only_space", 0x05b39): (115, "Tuple2Map"),
("read_only_space", 0x05b61): (116, "WasmExceptionTagMap"),
("read_only_space", 0x05b89): (117, "WasmIndirectFunctionTableMap"),
("read_only_space", 0x05bb1): (135, "SloppyArgumentsElementsMap"),
("read_only_space", 0x05bd9): (152, "DescriptorArrayMap"),
("read_only_space", 0x05c01): (157, "UncompiledDataWithoutPreparseDataMap"),
("read_only_space", 0x05c29): (156, "UncompiledDataWithPreparseDataMap"),
("read_only_space", 0x05c51): (173, "OnHeapBasicBlockProfilerDataMap"),
("read_only_space", 0x05c79): (169, "InternalClassMap"),
("read_only_space", 0x05ca1): (180, "SmiPairMap"),
("read_only_space", 0x05cc9): (179, "SmiBoxMap"),
("read_only_space", 0x05cf1): (146, "ExportedSubClassBaseMap"),
("read_only_space", 0x05d19): (147, "ExportedSubClassMap"),
("read_only_space", 0x05d41): (68, "AbstractInternalClassSubclass1Map"),
("read_only_space", 0x05d69): (69, "AbstractInternalClassSubclass2Map"),
("read_only_space", 0x05d91): (134, "InternalClassWithSmiElementsMap"),
("read_only_space", 0x05db9): (170, "InternalClassWithStructElementsMap"),
("read_only_space", 0x05de1): (148, "ExportedSubClass2Map"),
("read_only_space", 0x05e09): (181, "SortStateMap"),
("read_only_space", 0x05e31): (184, "WasmCapiFunctionDataMap"),
("read_only_space", 0x05e59): (89, "AllocationSiteWithWeakNextMap"),
("read_only_space", 0x05e81): (89, "AllocationSiteWithoutWeakNextMap"),
("read_only_space", 0x05ea9): (80, "LoadHandler1Map"),
("read_only_space", 0x05ed1): (80, "LoadHandler2Map"),
("read_only_space", 0x05ef9): (80, "LoadHandler3Map"),
("read_only_space", 0x05f21): (81, "StoreHandler0Map"),
("read_only_space", 0x05f49): (81, "StoreHandler1Map"),
("read_only_space", 0x05f71): (81, "StoreHandler2Map"),
("read_only_space", 0x05f99): (81, "StoreHandler3Map"),
("map_space", 0x02119): (1057, "ExternalMap"),
("map_space", 0x02141): (1098, "JSMessageObjectMap"),
}
@ -475,27 +475,27 @@ KNOWN_OBJECTS = {
("old_space", 0x029b5): "StringSplitCache",
("old_space", 0x02dbd): "RegExpMultipleCache",
("old_space", 0x031c5): "BuiltinsConstantsTable",
("old_space", 0x035c5): "AsyncFunctionAwaitRejectSharedFun",
("old_space", 0x035e9): "AsyncFunctionAwaitResolveSharedFun",
("old_space", 0x0360d): "AsyncGeneratorAwaitRejectSharedFun",
("old_space", 0x03631): "AsyncGeneratorAwaitResolveSharedFun",
("old_space", 0x03655): "AsyncGeneratorYieldResolveSharedFun",
("old_space", 0x03679): "AsyncGeneratorReturnResolveSharedFun",
("old_space", 0x0369d): "AsyncGeneratorReturnClosedRejectSharedFun",
("old_space", 0x036c1): "AsyncGeneratorReturnClosedResolveSharedFun",
("old_space", 0x036e5): "AsyncIteratorValueUnwrapSharedFun",
("old_space", 0x03709): "PromiseAllResolveElementSharedFun",
("old_space", 0x0372d): "PromiseAllSettledResolveElementSharedFun",
("old_space", 0x03751): "PromiseAllSettledRejectElementSharedFun",
("old_space", 0x03775): "PromiseAnyRejectElementSharedFun",
("old_space", 0x03799): "PromiseCapabilityDefaultRejectSharedFun",
("old_space", 0x037bd): "PromiseCapabilityDefaultResolveSharedFun",
("old_space", 0x037e1): "PromiseCatchFinallySharedFun",
("old_space", 0x03805): "PromiseGetCapabilitiesExecutorSharedFun",
("old_space", 0x03829): "PromiseThenFinallySharedFun",
("old_space", 0x0384d): "PromiseThrowerFinallySharedFun",
("old_space", 0x03871): "PromiseValueThunkFinallySharedFun",
("old_space", 0x03895): "ProxyRevokeSharedFun",
("old_space", 0x035d1): "AsyncFunctionAwaitRejectSharedFun",
("old_space", 0x035f5): "AsyncFunctionAwaitResolveSharedFun",
("old_space", 0x03619): "AsyncGeneratorAwaitRejectSharedFun",
("old_space", 0x0363d): "AsyncGeneratorAwaitResolveSharedFun",
("old_space", 0x03661): "AsyncGeneratorYieldResolveSharedFun",
("old_space", 0x03685): "AsyncGeneratorReturnResolveSharedFun",
("old_space", 0x036a9): "AsyncGeneratorReturnClosedRejectSharedFun",
("old_space", 0x036cd): "AsyncGeneratorReturnClosedResolveSharedFun",
("old_space", 0x036f1): "AsyncIteratorValueUnwrapSharedFun",
("old_space", 0x03715): "PromiseAllResolveElementSharedFun",
("old_space", 0x03739): "PromiseAllSettledResolveElementSharedFun",
("old_space", 0x0375d): "PromiseAllSettledRejectElementSharedFun",
("old_space", 0x03781): "PromiseAnyRejectElementSharedFun",
("old_space", 0x037a5): "PromiseCapabilityDefaultRejectSharedFun",
("old_space", 0x037c9): "PromiseCapabilityDefaultResolveSharedFun",
("old_space", 0x037ed): "PromiseCatchFinallySharedFun",
("old_space", 0x03811): "PromiseGetCapabilitiesExecutorSharedFun",
("old_space", 0x03835): "PromiseThenFinallySharedFun",
("old_space", 0x03859): "PromiseThrowerFinallySharedFun",
("old_space", 0x0387d): "PromiseValueThunkFinallySharedFun",
("old_space", 0x038a1): "ProxyRevokeSharedFun",
}
# Lower 32 bits of first page addresses for various heap spaces.