1 /**
2 * Copyright (c) 2021-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 <gtest/gtest.h>
17
18 #include "types/ets_object.h"
19
20 #include "ets_vm.h"
21 #include "ets_coroutine.h"
22 #include "ets_class_linker_extension.h"
23 #include "assembly-emitter.h"
24 #include "assembly-parser.h"
25
26 // NOLINTBEGIN(readability-magic-numbers)
27
28 namespace ark::ets::test {
29
30 class EtsObjectTest : public testing::Test {
31 public:
EtsObjectTest()32 EtsObjectTest()
33 {
34 options_.SetShouldLoadBootPandaFiles(true);
35 options_.SetShouldInitializeIntrinsics(false);
36 options_.SetCompilerEnableJit(false);
37 options_.SetGcType("epsilon");
38 options_.SetLoadRuntimes({"ets"});
39
40 auto stdlib = std::getenv("PANDA_STD_LIB");
41 if (stdlib == nullptr) {
42 std::cerr << "PANDA_STD_LIB env variable should be set and point to mock_stdlib.abc" << std::endl;
43 std::abort();
44 }
45 options_.SetBootPandaFiles({stdlib});
46
47 Runtime::Create(options_);
48
49 SetClassesPandasmSources();
50 }
51
~EtsObjectTest()52 ~EtsObjectTest() override
53 {
54 Runtime::Destroy();
55 }
56
57 NO_COPY_SEMANTIC(EtsObjectTest);
58 NO_MOVE_SEMANTIC(EtsObjectTest);
59
SetUp()60 void SetUp() override
61 {
62 coroutine_ = EtsCoroutine::GetCurrent();
63 coroutine_->ManagedCodeBegin();
64 }
65
TearDown()66 void TearDown() override
67 {
68 coroutine_->ManagedCodeEnd();
69 }
70
71 void SetClassesPandasmSources();
72 EtsClass *GetTestClass(std::string className);
73
74 private:
75 RuntimeOptions options_;
76 EtsCoroutine *coroutine_ = nullptr;
77 std::unordered_map<std::string, const char *> sources_;
78
79 protected:
GetSources()80 const std::unordered_map<std::string, const char *> &GetSources()
81 {
82 return sources_;
83 }
84 };
85
SetClassesPandasmSources()86 void EtsObjectTest::SetClassesPandasmSources()
87 {
88 const char *source = R"(
89 .language eTS
90 .record Rectangle {
91 i32 Width
92 f32 Height
93 i64 Color <static>
94 }
95 )";
96 sources_["Rectangle"] = source;
97
98 source = R"(
99 .language eTS
100 .record Triangle {
101 i32 firSide
102 i32 secSide
103 i32 thirdSide
104 i64 Color <static>
105 }
106 )";
107 sources_["Triangle"] = source;
108
109 source = R"(
110 .language eTS
111 .record Foo {
112 i32 member
113 }
114 .record Bar {
115 Foo foo1
116 Foo foo2
117 }
118 )";
119 sources_["Foo"] = source;
120 sources_["Bar"] = source;
121 }
122
GetTestClass(std::string className)123 EtsClass *EtsObjectTest::GetTestClass(std::string className)
124 {
125 std::unordered_map<std::string, const char *> sources = GetSources();
126 pandasm::Parser p;
127
128 auto res = p.Parse(sources[className]);
129 auto pf = pandasm::AsmEmitter::Emit(res.Value());
130
131 auto classLinker = Runtime::GetCurrent()->GetClassLinker();
132 classLinker->AddPandaFile(std::move(pf));
133
134 className.insert(0, 1, 'L');
135 className.push_back(';');
136
137 EtsClass *klass = coroutine_->GetPandaVM()->GetClassLinker()->GetClass(className.c_str());
138 ASSERT(klass);
139 return klass;
140 }
141
TEST_F(EtsObjectTest,GetClass)142 TEST_F(EtsObjectTest, GetClass)
143 {
144 EtsClass *klass = nullptr;
145 EtsObject *obj = nullptr;
146
147 for (const auto &it : GetSources()) {
148 klass = GetTestClass(it.first);
149 obj = EtsObject::Create(klass);
150 ASSERT_EQ(klass, obj->GetClass());
151 }
152 }
153
TEST_F(EtsObjectTest,SetClass)154 TEST_F(EtsObjectTest, SetClass)
155 {
156 EtsClass *klass1 = GetTestClass("Rectangle");
157 EtsClass *klass2 = GetTestClass("Triangle");
158 EtsObject *obj = EtsObject::Create(klass1);
159 ASSERT_EQ(obj->GetClass(), klass1);
160 obj->SetClass(klass2);
161 ASSERT_EQ(obj->GetClass(), klass2);
162 }
163
TEST_F(EtsObjectTest,IsInstanceOf)164 TEST_F(EtsObjectTest, IsInstanceOf)
165 {
166 EtsClass *klass1 = GetTestClass("Rectangle");
167 EtsClass *klass2 = GetTestClass("Triangle");
168 EtsObject *obj1 = EtsObject::Create(klass1);
169 EtsObject *obj2 = EtsObject::Create(klass2);
170
171 ASSERT_TRUE(obj1->IsInstanceOf(klass1));
172 ASSERT_TRUE(obj2->IsInstanceOf(klass2));
173 ASSERT_FALSE(obj1->IsInstanceOf(klass2));
174 ASSERT_FALSE(obj2->IsInstanceOf(klass1));
175 }
176
TEST_F(EtsObjectTest,GetAndSetFieldObject)177 TEST_F(EtsObjectTest, GetAndSetFieldObject)
178 {
179 EtsClass *barKlass = GetTestClass("Bar");
180 EtsClass *fooKlass = GetTestClass("Foo");
181
182 EtsObject *barObj = EtsObject::Create(barKlass);
183 EtsObject *fooObj1 = EtsObject::Create(fooKlass);
184 EtsObject *fooObj2 = EtsObject::Create(fooKlass);
185
186 EtsField *foo1Field = barKlass->GetFieldIDByName("foo1");
187 EtsField *foo2Field = barKlass->GetFieldIDByName("foo2");
188
189 barObj->SetFieldObject(foo1Field, fooObj1);
190 barObj->SetFieldObject(foo2Field, fooObj2);
191 ASSERT_EQ(barObj->GetFieldObject(foo1Field), fooObj1);
192 ASSERT_EQ(barObj->GetFieldObject(foo2Field), fooObj2);
193
194 EtsObject *res = barObj->GetAndSetFieldObject(foo2Field->GetOffset(), fooObj1, std::memory_order_relaxed);
195 ASSERT_EQ(res, fooObj2); // returned pointer was in foo2_field
196 ASSERT_EQ(barObj->GetFieldObject(foo2Field), fooObj1); // now in foo2_field is pointer to Foo_obj1
197
198 res = barObj->GetAndSetFieldObject(foo1Field->GetOffset(), fooObj2, std::memory_order_relaxed);
199 ASSERT_EQ(res, fooObj1);
200 ASSERT_EQ(barObj->GetFieldObject(foo1Field), fooObj2);
201 }
202
TEST_F(EtsObjectTest,SetAndGetFieldObject)203 TEST_F(EtsObjectTest, SetAndGetFieldObject)
204 {
205 EtsClass *barKlass = GetTestClass("Bar");
206 EtsClass *fooKlass = GetTestClass("Foo");
207
208 EtsObject *barObj = EtsObject::Create(barKlass);
209 EtsObject *fooObj1 = EtsObject::Create(fooKlass);
210 EtsObject *fooObj2 = EtsObject::Create(fooKlass);
211
212 EtsField *foo1Field = barKlass->GetFieldIDByName("foo1");
213 EtsField *foo2Field = barKlass->GetFieldIDByName("foo2");
214
215 barObj->SetFieldObject(foo1Field, fooObj1);
216 barObj->SetFieldObject(foo2Field, fooObj2);
217 ASSERT_EQ(barObj->GetFieldObject(foo1Field), fooObj1);
218 ASSERT_EQ(barObj->GetFieldObject(foo2Field), fooObj2);
219
220 barObj->SetFieldObject(foo1Field, fooObj2);
221 ASSERT_EQ(barObj->GetFieldObject(foo1Field), fooObj2);
222 barObj->SetFieldObject(foo2Field, fooObj1);
223 ASSERT_EQ(barObj->GetFieldObject(foo2Field), fooObj1);
224 }
225
TEST_F(EtsObjectTest,SetAndGetFieldPrimitive)226 TEST_F(EtsObjectTest, SetAndGetFieldPrimitive)
227 {
228 EtsClass *klass = GetTestClass("Rectangle");
229 EtsObject *obj = EtsObject::Create(klass);
230 EtsField *field = klass->GetFieldIDByName("Width");
231 int32_t testNmb1 = 77;
232 obj->SetFieldPrimitive<int32_t>(field, testNmb1);
233 ASSERT_EQ(obj->GetFieldPrimitive<int32_t>(field), testNmb1);
234
235 field = klass->GetFieldIDByName("Height");
236 float testNmb2 = 111.11;
237 obj->SetFieldPrimitive<float>(field, testNmb2);
238 ASSERT_EQ(obj->GetFieldPrimitive<float>(field), testNmb2);
239 }
240
TEST_F(EtsObjectTest,CompareAndSetFieldPrimitive)241 TEST_F(EtsObjectTest, CompareAndSetFieldPrimitive)
242 {
243 EtsClass *klass = GetTestClass("Rectangle");
244 EtsObject *obj = EtsObject::Create(klass);
245 EtsField *field = klass->GetFieldIDByName("Width");
246 int32_t firNmb = 134;
247 int32_t secNmb = 12;
248 obj->SetFieldPrimitive(field, firNmb);
249 obj->CompareAndSetFieldPrimitive(field->GetOffset(), firNmb, secNmb, std::memory_order_relaxed, true);
250 ASSERT_EQ(obj->GetFieldPrimitive<int32_t>(field), secNmb);
251 }
252
TEST_F(EtsObjectTest,CompareAndSetFieldObject)253 TEST_F(EtsObjectTest, CompareAndSetFieldObject)
254 {
255 EtsClass *barKlass = GetTestClass("Bar");
256 EtsClass *fooKlass = GetTestClass("Foo");
257
258 EtsObject *barObj = EtsObject::Create(barKlass);
259 EtsObject *fooObj1 = EtsObject::Create(fooKlass);
260 EtsObject *fooObj2 = EtsObject::Create(fooKlass);
261
262 EtsField *foo1Field = barKlass->GetFieldIDByName("foo1");
263 EtsField *foo2Field = barKlass->GetFieldIDByName("foo2");
264
265 barObj->SetFieldObject(foo1Field, fooObj1);
266 barObj->SetFieldObject(foo2Field, fooObj2);
267 ASSERT_EQ(barObj->GetFieldObject(foo1Field), fooObj1);
268 ASSERT_EQ(barObj->GetFieldObject(foo2Field), fooObj2);
269
270 ASSERT_TRUE(
271 barObj->CompareAndSetFieldObject(foo1Field->GetOffset(), fooObj1, fooObj2, std::memory_order_relaxed, false));
272 ASSERT_EQ(barObj->GetFieldObject(foo1Field), fooObj2);
273
274 ASSERT_TRUE(
275 barObj->CompareAndSetFieldObject(foo2Field->GetOffset(), fooObj2, fooObj1, std::memory_order_relaxed, false));
276 ASSERT_EQ(barObj->GetFieldObject(foo2Field), fooObj1);
277 }
278
279 } // namespace ark::ets::test
280
281 // NOLINTEND(readability-magic-numbers)
282