1/* 2 * Copyright (c) 2022-2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include "ecmascript/builtins/builtins_atomics.h" 17 18#include "ecmascript/base/atomic_helper.h" 19#include "ecmascript/base/typed_array_helper-inl.h" 20#include "libpandabase/utils/time.h" 21#include "ecmascript/checkpoint/thread_state_transition.h" 22 23namespace panda::ecmascript::builtins { 24using NumberHelper = base::NumberHelper; 25using AtomicHelper = base::AtomicHelper; 26using BytesSize = base::BytesSize; 27using BuiltinsArrayBuffer = builtins::BuiltinsArrayBuffer; 28 29WaiterList *g_waitLists = Singleton<WaiterList>::GetInstance(); 30Mutex *g_mutex = Singleton<Mutex>::GetInstance(); 31 32// 25.4.2 Atomics.add ( typedArray, index, value ) 33JSTaggedValue BuiltinsAtomics::Sub(EcmaRuntimeCallInfo *argv) 34{ 35 ASSERT(argv); 36 JSThread *thread = argv->GetThread(); 37 BUILTINS_API_TRACE(thread, Atomics, Sub); 38 [[maybe_unused]] EcmaHandleScope handleScope(thread); 39 JSHandle<JSTaggedValue> typedArray = GetCallArg(argv, 0); 40 JSHandle<JSTaggedValue> index = GetCallArg(argv, 1); 41 return AtomicReadModifyWrite(thread, typedArray, index, argv, AtomicHelper::SubFun()); 42} 43 44JSTaggedValue BuiltinsAtomics::Add(EcmaRuntimeCallInfo *argv) 45{ 46 ASSERT(argv); 47 JSThread *thread = argv->GetThread(); 48 BUILTINS_API_TRACE(thread, Atomics, Add); 49 [[maybe_unused]] EcmaHandleScope handleScope(thread); 50 JSHandle<JSTaggedValue> typedArray = GetCallArg(argv, 0); 51 JSHandle<JSTaggedValue> index = GetCallArg(argv, 1); 52 return AtomicReadModifyWrite(thread, typedArray, index, argv, AtomicHelper::AddFun()); 53} 54 55JSTaggedValue BuiltinsAtomics::And(EcmaRuntimeCallInfo *argv) 56{ 57 ASSERT(argv); 58 JSThread *thread = argv->GetThread(); 59 BUILTINS_API_TRACE(thread, Atomics, And); 60 [[maybe_unused]] EcmaHandleScope handleScope(thread); 61 JSHandle<JSTaggedValue> typedArray = GetCallArg(argv, 0); 62 JSHandle<JSTaggedValue> index = GetCallArg(argv, 1); 63 return AtomicReadModifyWrite(thread, typedArray, index, argv, AtomicHelper::AndFun()); 64} 65 66JSTaggedValue BuiltinsAtomics::Or(EcmaRuntimeCallInfo *argv) 67{ 68 ASSERT(argv); 69 JSThread *thread = argv->GetThread(); 70 BUILTINS_API_TRACE(thread, Atomics, Or); 71 [[maybe_unused]] EcmaHandleScope handleScope(thread); 72 JSHandle<JSTaggedValue> typedArray = GetCallArg(argv, 0); 73 JSHandle<JSTaggedValue> index = GetCallArg(argv, 1); 74 return AtomicReadModifyWrite(thread, typedArray, index, argv, AtomicHelper::OrFun()); 75} 76 77JSTaggedValue BuiltinsAtomics::Xor(EcmaRuntimeCallInfo *argv) 78{ 79 ASSERT(argv); 80 JSThread *thread = argv->GetThread(); 81 BUILTINS_API_TRACE(thread, Atomics, Xor); 82 [[maybe_unused]] EcmaHandleScope handleScope(thread); 83 JSHandle<JSTaggedValue> typedArray = GetCallArg(argv, 0); 84 JSHandle<JSTaggedValue> index = GetCallArg(argv, 1); 85 return AtomicReadModifyWrite(thread, typedArray, index, argv, AtomicHelper::XorFun()); 86} 87 88JSTaggedValue BuiltinsAtomics::CompareExchange(EcmaRuntimeCallInfo *argv) 89{ 90 ASSERT(argv); 91 JSThread *thread = argv->GetThread(); 92 BUILTINS_API_TRACE(thread, Atomics, CompareExchange); 93 [[maybe_unused]] EcmaHandleScope handleScope(thread); 94 JSHandle<JSTaggedValue> typedArray = GetCallArg(argv, 0); 95 JSHandle<JSTaggedValue> index = GetCallArg(argv, 1); 96 return AtomicReadModifyWrite(thread, typedArray, index, argv, AtomicHelper::CompareExchangeFun()); 97} 98 99JSTaggedValue BuiltinsAtomics::Exchange(EcmaRuntimeCallInfo *argv) 100{ 101 ASSERT(argv); 102 JSThread *thread = argv->GetThread(); 103 BUILTINS_API_TRACE(thread, Atomics, Exchange); 104 [[maybe_unused]] EcmaHandleScope handleScope(thread); 105 JSHandle<JSTaggedValue> typedArray = GetCallArg(argv, 0); 106 JSHandle<JSTaggedValue> index = GetCallArg(argv, 1); 107 return AtomicReadModifyWrite(thread, typedArray, index, argv, AtomicHelper::ExchangeFun()); 108} 109 110JSTaggedValue BuiltinsAtomics::Store(EcmaRuntimeCallInfo *argv) 111{ 112 ASSERT(argv); 113 JSThread *thread = argv->GetThread(); 114 BUILTINS_API_TRACE(thread, Atomics, Store); 115 [[maybe_unused]] EcmaHandleScope handleScope(thread); 116 JSHandle<JSTaggedValue> typedArray = GetCallArg(argv, 0); 117 JSHandle<JSTaggedValue> index = GetCallArg(argv, 1); 118 JSHandle<JSTaggedValue> value = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); 119 return AtomicHelper::AtomicStore(thread, typedArray, index, value); 120} 121 122JSTaggedValue BuiltinsAtomics::Load(EcmaRuntimeCallInfo *argv) 123{ 124 ASSERT(argv); 125 JSThread *thread = argv->GetThread(); 126 BUILTINS_API_TRACE(thread, Atomics, Load); 127 [[maybe_unused]] EcmaHandleScope handleScope(thread); 128 JSHandle<JSTaggedValue> typedArray = GetCallArg(argv, 0); 129 JSHandle<JSTaggedValue> index = GetCallArg(argv, 1); 130 return AtomicHelper::AtomicLoad(thread, typedArray, index); 131} 132 133JSTaggedValue BuiltinsAtomics::IsLockFree(EcmaRuntimeCallInfo *argv) 134{ 135 ASSERT(argv); 136 JSThread *thread = argv->GetThread(); 137 BUILTINS_API_TRACE(thread, Atomics, IsLockFree); 138 [[maybe_unused]] EcmaHandleScope handleScope(thread); 139 JSHandle<JSTaggedValue> sizeTag = GetCallArg(argv, 0); 140 BytesSize size = BytesSize(JSTaggedValue::ToInt32(thread, sizeTag)); 141 bool result = false; 142 switch (size) { 143 case BytesSize::ONEBYTES: 144 case BytesSize::TWOBYTES: 145 case BytesSize::FOURBYTES: 146 case BytesSize::EIGHTBYTES: 147 result = true; 148 break; 149 default: 150 result = false; 151 break; 152 } 153 return JSTaggedValue(result); 154} 155 156// 25.4.11 Atomics.wait ( typedArray, index, value, timeout ) 157JSTaggedValue BuiltinsAtomics::Wait(EcmaRuntimeCallInfo *argv) 158{ 159 ASSERT(argv); 160 JSThread *thread = argv->GetThread(); 161 BUILTINS_API_TRACE(thread, Atomics, Wait); 162 [[maybe_unused]] EcmaHandleScope handleScope(thread); 163 164 JSHandle<JSTaggedValue> array = GetCallArg(argv, 0); 165 JSHandle<JSTaggedValue> index = GetCallArg(argv, 1); 166 JSHandle<JSTaggedValue> value = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); 167 JSHandle<JSTaggedValue> timeout = GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH); 168 169 // 1. Let buffer be ? ValidateIntegerTypedArray(typedArray, true). 170 JSHandle<JSTaggedValue> arrayBuffer(thread, AtomicHelper::ValidateIntegerTypedArray(thread, array, true)); 171 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 172 173 // 2. If IsSharedArrayBuffer(buffer) is false, throw a TypeError exception. 174 if (!arrayBuffer->IsSharedArrayBuffer()) { 175 THROW_TYPE_ERROR_AND_RETURN(thread, "buffer is not sharedArrayBuffer.", 176 JSTaggedValue::Exception()); 177 } 178 179 // 3. Let indexedPosition be ? ValidateAtomicAccess(typedArray, index). 180 uint32_t indexedPosition = AtomicHelper::ValidateAtomicAccess(thread, array, index); 181 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 182 183 // 4. If typedArray.[[TypedArrayName]] is "BigInt64Array", let v be ? ToBigInt64(value). 184 // 5. Otherwise, let v be ? ToInt32(value). 185 int64_t v = 0; 186 if (array->IsJSBigInt64Array()) { 187 if (value->IsBoolean()) { 188 value = JSHandle<JSTaggedValue>(thread, JSTaggedValue::ToBigInt64(thread, value)); 189 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 190 } 191 v = JSHandle<BigInt>::Cast(value)->ToInt64(); 192 } else { 193 v = static_cast<int64_t>(JSTaggedValue::ToInt32(thread, value)); 194 } 195 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 196 197 // 6. Let q be ? ToNumber(timeout). 198 // 7. If q is NaN or +∞, let t be +∞; else if q is -∞, let t be 0; else let t be max(ℝ(q), 0). 199 double t = 0; 200 if (timeout->IsUndefined()) { 201 t = base::POSITIVE_INFINITY; 202 } else { 203 JSTaggedNumber q = JSTaggedValue::ToNumber(thread, timeout); 204 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 205 t = q.GetNumber(); 206 if (NumberHelper::IsNaN(q) || (!NumberHelper::IsFinite(q) && t > 0)) { 207 t = base::POSITIVE_INFINITY; 208 } else if (t < 0) { 209 t = 0; 210 } 211 } 212 213 // 8. Let B be AgentCanSuspend(). 214 // 9. If B is false, throw a TypeError exception. 215 if (!thread->GetCurrentEcmaContext()->GetAllowAtomicWait()) { 216 THROW_TYPE_ERROR_AND_RETURN(thread, "vm does not allow wait to block.", 217 JSTaggedValue::Exception()); 218 } 219 WaitResult res = WaitResult::OK; 220 if (array->IsJSBigInt64Array()) { 221 // AtomicHelper::Wait<int64_t>(thread, arrayBuffer, indexedPosition, v, t); 222 res = DoWait<int64_t>(thread, arrayBuffer, indexedPosition, v, t); 223 } else { 224 // AtomicHelper::Wait<int32_t>(thread, arrayBuffer, indexedPosition, static_cast<int32_t>(v), t); 225 res = DoWait<int32_t>(thread, arrayBuffer, indexedPosition, static_cast<int32_t>(v), t); 226 } 227 const GlobalEnvConstants *globalConst = thread->GlobalConstants(); 228 if (res == WaitResult::OK) { 229 return globalConst->GetOkString(); 230 } else if (res == WaitResult::NOT_EQ) { 231 return globalConst->GetNotEqualString(); 232 } 233 return globalConst->GetTimeoutString(); 234} 235 236// 25.4.12 Atomics.notify ( typedArray, index, count ) 237JSTaggedValue BuiltinsAtomics::Notify(EcmaRuntimeCallInfo *argv) 238{ 239 ASSERT(argv); 240 JSThread *thread = argv->GetThread(); 241 BUILTINS_API_TRACE(thread, Atomics, Notify); 242 [[maybe_unused]] EcmaHandleScope handleScope(thread); 243 244 JSHandle<JSTaggedValue> array = GetCallArg(argv, 0); 245 JSHandle<JSTaggedValue> index = GetCallArg(argv, 1); 246 JSHandle<JSTaggedValue> count = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); 247 248 // 1. Let buffer be ? ValidateIntegerTypedArray(typedArray, true). 249 JSHandle<JSTaggedValue> arrayBuffer(thread, AtomicHelper::ValidateIntegerTypedArray(thread, array, true)); 250 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 251 252 // 2. Let indexedPosition be ? ValidateAtomicAccess(typedArray, index). 253 uint32_t indexedPosition = AtomicHelper::ValidateAtomicAccess(thread, array, index); 254 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 255 256 // 3. If count is undefined, let c be +∞. 257 // 4. Else, 258 // a. Let intCount be ? ToIntegerOrInfinity(count). 259 // b. Let c be max(intCount, 0). 260 double c = 0; 261 if (count->IsUndefined()) { 262 c = base::POSITIVE_INFINITY; 263 } else { 264 JSTaggedNumber countTemp = JSTaggedValue::ToNumber(thread, count); 265 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 266 c = base::NumberHelper::TruncateDouble(countTemp.GetNumber()); 267 c = c < 0 ? 0 : c; 268 } 269 // 6. If IsSharedArrayBuffer(buffer) is false, return +0. 270 if (!arrayBuffer->IsSharedArrayBuffer()) { 271 return JSTaggedValue(0); 272 } 273 return JSTaggedValue(Signal(arrayBuffer, indexedPosition, c)); 274} 275 276template<typename callbackfun> 277JSTaggedValue BuiltinsAtomics::AtomicReadModifyWrite(JSThread *thread, const JSHandle<JSTaggedValue> &typedArray, 278 JSHandle<JSTaggedValue> &index, EcmaRuntimeCallInfo *argv, 279 const callbackfun &op) 280{ 281 BUILTINS_API_TRACE(thread, Atomics, AtomicReadModifyWrite); 282 if (!typedArray->IsTypedArray()) { 283 THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); 284 } 285 // 1. Let buffer be ? ValidateIntegerTypedArray(typedArray). 286 JSTaggedValue bufferValue = base::AtomicHelper::ValidateIntegerTypedArray(thread, typedArray); 287 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 288 JSHandle<JSTaggedValue> buffer(thread, bufferValue); 289 // 2. Let indexedPosition be ? ValidateAtomicAccess(typedArray, index). 290 uint32_t indexedPosition = base::AtomicHelper::ValidateAtomicAccess(thread, typedArray, index); 291 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 292 // 3. Let arrayTypeName be typedArray.[[TypedArrayName]]. 293 JSHandle<JSTaggedValue> arrayTypeName(thread, 294 JSTypedArray::Cast(typedArray->GetTaggedObject())->GetTypedArrayName()); 295 BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(typedArray)); 296 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 297 // 7. NOTE: The above check is not redundant with the check in ValidateIntegerTypedArray because the call to 298 // ToBigInt or ToIntegerOrInfinity on the preceding lines can have arbitrary side effects, which could cause the 299 // buffer to become detached. 300 // 8. Let elementType be the Element Type value in Table 60 for arrayTypeName. 301 DataViewType elementType = JSTypedArray::GetTypeFromName(thread, arrayTypeName); 302 // 9. Return GetModifySetValueInBuffer(buffer, indexedPosition, elementType, v, op). 303 return AtomicReadModifyWriteCase(thread, buffer.GetTaggedValue(), elementType, indexedPosition, argv, op); 304} 305 306template<typename callbackfun> 307JSTaggedValue BuiltinsAtomics::AtomicReadModifyWriteCase(JSThread *thread, JSTaggedValue arrBuf, 308 DataViewType type, uint32_t indexedPosition, 309 EcmaRuntimeCallInfo *argv, const callbackfun &op) 310{ 311 BUILTINS_API_TRACE(thread, Atomics, AtomicReadModifyWriteCase); 312 JSHandle<JSTaggedValue> arrBufHadle(thread, arrBuf); 313 JSHandle<JSTaggedValue> value = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); 314 void *pointer = BuiltinsArrayBuffer::GetDataPointFromBuffer(arrBufHadle.GetTaggedValue()); 315 uint8_t *block = reinterpret_cast<uint8_t *>(pointer); 316 uint32_t size = argv->GetArgsNumber(); 317 switch (type) { 318 case DataViewType::UINT8: { 319 uint8_t tag = JSTaggedValue::ToInt8(thread, value); 320 pointer = BuiltinsArrayBuffer::GetDataPointFromBuffer(arrBufHadle.GetTaggedValue()); 321 block = reinterpret_cast<uint8_t *>(pointer); 322 return HandleWithUint8(thread, size, block, indexedPosition, argv, op, tag); 323 } 324 case DataViewType::INT8:{ 325 int8_t tag = JSTaggedValue::ToInt8(thread, value); 326 pointer = BuiltinsArrayBuffer::GetDataPointFromBuffer(arrBufHadle.GetTaggedValue()); 327 block = reinterpret_cast<uint8_t *>(pointer); 328 return HandleWithInt8(thread, size, block, indexedPosition, argv, op, tag); 329 } 330 case DataViewType::UINT16: { 331 uint16_t tag = JSTaggedValue::ToInt16(thread, value); 332 pointer = BuiltinsArrayBuffer::GetDataPointFromBuffer(arrBufHadle.GetTaggedValue()); 333 block = reinterpret_cast<uint8_t *>(pointer); 334 return HandleWithUint16(thread, size, block, indexedPosition, argv, op, tag); 335 } 336 case DataViewType::INT16: { 337 int16_t tag = JSTaggedValue::ToInt16(thread, value); 338 pointer = BuiltinsArrayBuffer::GetDataPointFromBuffer(arrBufHadle.GetTaggedValue()); 339 block = reinterpret_cast<uint8_t *>(pointer); 340 return HandleWithInt16(thread, size, block, indexedPosition, argv, op, tag); 341 } 342 case DataViewType::UINT32: { 343 uint32_t tag = JSTaggedValue::ToUint32(thread, value); 344 pointer = BuiltinsArrayBuffer::GetDataPointFromBuffer(arrBufHadle.GetTaggedValue()); 345 block = reinterpret_cast<uint8_t *>(pointer); 346 return HandleWithUint32(thread, size, block, indexedPosition, argv, op, tag); 347 } 348 case DataViewType::INT32: { 349 int32_t tag = static_cast<int32_t>(JSTaggedValue::ToUint32(thread, value)); 350 pointer = BuiltinsArrayBuffer::GetDataPointFromBuffer(arrBufHadle.GetTaggedValue()); 351 block = reinterpret_cast<uint8_t *>(pointer); 352 return HandleWithInt32(thread, size, block, indexedPosition, argv, op, tag); 353 } 354 case DataViewType::BIGINT64: { 355 int64_t val = 0; 356 bool lossless = true; 357 BigInt::BigIntToInt64(thread, value, &val, &lossless); 358 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 359 return HandleWithBigInt64(thread, size, block, indexedPosition, argv, op, val, lossless); 360 } 361 case DataViewType::BIGUINT64: { 362 uint64_t val = 0; 363 bool lossless = true; 364 BigInt::BigIntToUint64(thread, value, &val, &lossless); 365 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 366 return HandleWithBigUint64(thread, size, block, indexedPosition, argv, op, val, lossless); 367 } 368 default: 369 break; 370 } 371 LOG_ECMA(FATAL) << "this branch is unreachable"; 372 UNREACHABLE(); 373} 374 375template<typename callbackfun> 376JSTaggedValue BuiltinsAtomics::HandleWithUint8(JSThread *thread, uint32_t size, uint8_t *block, 377 uint32_t indexedPosition, 378 EcmaRuntimeCallInfo *argv, const callbackfun &op, uint8_t &tag) 379{ 380 BUILTINS_API_TRACE(thread, Atomics, HandleWithUint8); 381 BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0))); 382 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 383 uint8_t arg[ARGS_NUMBER] = {0}; 384 arg[0] = tag; 385 if (size == 3) { // the number of parameters is 3 386 auto result = op((block + indexedPosition), arg, ARGS_NUMBER); 387 return BuiltinsBase::GetTaggedInt(result); 388 } 389 JSHandle<JSTaggedValue> newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH); 390 uint8_t newTag = JSTaggedValue::ToUint8(thread, newValue); 391 BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0))); 392 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 393 arg[1] = newTag; 394 auto result = op((block + indexedPosition), arg, ARGS_NUMBER); 395 return JSTaggedValue(result); 396} 397 398template<typename callbackfun> 399JSTaggedValue BuiltinsAtomics::HandleWithInt8(JSThread *thread, uint32_t size, uint8_t *block, 400 uint32_t indexedPosition, 401 EcmaRuntimeCallInfo *argv, const callbackfun &op, int8_t &tag) 402{ 403 BUILTINS_API_TRACE(thread, Atomics, HandleWithInt8); 404 BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0))); 405 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 406 int8_t arg[ARGS_NUMBER] = {0}; 407 arg[0] = tag; 408 if (size == 3) { // the number of parameters is 3 409 auto result = op(reinterpret_cast<int8_t *>(block + indexedPosition), arg, ARGS_NUMBER); 410 return BuiltinsBase::GetTaggedInt(result); 411 } 412 JSHandle<JSTaggedValue> newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH); 413 int8_t newTag = JSTaggedValue::ToInt8(thread, newValue); 414 BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0))); 415 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 416 arg[1] = newTag; 417 auto result = op(reinterpret_cast<int8_t *>(block + indexedPosition), arg, ARGS_NUMBER); 418 return JSTaggedValue(result); 419} 420 421template<typename callbackfun> 422JSTaggedValue BuiltinsAtomics::HandleWithUint16(JSThread *thread, uint32_t size, uint8_t *block, 423 uint32_t indexedPosition, 424 EcmaRuntimeCallInfo *argv, const callbackfun &op, uint16_t &tag) 425{ 426 BUILTINS_API_TRACE(thread, Atomics, HandleWithUint16); 427 BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0))); 428 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 429 uint16_t arg[ARGS_NUMBER] = {0}; 430 arg[0] = tag; 431 if (size == 3) { // the number of parameters is 3 432 auto result = op(reinterpret_cast<uint16_t *>(block + indexedPosition), arg, ARGS_NUMBER); 433 return BuiltinsBase::GetTaggedInt(result); 434 } 435 JSHandle<JSTaggedValue> newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH); 436 uint16_t newTag = JSTaggedValue::ToUint16(thread, newValue); 437 BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0))); 438 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 439 arg[1] = newTag; 440 auto result = op(reinterpret_cast<uint16_t *>(block + indexedPosition), arg, ARGS_NUMBER); 441 return JSTaggedValue(result); 442} 443 444template<typename callbackfun> 445JSTaggedValue BuiltinsAtomics::HandleWithInt16(JSThread *thread, uint32_t size, uint8_t *block, 446 uint32_t indexedPosition, 447 EcmaRuntimeCallInfo *argv, const callbackfun &op, int16_t &tag) 448{ 449 BUILTINS_API_TRACE(thread, Atomics, HandleWithInt16); 450 BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0))); 451 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 452 int16_t arg[ARGS_NUMBER] = {0}; 453 arg[0] = tag; 454 if (size == 3) { // the number of parameters is 3 455 auto result = op(reinterpret_cast<int16_t *>(block + indexedPosition), arg, ARGS_NUMBER); 456 return BuiltinsBase::GetTaggedInt(result); 457 } 458 JSHandle<JSTaggedValue> newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH); 459 int16_t newTag = JSTaggedValue::ToInt16(thread, newValue); 460 BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0))); 461 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 462 arg[1] = newTag; 463 auto result = op(reinterpret_cast<int16_t *>(block + indexedPosition), arg, ARGS_NUMBER); 464 return JSTaggedValue(result); 465} 466 467template<typename callbackfun> 468JSTaggedValue BuiltinsAtomics::HandleWithUint32(JSThread *thread, uint32_t size, uint8_t *block, 469 uint32_t indexedPosition, 470 EcmaRuntimeCallInfo *argv, const callbackfun &op, uint32_t &tag) 471{ 472 BUILTINS_API_TRACE(thread, Atomics, HandleWithUint32); 473 BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0))); 474 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 475 uint32_t arg[ARGS_NUMBER] = {0}; 476 arg[0] = tag; 477 if (size == 3) { // the number of parameters is 3 478 auto result = op(reinterpret_cast<uint32_t *>(block + indexedPosition), arg, ARGS_NUMBER); 479 return JSTaggedValue(result); 480 } 481 JSHandle<JSTaggedValue> newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH); 482 uint32_t newTag = JSTaggedValue::ToUint32(thread, newValue); 483 BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0))); 484 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 485 arg[1] = newTag; 486 auto result = op(reinterpret_cast<uint32_t *>(block + indexedPosition), arg, ARGS_NUMBER); 487 return JSTaggedValue(result); 488} 489 490template<typename callbackfun> 491JSTaggedValue BuiltinsAtomics::HandleWithInt32(JSThread *thread, uint32_t size, uint8_t *block, 492 uint32_t indexedPosition, 493 EcmaRuntimeCallInfo *argv, const callbackfun &op, int32_t &tag) 494{ 495 BUILTINS_API_TRACE(thread, Atomics, HandleWithInt32); 496 BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0))); 497 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 498 int32_t arg[ARGS_NUMBER] = {0}; 499 arg[0] = tag; 500 if (size == 3) { // the number of parameters is 3 501 auto result = op(reinterpret_cast<int32_t *>(block + indexedPosition), arg, ARGS_NUMBER); 502 return BuiltinsBase::GetTaggedInt(result); 503 } 504 JSHandle<JSTaggedValue> newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH); 505 int32_t newTag = JSTaggedValue::ToInt32(thread, newValue); 506 BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0))); 507 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 508 arg[1] = newTag; 509 auto result = op(reinterpret_cast<int32_t *>(block + indexedPosition), arg, ARGS_NUMBER); 510 return JSTaggedValue(result); 511} 512 513template<typename callbackfun> 514JSTaggedValue BuiltinsAtomics::HandleWithBigInt64(JSThread *thread, uint32_t size, uint8_t *block, 515 uint32_t indexedPosition, 516 EcmaRuntimeCallInfo *argv, const callbackfun &op, 517 int64_t &tag, bool &lossless) 518{ 519 BUILTINS_API_TRACE(thread, Atomics, HandleWithBigInt64); 520 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 521 BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0))); 522 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 523 int64_t arg[ARGS_NUMBER] = {0}; 524 arg[0] = tag; 525 if (size == 3) { // the number of parameters is 3 526 auto result = op(reinterpret_cast<int64_t *>(block + indexedPosition), arg, ARGS_NUMBER); 527 return BigInt::Int64ToBigInt(thread, result).GetTaggedValue(); 528 } 529 JSHandle<JSTaggedValue> newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH); 530 int64_t newVal = 0; 531 BigInt::BigIntToInt64(thread, newValue, &newVal, &lossless); 532 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 533 BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0))); 534 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 535 arg[1] = newVal; 536 auto result = op(reinterpret_cast<int64_t *>(block + indexedPosition), arg, ARGS_NUMBER); 537 return BigInt::Int64ToBigInt(thread, result).GetTaggedValue(); 538} 539 540template<typename callbackfun> 541JSTaggedValue BuiltinsAtomics::HandleWithBigUint64(JSThread *thread, uint32_t size, uint8_t *block, 542 uint32_t indexedPosition, 543 EcmaRuntimeCallInfo *argv, const callbackfun &op, 544 uint64_t &tag, bool &lossless) 545{ 546 BUILTINS_API_TRACE(thread, Atomics, HandleWithBigUint64); 547 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 548 BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0))); 549 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 550 uint64_t arg[ARGS_NUMBER] = {0}; 551 arg[0] = tag; 552 if (size == 3) { // the number of parameters is 3 553 auto result = op(reinterpret_cast<uint64_t *>(block + indexedPosition), arg, ARGS_NUMBER); 554 return BigInt::Uint64ToBigInt(thread, result).GetTaggedValue(); 555 } 556 JSHandle<JSTaggedValue> newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH); 557 uint64_t newVal = 0; 558 BigInt::BigIntToUint64(thread, newValue, &newVal, &lossless); 559 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 560 BuiltinsArrayBuffer::IsDetachedBuffer(thread, JSHandle<JSTypedArray>::Cast(GetCallArg(argv, 0))); 561 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 562 arg[1] = newVal; 563 auto result = op(reinterpret_cast<uint64_t *>(block + indexedPosition), arg, ARGS_NUMBER); 564 return BigInt::Uint64ToBigInt(thread, result).GetTaggedValue(); 565} 566 567template <typename T> 568WaitResult BuiltinsAtomics::DoWait(JSThread *thread, JSHandle<JSTaggedValue> &arrayBuffer, 569 size_t index, T execpt, double timeout) 570{ 571 BUILTINS_API_TRACE(thread, Atomics, DoWait); 572 MutexGuard lockGuard(g_mutex); 573 void *buffer = BuiltinsArrayBuffer::GetDataPointFromBuffer(arrayBuffer.GetTaggedValue()); 574 ASSERT(buffer != nullptr); 575 WaiterListNode *node = thread->GetCurrentEcmaContext()->GetWaiterListNode(); 576 node->date_ = buffer; 577 node->index_ = index; 578 node->waitPointer_ = reinterpret_cast<int8_t*>(buffer) + index; 579 node->waiting_ = true; 580 std::atomic<T> *atomicValue = reinterpret_cast<std::atomic<T> *>(node->waitPointer_); 581 T value = atomicValue->load(); 582 if (value != execpt) { 583 return WaitResult::NOT_EQ; 584 } 585 g_waitLists->AddNode(node); 586 uint64_t currentTime = 0; 587 uint64_t timeoutTime = 0; 588 bool hasTimeout = timeout != base::POSITIVE_INFINITY; 589 if (hasTimeout) { 590 currentTime = time::GetCurrentTimeInMillis(); 591 timeoutTime = currentTime + static_cast<uint64_t>(timeout); 592 } 593 WaitResult res = WaitResult::OK; 594 ThreadNativeScope nativeScope(thread); 595 while (true) { 596 if (!node->waiting_) { 597 res = WaitResult::OK; 598 break; 599 } 600 if (hasTimeout) { 601 currentTime = time::GetCurrentTimeInMillis(); 602 if (currentTime >= timeoutTime) { 603 res = WaitResult::TIME_OUT; 604 break; 605 } 606 uint64_t untilTime = timeoutTime - currentTime; 607 ASSERT(untilTime != 0); 608 609 node->cond_.TimedWait(g_mutex, untilTime); 610 } else { 611 node->cond_.Wait(g_mutex); 612 } 613 } 614 g_waitLists->DeleteNode(node); 615 node->waiting_ = false; 616 return res; 617} 618 619uint32_t BuiltinsAtomics::Signal(JSHandle<JSTaggedValue> &arrayBuffer, const size_t &index, double wakeCount) 620{ 621 void *buffer = BuiltinsArrayBuffer::GetDataPointFromBuffer(arrayBuffer.GetTaggedValue()); 622 ASSERT(buffer != nullptr); 623 MutexGuard lockGuard(g_mutex); 624 auto &locationListMap = g_waitLists->locationListMap_; 625 auto iter = locationListMap.find(reinterpret_cast<int8_t *>(buffer) + index); 626 if (iter == locationListMap.end()) { 627 return 0; 628 } 629 WaiterListNode *node = iter->second.pHead; 630 uint32_t wokenUpCount = 0; 631 while (node != nullptr && wakeCount > 0.0) { 632 if (!node->waiting_) { 633 node = node->next_; 634 continue; 635 } 636 if (buffer == node->date_) { 637 ASSERT(index == node->index_); 638 node->waiting_ = false; 639 640 WaiterListNode *oldNode = node; 641 node = node->next_; 642 oldNode->cond_.Signal(); 643 if (wakeCount != base::POSITIVE_INFINITY) { 644 wakeCount--; 645 } 646 wokenUpCount++; 647 continue; 648 } 649 node = node->next_; 650 } 651 return wokenUpCount; 652} 653} // namespace panda::ecmascript::builtins 654