1/*
2 * Copyright (c) 2022-2024 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/object_operator.h"
17#include "ecmascript/ecma_string.h"
18#include "ecmascript/global_env.h"
19#include "ecmascript/global_dictionary-inl.h"
20#include "ecmascript/js_array.h"
21#include "ecmascript/js_object-inl.h"
22#include "ecmascript/property_attributes.h"
23#include "ecmascript/tagged_array.h"
24#include "ecmascript/tagged_dictionary.h"
25#include "ecmascript/tests/test_helper.h"
26
27using namespace panda::ecmascript;
28
29namespace panda::test {
30class ObjectOperatorTest : public BaseTestWithScope<false> {
31};
32
33JSTaggedValue TestDefinedSetter([[maybe_unused]] EcmaRuntimeCallInfo *argv)
34{
35    // 12 : test case
36    return JSTaggedValue(12);
37}
38
39static JSFunction *JSObjectTestCreate(JSThread *thread)
40{
41    JSHandle<GlobalEnv> globalEnv = thread->GetEcmaVM()->GetGlobalEnv();
42    return globalEnv->GetObjectFunction().GetObject<JSFunction>();
43}
44
45bool TestBoolSetter([[maybe_unused]] JSThread *thread,
46                    [[maybe_unused]] const JSHandle<JSObject> &jsObject,
47                    [[maybe_unused]] const JSHandle<JSTaggedValue> &value,
48                    [[maybe_unused]] bool success)
49{
50    return true;
51}
52
53HWTEST_F_L0(ObjectOperatorTest, ObjectOperator_Constructor1)
54{
55    JSHandle<JSTaggedValue> handleKey(thread, JSTaggedValue(1));
56    OperatorType type = OperatorType::PROTOTYPE_CHAIN;
57    // ObjectOperator(thread, JSHandle<JSTaggedVale>(), type)
58    ObjectOperator objectOperator1(thread, handleKey, type);
59    EXPECT_TRUE(objectOperator1.IsOnPrototype());
60    EXPECT_TRUE(objectOperator1.GetReceiver()->IsJSGlobalObject());
61    EXPECT_FALSE(objectOperator1.GetHolder()->IsJSGlobalObject());
62    type = OperatorType::OWN;
63    ObjectOperator objectOperator2(thread, handleKey, type);
64    EXPECT_FALSE(objectOperator2.IsOnPrototype());
65    EXPECT_TRUE(objectOperator2.GetReceiver()->IsJSGlobalObject());
66    EXPECT_TRUE(objectOperator2.GetHolder()->IsJSGlobalObject());
67}
68
69HWTEST_F_L0(ObjectOperatorTest, ObjectOperator_Constructor2)
70{
71    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
72    JSHandle<JSTaggedValue> objFunc(thread, JSObjectTestCreate(thread));
73    JSHandle<JSObject> handleHolder = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFunc), objFunc);
74    JSHandle<JSTaggedValue> handleKey(factory->NewFromASCII("key"));
75    OperatorType type = OperatorType::PROTOTYPE_CHAIN;
76    // ObjectOperator(thread, JSHandle<JSObject>(), JSHandle<JSTaggedVale>(), type)
77    ObjectOperator objectOperator1(thread, handleHolder, handleKey, type);
78    EXPECT_TRUE(objectOperator1.IsOnPrototype());
79    type = OperatorType::OWN;
80    ObjectOperator objectOperator2(thread, handleHolder, handleKey, type);
81    EXPECT_FALSE(objectOperator2.IsOnPrototype());
82}
83
84HWTEST_F_L0(ObjectOperatorTest, ObjectOperator_Constructor3)
85{
86    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
87    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
88    JSHandle<JSTaggedValue> symbolFunc = env->GetSymbolFunction();
89    JSHandle<JSTaggedValue> handleHolder(thread, symbolFunc.GetTaggedValue());
90    JSHandle<JSTaggedValue> handleReceiver(thread, symbolFunc.GetTaggedValue());
91    JSHandle<JSTaggedValue> handleKey(factory->NewFromASCII("key"));
92    OperatorType type = OperatorType::PROTOTYPE_CHAIN;
93    // ObjectOperator(thread, JSHandle<JSTaggedVale>(), JSHandle<JSTaggedVale>(), type)
94    ObjectOperator objectOperator1(thread, handleHolder, handleKey, type);
95    EXPECT_TRUE(objectOperator1.IsOnPrototype());
96    type = OperatorType::OWN;
97    ObjectOperator objectOperator2(thread, handleHolder, handleKey, type);
98    EXPECT_FALSE(objectOperator2.IsOnPrototype());
99}
100
101HWTEST_F_L0(ObjectOperatorTest, ObjectOperator_Constructor4)
102{
103    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
104    JSHandle<JSTaggedValue> stringFunc = env->GetStringFunction();
105    JSHandle<JSTaggedValue> handleHolder(thread, stringFunc.GetTaggedValue());
106    JSHandle<JSTaggedValue> handleReceiver(thread, stringFunc.GetTaggedValue());
107    OperatorType type = OperatorType::PROTOTYPE_CHAIN;
108    // ObjectOperator(thread, JSHandle<JSTaggedVale>(), JSHandle<JSTaggedVale>(), type)
109    ObjectOperator objectOperator1(thread, handleHolder, handleReceiver, type);
110    EXPECT_TRUE(objectOperator1.IsOnPrototype());
111    type = OperatorType::OWN;
112    ObjectOperator objectOperator2(thread, handleHolder, handleReceiver, type);
113    EXPECT_FALSE(objectOperator2.IsOnPrototype());
114}
115
116HWTEST_F_L0(ObjectOperatorTest, ObjectOperator_Constructor5)
117{
118    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
119    JSHandle<JSTaggedValue> boolFunc = env->GetBooleanFunction();
120    JSHandle<JSTaggedValue> handleHolder(thread, boolFunc.GetTaggedValue());
121    uint32_t index = 1;
122    OperatorType type = OperatorType::PROTOTYPE_CHAIN;
123    // ObjectOperator(thread, JSHandle<JSTaggedVale>(), index, type)
124    ObjectOperator objectOperator1(thread, handleHolder, index, type);
125    EXPECT_TRUE(objectOperator1.IsOnPrototype());
126    type = OperatorType::OWN;
127    ObjectOperator objectOperator2(thread, handleHolder, index, type);
128    EXPECT_FALSE(objectOperator2.IsOnPrototype());
129}
130
131HWTEST_F_L0(ObjectOperatorTest, ObjectOperator_Constructor6)
132{
133    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
134    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
135    JSHandle<JSTaggedValue> numFunc = env->GetNumberFunction();
136    JSHandle<JSTaggedValue> handleReceiver(thread, numFunc.GetTaggedValue());
137    JSHandle<JSTaggedValue> handleName(factory->NewFromASCII("name"));
138    OperatorType type = OperatorType::PROTOTYPE_CHAIN;
139    // ObjectOperator(thread, JSTaggedVale(), JSTaggedValue(), type)
140    ObjectOperator objectOperator1(thread, handleReceiver.GetTaggedValue(), handleName.GetTaggedValue(), type);
141    EXPECT_FALSE(objectOperator1.IsOnPrototype());
142    type = OperatorType::OWN;
143    ObjectOperator objectOperator2(thread, handleReceiver.GetTaggedValue(), handleName.GetTaggedValue(), type);
144    EXPECT_FALSE(objectOperator2.IsOnPrototype());
145}
146
147HWTEST_F_L0(ObjectOperatorTest, ObjectOperator_Constructor7)
148{
149    JSHandle<JSTaggedValue> handleReceiver(thread, JSTaggedValue(1));
150    JSHandle<JSTaggedValue> handleName(thread, JSTaggedValue(2));
151    PropertyAttributes handleAttr(4);
152    // ObjectOperator(thread, JSTaggedVale(), JSTaggedValue(), PropertyAttributes())
153    ObjectOperator objectOperator(thread, handleReceiver.GetTaggedValue(), handleName.GetTaggedValue(), handleAttr);
154    EXPECT_EQ(objectOperator.GetReceiver()->GetInt(), 1);
155    EXPECT_EQ(objectOperator.GetAttr().GetPropertyMetaData(), 4);
156    EXPECT_EQ(objectOperator.GetKey()->GetInt(), 2);
157}
158
159HWTEST_F_L0(ObjectOperatorTest, UpdateDateValue_001)
160{
161    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
162    JSHandle<JSTaggedValue> objFunc(thread, JSObjectTestCreate(thread));
163    JSHandle<JSTaggedValue> handleKey(factory->NewFromASCII("key"));
164    JSHandle<JSTaggedValue> handleKey2(thread, JSTaggedValue(2));
165    JSHandle<JSTaggedValue> handleValue(thread, JSTaggedValue(4));
166    JSHandle<JSTaggedValue> handleValue1(thread, JSTaggedValue(5));
167    ObjectOperator objectOperator1(thread, handleValue);
168    objectOperator1.SetIndex(1);
169
170    // object is not DictionaryMode
171    JSHandle<JSObject> handleObject = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFunc), objFunc);
172    for (int i = 0; i < 3; i++) {
173        JSHandle<JSTaggedValue> newKey(thread, JSTaggedValue(i));
174        JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(handleObject), newKey, newKey);
175    }
176    EXPECT_TRUE(objectOperator1.UpdateDataValue(handleObject, handleValue, false));
177    auto *resultElements =TaggedArray::Cast(handleObject->GetElements().GetTaggedObject());
178    EXPECT_EQ(resultElements->Get(objectOperator1.GetIndex()).GetInt(), 4);
179
180    // object is DictionaryMode
181    JSObject::DeleteProperty(thread, handleObject, handleKey2);
182    EXPECT_TRUE(objectOperator1.UpdateDataValue(handleObject, handleValue1, false));
183    auto *resultDict = NumberDictionary::Cast(handleObject->GetElements().GetTaggedObject());
184    EXPECT_EQ(resultDict->GetValue(objectOperator1.GetIndex()).GetInt(), 5);
185
186    // object value is InternalAccessor
187    JSHandle<AccessorData> handleAccessorData = factory->NewInternalAccessor(
188        reinterpret_cast<void*>(TestBoolSetter), nullptr);
189    JSHandle<JSTaggedValue> handleValue2(handleAccessorData);
190    ObjectOperator objectOperator2(thread, handleKey);
191    objectOperator2.SetValue(handleAccessorData.GetTaggedValue());
192    objectOperator2.SetIndex(1);
193    EXPECT_TRUE(objectOperator2.UpdateDataValue(handleObject, handleValue, true));
194}
195
196HWTEST_F_L0(ObjectOperatorTest, UpdateDataValue_002)
197{
198    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
199    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
200    JSHandle<JSTaggedValue> objFunc(thread, JSObjectTestCreate(thread));
201    JSHandle<JSTaggedValue> handleKey(factory->NewFromASCII("key"));
202    JSHandle<JSTaggedValue> handleValue(thread, JSTaggedValue(100));
203    // object is JSGlobalObject
204    JSHandle<JSTaggedValue> globalObj = env->GetJSGlobalObject();
205    JSHandle<JSObject> handleGlobalObject(globalObj);
206
207    JSMutableHandle<GlobalDictionary> holderDict(thread, handleGlobalObject->GetProperties());
208    JSHandle<GlobalDictionary> handleDict = GlobalDictionary::Create(thread, 4); // numberofElements = 4
209    holderDict.Update(handleDict.GetTaggedValue());
210    JSHandle<PropertyBox> cellHandle = factory->NewPropertyBox(handleKey);
211    cellHandle->SetValue(thread, JSTaggedValue(4));
212    JSHandle<GlobalDictionary> handleProperties =
213        GlobalDictionary::PutIfAbsent(thread, holderDict, handleKey,
214                                      JSHandle<JSTaggedValue>(cellHandle), PropertyAttributes(4));
215    handleGlobalObject->SetProperties(thread, handleProperties.GetTaggedValue()); // Set Properties
216    int keyEntry = handleProperties->FindEntry(handleKey.GetTaggedValue());
217
218    ObjectOperator objectOperator(thread, handleGlobalObject, handleKey);
219    objectOperator.SetIndex(keyEntry);
220    EXPECT_TRUE(objectOperator.UpdateDataValue(handleGlobalObject, handleValue, false));
221    auto *resultDict = GlobalDictionary::Cast(handleGlobalObject->GetProperties().GetTaggedObject());
222    PropertyBox *resultCell = resultDict->GetBox(objectOperator.GetIndex());
223    EXPECT_EQ(resultCell->GetValue().GetInt(), 100);
224}
225
226HWTEST_F_L0(ObjectOperatorTest, UpdateDataValue_003)
227{
228    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
229    JSHandle<JSTaggedValue> objFunc(thread, JSObjectTestCreate(thread));
230    JSHandle<JSTaggedValue> handleValue(thread, JSTaggedValue(4));
231    JSHandle<JSTaggedValue> handleValue1(thread, JSTaggedValue(3));
232    JSHandle<JSTaggedValue> handleKey(factory->NewFromASCII("key"));
233    JSHandle<EcmaString> handleKey1 = factory->NewFromASCII("value");
234    JSHandle<JSTaggedValue> handleKey2(factory->NewFromASCII("value1"));
235
236    ObjectOperator objectOperator(thread, handleKey);
237    objectOperator.SetIndex(1);
238    PropertyDescriptor handleDesc(thread);
239    PropertyAttributes handleAttr(handleDesc);
240    handleAttr.SetIsInlinedProps(true);
241    objectOperator.SetAttr(PropertyAttributes(handleDesc));
242
243    // object is not DictionaryMode
244    JSHandle<JSObject> handleObject = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFunc), objFunc);
245    for (int i = 0; i < 10; i++) {
246        JSHandle<JSTaggedValue> newValue(thread, JSTaggedValue(i));
247        JSHandle<EcmaString> newString =
248            factory->ConcatFromString(handleKey1, JSTaggedValue::ToString(thread, newValue));
249        JSHandle<JSTaggedValue> newKey(thread, newString.GetTaggedValue());
250        JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(handleObject), newKey, newValue);
251    }
252    EXPECT_TRUE(objectOperator.UpdateDataValue(handleObject, handleValue1, false));
253    EXPECT_EQ(handleObject->GetPropertyInlinedProps(objectOperator.GetIndex()).GetInt(), 3);
254
255    // object is DictionaryMode
256    JSObject::DeleteProperty(thread, handleObject, handleKey2);
257    EXPECT_TRUE(objectOperator.UpdateDataValue(handleObject, handleValue, false));
258    TaggedArray *resultElements2 = TaggedArray::Cast(handleObject->GetProperties().GetTaggedObject());
259    auto *resultDict = NumberDictionary::Cast(resultElements2);
260    EXPECT_EQ(resultDict->GetValue(objectOperator.GetIndex()).GetInt(), 4);
261}
262} // namespace panda::test