/* * 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/containers/containers_stack.h" #include "ecmascript/containers/containers_private.h" #include "ecmascript/ecma_runtime_call_info.h" #include "ecmascript/global_env.h" #include "ecmascript/js_api/js_api_stack.h" #include "ecmascript/js_api/js_api_stack_iterator.h" #include "ecmascript/js_handle.h" #include "ecmascript/js_tagged_value-inl.h" #include "ecmascript/js_thread.h" #include "ecmascript/object_factory.h" #include "ecmascript/tests/test_helper.h" #include "ecmascript/containers/tests/containers_test_helper.h" using namespace panda::ecmascript; using namespace panda::ecmascript::containers; namespace panda::test { class ContainersStackTest : 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}; class TestClass : public base::BuiltinsBase { public: static JSTaggedValue TestForEachFunc(EcmaRuntimeCallInfo *argv) { JSHandle value = GetCallArg(argv, 0); JSHandle key = GetCallArg(argv, 1); JSHandle stack = GetCallArg(argv, 2); // 2 means the secode arg if (!stack->IsUndefined()) { if (value->IsNumber()) { TaggedArray *elements = TaggedArray::Cast(JSAPIStack::Cast(stack.GetTaggedValue(). GetTaggedObject())->GetElements().GetTaggedObject()); JSTaggedValue result = elements->Get(key->GetInt()); EXPECT_EQ(result, value.GetTaggedValue()); } } return JSTaggedValue::True(); } }; protected: JSTaggedValue InitializeStackConstructor() { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); JSHandle globalObject = env->GetJSGlobalObject(); JSHandle key(factory->NewFromASCII("ArkPrivate")); JSHandle value = JSObject::GetProperty(thread, JSHandle(globalObject), key).GetValue(); auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); objCallInfo->SetFunction(JSTaggedValue::Undefined()); objCallInfo->SetThis(value.GetTaggedValue()); objCallInfo->SetCallArg(0, JSTaggedValue(static_cast(ContainerTag::Stack))); [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo); JSTaggedValue result = ContainersPrivate::Load(objCallInfo); TestHelper::TearDownFrame(thread, prev); return result; } JSHandle CreateJSAPIStack(JSTaggedValue compare = JSTaggedValue::Undefined()) { JSHandle compareHandle(thread, compare); JSHandle newTarget(thread, InitializeStackConstructor()); auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); objCallInfo->SetFunction(newTarget.GetTaggedValue()); objCallInfo->SetNewTarget(newTarget.GetTaggedValue()); objCallInfo->SetThis(JSTaggedValue::Undefined()); objCallInfo->SetCallArg(0, compareHandle.GetTaggedValue()); [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo); JSTaggedValue result = ContainersStack::StackConstructor(objCallInfo); TestHelper::TearDownFrame(thread, prev); JSHandle stack(thread, result); return stack; } }; HWTEST_F_L0(ContainersStackTest, StackConstructor) { InitializeStackConstructor(); JSHandle newTarget(thread, InitializeStackConstructor()); auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); objCallInfo->SetFunction(newTarget.GetTaggedValue()); objCallInfo->SetNewTarget(newTarget.GetTaggedValue()); objCallInfo->SetThis(JSTaggedValue::Undefined()); [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo); JSTaggedValue result = ContainersStack::StackConstructor(objCallInfo); TestHelper::TearDownFrame(thread, prev); ASSERT_TRUE(result.IsJSAPIStack()); JSHandle stack(thread, result); JSTaggedValue resultProto = JSTaggedValue::GetPrototype(thread, JSHandle(stack)); JSTaggedValue funcProto = newTarget->GetFunctionPrototype(); ASSERT_EQ(resultProto, funcProto); // test StackConstructor exception objCallInfo->SetNewTarget(JSTaggedValue::Undefined()); CONTAINERS_API_EXCEPTION_TEST(ContainersStack, StackConstructor, objCallInfo); } HWTEST_F_L0(ContainersStackTest, PushAndPeek) { constexpr uint32_t NODE_NUMBERS = 8; JSHandle stack = CreateJSAPIStack(); for (uint32_t i = 0; i < NODE_NUMBERS; i++) { auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); callInfo->SetFunction(JSTaggedValue::Undefined()); callInfo->SetThis(stack.GetTaggedValue()); callInfo->SetCallArg(0, JSTaggedValue(i)); [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo); JSTaggedValue result = ContainersStack::Push(callInfo); TestHelper::TearDownFrame(thread, prev); EXPECT_EQ(result, JSTaggedValue(i)); EXPECT_EQ(ContainersStack::Peek(callInfo), JSTaggedValue(i)); } } HWTEST_F_L0(ContainersStackTest, Pop) { constexpr uint32_t NODE_NUMBERS = 8; JSHandle stack = CreateJSAPIStack(); for (uint32_t i = 0; i < NODE_NUMBERS; i++) { auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); callInfo->SetFunction(JSTaggedValue::Undefined()); callInfo->SetThis(stack.GetTaggedValue()); callInfo->SetCallArg(0, JSTaggedValue(i)); [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo); ContainersStack::Push(callInfo); TestHelper::TearDownFrame(thread, prev); } int num = 7; for (uint32_t i = 0; i < NODE_NUMBERS; i++) { auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); callInfo->SetFunction(JSTaggedValue::Undefined()); callInfo->SetThis(stack.GetTaggedValue()); callInfo->SetCallArg(0, JSTaggedValue(i)); [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo); JSTaggedValue result = ContainersStack::Pop(callInfo); TestHelper::TearDownFrame(thread, prev); EXPECT_EQ(result, JSTaggedValue(num--)); } } HWTEST_F_L0(ContainersStackTest, IsEmpty) { constexpr uint32_t NODE_NUMBERS = 8; JSHandle stack = CreateJSAPIStack(); for (uint32_t i = 0; i < NODE_NUMBERS; i++) { auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); callInfo->SetFunction(JSTaggedValue::Undefined()); callInfo->SetThis(stack.GetTaggedValue()); callInfo->SetCallArg(0, JSTaggedValue(i)); [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo); ContainersStack::Push(callInfo); TestHelper::TearDownFrame(thread, prev); JSTaggedValue result = ContainersStack::IsEmpty(callInfo); EXPECT_EQ(result, JSTaggedValue::False()); } int num = 7; for (uint32_t i = 0; i < NODE_NUMBERS; i++) { auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); callInfo->SetFunction(JSTaggedValue::Undefined()); callInfo->SetThis(stack.GetTaggedValue()); callInfo->SetCallArg(0, JSTaggedValue(i)); [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo); JSTaggedValue result = ContainersStack::Pop(callInfo); TestHelper::TearDownFrame(thread, prev); EXPECT_EQ(result, JSTaggedValue(num--)); if (num == -1) { JSTaggedValue consequence = ContainersStack::IsEmpty(callInfo); EXPECT_EQ(consequence, JSTaggedValue::True()); } } } HWTEST_F_L0(ContainersStackTest, Locate) { constexpr uint32_t NODE_NUMBERS = 8; JSHandle stack = CreateJSAPIStack(); for (uint32_t i = 0; i < NODE_NUMBERS; i++) { auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); callInfo->SetFunction(JSTaggedValue::Undefined()); callInfo->SetThis(stack.GetTaggedValue()); callInfo->SetCallArg(0, JSTaggedValue(i)); [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo); ContainersStack::Push(callInfo); JSTaggedValue result = ContainersStack::Locate(callInfo); EXPECT_EQ(result, JSTaggedValue(i)); TestHelper::TearDownFrame(thread, prev); } } HWTEST_F_L0(ContainersStackTest, ForEach) { constexpr uint32_t NODE_NUMBERS = 8; JSHandle stack = CreateJSAPIStack(); for (uint32_t i = 0; i < NODE_NUMBERS; i++) { auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); callInfo->SetFunction(JSTaggedValue::Undefined()); callInfo->SetThis(stack.GetTaggedValue()); callInfo->SetCallArg(0, JSTaggedValue(i)); [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo); ContainersStack::Push(callInfo); TestHelper::TearDownFrame(thread, prev); } ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle dlist = CreateJSAPIStack(); { JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestClass::TestForEachFunc)); auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); callInfo->SetFunction(JSTaggedValue::Undefined()); callInfo->SetThis(stack.GetTaggedValue()); callInfo->SetCallArg(0, func.GetTaggedValue()); callInfo->SetCallArg(1, dlist.GetTaggedValue()); [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo); ContainersStack::ForEach(callInfo); TestHelper::TearDownFrame(thread, prev); } } HWTEST_F_L0(ContainersStackTest, ProxyOfGetLength) { constexpr uint32_t NODE_NUMBERS = 8; JSHandle stack = CreateJSAPIStack(); auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); callInfo->SetFunction(JSTaggedValue::Undefined()); JSHandle proxy = CreateJSProxyHandle(thread); proxy->SetTarget(thread, stack.GetTaggedValue()); callInfo->SetThis(proxy.GetTaggedValue()); for (uint32_t i = 0; i < NODE_NUMBERS; i++) { callInfo->SetCallArg(0, JSTaggedValue(i)); callInfo->SetCallArg(1, JSTaggedValue(i + 1)); [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo); ContainersStack::Push(callInfo); TestHelper::TearDownFrame(thread, prev); [[maybe_unused]] auto prev1 = TestHelper::SetupFrame(thread, callInfo); JSTaggedValue retult = ContainersStack::GetLength(callInfo); TestHelper::TearDownFrame(thread, prev1); EXPECT_EQ(retult, JSTaggedValue(i + 1)); } } HWTEST_F_L0(ContainersStackTest, ExceptionReturn) { CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersStack, IsEmpty); CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersStack, Push); CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersStack, Peek); CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersStack, Locate); CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersStack, Pop); CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersStack, ForEach); CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersStack, Iterator); CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersStack, GetLength); } } // namespace panda::test