| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #include "RuntimeLibraryPch.h"
- #define ATOMICS_FUNCTION_ENTRY_CHECKS(length, methodName) \
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); \
- ARGUMENTS(args, callInfo); \
- ScriptContext* scriptContext = function->GetScriptContext(); \
- Assert(!(callInfo.Flags & CallFlags_New)); \
- if (args.Info.Count <= length) \
- { \
- JavascriptError::ThrowRangeError(scriptContext, JSERR_WinRTFunction_TooFewArguments, _u(methodName)); \
- } \
- namespace Js
- {
- Var AtomicsObject::ValidateSharedIntegerTypedArray(Var typedArray, ScriptContext *scriptContext, bool onlyInt32)
- {
- if (!VarIs<TypedArrayBase>(typedArray))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedTypedArrayObject);
- }
- TypeId typeId = JavascriptOperators::GetTypeId(typedArray);
- if (onlyInt32)
- {
- if (typeId != TypeIds_Int32Array)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_InvalidOperationOnTypedArray);
- }
- }
- else
- {
- if (!(typeId == TypeIds_Int8Array || typeId == TypeIds_Uint8Array || typeId == TypeIds_Int16Array ||
- typeId == TypeIds_Uint16Array || typeId == TypeIds_Int32Array || typeId == TypeIds_Uint32Array))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_InvalidOperationOnTypedArray);
- }
- }
- TypedArrayBase *typedArrayBase = UnsafeVarTo<TypedArrayBase>(typedArray);
- ArrayBufferBase* arrayBuffer = typedArrayBase->GetArrayBuffer();
- if (arrayBuffer == nullptr || !VarIsCorrectType(arrayBuffer) || !arrayBuffer->IsSharedArrayBuffer())
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedSharedArrayBufferObject);
- }
- return arrayBuffer;
- }
- uint32 AtomicsObject::ValidateAtomicAccess(Var typedArray, Var requestIndex, ScriptContext *scriptContext)
- {
- Assert(VarIs<TypedArrayBase>(typedArray));
- int32 accessIndex = -1;
- if (TaggedInt::Is(requestIndex))
- {
- accessIndex = TaggedInt::ToInt32(requestIndex);
- }
- else if(Js::JavascriptOperators::IsUndefined(requestIndex))
- {
- accessIndex = 0;
- }
- else
- {
- accessIndex = JavascriptConversion::ToInt32_Full(requestIndex, scriptContext);
- double dblValue = JavascriptConversion::ToInteger(requestIndex, scriptContext);
- if (dblValue != accessIndex)
- {
- JavascriptError::ThrowRangeError(scriptContext, JSERR_InvalidTypedArrayIndex);
- }
- }
- if (accessIndex < 0 || accessIndex >= (int32)VarTo<TypedArrayBase>(typedArray)->GetLength())
- {
- JavascriptError::ThrowRangeError(scriptContext, JSERR_InvalidTypedArrayIndex);
- }
- return (uint32)accessIndex;
- }
- TypedArrayBase * AtomicsObject::ValidateAndGetTypedArray(Var typedArray, Var index, __out uint32 *accessIndex, ScriptContext *scriptContext, bool onlyInt32)
- {
- ValidateSharedIntegerTypedArray(typedArray, scriptContext, onlyInt32);
- uint32 i = ValidateAtomicAccess(typedArray, index, scriptContext);
- if (accessIndex != nullptr)
- {
- *accessIndex = i;
- }
- return VarTo<TypedArrayBase>(typedArray);
- }
- Var AtomicsObject::EntryAdd(RecyclableObject* function, CallInfo callInfo, ...)
- {
- ATOMICS_FUNCTION_ENTRY_CHECKS(3, "Atomics.add");
- uint32 accessIndex = 0;
- TypedArrayBase *typedArrayBase = ValidateAndGetTypedArray(args[1], args[2], &accessIndex, scriptContext);
- return typedArrayBase->TypedAdd(accessIndex, args[3]);
- }
- Var AtomicsObject::EntryAnd(RecyclableObject* function, CallInfo callInfo, ...)
- {
- ATOMICS_FUNCTION_ENTRY_CHECKS(3, "Atomics.and");
- uint32 accessIndex = 0;
- TypedArrayBase *typedArrayBase = ValidateAndGetTypedArray(args[1], args[2], &accessIndex, scriptContext);
- return typedArrayBase->TypedAnd(accessIndex, args[3]);
- }
- Var AtomicsObject::EntryCompareExchange(RecyclableObject* function, CallInfo callInfo, ...)
- {
- ATOMICS_FUNCTION_ENTRY_CHECKS(4, "Atomics.compareExchange");
- uint32 accessIndex = 0;
- TypedArrayBase *typedArrayBase = ValidateAndGetTypedArray(args[1], args[2], &accessIndex, scriptContext);
- return typedArrayBase->TypedCompareExchange(accessIndex, args[3], args[4]);
- }
- Var AtomicsObject::EntryExchange(RecyclableObject* function, CallInfo callInfo, ...)
- {
- ATOMICS_FUNCTION_ENTRY_CHECKS(3, "Atomics.exchange");
- uint32 accessIndex = 0;
- TypedArrayBase *typedArrayBase = ValidateAndGetTypedArray(args[1], args[2], &accessIndex, scriptContext);
- return typedArrayBase->TypedExchange(accessIndex, args[3]);
- }
- Var AtomicsObject::EntryIsLockFree(RecyclableObject* function, CallInfo callInfo, ...)
- {
- ATOMICS_FUNCTION_ENTRY_CHECKS(1, "Atomics.isLockFree");
- uint32 size = JavascriptConversion::ToUInt32(args[1], scriptContext);
- // TODO : Currently for the size 1, 2 and 4 we are treating them lock free, we will take a look at this later.
- return (size == 1 || size == 2 || size == 4) ? scriptContext->GetLibrary()->GetTrue() : scriptContext->GetLibrary()->GetFalse();
- }
- Var AtomicsObject::EntryLoad(RecyclableObject* function, CallInfo callInfo, ...)
- {
- ATOMICS_FUNCTION_ENTRY_CHECKS(2, "Atomics.load");
- uint32 accessIndex = 0;
- TypedArrayBase *typedArrayBase = ValidateAndGetTypedArray(args[1], args[2], &accessIndex, scriptContext);
- return typedArrayBase->TypedLoad(accessIndex);
- }
- Var AtomicsObject::EntryOr(RecyclableObject* function, CallInfo callInfo, ...)
- {
- ATOMICS_FUNCTION_ENTRY_CHECKS(3, "Atomics.or");
- uint32 accessIndex = 0;
- TypedArrayBase *typedArrayBase = ValidateAndGetTypedArray(args[1], args[2], &accessIndex, scriptContext);
- return typedArrayBase->TypedOr(accessIndex, args[3]);
- }
- Var AtomicsObject::EntryStore(RecyclableObject* function, CallInfo callInfo, ...)
- {
- ATOMICS_FUNCTION_ENTRY_CHECKS(3, "Atomics.store");
- uint32 accessIndex = 0;
- TypedArrayBase *typedArrayBase = ValidateAndGetTypedArray(args[1], args[2], &accessIndex, scriptContext);
- return typedArrayBase->TypedStore(accessIndex, args[3]);
- }
- Var AtomicsObject::EntrySub(RecyclableObject* function, CallInfo callInfo, ...)
- {
- ATOMICS_FUNCTION_ENTRY_CHECKS(3, "Atomics.sub");
- uint32 accessIndex = 0;
- TypedArrayBase *typedArrayBase = ValidateAndGetTypedArray(args[1], args[2], &accessIndex, scriptContext);
- return typedArrayBase->TypedSub(accessIndex, args[3]);
- }
- Var AtomicsObject::EntryWait(RecyclableObject* function, CallInfo callInfo, ...)
- {
- ATOMICS_FUNCTION_ENTRY_CHECKS(3, "Atomics.wait");
- uint32 accessIndex = 0;
- TypedArrayBase *typedArrayBase = ValidateAndGetTypedArray(args[1], args[2], &accessIndex, scriptContext, true /*onlyInt32*/);
- int32 value = JavascriptConversion::ToInt32(args[3], scriptContext);
- uint32 timeout = INFINITE;
- if (args.Info.Count > 4 && !JavascriptOperators::IsUndefinedObject(args[4]))
- {
- double t =JavascriptConversion::ToNumber(args[4], scriptContext);
- if (!(NumberUtilities::IsNan(t) || JavascriptNumber::IsPosInf(t)))
- {
- int32 t1 = JavascriptConversion::ToInt32(t);
- timeout = (uint32)max(0, t1);
- }
- }
- if (!AgentOfBuffer::AgentCanSuspend(scriptContext))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_CannotSuspendBuffer);
- }
- Assert(typedArrayBase->GetBytesPerElement() == 4);
- uint32 bufferIndex = (accessIndex * 4) + typedArrayBase->GetByteOffset();
- Assert(bufferIndex < typedArrayBase->GetArrayBuffer()->GetByteLength());
- SharedArrayBuffer *sharedArrayBuffer = typedArrayBase->GetArrayBuffer()->GetAsSharedArrayBuffer();
- WaiterList *waiterList = sharedArrayBuffer->GetWaiterList(bufferIndex);
- bool awoken = false;
- {
- AutoCriticalSection autoCS(waiterList->GetCriticalSectionForAccess());
- int32 w = JavascriptConversion::ToInt32(typedArrayBase->DirectGetItem(accessIndex), scriptContext);
- if (value != w)
- {
- return scriptContext->GetLibrary()->CreateStringFromCppLiteral(_u("not-equal"));
- }
- DWORD_PTR agent = (DWORD_PTR)scriptContext;
- Assert(sharedArrayBuffer->GetSharedContents()->IsValidAgent(agent));
- #pragma prefast(suppress:__WARNING_CALLER_FAILING_TO_HOLD, "This is a prefast false-positive caused by it being unable to identify that the critical section used here is the same as the one held by the AutoCriticalSection")
- awoken = waiterList->AddAndSuspendWaiter(agent, timeout);
- if (!awoken)
- {
- waiterList->RemoveWaiter(agent);
- }
- }
- return awoken ? scriptContext->GetLibrary()->CreateStringFromCppLiteral(_u("ok"))
- : scriptContext->GetLibrary()->CreateStringFromCppLiteral(_u("timed-out"));
- }
- Var AtomicsObject::EntryNotify(RecyclableObject* function, CallInfo callInfo, ...)
- {
- ATOMICS_FUNCTION_ENTRY_CHECKS(2, "Atomics.notify");
- uint32 accessIndex = 0;
- TypedArrayBase *typedArrayBase = ValidateAndGetTypedArray(args[1], args[2], &accessIndex, scriptContext, true /*onlyInt32*/);
- int32 count = INT_MAX;
- if (args.Info.Count > 3 && !JavascriptOperators::IsUndefinedObject(args[3]))
- {
- double d = JavascriptConversion::ToInteger(args[3], scriptContext);
- if (!(NumberUtilities::IsNan(d) || JavascriptNumber::IsPosInf(d)))
- {
- int32 c = JavascriptConversion::ToInt32(d);
- count = max(0, c);
- }
- }
- Assert(typedArrayBase->GetBytesPerElement() == 4);
- uint32 bufferIndex = (accessIndex * 4) + typedArrayBase->GetByteOffset();
- Assert(bufferIndex < typedArrayBase->GetArrayBuffer()->GetByteLength());
- SharedArrayBuffer *sharedArrayBuffer = typedArrayBase->GetArrayBuffer()->GetAsSharedArrayBuffer();
- WaiterList *waiterList = sharedArrayBuffer->GetWaiterList(bufferIndex);
- uint32 removed = 0;
- {
- AutoCriticalSection autoCS(waiterList->GetCriticalSectionForAccess());
- removed = waiterList->RemoveAndWakeWaiters(count);
- }
- return JavascriptNumber::ToVar(removed, scriptContext);
- }
- Var AtomicsObject::EntryXor(RecyclableObject* function, CallInfo callInfo, ...)
- {
- ATOMICS_FUNCTION_ENTRY_CHECKS(3, "Atomics.xor");
- uint32 accessIndex = 0;
- TypedArrayBase *typedArrayBase = ValidateAndGetTypedArray(args[1], args[2], &accessIndex, scriptContext);
- return typedArrayBase->TypedXor(accessIndex, args[3]);
- }
- }
|