1 /*
2  * Copyright (c) 2021 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/builtins/builtins_weak_map.h"
17 
18 #include "ecmascript/base/builtins_base.h"
19 #include "ecmascript/ecma_runtime_call_info.h"
20 #include "ecmascript/ecma_string.h"
21 #include "ecmascript/ecma_vm.h"
22 #include "ecmascript/global_env.h"
23 #include "ecmascript/js_array.h"
24 #include "ecmascript/js_handle.h"
25 #include "ecmascript/js_hclass.h"
26 #include "ecmascript/js_map_iterator.h"
27 #include "ecmascript/js_object-inl.h"
28 #include "ecmascript/js_tagged_value.h"
29 #include "ecmascript/js_thread.h"
30 #include "ecmascript/js_weak_container.h"
31 #include "ecmascript/object_factory.h"
32 #include "ecmascript/tagged_array-inl.h"
33 #include "ecmascript/tests/test_helper.h"
34 
35 using namespace panda::ecmascript;
36 using namespace panda::ecmascript::builtins;
37 
38 namespace panda::test {
39 using BuiltinsWeakMap = ecmascript::builtins::BuiltinsWeakMap;
40 using JSWeakMap = ecmascript::JSWeakMap;
41 
42 class BuiltinsWeakMapTest : public BaseTestWithScope<false> {
43 };
44 
JSObjectTestCreate(JSThread *thread)45 static JSObject *JSObjectTestCreate(JSThread *thread)
46 {
47     EcmaVM *ecmaVM = thread->GetEcmaVM();
48     ObjectFactory *factory = ecmaVM->GetFactory();
49     [[maybe_unused]] EcmaHandleScope scope(thread);
50     JSHandle<GlobalEnv> globalEnv = ecmaVM->GetGlobalEnv();
51     JSHandle<JSTaggedValue> jsFunc = globalEnv->GetObjectFunction();
52     return *factory->NewJSObjectByConstructor(JSHandle<JSFunction>(jsFunc), jsFunc);
53 }
54 
CreateBuiltinsWeakMap(JSThread *thread)55 JSWeakMap *CreateBuiltinsWeakMap(JSThread *thread)
56 {
57     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
58     JSHandle<JSFunction> newTarget(env->GetBuiltinsWeakMapFunction());
59     // 4 : test case
60     auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, newTarget.GetTaggedValue(), 4);
61     ecmaRuntimeCallInfo->SetFunction(newTarget.GetTaggedValue());
62     ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
63 
64     [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
65     JSTaggedValue result = BuiltinsWeakMap::WeakMapConstructor(ecmaRuntimeCallInfo);
66     TestHelper::TearDownFrame(thread, prev);
67 
68     EXPECT_TRUE(result.IsECMAObject());
69     return JSWeakMap::Cast(reinterpret_cast<TaggedObject *>(result.GetRawData()));
70 }
71 
72 // new Map("abrupt").toString()
HWTEST_F_L0(BuiltinsWeakMapTest, CreateAndGetSize)73 HWTEST_F_L0(BuiltinsWeakMapTest, CreateAndGetSize)
74 {
75     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
76     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
77     JSHandle<JSFunction> newTarget(env->GetBuiltinsWeakMapFunction());
78     JSHandle<JSWeakMap> map(thread, CreateBuiltinsWeakMap(thread));
79 
80     JSHandle<TaggedArray> array(factory->NewTaggedArray(1));
81     JSHandle<TaggedArray> internalArray(factory->NewTaggedArray(2));
82     JSTaggedValue value(JSObjectTestCreate(thread));
83     internalArray->Set(thread, 0, value);
84     internalArray->Set(thread, 1, JSTaggedValue(0));
85     auto result = JSArray::CreateArrayFromList(thread, internalArray);
86     array->Set(thread, 0, result);
87 
88     JSHandle<JSArray> values = JSArray::CreateArrayFromList(thread, array);
89 
90     auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
91     ecmaRuntimeCallInfo->SetFunction(newTarget.GetTaggedValue());
92     ecmaRuntimeCallInfo->SetThis(map.GetTaggedValue());
93     ecmaRuntimeCallInfo->SetCallArg(0, values.GetTaggedValue());
94     ecmaRuntimeCallInfo->SetNewTarget(newTarget.GetTaggedValue());
95 
96     [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
97 
98     JSTaggedValue result1 = BuiltinsWeakMap::WeakMapConstructor(ecmaRuntimeCallInfo);
99     JSHandle<JSWeakMap> weakMap(thread, JSWeakMap::Cast(reinterpret_cast<TaggedObject *>(result1.GetRawData())));
100     EXPECT_EQ(weakMap->GetSize(), 1);
101 }
102 
HWTEST_F_L0(BuiltinsWeakMapTest, SetAndHas)103 HWTEST_F_L0(BuiltinsWeakMapTest, SetAndHas)
104 {
105     // create jsWeakMap
106     JSHandle<JSWeakMap> weakMap(thread, CreateBuiltinsWeakMap(thread));
107     JSHandle<JSTaggedValue> key(thread, JSObjectTestCreate(thread));
108 
109     auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
110     ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
111     ecmaRuntimeCallInfo->SetThis(weakMap.GetTaggedValue());
112     ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue());
113     ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast<int32_t>(1)));
114 
115     {
116         [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
117         JSTaggedValue result1 = BuiltinsWeakMap::Has(ecmaRuntimeCallInfo);
118         TestHelper::TearDownFrame(thread, prev);
119 
120         EXPECT_EQ(result1.GetRawData(), JSTaggedValue::False().GetRawData());
121     }
122 
123     // test Set()
124     JSTaggedValue result2 = BuiltinsWeakMap::Set(ecmaRuntimeCallInfo);
125     EXPECT_TRUE(result2.IsECMAObject());
126     JSWeakMap *jsWeakMap = JSWeakMap::Cast(reinterpret_cast<TaggedObject *>(result2.GetRawData()));
127     EXPECT_EQ(jsWeakMap->GetSize(), 1);
128 
129     // test Has()
130     auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
131     ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
132     ecmaRuntimeCallInfo1->SetCallArg(0, key.GetTaggedValue());
133     ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast<int32_t>(1)));
134     ecmaRuntimeCallInfo1->SetThis(JSTaggedValue(jsWeakMap));
135     {
136         [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1);
137         JSTaggedValue result3 = BuiltinsWeakMap::Has(ecmaRuntimeCallInfo1);
138         TestHelper::TearDownFrame(thread, prev);
139 
140         EXPECT_EQ(result3.GetRawData(), JSTaggedValue::True().GetRawData());
141     }
142 }
143 
KeySetCommon(JSThread* thread, JSHandle<JSWeakMap>& weakMap, JSHandle<JSTaggedValue>& key, int32_t val)144 void KeySetCommon(JSThread* thread, JSHandle<JSWeakMap>& weakMap, JSHandle<JSTaggedValue>& key, int32_t val)
145 {
146     auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
147     ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
148     ecmaRuntimeCallInfo->SetThis(weakMap.GetTaggedValue());
149     ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue());
150     ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(val));
151 
152     [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
153     JSTaggedValue result1 = BuiltinsWeakMap::Set(ecmaRuntimeCallInfo);
154     TestHelper::TearDownFrame(thread, prev);
155 
156     EXPECT_TRUE(result1.IsECMAObject());
157     JSWeakMap *jsWeakMap = JSWeakMap::Cast(reinterpret_cast<TaggedObject *>(result1.GetRawData()));
158     EXPECT_EQ(jsWeakMap->GetSize(), static_cast<int>(val) + 1);
159 }
160 
HWTEST_F_L0(BuiltinsWeakMapTest, DeleteAndRemove)161 HWTEST_F_L0(BuiltinsWeakMapTest, DeleteAndRemove)
162 {
163     // create jsWeakMap
164     JSHandle<JSWeakMap> weakMap(thread, CreateBuiltinsWeakMap(thread));
165 
166     // add 40 keys
167     JSTaggedValue lastKey(JSTaggedValue::Undefined());
168     for (int i = 0; i < 40; i++) {
169         JSHandle<JSTaggedValue> key(thread, JSObjectTestCreate(thread));
170         KeySetCommon(thread, weakMap, key, static_cast<int32_t>(i));
171         lastKey = key.GetTaggedValue();
172     }
173 
174     // whether jsWeakMap has delete lastKey
175 
176     auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
177     ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
178     ecmaRuntimeCallInfo1->SetThis(weakMap.GetTaggedValue());
179     ecmaRuntimeCallInfo1->SetCallArg(0, lastKey);
180 
181     [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1);
182     JSTaggedValue result2 = BuiltinsWeakMap::Has(ecmaRuntimeCallInfo1);
183     TestHelper::TearDownFrame(thread, prev);
184 
185     EXPECT_EQ(result2.GetRawData(), JSTaggedValue::True().GetRawData());
186 
187     // delete
188     JSTaggedValue result3 = BuiltinsWeakMap::Delete(ecmaRuntimeCallInfo1);
189 
190     EXPECT_EQ(result3.GetRawData(), JSTaggedValue::True().GetRawData());
191 
192     // check deleteKey is deleted
193     JSTaggedValue result4 = BuiltinsWeakMap::Has(ecmaRuntimeCallInfo1);
194 
195     EXPECT_EQ(result4.GetRawData(), JSTaggedValue::False().GetRawData());
196 }
197 
HWTEST_F_L0(BuiltinsWeakMapTest, SymbolKey)198 HWTEST_F_L0(BuiltinsWeakMapTest, SymbolKey)
199 {
200     // create jsWeakMap
201     JSHandle<JSWeakMap> weakMap(thread, CreateBuiltinsWeakMap(thread));
202 
203     // add 2 symbol keys
204     JSTaggedValue lastKey(JSTaggedValue::Undefined());
205     for (int i = 0; i < 2; i++) {
206         JSHandle<JSSymbol> symbolKey = thread->GetEcmaVM()->GetFactory()->NewJSSymbol();
207         JSHandle<JSTaggedValue> key(symbolKey);
208         KeySetCommon(thread, weakMap, key, static_cast<int32_t>(i));
209         lastKey = key.GetTaggedValue();
210     }
211 
212     // check whether jsWeakMap can get and delete lastKey
213 
214     auto ecmaRuntimeCallInfo1 =
215         TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); // 6 means 1 call arg
216     ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
217     ecmaRuntimeCallInfo1->SetThis(weakMap.GetTaggedValue());
218     ecmaRuntimeCallInfo1->SetCallArg(0, lastKey);
219 
220     [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1);
221     // get
222     JSTaggedValue result2 = BuiltinsWeakMap::Get(ecmaRuntimeCallInfo1);
223     TestHelper::TearDownFrame(thread, prev);
224     EXPECT_EQ(result2, JSTaggedValue(1));
225 
226     // delete
227     JSTaggedValue result3 = BuiltinsWeakMap::Delete(ecmaRuntimeCallInfo1);
228     EXPECT_EQ(result3.GetRawData(), JSTaggedValue::True().GetRawData());
229 
230     // check deleteKey is deleted
231     JSTaggedValue result4 = BuiltinsWeakMap::Has(ecmaRuntimeCallInfo1);
232     EXPECT_EQ(result4.GetRawData(), JSTaggedValue::False().GetRawData());
233 }
234 }  // namespace panda::test
235