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