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