1/*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "ecmascript/containers/containers_stack.h"
17#include "ecmascript/containers/containers_private.h"
18#include "ecmascript/ecma_runtime_call_info.h"
19#include "ecmascript/global_env.h"
20#include "ecmascript/js_api/js_api_stack.h"
21#include "ecmascript/js_api/js_api_stack_iterator.h"
22#include "ecmascript/js_handle.h"
23#include "ecmascript/js_tagged_value-inl.h"
24#include "ecmascript/js_thread.h"
25#include "ecmascript/object_factory.h"
26#include "ecmascript/tests/test_helper.h"
27#include "ecmascript/containers/tests/containers_test_helper.h"
28
29using namespace panda::ecmascript;
30using namespace panda::ecmascript::containers;
31
32namespace panda::test {
33class ContainersStackTest : public testing::Test {
34public:
35    static void SetUpTestCase()
36    {
37        GTEST_LOG_(INFO) << "SetUpTestCase";
38    }
39
40    static void TearDownTestCase()
41    {
42        GTEST_LOG_(INFO) << "TearDownCase";
43    }
44
45    void SetUp() override
46    {
47        TestHelper::CreateEcmaVMWithScope(instance, thread, scope);
48    }
49
50    void TearDown() override
51    {
52        TestHelper::DestroyEcmaVMWithScope(instance, scope);
53    }
54
55    EcmaVM *instance {nullptr};
56    EcmaHandleScope *scope {nullptr};
57    JSThread *thread {nullptr};
58
59    class TestClass : public base::BuiltinsBase {
60    public:
61        static JSTaggedValue TestForEachFunc(EcmaRuntimeCallInfo *argv)
62        {
63            JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
64            JSHandle<JSTaggedValue> key = GetCallArg(argv, 1);
65            JSHandle<JSTaggedValue> stack = GetCallArg(argv, 2); // 2 means the secode arg
66            if (!stack->IsUndefined()) {
67                if (value->IsNumber()) {
68                    TaggedArray *elements = TaggedArray::Cast(JSAPIStack::Cast(stack.GetTaggedValue().
69                                            GetTaggedObject())->GetElements().GetTaggedObject());
70                    JSTaggedValue result = elements->Get(key->GetInt());
71                    EXPECT_EQ(result, value.GetTaggedValue());
72                }
73            }
74            return JSTaggedValue::True();
75        }
76    };
77protected:
78    JSTaggedValue InitializeStackConstructor()
79    {
80        ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
81        JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
82
83        JSHandle<JSTaggedValue> globalObject = env->GetJSGlobalObject();
84        JSHandle<JSTaggedValue> key(factory->NewFromASCII("ArkPrivate"));
85        JSHandle<JSTaggedValue> value =
86            JSObject::GetProperty(thread, JSHandle<JSTaggedValue>(globalObject), key).GetValue();
87
88        auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
89        objCallInfo->SetFunction(JSTaggedValue::Undefined());
90        objCallInfo->SetThis(value.GetTaggedValue());
91        objCallInfo->SetCallArg(0, JSTaggedValue(static_cast<int>(ContainerTag::Stack)));
92        [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo);
93        JSTaggedValue result = ContainersPrivate::Load(objCallInfo);
94        TestHelper::TearDownFrame(thread, prev);
95
96        return result;
97    }
98
99    JSHandle<JSAPIStack> CreateJSAPIStack(JSTaggedValue compare = JSTaggedValue::Undefined())
100    {
101        JSHandle<JSTaggedValue> compareHandle(thread, compare);
102        JSHandle<JSFunction> newTarget(thread, InitializeStackConstructor());
103        auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
104        objCallInfo->SetFunction(newTarget.GetTaggedValue());
105        objCallInfo->SetNewTarget(newTarget.GetTaggedValue());
106        objCallInfo->SetThis(JSTaggedValue::Undefined());
107        objCallInfo->SetCallArg(0, compareHandle.GetTaggedValue());
108
109        [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo);
110        JSTaggedValue result = ContainersStack::StackConstructor(objCallInfo);
111        TestHelper::TearDownFrame(thread, prev);
112        JSHandle<JSAPIStack> stack(thread, result);
113        return stack;
114    }
115};
116
117HWTEST_F_L0(ContainersStackTest, StackConstructor)
118{
119    InitializeStackConstructor();
120    JSHandle<JSFunction> newTarget(thread, InitializeStackConstructor());
121
122    auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
123    objCallInfo->SetFunction(newTarget.GetTaggedValue());
124    objCallInfo->SetNewTarget(newTarget.GetTaggedValue());
125    objCallInfo->SetThis(JSTaggedValue::Undefined());
126
127    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo);
128    JSTaggedValue result = ContainersStack::StackConstructor(objCallInfo);
129    TestHelper::TearDownFrame(thread, prev);
130
131    ASSERT_TRUE(result.IsJSAPIStack());
132    JSHandle<JSAPIStack> stack(thread, result);
133    JSTaggedValue resultProto = JSTaggedValue::GetPrototype(thread, JSHandle<JSTaggedValue>(stack));
134    JSTaggedValue funcProto = newTarget->GetFunctionPrototype();
135    ASSERT_EQ(resultProto, funcProto);
136
137    // test StackConstructor exception
138    objCallInfo->SetNewTarget(JSTaggedValue::Undefined());
139    CONTAINERS_API_EXCEPTION_TEST(ContainersStack, StackConstructor, objCallInfo);
140}
141
142HWTEST_F_L0(ContainersStackTest, PushAndPeek)
143{
144    constexpr uint32_t NODE_NUMBERS = 8;
145    JSHandle<JSAPIStack> stack = CreateJSAPIStack();
146    for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
147        auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
148        callInfo->SetFunction(JSTaggedValue::Undefined());
149        callInfo->SetThis(stack.GetTaggedValue());
150        callInfo->SetCallArg(0, JSTaggedValue(i));
151
152        [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
153        JSTaggedValue result = ContainersStack::Push(callInfo);
154        TestHelper::TearDownFrame(thread, prev);
155        EXPECT_EQ(result, JSTaggedValue(i));
156        EXPECT_EQ(ContainersStack::Peek(callInfo), JSTaggedValue(i));
157    }
158}
159
160HWTEST_F_L0(ContainersStackTest, Pop)
161{
162    constexpr uint32_t NODE_NUMBERS = 8;
163    JSHandle<JSAPIStack> stack = CreateJSAPIStack();
164    for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
165        auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
166        callInfo->SetFunction(JSTaggedValue::Undefined());
167        callInfo->SetThis(stack.GetTaggedValue());
168        callInfo->SetCallArg(0, JSTaggedValue(i));
169
170        [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
171        ContainersStack::Push(callInfo);
172        TestHelper::TearDownFrame(thread, prev);
173    }
174
175    int num = 7;
176    for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
177        auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
178        callInfo->SetFunction(JSTaggedValue::Undefined());
179        callInfo->SetThis(stack.GetTaggedValue());
180        callInfo->SetCallArg(0, JSTaggedValue(i));
181
182        [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
183        JSTaggedValue result = ContainersStack::Pop(callInfo);
184        TestHelper::TearDownFrame(thread, prev);
185        EXPECT_EQ(result, JSTaggedValue(num--));
186    }
187}
188
189HWTEST_F_L0(ContainersStackTest, IsEmpty)
190{
191    constexpr uint32_t NODE_NUMBERS = 8;
192    JSHandle<JSAPIStack> stack = CreateJSAPIStack();
193    for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
194        auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
195        callInfo->SetFunction(JSTaggedValue::Undefined());
196        callInfo->SetThis(stack.GetTaggedValue());
197        callInfo->SetCallArg(0, JSTaggedValue(i));
198
199        [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
200        ContainersStack::Push(callInfo);
201        TestHelper::TearDownFrame(thread, prev);
202        JSTaggedValue result = ContainersStack::IsEmpty(callInfo);
203        EXPECT_EQ(result, JSTaggedValue::False());
204    }
205
206    int num = 7;
207    for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
208        auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
209        callInfo->SetFunction(JSTaggedValue::Undefined());
210        callInfo->SetThis(stack.GetTaggedValue());
211        callInfo->SetCallArg(0, JSTaggedValue(i));
212
213        [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
214        JSTaggedValue result = ContainersStack::Pop(callInfo);
215        TestHelper::TearDownFrame(thread, prev);
216        EXPECT_EQ(result, JSTaggedValue(num--));
217        if (num == -1) {
218            JSTaggedValue consequence = ContainersStack::IsEmpty(callInfo);
219            EXPECT_EQ(consequence, JSTaggedValue::True());
220        }
221    }
222}
223
224HWTEST_F_L0(ContainersStackTest, Locate)
225{
226    constexpr uint32_t NODE_NUMBERS = 8;
227    JSHandle<JSAPIStack> stack = CreateJSAPIStack();
228    for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
229        auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
230        callInfo->SetFunction(JSTaggedValue::Undefined());
231        callInfo->SetThis(stack.GetTaggedValue());
232        callInfo->SetCallArg(0, JSTaggedValue(i));
233
234        [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
235        ContainersStack::Push(callInfo);
236        JSTaggedValue result = ContainersStack::Locate(callInfo);
237        EXPECT_EQ(result, JSTaggedValue(i));
238        TestHelper::TearDownFrame(thread, prev);
239    }
240}
241
242HWTEST_F_L0(ContainersStackTest, ForEach)
243{
244    constexpr uint32_t NODE_NUMBERS = 8;
245    JSHandle<JSAPIStack> stack = CreateJSAPIStack();
246    for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
247        auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
248        callInfo->SetFunction(JSTaggedValue::Undefined());
249        callInfo->SetThis(stack.GetTaggedValue());
250        callInfo->SetCallArg(0, JSTaggedValue(i));
251
252        [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
253        ContainersStack::Push(callInfo);
254        TestHelper::TearDownFrame(thread, prev);
255    }
256    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
257    JSHandle<JSAPIStack> dlist = CreateJSAPIStack();
258    {
259        JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
260        JSHandle<JSFunction> func = factory->NewJSFunction(env, reinterpret_cast<void *>(TestClass::TestForEachFunc));
261        auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
262        callInfo->SetFunction(JSTaggedValue::Undefined());
263        callInfo->SetThis(stack.GetTaggedValue());
264        callInfo->SetCallArg(0, func.GetTaggedValue());
265        callInfo->SetCallArg(1, dlist.GetTaggedValue());
266
267        [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
268        ContainersStack::ForEach(callInfo);
269        TestHelper::TearDownFrame(thread, prev);
270    }
271}
272
273HWTEST_F_L0(ContainersStackTest, ProxyOfGetLength)
274{
275    constexpr uint32_t NODE_NUMBERS = 8;
276    JSHandle<JSAPIStack> stack = CreateJSAPIStack();
277    auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
278    callInfo->SetFunction(JSTaggedValue::Undefined());
279    JSHandle<JSProxy> proxy = CreateJSProxyHandle(thread);
280    proxy->SetTarget(thread, stack.GetTaggedValue());
281    callInfo->SetThis(proxy.GetTaggedValue());
282
283    for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
284        callInfo->SetCallArg(0, JSTaggedValue(i));
285        callInfo->SetCallArg(1, JSTaggedValue(i + 1));
286        [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
287        ContainersStack::Push(callInfo);
288        TestHelper::TearDownFrame(thread, prev);
289
290        [[maybe_unused]] auto prev1 = TestHelper::SetupFrame(thread, callInfo);
291        JSTaggedValue retult = ContainersStack::GetLength(callInfo);
292        TestHelper::TearDownFrame(thread, prev1);
293        EXPECT_EQ(retult, JSTaggedValue(i + 1));
294    }
295}
296
297HWTEST_F_L0(ContainersStackTest, ExceptionReturn)
298{
299    CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersStack, IsEmpty);
300    CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersStack, Push);
301    CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersStack, Peek);
302    CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersStack, Locate);
303    CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersStack, Pop);
304    CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersStack, ForEach);
305    CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersStack, Iterator);
306    CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersStack, GetLength);
307}
308}  // namespace panda::test
309