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
35using namespace panda::ecmascript;
36using namespace panda::ecmascript::builtins;
37
38namespace panda::test {
39using BuiltinsWeakMap = ecmascript::builtins::BuiltinsWeakMap;
40using JSWeakMap = ecmascript::JSWeakMap;
41
42class BuiltinsWeakMapTest : public BaseTestWithScope<false> {
43};
44
45static 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
55JSWeakMap *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()
73HWTEST_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
103HWTEST_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
144void 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
161HWTEST_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
198HWTEST_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