diff --git a/BUILD.gn b/BUILD.gn index 1986d36d..9b5c3804 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -301,6 +301,7 @@ config("icu_path_test_config") { ecma_source = [ "ecmascript/base/array_helper.cpp", + "ecmascript/base/atomic_helper.cpp", "ecmascript/base/builtins_base.cpp", "ecmascript/base/error_helper.cpp", "ecmascript/base/json_parser.cpp", @@ -314,6 +315,7 @@ ecma_source = [ "ecmascript/builtins/builtins_array.cpp", "ecmascript/builtins/builtins_arraybuffer.cpp", "ecmascript/builtins/builtins_async_function.cpp", + "ecmascript/builtins/builtins_atomics.cpp", "ecmascript/builtins/builtins_bigint.cpp", "ecmascript/builtins/builtins_boolean.cpp", "ecmascript/builtins/builtins_dataview.cpp", @@ -476,6 +478,7 @@ ecma_source = [ "ecmascript/tagged_dictionary.cpp", "ecmascript/tagged_tree.cpp", "ecmascript/template_string.cpp", + "ecmascript/waiter_list.cpp", "ecmascript/weak_vector.cpp", "ecmascript/compiler/llvm/llvm_stackmap_parser.cpp", "ecmascript/stubs/runtime_stubs.cpp", diff --git a/ecmascript/base/atomic_helper.cpp b/ecmascript/base/atomic_helper.cpp new file mode 100644 index 00000000..d3d790e9 --- /dev/null +++ b/ecmascript/base/atomic_helper.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2022 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/base/atomic_helper.h" +#include "ecmascript/base/typed_array_helper-inl.h" + +namespace panda::ecmascript::base { +using BuiltinsArrayBuffer = builtins::BuiltinsArrayBuffer; + +JSTaggedValue AtomicHelper::ValidateIntegerTypedArray(JSThread *thread, JSHandle typedArray, + bool waitable) +{ + // 1. If waitable is not present, set waitable to false. + // 2. Let buffer be ? ValidateTypedArray(typedArray). + JSTaggedValue buffer = TypedArrayHelper::ValidateTypedArray(thread, typedArray); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSHandle bufferHandle(thread, buffer); + + // 3. Let typeName be typedArray.[[TypedArrayName]]. + // 4. Let type be the Element Type value in Table 60 for typeName. + JSHandle typeName(thread, JSTypedArray::Cast(typedArray->GetTaggedObject())->GetTypedArrayName()); + DataViewType type = JSTypedArray::GetTypeFromName(thread, typeName); + + // 5. If waitable is true, then + // a. If typeName is not "Int32Array" or "BigInt64Array", throw a TypeError exception. + // 6. Else, + // a. If ! IsUnclampedIntegerElementType(type) is false and ! IsBigIntElementType(type) is false, + // throw a TypeError exception. + if (waitable) { + if (!(type == DataViewType::INT32 || type == DataViewType::BIGINT64)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The typeName is not Int32Array/BigInt64Array.", + JSTaggedValue::Exception()); + } + } else { + if (!(BuiltinsArrayBuffer::IsUnclampedIntegerElementType(type) || + BuiltinsArrayBuffer::IsBigIntElementType(type))) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The typedArray type is not UnclampedInteger/BigInt.", + JSTaggedValue::Exception()); + } + } + // 7. Return buffer. + return bufferHandle.GetTaggedValue(); +} + +int32_t AtomicHelper::ValidateAtomicAccess(JSThread *thread, const JSHandle typedArray, + JSHandle requestIndex) +{ + // 1. Assert: typedArray is an Object that has a [[ViewedArrayBuffer]] internal slot. + ASSERT(typedArray->IsECMAObject() && typedArray->IsTypedArray()); + // 2. Let length be typedArray.[[ArrayLength]]. + JSHandle typedArrayObj(typedArray); + JSHandle srcObj(typedArray); + int32_t length = static_cast(srcObj->GetArrayLength()); + + // 3. Let accessIndex be ? ToIndex(requestIndex). + JSTaggedNumber accessIndex = JSTaggedValue::ToIndex(thread, requestIndex); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); + int32_t index = base::NumberHelper::DoubleInRangeInt32(accessIndex.GetNumber()); + + // 4. Assert: accessIndex ≥ 0. + ASSERT(index >= 0); + + // 5. If accessIndex ≥ length, throw a RangeError exception. + if (index >= length) { + THROW_RANGE_ERROR_AND_RETURN(thread, "Index is overflow.", 0); + } + + // 6. Let arrayTypeName be typedArray.[[TypedArrayName]]. + // 7. Let elementSize be the Element Size value specified in Table 60 for arrayTypeName. + // 8. Let offset be typedArray.[[ByteOffset]]. + JSHandle arrayTypeName(thread, JSTypedArray::Cast(*typedArrayObj)->GetTypedArrayName()); + int32_t elementSize = TypedArrayHelper::GetSizeFromName(thread, arrayTypeName); + uint32_t offset = srcObj->GetByteOffset(); + // 9. Return (accessIndex × elementSize) + offset. + int32_t allOffset = index * elementSize + offset; + return allOffset; +} + +JSTaggedValue AtomicHelper::AtomicStore(JSThread *thread, const JSHandle &typedArray, + JSHandle index, JSHandle &value) +{ + JSTaggedValue bufferValue = ValidateIntegerTypedArray(thread, typedArray); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle buffer(thread, bufferValue); + uint32_t indexedPosition = ValidateAtomicAccess(thread, typedArray, index); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle arrayTypeName(thread, + JSTypedArray::Cast(typedArray->GetTaggedObject())->GetTypedArrayName()); + DataViewType type = JSTypedArray::GetTypeFromName(thread, arrayTypeName); + JSHandle bufferTag; + if (type == DataViewType::BIGUINT64 || type == DataViewType::BIGINT64) { + JSTaggedValue integerValue = JSTaggedValue::ToBigInt(thread, value); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + bufferTag = JSHandle(thread, integerValue); + } else { + JSTaggedNumber integerValue = JSTaggedValue::ToInteger(thread, value); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + bufferTag = JSHandle(thread, integerValue); + } + if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The ArrayBuffer of this value is detached buffer.", + JSTaggedValue::Exception()); + } + BuiltinsArrayBuffer::SetValueInBuffer(thread, buffer.GetTaggedValue(), indexedPosition, type, bufferTag, true); + return bufferTag.GetTaggedValue(); +} + +JSTaggedValue AtomicHelper::AtomicLoad(JSThread *thread, const JSHandle &typedArray, + JSHandle index) +{ + JSTaggedValue bufferValue = ValidateIntegerTypedArray(thread, typedArray); + JSHandle buffer(thread, bufferValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t indexedPosition = ValidateAtomicAccess(thread, typedArray, index); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The ArrayBuffer of this value is detached buffer.", + JSTaggedValue::Exception()); + } + JSHandle arrayTypeName(thread, + JSTypedArray::Cast(typedArray->GetTaggedObject())->GetTypedArrayName()); + DataViewType elementType = JSTypedArray::GetTypeFromName(thread, arrayTypeName); + return BuiltinsArrayBuffer::GetValueFromBuffer(thread, buffer.GetTaggedValue(), + indexedPosition, elementType, true); +} +} // panda::ecmascript::base + diff --git a/ecmascript/base/atomic_helper.h b/ecmascript/base/atomic_helper.h new file mode 100644 index 00000000..5b17c50b --- /dev/null +++ b/ecmascript/base/atomic_helper.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2022 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. + */ + +#ifndef ECMASCRIPT_BASE_ATOMIC_HELPER_H +#define ECMASCRIPT_BASE_ATOMIC_HELPER_H + +#include "ecmascript/js_dataview.h" + +namespace panda::ecmascript::base { +enum class BytesSize : int32_t {ONEBYTES = 1, TWOBYTES = 2, FOURBYTES = 4, EIGHTBYTES = 8}; + +class AtomicHelper final { +public: + struct SubFun { + template + T operator()(T *ptr, const T *arg) const + { + std::atomic *atomicValue = reinterpret_cast *>(ptr); + return atomicValue->fetch_sub(arg[0], std::memory_order_seq_cst); + } + }; + + struct AddFun { + template + T operator()(T *ptr, const T *arg) const + { + std::atomic *atomicValue = reinterpret_cast *>(ptr); + return atomicValue->fetch_add(arg[0], std::memory_order_seq_cst); + } + }; + + struct AndFun { + template + T operator()(T *ptr, const T *arg) const + { + std::atomic *atomicValue = reinterpret_cast *>(ptr); + return atomicValue->fetch_and(arg[0], std::memory_order_seq_cst); + } + }; + + struct OrFun { + template + T operator()(T *ptr, const T *arg) const + { + std::atomic *atomicValue = reinterpret_cast *>(ptr); + return atomicValue->fetch_or(arg[0], std::memory_order_seq_cst); + } + }; + + struct XorFun { + template + T operator()(T *ptr, const T *arg) const + { + std::atomic *atomicValue = reinterpret_cast *>(ptr); + return atomicValue->fetch_xor(arg[0], std::memory_order_seq_cst); + } + }; + + struct CompareExchangeFun { + template + T operator()(T *ptr, const T *arg) const + { + T a = arg[0]; + std::atomic *atomicValue = reinterpret_cast *>(ptr); + atomicValue->compare_exchange_strong(a, arg[1], std::memory_order_seq_cst); + return a; + } + }; + + struct ExchangeFun { + template + T operator()(T *ptr, const T *arg) const + { + std::atomic *atomicValue = reinterpret_cast *>(ptr); + return atomicValue->exchange(arg[0], std::memory_order_seq_cst); + } + }; + + // 25.4.1.1 ValidateIntegerTypedArray ( typedArray [ , waitable ] ) + static JSTaggedValue ValidateIntegerTypedArray(JSThread *thread, JSHandle typedArray, + bool waitable = false); + // 25.4.2.2 ValidateAtomicAccess ( typedArray, requestIndex ) + static int32_t ValidateAtomicAccess(JSThread *thread, const JSHandle typedArray, + JSHandle requestIndex); + static JSTaggedValue AtomicStore(JSThread *thread, const JSHandle &typedArray, + JSHandle index, JSHandle &value); + static JSTaggedValue AtomicLoad(JSThread *thread, const JSHandle &typedArray, + JSHandle index); +}; +} // namespace panda::ecmascript::base + +#endif // ECMASCRIPT_BASE_ATOMIC_HELPER_H \ No newline at end of file diff --git a/ecmascript/base/typed_array_helper-inl.h b/ecmascript/base/typed_array_helper-inl.h index 388ebe2e..efb7a9d7 100644 --- a/ecmascript/base/typed_array_helper-inl.h +++ b/ecmascript/base/typed_array_helper-inl.h @@ -92,42 +92,6 @@ uint32_t TypedArrayHelper::GetElementSize(JSType type) } } -DataViewType TypedArrayHelper::GetTypeFromName(JSThread *thread, const JSHandle &typeName) -{ - const GlobalEnvConstants *globalConst = thread->GlobalConstants(); - if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledInt8ArrayString())) { - return DataViewType::INT8; - } - if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint8ArrayString())) { - return DataViewType::UINT8; - } - if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint8ClampedArrayString())) { - return DataViewType::UINT8_CLAMPED; - } - if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledInt16ArrayString())) { - return DataViewType::INT16; - } - if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint16ArrayString())) { - return DataViewType::UINT16; - } - if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledInt32ArrayString())) { - return DataViewType::INT32; - } - if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint32ArrayString())) { - return DataViewType::UINT32; - } - if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledFloat32ArrayString())) { - return DataViewType::FLOAT32; - } - if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledFloat64ArrayString())) { - return DataViewType::FLOAT64; - } - if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledBigInt64ArrayString())) { - return DataViewType::BIGINT64; - } - return DataViewType::BIGUINT64; -} - JSHandle TypedArrayHelper::GetConstructor(JSThread *thread, const JSHandle &obj) { JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); diff --git a/ecmascript/base/typed_array_helper.cpp b/ecmascript/base/typed_array_helper.cpp index 9d2a5a8d..1745b805 100644 --- a/ecmascript/base/typed_array_helper.cpp +++ b/ecmascript/base/typed_array_helper.cpp @@ -186,14 +186,14 @@ JSTaggedValue TypedArrayHelper::CreateFromTypedArray(EcmaRuntimeCallInfo *argv, THROW_TYPE_ERROR_AND_RETURN(thread, "The srcData is detached buffer.", JSTaggedValue::Exception()); } // 8. Let elementType be the Element Type value in Table 61 for constructorName. - DataViewType elementType = TypedArrayHelper::GetTypeFromName(thread, constructorName); + DataViewType elementType = JSTypedArray::GetTypeFromName(thread, constructorName); // 9. Let elementLength be srcArray.[[ArrayLength]]. // 10. Let srcName be the String value of srcArray.[[TypedArrayName]]. // 11. Let srcType be the Element Type value in Table 61 for srcName. // 12. Let srcElementSize be the Element Size value specified in Table 61 for srcName. uint32_t elementLength = srcObj->GetArrayLength(); JSHandle srcName(thread, srcObj->GetTypedArrayName()); - DataViewType srcType = TypedArrayHelper::GetTypeFromName(thread, srcName); + DataViewType srcType = JSTypedArray::GetTypeFromName(thread, srcName); uint32_t srcElementSize = TypedArrayHelper::GetSizeFromName(thread, srcName); // 13. Let srcByteOffset be srcArray.[[ByteOffset]]. // 14. Let elementSize be the Element Size value specified in Table 61 for constructorName. diff --git a/ecmascript/base/typed_array_helper.h b/ecmascript/base/typed_array_helper.h index 8f032054..d4bb38e9 100644 --- a/ecmascript/base/typed_array_helper.h +++ b/ecmascript/base/typed_array_helper.h @@ -43,7 +43,6 @@ public: inline static DataViewType GetType(JSType type); inline static uint32_t GetElementSize(const JSHandle &obj); inline static uint32_t GetElementSize(JSType type); - inline static DataViewType GetTypeFromName(JSThread *thread, const JSHandle &typeName); inline static JSHandle GetConstructor(JSThread *thread, const JSHandle &obj); inline static JSHandle GetConstructorFromName(JSThread *thread, const JSHandle &typeName); diff --git a/ecmascript/builtins.cpp b/ecmascript/builtins.cpp index 38a0c1dd..d0a48e81 100644 --- a/ecmascript/builtins.cpp +++ b/ecmascript/builtins.cpp @@ -30,6 +30,7 @@ #include "ecmascript/builtins/builtins_array.h" #include "ecmascript/builtins/builtins_arraybuffer.h" #include "ecmascript/builtins/builtins_async_function.h" +#include "ecmascript/builtins/builtins_atomics.h" #include "ecmascript/builtins/builtins_bigint.h" #include "ecmascript/builtins/builtins_boolean.h" #include "ecmascript/builtins/builtins_collator.h" @@ -134,6 +135,7 @@ using StringIterator = builtins::BuiltinsStringIterator; using RegExp = builtins::BuiltinsRegExp; using Function = builtins::BuiltinsFunction; using Math = builtins::BuiltinsMath; +using Atomics = builtins::BuiltinsAtomics; using ArrayBuffer = builtins::BuiltinsArrayBuffer; using Json = builtins::BuiltinsJson; using Proxy = builtins::BuiltinsProxy; @@ -314,6 +316,7 @@ void Builtins::Initialize(const JSHandle &env, JSThread *thread) InitializeGlobalObject(env, globalObject); InitializeMath(env, objFuncPrototypeVal); + InitializeAtomics(env, objFuncPrototypeVal); InitializeJson(env, objFuncPrototypeVal); InitializeIterator(env, objFuncDynclass); InitializeProxy(env); @@ -1357,6 +1360,34 @@ void Builtins::InitializeWeakSet(const JSHandle &env, const JSHandle< env->SetBuiltinsWeakSetFunction(thread_, weakSetFunction); } +void Builtins::InitializeAtomics(const JSHandle &env, + const JSHandle &objFuncPrototypeVal) const +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + JSHandle atomicsDynclass = factory_->NewEcmaDynClass(JSObject::SIZE, JSType::JS_OBJECT, + objFuncPrototypeVal); + JSHandle atomicsObject = factory_->NewJSObject(atomicsDynclass); + SetFunction(env, atomicsObject, "add", Atomics::Add, FunctionLength::THREE); + SetFunction(env, atomicsObject, "and", Atomics::And, FunctionLength::THREE); + SetFunction(env, atomicsObject, "sub", Atomics::Sub, FunctionLength::THREE); + SetFunction(env, atomicsObject, "or", Atomics::Or, FunctionLength::THREE); + SetFunction(env, atomicsObject, "xor", Atomics::Xor, FunctionLength::THREE); + SetFunction(env, atomicsObject, "compareExchange", Atomics::CompareExchange, FunctionLength::FOUR); + SetFunction(env, atomicsObject, "exchange", Atomics::Exchange, FunctionLength::THREE); + SetFunction(env, atomicsObject, "isLockFree", Atomics::IsLockFree, FunctionLength::ONE); + SetFunction(env, atomicsObject, "load", Atomics::Load, FunctionLength::TWO); + SetFunction(env, atomicsObject, "store", Atomics::Store, FunctionLength::THREE); + SetFunction(env, atomicsObject, "wait", Atomics::Wait, FunctionLength::FOUR); + SetFunction(env, atomicsObject, "notify", Atomics::Notify, FunctionLength::THREE); + JSHandle atomicsString(factory_->NewFromASCII("Atomics")); + JSHandle globalObject(thread_, env->GetGlobalObject()); + PropertyDescriptor atomicsDesc(thread_, JSHandle::Cast(atomicsObject), true, false, true); + JSObject::DefineOwnProperty(thread_, globalObject, atomicsString, atomicsDesc); + // @@ToStringTag + SetStringTagSymbol(env, atomicsObject, "Atomics"); + env->SetAtomicsFunction(thread_, atomicsObject); +} + void Builtins::InitializeMath(const JSHandle &env, const JSHandle &objFuncPrototypeVal) const { [[maybe_unused]] EcmaHandleScope scope(thread_); diff --git a/ecmascript/builtins.h b/ecmascript/builtins.h index ed0d5f1e..1d8ce4d2 100644 --- a/ecmascript/builtins.h +++ b/ecmascript/builtins.h @@ -141,6 +141,8 @@ private: void InitializeMath(const JSHandle &env, const JSHandle &objFuncPrototypeVal) const; + void InitializeAtomics(const JSHandle &env, const JSHandle &objFuncPrototypeVal) const; + void InitializeJson(const JSHandle &env, const JSHandle &objFuncPrototypeVal) const; void InitializeString(const JSHandle &env, const JSHandle &primRefObjDynclass) const; diff --git a/ecmascript/builtins/builtins_arraybuffer.cpp b/ecmascript/builtins/builtins_arraybuffer.cpp index a7933aaa..0519014d 100644 --- a/ecmascript/builtins/builtins_arraybuffer.cpp +++ b/ecmascript/builtins/builtins_arraybuffer.cpp @@ -386,6 +386,22 @@ bool BuiltinsArrayBuffer::IsBigIntElementType(DataViewType type) return false; } +// es12 25.1.2.6 IsUnclampedIntegerElementType ( type ) +bool BuiltinsArrayBuffer::IsUnclampedIntegerElementType(DataViewType type) +{ + switch (type) { + case DataViewType::INT8: + case DataViewType::INT16: + case DataViewType::INT32: + case DataViewType::UINT8: + case DataViewType::UINT16: + case DataViewType::UINT32: + return true; + default: + return false; + } +} + template void BuiltinsArrayBuffer::SetTypeData(uint8_t *block, T value, uint32_t index) { diff --git a/ecmascript/builtins/builtins_arraybuffer.h b/ecmascript/builtins/builtins_arraybuffer.h index 63498eed..1c2d2f94 100644 --- a/ecmascript/builtins/builtins_arraybuffer.h +++ b/ecmascript/builtins/builtins_arraybuffer.h @@ -66,6 +66,8 @@ public: // 24.1.1.1 AllocateArrayBuffer(constructor, byteLength) static JSTaggedValue AllocateArrayBuffer(JSThread *thread, const JSHandle &newTarget, double byteLength); + // es12 25.1.2.6 IsUnclampedIntegerElementType ( type ) + static bool IsUnclampedIntegerElementType(DataViewType type); // es12 25.1.2.7 IsBigIntElementType ( type ) static bool IsBigIntElementType(DataViewType type); diff --git a/ecmascript/builtins/builtins_atomics.cpp b/ecmascript/builtins/builtins_atomics.cpp new file mode 100644 index 00000000..43e086bc --- /dev/null +++ b/ecmascript/builtins/builtins_atomics.cpp @@ -0,0 +1,587 @@ +/* + * Copyright (c) 2022 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 "utils/time.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; + 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, Atomic, 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). + int32_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()) { + 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()->GetAllowAtomicWait()) { + 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, Atomic, 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). + int32_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(arrayBuffer, indexedPosition, c)); +} + +template +JSTaggedValue BuiltinsAtomics::AtomicReadModifyWrite(JSThread *thread, const JSHandle &typedArray, + JSHandle &index, EcmaRuntimeCallInfo *argv, + const callbackfun &op) +{ + 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). + int32_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()); + if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer.GetTaggedValue())) { + THROW_TYPE_ERROR_AND_RETURN(thread, "The ArrayBuffer of this value is detached buffer.", + JSTaggedValue::Exception()); + } + // 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, int32_t indexedPosition, + EcmaRuntimeCallInfo *argv, const callbackfun &op) +{ + JSArrayBuffer *jsArrayBuffer = JSArrayBuffer::Cast(arrBuf.GetTaggedObject()); + JSTaggedValue data = jsArrayBuffer->GetArrayBufferData(); + void *pointer = JSNativePointer::Cast(data.GetTaggedObject())->GetExternalPointer(); + auto *block = reinterpret_cast(pointer); + uint32_t size = argv->GetArgsNumber(); + switch (type) { + case DataViewType::UINT8: + return HandleWithUint8(thread, size, block, indexedPosition, argv, op); + case DataViewType::INT8: + return HandleWithInt8(thread, size, block, indexedPosition, argv, op); + case DataViewType::UINT16: + return HandleWithUint16(thread, size, block, indexedPosition, argv, op); + case DataViewType::INT16: + return HandleWithInt16(thread, size, block, indexedPosition, argv, op); + case DataViewType::UINT32: + return HandleWithUint32(thread, size, block, indexedPosition, argv, op); + case DataViewType::INT32: + return HandleWithInt32(thread, size, block, indexedPosition, argv, op); + case DataViewType::BIGINT64: + return HandleWithBigInt64(thread, size, block, indexedPosition, argv, op); + case DataViewType::BIGUINT64: + return HandleWithBigUint64(thread, size, block, indexedPosition, argv, op); + default: + break; + } + UNREACHABLE(); +} + +template +JSTaggedValue BuiltinsAtomics::HandleWithUint8(JSThread *thread, uint32_t size, uint8_t *block, + int32_t indexedPosition, + EcmaRuntimeCallInfo *argv, const callbackfun &op) +{ + JSHandle value = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); + uint8_t tag = JSTaggedValue::ToUint8(thread, value); + if (size == 3) { // the number of parameters is 3 + auto result = op((block + indexedPosition), &tag); + return BuiltinsBase::GetTaggedInt(result); + } + JSHandle newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH); + uint8_t newTag = JSTaggedValue::ToUint8(thread, newValue); + uint8_t arg[ARGS_NUMBER] = {0}; + arg[0] = tag; + arg[1] = newTag; + auto result = op((block + indexedPosition), arg); + return JSTaggedValue(result); +} + +template +JSTaggedValue BuiltinsAtomics::HandleWithInt8(JSThread *thread, uint32_t size, uint8_t *block, + int32_t indexedPosition, + EcmaRuntimeCallInfo *argv, const callbackfun &op) +{ + JSHandle value = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); + int8_t tag = JSTaggedValue::ToInt8(thread, value); + if (size == 3) { // the number of parameters is 3 + auto result = op(reinterpret_cast(block + indexedPosition), &tag); + return BuiltinsBase::GetTaggedInt(result); + } + JSHandle newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH); + int8_t newTag = JSTaggedValue::ToInt8(thread, newValue); + int8_t arg[ARGS_NUMBER] = {0}; + arg[0] = tag; + arg[1] = newTag; + auto result = op(reinterpret_cast(block + indexedPosition), arg); + return JSTaggedValue(result); +} + +template +JSTaggedValue BuiltinsAtomics::HandleWithUint16(JSThread *thread, uint32_t size, uint8_t *block, + int32_t indexedPosition, + EcmaRuntimeCallInfo *argv, const callbackfun &op) +{ + JSHandle value = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); + uint16_t tag = JSTaggedValue::ToUint16(thread, value); + if (size == 3) { // the number of parameters is 3 + auto result = op(reinterpret_cast(block + indexedPosition), &tag); + return BuiltinsBase::GetTaggedInt(result); + } + JSHandle newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH); + uint16_t newTag = JSTaggedValue::ToUint16(thread, newValue); + uint16_t arg[ARGS_NUMBER] = {0}; + arg[0] = tag; + arg[1] = newTag; + auto result = op(reinterpret_cast(block + indexedPosition), arg); + return JSTaggedValue(result); +} + +template +JSTaggedValue BuiltinsAtomics::HandleWithInt16(JSThread *thread, uint32_t size, uint8_t *block, + int32_t indexedPosition, + EcmaRuntimeCallInfo *argv, const callbackfun &op) +{ + JSHandle value = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); + int16_t tag = JSTaggedValue::ToInt16(thread, value); + if (size == 3) { // the number of parameters is 3 + auto result = op(reinterpret_cast(block + indexedPosition), &tag); + return BuiltinsBase::GetTaggedInt(result); + } + JSHandle newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH); + int16_t newTag = JSTaggedValue::ToInt16(thread, newValue); + int16_t arg[ARGS_NUMBER] = {0}; + arg[0] = tag; + arg[1] = newTag; + auto result = op(reinterpret_cast(block + indexedPosition), arg); + return JSTaggedValue(result); +} + +template +JSTaggedValue BuiltinsAtomics::HandleWithUint32(JSThread *thread, uint32_t size, uint8_t *block, + int32_t indexedPosition, + EcmaRuntimeCallInfo *argv, const callbackfun &op) +{ + JSHandle value = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); + uint32_t tag = JSTaggedValue::ToUint32(thread, value); + if (size == 3) { // the number of parameters is 3 + auto result = op(reinterpret_cast(block + indexedPosition), &tag); + return JSTaggedValue(result); + } + JSHandle newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH); + uint32_t newTag = JSTaggedValue::ToUint32(thread, newValue); + uint32_t arg[ARGS_NUMBER] = {0}; + arg[0] = tag; + arg[1] = newTag; + auto result = op(reinterpret_cast(block + indexedPosition), arg); + return JSTaggedValue(result); +} + +template +JSTaggedValue BuiltinsAtomics::HandleWithInt32(JSThread *thread, uint32_t size, uint8_t *block, + int32_t indexedPosition, + EcmaRuntimeCallInfo *argv, const callbackfun &op) +{ + JSHandle value = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); + int32_t tag = JSTaggedValue::ToInt32(thread, value); + if (size == 3) { // the number of parameters is 3 + auto result = op(reinterpret_cast(block + indexedPosition), &tag); + return BuiltinsBase::GetTaggedInt(result); + } + JSHandle newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH); + int32_t newTag = JSTaggedValue::ToInt32(thread, newValue); + int32_t arg[ARGS_NUMBER] = {0}; + arg[0] = tag; + arg[1] = newTag; + auto result = op(reinterpret_cast(block + indexedPosition), arg); + return JSTaggedValue(result); +} + +template +JSTaggedValue BuiltinsAtomics::HandleWithBigInt64(JSThread *thread, uint32_t size, uint8_t *block, + int32_t indexedPosition, + EcmaRuntimeCallInfo *argv, const callbackfun &op) +{ + JSHandle value = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); + int64_t val = 0; + bool lossless = true; + BigInt::BigIntToInt64(thread, value, &val, &lossless); + if (size == 3) { // the number of parameters is 3 + auto result = op(reinterpret_cast(block + indexedPosition), &val); + return BigInt::Int64ToBigInt(thread, result).GetTaggedValue(); + } + JSHandle newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH); + int64_t newVal = 0; + BigInt::BigIntToInt64(thread, newValue, &newVal, &lossless); + int64_t arg[ARGS_NUMBER] = {0}; + arg[0] = val; + arg[1] = newVal; + auto result = op(reinterpret_cast(block + indexedPosition), arg); + return BigInt::Int64ToBigInt(thread, result).GetTaggedValue(); +} + +template +JSTaggedValue BuiltinsAtomics::HandleWithBigUint64(JSThread *thread, uint32_t size, uint8_t *block, + int32_t indexedPosition, + EcmaRuntimeCallInfo *argv, const callbackfun &op) +{ + JSHandle value = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); + uint64_t val = 0; + bool lossless = true; + BigInt::BigIntToUint64(thread, value, &val, &lossless); + if (size == 3) { // the number of parameters is 3 + auto result = op(reinterpret_cast(block + indexedPosition), &val); + return BigInt::Uint64ToBigInt(thread, result).GetTaggedValue(); + } + JSHandle newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH); + uint64_t newVal = 0; + BigInt::BigIntToUint64(thread, newValue, &newVal, &lossless); + uint64_t arg[ARGS_NUMBER] = {0}; + arg[0] = val; + arg[1] = newVal; + auto result = op(reinterpret_cast(block + indexedPosition), arg); + return BigInt::Uint64ToBigInt(thread, result).GetTaggedValue(); +} + +template +WaitResult BuiltinsAtomics::DoWait(JSThread *thread, JSHandle &arrayBuffer, + size_t index, T execpt, double timeout) +{ + MutexGuard lock_guard(g_mutex); + JSHandle np(thread, JSHandle::Cast(arrayBuffer)->GetArrayBufferData()); + void *buffer = np->GetExternalPointer(); + 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; + 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(JSHandle &arrayBuffer, const size_t &index, double wakeCount) +{ + JSTaggedValue data = JSHandle::Cast(arrayBuffer)->GetArrayBufferData(); + void *buffer = JSNativePointer::Cast(data.GetTaggedObject())->GetExternalPointer(); + ASSERT(buffer != nullptr); + MutexGuard lock_guard(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 diff --git a/ecmascript/builtins/builtins_atomics.h b/ecmascript/builtins/builtins_atomics.h new file mode 100644 index 00000000..b10f6479 --- /dev/null +++ b/ecmascript/builtins/builtins_atomics.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2022 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. + */ + +#ifndef ECMASCRIPT_BUILTINS_BUILTINS_ATOMICS_H +#define ECMASCRIPT_BUILTINS_BUILTINS_ATOMICS_H + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/js_dataview.h" +#include "ecmascript/waiter_list.h" + +namespace panda::ecmascript::builtins { +enum class WaitResult: uint8_t {OK = 0, NOT_EQ, TIME_OUT}; + +class BuiltinsAtomics : public base::BuiltinsBase { +public: + // 25.4.2 Atomics.add ( typedArray, index, value ) + static JSTaggedValue Add(EcmaRuntimeCallInfo *argv); + // 25.4.3 Atomics.and ( typedArray, index, value ) + static JSTaggedValue And(EcmaRuntimeCallInfo *argv); + // 25.4.4 Atomics.compareExchange ( typedArray, index, expectedValue, replacementValue) + static JSTaggedValue CompareExchange(EcmaRuntimeCallInfo *argv); + // 25.4.5 Atomics.exchange ( typedArray, index, value ) + static JSTaggedValue Exchange(EcmaRuntimeCallInfo *argv); + // 25.4.6 Atomics.isLockFree ( size ) + static JSTaggedValue IsLockFree(EcmaRuntimeCallInfo *argv); + // 25.4.7 Atomics.load ( typedArray, index ) + static JSTaggedValue Load(EcmaRuntimeCallInfo *argv); + // 25.4.8 Atomics.or ( typedArray, index, value ) + static JSTaggedValue Or(EcmaRuntimeCallInfo *argv); + // 25.4.9 Atomics.store ( typedArray, index, value ) + static JSTaggedValue Store(EcmaRuntimeCallInfo *argv); + // 25.4.10 Atomics.sub ( typedArray, index, value ) + static JSTaggedValue Sub(EcmaRuntimeCallInfo *argv); + // 25.4.11 Atomics.wait ( typedArray, index, value, timeout) + static JSTaggedValue Wait(EcmaRuntimeCallInfo *argv); + // 25.4.12 Atomics.notify ( typedArray, index, count) + static JSTaggedValue Notify(EcmaRuntimeCallInfo *argv); + // 25.4.13 Atomics.xor ( typedArray, index, value ) + static JSTaggedValue Xor(EcmaRuntimeCallInfo *argv); + +private: + static uint32_t Signal(JSHandle &arrayBuffer, const size_t &index, double wakeCount); + template + static WaitResult DoWait(JSThread *thread, JSHandle &arrayBuffer, + size_t index, T execpt, double timeout); + template + static JSTaggedValue AtomicReadModifyWrite(JSThread *thread, const JSHandle &typedArray, + JSHandle &index, EcmaRuntimeCallInfo *argv, + const callbackfun &op); + template + static JSTaggedValue AtomicReadModifyWriteCase(JSThread *thread, JSTaggedValue buffer, DataViewType type, + int32_t indexedPosition, EcmaRuntimeCallInfo *argv, + const callbackfun &op); + template + static JSTaggedValue HandleWithUint8(JSThread *thread, uint32_t size, uint8_t *block, int32_t indexedPosition, + EcmaRuntimeCallInfo *argv, const callbackfun &op); + template + static JSTaggedValue HandleWithInt8(JSThread *thread, uint32_t size, uint8_t *block, int32_t indexedPosition, + EcmaRuntimeCallInfo *argv, const callbackfun &op); + template + static JSTaggedValue HandleWithUint16(JSThread *thread, uint32_t size, uint8_t *block, int32_t indexedPosition, + EcmaRuntimeCallInfo *argv, const callbackfun &op); + template + static JSTaggedValue HandleWithInt16(JSThread *thread, uint32_t size, uint8_t *block, int32_t indexedPosition, + EcmaRuntimeCallInfo *argv, const callbackfun &op); + template + static JSTaggedValue HandleWithUint32(JSThread *thread, uint32_t size, uint8_t *block, int32_t indexedPosition, + EcmaRuntimeCallInfo *argv, const callbackfun &op); + template + static JSTaggedValue HandleWithInt32(JSThread *thread, uint32_t size, uint8_t *block, int32_t indexedPosition, + EcmaRuntimeCallInfo *argv, const callbackfun &op); + template + static JSTaggedValue HandleWithBigInt64(JSThread *thread, uint32_t size, uint8_t *block, int32_t indexedPosition, + EcmaRuntimeCallInfo *argv, const callbackfun &op); + template + static JSTaggedValue HandleWithBigUint64(JSThread *thread, uint32_t size, uint8_t *block, int32_t indexedPosition, + EcmaRuntimeCallInfo *argv, const callbackfun &op); + + static constexpr int ARGS_NUMBER = 2; +}; +} // namespace panda::ecmascript::builtins +#endif // ECMASCRIPT_BUILTINS_BUILTINS_MATH_H \ No newline at end of file diff --git a/ecmascript/builtins/builtins_typedarray.cpp b/ecmascript/builtins/builtins_typedarray.cpp index 8fa4021b..3674011d 100644 --- a/ecmascript/builtins/builtins_typedarray.cpp +++ b/ecmascript/builtins/builtins_typedarray.cpp @@ -992,7 +992,7 @@ JSTaggedValue BuiltinsTypedArray::Set(EcmaRuntimeCallInfo *argv) uint32_t targetLength = targetObj->GetArrayLength(); JSHandle targetName(thread, targetObj->GetTypedArrayName()); uint32_t targetElementSize = TypedArrayHelper::GetSizeFromName(thread, targetName); - DataViewType targetType = TypedArrayHelper::GetTypeFromName(thread, targetName); + DataViewType targetType = JSTypedArray::GetTypeFromName(thread, targetName); uint32_t targetByteOffset = targetObj->GetByteOffset(); JSHandle argArray = GetCallArg(argv, 0); @@ -1084,7 +1084,7 @@ JSTaggedValue BuiltinsTypedArray::Set(EcmaRuntimeCallInfo *argv) // 21. Let srcLength be the value of typedArray’s [[ArrayLength]] internal slot. // 22. Let srcByteOffset be the value of typedArray’s [[ByteOffset]] internal slot. JSHandle srcName(thread, typedArray->GetTypedArrayName()); - DataViewType srcType = TypedArrayHelper::GetTypeFromName(thread, srcName); + DataViewType srcType = JSTypedArray::GetTypeFromName(thread, srcName); uint32_t srcElementSize = TypedArrayHelper::GetSizeFromName(thread, srcName); uint32_t srcLength = typedArray->GetArrayLength(); uint32_t srcByteOffset = typedArray->GetByteOffset(); @@ -1214,11 +1214,11 @@ JSTaggedValue BuiltinsTypedArray::Slice(EcmaRuntimeCallInfo *argv) // 17. Let srcName be the String value of O’s [[TypedArrayName]] internal slot. // 18. Let srcType be the String value of the Element Type value in Table 49 for srcName. JSHandle srcName(thread, thisObj->GetTypedArrayName()); - DataViewType srcType = TypedArrayHelper::GetTypeFromName(thread, srcName); + DataViewType srcType = JSTypedArray::GetTypeFromName(thread, srcName); // 19. Let targetName be the String value of A’s [[TypedArrayName]] internal slot. // 20. Let targetType be the String value of the Element Type value in Table 49 for targetName. JSHandle targetName(thread, JSTypedArray::Cast(*newArrObj)->GetTypedArrayName()); - DataViewType targetType = TypedArrayHelper::GetTypeFromName(thread, targetName); + DataViewType targetType = JSTypedArray::GetTypeFromName(thread, targetName); // 21. If SameValue(srcType, targetType) is false, then // a. Let n be 0. // b. Repeat, while k < final diff --git a/ecmascript/builtins/tests/BUILD.gn b/ecmascript/builtins/tests/BUILD.gn index 68d45637..da76a36f 100755 --- a/ecmascript/builtins/tests/BUILD.gn +++ b/ecmascript/builtins/tests/BUILD.gn @@ -52,6 +52,7 @@ host_unittest_action("BuiltinsNaturalTest") { # test file "builtins_array_test.cpp", "builtins_arraybuffer_test.cpp", + "builtins_atomics_test.cpp", "builtins_boolean_test.cpp", "builtins_dataview_test.cpp", "builtins_date_test.cpp", diff --git a/ecmascript/builtins/tests/builtins_atomics_test.cpp b/ecmascript/builtins/tests/builtins_atomics_test.cpp new file mode 100644 index 00000000..b4f7a867 --- /dev/null +++ b/ecmascript/builtins/tests/builtins_atomics_test.cpp @@ -0,0 +1,626 @@ +/* + * Copyright (c) 2022 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_array.h" +#include "ecmascript/builtins/builtins_atomics.h" +#include "ecmascript/builtins/builtins_typedarray.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/js_typed_array.h" +#include "ecmascript/tests/test_helper.h" + +using namespace panda::ecmascript; +using namespace panda::ecmascript::builtins; + +namespace panda::test { +using TypedArray = ecmascript::builtins::BuiltinsTypedArray; +class BuiltinsAtomicsTest : public testing::Test { +public: + static void SetUpTestCase() + { + GTEST_LOG_(INFO) << "SetUpTestCase"; + } + + static void TearDownTestCase() + { + GTEST_LOG_(INFO) << "TearDownCase"; + } + + void SetUp() override + { + TestHelper::CreateEcmaVMWithScope(instance, thread, scope); + } + + void TearDown() override + { + TestHelper::DestroyEcmaVMWithScope(instance, scope); + } + + EcmaVM *instance {nullptr}; + EcmaHandleScope *scope {nullptr}; + JSThread *thread {nullptr}; +}; + +JSTypedArray *CreateTypedArray(JSThread *thread, const JSHandle &array) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + + JSHandle jsarray(JSArray::CreateArrayFromList(thread, array)); + JSHandle int8_array(env->GetInt8ArrayFunction()); + JSHandle globalObject(thread, env->GetGlobalObject()); + // 6 : test case + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*int8_array), 6); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue(*int8_array)); + ecmaRuntimeCallInfo1->SetThis(JSTaggedValue(*globalObject)); + ecmaRuntimeCallInfo1->SetCallArg(0, jsarray.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1.get()); + JSTaggedValue result = TypedArray::Int8ArrayConstructor(ecmaRuntimeCallInfo1.get()); + + EXPECT_TRUE(result.IsECMAObject()); + JSTypedArray *int8arr = JSTypedArray::Cast(reinterpret_cast(result.GetRawData())); + return int8arr; +} + +HWTEST_F_L0(BuiltinsAtomicsTest, Add_1) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + [[maybe_unused]] JSHandle array(factory->NewTaggedArray(3)); + array->Set(thread, 0, JSTaggedValue(7)); + array->Set(thread, 1, JSTaggedValue(8)); + array->Set(thread, 2, JSTaggedValue(9)); + + JSHandle obj = JSHandle(thread, CreateTypedArray(thread, array)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(0))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(5))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsAtomics::Add(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetInt(), 7); +} + +HWTEST_F_L0(BuiltinsAtomicsTest, Add_2) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + [[maybe_unused]] JSHandle array(factory->NewTaggedArray(10)); + + JSHandle obj = JSHandle(thread, CreateTypedArray(thread, array)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(0))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsAtomics::Add(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetInt(), 0); +} + +HWTEST_F_L0(BuiltinsAtomicsTest, Add_3) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + [[maybe_unused]] JSHandle array(factory->NewTaggedArray(3)); + + JSHandle obj = JSHandle(thread, CreateTypedArray(thread, array)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(0))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsAtomics::Add(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetInt(), 0); + + + auto ecmaRuntimeCallInfos = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfos->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfos->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfos->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfos->SetCallArg(1, JSTaggedValue(static_cast(0))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfos.get()); + JSTaggedValue results = BuiltinsAtomics::Load(ecmaRuntimeCallInfos.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(results.GetInt(), 2); +} + +HWTEST_F_L0(BuiltinsAtomicsTest, And_1) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + [[maybe_unused]] JSHandle array(factory->NewTaggedArray(10)); + array->Set(thread, 0, JSTaggedValue(7)); + array->Set(thread, 1, JSTaggedValue(0)); + array->Set(thread, 2, JSTaggedValue(0)); + + JSHandle obj = JSHandle(thread, CreateTypedArray(thread, array)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(0))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsAtomics::Add(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetInt(), 7); +} + +HWTEST_F_L0(BuiltinsAtomicsTest, And_2) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + [[maybe_unused]] JSHandle array(factory->NewTaggedArray(10)); + array->Set(thread, 0, JSTaggedValue(7)); + array->Set(thread, 1, JSTaggedValue(0)); + array->Set(thread, 2, JSTaggedValue(0)); + + JSHandle obj = JSHandle(thread, CreateTypedArray(thread, array)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(0))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsAtomics::And(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetInt(), 7); + + + auto ecmaRuntimeCallInfos = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfos->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfos->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfos->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfos->SetCallArg(1, JSTaggedValue(static_cast(0))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfos.get()); + result = BuiltinsAtomics::Load(ecmaRuntimeCallInfos.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetInt(), 2); +} + +HWTEST_F_L0(BuiltinsAtomicsTest, CompareExchange_1) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + [[maybe_unused]] JSHandle array(factory->NewTaggedArray(3)); + array->Set(thread, 0, JSTaggedValue(5)); + array->Set(thread, 1, JSTaggedValue(0)); + array->Set(thread, 2, JSTaggedValue(0)); + + JSHandle obj = JSHandle(thread, CreateTypedArray(thread, array)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 12); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(0))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(5))); + ecmaRuntimeCallInfo->SetCallArg(3, JSTaggedValue(static_cast(2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsAtomics::CompareExchange(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetInt(), 5); +} + +HWTEST_F_L0(BuiltinsAtomicsTest, CompareExchange_2) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + [[maybe_unused]] JSHandle array(factory->NewTaggedArray(3)); + array->Set(thread, 0, JSTaggedValue(5)); + array->Set(thread, 1, JSTaggedValue(0)); + array->Set(thread, 2, JSTaggedValue(0)); + + JSHandle obj = JSHandle(thread, CreateTypedArray(thread, array)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 12); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(0))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(5))); + ecmaRuntimeCallInfo->SetCallArg(3, JSTaggedValue(static_cast(2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsAtomics::CompareExchange(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetInt(), 5); + + + auto ecmaRuntimeCallInfos = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfos->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfos->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfos->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfos->SetCallArg(1, JSTaggedValue(static_cast(0))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfos.get()); + result = BuiltinsAtomics::Load(ecmaRuntimeCallInfos.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetInt(), 2); +} + +HWTEST_F_L0(BuiltinsAtomicsTest, Exchange_1) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + [[maybe_unused]] JSHandle array(factory->NewTaggedArray(3)); + array->Set(thread, 0, JSTaggedValue(3)); + array->Set(thread, 1, JSTaggedValue(0)); + array->Set(thread, 2, JSTaggedValue(0)); + + JSHandle obj = JSHandle(thread, CreateTypedArray(thread, array)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(0))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(6))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsAtomics::Exchange(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetInt(), 3); +} + +HWTEST_F_L0(BuiltinsAtomicsTest, Exchange_2) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + [[maybe_unused]] JSHandle array(factory->NewTaggedArray(3)); + array->Set(thread, 0, JSTaggedValue(3)); + array->Set(thread, 1, JSTaggedValue(0)); + array->Set(thread, 2, JSTaggedValue(0)); + + JSHandle obj = JSHandle(thread, CreateTypedArray(thread, array)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(0))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(6))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsAtomics::Exchange(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetInt(), 3); + + + auto ecmaRuntimeCallInfos = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfos->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfos->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfos->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfos->SetCallArg(1, JSTaggedValue(static_cast(0))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfos.get()); + result = BuiltinsAtomics::Load(ecmaRuntimeCallInfos.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetInt(), 6); +} + +HWTEST_F_L0(BuiltinsAtomicsTest, Or_1) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + [[maybe_unused]] JSHandle array(factory->NewTaggedArray(3)); + array->Set(thread, 0, JSTaggedValue(5)); + array->Set(thread, 1, JSTaggedValue(0)); + array->Set(thread, 2, JSTaggedValue(0)); + + JSHandle obj = JSHandle(thread, CreateTypedArray(thread, array)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(0))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsAtomics::Or(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetInt(), 5); +} + +HWTEST_F_L0(BuiltinsAtomicsTest, Or_2) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + [[maybe_unused]] JSHandle array(factory->NewTaggedArray(3)); + array->Set(thread, 0, JSTaggedValue(5)); + array->Set(thread, 1, JSTaggedValue(0)); + array->Set(thread, 2, JSTaggedValue(0)); + + JSHandle obj = JSHandle(thread, CreateTypedArray(thread, array)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 12); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(0))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsAtomics::Or(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetInt(), 5); + + + auto ecmaRuntimeCallInfos = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfos->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfos->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfos->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfos->SetCallArg(1, JSTaggedValue(static_cast(0))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfos.get()); + result = BuiltinsAtomics::Load(ecmaRuntimeCallInfos.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetInt(), 7); +} + +HWTEST_F_L0(BuiltinsAtomicsTest, Sub_1) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + [[maybe_unused]] JSHandle array(factory->NewTaggedArray(3)); + array->Set(thread, 0, JSTaggedValue(5)); + array->Set(thread, 1, JSTaggedValue(0)); + array->Set(thread, 2, JSTaggedValue(0)); + + JSHandle obj = JSHandle(thread, CreateTypedArray(thread, array)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(0))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsAtomics::Sub(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetInt(), 5); +} + +HWTEST_F_L0(BuiltinsAtomicsTest, Sub_2) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + [[maybe_unused]] JSHandle array(factory->NewTaggedArray(3)); + array->Set(thread, 0, JSTaggedValue(0)); + array->Set(thread, 1, JSTaggedValue(5)); + array->Set(thread, 2, JSTaggedValue(0)); + + JSHandle obj = JSHandle(thread, CreateTypedArray(thread, array)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(1))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsAtomics::Sub(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetInt(), 5); + + auto ecmaRuntimeCallInfos = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfos->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfos->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfos->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfos->SetCallArg(1, JSTaggedValue(static_cast(1))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfos.get()); + result = BuiltinsAtomics::Load(ecmaRuntimeCallInfos.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetInt(), 3); +} + +HWTEST_F_L0(BuiltinsAtomicsTest, Xor_1) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + [[maybe_unused]] JSHandle array(factory->NewTaggedArray(3)); + array->Set(thread, 0, JSTaggedValue(5)); + array->Set(thread, 1, JSTaggedValue(7)); + array->Set(thread, 2, JSTaggedValue(0)); + + JSHandle obj = JSHandle(thread, CreateTypedArray(thread, array)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(1))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsAtomics::Xor(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetInt(), 7); +} + +HWTEST_F_L0(BuiltinsAtomicsTest, Xor_2) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + [[maybe_unused]] JSHandle array(factory->NewTaggedArray(3)); + array->Set(thread, 0, JSTaggedValue(5)); + array->Set(thread, 1, JSTaggedValue(7)); + array->Set(thread, 2, JSTaggedValue(0)); + + JSHandle obj = JSHandle(thread, CreateTypedArray(thread, array)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(1))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsAtomics::Xor(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetInt(), 7); + + auto ecmaRuntimeCallInfos = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfos->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfos->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfos->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfos->SetCallArg(1, JSTaggedValue(static_cast(1))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfos.get()); + result = BuiltinsAtomics::Load(ecmaRuntimeCallInfos.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetInt(), 5); +} + +HWTEST_F_L0(BuiltinsAtomicsTest, IsLockFree_1) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(1))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsAtomics::IsLockFree(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_TRUE(result.ToBoolean()); +} + +HWTEST_F_L0(BuiltinsAtomicsTest, IsLockFree_2) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsAtomics::IsLockFree(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_TRUE(result.ToBoolean()); +} + +HWTEST_F_L0(BuiltinsAtomicsTest, IsLockFree_3) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(4))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsAtomics::IsLockFree(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_TRUE(result.ToBoolean()); +} + +HWTEST_F_L0(BuiltinsAtomicsTest, IsLockFree_4) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(-3))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsAtomics::IsLockFree(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_FALSE(result.ToBoolean()); +} + +HWTEST_F_L0(BuiltinsAtomicsTest, IsLockFree_5) +{ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast(8))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsAtomics::IsLockFree(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_TRUE(result.ToBoolean()); +} + + +HWTEST_F_L0(BuiltinsAtomicsTest, Store_1) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + [[maybe_unused]] JSHandle array(factory->NewTaggedArray(3)); + array->Set(thread, 0, JSTaggedValue(5)); + array->Set(thread, 1, JSTaggedValue(6)); + array->Set(thread, 2, JSTaggedValue(7)); + + JSHandle obj = JSHandle(thread, CreateTypedArray(thread, array)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(0))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsAtomics::Store(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetDouble(), 2); +} + +HWTEST_F_L0(BuiltinsAtomicsTest, Store_2) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + [[maybe_unused]] JSHandle array(factory->NewTaggedArray(3)); + array->Set(thread, 0, JSTaggedValue(5)); + array->Set(thread, 1, JSTaggedValue(6)); + array->Set(thread, 2, JSTaggedValue(7)); + + JSHandle obj = JSHandle(thread, CreateTypedArray(thread, array)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(0))); + ecmaRuntimeCallInfo->SetCallArg(2, JSTaggedValue(static_cast(2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get()); + JSTaggedValue result = BuiltinsAtomics::Store(ecmaRuntimeCallInfo.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetDouble(), 2); + + auto ecmaRuntimeCallInfos = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfos->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfos->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfos->SetCallArg(0, obj.GetTaggedValue()); + ecmaRuntimeCallInfos->SetCallArg(1, JSTaggedValue(static_cast(0))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfos.get()); + result = BuiltinsAtomics::Load(ecmaRuntimeCallInfos.get()); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetInt(), 2); +} +} \ No newline at end of file diff --git a/ecmascript/dump.cpp b/ecmascript/dump.cpp index 026e5f02..757292dd 100644 --- a/ecmascript/dump.cpp +++ b/ecmascript/dump.cpp @@ -1711,6 +1711,8 @@ void GlobalEnv::Dump(std::ostream &os) const GetBuiltinsWeakMapFunction().GetTaggedValue().Dump(os); os << " - MathFunction: "; GetMathFunction().GetTaggedValue().Dump(os); + os << " - AtomicsFunction: "; + GetAtomicsFunction().GetTaggedValue().Dump(os); os << " - JsonFunction: "; GetJsonFunction().GetTaggedValue().Dump(os); os << " - StringFunction: "; @@ -3589,6 +3591,7 @@ void GlobalEnv::DumpForSnapshot(std::vector> & vec.push_back(std::make_pair(CString("BuiltinsWeakSetFunction"), GetBuiltinsWeakSetFunction().GetTaggedValue())); vec.push_back(std::make_pair(CString("BuiltinsWeakMapFunction"), GetBuiltinsWeakMapFunction().GetTaggedValue())); vec.push_back(std::make_pair(CString("MathFunction"), GetMathFunction().GetTaggedValue())); + vec.push_back(std::make_pair(CString("AtomicsFunction"), GetAtomicsFunction().GetTaggedValue())); vec.push_back(std::make_pair(CString("JsonFunction"), GetJsonFunction().GetTaggedValue())); vec.push_back(std::make_pair(CString("StringFunction"), GetStringFunction().GetTaggedValue())); vec.push_back(std::make_pair(CString("ProxyFunction"), GetProxyFunction().GetTaggedValue())); diff --git a/ecmascript/ecma_vm.h b/ecmascript/ecma_vm.h index 4f9821ec..5ff6d3d6 100644 --- a/ecmascript/ecma_vm.h +++ b/ecmascript/ecma_vm.h @@ -24,6 +24,7 @@ #include "ecmascript/mem/chunk_containers.h" #include "ecmascript/taskpool/taskpool.h" #include "ecmascript/js_thread.h" +#include "ecmascript/waiter_list.h" namespace panda { class JSNApi; @@ -273,6 +274,21 @@ public: hostPromiseRejectionTracker_ = cb; } + void SetAllowAtomicWait(bool wait) + { + AllowAtomicWait_ = wait; + } + + bool GetAllowAtomicWait() const + { + return AllowAtomicWait_; + } + + WaiterListNode *GetWaiterListNode() + { + return &waiterListNode_; + } + void PromiseRejectionTracker(const JSHandle &promise, const JSHandle &reason, PromiseRejectionEvent operation) { @@ -374,6 +390,9 @@ private: void* data_ {nullptr}; bool isProcessingPendingJob_ = false; + // atomics + bool AllowAtomicWait_ {true}; + WaiterListNode waiterListNode_; friend class Snapshot; friend class SnapshotProcessor; diff --git a/ecmascript/global_env.h b/ecmascript/global_env.h index ea92188c..43705f20 100644 --- a/ecmascript/global_env.h +++ b/ecmascript/global_env.h @@ -81,6 +81,7 @@ class JSThread; V(JSTaggedValue, BuiltinsWeakSetFunction, BUILTINS_WEAK_SET_FUNCTION_INDEX) \ V(JSTaggedValue, MapPrototype, MAP_PROTOTYPE_INDEX) \ V(JSTaggedValue, MathFunction, MATH_FUNCTION_INDEX) \ + V(JSTaggedValue, AtomicsFunction, ATOMICS_FUNCTION_INDEX) \ V(JSTaggedValue, JsonFunction, JSON_FUNCTION_INDEX) \ V(JSTaggedValue, StringFunction, STRING_FUNCTION_INDEX) \ V(JSTaggedValue, ProxyFunction, PROXY_FUNCTION_INDEX) \ diff --git a/ecmascript/global_env_constants.cpp b/ecmascript/global_env_constants.cpp index 4c6b087a..60aa5892 100644 --- a/ecmascript/global_env_constants.cpp +++ b/ecmascript/global_env_constants.cpp @@ -474,6 +474,9 @@ void GlobalEnvConstants::InitGlobalConstant(JSThread *thread) SetConstant(ConstantIndex::ELEMENT_INDEX, factory->NewFromASCII("element")); SetConstant(ConstantIndex::FLAGS_INDEX, factory->NewFromASCII("flags")); SetConstant(ConstantIndex::G_INDEX, factory->NewFromASCII("g")); + SetConstant(ConstantIndex::NOT_EQUAL_INDEX, factory->NewFromASCII("not-equal")); + SetConstant(ConstantIndex::OK_INDEX, factory->NewFromASCII("ok")); + SetConstant(ConstantIndex::TIMEOUT_INDEX, factory->NewFromASCII("timed-out")); auto accessor = factory->NewInternalAccessor(reinterpret_cast(JSFunction::PrototypeSetter), reinterpret_cast(JSFunction::PrototypeGetter)); diff --git a/ecmascript/global_env_constants.h b/ecmascript/global_env_constants.h index 862fdf06..47606193 100644 --- a/ecmascript/global_env_constants.h +++ b/ecmascript/global_env_constants.h @@ -342,7 +342,10 @@ class JSThread; V(JSTaggedValue, DisjunctionString, DISJUNCTION_INDEX, disjunction) \ V(JSTaggedValue, ElementString, ELEMENT_INDEX, element) \ V(JSTaggedValue, FlagsString, FLAGS_INDEX, flags) \ - V(JSTaggedValue, GString, G_INDEX, g) + V(JSTaggedValue, GString, G_INDEX, g) \ + V(JSTaggedValue, NotEqualString, NOT_EQUAL_INDEX, notEqual) \ + V(JSTaggedValue, OkString, OK_INDEX, ok) \ + V(JSTaggedValue, TimeoutString, TIMEOUT_INDEX, timedout) // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define GLOBAL_ENV_CONSTANT_ACCESSOR(V) \ diff --git a/ecmascript/js_bigint.cpp b/ecmascript/js_bigint.cpp index a4d36bb6..6399688a 100644 --- a/ecmascript/js_bigint.cpp +++ b/ecmascript/js_bigint.cpp @@ -954,7 +954,7 @@ std::string BigIntHelper::MultiplyImpl(std::string &a, std::string &b) str[i + j + 1] = static_cast(temp2 % 10 + 48); // 2 and 10 and 48 is number addflag = temp2 / 10; } - str[i] += static_cast(mulflag + addflag); + str[i] += static_cast(mulflag + addflag); } if (str[0] == '0') { str = str.substr(1, str.size()); @@ -1429,4 +1429,19 @@ ComparisonResult BigInt::CompareWithNumber(JSHandle bigint, JSHandle(&value); + for (int index = len - 1; index >= 0; --index) { + *(addr + index) = GetDigit(index); + } + if (GetSign()) { + value = ~(value - 1); + } + return static_cast(value); +} } // namespace diff --git a/ecmascript/js_bigint.h b/ecmascript/js_bigint.h index d40a3013..edcf8b26 100644 --- a/ecmascript/js_bigint.h +++ b/ecmascript/js_bigint.h @@ -89,6 +89,8 @@ public: static JSTaggedValue AsintN(JSThread *thread, JSTaggedNumber &bits, JSHandle bigint); static JSTaggedNumber BigIntToNumber(JSHandle bigint); static ComparisonResult CompareWithNumber(JSHandle bigint, JSHandle number); + + int64_t ToInt64(); inline bool IsZero() { return GetLength() == 1 && !GetDigit(0); diff --git a/ecmascript/js_typed_array.cpp b/ecmascript/js_typed_array.cpp index c73d6ce4..714660cd 100644 --- a/ecmascript/js_typed_array.cpp +++ b/ecmascript/js_typed_array.cpp @@ -406,6 +406,42 @@ bool JSTypedArray::IsValidIntegerIndex(const JSHandle &typedArray return true; } +DataViewType JSTypedArray::GetTypeFromName(JSThread *thread, const JSHandle &typeName) +{ + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledInt8ArrayString())) { + return DataViewType::INT8; + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint8ArrayString())) { + return DataViewType::UINT8; + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint8ClampedArrayString())) { + return DataViewType::UINT8_CLAMPED; + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledInt16ArrayString())) { + return DataViewType::INT16; + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint16ArrayString())) { + return DataViewType::UINT16; + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledInt32ArrayString())) { + return DataViewType::INT32; + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint32ArrayString())) { + return DataViewType::UINT32; + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledFloat32ArrayString())) { + return DataViewType::FLOAT32; + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledFloat64ArrayString())) { + return DataViewType::FLOAT64; + } + if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledBigInt64ArrayString())) { + return DataViewType::BIGINT64; + } + return DataViewType::BIGUINT64; +} + // static bool JSTypedArray::FastCopyElementToArray(JSThread *thread, const JSHandle &typedArray, JSHandle &array) diff --git a/ecmascript/js_typed_array.h b/ecmascript/js_typed_array.h index e4c63198..b422d096 100644 --- a/ecmascript/js_typed_array.h +++ b/ecmascript/js_typed_array.h @@ -17,6 +17,7 @@ #define ECMASCRIPT_JS_TYPED_ARRAY_H #include "ecmascript/tagged_array.h" +#include "ecmascript/js_dataview.h" #include "js_object.h" namespace panda::ecmascript { @@ -94,6 +95,7 @@ public: // only use in TypeArray fast set property static JSTaggedNumber NonEcmaObjectToNumber(JSThread *thread, const JSTaggedValue tagged); static constexpr size_t VIEWED_ARRAY_BUFFER_OFFSET = JSObject::SIZE; + static DataViewType GetTypeFromName(JSThread *thread, const JSHandle &typeName); ACCESSORS(ViewedArrayBuffer, VIEWED_ARRAY_BUFFER_OFFSET, TYPED_ARRAY_NAME_OFFSET) ACCESSORS(TypedArrayName, TYPED_ARRAY_NAME_OFFSET, BYTE_LENGTH_OFFSET) ACCESSORS_PRIMITIVE_FIELD(ByteLength, uint32_t, BYTE_LENGTH_OFFSET, BYTE_OFFSET_OFFSET) diff --git a/ecmascript/mem/concurrent_marker.cpp b/ecmascript/mem/concurrent_marker.cpp index dec51b3f..6311d397 100644 --- a/ecmascript/mem/concurrent_marker.cpp +++ b/ecmascript/mem/concurrent_marker.cpp @@ -104,6 +104,7 @@ void ConcurrentMarker::Reset(bool revertCSet) auto callback = [](Region *region) { region->ClearMarkGCBitset(); region->ClearCrossRegionRSet(); + region->ResetAliveObject(); }; if (heap_->IsFullMark()) { heap_->EnumerateRegions(callback); diff --git a/ecmascript/runtime_call_id.h b/ecmascript/runtime_call_id.h index 2c5ff96e..5cc29a54 100644 --- a/ecmascript/runtime_call_id.h +++ b/ecmascript/runtime_call_id.h @@ -371,6 +371,18 @@ namespace panda::ecmascript { V(Math, Tan) \ V(Math, Tanh) \ V(Math, Trunc) \ + V(Atomics, Add) \ + V(Atomics, And) \ + V(Atomics, Or) \ + V(Atomics, Xor) \ + V(Atomics, Sub) \ + V(Atomics, Exchange) \ + V(Atomics, CompareEchange) \ + V(Atomics, Store) \ + V(Atomics, Load) \ + V(Atomics, IsLockFree) \ + V(Atomics, Wait) \ + V(Atomics, Notify) \ V(Number, Constructor) \ V(Number, IsFinite) \ V(Number, IsInteger) \ diff --git a/ecmascript/snapshot/mem/snapshot_processor.cpp b/ecmascript/snapshot/mem/snapshot_processor.cpp index 72e7d0ee..96d4d920 100644 --- a/ecmascript/snapshot/mem/snapshot_processor.cpp +++ b/ecmascript/snapshot/mem/snapshot_processor.cpp @@ -19,6 +19,7 @@ #include "ecmascript/builtins/builtins_array.h" #include "ecmascript/builtins/builtins_arraybuffer.h" #include "ecmascript/builtins/builtins_async_function.h" +#include "ecmascript/builtins/builtins_atomics.h" #include "ecmascript/builtins/builtins_bigint.h" #include "ecmascript/builtins/builtins_boolean.h" #include "ecmascript/builtins/builtins_collator.h" @@ -116,6 +117,7 @@ using StringIterator = builtins::BuiltinsStringIterator; using RegExp = builtins::BuiltinsRegExp; using Function = builtins::BuiltinsFunction; using Math = builtins::BuiltinsMath; +using Atomics = builtins::BuiltinsAtomics; using ArrayBuffer = builtins::BuiltinsArrayBuffer; using Json = builtins::BuiltinsJson; using Proxy = builtins::BuiltinsProxy; @@ -504,6 +506,17 @@ static uintptr_t g_nativeTable[] = { reinterpret_cast(Math::Tan), reinterpret_cast(Math::Tanh), reinterpret_cast(Math::Trunc), + reinterpret_cast(Atomics::Wait), + reinterpret_cast(Atomics::Exchange), + reinterpret_cast(Atomics::CompareExchange), + reinterpret_cast(Atomics::Store), + reinterpret_cast(Atomics::Load), + reinterpret_cast(Atomics::Notify), + reinterpret_cast(Atomics::Xor), + reinterpret_cast(Atomics::Or), + reinterpret_cast(Atomics::Sub), + reinterpret_cast(Atomics::And), + reinterpret_cast(Atomics::Add), reinterpret_cast(Json::Parse), reinterpret_cast(Json::Stringify), reinterpret_cast(BuiltinsIterator::Next), diff --git a/ecmascript/waiter_list.cpp b/ecmascript/waiter_list.cpp new file mode 100644 index 00000000..1dd110b0 --- /dev/null +++ b/ecmascript/waiter_list.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2022 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/waiter_list.h" +#include "ecmascript/base/number_helper.h" +#include "os/time.h" + +namespace panda::ecmascript { +// --------------------------WaiterList------------------------------ +void WaiterList::AddNode(WaiterListNode *node) +{ + ASSERT(node->prev_ == nullptr); + ASSERT(node->next_ == nullptr); + auto iter = locationListMap_.find(node->waitPointer_); + if (iter != locationListMap_.end()) { + iter->second.pTail->next_ = node; + node->prev_ = iter->second.pTail; + iter->second.pTail = node; + } else { + locationListMap_.insert(std::make_pair(node->waitPointer_, HeadAndTail {node, node})); + } +} + +void WaiterList::DeleteNode(WaiterListNode *node) +{ + auto iter = locationListMap_.find(node->waitPointer_); + ASSERT(iter != locationListMap_.end()); + WaiterListNode *temp = iter->second.pHead; + bool flag = false; + while (temp != nullptr) { + if (temp == node) { + flag = true; + break; + } + temp = temp->next_; + } + ASSERT(flag); + if (node == iter->second.pHead && node == iter->second.pTail) { + locationListMap_.erase(iter); + return; + } + + if (node == iter->second.pHead) { + iter->second.pHead = node->next_; + } else { + ASSERT(node->prev_); + node->prev_->next_ = node->next_; + } + + if (node == iter->second.pTail) { + iter->second.pTail = node->prev_; + } else { + ASSERT(node->next_); + node->next_->prev_ = node->prev_; + } + + node->prev_ = node->next_ = nullptr; +} +} // namespace \ No newline at end of file diff --git a/ecmascript/waiter_list.h b/ecmascript/waiter_list.h new file mode 100644 index 00000000..9b0abbf0 --- /dev/null +++ b/ecmascript/waiter_list.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2022 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. + */ + +#ifndef ECMASCRIPT_WAITER_LIST_H +#define ECMASCRIPT_WAITER_LIST_H + +#include "ecmascript/ecma_macros.h" +#include "ecmascript/mem/c_containers.h" +#include "os/mutex.h" + +namespace panda::ecmascript { +using Mutex = os::memory::Mutex; +using LockHolder = os::memory::LockHolder; + +class WaiterListNode { +public: + WaiterListNode() = default; + ~WaiterListNode() = default; + +public: + NO_COPY_SEMANTIC(WaiterListNode); + NO_MOVE_SEMANTIC(WaiterListNode); + + WaiterListNode *prev_ {nullptr}; + WaiterListNode *next_ {nullptr}; + // Used to call wait or Signal() to unlock wait and wake up + os::memory::ConditionVariable cond_; + + // Managed Arraybuffer or SharedArrayBuffer memory data + void *date_ {nullptr}; + + // the offset of the element in the typedArray + size_t index_ {0}; + + // the memory address data corresponding to the offset + int8_t *waitPointer_ {nullptr}; + + // used to determine whether to wait, start wait when waiting_ is true + bool waiting_ {false}; + +private: + friend class WaiterList; +}; + +// WaiterList to manage WaiterListNode +class WaiterList { +public: + WaiterList() = default; + void AddNode(WaiterListNode *node); + void DeleteNode(WaiterListNode *node); + struct HeadAndTail { + WaiterListNode *pHead {nullptr}; + WaiterListNode *pTail {nullptr}; + }; + + // locationListMap_ is used AddNode or DeleteNode + // When calling addnode If there is no corresponding memory data, add the node corresponding to the key + CMap locationListMap_; +}; + +// The Singleton pattern is used to creat a global metux and WaiterList +template +class Singleton { +public: + ~Singleton() {} + NO_COPY_SEMANTIC(Singleton); + NO_MOVE_SEMANTIC(Singleton); + static T *GetInstance() + { + static T instance; + return &instance; + } + +private: + Singleton() = default; +}; + +class SCOPED_CAPABILITY MutexGuard +{ +public: + explicit MutexGuard(Mutex *mutex) : mutex_(mutex), lockHolder_(*mutex) {} + void Unlock() RELEASE() + { + mutex_->Unlock(); + } + void Lock() ACQUIRE() + { + mutex_->Lock(); + } + +public: + Mutex *mutex_; + LockHolder lockHolder_; +}; +} // namespace +#endif // ECMASCRIPT_WAITER_LIST_H \ No newline at end of file