[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:
parent
4cd2b536e1
commit
3160edf011
@ -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.
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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) \
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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,
|
||||
¬_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(¬_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, ¬_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(¬_found);
|
||||
{ Unreachable(); }
|
||||
BIND(&end);
|
||||
return result.value();
|
||||
}
|
||||
|
||||
TNode<JSArrayBuffer> CodeStubAssembler::GetTypedArrayBuffer(
|
||||
TNode<Context> context, TNode<JSTypedArray> array) {
|
||||
Label call_runtime(this), done(this);
|
||||
|
@ -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);
|
||||
|
@ -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>();
|
||||
}
|
||||
|
@ -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") \
|
||||
|
@ -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") \
|
||||
|
@ -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_++;
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
@ -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) \
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
14
src/ic/ic.cc
14
src/ic/ic.cc
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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") \
|
||||
|
@ -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>;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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) \
|
||||
|
@ -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>,
|
||||
|
@ -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";
|
||||
|
@ -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.
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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()) {
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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 =
|
||||
|
@ -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 {
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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();
|
||||
|
@ -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:
|
||||
|
@ -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();
|
||||
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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) \
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()) {
|
||||
|
@ -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),
|
||||
|
@ -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),
|
||||
|
@ -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),
|
||||
|
430
test/mjsunit/resizablearraybuffer-growablesharedarraybuffer.js
Normal file
430
test/mjsunit/resizablearraybuffer-growablesharedarraybuffer.js
Normal 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);
|
||||
})();
|
354
test/mjsunit/typedarray-growablesharedarraybuffer.js
Normal file
354
test/mjsunit/typedarray-growablesharedarraybuffer.js
Normal 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);
|
||||
}
|
||||
}());
|
138
test/mjsunit/typedarray-resizablearraybuffer-detach.js
Normal file
138
test/mjsunit/typedarray-resizablearraybuffer-detach.js
Normal 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]);
|
||||
}
|
||||
})();
|
605
test/mjsunit/typedarray-resizablearraybuffer.js
Normal file
605
test/mjsunit/typedarray-resizablearraybuffer.js
Normal 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);
|
||||
}
|
||||
}());
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user