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
23 namespace panda::ecmascript::builtins {
24 using NumberHelper = base::NumberHelper;
25 using AtomicHelper = base::AtomicHelper;
26 using BytesSize = base::BytesSize;
27 using BuiltinsArrayBuffer = builtins::BuiltinsArrayBuffer;
28
29 WaiterList *g_waitLists = Singleton<WaiterList>::GetInstance();
30 Mutex *g_mutex = Singleton<Mutex>::GetInstance();
31
32 // 25.4.2 Atomics.add ( typedArray, index, value )
Sub(EcmaRuntimeCallInfo *argv)33 JSTaggedValue 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
Add(EcmaRuntimeCallInfo *argv)44 JSTaggedValue 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
And(EcmaRuntimeCallInfo *argv)55 JSTaggedValue 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
Or(EcmaRuntimeCallInfo *argv)66 JSTaggedValue 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
Xor(EcmaRuntimeCallInfo *argv)77 JSTaggedValue 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
CompareExchange(EcmaRuntimeCallInfo *argv)88 JSTaggedValue 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
Exchange(EcmaRuntimeCallInfo *argv)99 JSTaggedValue 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
Store(EcmaRuntimeCallInfo *argv)110 JSTaggedValue 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
Load(EcmaRuntimeCallInfo *argv)122 JSTaggedValue 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
IsLockFree(EcmaRuntimeCallInfo *argv)133 JSTaggedValue 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 )
Wait(EcmaRuntimeCallInfo *argv)157 JSTaggedValue 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 )
Notify(EcmaRuntimeCallInfo *argv)237 JSTaggedValue 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
276 template<typename callbackfun>
AtomicReadModifyWrite(JSThread *thread, const JSHandle<JSTaggedValue> &typedArray, JSHandle<JSTaggedValue> &index, EcmaRuntimeCallInfo *argv, const callbackfun &op)277 JSTaggedValue 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
306 template<typename callbackfun>
AtomicReadModifyWriteCase(JSThread *thread, JSTaggedValue arrBuf, DataViewType type, uint32_t indexedPosition, EcmaRuntimeCallInfo *argv, const callbackfun &op)307 JSTaggedValue 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
375 template<typename callbackfun>
HandleWithUint8(JSThread *thread, uint32_t size, uint8_t *block, uint32_t indexedPosition, EcmaRuntimeCallInfo *argv, const callbackfun &op, uint8_t &tag)376 JSTaggedValue 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
398 template<typename callbackfun>
HandleWithInt8(JSThread *thread, uint32_t size, uint8_t *block, uint32_t indexedPosition, EcmaRuntimeCallInfo *argv, const callbackfun &op, int8_t &tag)399 JSTaggedValue 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
421 template<typename callbackfun>
HandleWithUint16(JSThread *thread, uint32_t size, uint8_t *block, uint32_t indexedPosition, EcmaRuntimeCallInfo *argv, const callbackfun &op, uint16_t &tag)422 JSTaggedValue 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
444 template<typename callbackfun>
HandleWithInt16(JSThread *thread, uint32_t size, uint8_t *block, uint32_t indexedPosition, EcmaRuntimeCallInfo *argv, const callbackfun &op, int16_t &tag)445 JSTaggedValue 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
467 template<typename callbackfun>
HandleWithUint32(JSThread *thread, uint32_t size, uint8_t *block, uint32_t indexedPosition, EcmaRuntimeCallInfo *argv, const callbackfun &op, uint32_t &tag)468 JSTaggedValue 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
490 template<typename callbackfun>
HandleWithInt32(JSThread *thread, uint32_t size, uint8_t *block, uint32_t indexedPosition, EcmaRuntimeCallInfo *argv, const callbackfun &op, int32_t &tag)491 JSTaggedValue 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
513 template<typename callbackfun>
HandleWithBigInt64(JSThread *thread, uint32_t size, uint8_t *block, uint32_t indexedPosition, EcmaRuntimeCallInfo *argv, const callbackfun &op, int64_t &tag, bool &lossless)514 JSTaggedValue 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
540 template<typename callbackfun>
HandleWithBigUint64(JSThread *thread, uint32_t size, uint8_t *block, uint32_t indexedPosition, EcmaRuntimeCallInfo *argv, const callbackfun &op, uint64_t &tag, bool &lossless)541 JSTaggedValue 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
567 template <typename T>
DoWait(JSThread *thread, JSHandle<JSTaggedValue> &arrayBuffer, size_t index, T execpt, double timeout)568 WaitResult 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
Signal(JSHandle<JSTaggedValue> &arrayBuffer, const size_t &index, double wakeCount)619 uint32_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