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
29 using namespace panda::ecmascript;
30 using namespace panda::ecmascript::containers;
31
32 namespace panda::test {
33 class ContainersStackTest : public testing::Test {
34 public:
SetUpTestCase()35 static void SetUpTestCase()
36 {
37 GTEST_LOG_(INFO) << "SetUpTestCase";
38 }
39
TearDownTestCase()40 static void TearDownTestCase()
41 {
42 GTEST_LOG_(INFO) << "TearDownCase";
43 }
44
SetUp()45 void SetUp() override
46 {
47 TestHelper::CreateEcmaVMWithScope(instance, thread, scope);
48 }
49
TearDown()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:
TestForEachFunc(EcmaRuntimeCallInfo * argv)61 static JSTaggedValue TestForEachFunc(EcmaRuntimeCallInfo *argv)
62 {
63 JSThread *thread = argv->GetThread();
64 JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
65 JSHandle<JSTaggedValue> key = GetCallArg(argv, 1);
66 JSHandle<JSTaggedValue> stack = GetCallArg(argv, 2); // 2 means the secode arg
67 if (!stack->IsUndefined()) {
68 if (value->IsNumber()) {
69 TaggedArray *elements = TaggedArray::Cast(JSAPIStack::Cast(stack.GetTaggedValue().
70 GetTaggedObject())->GetElements(thread).GetTaggedObject());
71 JSTaggedValue result = elements->Get(thread, key->GetInt());
72 EXPECT_EQ(result, value.GetTaggedValue());
73 }
74 }
75 return JSTaggedValue::True();
76 }
77 };
78 protected:
InitializeStackConstructor()79 JSTaggedValue InitializeStackConstructor()
80 {
81 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
82 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
83
84 JSHandle<JSTaggedValue> globalObject = env->GetJSGlobalObject();
85 JSHandle<JSTaggedValue> key(factory->NewFromASCII("ArkPrivate"));
86 JSHandle<JSTaggedValue> value =
87 JSObject::GetProperty(thread, JSHandle<JSTaggedValue>(globalObject), key).GetValue();
88
89 auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
90 objCallInfo->SetFunction(JSTaggedValue::Undefined());
91 objCallInfo->SetThis(value.GetTaggedValue());
92 objCallInfo->SetCallArg(0, JSTaggedValue(static_cast<int>(ContainerTag::Stack)));
93 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo);
94 JSTaggedValue result = ContainersPrivate::Load(objCallInfo);
95 TestHelper::TearDownFrame(thread, prev);
96
97 return result;
98 }
99
CreateJSAPIStack(JSTaggedValue compare=JSTaggedValue::Undefined ())100 JSHandle<JSAPIStack> CreateJSAPIStack(JSTaggedValue compare = JSTaggedValue::Undefined())
101 {
102 JSHandle<JSTaggedValue> compareHandle(thread, compare);
103 JSHandle<JSFunction> newTarget(thread, InitializeStackConstructor());
104 auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
105 objCallInfo->SetFunction(newTarget.GetTaggedValue());
106 objCallInfo->SetNewTarget(newTarget.GetTaggedValue());
107 objCallInfo->SetThis(JSTaggedValue::Undefined());
108 objCallInfo->SetCallArg(0, compareHandle.GetTaggedValue());
109
110 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo);
111 JSTaggedValue result = ContainersStack::StackConstructor(objCallInfo);
112 TestHelper::TearDownFrame(thread, prev);
113 JSHandle<JSAPIStack> stack(thread, result);
114 return stack;
115 }
116 };
117
HWTEST_F_L0(ContainersStackTest,StackConstructor)118 HWTEST_F_L0(ContainersStackTest, StackConstructor)
119 {
120 InitializeStackConstructor();
121 JSHandle<JSFunction> newTarget(thread, InitializeStackConstructor());
122
123 auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
124 objCallInfo->SetFunction(newTarget.GetTaggedValue());
125 objCallInfo->SetNewTarget(newTarget.GetTaggedValue());
126 objCallInfo->SetThis(JSTaggedValue::Undefined());
127
128 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo);
129 JSTaggedValue result = ContainersStack::StackConstructor(objCallInfo);
130 TestHelper::TearDownFrame(thread, prev);
131
132 ASSERT_TRUE(result.IsJSAPIStack());
133 JSHandle<JSAPIStack> stack(thread, result);
134 JSTaggedValue resultProto = JSTaggedValue::GetPrototype(thread, JSHandle<JSTaggedValue>(stack));
135 JSTaggedValue funcProto = newTarget->GetFunctionPrototype(thread);
136 ASSERT_EQ(resultProto, funcProto);
137
138 // test StackConstructor exception
139 objCallInfo->SetNewTarget(JSTaggedValue::Undefined());
140 CONTAINERS_API_EXCEPTION_TEST(ContainersStack, StackConstructor, objCallInfo);
141 }
142
HWTEST_F_L0(ContainersStackTest,PushAndPeek)143 HWTEST_F_L0(ContainersStackTest, PushAndPeek)
144 {
145 constexpr uint32_t NODE_NUMBERS = 8;
146 JSHandle<JSAPIStack> stack = CreateJSAPIStack();
147 for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
148 auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
149 callInfo->SetFunction(JSTaggedValue::Undefined());
150 callInfo->SetThis(stack.GetTaggedValue());
151 callInfo->SetCallArg(0, JSTaggedValue(i));
152
153 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
154 JSTaggedValue result = ContainersStack::Push(callInfo);
155 TestHelper::TearDownFrame(thread, prev);
156 EXPECT_EQ(result, JSTaggedValue(i));
157 EXPECT_EQ(ContainersStack::Peek(callInfo), JSTaggedValue(i));
158 }
159 }
160
HWTEST_F_L0(ContainersStackTest,Pop)161 HWTEST_F_L0(ContainersStackTest, Pop)
162 {
163 constexpr uint32_t NODE_NUMBERS = 8;
164 JSHandle<JSAPIStack> stack = CreateJSAPIStack();
165 for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
166 auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
167 callInfo->SetFunction(JSTaggedValue::Undefined());
168 callInfo->SetThis(stack.GetTaggedValue());
169 callInfo->SetCallArg(0, JSTaggedValue(i));
170
171 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
172 ContainersStack::Push(callInfo);
173 TestHelper::TearDownFrame(thread, prev);
174 }
175
176 int num = 7;
177 for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
178 auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
179 callInfo->SetFunction(JSTaggedValue::Undefined());
180 callInfo->SetThis(stack.GetTaggedValue());
181 callInfo->SetCallArg(0, JSTaggedValue(i));
182
183 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
184 JSTaggedValue result = ContainersStack::Pop(callInfo);
185 TestHelper::TearDownFrame(thread, prev);
186 EXPECT_EQ(result, JSTaggedValue(num--));
187 }
188 }
189
HWTEST_F_L0(ContainersStackTest,IsEmpty)190 HWTEST_F_L0(ContainersStackTest, IsEmpty)
191 {
192 constexpr uint32_t NODE_NUMBERS = 8;
193 JSHandle<JSAPIStack> stack = CreateJSAPIStack();
194 for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
195 auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
196 callInfo->SetFunction(JSTaggedValue::Undefined());
197 callInfo->SetThis(stack.GetTaggedValue());
198 callInfo->SetCallArg(0, JSTaggedValue(i));
199
200 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
201 ContainersStack::Push(callInfo);
202 TestHelper::TearDownFrame(thread, prev);
203 JSTaggedValue result = ContainersStack::IsEmpty(callInfo);
204 EXPECT_EQ(result, JSTaggedValue::False());
205 }
206
207 int num = 7;
208 for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
209 auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
210 callInfo->SetFunction(JSTaggedValue::Undefined());
211 callInfo->SetThis(stack.GetTaggedValue());
212 callInfo->SetCallArg(0, JSTaggedValue(i));
213
214 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
215 JSTaggedValue result = ContainersStack::Pop(callInfo);
216 TestHelper::TearDownFrame(thread, prev);
217 EXPECT_EQ(result, JSTaggedValue(num--));
218 if (num == -1) {
219 JSTaggedValue consequence = ContainersStack::IsEmpty(callInfo);
220 EXPECT_EQ(consequence, JSTaggedValue::True());
221 }
222 }
223 }
224
HWTEST_F_L0(ContainersStackTest,Locate)225 HWTEST_F_L0(ContainersStackTest, Locate)
226 {
227 constexpr uint32_t NODE_NUMBERS = 8;
228 JSHandle<JSAPIStack> stack = CreateJSAPIStack();
229 for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
230 auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
231 callInfo->SetFunction(JSTaggedValue::Undefined());
232 callInfo->SetThis(stack.GetTaggedValue());
233 callInfo->SetCallArg(0, JSTaggedValue(i));
234
235 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
236 ContainersStack::Push(callInfo);
237 JSTaggedValue result = ContainersStack::Locate(callInfo);
238 EXPECT_EQ(result, JSTaggedValue(i));
239 TestHelper::TearDownFrame(thread, prev);
240 }
241 }
242
HWTEST_F_L0(ContainersStackTest,ForEach)243 HWTEST_F_L0(ContainersStackTest, ForEach)
244 {
245 constexpr uint32_t NODE_NUMBERS = 8;
246 JSHandle<JSAPIStack> stack = CreateJSAPIStack();
247 for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
248 auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
249 callInfo->SetFunction(JSTaggedValue::Undefined());
250 callInfo->SetThis(stack.GetTaggedValue());
251 callInfo->SetCallArg(0, JSTaggedValue(i));
252
253 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
254 ContainersStack::Push(callInfo);
255 TestHelper::TearDownFrame(thread, prev);
256 }
257 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
258 JSHandle<JSAPIStack> dlist = CreateJSAPIStack();
259 {
260 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
261 JSHandle<JSFunction> func = factory->NewJSFunction(env, reinterpret_cast<void *>(TestClass::TestForEachFunc));
262 auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
263 callInfo->SetFunction(JSTaggedValue::Undefined());
264 callInfo->SetThis(stack.GetTaggedValue());
265 callInfo->SetCallArg(0, func.GetTaggedValue());
266 callInfo->SetCallArg(1, dlist.GetTaggedValue());
267
268 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
269 ContainersStack::ForEach(callInfo);
270 TestHelper::TearDownFrame(thread, prev);
271 }
272 }
273
HWTEST_F_L0(ContainersStackTest,ProxyOfGetLength)274 HWTEST_F_L0(ContainersStackTest, ProxyOfGetLength)
275 {
276 constexpr uint32_t NODE_NUMBERS = 8;
277 JSHandle<JSAPIStack> stack = CreateJSAPIStack();
278 auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
279 callInfo->SetFunction(JSTaggedValue::Undefined());
280 JSHandle<JSProxy> proxy = CreateJSProxyHandle(thread);
281 proxy->SetTarget(thread, stack.GetTaggedValue());
282 callInfo->SetThis(proxy.GetTaggedValue());
283
284 for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
285 callInfo->SetCallArg(0, JSTaggedValue(i));
286 callInfo->SetCallArg(1, JSTaggedValue(i + 1));
287 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
288 ContainersStack::Push(callInfo);
289 TestHelper::TearDownFrame(thread, prev);
290
291 [[maybe_unused]] auto prev1 = TestHelper::SetupFrame(thread, callInfo);
292 JSTaggedValue retult = ContainersStack::GetLength(callInfo);
293 TestHelper::TearDownFrame(thread, prev1);
294 EXPECT_EQ(retult, JSTaggedValue(i + 1));
295 }
296 }
297
HWTEST_F_L0(ContainersStackTest,ExceptionReturn)298 HWTEST_F_L0(ContainersStackTest, ExceptionReturn)
299 {
300 CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersStack, IsEmpty);
301 CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersStack, Push);
302 CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersStack, Peek);
303 CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersStack, Locate);
304 CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersStack, Pop);
305 CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersStack, ForEach);
306 CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersStack, Iterator);
307 CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersStack, GetLength);
308 }
309 } // namespace panda::test
310