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/ic/ic_handler.h"
17#include "ecmascript/ic/proto_change_details.h"
18#include "ecmascript/global_env.h"
19#include "ecmascript/js_array.h"
20#include "ecmascript/js_hclass.h"
21#include "ecmascript/js_object-inl.h"
22#include "ecmascript/object_operator.h"
23#include "ecmascript/tests/test_helper.h"
24
25using namespace panda::ecmascript;
26
27namespace panda::test {
28using HandlerKind = ecmascript::HandlerBase::HandlerKind;
29class ICHandlerTest : public testing::Test {
30public:
31    static void SetUpTestCase()
32    {
33        GTEST_LOG_(INFO) << "SetUpTestCase";
34    }
35
36    static void TearDownTestCase()
37    {
38        GTEST_LOG_(INFO) << "TearDownCase";
39    }
40
41    void SetUp() override
42    {
43        TestHelper::CreateEcmaVMWithScope(instance, thread, scope);
44    }
45
46    void TearDown() override
47    {
48        TestHelper::DestroyEcmaVMWithScope(instance, scope);
49    }
50
51    EcmaVM *instance {nullptr};
52    EcmaHandleScope *scope {nullptr};
53    JSThread *thread {nullptr};
54};
55
56/**
57 * @tc.name: LoadElement
58 * @tc.desc: Call "LoadElement" function,check whether the Element is loaded successfully by checking returned value.
59 * @tc.type: FUNC
60 * @tc.require:
61 */
62HWTEST_F_L0(ICHandlerTest, LoadElement)
63{
64    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
65    JSHandle<JSTaggedValue> handleKey(factory->NewFromASCII("key"));
66    ObjectOperator handleOp(thread, handleKey);
67    JSTaggedValue result = LoadHandler::LoadElement(thread, handleOp).GetTaggedValue();
68    EXPECT_TRUE(HandlerBase::IsNormalElement(result.GetNumber()));
69    EXPECT_EQ(HandlerBase::GetKind(result.GetNumber()), HandlerKind::ELEMENT);
70}
71
72/**
73 * @tc.name: LoadProperty
74 * @tc.desc: Call "LoadProperty" function,check whether the Property is loaded successfully by checking returned value.
75 * @tc.type: FUNC
76 * @tc.require:
77 */
78HWTEST_F_L0(ICHandlerTest, LoadProperty)
79{
80    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
81    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
82
83    JSHandle<JSTaggedValue> objFun = env->GetArrayFunction();
84    JSHandle<JSTaggedValue> handleValue(thread, JSTaggedValue(1));
85    JSHandle<JSTaggedValue> handleKey(factory->NewFromASCII("key"));
86    int index = 1;
87    PropertyAttributes handleAttriButes(2);
88    handleAttriButes.SetIsInlinedProps(true);
89    // test op is not Found
90    ObjectOperator handleOp1(thread, handleKey);
91    JSHandle<JSTaggedValue> handlerInfo1 = LoadHandler::LoadProperty(thread, handleOp1);
92    EXPECT_TRUE(HandlerBase::IsNonExist(handlerInfo1->GetInt()));
93    EXPECT_EQ(HandlerBase::GetKind(handlerInfo1->GetInt()), HandlerKind::NON_EXIST);
94    // test op is Found and FastMode
95    JSHandle<JSTaggedValue> handleHolder(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun));
96    ObjectOperator handleOp2(thread, handleHolder, handleKey);
97    handleOp2.SetFastMode(true);
98    handleOp2.SetIndex(index);
99    JSHandle<JSTaggedValue> handlerInfo2 = LoadHandler::LoadProperty(thread, handleOp2);
100    EXPECT_EQ(HandlerBase::GetOffset(handlerInfo2->GetInt()), 1);
101    // test op is Found and InlinedProps
102    handleOp2.SetAttr(handleAttriButes);
103    JSHandle<JSTaggedValue> handlerInfo3 = LoadHandler::LoadProperty(thread, handleOp2);
104    EXPECT_EQ(HandlerBase::GetKind(handlerInfo3->GetInt()), HandlerKind::FIELD);
105    EXPECT_EQ(HandlerBase::GetOffset(handlerInfo3->GetInt()), 5);
106    EXPECT_TRUE(HandlerBase::IsInlinedProps(handlerInfo3->GetInt()));
107}
108
109/**
110 * @tc.name: StoreElement
111 * @tc.desc: Call "StoreElement" function,check whether the Element is stored successfully by checking returned value.
112 * @tc.type: FUNC
113 * @tc.require:
114 */
115HWTEST_F_L0(ICHandlerTest, StoreElement)
116{
117    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
118
119    JSArray *handleArr = JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetObject<JSArray>();
120    JSHandle<JSTaggedValue> handleReceiver1(thread, handleArr);
121    JSHandle<JSTaggedValue> handleReceiver2(factory->NewJSArray());
122    JSHandle<JSTaggedValue> handleValue(thread, JSTaggedValue(1));
123
124    JSHandle<JSTaggedValue> handlerInfo1 = StoreHandler::StoreElement(thread, handleReceiver1, 0);
125    JSHandle<JSTaggedValue> handlerInfo2 = StoreHandler::StoreElement(thread, handleReceiver2, 0);
126    JSHandle<JSTaggedValue> handlerInfo3 = StoreHandler::StoreElement(thread, handleValue, 0);
127
128    EXPECT_EQ(HandlerBase::GetKind(handlerInfo1->GetInt()), HandlerKind::ELEMENT);
129    EXPECT_EQ(HandlerBase::GetKind(handlerInfo2->GetInt()), HandlerKind::ELEMENT);
130    EXPECT_EQ(HandlerBase::GetKind(handlerInfo3->GetInt()), HandlerKind::ELEMENT);
131
132    EXPECT_TRUE(HandlerBase::IsJSArray(handlerInfo1->GetInt()));
133    EXPECT_TRUE(HandlerBase::IsJSArray(handlerInfo2->GetInt()));
134    EXPECT_FALSE(HandlerBase::IsJSArray(handlerInfo3->GetInt()));
135}
136
137/**
138 * @tc.name: StoreProperty
139 * @tc.desc: Call "StoreProperty" function,check whether the Property is stored successfully by checking returned value.
140 *           according to the ObjectOperation object,the stored Property is different,the stored ObjectOperation object
141 *           is Found.
142 * @tc.type: FUNC
143 * @tc.require:
144 */
145HWTEST_F_L0(ICHandlerTest, StoreProperty)
146{
147    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
148    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
149
150    JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
151    JSHandle<JSTaggedValue> handleValue(thread, JSTaggedValue(1));
152    JSHandle<JSTaggedValue> handleKey(factory->NewFromASCII("key"));
153    int index = 2;
154    PropertyAttributes handleAttriButes(2);
155    handleAttriButes.SetIsInlinedProps(true);
156
157    JSHandle<PropertyBox> cellHandle = factory->NewPropertyBox(handleKey);
158    ObjectOperator handleOp1(thread, handleKey);
159    handleOp1.SetValue(cellHandle.GetTaggedValue());
160    // test op value is PropertyBox
161    JSHandle<JSTaggedValue> handlerInfo1 = StoreHandler::StoreProperty(thread, handleOp1);
162    EXPECT_TRUE(handlerInfo1->IsPropertyBox());
163    // test op is FastMode/Found and not AccessorDescriptor
164    JSHandle<JSTaggedValue> handleReceiver(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun));
165    ObjectOperator handleOp2(thread, handleReceiver, handleKey);
166    handleOp2.SetFastMode(true);
167    handleOp2.SetIndex(index);
168    JSHandle<JSTaggedValue> handlerInfo2 = StoreHandler::StoreProperty(thread, handleOp2);
169    EXPECT_EQ(HandlerBase::GetOffset(handlerInfo2->GetInt()), 2);
170    EXPECT_FALSE(HandlerBase::IsAccessor(handlerInfo2->GetInt()));
171    // test op is InlinedProps/Found and not AccessorDescriptor
172    handleOp2.SetAttr(handleAttriButes);
173    JSHandle<JSTaggedValue> handlerInfo3 = StoreHandler::StoreProperty(thread, handleOp2);
174    EXPECT_EQ(HandlerBase::GetKind(handlerInfo3->GetInt()), HandlerKind::FIELD);
175    EXPECT_EQ(HandlerBase::GetOffset(handlerInfo3->GetInt()), 6);
176    EXPECT_TRUE(HandlerBase::IsInlinedProps(handlerInfo3->GetInt()));
177}
178
179/**
180 * @tc.name: StoreTransition
181 * @tc.desc: Call "StoreTransition" function,check whether the Transition is stored successfully by checking
182 *           returned value.
183 * @tc.type: FUNC
184 * @tc.require:
185 */
186HWTEST_F_L0(ICHandlerTest, StoreTransition)
187{
188    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
189    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
190    JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
191    JSHandle<JSTaggedValue> handleValue(thread, JSTaggedValue(1));
192    JSHandle<JSTaggedValue> handleKey(factory->NewFromASCII("key"));
193
194    JSHandle<PropertyBox> cellHandle = factory->NewPropertyBox(handleKey);
195    JSHandle<JSTaggedValue> handleHolder(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun));
196
197    ObjectOperator handleOp(thread, handleHolder, handleKey);
198    handleOp.SetValue(cellHandle.GetTaggedValue());
199
200    JSHandle<JSTaggedValue> handlerValue = TransitionHandler::StoreTransition(thread, handleOp);
201    JSHandle<TransitionHandler> handler = JSHandle<TransitionHandler>::Cast(handlerValue);
202    EXPECT_TRUE(handler->GetHandlerInfo().IsPropertyBox());
203    EXPECT_TRUE(handler->GetTransitionHClass().IsHeapObject());
204}
205
206/**
207 * @tc.name: LoadPrototype
208 * @tc.desc: Call "LoadPrototype" function,check whether the Prototype is loaded successfully by checking returned value
209 *           according to the ObjectOperation object,the stored Property is different.
210 * @tc.type: FUNC
211 * @tc.require:
212 */
213HWTEST_F_L0(ICHandlerTest, LoadPrototype)
214{
215    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
216
217    JSHandle<JSTaggedValue> handleKey(factory->NewFromASCII("key"));
218    JSHandle<JSTaggedValue> handleValue(thread, JSTaggedValue(3));
219
220    JSHandle<JSObject> nullHandle(thread, JSTaggedValue::Null());
221    JSHandle<JSObject> handleObj1 = JSObject::ObjectCreate(thread, nullHandle);
222    JSHandle<JSObject> handleObj2 = JSObject::ObjectCreate(thread, handleObj1);
223
224    JSHandle<JSHClass> obj1Class(thread, handleObj1->GetJSHClass());
225    JSHandle<JSHClass> obj2Class(thread, handleObj2->GetJSHClass());
226
227    ObjectOperator handleOp1(thread, handleKey, OperatorType::OWN);
228    ObjectOperator handleOp2(thread, handleKey, OperatorType::OWN);
229    handleOp1.SetFastMode(true);
230    handleOp2.SetFastMode(true);
231    handleOp2.SetIndex(2);
232    // test op is not Found and hclass has no Prototype
233    JSHandle<JSTaggedValue> handlerValue1 = LoadHandler::LoadProperty(thread, handleOp1);
234    EXPECT_TRUE(HandlerBase::IsNonExist(handlerValue1->GetInt()));
235    // test op is Found and hclass has Prototype
236    JSHandle<JSTaggedValue> handlerValue2 = PrototypeHandler::LoadPrototype(thread, handleOp2, obj2Class);
237    JSHandle<PrototypeHandler> handler2 = JSHandle<PrototypeHandler>::Cast(handlerValue2);
238    JSHandle<JSTaggedValue> handlerInfo2(thread, handler2->GetHandlerInfo());
239    EXPECT_EQ(HandlerBase::GetOffset(handlerInfo2->GetInt()), 2);
240    JSHandle<JSTaggedValue> resultMarker(thread, handler2->GetProtoCell());
241    EXPECT_TRUE(resultMarker->IsProtoChangeMarker());
242    EXPECT_TRUE(handler2->GetHolder().IsJSGlobalObject());
243}
244
245/**
246 * @tc.name: StorePrototype
247 * @tc.desc: Call StorePrototype function,check whether the Prototype is stored successfully by checking returned value
248 *           according to the ObjectOperation object,the stored Property is different.the stored ObjectOperation object
249 *           must be Found.
250 * @tc.type: FUNC
251 * @tc.require:
252 */
253HWTEST_F_L0(ICHandlerTest, StorePrototype)
254{
255    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
256
257    JSHandle<JSTaggedValue> handleKey(factory->NewFromASCII("key"));
258    JSHandle<JSTaggedValue> handleValue(thread, JSTaggedValue(3));
259
260    JSHandle<JSObject> nullHandle(thread, JSTaggedValue::Null());
261    JSHandle<JSObject> nullObj = JSObject::ObjectCreate(thread, nullHandle);
262    JSHandle<JSObject> handleObj = JSObject::ObjectCreate(thread, nullObj);
263
264    JSHandle<JSHClass> objClass(thread, handleObj->GetJSHClass());
265
266    ObjectOperator handleOp(thread, handleKey, OperatorType::OWN);
267    handleOp.SetFastMode(true);
268    handleOp.SetIndex(2);
269    // test hclass has Prototype
270    JSHandle<JSTaggedValue> handlerValue = PrototypeHandler::StorePrototype(thread, handleOp, objClass);
271    JSHandle<PrototypeHandler> handler = JSHandle<PrototypeHandler>::Cast(handlerValue);
272    JSHandle<JSTaggedValue> handlerInfo(thread, handler->GetHandlerInfo());
273    EXPECT_EQ(HandlerBase::GetOffset(handlerInfo->GetInt()), 2);
274    JSHandle<JSTaggedValue> resultMarker(thread, handler->GetProtoCell());
275    EXPECT_TRUE(resultMarker->IsProtoChangeMarker());
276    EXPECT_TRUE(handler->GetHolder().IsJSGlobalObject());
277}
278} // namespace panda::test
279