[atomics] Relax Atomics methods to work on ArrayBuffers

This reached consensus in the March 2020 TC39.
https://github.com/tc39/ecma262/pull/1908

This aligns JS with wasm, which allows atomics operations on non-shared
linear memory.

Bug: v8:10687, v8:9921
Change-Id: I7b60473b271cee6bccb342e97a4fd3781aedddb4
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2330802
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#69392}
This commit is contained in:
Shu-yu Guo 2020-08-13 14:25:47 -07:00 committed by Commit Bot
parent c5722641da
commit 6962221295
18 changed files with 463 additions and 189 deletions

View File

@ -23,10 +23,10 @@ class SharedArrayBufferBuiltinsAssembler : public CodeStubAssembler {
Node* base, Node* offset,
Node* value,
Node* value_high);
void ValidateSharedTypedArray(TNode<Object> maybe_array,
TNode<Context> context,
TNode<Int32T>* out_elements_kind,
TNode<RawPtrT>* out_backing_store);
TNode<JSArrayBuffer> ValidateIntegerTypedArray(
TNode<Object> maybe_array, TNode<Context> context,
TNode<Int32T>* out_elements_kind, TNode<RawPtrT>* out_backing_store,
Label* detached);
TNode<UintPtrT> ValidateAtomicAccess(TNode<JSTypedArray> array,
TNode<Object> index,
@ -38,7 +38,8 @@ class SharedArrayBufferBuiltinsAssembler : public CodeStubAssembler {
void AtomicBinopBuiltinCommon(TNode<Object> maybe_array, TNode<Object> index,
TNode<Object> value, TNode<Context> context,
AssemblerFunction function,
Runtime::FunctionId runtime_function);
Runtime::FunctionId runtime_function,
const char* method_name);
// Create a BigInt from the result of a 64-bit atomic operation, using
// projections on 32-bit platforms.
@ -46,12 +47,17 @@ class SharedArrayBufferBuiltinsAssembler : public CodeStubAssembler {
TNode<BigInt> BigIntFromUnsigned64(Node* unsigned64);
};
// https://tc39.es/ecma262/#sec-validatesharedintegertypedarray
void SharedArrayBufferBuiltinsAssembler::ValidateSharedTypedArray(
// https://tc39.es/ecma262/#sec-validateintegertypedarray
TNode<JSArrayBuffer>
SharedArrayBufferBuiltinsAssembler::ValidateIntegerTypedArray(
TNode<Object> maybe_array, TNode<Context> context,
TNode<Int32T>* out_elements_kind, TNode<RawPtrT>* out_backing_store) {
TNode<Int32T>* out_elements_kind, TNode<RawPtrT>* out_backing_store,
Label* detached) {
Label not_float_or_clamped(this), invalid(this);
// The logic of TypedArrayBuiltinsAssembler::ValidateTypedArrayBuffer is
// inlined to avoid duplicate error branches.
// Fail if it is not a heap object.
GotoIf(TaggedIsSmi(maybe_array), &invalid);
@ -60,10 +66,9 @@ void SharedArrayBufferBuiltinsAssembler::ValidateSharedTypedArray(
GotoIfNot(IsJSTypedArrayMap(map), &invalid);
TNode<JSTypedArray> array = CAST(maybe_array);
// Fail if the array's JSArrayBuffer is not shared.
TNode<JSArrayBuffer> array_buffer = LoadJSArrayBufferViewBuffer(array);
TNode<Uint32T> bitfield = LoadJSArrayBufferBitField(array_buffer);
GotoIfNot(IsSetWord32<JSArrayBuffer::IsSharedBit>(bitfield), &invalid);
// Fail if the array's JSArrayBuffer is detached.
TNode<JSArrayBuffer> array_buffer = GetTypedArrayBuffer(context, array);
GotoIf(IsDetachedBuffer(array_buffer), detached);
// Fail if the array's element type is float32, float64 or clamped.
STATIC_ASSERT(INT8_ELEMENTS < FLOAT32_ELEMENTS);
@ -82,7 +87,7 @@ void SharedArrayBufferBuiltinsAssembler::ValidateSharedTypedArray(
BIND(&invalid);
{
ThrowTypeError(context, MessageTemplate::kNotIntegerSharedTypedArray,
ThrowTypeError(context, MessageTemplate::kNotIntegerTypedArray,
maybe_array);
}
@ -92,6 +97,8 @@ void SharedArrayBufferBuiltinsAssembler::ValidateSharedTypedArray(
TNode<RawPtrT> backing_store = LoadJSArrayBufferBackingStorePtr(array_buffer);
TNode<UintPtrT> byte_offset = LoadJSArrayBufferViewByteOffset(array);
*out_backing_store = RawPtrAdd(backing_store, Signed(byte_offset));
return array_buffer;
}
// https://tc39.github.io/ecma262/#sec-validateatomicaccess
@ -100,24 +107,35 @@ TNode<UintPtrT> SharedArrayBufferBuiltinsAssembler::ValidateAtomicAccess(
TNode<JSTypedArray> array, TNode<Object> index, TNode<Context> context) {
Label done(this), range_error(this);
// 1. Assert: typedArray is an Object that has a [[ViewedArrayBuffer]]
// internal slot.
// 2. Let length be typedArray.[[ArrayLength]].
TNode<UintPtrT> array_length = LoadJSTypedArrayLength(array);
// 3. Let accessIndex be ? ToIndex(requestIndex).
TNode<UintPtrT> index_uintptr = ToIndex(context, index, &range_error);
TNode<UintPtrT> array_length = LoadJSTypedArrayLength(array);
// 4. Assert: accessIndex ≥ 0.
// 5. If accessIndex ≥ length, throw a RangeError exception.
Branch(UintPtrLessThan(index_uintptr, array_length), &done, &range_error);
BIND(&range_error);
ThrowRangeError(context, MessageTemplate::kInvalidAtomicAccessIndex);
// 6. Return accessIndex.
BIND(&done);
return index_uintptr;
}
void SharedArrayBufferBuiltinsAssembler::DebugSanityCheckAtomicIndex(
TNode<JSTypedArray> array, TNode<UintPtrT> index) {
// In Debug mode, we re-validate the index as a sanity check because
// ToInteger above calls out to JavaScript. A SharedArrayBuffer can't be
// detached and the TypedArray length can't change either, so skipping this
// check in Release mode is safe.
// In Debug mode, we re-validate the index as a sanity check because ToInteger
// above calls out to JavaScript. Atomics work on ArrayBuffers, which may be
// detached, and detachment state must be checked and throw before this
// check. The length cannot change.
//
// This function must always be called after ValidateIntegerTypedArray, which
// will ensure that LoadJSArrayBufferViewBuffer will not be null.
CSA_ASSERT(this, Word32BinaryNot(
IsDetachedBuffer(LoadJSArrayBufferViewBuffer(array))));
CSA_ASSERT(this, UintPtrLessThan(index, LoadJSTypedArrayLength(array)));
@ -151,14 +169,27 @@ TF_BUILTIN(AtomicsLoad, SharedArrayBufferBuiltinsAssembler) {
TNode<Object> index = CAST(Parameter(Descriptor::kIndex));
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
// 1. Let buffer be ? ValidateIntegerTypedArray(typedArray).
Label detached(this);
TNode<Int32T> elements_kind;
TNode<RawPtrT> backing_store;
ValidateSharedTypedArray(maybe_array, context, &elements_kind,
&backing_store);
TNode<JSArrayBuffer> array_buffer = ValidateIntegerTypedArray(
maybe_array, context, &elements_kind, &backing_store, &detached);
TNode<JSTypedArray> array = CAST(maybe_array);
// 2. Let i be ? ValidateAtomicAccess(typedArray, index).
TNode<UintPtrT> index_word = ValidateAtomicAccess(array, index, context);
// 3. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
// 4. NOTE: The above check is not redundant with the check in
// ValidateIntegerTypedArray because the call to ValidateAtomicAccess on the
// preceding line can have arbitrary side effects, which could cause the
// buffer to become detached.
GotoIf(IsDetachedBuffer(array_buffer), &detached);
// Steps 5-10.
//
// (Not copied from ecma262 due to the axiomatic nature of the memory model.)
Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this),
i64(this), u64(this), other(this);
int32_t case_values[] = {
@ -213,9 +244,16 @@ TF_BUILTIN(AtomicsLoad, SharedArrayBufferBuiltinsAssembler) {
Return(BigIntFromUnsigned64(AtomicLoad(MachineType::Uint64(), backing_store,
WordShl(index_word, 3))));
#endif
// This shouldn't happen, we've already validated the type.
BIND(&other);
Unreachable();
BIND(&detached);
{
ThrowTypeError(context, MessageTemplate::kDetachedOperation,
"Atomics.load");
}
}
// https://tc39.es/ecma262/#sec-atomics.store
@ -225,24 +263,43 @@ TF_BUILTIN(AtomicsStore, SharedArrayBufferBuiltinsAssembler) {
TNode<Object> value = CAST(Parameter(Descriptor::kValue));
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
// 1. Let buffer be ? ValidateIntegerTypedArray(typedArray).
Label detached(this);
TNode<Int32T> elements_kind;
TNode<RawPtrT> backing_store;
ValidateSharedTypedArray(maybe_array, context, &elements_kind,
&backing_store);
TNode<JSArrayBuffer> array_buffer = ValidateIntegerTypedArray(
maybe_array, context, &elements_kind, &backing_store, &detached);
TNode<JSTypedArray> array = CAST(maybe_array);
// 2. Let i be ? ValidateAtomicAccess(typedArray, index).
TNode<UintPtrT> index_word = ValidateAtomicAccess(array, index, context);
Label u8(this), u16(this), u32(this), u64(this), other(this);
// 3. Let arrayTypeName be typedArray.[[TypedArrayName]].
// 4. If arrayTypeName is "BigUint64Array" or "BigInt64Array",
// let v be ? ToBigInt(value).
STATIC_ASSERT(BIGINT64_ELEMENTS > INT32_ELEMENTS);
STATIC_ASSERT(BIGUINT64_ELEMENTS > INT32_ELEMENTS);
GotoIf(Int32GreaterThan(elements_kind, Int32Constant(INT32_ELEMENTS)), &u64);
// 5. Otherwise, let v be ? ToInteger(value).
TNode<Number> value_integer = ToInteger_Inline(context, value);
// 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
// 7. NOTE: The above check is not redundant with the check in
// ValidateIntegerTypedArray because the call to ToBigInt or ToInteger on the
// preceding lines can have arbitrary side effects, which could cause the
// buffer to become detached.
GotoIf(IsDetachedBuffer(array_buffer), &detached);
TNode<Word32T> value_word32 = TruncateTaggedToWord32(context, value_integer);
DebugSanityCheckAtomicIndex(array, index_word);
// Steps 8-13.
//
// (Not copied from ecma262 due to the axiomatic nature of the memory model.)
int32_t case_values[] = {
INT8_ELEMENTS, UINT8_ELEMENTS, INT16_ELEMENTS,
UINT16_ELEMENTS, INT32_ELEMENTS, UINT32_ELEMENTS,
@ -272,8 +329,13 @@ TF_BUILTIN(AtomicsStore, SharedArrayBufferBuiltinsAssembler) {
Return(CallRuntime(Runtime::kAtomicsStore64, context, array, index_number,
value));
#else
// 4. If arrayTypeName is "BigUint64Array" or "BigInt64Array",
// let v be ? ToBigInt(value).
TNode<BigInt> value_bigint = ToBigInt(context, value);
// 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
GotoIf(IsDetachedBuffer(array_buffer), &detached);
DebugSanityCheckAtomicIndex(array, index_word);
TVARIABLE(UintPtrT, var_low);
@ -288,6 +350,12 @@ TF_BUILTIN(AtomicsStore, SharedArrayBufferBuiltinsAssembler) {
// This shouldn't happen, we've already validated the type.
BIND(&other);
Unreachable();
BIND(&detached);
{
ThrowTypeError(context, MessageTemplate::kDetachedOperation,
"Atomics.store");
}
}
// https://tc39.es/ecma262/#sec-atomics.exchange
@ -297,12 +365,18 @@ TF_BUILTIN(AtomicsExchange, SharedArrayBufferBuiltinsAssembler) {
TNode<Object> value = CAST(Parameter(Descriptor::kValue));
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
// Inlines AtomicReadModifyWrite
// https://tc39.es/ecma262/#sec-atomicreadmodifywrite
// 1. Let buffer be ? ValidateIntegerTypedArray(typedArray).
Label detached(this);
TNode<Int32T> elements_kind;
TNode<RawPtrT> backing_store;
ValidateSharedTypedArray(maybe_array, context, &elements_kind,
&backing_store);
TNode<JSArrayBuffer> array_buffer = ValidateIntegerTypedArray(
maybe_array, context, &elements_kind, &backing_store, &detached);
TNode<JSTypedArray> array = CAST(maybe_array);
// 2. Let i be ? ValidateAtomicAccess(typedArray, index).
TNode<UintPtrT> index_word = ValidateAtomicAccess(array, index, context);
#if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64
@ -313,16 +387,30 @@ TF_BUILTIN(AtomicsExchange, SharedArrayBufferBuiltinsAssembler) {
Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this),
i64(this), u64(this), big(this), other(this);
// 3. Let arrayTypeName be typedArray.[[TypedArrayName]].
// 4. If typedArray.[[ContentType]] is BigInt, let v be ? ToBigInt(value).
STATIC_ASSERT(BIGINT64_ELEMENTS > INT32_ELEMENTS);
STATIC_ASSERT(BIGUINT64_ELEMENTS > INT32_ELEMENTS);
GotoIf(Int32GreaterThan(elements_kind, Int32Constant(INT32_ELEMENTS)), &big);
// 5. Otherwise, let v be ? ToInteger(value).
TNode<Number> value_integer = ToInteger_Inline(context, value);
// 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
// 7. NOTE: The above check is not redundant with the check in
// ValidateIntegerTypedArray because the call to ToBigInt or ToInteger on the
// preceding lines can have arbitrary side effects, which could cause the
// buffer to become detached.
GotoIf(IsDetachedBuffer(array_buffer), &detached);
DebugSanityCheckAtomicIndex(array, index_word);
TNode<Word32T> value_word32 = TruncateTaggedToWord32(context, value_integer);
// Steps 8-12.
//
// (Not copied from ecma262 due to the axiomatic nature of the memory model.)
int32_t case_values[] = {
INT8_ELEMENTS, UINT8_ELEMENTS, INT16_ELEMENTS,
UINT16_ELEMENTS, INT32_ELEMENTS, UINT32_ELEMENTS,
@ -360,8 +448,12 @@ TF_BUILTIN(AtomicsExchange, SharedArrayBufferBuiltinsAssembler) {
WordShl(index_word, 2), value_word32)));
BIND(&big);
// 4. If typedArray.[[ContentType]] is BigInt, let v be ? ToBigInt(value).
TNode<BigInt> value_bigint = ToBigInt(context, value);
// 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
GotoIf(IsDetachedBuffer(array_buffer), &detached);
DebugSanityCheckAtomicIndex(array, index_word);
TVARIABLE(UintPtrT, var_low);
@ -388,6 +480,12 @@ TF_BUILTIN(AtomicsExchange, SharedArrayBufferBuiltinsAssembler) {
// This shouldn't happen, we've already validated the type.
BIND(&other);
Unreachable();
BIND(&detached);
{
ThrowTypeError(context, MessageTemplate::kDetachedOperation,
"Atomics.exchange");
}
#endif // V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64
}
@ -399,12 +497,15 @@ TF_BUILTIN(AtomicsCompareExchange, SharedArrayBufferBuiltinsAssembler) {
TNode<Object> new_value = CAST(Parameter(Descriptor::kNewValue));
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
// 1. Let buffer be ? ValidateIntegerTypedArray(typedArray).
Label detached(this);
TNode<Int32T> elements_kind;
TNode<RawPtrT> backing_store;
ValidateSharedTypedArray(maybe_array, context, &elements_kind,
&backing_store);
TNode<JSArrayBuffer> array_buffer = ValidateIntegerTypedArray(
maybe_array, context, &elements_kind, &backing_store, &detached);
TNode<JSTypedArray> array = CAST(maybe_array);
// 2. Let i be ? ValidateAtomicAccess(typedArray, index).
TNode<UintPtrT> index_word = ValidateAtomicAccess(array, index, context);
#if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64 || \
@ -415,13 +516,28 @@ TF_BUILTIN(AtomicsCompareExchange, SharedArrayBufferBuiltinsAssembler) {
#else
Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this),
i64(this), u64(this), big(this), other(this);
// 3. Let arrayTypeName be typedArray.[[TypedArrayName]].
// 4. If typedArray.[[ContentType]] is BigInt, then
// a. Let expected be ? ToBigInt(expectedValue).
// b. Let replacement be ? ToBigInt(replacementValue).
STATIC_ASSERT(BIGINT64_ELEMENTS > INT32_ELEMENTS);
STATIC_ASSERT(BIGUINT64_ELEMENTS > INT32_ELEMENTS);
GotoIf(Int32GreaterThan(elements_kind, Int32Constant(INT32_ELEMENTS)), &big);
// 5. Else,
// a. Let expected be ? ToInteger(expectedValue).
// b. Let replacement be ? ToInteger(replacementValue).
TNode<Number> old_value_integer = ToInteger_Inline(context, old_value);
TNode<Number> new_value_integer = ToInteger_Inline(context, new_value);
// 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
// 7. NOTE: The above check is not redundant with the check in
// ValidateIntegerTypedArray because the call to ToBigInt or ToInteger on the
// preceding lines can have arbitrary side effects, which could cause the
// buffer to become detached.
GotoIf(IsDetachedBuffer(array_buffer), &detached);
DebugSanityCheckAtomicIndex(array, index_word);
TNode<Word32T> old_value_word32 =
@ -429,6 +545,9 @@ TF_BUILTIN(AtomicsCompareExchange, SharedArrayBufferBuiltinsAssembler) {
TNode<Word32T> new_value_word32 =
TruncateTaggedToWord32(context, new_value_integer);
// Steps 8-14.
//
// (Not copied from ecma262 due to the axiomatic nature of the memory model.)
int32_t case_values[] = {
INT8_ELEMENTS, UINT8_ELEMENTS, INT16_ELEMENTS,
UINT16_ELEMENTS, INT32_ELEMENTS, UINT32_ELEMENTS,
@ -470,9 +589,15 @@ TF_BUILTIN(AtomicsCompareExchange, SharedArrayBufferBuiltinsAssembler) {
old_value_word32, new_value_word32)));
BIND(&big);
// 4. If typedArray.[[ContentType]] is BigInt, then
// a. Let expected be ? ToBigInt(expectedValue).
// b. Let replacement be ? ToBigInt(replacementValue).
TNode<BigInt> old_value_bigint = ToBigInt(context, old_value);
TNode<BigInt> new_value_bigint = ToBigInt(context, new_value);
// 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
GotoIf(IsDetachedBuffer(array_buffer), &detached);
DebugSanityCheckAtomicIndex(array, index_word);
TVARIABLE(UintPtrT, var_old_low);
@ -503,11 +628,17 @@ TF_BUILTIN(AtomicsCompareExchange, SharedArrayBufferBuiltinsAssembler) {
// This shouldn't happen, we've already validated the type.
BIND(&other);
Unreachable();
BIND(&detached);
{
ThrowTypeError(context, MessageTemplate::kDetachedOperation,
"Atomics.store");
}
#endif // V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64
// || V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X
}
#define BINOP_BUILTIN(op) \
#define BINOP_BUILTIN(op, method_name) \
TF_BUILTIN(Atomics##op, SharedArrayBufferBuiltinsAssembler) { \
TNode<Object> array = CAST(Parameter(Descriptor::kArray)); \
TNode<Object> index = CAST(Parameter(Descriptor::kIndex)); \
@ -515,31 +646,34 @@ TF_BUILTIN(AtomicsCompareExchange, SharedArrayBufferBuiltinsAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext)); \
AtomicBinopBuiltinCommon(array, index, value, context, \
&CodeAssembler::Atomic##op, \
Runtime::kAtomics##op); \
Runtime::kAtomics##op, method_name); \
}
// https://tc39.es/ecma262/#sec-atomics.add
BINOP_BUILTIN(Add)
BINOP_BUILTIN(Add, "Atomics.add")
// https://tc39.es/ecma262/#sec-atomics.sub
BINOP_BUILTIN(Sub)
BINOP_BUILTIN(Sub, "Atomics.sub")
// https://tc39.es/ecma262/#sec-atomics.and
BINOP_BUILTIN(And)
BINOP_BUILTIN(And, "Atomics.and")
// https://tc39.es/ecma262/#sec-atomics.or
BINOP_BUILTIN(Or)
BINOP_BUILTIN(Or, "Atomics.or")
// https://tc39.es/ecma262/#sec-atomics.xor
BINOP_BUILTIN(Xor)
BINOP_BUILTIN(Xor, "Atomics.xor")
#undef BINOP_BUILTIN
// https://tc39.es/ecma262/#sec-atomicreadmodifywrite
void SharedArrayBufferBuiltinsAssembler::AtomicBinopBuiltinCommon(
TNode<Object> maybe_array, TNode<Object> index, TNode<Object> value,
TNode<Context> context, AssemblerFunction function,
Runtime::FunctionId runtime_function) {
Runtime::FunctionId runtime_function, const char* method_name) {
// 1. Let buffer be ? ValidateIntegerTypedArray(typedArray).
Label detached(this);
TNode<Int32T> elements_kind;
TNode<RawPtrT> backing_store;
ValidateSharedTypedArray(maybe_array, context, &elements_kind,
&backing_store);
TNode<JSArrayBuffer> array_buffer = ValidateIntegerTypedArray(
maybe_array, context, &elements_kind, &backing_store, &detached);
TNode<JSTypedArray> array = CAST(maybe_array);
// 2. Let i be ? ValidateAtomicAccess(typedArray, index).
TNode<UintPtrT> index_word = ValidateAtomicAccess(array, index, context);
#if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64 || \
@ -550,16 +684,29 @@ void SharedArrayBufferBuiltinsAssembler::AtomicBinopBuiltinCommon(
Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this),
i64(this), u64(this), big(this), other(this);
// 3. Let arrayTypeName be typedArray.[[TypedArrayName]].
// 4. If typedArray.[[ContentType]] is BigInt, let v be ? ToBigInt(value).
STATIC_ASSERT(BIGINT64_ELEMENTS > INT32_ELEMENTS);
STATIC_ASSERT(BIGUINT64_ELEMENTS > INT32_ELEMENTS);
GotoIf(Int32GreaterThan(elements_kind, Int32Constant(INT32_ELEMENTS)), &big);
// 5. Otherwise, let v be ? ToInteger(value).
TNode<Number> value_integer = ToInteger_Inline(context, value);
// 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
// 7. NOTE: The above check is not redundant with the check in
// ValidateIntegerTypedArray because the call to ToBigInt or ToInteger on the
// preceding lines can have arbitrary side effects, which could cause the
// buffer to become detached.
GotoIf(IsDetachedBuffer(array_buffer), &detached);
DebugSanityCheckAtomicIndex(array, index_word);
TNode<Word32T> value_word32 = TruncateTaggedToWord32(context, value_integer);
// Steps 8-12.
//
// (Not copied from ecma262 due to the axiomatic nature of the memory model.)
int32_t case_values[] = {
INT8_ELEMENTS, UINT8_ELEMENTS, INT16_ELEMENTS,
UINT16_ELEMENTS, INT32_ELEMENTS, UINT32_ELEMENTS,
@ -599,8 +746,12 @@ void SharedArrayBufferBuiltinsAssembler::AtomicBinopBuiltinCommon(
WordShl(index_word, 2), value_word32, nullptr)));
BIND(&big);
// 4. If typedArray.[[ContentType]] is BigInt, let v be ? ToBigInt(value).
TNode<BigInt> value_bigint = ToBigInt(context, value);
// 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
GotoIf(IsDetachedBuffer(array_buffer), &detached);
DebugSanityCheckAtomicIndex(array, index_word);
TVARIABLE(UintPtrT, var_low);
@ -627,6 +778,9 @@ void SharedArrayBufferBuiltinsAssembler::AtomicBinopBuiltinCommon(
// This shouldn't happen, we've already validated the type.
BIND(&other);
Unreachable();
BIND(&detached);
ThrowTypeError(context, MessageTemplate::kDetachedOperation, method_name);
#endif // V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64
// || V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X
}

View File

@ -20,9 +20,9 @@ namespace v8 {
namespace internal {
// See builtins-arraybuffer.cc for implementations of
// SharedArrayBuffer.prototye.byteLength and SharedArrayBuffer.prototype.slice
// SharedArrayBuffer.prototype.byteLength and SharedArrayBuffer.prototype.slice
// #sec-atomics.islockfree
// https://tc39.es/ecma262/#sec-atomics.islockfree
inline bool AtomicIsLockFree(double size) {
// According to the standard, 1, 2, and 4 byte atomics are supposed to be
// 'lock free' on every platform. 'Lock free' means that all possible uses of
@ -39,7 +39,7 @@ inline bool AtomicIsLockFree(double size) {
return size == 1 || size == 2 || size == 4 || size == 8;
}
// ES #sec-atomics.islockfree
// https://tc39.es/ecma262/#sec-atomics.islockfree
BUILTIN(AtomicsIsLockFree) {
HandleScope scope(isolate);
Handle<Object> size = args.atOrUndefined(isolate, 1);
@ -48,37 +48,45 @@ BUILTIN(AtomicsIsLockFree) {
return *isolate->factory()->ToBoolean(AtomicIsLockFree(size->Number()));
}
// ES #sec-validatesharedintegertypedarray
V8_WARN_UNUSED_RESULT MaybeHandle<JSTypedArray> ValidateSharedIntegerTypedArray(
Isolate* isolate, Handle<Object> object,
// https://tc39.es/ecma262/#sec-validatesharedintegertypedarray
V8_WARN_UNUSED_RESULT MaybeHandle<JSTypedArray> ValidateIntegerTypedArray(
Isolate* isolate, Handle<Object> object, const char* method_name,
bool only_int32_and_big_int64 = false) {
if (object->IsJSTypedArray()) {
Handle<JSTypedArray> typed_array = Handle<JSTypedArray>::cast(object);
if (typed_array->GetBuffer()->is_shared()) {
if (only_int32_and_big_int64) {
if (typed_array->type() == kExternalInt32Array ||
typed_array->type() == kExternalBigInt64Array) {
return typed_array;
}
} else {
if (typed_array->type() != kExternalFloat32Array &&
typed_array->type() != kExternalFloat64Array &&
typed_array->type() != kExternalUint8ClampedArray)
return typed_array;
if (typed_array->WasDetached()) {
THROW_NEW_ERROR(
isolate,
NewTypeError(
MessageTemplate::kDetachedOperation,
isolate->factory()->NewStringFromAsciiChecked(method_name)),
JSTypedArray);
}
if (only_int32_and_big_int64) {
if (typed_array->type() == kExternalInt32Array ||
typed_array->type() == kExternalBigInt64Array) {
return typed_array;
}
} else {
if (typed_array->type() != kExternalFloat32Array &&
typed_array->type() != kExternalFloat64Array &&
typed_array->type() != kExternalUint8ClampedArray)
return typed_array;
}
}
THROW_NEW_ERROR(
isolate,
NewTypeError(only_int32_and_big_int64
? MessageTemplate::kNotInt32OrBigInt64SharedTypedArray
: MessageTemplate::kNotIntegerSharedTypedArray,
? MessageTemplate::kNotInt32OrBigInt64TypedArray
: MessageTemplate::kNotIntegerTypedArray,
object),
JSTypedArray);
}
// ES #sec-validateatomicaccess
// https://tc39.es/ecma262/#sec-validateatomicaccess
// ValidateAtomicAccess( typedArray, requestIndex )
V8_WARN_UNUSED_RESULT Maybe<size_t> ValidateAtomicAccess(
Isolate* isolate, Handle<JSTypedArray> typed_array,
@ -91,8 +99,9 @@ V8_WARN_UNUSED_RESULT Maybe<size_t> ValidateAtomicAccess(
Nothing<size_t>());
size_t access_index;
size_t typed_array_length = typed_array->length();
if (!TryNumberToSize(*access_index_obj, &access_index) ||
typed_array->WasDetached() || access_index >= typed_array->length()) {
access_index >= typed_array_length) {
isolate->Throw(*isolate->factory()->NewRangeError(
MessageTemplate::kInvalidAtomicAccessIndex));
return Nothing<size_t>();
@ -122,12 +131,18 @@ BUILTIN(AtomicsNotify) {
Handle<JSTypedArray> sta;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, sta, ValidateSharedIntegerTypedArray(isolate, array, true));
isolate, sta,
ValidateIntegerTypedArray(isolate, array, "Atomics.notify", true));
// 2. Let i be ? ValidateAtomicAccess(typedArray, index).
Maybe<size_t> maybe_index = ValidateAtomicAccess(isolate, sta, index);
if (maybe_index.IsNothing()) return ReadOnlyRoots(isolate).exception();
size_t i = maybe_index.FromJust();
// 3. If count is undefined, let c be +∞.
// 4. Else,
// a. Let intCount be ? ToInteger(count).
// b. Let c be max(intCount, 0).
uint32_t c;
if (count->IsUndefined(isolate)) {
c = kMaxUInt32;
@ -143,9 +158,17 @@ BUILTIN(AtomicsNotify) {
c = static_cast<uint32_t>(count_double);
}
// Steps 5-9 performed in FutexEmulation::Wake.
// 10. If IsSharedArrayBuffer(buffer) is false, return 0.
Handle<JSArrayBuffer> array_buffer = sta->GetBuffer();
size_t wake_addr;
if (V8_UNLIKELY(!sta->GetBuffer()->is_shared())) {
return Smi::FromInt(0);
}
// Steps 11-17 performed in FutexEmulation::Wake.
if (sta->type() == kExternalBigInt64Array) {
wake_addr = GetAddress64(i, sta->byte_offset());
} else {
@ -158,19 +181,26 @@ BUILTIN(AtomicsNotify) {
Object DoWait(Isolate* isolate, FutexEmulation::WaitMode mode,
Handle<Object> array, Handle<Object> index, Handle<Object> value,
Handle<Object> timeout) {
// 1. Let buffer be ? ValidateSharedIntegerTypedArray(typedArray, true).
// 1. Let buffer be ? ValidateIntegerTypedArray(typedArray, true).
Handle<JSTypedArray> sta;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, sta, ValidateSharedIntegerTypedArray(isolate, array, true));
isolate, sta,
ValidateIntegerTypedArray(isolate, array, "Atomics.wait", true));
// 2. Let i be ? ValidateAtomicAccess(typedArray, index).
// 2. If IsSharedArrayBuffer(buffer) is false, throw a TypeError exception.
if (V8_UNLIKELY(!sta->GetBuffer()->is_shared())) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kNotSharedTypedArray, array));
}
// 3. Let i be ? ValidateAtomicAccess(typedArray, index).
Maybe<size_t> maybe_index = ValidateAtomicAccess(isolate, sta, index);
if (maybe_index.IsNothing()) return ReadOnlyRoots(isolate).exception();
size_t i = maybe_index.FromJust();
// 3. Let arrayTypeName be typedArray.[[TypedArrayName]].
// 4. If arrayTypeName is "BigInt64Array", let v be ? ToBigInt64(value).
// 5. Otherwise, let v be ? ToInt32(value).
// 4. Let arrayTypeName be typedArray.[[TypedArrayName]].
// 5. If arrayTypeName is "BigInt64Array", let v be ? ToBigInt64(value).
// 6. Otherwise, let v be ? ToInt32(value).
if (sta->type() == kExternalBigInt64Array) {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, value,
BigInt::FromObject(isolate, value));
@ -180,8 +210,8 @@ Object DoWait(Isolate* isolate, FutexEmulation::WaitMode mode,
Object::ToInt32(isolate, value));
}
// 6. Let q be ? ToNumber(timeout).
// 7. If q is NaN, let t be +∞, else let t be max(q, 0).
// 7. Let q be ? ToNumber(timeout).
// 8. If q is NaN, let t be +∞, else let t be max(q, 0).
double timeout_number;
if (timeout->IsUndefined(isolate)) {
timeout_number = ReadOnlyRoots(isolate).infinity_value().Number();
@ -195,7 +225,7 @@ Object DoWait(Isolate* isolate, FutexEmulation::WaitMode mode,
timeout_number = 0;
}
// 8. If mode is sync, then
// 9. If mode is sync, then
// a. Let B be AgentCanSuspend().
// b. If B is false, throw a TypeError exception.
if (mode == FutexEmulation::WaitMode::kSync &&
@ -218,7 +248,7 @@ Object DoWait(Isolate* isolate, FutexEmulation::WaitMode mode,
}
}
// ES #sec-atomics.wait
// https://tc39.es/ecma262/#sec-atomics.wait
// Atomics.wait( typedArray, index, value, timeout )
BUILTIN(AtomicsWait) {
HandleScope scope(isolate);

View File

@ -231,28 +231,6 @@ TNode<JSFunction> TypedArrayBuiltinsAssembler::GetDefaultConstructor(
LoadContextElement(LoadNativeContext(context), context_slot.value()));
}
TNode<JSArrayBuffer> TypedArrayBuiltinsAssembler::GetBuffer(
TNode<Context> context, TNode<JSTypedArray> array) {
Label call_runtime(this), done(this);
TVARIABLE(Object, var_result);
TNode<JSArrayBuffer> buffer = LoadJSArrayBufferViewBuffer(array);
GotoIf(IsDetachedBuffer(buffer), &call_runtime);
TNode<RawPtrT> backing_store = LoadJSArrayBufferBackingStorePtr(buffer);
GotoIf(WordEqual(backing_store, IntPtrConstant(0)), &call_runtime);
var_result = buffer;
Goto(&done);
BIND(&call_runtime);
{
var_result = CallRuntime(Runtime::kTypedArrayGetBuffer, context, array);
Goto(&done);
}
BIND(&done);
return CAST(var_result.value());
}
TNode<JSTypedArray> TypedArrayBuiltinsAssembler::ValidateTypedArray(
TNode<Context> context, TNode<Object> obj, const char* method_name) {
// If it is not a typed array, throw

View File

@ -45,9 +45,6 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
TNode<JSFunction> GetDefaultConstructor(TNode<Context> context,
TNode<JSTypedArray> exemplar);
TNode<JSArrayBuffer> GetBuffer(TNode<Context> context,
TNode<JSTypedArray> array);
TNode<JSTypedArray> ValidateTypedArray(TNode<Context> context,
TNode<Object> obj,
const char* method_name);

View File

@ -17,7 +17,7 @@ transitioning javascript builtin TypedArrayPrototypeSubArray(
MessageTemplate::kIncompatibleMethodReceiver, methodName);
// 5. Let buffer be O.[[ViewedArrayBuffer]].
const buffer = typed_array::GetBuffer(source);
const buffer = typed_array::GetTypedArrayBuffer(source);
// 6. Let srcLength be O.[[ArrayLength]].
const srcLength: uintptr = source.length;

View File

@ -63,8 +63,8 @@ extern macro TypedArrayBuiltinsAssembler::CallCMemmove(
RawPtr, RawPtr, uintptr): void;
extern macro TypedArrayBuiltinsAssembler::CallCMemset(
RawPtr, intptr, uintptr): void;
extern macro TypedArrayBuiltinsAssembler::GetBuffer(implicit context: Context)(
JSTypedArray): JSArrayBuffer;
extern macro GetTypedArrayBuffer(implicit context: Context)(JSTypedArray):
JSArrayBuffer;
extern macro TypedArrayBuiltinsAssembler::GetTypedArrayElementsInfo(
JSTypedArray): TypedArrayElementsInfo;
extern macro TypedArrayBuiltinsAssembler::GetTypedArrayElementsInfo(Map):

View File

@ -12528,6 +12528,28 @@ TNode<UintPtrT> CodeStubAssembler::LoadJSTypedArrayLength(
return LoadObjectField<UintPtrT>(typed_array, JSTypedArray::kLengthOffset);
}
TNode<JSArrayBuffer> CodeStubAssembler::GetTypedArrayBuffer(
TNode<Context> context, TNode<JSTypedArray> array) {
Label call_runtime(this), done(this);
TVARIABLE(Object, var_result);
TNode<JSArrayBuffer> buffer = LoadJSArrayBufferViewBuffer(array);
GotoIf(IsDetachedBuffer(buffer), &call_runtime);
TNode<RawPtrT> backing_store = LoadJSArrayBufferBackingStorePtr(buffer);
GotoIf(WordEqual(backing_store, IntPtrConstant(0)), &call_runtime);
var_result = buffer;
Goto(&done);
BIND(&call_runtime);
{
var_result = CallRuntime(Runtime::kTypedArrayGetBuffer, context, array);
Goto(&done);
}
BIND(&done);
return CAST(var_result.value());
}
CodeStubArguments::CodeStubArguments(CodeStubAssembler* assembler,
TNode<IntPtrT> argc, TNode<RawPtrT> fp)
: assembler_(assembler),

View File

@ -3442,6 +3442,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
// JSTypedArray helpers
TNode<UintPtrT> LoadJSTypedArrayLength(TNode<JSTypedArray> typed_array);
TNode<RawPtrT> LoadJSTypedArrayDataPtr(TNode<JSTypedArray> typed_array);
TNode<JSArrayBuffer> GetTypedArrayBuffer(TNode<Context> context,
TNode<JSTypedArray> array);
template <typename TIndex>
TNode<IntPtrT> ElementOffsetFromIndex(TNode<TIndex> index, ElementsKind kind,

View File

@ -144,9 +144,10 @@ namespace internal {
T(NotSuperConstructor, "Super constructor % of % is not a constructor") \
T(NotSuperConstructorAnonymousClass, \
"Super constructor % of anonymous class is not a constructor") \
T(NotIntegerSharedTypedArray, "% is not an integer shared typed array.") \
T(NotInt32OrBigInt64SharedTypedArray, \
"% is not an int32 or BigInt64 shared typed array.") \
T(NotIntegerTypedArray, "% is not an integer typed array.") \
T(NotInt32OrBigInt64TypedArray, \
"% is not an int32 or BigInt64 typed array.") \
T(NotSharedTypedArray, "% is not a shared typed array.") \
T(ObjectGetterExpectingFunction, \
"Object.prototype.__defineGetter__: Expecting function") \
T(ObjectGetterCallable, "Getter must be a function: %") \

View File

@ -11,9 +11,8 @@
#include "src/objects/js-array-buffer-inl.h"
#include "src/runtime/runtime-utils.h"
// Implement Atomic accesses to SharedArrayBuffers as defined in the
// SharedArrayBuffer draft spec, found here
// https://github.com/tc39/ecmascript_sharedmem
// Implement Atomic accesses to ArrayBuffers and SharedArrayBuffers.
// https://tc39.es/ecma262/#sec-atomics
namespace v8 {
namespace internal {
@ -341,17 +340,27 @@ struct Xor {
V(Uint32, uint32, UINT32, uint32_t) \
V(Int32, int32, INT32, int32_t)
#define THROW_ERROR_RETURN_FAILURE_ON_DETACHED(isolate, sta, method_name) \
do { \
if (V8_UNLIKELY(sta->WasDetached())) { \
THROW_NEW_ERROR_RETURN_FAILURE( \
isolate, NewTypeError(MessageTemplate::kDetachedOperation, \
isolate->factory()->NewStringFromAsciiChecked( \
method_name))); \
} \
} while (false)
// This is https://tc39.github.io/ecma262/#sec-getmodifysetvalueinbuffer
// but also includes the ToInteger/ToBigInt conversion that's part of
// https://tc39.github.io/ecma262/#sec-atomicreadmodifywrite
template <template <typename> class Op>
Object GetModifySetValueInBuffer(RuntimeArguments args, Isolate* isolate) {
Object GetModifySetValueInBuffer(RuntimeArguments args, Isolate* isolate,
const char* method_name) {
HandleScope scope(isolate);
DCHECK_EQ(3, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, sta, 0);
CONVERT_SIZE_ARG_CHECKED(index, 1);
CONVERT_ARG_HANDLE_CHECKED(Object, value_obj, 2);
CHECK(sta->GetBuffer()->is_shared());
uint8_t* source = static_cast<uint8_t*>(sta->GetBuffer()->backing_store()) +
sta->byte_offset();
@ -360,7 +369,9 @@ Object GetModifySetValueInBuffer(RuntimeArguments args, Isolate* isolate) {
Handle<BigInt> bigint;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, bigint,
BigInt::FromObject(isolate, value_obj));
// SharedArrayBuffers are not detachable.
THROW_ERROR_RETURN_FAILURE_ON_DETACHED(isolate, sta, method_name);
CHECK_LT(index, sta->length());
if (sta->type() == kExternalBigInt64Array) {
return Op<int64_t>::Do(isolate, source, index, bigint);
@ -372,7 +383,9 @@ Object GetModifySetValueInBuffer(RuntimeArguments args, Isolate* isolate) {
Handle<Object> value;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, value,
Object::ToInteger(isolate, value_obj));
// SharedArrayBuffers are not detachable.
THROW_ERROR_RETURN_FAILURE_ON_DETACHED(isolate, sta, method_name);
CHECK_LT(index, sta->length());
switch (sta->type()) {
@ -395,14 +408,13 @@ RUNTIME_FUNCTION(Runtime_AtomicsLoad64) {
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, sta, 0);
CONVERT_SIZE_ARG_CHECKED(index, 1);
CHECK(sta->GetBuffer()->is_shared());
uint8_t* source = static_cast<uint8_t*>(sta->GetBuffer()->backing_store()) +
sta->byte_offset();
DCHECK(sta->type() == kExternalBigInt64Array ||
sta->type() == kExternalBigUint64Array);
// SharedArrayBuffers are not detachable.
DCHECK(!sta->WasDetached());
CHECK_LT(index, sta->length());
if (sta->type() == kExternalBigInt64Array) {
return Load<int64_t>::Do(isolate, source, index);
@ -417,7 +429,6 @@ RUNTIME_FUNCTION(Runtime_AtomicsStore64) {
CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, sta, 0);
CONVERT_SIZE_ARG_CHECKED(index, 1);
CONVERT_ARG_HANDLE_CHECKED(Object, value_obj, 2);
CHECK(sta->GetBuffer()->is_shared());
uint8_t* source = static_cast<uint8_t*>(sta->GetBuffer()->backing_store()) +
sta->byte_offset();
@ -428,7 +439,7 @@ RUNTIME_FUNCTION(Runtime_AtomicsStore64) {
DCHECK(sta->type() == kExternalBigInt64Array ||
sta->type() == kExternalBigUint64Array);
// SharedArrayBuffers are not detachable.
DCHECK(!sta->WasDetached());
CHECK_LT(index, sta->length());
if (sta->type() == kExternalBigInt64Array) {
Store<int64_t>::Do(isolate, source, index, bigint);
@ -440,7 +451,7 @@ RUNTIME_FUNCTION(Runtime_AtomicsStore64) {
}
RUNTIME_FUNCTION(Runtime_AtomicsExchange) {
return GetModifySetValueInBuffer<Exchange>(args, isolate);
return GetModifySetValueInBuffer<Exchange>(args, isolate, "Atomics.exchange");
}
RUNTIME_FUNCTION(Runtime_AtomicsCompareExchange) {
@ -450,7 +461,6 @@ RUNTIME_FUNCTION(Runtime_AtomicsCompareExchange) {
CONVERT_SIZE_ARG_CHECKED(index, 1);
CONVERT_ARG_HANDLE_CHECKED(Object, old_value_obj, 2);
CONVERT_ARG_HANDLE_CHECKED(Object, new_value_obj, 3);
CHECK(sta->GetBuffer()->is_shared());
CHECK_LT(index, sta->length());
uint8_t* source = static_cast<uint8_t*>(sta->GetBuffer()->backing_store()) +
@ -463,7 +473,10 @@ RUNTIME_FUNCTION(Runtime_AtomicsCompareExchange) {
isolate, old_bigint, BigInt::FromObject(isolate, old_value_obj));
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, new_bigint, BigInt::FromObject(isolate, new_value_obj));
// SharedArrayBuffers are not detachable.
THROW_ERROR_RETURN_FAILURE_ON_DETACHED(isolate, sta,
"Atomics.compareExchange");
CHECK_LT(index, sta->length());
if (sta->type() == kExternalBigInt64Array) {
return DoCompareExchange<int64_t>(isolate, source, index, old_bigint,
@ -480,8 +493,9 @@ RUNTIME_FUNCTION(Runtime_AtomicsCompareExchange) {
Object::ToInteger(isolate, old_value_obj));
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, new_value,
Object::ToInteger(isolate, new_value_obj));
// SharedArrayBuffers are not detachable.
CHECK_LT(index, sta->length());
THROW_ERROR_RETURN_FAILURE_ON_DETACHED(isolate, sta,
"Atomics.compareExchange");
switch (sta->type()) {
#define TYPED_ARRAY_CASE(Type, typeName, TYPE, ctype) \
@ -502,31 +516,31 @@ RUNTIME_FUNCTION(Runtime_AtomicsCompareExchange) {
// ES #sec-atomics.add
// Atomics.add( typedArray, index, value )
RUNTIME_FUNCTION(Runtime_AtomicsAdd) {
return GetModifySetValueInBuffer<Add>(args, isolate);
return GetModifySetValueInBuffer<Add>(args, isolate, "Atomics.add");
}
// ES #sec-atomics.sub
// Atomics.sub( typedArray, index, value )
RUNTIME_FUNCTION(Runtime_AtomicsSub) {
return GetModifySetValueInBuffer<Sub>(args, isolate);
return GetModifySetValueInBuffer<Sub>(args, isolate, "Atomics.sub");
}
// ES #sec-atomics.and
// Atomics.and( typedArray, index, value )
RUNTIME_FUNCTION(Runtime_AtomicsAnd) {
return GetModifySetValueInBuffer<And>(args, isolate);
return GetModifySetValueInBuffer<And>(args, isolate, "Atomics.and");
}
// ES #sec-atomics.or
// Atomics.or( typedArray, index, value )
RUNTIME_FUNCTION(Runtime_AtomicsOr) {
return GetModifySetValueInBuffer<Or>(args, isolate);
return GetModifySetValueInBuffer<Or>(args, isolate, "Atomics.or");
}
// ES #sec-atomics.xor
// Atomics.xor( typedArray, index, value )
RUNTIME_FUNCTION(Runtime_AtomicsXor) {
return GetModifySetValueInBuffer<Xor>(args, isolate);
return GetModifySetValueInBuffer<Xor>(args, isolate, "Atomics.xor");
}
#undef INTEGER_TYPED_ARRAYS

View File

@ -84,7 +84,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(265),
/* 53 S> */ B(Wide), B(LdaSmi), I16(266),
B(Star), R(3),
B(LdaConstant), U8(0),
B(Star), R(4),
@ -115,7 +115,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(264),
/* 46 S> */ B(Wide), B(LdaSmi), I16(265),
B(Star), R(3),
B(LdaConstant), U8(0),
B(Star), R(4),
@ -146,7 +146,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(265),
/* 53 S> */ B(Wide), B(LdaSmi), I16(266),
B(Star), R(3),
B(LdaConstant), U8(0),
B(Star), R(4),
@ -177,7 +177,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(264),
/* 46 S> */ B(Wide), B(LdaSmi), I16(265),
B(Star), R(4),
B(LdaConstant), U8(0),
B(Star), R(5),

View File

@ -57,7 +57,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(263),
/* 49 S> */ B(Wide), B(LdaSmi), I16(264),
B(Star), R(3),
B(LdaConstant), U8(0),
B(Star), R(4),
@ -89,7 +89,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(263),
/* 49 S> */ B(Wide), B(LdaSmi), I16(264),
B(Star), R(3),
B(LdaConstant), U8(0),
B(Star), R(4),

View File

@ -25,7 +25,7 @@ bytecodes: [
B(TestReferenceEqual), R(this),
B(Mov), R(this), R(1),
B(JumpIfTrue), U8(18),
B(Wide), B(LdaSmi), I16(261),
B(Wide), B(LdaSmi), I16(262),
B(Star), R(2),
B(LdaConstant), U8(0),
B(Star), R(3),
@ -56,7 +56,7 @@ frame size: 2
parameter count: 1
bytecode array length: 16
bytecodes: [
/* 56 S> */ B(Wide), B(LdaSmi), I16(263),
/* 56 S> */ B(Wide), B(LdaSmi), I16(264),
B(Star), R(0),
B(LdaConstant), U8(0),
B(Star), R(1),
@ -83,7 +83,7 @@ frame size: 2
parameter count: 1
bytecode array length: 16
bytecodes: [
/* 56 S> */ B(Wide), B(LdaSmi), I16(263),
/* 56 S> */ B(Wide), B(LdaSmi), I16(264),
B(Star), R(0),
B(LdaConstant), U8(0),
B(Star), R(1),
@ -122,7 +122,7 @@ bytecodes: [
/* 94 E> */ B(TestReferenceEqual), R(this),
B(Mov), R(this), R(0),
B(JumpIfTrue), U8(18),
B(Wide), B(LdaSmi), I16(261),
B(Wide), B(LdaSmi), I16(262),
B(Star), R(2),
B(LdaConstant), U8(0),
B(Star), R(3),
@ -144,7 +144,7 @@ bytecodes: [
/* 109 E> */ B(TestReferenceEqual), R(this),
B(Mov), R(this), R(1),
B(JumpIfTrue), U8(18),
B(Wide), B(LdaSmi), I16(262),
B(Wide), B(LdaSmi), I16(263),
B(Star), R(3),
B(LdaConstant), U8(0),
B(Star), R(4),
@ -159,7 +159,7 @@ bytecodes: [
/* 133 E> */ B(TestReferenceEqual), R(this),
B(Mov), R(this), R(0),
B(JumpIfTrue), U8(18),
B(Wide), B(LdaSmi), I16(261),
B(Wide), B(LdaSmi), I16(262),
B(Star), R(2),
B(LdaConstant), U8(0),
B(Star), R(3),
@ -189,7 +189,7 @@ frame size: 2
parameter count: 1
bytecode array length: 16
bytecodes: [
/* 60 S> */ B(Wide), B(LdaSmi), I16(265),
/* 60 S> */ B(Wide), B(LdaSmi), I16(266),
B(Star), R(0),
B(LdaConstant), U8(0),
B(Star), R(1),
@ -215,7 +215,7 @@ frame size: 2
parameter count: 1
bytecode array length: 16
bytecodes: [
/* 53 S> */ B(Wide), B(LdaSmi), I16(264),
/* 53 S> */ B(Wide), B(LdaSmi), I16(265),
B(Star), R(0),
B(LdaConstant), U8(0),
B(Star), R(1),
@ -241,7 +241,7 @@ frame size: 2
parameter count: 1
bytecode array length: 16
bytecodes: [
/* 60 S> */ B(Wide), B(LdaSmi), I16(265),
/* 60 S> */ B(Wide), B(LdaSmi), I16(266),
B(Star), R(0),
B(LdaConstant), U8(0),
B(Star), R(1),
@ -267,7 +267,7 @@ frame size: 3
parameter count: 1
bytecode array length: 16
bytecodes: [
/* 46 S> */ B(Wide), B(LdaSmi), I16(264),
/* 46 S> */ B(Wide), B(LdaSmi), I16(265),
B(Star), R(1),
B(LdaConstant), U8(0),
B(Star), R(2),

View File

@ -0,0 +1,99 @@
// Copyright 2020 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-sharedarraybuffer --allow-natives-syntax
// Test for the buffer becoming detached in surprising places. Test262 covers
// the case where the buffer starts off detached.
function makePoison(arr, val) {
return { valueOf() { %ArrayBufferDetach(arr.buffer); return val; } };
}
function forEachIntegerTypedArray(cb) {
for (let C of [Int8Array, Uint8Array, Uint8ClampedArray,
Int16Array, Uint16Array,
Int32Array, Uint32Array,
BigInt64Array, BigUint64Array]) {
let arr = new C(32);
let zero, valuePoison;
if (C === BigInt64Array || C === BigUint64Array) {
zero = 0n;
valuePoison = makePoison(arr, 0n);
} else {
zero = 0;
valuePoison = makePoison(arr, 0);
}
cb(arr, makePoison(arr, 0), valuePoison, zero);
}
}
forEachIntegerTypedArray((arr, indexPoison, valuePoison, zero) => {
assertThrows(() => Atomics.add(arr, indexPoison, zero), TypeError);
});
forEachIntegerTypedArray((arr, indexPoison, valuePoison, zero) => {
assertThrows(() => Atomics.add(arr, 0, valuePoison), TypeError);
});
forEachIntegerTypedArray((arr, indexPoison, valuePoison, zero) => {
assertThrows(() => Atomics.and(arr, indexPoison, zero), TypeError);
});
forEachIntegerTypedArray((arr, indexPoison, valuePoison, zero) => {
assertThrows(() => Atomics.and(arr, 0, valuePoison), TypeError);
});
forEachIntegerTypedArray((arr, indexPoison, valuePoison, zero) => {
assertThrows(() => Atomics.compareExchange(arr, indexPoison, zero, zero),
TypeError);
});
forEachIntegerTypedArray((arr, indexPoison, valuePoison, zero) => {
assertThrows(() => Atomics.compareExchange(arr, 0, valuePoison, zero),
TypeError);
});
forEachIntegerTypedArray((arr, indexPoison, valuePoison, zero) => {
assertThrows(() => Atomics.compareExchange(arr, 0, zero, valuePoison),
TypeError);
});
forEachIntegerTypedArray((arr, indexPoison, valuePoison, zero) => {
assertThrows(() => Atomics.exchange(arr, indexPoison, zero), TypeError);
});
forEachIntegerTypedArray((arr, indexPoison, valuePoison, zero) => {
assertThrows(() => Atomics.exchange(arr, 0, valuePoison), TypeError);
});
forEachIntegerTypedArray((arr, indexPoison, valuePoison, zero) => {
assertThrows(() => Atomics.load(arr, indexPoison), TypeError);
});
forEachIntegerTypedArray((arr, indexPoison, valuePoison, zero) => {
assertThrows(() => Atomics.or(arr, indexPoison, zero), TypeError);
});
forEachIntegerTypedArray((arr, indexPoison, valuePoison, zero) => {
assertThrows(() => Atomics.or(arr, 0, valuePoison), TypeError);
});
forEachIntegerTypedArray((arr, indexPoison, valuePoison, zero) => {
assertThrows(() => Atomics.store(arr, indexPoison, 0n), TypeError);
});
forEachIntegerTypedArray((arr, indexPoison, valuePoison, zero) => {
assertThrows(() => Atomics.store(arr, 0, valuePoison), TypeError);
});
forEachIntegerTypedArray((arr, indexPoison, valuePoison, zero) => {
assertThrows(() => Atomics.sub(arr, indexPoison, zero), TypeError);
});
forEachIntegerTypedArray((arr, indexPoison, valuePoison, zero) => {
assertThrows(() => Atomics.sub(arr, 0, valuePoison), TypeError);
});
forEachIntegerTypedArray((arr, indexPoison, valuePoison, zero) => {
assertThrows(() => Atomics.xor(arr, indexPoison, zero), TypeError);
});
forEachIntegerTypedArray((arr, indexPoison, valuePoison, zero) => {
assertThrows(() => Atomics.xor(arr, 0, valuePoison), TypeError);
});
// Atomics.wait always throws on non-SABs.
// Atomics.notify always returns 0 on non-SABs.

View File

@ -53,15 +53,14 @@ var IntegerTypedArrayConstructors = [
(function TestBadArray() {
var ab = new ArrayBuffer(16);
var u32a = new Uint32Array(16);
var sab = new SharedArrayBuffer(128);
var sf32a = new Float32Array(sab);
var sf64a = new Float64Array(sab);
var u8ca = new Uint8ClampedArray(sab);
// Atomic ops required integer shared typed arrays
// Atomic ops required integer typed arrays
var badArrayTypes = [
undefined, 1, 'hi', 3.4, ab, u32a, sab, sf32a, sf64a, u8ca
undefined, 1, 'hi', 3.4, ab, sab, sf32a, sf64a, u8ca
];
badArrayTypes.forEach(function(o) {
assertThrows(function() { Atomics.compareExchange(o, 0, 0, 0); },

View File

@ -4,7 +4,7 @@
// Flags: --allow-natives-syntax --harmony-sharedarraybuffer
(function TestFailsWithNonSharedArray() {
(function TestNonSharedArrayBehavior() {
var ab = new ArrayBuffer(16);
var i8a = new Int8Array(ab);
@ -18,9 +18,13 @@
var f64a = new Float64Array(ab);
[i8a, i16a, i32a, ui8a, ui8ca, ui16a, ui32a, f32a, f64a].forEach(function(
ta) {
ta) {
assertThrows(function() { Atomics.wait(ta, 0, 0); });
assertThrows(function() { Atomics.notify(ta, 0, 1); });
if (ta === i32a) {
assertEquals(0, Atomics.notify(ta, 0, 1));
} else {
assertThrows(function() { Atomics.notify(ta, 0, 1); });
}
});
})();

View File

@ -525,32 +525,6 @@
# https://bugs.chromium.org/p/v8/issues/detail?id=10383
'built-ins/RegExp/prototype/Symbol.replace/fn-invoke-args-empty-result': [FAIL],
# https://crbug.com/v8/10687
'built-ins/Atomics/add/bigint/non-shared-bufferdata': [FAIL],
'built-ins/Atomics/add/non-shared-bufferdata': [FAIL],
'built-ins/Atomics/and/bigint/non-shared-bufferdata': [FAIL],
'built-ins/Atomics/and/non-shared-bufferdata': [FAIL],
'built-ins/Atomics/compareExchange/bigint/non-shared-bufferdata': [FAIL],
'built-ins/Atomics/compareExchange/non-shared-bufferdata': [FAIL],
'built-ins/Atomics/exchange/bigint/non-shared-bufferdata': [FAIL],
'built-ins/Atomics/exchange/non-shared-bufferdata': [FAIL],
'built-ins/Atomics/load/bigint/non-shared-bufferdata': [FAIL],
'built-ins/Atomics/load/non-shared-bufferdata': [FAIL],
'built-ins/Atomics/notify/bigint/non-shared-bufferdata-count-evaluation-throws': [FAIL],
'built-ins/Atomics/notify/bigint/non-shared-bufferdata-index-evaluation-throws': [FAIL],
'built-ins/Atomics/notify/bigint/non-shared-bufferdata-returns-0': [FAIL],
'built-ins/Atomics/notify/non-shared-bufferdata-count-evaluation-throws': [FAIL],
'built-ins/Atomics/notify/non-shared-bufferdata-index-evaluation-throws': [FAIL],
'built-ins/Atomics/notify/non-shared-bufferdata-returns-0': [FAIL],
'built-ins/Atomics/or/bigint/non-shared-bufferdata': [FAIL],
'built-ins/Atomics/or/non-shared-bufferdata': [FAIL],
'built-ins/Atomics/store/bigint/non-shared-bufferdata': [FAIL],
'built-ins/Atomics/store/non-shared-bufferdata': [FAIL],
'built-ins/Atomics/sub/bigint/non-shared-bufferdata': [FAIL],
'built-ins/Atomics/sub/non-shared-bufferdata': [FAIL],
'built-ins/Atomics/xor/bigint/non-shared-bufferdata': [FAIL],
'built-ins/Atomics/xor/non-shared-bufferdata': [FAIL],
# https://crbug.com/v8/10789
'built-ins/Function/prototype/toString/built-in-function-object': [FAIL],

View File

@ -444,27 +444,27 @@ KNOWN_OBJECTS = {
("old_space", 0x02a61): "StringSplitCache",
("old_space", 0x02e69): "RegExpMultipleCache",
("old_space", 0x03271): "BuiltinsConstantsTable",
("old_space", 0x03625): "AsyncFunctionAwaitRejectSharedFun",
("old_space", 0x0364d): "AsyncFunctionAwaitResolveSharedFun",
("old_space", 0x03675): "AsyncGeneratorAwaitRejectSharedFun",
("old_space", 0x0369d): "AsyncGeneratorAwaitResolveSharedFun",
("old_space", 0x036c5): "AsyncGeneratorYieldResolveSharedFun",
("old_space", 0x036ed): "AsyncGeneratorReturnResolveSharedFun",
("old_space", 0x03715): "AsyncGeneratorReturnClosedRejectSharedFun",
("old_space", 0x0373d): "AsyncGeneratorReturnClosedResolveSharedFun",
("old_space", 0x03765): "AsyncIteratorValueUnwrapSharedFun",
("old_space", 0x0378d): "PromiseAllResolveElementSharedFun",
("old_space", 0x037b5): "PromiseAllSettledResolveElementSharedFun",
("old_space", 0x037dd): "PromiseAllSettledRejectElementSharedFun",
("old_space", 0x03805): "PromiseAnyRejectElementSharedFun",
("old_space", 0x0382d): "PromiseCapabilityDefaultRejectSharedFun",
("old_space", 0x03855): "PromiseCapabilityDefaultResolveSharedFun",
("old_space", 0x0387d): "PromiseCatchFinallySharedFun",
("old_space", 0x038a5): "PromiseGetCapabilitiesExecutorSharedFun",
("old_space", 0x038cd): "PromiseThenFinallySharedFun",
("old_space", 0x038f5): "PromiseThrowerFinallySharedFun",
("old_space", 0x0391d): "PromiseValueThunkFinallySharedFun",
("old_space", 0x03945): "ProxyRevokeSharedFun",
("old_space", 0x03645): "AsyncFunctionAwaitRejectSharedFun",
("old_space", 0x0366d): "AsyncFunctionAwaitResolveSharedFun",
("old_space", 0x03695): "AsyncGeneratorAwaitRejectSharedFun",
("old_space", 0x036bd): "AsyncGeneratorAwaitResolveSharedFun",
("old_space", 0x036e5): "AsyncGeneratorYieldResolveSharedFun",
("old_space", 0x0370d): "AsyncGeneratorReturnResolveSharedFun",
("old_space", 0x03735): "AsyncGeneratorReturnClosedRejectSharedFun",
("old_space", 0x0375d): "AsyncGeneratorReturnClosedResolveSharedFun",
("old_space", 0x03785): "AsyncIteratorValueUnwrapSharedFun",
("old_space", 0x037ad): "PromiseAllResolveElementSharedFun",
("old_space", 0x037d5): "PromiseAllSettledResolveElementSharedFun",
("old_space", 0x037fd): "PromiseAllSettledRejectElementSharedFun",
("old_space", 0x03825): "PromiseAnyRejectElementSharedFun",
("old_space", 0x0384d): "PromiseCapabilityDefaultRejectSharedFun",
("old_space", 0x03875): "PromiseCapabilityDefaultResolveSharedFun",
("old_space", 0x0389d): "PromiseCatchFinallySharedFun",
("old_space", 0x038c5): "PromiseGetCapabilitiesExecutorSharedFun",
("old_space", 0x038ed): "PromiseThenFinallySharedFun",
("old_space", 0x03915): "PromiseThrowerFinallySharedFun",
("old_space", 0x0393d): "PromiseValueThunkFinallySharedFun",
("old_space", 0x03965): "ProxyRevokeSharedFun",
}
# Lower 32 bits of first page addresses for various heap spaces.