641 lines
25 KiB
C++
641 lines
25 KiB
C++
/*
|
|
* Copyright (C) 2016-2019 Apple Inc. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "AtomicsObject.h"
|
|
|
|
#include "FrameTracers.h"
|
|
#include "JSCInlines.h"
|
|
#include "JSTypedArrays.h"
|
|
#include "ReleaseHeapAccessScope.h"
|
|
#include "TypedArrayController.h"
|
|
|
|
namespace JSC {
|
|
|
|
STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(AtomicsObject);
|
|
|
|
#define FOR_EACH_ATOMICS_FUNC(macro) \
|
|
macro(add, Add, 3) \
|
|
macro(and, And, 3) \
|
|
macro(compareExchange, CompareExchange, 4) \
|
|
macro(exchange, Exchange, 3) \
|
|
macro(isLockFree, IsLockFree, 1) \
|
|
macro(load, Load, 2) \
|
|
macro(notify, Notify, 3) \
|
|
macro(or, Or, 3) \
|
|
macro(store, Store, 3) \
|
|
macro(sub, Sub, 3) \
|
|
macro(wait, Wait, 4) \
|
|
macro(xor, Xor, 3)
|
|
|
|
#define DECLARE_FUNC_PROTO(lowerName, upperName, count) \
|
|
static JSC_DECLARE_HOST_FUNCTION(atomicsFunc ## upperName);
|
|
FOR_EACH_ATOMICS_FUNC(DECLARE_FUNC_PROTO)
|
|
#undef DECLARE_FUNC_PROTO
|
|
|
|
const ClassInfo AtomicsObject::s_info = { "Atomics", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(AtomicsObject) };
|
|
|
|
AtomicsObject::AtomicsObject(VM& vm, Structure* structure)
|
|
: JSNonFinalObject(vm, structure)
|
|
{
|
|
}
|
|
|
|
AtomicsObject* AtomicsObject::create(VM& vm, JSGlobalObject* globalObject, Structure* structure)
|
|
{
|
|
AtomicsObject* object = new (NotNull, allocateCell<AtomicsObject>(vm.heap)) AtomicsObject(vm, structure);
|
|
object->finishCreation(vm, globalObject);
|
|
return object;
|
|
}
|
|
|
|
Structure* AtomicsObject::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
|
|
{
|
|
return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
|
|
}
|
|
|
|
void AtomicsObject::finishCreation(VM& vm, JSGlobalObject* globalObject)
|
|
{
|
|
Base::finishCreation(vm);
|
|
ASSERT(inherits(vm, info()));
|
|
|
|
#define PUT_DIRECT_NATIVE_FUNC(lowerName, upperName, count) \
|
|
putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(vm, #lowerName), count, atomicsFunc ## upperName, Atomics ## upperName ## Intrinsic, static_cast<unsigned>(PropertyAttribute::DontEnum));
|
|
FOR_EACH_ATOMICS_FUNC(PUT_DIRECT_NATIVE_FUNC)
|
|
#undef PUT_DIRECT_NATIVE_FUNC
|
|
|
|
JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
|
|
}
|
|
|
|
namespace {
|
|
|
|
template<typename Adaptor, typename Func>
|
|
EncodedJSValue atomicReadModifyWriteCase(JSGlobalObject* globalObject, VM& vm, const JSValue* args, JSArrayBufferView* typedArrayView, unsigned accessIndex, const Func& func)
|
|
{
|
|
auto scope = DECLARE_THROW_SCOPE(vm);
|
|
|
|
JSGenericTypedArrayView<Adaptor>* typedArray = jsCast<JSGenericTypedArrayView<Adaptor>*>(typedArrayView);
|
|
|
|
typename Adaptor::Type extraArgs[Func::numExtraArgs + 1]; // Add 1 to avoid 0 size array error in VS.
|
|
for (unsigned i = 0; i < Func::numExtraArgs; ++i) {
|
|
auto value = toNativeFromValue<Adaptor>(globalObject, args[2 + i]);
|
|
RETURN_IF_EXCEPTION(scope, { });
|
|
extraArgs[i] = value;
|
|
}
|
|
|
|
if (typedArray->isDetached())
|
|
return throwVMTypeError(globalObject, scope, typedArrayBufferHasBeenDetachedErrorMessage);
|
|
|
|
auto result = func(typedArray->typedVector() + accessIndex, extraArgs);
|
|
RELEASE_AND_RETURN(scope, JSValue::encode(Adaptor::toJSValue(globalObject, result)));
|
|
}
|
|
|
|
static unsigned validateAtomicAccess(JSGlobalObject* globalObject, VM& vm, JSArrayBufferView* typedArrayView, JSValue accessIndexValue)
|
|
{
|
|
auto scope = DECLARE_THROW_SCOPE(vm);
|
|
unsigned accessIndex = 0;
|
|
if (LIKELY(accessIndexValue.isUInt32()))
|
|
accessIndex = accessIndexValue.asUInt32();
|
|
else {
|
|
accessIndex = accessIndexValue.toIndex(globalObject, "accessIndex");
|
|
RETURN_IF_EXCEPTION(scope, 0);
|
|
}
|
|
|
|
ASSERT(typedArrayView->length() <= static_cast<unsigned>(INT_MAX));
|
|
if (accessIndex >= typedArrayView->length()) {
|
|
throwRangeError(globalObject, scope, "Access index out of bounds for atomic access."_s);
|
|
return 0;
|
|
}
|
|
|
|
return accessIndex;
|
|
}
|
|
|
|
enum class TypedArrayOperationMode { ReadWrite, Wait };
|
|
template<TypedArrayOperationMode mode>
|
|
inline JSArrayBufferView* validateIntegerTypedArray(JSGlobalObject* globalObject, JSValue typedArrayValue)
|
|
{
|
|
VM& vm = globalObject->vm();
|
|
auto scope = DECLARE_THROW_SCOPE(vm);
|
|
|
|
JSArrayBufferView* typedArray = validateTypedArray(globalObject, typedArrayValue);
|
|
RETURN_IF_EXCEPTION(scope, { });
|
|
|
|
if constexpr (mode == TypedArrayOperationMode::Wait) {
|
|
switch (typedArray->type()) {
|
|
case Int32ArrayType:
|
|
case BigInt64ArrayType:
|
|
break;
|
|
default:
|
|
throwTypeError(globalObject, scope, "Typed array argument must be an Int32Array or BigInt64Array."_s);
|
|
return { };
|
|
}
|
|
} else {
|
|
switch (typedArray->type()) {
|
|
case Int8ArrayType:
|
|
case Int16ArrayType:
|
|
case Int32ArrayType:
|
|
case Uint8ArrayType:
|
|
case Uint16ArrayType:
|
|
case Uint32ArrayType:
|
|
case BigInt64ArrayType:
|
|
case BigUint64ArrayType:
|
|
break;
|
|
default:
|
|
throwTypeError(globalObject, scope, "Typed array argument must be an Int8Array, Int16Array, Int32Array, Uint8Array, Uint16Array, Uint32Array, BigInt64Array, or BigUint64Array."_s);
|
|
return { };
|
|
}
|
|
}
|
|
return typedArray;
|
|
}
|
|
|
|
template<typename Func>
|
|
EncodedJSValue atomicReadModifyWrite(JSGlobalObject* globalObject, VM& vm, const JSValue* args, const Func& func)
|
|
{
|
|
auto scope = DECLARE_THROW_SCOPE(vm);
|
|
|
|
JSArrayBufferView* typedArrayView = validateIntegerTypedArray<TypedArrayOperationMode::ReadWrite>(globalObject, args[0]);
|
|
RETURN_IF_EXCEPTION(scope, { });
|
|
|
|
unsigned accessIndex = validateAtomicAccess(globalObject, vm, typedArrayView, args[1]);
|
|
RETURN_IF_EXCEPTION(scope, { });
|
|
|
|
scope.release();
|
|
switch (typedArrayView->type()) {
|
|
case Int8ArrayType:
|
|
return atomicReadModifyWriteCase<Int8Adaptor>(globalObject, vm, args, typedArrayView, accessIndex, func);
|
|
case Int16ArrayType:
|
|
return atomicReadModifyWriteCase<Int16Adaptor>(globalObject, vm, args, typedArrayView, accessIndex, func);
|
|
case Int32ArrayType:
|
|
return atomicReadModifyWriteCase<Int32Adaptor>(globalObject, vm, args, typedArrayView, accessIndex, func);
|
|
case Uint8ArrayType:
|
|
return atomicReadModifyWriteCase<Uint8Adaptor>(globalObject, vm, args, typedArrayView, accessIndex, func);
|
|
case Uint16ArrayType:
|
|
return atomicReadModifyWriteCase<Uint16Adaptor>(globalObject, vm, args, typedArrayView, accessIndex, func);
|
|
case Uint32ArrayType:
|
|
return atomicReadModifyWriteCase<Uint32Adaptor>(globalObject, vm, args, typedArrayView, accessIndex, func);
|
|
case BigInt64ArrayType:
|
|
return atomicReadModifyWriteCase<BigInt64Adaptor>(globalObject, vm, args, typedArrayView, accessIndex, func);
|
|
case BigUint64ArrayType:
|
|
return atomicReadModifyWriteCase<BigUint64Adaptor>(globalObject, vm, args, typedArrayView, accessIndex, func);
|
|
default:
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
return JSValue::encode(jsUndefined());
|
|
}
|
|
}
|
|
|
|
template<typename Func>
|
|
EncodedJSValue atomicReadModifyWrite(JSGlobalObject* globalObject, CallFrame* callFrame, const Func& func)
|
|
{
|
|
JSValue args[2 + Func::numExtraArgs];
|
|
for (unsigned i = 2 + Func::numExtraArgs; i--;)
|
|
args[i] = callFrame->argument(i);
|
|
return atomicReadModifyWrite(globalObject, globalObject->vm(), args, func);
|
|
}
|
|
|
|
struct AddFunc {
|
|
static constexpr unsigned numExtraArgs = 1;
|
|
|
|
template<typename T>
|
|
T operator()(T* ptr, const T* args) const
|
|
{
|
|
return WTF::atomicExchangeAdd(ptr, args[0]);
|
|
}
|
|
};
|
|
|
|
struct AndFunc {
|
|
static constexpr unsigned numExtraArgs = 1;
|
|
|
|
template<typename T>
|
|
T operator()(T* ptr, const T* args) const
|
|
{
|
|
return WTF::atomicExchangeAnd(ptr, args[0]);
|
|
}
|
|
};
|
|
|
|
struct CompareExchangeFunc {
|
|
static constexpr unsigned numExtraArgs = 2;
|
|
|
|
template<typename T>
|
|
T operator()(T* ptr, const T* args) const
|
|
{
|
|
T expected = args[0];
|
|
T newValue = args[1];
|
|
return WTF::atomicCompareExchangeStrong(ptr, expected, newValue);
|
|
}
|
|
};
|
|
|
|
struct ExchangeFunc {
|
|
static constexpr unsigned numExtraArgs = 1;
|
|
|
|
template<typename T>
|
|
T operator()(T* ptr, const T* args) const
|
|
{
|
|
return WTF::atomicExchange(ptr, args[0]);
|
|
}
|
|
};
|
|
|
|
struct LoadFunc {
|
|
static constexpr unsigned numExtraArgs = 0;
|
|
|
|
template<typename T>
|
|
T operator()(T* ptr, const T*) const
|
|
{
|
|
return WTF::atomicLoadFullyFenced(ptr);
|
|
}
|
|
};
|
|
|
|
struct OrFunc {
|
|
static constexpr unsigned numExtraArgs = 1;
|
|
|
|
template<typename T>
|
|
T operator()(T* ptr, const T* args) const
|
|
{
|
|
return WTF::atomicExchangeOr(ptr, args[0]);
|
|
}
|
|
};
|
|
|
|
struct SubFunc {
|
|
static constexpr unsigned numExtraArgs = 1;
|
|
|
|
template<typename T>
|
|
T operator()(T* ptr, const T* args) const
|
|
{
|
|
return WTF::atomicExchangeSub(ptr, args[0]);
|
|
}
|
|
};
|
|
|
|
struct XorFunc {
|
|
static constexpr unsigned numExtraArgs = 1;
|
|
|
|
template<typename T>
|
|
T operator()(T* ptr, const T* args) const
|
|
{
|
|
return WTF::atomicExchangeXor(ptr, args[0]);
|
|
}
|
|
};
|
|
|
|
EncodedJSValue isLockFree(JSGlobalObject* globalObject, JSValue arg)
|
|
{
|
|
VM& vm = globalObject->vm();
|
|
auto scope = DECLARE_THROW_SCOPE(vm);
|
|
|
|
int32_t size = arg.toInt32(globalObject);
|
|
RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined()));
|
|
|
|
bool result;
|
|
switch (size) {
|
|
case 1:
|
|
case 2:
|
|
case 4:
|
|
case 8:
|
|
result = true;
|
|
break;
|
|
default:
|
|
result = false;
|
|
break;
|
|
}
|
|
return JSValue::encode(jsBoolean(result));
|
|
}
|
|
|
|
template<typename Adaptor>
|
|
EncodedJSValue atomicStoreCase(JSGlobalObject* globalObject, VM& vm, JSValue operand, JSArrayBufferView* typedArrayView, unsigned accessIndex)
|
|
{
|
|
auto scope = DECLARE_THROW_SCOPE(vm);
|
|
|
|
JSGenericTypedArrayView<Adaptor>* typedArray = jsCast<JSGenericTypedArrayView<Adaptor>*>(typedArrayView);
|
|
|
|
typename Adaptor::Type extraArg;
|
|
JSValue value;
|
|
if constexpr (std::is_same_v<Adaptor, BigInt64Adaptor> || std::is_same_v<Adaptor, BigUint64Adaptor>) {
|
|
value = operand.toBigInt(globalObject);
|
|
RETURN_IF_EXCEPTION(scope, { });
|
|
extraArg = toNativeFromValue<Adaptor>(globalObject, value);
|
|
RETURN_IF_EXCEPTION(scope, { });
|
|
} else {
|
|
value = jsNumber(operand.toIntegerOrInfinity(globalObject));
|
|
RETURN_IF_EXCEPTION(scope, { });
|
|
extraArg = toNativeFromValue<Adaptor>(globalObject, value);
|
|
RETURN_IF_EXCEPTION(scope, { });
|
|
}
|
|
|
|
if (typedArray->isDetached())
|
|
return throwVMTypeError(globalObject, scope, typedArrayBufferHasBeenDetachedErrorMessage);
|
|
|
|
WTF::atomicStoreFullyFenced(typedArray->typedVector() + accessIndex, extraArg);
|
|
RELEASE_AND_RETURN(scope, JSValue::encode(value));
|
|
}
|
|
|
|
EncodedJSValue atomicStore(JSGlobalObject* globalObject, VM& vm, JSValue base, JSValue index, JSValue operand)
|
|
{
|
|
// https://tc39.es/ecma262/#sec-atomics.store
|
|
|
|
auto scope = DECLARE_THROW_SCOPE(vm);
|
|
|
|
JSArrayBufferView* typedArrayView = validateIntegerTypedArray<TypedArrayOperationMode::ReadWrite>(globalObject, base);
|
|
RETURN_IF_EXCEPTION(scope, { });
|
|
|
|
unsigned accessIndex = validateAtomicAccess(globalObject, vm, typedArrayView, index);
|
|
RETURN_IF_EXCEPTION(scope, { });
|
|
|
|
scope.release();
|
|
switch (typedArrayView->type()) {
|
|
case Int8ArrayType:
|
|
return atomicStoreCase<Int8Adaptor>(globalObject, vm, operand, typedArrayView, accessIndex);
|
|
case Int16ArrayType:
|
|
return atomicStoreCase<Int16Adaptor>(globalObject, vm, operand, typedArrayView, accessIndex);
|
|
case Int32ArrayType:
|
|
return atomicStoreCase<Int32Adaptor>(globalObject, vm, operand, typedArrayView, accessIndex);
|
|
case Uint8ArrayType:
|
|
return atomicStoreCase<Uint8Adaptor>(globalObject, vm, operand, typedArrayView, accessIndex);
|
|
case Uint16ArrayType:
|
|
return atomicStoreCase<Uint16Adaptor>(globalObject, vm, operand, typedArrayView, accessIndex);
|
|
case Uint32ArrayType:
|
|
return atomicStoreCase<Uint32Adaptor>(globalObject, vm, operand, typedArrayView, accessIndex);
|
|
case BigInt64ArrayType:
|
|
return atomicStoreCase<BigInt64Adaptor>(globalObject, vm, operand, typedArrayView, accessIndex);
|
|
case BigUint64ArrayType:
|
|
return atomicStoreCase<BigUint64Adaptor>(globalObject, vm, operand, typedArrayView, accessIndex);
|
|
default:
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
return { };
|
|
}
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
JSC_DEFINE_HOST_FUNCTION(atomicsFuncAdd, (JSGlobalObject* globalObject, CallFrame* callFrame))
|
|
{
|
|
return atomicReadModifyWrite(globalObject, callFrame, AddFunc());
|
|
}
|
|
|
|
JSC_DEFINE_HOST_FUNCTION(atomicsFuncAnd, (JSGlobalObject* globalObject, CallFrame* callFrame))
|
|
{
|
|
return atomicReadModifyWrite(globalObject, callFrame, AndFunc());
|
|
}
|
|
|
|
JSC_DEFINE_HOST_FUNCTION(atomicsFuncCompareExchange, (JSGlobalObject* globalObject, CallFrame* callFrame))
|
|
{
|
|
return atomicReadModifyWrite(globalObject, callFrame, CompareExchangeFunc());
|
|
}
|
|
|
|
JSC_DEFINE_HOST_FUNCTION(atomicsFuncExchange, (JSGlobalObject* globalObject, CallFrame* callFrame))
|
|
{
|
|
return atomicReadModifyWrite(globalObject, callFrame, ExchangeFunc());
|
|
}
|
|
|
|
JSC_DEFINE_HOST_FUNCTION(atomicsFuncIsLockFree, (JSGlobalObject* globalObject, CallFrame* callFrame))
|
|
{
|
|
return isLockFree(globalObject, callFrame->argument(0));
|
|
}
|
|
|
|
JSC_DEFINE_HOST_FUNCTION(atomicsFuncLoad, (JSGlobalObject* globalObject, CallFrame* callFrame))
|
|
{
|
|
return atomicReadModifyWrite(globalObject, callFrame, LoadFunc());
|
|
}
|
|
|
|
JSC_DEFINE_HOST_FUNCTION(atomicsFuncOr, (JSGlobalObject* globalObject, CallFrame* callFrame))
|
|
{
|
|
return atomicReadModifyWrite(globalObject, callFrame, OrFunc());
|
|
}
|
|
|
|
JSC_DEFINE_HOST_FUNCTION(atomicsFuncStore, (JSGlobalObject* globalObject, CallFrame* callFrame))
|
|
{
|
|
return atomicStore(globalObject, globalObject->vm(), callFrame->argument(0), callFrame->argument(1), callFrame->argument(2));
|
|
}
|
|
|
|
JSC_DEFINE_HOST_FUNCTION(atomicsFuncSub, (JSGlobalObject* globalObject, CallFrame* callFrame))
|
|
{
|
|
return atomicReadModifyWrite(globalObject, callFrame, SubFunc());
|
|
}
|
|
|
|
template<typename ValueType, typename JSArrayType>
|
|
JSValue atomicsWaitImpl(JSGlobalObject* globalObject, JSArrayType* typedArray, unsigned accessIndex, ValueType expectedValue, JSValue timeoutValue)
|
|
{
|
|
VM& vm = globalObject->vm();
|
|
auto scope = DECLARE_THROW_SCOPE(vm);
|
|
|
|
ValueType* ptr = typedArray->typedVector() + accessIndex;
|
|
|
|
double timeoutInMilliseconds = timeoutValue.toNumber(globalObject);
|
|
RETURN_IF_EXCEPTION(scope, { });
|
|
Seconds timeout = Seconds::infinity();
|
|
if (!std::isnan(timeoutInMilliseconds))
|
|
timeout = std::max(Seconds::fromMilliseconds(timeoutInMilliseconds), 0_s);
|
|
|
|
if (!vm.m_typedArrayController->isAtomicsWaitAllowedOnCurrentThread()) {
|
|
throwTypeError(globalObject, scope, "Atomics.wait cannot be called from the current thread."_s);
|
|
return { };
|
|
}
|
|
|
|
bool didPassValidation = false;
|
|
ParkingLot::ParkResult result;
|
|
{
|
|
ReleaseHeapAccessScope releaseHeapAccessScope(vm.heap);
|
|
result = ParkingLot::parkConditionally(
|
|
ptr,
|
|
[&] () -> bool {
|
|
didPassValidation = WTF::atomicLoad(ptr) == expectedValue;
|
|
return didPassValidation;
|
|
},
|
|
[] () { },
|
|
MonotonicTime::now() + timeout);
|
|
}
|
|
if (!didPassValidation)
|
|
return vm.smallStrings.notEqualString();
|
|
if (!result.wasUnparked)
|
|
return vm.smallStrings.timedOutString();
|
|
return vm.smallStrings.okString();
|
|
}
|
|
|
|
JSC_DEFINE_HOST_FUNCTION(atomicsFuncWait, (JSGlobalObject* globalObject, CallFrame* callFrame))
|
|
{
|
|
VM& vm = globalObject->vm();
|
|
auto scope = DECLARE_THROW_SCOPE(vm);
|
|
|
|
auto* typedArrayView = validateIntegerTypedArray<TypedArrayOperationMode::Wait>(globalObject, callFrame->argument(0));
|
|
RETURN_IF_EXCEPTION(scope, { });
|
|
|
|
if (!typedArrayView->isShared())
|
|
return throwVMTypeError(globalObject, scope, "Typed array for wait/notify must wrap a SharedArrayBuffer."_s);
|
|
|
|
unsigned accessIndex = validateAtomicAccess(globalObject, vm, typedArrayView, callFrame->argument(1));
|
|
RETURN_IF_EXCEPTION(scope, { });
|
|
|
|
switch (typedArrayView->type()) {
|
|
case Int32ArrayType: {
|
|
int32_t expectedValue = callFrame->argument(2).toInt32(globalObject);
|
|
RETURN_IF_EXCEPTION(scope, { });
|
|
RELEASE_AND_RETURN(scope, JSValue::encode(atomicsWaitImpl<int32_t>(globalObject, jsCast<JSInt32Array*>(typedArrayView), accessIndex, expectedValue, callFrame->argument(3))));
|
|
}
|
|
case BigInt64ArrayType: {
|
|
int64_t expectedValue = callFrame->argument(2).toBigInt64(globalObject);
|
|
RETURN_IF_EXCEPTION(scope, { });
|
|
RELEASE_AND_RETURN(scope, JSValue::encode(atomicsWaitImpl<int64_t>(globalObject, jsCast<JSBigInt64Array*>(typedArrayView), accessIndex, expectedValue, callFrame->argument(3))));
|
|
}
|
|
default:
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
break;
|
|
}
|
|
return { };
|
|
}
|
|
|
|
JSC_DEFINE_HOST_FUNCTION(atomicsFuncNotify, (JSGlobalObject* globalObject, CallFrame* callFrame))
|
|
{
|
|
VM& vm = globalObject->vm();
|
|
auto scope = DECLARE_THROW_SCOPE(vm);
|
|
|
|
auto* typedArrayView = validateIntegerTypedArray<TypedArrayOperationMode::Wait>(globalObject, callFrame->argument(0));
|
|
RETURN_IF_EXCEPTION(scope, { });
|
|
|
|
unsigned accessIndex = validateAtomicAccess(globalObject, vm, typedArrayView, callFrame->argument(1));
|
|
RETURN_IF_EXCEPTION(scope, { });
|
|
|
|
JSValue countValue = callFrame->argument(2);
|
|
unsigned count = UINT_MAX;
|
|
if (!countValue.isUndefined()) {
|
|
double countInt = countValue.toIntegerOrInfinity(globalObject);
|
|
RETURN_IF_EXCEPTION(scope, { });
|
|
double countDouble = std::max(0.0, countInt);
|
|
if (countDouble < UINT_MAX)
|
|
count = static_cast<unsigned>(countDouble);
|
|
}
|
|
|
|
if (!typedArrayView->isShared())
|
|
return JSValue::encode(jsNumber(0));
|
|
|
|
switch (typedArrayView->type()) {
|
|
case Int32ArrayType: {
|
|
int32_t* ptr = jsCast<JSInt32Array*>(typedArrayView)->typedVector() + accessIndex;
|
|
return JSValue::encode(jsNumber(ParkingLot::unparkCount(ptr, count)));
|
|
}
|
|
case BigInt64ArrayType: {
|
|
int64_t* ptr = jsCast<JSBigInt64Array*>(typedArrayView)->typedVector() + accessIndex;
|
|
return JSValue::encode(jsNumber(ParkingLot::unparkCount(ptr, count)));
|
|
}
|
|
default:
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
break;
|
|
}
|
|
return { };
|
|
}
|
|
|
|
JSC_DEFINE_HOST_FUNCTION(atomicsFuncXor, (JSGlobalObject* globalObject, CallFrame* callFrame))
|
|
{
|
|
return atomicReadModifyWrite(globalObject, callFrame, XorFunc());
|
|
}
|
|
|
|
IGNORE_WARNINGS_BEGIN("frame-address")
|
|
|
|
JSC_DEFINE_JIT_OPERATION(operationAtomicsAdd, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue base, EncodedJSValue index, EncodedJSValue operand))
|
|
{
|
|
VM& vm = globalObject->vm();
|
|
CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
|
|
JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
|
|
JSValue args[] = {JSValue::decode(base), JSValue::decode(index), JSValue::decode(operand)};
|
|
return atomicReadModifyWrite(globalObject, vm, args, AddFunc());
|
|
}
|
|
|
|
JSC_DEFINE_JIT_OPERATION(operationAtomicsAnd, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue base, EncodedJSValue index, EncodedJSValue operand))
|
|
{
|
|
VM& vm = globalObject->vm();
|
|
CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
|
|
JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
|
|
JSValue args[] = {JSValue::decode(base), JSValue::decode(index), JSValue::decode(operand)};
|
|
return atomicReadModifyWrite(globalObject, vm, args, AndFunc());
|
|
}
|
|
|
|
JSC_DEFINE_JIT_OPERATION(operationAtomicsCompareExchange, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue base, EncodedJSValue index, EncodedJSValue expected, EncodedJSValue newValue))
|
|
{
|
|
VM& vm = globalObject->vm();
|
|
CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
|
|
JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
|
|
JSValue args[] = {JSValue::decode(base), JSValue::decode(index), JSValue::decode(expected), JSValue::decode(newValue)};
|
|
return atomicReadModifyWrite(globalObject, vm, args, CompareExchangeFunc());
|
|
}
|
|
|
|
JSC_DEFINE_JIT_OPERATION(operationAtomicsExchange, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue base, EncodedJSValue index, EncodedJSValue operand))
|
|
{
|
|
VM& vm = globalObject->vm();
|
|
CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
|
|
JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
|
|
JSValue args[] = {JSValue::decode(base), JSValue::decode(index), JSValue::decode(operand)};
|
|
return atomicReadModifyWrite(globalObject, vm, args, ExchangeFunc());
|
|
}
|
|
|
|
JSC_DEFINE_JIT_OPERATION(operationAtomicsIsLockFree, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue size))
|
|
{
|
|
VM& vm = globalObject->vm();
|
|
CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
|
|
JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
|
|
return isLockFree(globalObject, JSValue::decode(size));
|
|
}
|
|
|
|
JSC_DEFINE_JIT_OPERATION(operationAtomicsLoad, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue base, EncodedJSValue index))
|
|
{
|
|
VM& vm = globalObject->vm();
|
|
CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
|
|
JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
|
|
JSValue args[] = {JSValue::decode(base), JSValue::decode(index)};
|
|
return atomicReadModifyWrite(globalObject, vm, args, LoadFunc());
|
|
}
|
|
|
|
JSC_DEFINE_JIT_OPERATION(operationAtomicsOr, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue base, EncodedJSValue index, EncodedJSValue operand))
|
|
{
|
|
VM& vm = globalObject->vm();
|
|
CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
|
|
JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
|
|
JSValue args[] = {JSValue::decode(base), JSValue::decode(index), JSValue::decode(operand)};
|
|
return atomicReadModifyWrite(globalObject, vm, args, OrFunc());
|
|
}
|
|
|
|
JSC_DEFINE_JIT_OPERATION(operationAtomicsStore, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue base, EncodedJSValue index, EncodedJSValue operand))
|
|
{
|
|
VM& vm = globalObject->vm();
|
|
CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
|
|
JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
|
|
return atomicStore(globalObject, vm, JSValue::decode(base), JSValue::decode(index), JSValue::decode(operand));
|
|
}
|
|
|
|
JSC_DEFINE_JIT_OPERATION(operationAtomicsSub, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue base, EncodedJSValue index, EncodedJSValue operand))
|
|
{
|
|
VM& vm = globalObject->vm();
|
|
CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
|
|
JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
|
|
JSValue args[] = {JSValue::decode(base), JSValue::decode(index), JSValue::decode(operand)};
|
|
return atomicReadModifyWrite(globalObject, vm, args, SubFunc());
|
|
}
|
|
|
|
JSC_DEFINE_JIT_OPERATION(operationAtomicsXor, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue base, EncodedJSValue index, EncodedJSValue operand))
|
|
{
|
|
VM& vm = globalObject->vm();
|
|
CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
|
|
JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
|
|
JSValue args[] = {JSValue::decode(base), JSValue::decode(index), JSValue::decode(operand)};
|
|
return atomicReadModifyWrite(globalObject, vm, args, XorFunc());
|
|
}
|
|
|
|
IGNORE_WARNINGS_END
|
|
|
|
} // namespace JSC
|
|
|