/* * Copyright (c) 2022-2024 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ecmascript/builtins/builtins_atomics.h" #include "ecmascript/base/atomic_helper.h" #include "ecmascript/base/typed_array_helper-inl.h" #include "libpandabase/utils/time.h" #include "ecmascript/checkpoint/thread_state_transition.h" namespace panda::ecmascript::builtins { using NumberHelper = base::NumberHelper; using AtomicHelper = base::AtomicHelper; using BytesSize = base::BytesSize; using BuiltinsArrayBuffer = builtins::BuiltinsArrayBuffer; WaiterList *g_waitLists = Singleton::GetInstance(); Mutex *g_mutex = Singleton::GetInstance(); // 25.4.2 Atomics.add ( typedArray, index, value ) JSTaggedValue BuiltinsAtomics::Sub(EcmaRuntimeCallInfo *argv) { ASSERT(argv); JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, Atomics, Sub); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle typedArray = GetCallArg(argv, 0); JSHandle index = GetCallArg(argv, 1); return AtomicReadModifyWrite(thread, typedArray, index, argv, AtomicHelper::SubFun()); } JSTaggedValue BuiltinsAtomics::Add(EcmaRuntimeCallInfo *argv) { ASSERT(argv); JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, Atomics, Add); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle typedArray = GetCallArg(argv, 0); JSHandle index = GetCallArg(argv, 1); return AtomicReadModifyWrite(thread, typedArray, index, argv, AtomicHelper::AddFun()); } JSTaggedValue BuiltinsAtomics::And(EcmaRuntimeCallInfo *argv) { ASSERT(argv); JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, Atomics, And); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle typedArray = GetCallArg(argv, 0); JSHandle index = GetCallArg(argv, 1); return AtomicReadModifyWrite(thread, typedArray, index, argv, AtomicHelper::AndFun()); } JSTaggedValue BuiltinsAtomics::Or(EcmaRuntimeCallInfo *argv) { ASSERT(argv); JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, Atomics, Or); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle typedArray = GetCallArg(argv, 0); JSHandle index = GetCallArg(argv, 1); return AtomicReadModifyWrite(thread, typedArray, index, argv, AtomicHelper::OrFun()); } JSTaggedValue BuiltinsAtomics::Xor(EcmaRuntimeCallInfo *argv) { ASSERT(argv); JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, Atomics, Xor); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle typedArray = GetCallArg(argv, 0); JSHandle index = GetCallArg(argv, 1); return AtomicReadModifyWrite(thread, typedArray, index, argv, AtomicHelper::XorFun()); } JSTaggedValue BuiltinsAtomics::CompareExchange(EcmaRuntimeCallInfo *argv) { ASSERT(argv); JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, Atomics, CompareExchange); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle typedArray = GetCallArg(argv, 0); JSHandle index = GetCallArg(argv, 1); return AtomicReadModifyWrite(thread, typedArray, index, argv, AtomicHelper::CompareExchangeFun()); } JSTaggedValue BuiltinsAtomics::Exchange(EcmaRuntimeCallInfo *argv) { ASSERT(argv); JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, Atomics, Exchange); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle typedArray = GetCallArg(argv, 0); JSHandle index = GetCallArg(argv, 1); return AtomicReadModifyWrite(thread, typedArray, index, argv, AtomicHelper::ExchangeFun()); } JSTaggedValue BuiltinsAtomics::Store(EcmaRuntimeCallInfo *argv) { ASSERT(argv); JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, Atomics, Store); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle typedArray = GetCallArg(argv, 0); JSHandle index = GetCallArg(argv, 1); JSHandle value = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); return AtomicHelper::AtomicStore(thread, typedArray, index, value); } JSTaggedValue BuiltinsAtomics::Load(EcmaRuntimeCallInfo *argv) { ASSERT(argv); JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, Atomics, Load); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle typedArray = GetCallArg(argv, 0); JSHandle index = GetCallArg(argv, 1); return AtomicHelper::AtomicLoad(thread, typedArray, index); } JSTaggedValue BuiltinsAtomics::IsLockFree(EcmaRuntimeCallInfo *argv) { ASSERT(argv); JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, Atomics, IsLockFree); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle sizeTag = GetCallArg(argv, 0); BytesSize size = BytesSize(JSTaggedValue::ToInt32(thread, sizeTag)); bool result = false; switch (size) { case BytesSize::ONEBYTES: case BytesSize::TWOBYTES: case BytesSize::FOURBYTES: case BytesSize::EIGHTBYTES: result = true; break; default: result = false; break; } return JSTaggedValue(result); } // 25.4.11 Atomics.wait ( typedArray, index, value, timeout ) JSTaggedValue BuiltinsAtomics::Wait(EcmaRuntimeCallInfo *argv) { ASSERT(argv); JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, Atomics, Wait); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle array = GetCallArg(argv, 0); JSHandle index = GetCallArg(argv, 1); JSHandle value = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); JSHandle timeout = GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH); // 1. Let buffer be ? ValidateIntegerTypedArray(typedArray, true). JSHandle arrayBuffer(thread, AtomicHelper::ValidateIntegerTypedArray(thread, array, true)); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 2. If IsSharedArrayBuffer(buffer) is false, throw a TypeError exception. if (!arrayBuffer->IsSharedArrayBuffer()) { THROW_TYPE_ERROR_AND_RETURN(thread, "buffer is not sharedArrayBuffer.", JSTaggedValue::Exception()); } // 3. Let indexedPosition be ? ValidateAtomicAccess(typedArray, index). uint32_t indexedPosition = AtomicHelper::ValidateAtomicAccess(thread, array, index); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 4. If typedArray.[[TypedArrayName]] is "BigInt64Array", let v be ? ToBigInt64(value). // 5. Otherwise, let v be ? ToInt32(value). int64_t v = 0; if (array->IsJSBigInt64Array()) { if (value->IsBoolean()) { value = JSHandle(thread, JSTaggedValue::ToBigInt64(thread, value)); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); } v = JSHandle::Cast(value)->ToInt64(); } else { v = static_cast(JSTaggedValue::ToInt32(thread, value)); } RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 6. Let q be ? ToNumber(timeout). // 7. If q is NaN or +∞𝔽, let t be +∞; else if q is -∞𝔽, let t be 0; else let t be max(ℝ(q), 0). double t = 0; if (timeout->IsUndefined()) { t = base::POSITIVE_INFINITY; } else { JSTaggedNumber q = JSTaggedValue::ToNumber(thread, timeout); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); t = q.GetNumber(); if (NumberHelper::IsNaN(q) || (!NumberHelper::IsFinite(q) && t > 0)) { t = base::POSITIVE_INFINITY; } else if (t < 0) { t = 0; } } // 8. Let B be AgentCanSuspend(). // 9. If B is false, throw a TypeError exception. if (!thread->GetEcmaVM()->GetAgentCanSuspend()) { THROW_TYPE_ERROR_AND_RETURN(thread, "vm does not allow wait to block.", JSTaggedValue::Exception()); } WaitResult res = WaitResult::OK; if (array->IsJSBigInt64Array()) { // AtomicHelper::Wait(thread, arrayBuffer, indexedPosition, v, t); res = DoWait(thread, arrayBuffer, indexedPosition, v, t); } else { // AtomicHelper::Wait(thread, arrayBuffer, indexedPosition, static_cast(v), t); res = DoWait(thread, arrayBuffer, indexedPosition, static_cast(v), t); } const GlobalEnvConstants *globalConst = thread->GlobalConstants(); if (res == WaitResult::OK) { return globalConst->GetOkString(); } else if (res == WaitResult::NOT_EQ) { return globalConst->GetNotEqualString(); } return globalConst->GetTimeoutString(); } // 25.4.12 Atomics.notify ( typedArray, index, count ) JSTaggedValue BuiltinsAtomics::Notify(EcmaRuntimeCallInfo *argv) { ASSERT(argv); JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, Atomics, Notify); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle array = GetCallArg(argv, 0); JSHandle index = GetCallArg(argv, 1); JSHandle count = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); // 1. Let buffer be ? ValidateIntegerTypedArray(typedArray, true). JSHandle arrayBuffer(thread, AtomicHelper::ValidateIntegerTypedArray(thread, array, true)); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 2. Let indexedPosition be ? ValidateAtomicAccess(typedArray, index). uint32_t indexedPosition = AtomicHelper::ValidateAtomicAccess(thread, array, index); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 3. If count is undefined, let c be +∞. // 4. Else, // a. Let intCount be ? ToIntegerOrInfinity(count). // b. Let c be max(intCount, 0). double c = 0; if (count->IsUndefined()) { c = base::POSITIVE_INFINITY; } else { JSTaggedNumber countTemp = JSTaggedValue::ToNumber(thread, count); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); c = base::NumberHelper::TruncateDouble(countTemp.GetNumber()); c = c < 0 ? 0 : c; } // 6. If IsSharedArrayBuffer(buffer) is false, return +0𝔽. if (!arrayBuffer->IsSharedArrayBuffer()) { return JSTaggedValue(0); } return JSTaggedValue(Signal(thread, arrayBuffer, indexedPosition, c)); } template JSTaggedValue BuiltinsAtomics::AtomicReadModifyWrite(JSThread *thread, const JSHandle &typedArray, JSHandle &index, EcmaRuntimeCallInfo *argv, const callbackfun &op) { BUILTINS_API_TRACE(thread, Atomics, AtomicReadModifyWrite); if (!typedArray->IsTypedArray()) { THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); } // 1. Let buffer be ? ValidateIntegerTypedArray(typedArray). JSTaggedValue bufferValue = base::AtomicHelper::ValidateIntegerTypedArray(thread, typedArray); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle buffer(thread, bufferValue); // 2. Let indexedPosition be ? ValidateAtomicAccess(typedArray, index). uint32_t indexedPosition = base::AtomicHelper::ValidateAtomicAccess(thread, typedArray, index); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 3. Let arrayTypeName be typedArray.[[TypedArrayName]]. JSHandle arrayTypeName(thread, JSTypedArray::Cast(typedArray->GetTaggedObject())->GetTypedArrayName(thread)); BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle::Cast(typedArray)); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 7. NOTE: The above check is not redundant with the check in ValidateIntegerTypedArray because the call to // ToBigInt or ToIntegerOrInfinity on the preceding lines can have arbitrary side effects, which could cause the // buffer to become detached. // 8. Let elementType be the Element Type value in Table 60 for arrayTypeName. DataViewType elementType = JSTypedArray::GetTypeFromName(thread, arrayTypeName); // 9. Return GetModifySetValueInBuffer(buffer, indexedPosition, elementType, v, op). return AtomicReadModifyWriteCase(thread, buffer.GetTaggedValue(), elementType, indexedPosition, argv, op); } template JSTaggedValue BuiltinsAtomics::AtomicReadModifyWriteCase(JSThread *thread, JSTaggedValue arrBuf, DataViewType type, uint32_t indexedPosition, EcmaRuntimeCallInfo *argv, const callbackfun &op) { BUILTINS_API_TRACE(thread, Atomics, AtomicReadModifyWriteCase); JSHandle arrBufHadle(thread, arrBuf); JSHandle value = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); void *pointer = BuiltinsArrayBuffer::GetDataPointFromBuffer(thread, arrBufHadle.GetTaggedValue()); uint8_t *block = reinterpret_cast(pointer); uint32_t size = argv->GetArgsNumber(); switch (type) { case DataViewType::UINT8: { uint8_t tag = JSTaggedValue::ToInt8(thread, value); pointer = BuiltinsArrayBuffer::GetDataPointFromBuffer(thread, arrBufHadle.GetTaggedValue()); block = reinterpret_cast(pointer); return HandleWithUint8(thread, size, block, indexedPosition, argv, op, tag); } case DataViewType::INT8:{ int8_t tag = JSTaggedValue::ToInt8(thread, value); pointer = BuiltinsArrayBuffer::GetDataPointFromBuffer(thread, arrBufHadle.GetTaggedValue()); block = reinterpret_cast(pointer); return HandleWithInt8(thread, size, block, indexedPosition, argv, op, tag); } case DataViewType::UINT16: { uint16_t tag = JSTaggedValue::ToInt16(thread, value); pointer = BuiltinsArrayBuffer::GetDataPointFromBuffer(thread, arrBufHadle.GetTaggedValue()); block = reinterpret_cast(pointer); return HandleWithUint16(thread, size, block, indexedPosition, argv, op, tag); } case DataViewType::INT16: { int16_t tag = JSTaggedValue::ToInt16(thread, value); pointer = BuiltinsArrayBuffer::GetDataPointFromBuffer(thread, arrBufHadle.GetTaggedValue()); block = reinterpret_cast(pointer); return HandleWithInt16(thread, size, block, indexedPosition, argv, op, tag); } case DataViewType::UINT32: { uint32_t tag = JSTaggedValue::ToUint32(thread, value); pointer = BuiltinsArrayBuffer::GetDataPointFromBuffer(thread, arrBufHadle.GetTaggedValue()); block = reinterpret_cast(pointer); return HandleWithUint32(thread, size, block, indexedPosition, argv, op, tag); } case DataViewType::INT32: { int32_t tag = static_cast(JSTaggedValue::ToUint32(thread, value)); pointer = BuiltinsArrayBuffer::GetDataPointFromBuffer(thread, arrBufHadle.GetTaggedValue()); block = reinterpret_cast(pointer); return HandleWithInt32(thread, size, block, indexedPosition, argv, op, tag); } case DataViewType::BIGINT64: { int64_t val = 0; bool lossless = true; BigInt::BigIntToInt64(thread, value, &val, &lossless); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return HandleWithBigInt64(thread, size, block, indexedPosition, argv, op, val, lossless); } case DataViewType::BIGUINT64: { uint64_t val = 0; bool lossless = true; BigInt::BigIntToUint64(thread, value, &val, &lossless); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return HandleWithBigUint64(thread, size, block, indexedPosition, argv, op, val, lossless); } default: break; } LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); } template JSTaggedValue BuiltinsAtomics::HandleWithUint8(JSThread *thread, uint32_t size, uint8_t *block, uint32_t indexedPosition, EcmaRuntimeCallInfo *argv, const callbackfun &op, uint8_t &tag) { BUILTINS_API_TRACE(thread, Atomics, HandleWithUint8); BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle::Cast(GetCallArg(argv, 0))); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); uint8_t arg[ARGS_NUMBER] = {0}; arg[0] = tag; if (size == 3) { // the number of parameters is 3 auto result = op((block + indexedPosition), arg, ARGS_NUMBER); return BuiltinsBase::GetTaggedInt(result); } JSHandle newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH); uint8_t newTag = JSTaggedValue::ToUint8(thread, newValue); BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle::Cast(GetCallArg(argv, 0))); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); arg[1] = newTag; auto result = op((block + indexedPosition), arg, ARGS_NUMBER); return JSTaggedValue(result); } template JSTaggedValue BuiltinsAtomics::HandleWithInt8(JSThread *thread, uint32_t size, uint8_t *block, uint32_t indexedPosition, EcmaRuntimeCallInfo *argv, const callbackfun &op, int8_t &tag) { BUILTINS_API_TRACE(thread, Atomics, HandleWithInt8); BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle::Cast(GetCallArg(argv, 0))); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); int8_t arg[ARGS_NUMBER] = {0}; arg[0] = tag; if (size == 3) { // the number of parameters is 3 auto result = op(reinterpret_cast(block + indexedPosition), arg, ARGS_NUMBER); return BuiltinsBase::GetTaggedInt(result); } JSHandle newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH); int8_t newTag = JSTaggedValue::ToInt8(thread, newValue); BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle::Cast(GetCallArg(argv, 0))); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); arg[1] = newTag; auto result = op(reinterpret_cast(block + indexedPosition), arg, ARGS_NUMBER); return JSTaggedValue(result); } template JSTaggedValue BuiltinsAtomics::HandleWithUint16(JSThread *thread, uint32_t size, uint8_t *block, uint32_t indexedPosition, EcmaRuntimeCallInfo *argv, const callbackfun &op, uint16_t &tag) { BUILTINS_API_TRACE(thread, Atomics, HandleWithUint16); BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle::Cast(GetCallArg(argv, 0))); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); uint16_t arg[ARGS_NUMBER] = {0}; arg[0] = tag; if (size == 3) { // the number of parameters is 3 auto result = op(reinterpret_cast(block + indexedPosition), arg, ARGS_NUMBER); return BuiltinsBase::GetTaggedInt(result); } JSHandle newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH); uint16_t newTag = JSTaggedValue::ToUint16(thread, newValue); BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle::Cast(GetCallArg(argv, 0))); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); arg[1] = newTag; auto result = op(reinterpret_cast(block + indexedPosition), arg, ARGS_NUMBER); return JSTaggedValue(result); } template JSTaggedValue BuiltinsAtomics::HandleWithInt16(JSThread *thread, uint32_t size, uint8_t *block, uint32_t indexedPosition, EcmaRuntimeCallInfo *argv, const callbackfun &op, int16_t &tag) { BUILTINS_API_TRACE(thread, Atomics, HandleWithInt16); BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle::Cast(GetCallArg(argv, 0))); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); int16_t arg[ARGS_NUMBER] = {0}; arg[0] = tag; if (size == 3) { // the number of parameters is 3 auto result = op(reinterpret_cast(block + indexedPosition), arg, ARGS_NUMBER); return BuiltinsBase::GetTaggedInt(result); } JSHandle newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH); int16_t newTag = JSTaggedValue::ToInt16(thread, newValue); BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle::Cast(GetCallArg(argv, 0))); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); arg[1] = newTag; auto result = op(reinterpret_cast(block + indexedPosition), arg, ARGS_NUMBER); return JSTaggedValue(result); } template JSTaggedValue BuiltinsAtomics::HandleWithUint32(JSThread *thread, uint32_t size, uint8_t *block, uint32_t indexedPosition, EcmaRuntimeCallInfo *argv, const callbackfun &op, uint32_t &tag) { BUILTINS_API_TRACE(thread, Atomics, HandleWithUint32); BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle::Cast(GetCallArg(argv, 0))); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); uint32_t arg[ARGS_NUMBER] = {0}; arg[0] = tag; if (size == 3) { // the number of parameters is 3 auto result = op(reinterpret_cast(block + indexedPosition), arg, ARGS_NUMBER); return JSTaggedValue(result); } JSHandle newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH); uint32_t newTag = JSTaggedValue::ToUint32(thread, newValue); BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle::Cast(GetCallArg(argv, 0))); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); arg[1] = newTag; auto result = op(reinterpret_cast(block + indexedPosition), arg, ARGS_NUMBER); return JSTaggedValue(result); } template JSTaggedValue BuiltinsAtomics::HandleWithInt32(JSThread *thread, uint32_t size, uint8_t *block, uint32_t indexedPosition, EcmaRuntimeCallInfo *argv, const callbackfun &op, int32_t &tag) { BUILTINS_API_TRACE(thread, Atomics, HandleWithInt32); BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle::Cast(GetCallArg(argv, 0))); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); int32_t arg[ARGS_NUMBER] = {0}; arg[0] = tag; if (size == 3) { // the number of parameters is 3 auto result = op(reinterpret_cast(block + indexedPosition), arg, ARGS_NUMBER); return BuiltinsBase::GetTaggedInt(result); } JSHandle newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH); int32_t newTag = JSTaggedValue::ToInt32(thread, newValue); BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle::Cast(GetCallArg(argv, 0))); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); arg[1] = newTag; auto result = op(reinterpret_cast(block + indexedPosition), arg, ARGS_NUMBER); return JSTaggedValue(result); } template JSTaggedValue BuiltinsAtomics::HandleWithBigInt64(JSThread *thread, uint32_t size, uint8_t *block, uint32_t indexedPosition, EcmaRuntimeCallInfo *argv, const callbackfun &op, int64_t &tag, bool &lossless) { BUILTINS_API_TRACE(thread, Atomics, HandleWithBigInt64); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle::Cast(GetCallArg(argv, 0))); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); int64_t arg[ARGS_NUMBER] = {0}; arg[0] = tag; if (size == 3) { // the number of parameters is 3 auto result = op(reinterpret_cast(block + indexedPosition), arg, ARGS_NUMBER); return BigInt::Int64ToBigInt(thread, result).GetTaggedValue(); } JSHandle newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH); int64_t newVal = 0; BigInt::BigIntToInt64(thread, newValue, &newVal, &lossless); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle::Cast(GetCallArg(argv, 0))); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); arg[1] = newVal; auto result = op(reinterpret_cast(block + indexedPosition), arg, ARGS_NUMBER); return BigInt::Int64ToBigInt(thread, result).GetTaggedValue(); } template JSTaggedValue BuiltinsAtomics::HandleWithBigUint64(JSThread *thread, uint32_t size, uint8_t *block, uint32_t indexedPosition, EcmaRuntimeCallInfo *argv, const callbackfun &op, uint64_t &tag, bool &lossless) { BUILTINS_API_TRACE(thread, Atomics, HandleWithBigUint64); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle::Cast(GetCallArg(argv, 0))); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); uint64_t arg[ARGS_NUMBER] = {0}; arg[0] = tag; if (size == 3) { // the number of parameters is 3 auto result = op(reinterpret_cast(block + indexedPosition), arg, ARGS_NUMBER); return BigInt::Uint64ToBigInt(thread, result).GetTaggedValue(); } JSHandle newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH); uint64_t newVal = 0; BigInt::BigIntToUint64(thread, newValue, &newVal, &lossless); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle::Cast(GetCallArg(argv, 0))); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); arg[1] = newVal; auto result = op(reinterpret_cast(block + indexedPosition), arg, ARGS_NUMBER); return BigInt::Uint64ToBigInt(thread, result).GetTaggedValue(); } template WaitResult BuiltinsAtomics::DoWait(JSThread *thread, JSHandle &arrayBuffer, size_t index, T execpt, double timeout) { BUILTINS_API_TRACE(thread, Atomics, DoWait); MutexGuard lockGuard(g_mutex); void *buffer = BuiltinsArrayBuffer::GetDataPointFromBuffer(thread, arrayBuffer.GetTaggedValue()); ASSERT(buffer != nullptr); WaiterListNode *node = thread->GetEcmaVM()->GetWaiterListNode(); node->date_ = buffer; node->index_ = index; node->waitPointer_ = reinterpret_cast(buffer) + index; node->waiting_ = true; std::atomic *atomicValue = reinterpret_cast *>(node->waitPointer_); T value = atomicValue->load(); if (value != execpt) { return WaitResult::NOT_EQ; } g_waitLists->AddNode(node); uint64_t currentTime = 0; uint64_t timeoutTime = 0; bool hasTimeout = timeout != base::POSITIVE_INFINITY; if (hasTimeout) { currentTime = time::GetCurrentTimeInMillis(); timeoutTime = currentTime + static_cast(timeout); } WaitResult res = WaitResult::OK; ThreadNativeScope nativeScope(thread); while (true) { if (!node->waiting_) { res = WaitResult::OK; break; } if (hasTimeout) { currentTime = time::GetCurrentTimeInMillis(); if (currentTime >= timeoutTime) { res = WaitResult::TIME_OUT; break; } uint64_t untilTime = timeoutTime - currentTime; ASSERT(untilTime != 0); node->cond_.TimedWait(g_mutex, untilTime); } else { node->cond_.Wait(g_mutex); } } g_waitLists->DeleteNode(node); node->waiting_ = false; return res; } uint32_t BuiltinsAtomics::Signal(JSThread *thread, JSHandle &arrayBuffer, const size_t &index, double wakeCount) { void *buffer = BuiltinsArrayBuffer::GetDataPointFromBuffer(thread, arrayBuffer.GetTaggedValue()); ASSERT(buffer != nullptr); MutexGuard lockGuard(g_mutex); auto &locationListMap = g_waitLists->locationListMap_; auto iter = locationListMap.find(reinterpret_cast(buffer) + index); if (iter == locationListMap.end()) { return 0; } WaiterListNode *node = iter->second.pHead; uint32_t wokenUpCount = 0; while (node != nullptr && wakeCount > 0.0) { if (!node->waiting_) { node = node->next_; continue; } if (buffer == node->date_) { ASSERT(index == node->index_); node->waiting_ = false; WaiterListNode *oldNode = node; node = node->next_; oldNode->cond_.Signal(); if (wakeCount != base::POSITIVE_INFINITY) { wakeCount--; } wokenUpCount++; continue; } node = node->next_; } return wokenUpCount; } } // namespace panda::ecmascript::builtins