• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2025 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 #include <cstdint>
18 
19 #include "types/ets_object.h"
20 
21 #include "ets_vm.h"
22 #include "ets_coroutine.h"
23 #include "ets_class_linker_extension.h"
24 #include "assembly-emitter.h"
25 #include "assembly-parser.h"
26 
27 // NOLINTBEGIN(readability-magic-numbers)
28 
29 namespace ark::ets::test {
30 
31 class EtsObjectTest : public testing::Test {
32 public:
EtsObjectTest()33     EtsObjectTest()
34     {
35         options_.SetShouldLoadBootPandaFiles(true);
36         options_.SetShouldInitializeIntrinsics(false);
37         options_.SetCompilerEnableJit(false);
38         options_.SetGcType("epsilon");
39         options_.SetLoadRuntimes({"ets"});
40 
41         auto stdlib = std::getenv("PANDA_STD_LIB");
42         if (stdlib == nullptr) {
43             std::cerr << "PANDA_STD_LIB env variable should be set and point to mock_stdlib.abc" << std::endl;
44             std::abort();
45         }
46         options_.SetBootPandaFiles({stdlib});
47 
48         Runtime::Create(options_);
49 
50         SetClassesPandasmSources();
51     }
52 
~EtsObjectTest()53     ~EtsObjectTest() override
54     {
55         Runtime::Destroy();
56     }
57 
58     NO_COPY_SEMANTIC(EtsObjectTest);
59     NO_MOVE_SEMANTIC(EtsObjectTest);
60 
SetUp()61     void SetUp() override
62     {
63         coroutine_ = EtsCoroutine::GetCurrent();
64         coroutine_->ManagedCodeBegin();
65     }
66 
TearDown()67     void TearDown() override
68     {
69         coroutine_->ManagedCodeEnd();
70     }
71 
72     void SetClassesPandasmSources();
73     EtsClass *GetTestClass(std::string className);
74 
75 private:
76     RuntimeOptions options_;
77     EtsCoroutine *coroutine_ = nullptr;
78     std::unordered_map<std::string, const char *> sources_;
79 
80 protected:
GetSources()81     const std::unordered_map<std::string, const char *> &GetSources()
82     {
83         return sources_;
84     }
85 };
86 
SetClassesPandasmSources()87 void EtsObjectTest::SetClassesPandasmSources()
88 {
89     const char *source = R"(
90         .language eTS
91         .record Rectangle {
92             i32 Width
93             f32 Height
94             i64 Color <static>
95         }
96     )";
97     sources_["Rectangle"] = source;
98 
99     source = R"(
100         .language eTS
101         .record Triangle {
102             i32 firSide
103             i32 secSide
104             i32 thirdSide
105             i64 Color <static>
106         }
107     )";
108     sources_["Triangle"] = source;
109 
110     source = R"(
111         .language eTS
112         .record Foo {
113             i32 member
114         }
115         .record Bar {
116             Foo foo1
117             Foo foo2
118         }
119     )";
120     sources_["Foo"] = source;
121     sources_["Bar"] = source;
122 }
123 
GetTestClass(std::string className)124 EtsClass *EtsObjectTest::GetTestClass(std::string className)
125 {
126     std::unordered_map<std::string, const char *> sources = GetSources();
127     pandasm::Parser p;
128 
129     auto res = p.Parse(sources[className]);
130     auto pf = pandasm::AsmEmitter::Emit(res.Value());
131 
132     auto classLinker = Runtime::GetCurrent()->GetClassLinker();
133     classLinker->AddPandaFile(std::move(pf));
134 
135     className.insert(0, 1, 'L');
136     className.push_back(';');
137 
138     EtsClass *klass = coroutine_->GetPandaVM()->GetClassLinker()->GetClass(className.c_str());
139     ASSERT(klass);
140     return klass;
141 }
142 
TEST_F(EtsObjectTest,GetClass)143 TEST_F(EtsObjectTest, GetClass)
144 {
145     EtsClass *klass = nullptr;
146     EtsObject *obj = nullptr;
147 
148     for (const auto &it : GetSources()) {
149         klass = GetTestClass(it.first);
150         obj = EtsObject::Create(klass);
151         ASSERT_EQ(klass, obj->GetClass());
152     }
153 }
154 
TEST_F(EtsObjectTest,SetClass)155 TEST_F(EtsObjectTest, SetClass)
156 {
157     EtsClass *klass1 = GetTestClass("Rectangle");
158     EtsClass *klass2 = GetTestClass("Triangle");
159     EtsObject *obj = EtsObject::Create(klass1);
160     ASSERT_EQ(obj->GetClass(), klass1);
161     obj->SetClass(klass2);
162     ASSERT_EQ(obj->GetClass(), klass2);
163 }
164 
TEST_F(EtsObjectTest,IsInstanceOf)165 TEST_F(EtsObjectTest, IsInstanceOf)
166 {
167     EtsClass *klass1 = GetTestClass("Rectangle");
168     EtsClass *klass2 = GetTestClass("Triangle");
169     EtsObject *obj1 = EtsObject::Create(klass1);
170     EtsObject *obj2 = EtsObject::Create(klass2);
171 
172     ASSERT_TRUE(obj1->IsInstanceOf(klass1));
173     ASSERT_TRUE(obj2->IsInstanceOf(klass2));
174     ASSERT_FALSE(obj1->IsInstanceOf(klass2));
175     ASSERT_FALSE(obj2->IsInstanceOf(klass1));
176 }
177 
TEST_F(EtsObjectTest,GetAndSetFieldObject)178 TEST_F(EtsObjectTest, GetAndSetFieldObject)
179 {
180     EtsClass *barKlass = GetTestClass("Bar");
181     EtsClass *fooKlass = GetTestClass("Foo");
182 
183     EtsObject *barObj = EtsObject::Create(barKlass);
184     EtsObject *fooObj1 = EtsObject::Create(fooKlass);
185     EtsObject *fooObj2 = EtsObject::Create(fooKlass);
186 
187     EtsField *foo1Field = barKlass->GetFieldIDByName("foo1");
188     EtsField *foo2Field = barKlass->GetFieldIDByName("foo2");
189 
190     barObj->SetFieldObject(foo1Field, fooObj1);
191     barObj->SetFieldObject(foo2Field, fooObj2);
192     ASSERT_EQ(barObj->GetFieldObject(foo1Field), fooObj1);
193     ASSERT_EQ(barObj->GetFieldObject(foo2Field), fooObj2);
194 
195     EtsObject *res = barObj->GetAndSetFieldObject(foo2Field->GetOffset(), fooObj1, std::memory_order_relaxed);
196     ASSERT_EQ(res, fooObj2);                                // returned pointer was in foo2_field
197     ASSERT_EQ(barObj->GetFieldObject(foo2Field), fooObj1);  // now in foo2_field is pointer to Foo_obj1
198 
199     res = barObj->GetAndSetFieldObject(foo1Field->GetOffset(), fooObj2, std::memory_order_relaxed);
200     ASSERT_EQ(res, fooObj1);
201     ASSERT_EQ(barObj->GetFieldObject(foo1Field), fooObj2);
202 }
203 
TEST_F(EtsObjectTest,SetAndGetFieldObject)204 TEST_F(EtsObjectTest, SetAndGetFieldObject)
205 {
206     EtsClass *barKlass = GetTestClass("Bar");
207     EtsClass *fooKlass = GetTestClass("Foo");
208 
209     EtsObject *barObj = EtsObject::Create(barKlass);
210     EtsObject *fooObj1 = EtsObject::Create(fooKlass);
211     EtsObject *fooObj2 = EtsObject::Create(fooKlass);
212 
213     EtsField *foo1Field = barKlass->GetFieldIDByName("foo1");
214     EtsField *foo2Field = barKlass->GetFieldIDByName("foo2");
215 
216     barObj->SetFieldObject(foo1Field, fooObj1);
217     barObj->SetFieldObject(foo2Field, fooObj2);
218     ASSERT_EQ(barObj->GetFieldObject(foo1Field), fooObj1);
219     ASSERT_EQ(barObj->GetFieldObject(foo2Field), fooObj2);
220 
221     barObj->SetFieldObject(foo1Field, fooObj2);
222     ASSERT_EQ(barObj->GetFieldObject(foo1Field), fooObj2);
223     barObj->SetFieldObject(foo2Field, fooObj1);
224     ASSERT_EQ(barObj->GetFieldObject(foo2Field), fooObj1);
225 }
226 
TEST_F(EtsObjectTest,SetAndGetFieldPrimitive)227 TEST_F(EtsObjectTest, SetAndGetFieldPrimitive)
228 {
229     EtsClass *klass = GetTestClass("Rectangle");
230     EtsObject *obj = EtsObject::Create(klass);
231     EtsField *field = klass->GetFieldIDByName("Width");
232     int32_t testNmb1 = 77;
233     obj->SetFieldPrimitive<int32_t>(field, testNmb1);
234     ASSERT_EQ(obj->GetFieldPrimitive<int32_t>(field), testNmb1);
235 
236     field = klass->GetFieldIDByName("Height");
237     float testNmb2 = 111.11;
238     obj->SetFieldPrimitive<float>(field, testNmb2);
239     ASSERT_EQ(obj->GetFieldPrimitive<float>(field), testNmb2);
240 }
241 
TEST_F(EtsObjectTest,CompareAndSetFieldPrimitive)242 TEST_F(EtsObjectTest, CompareAndSetFieldPrimitive)
243 {
244     EtsClass *klass = GetTestClass("Rectangle");
245     EtsObject *obj = EtsObject::Create(klass);
246     EtsField *field = klass->GetFieldIDByName("Width");
247     int32_t firNmb = 134;
248     int32_t secNmb = 12;
249     obj->SetFieldPrimitive(field, firNmb);
250     obj->CompareAndSetFieldPrimitive(field->GetOffset(), firNmb, secNmb, std::memory_order_relaxed, true);
251     ASSERT_EQ(obj->GetFieldPrimitive<int32_t>(field), secNmb);
252 }
253 
TEST_F(EtsObjectTest,CompareAndSetFieldObject)254 TEST_F(EtsObjectTest, CompareAndSetFieldObject)
255 {
256     EtsClass *barKlass = GetTestClass("Bar");
257     EtsClass *fooKlass = GetTestClass("Foo");
258 
259     EtsObject *barObj = EtsObject::Create(barKlass);
260     EtsObject *fooObj1 = EtsObject::Create(fooKlass);
261     EtsObject *fooObj2 = EtsObject::Create(fooKlass);
262 
263     EtsField *foo1Field = barKlass->GetFieldIDByName("foo1");
264     EtsField *foo2Field = barKlass->GetFieldIDByName("foo2");
265 
266     barObj->SetFieldObject(foo1Field, fooObj1);
267     barObj->SetFieldObject(foo2Field, fooObj2);
268     ASSERT_EQ(barObj->GetFieldObject(foo1Field), fooObj1);
269     ASSERT_EQ(barObj->GetFieldObject(foo2Field), fooObj2);
270 
271     ASSERT_TRUE(
272         barObj->CompareAndSetFieldObject(foo1Field->GetOffset(), fooObj1, fooObj2, std::memory_order_relaxed, true));
273     ASSERT_EQ(barObj->GetFieldObject(foo1Field), fooObj2);
274 
275     ASSERT_TRUE(
276         barObj->CompareAndSetFieldObject(foo2Field->GetOffset(), fooObj2, fooObj1, std::memory_order_relaxed, true));
277     ASSERT_EQ(barObj->GetFieldObject(foo2Field), fooObj1);
278 }
279 
TEST_F(EtsObjectTest,GetHashCode_SingleThread)280 TEST_F(EtsObjectTest, GetHashCode_SingleThread)
281 {
282     // create class for testing
283     EtsClass *barKlass = GetTestClass("Bar");
284     EtsObject *obj = EtsObject::Create(barKlass);
285 
286     // after creation EtsObject should have no hash code
287     ASSERT_FALSE(obj->IsHashed());
288 
289     // First we will test work with hash only
290     // after getting hash it should be generated
291     auto hash = obj->GetHashCode();
292     ASSERT_TRUE(obj->IsHashed());
293     ASSERT_EQ(hash, obj->GetHashCode());
294 
295     // next we will test usage of ets object state table
296     // for this we should set interop hash
297     static constexpr uint32_t INTEROP_INDEX_VALUE = 42U;
298     obj->SetInteropIndex(INTEROP_INDEX_VALUE);
299     // switched hashed to used info
300     ASSERT_TRUE(obj->IsUsedInfo());
301     ASSERT_TRUE(obj->IsHashed());
302     ASSERT_TRUE(obj->HasInteropIndex());
303     // after this getting hash should works correct
304     ASSERT_EQ(hash, obj->GetHashCode());
305     ASSERT_EQ(INTEROP_INDEX_VALUE, obj->GetInteropIndex());
306 
307     // next we should test droping of interop hash
308     obj->DropInteropIndex();
309     ASSERT_TRUE(obj->IsUsedInfo());
310     ASSERT_TRUE(obj->IsHashed());
311     ASSERT_FALSE(obj->HasInteropIndex());
312     ASSERT_EQ(hash, obj->GetHashCode());
313 
314     // finally we deflect object
315     EtsCoroutine::GetCurrent()->GetVM()->FreeInternalResources();
316     ASSERT_TRUE(obj->IsHashed());
317     ASSERT_EQ(hash, obj->GetHashCode());
318 }
319 
TEST_F(EtsObjectTest,EtsMarkWordTest)320 TEST_F(EtsObjectTest, EtsMarkWordTest)
321 {
322     // create mark word for testing using object
323     EtsClass *barKlass = GetTestClass("Bar");
324     EtsObject *obj = EtsObject::Create(barKlass);
325     auto markWord = obj->GetMark();
326     ASSERT_EQ(markWord.GetState(), EtsMarkWord::STATE_UNLOCKED);
327 
328     // CC-OFFNXT(G.NAM.03) project code style
329     static constexpr uint32_t HASH_TO_DECODE = 12345U;
330     auto hashedMarkWord = markWord.DecodeFromHash(HASH_TO_DECODE);
331     ASSERT_EQ(hashedMarkWord.GetState(), EtsMarkWord::STATE_HASHED);
332     ASSERT_EQ(hashedMarkWord.GetHash(), HASH_TO_DECODE);
333 
334     // CC-OFFNXT(G.NAM.03) project code style
335     static constexpr uint32_t INTEROP_INDEX_TO_DECODE = 42U;
336     auto markWordWithInteropIndex = markWord.DecodeFromInteropIndex(INTEROP_INDEX_TO_DECODE);
337     ASSERT_EQ(markWordWithInteropIndex.GetState(), EtsMarkWord::STATE_HAS_INTEROP_INDEX);
338     ASSERT_EQ(markWordWithInteropIndex.GetInteropIndex(), INTEROP_INDEX_TO_DECODE);
339 
340     // CC-OFFNXT(G.NAM.03) project code style
341     static constexpr uint32_t USE_INFO_ID_TO_DECODE = 664U;
342     auto markWordWithInfo = markWord.DecodeFromInfo(USE_INFO_ID_TO_DECODE);
343     ASSERT_EQ(markWordWithInfo.GetState(), EtsMarkWord::STATE_USE_INFO);
344     ASSERT_EQ(markWordWithInfo.GetInfoId(), USE_INFO_ID_TO_DECODE);
345 }
346 
TEST_F(EtsObjectTest,SetGetAndDropInteropIndex_SingleThread)347 TEST_F(EtsObjectTest, SetGetAndDropInteropIndex_SingleThread)
348 {
349     // create class for testing
350     EtsClass *barKlass = GetTestClass("Bar");
351     EtsObject *obj = EtsObject::Create(barKlass);
352     // after creation EtsObject should have no hash code
353     ASSERT_FALSE(obj->IsHashed());
354     // we should set interop hash by object method
355     // CC-OFFNXT(G.NAM.03) project code style
356     static constexpr uint32_t INTEROP_INDEX_VALUE = 42U;
357     obj->SetInteropIndex(INTEROP_INDEX_VALUE);
358     ASSERT_TRUE(obj->HasInteropIndex());
359     ASSERT_EQ(obj->GetInteropIndex(), INTEROP_INDEX_VALUE);
360 
361     // Next test of interop hash droping
362     obj->DropInteropIndex();
363     ASSERT_FALSE(obj->IsHashed());
364     ASSERT_FALSE(obj->HasInteropIndex());
365 
366     // Next test of info table usage
367     obj->SetInteropIndex(INTEROP_INDEX_VALUE);
368     auto hash = obj->GetHashCode();
369     // object still should be hashed
370     ASSERT_TRUE(obj->IsUsedInfo());
371     ASSERT_TRUE(obj->IsHashed());
372     ASSERT_TRUE(obj->HasInteropIndex());
373     // after this getting interop hash should works correct
374     ASSERT_EQ(hash, obj->GetHashCode());
375     ASSERT_EQ(INTEROP_INDEX_VALUE, obj->GetInteropIndex());
376 
377     // next we should test droping of interop hash
378     obj->DropInteropIndex();
379     ASSERT_TRUE(obj->IsUsedInfo());
380     ASSERT_TRUE(obj->IsHashed());
381     ASSERT_FALSE(obj->HasInteropIndex());
382     ASSERT_EQ(hash, obj->GetHashCode());
383 
384     // finally we deflect object
385     EtsCoroutine::GetCurrent()->GetVM()->FreeInternalResources();
386     ASSERT_TRUE(obj->IsHashed());
387     ASSERT_TRUE(obj->IsHashed());
388     ASSERT_EQ(hash, obj->GetHashCode());
389 }
390 
TEST_F(EtsObjectTest,InteropIndex_MultiThread)391 TEST_F(EtsObjectTest, InteropIndex_MultiThread)
392 {
393     // CC-OFFNXT(G.NAM.03) project code style
394     static constexpr uint32_t ITERATION_COUNT = 1000;
395     for (size_t i = 0; i < ITERATION_COUNT; i++) {
396         // create class for testing
397         EtsClass *barKlass = GetTestClass("Bar");
398         EtsObject *obj = EtsObject::Create(barKlass);
399         std::atomic_bool finish = false;
400         auto interopHashGetter = [obj, &finish, coro = EtsCoroutine::GetCurrent()] {
401             EtsCoroutine::SetCurrent(coro);
402             // CC-OFFNXT(G.NAM.03) project code style
403             static constexpr uint32_t INTEROP_INDEX = 42U;
404             obj->SetInteropIndex(INTEROP_INDEX);
405             while (!finish) {
406                 ASSERT_EQ(INTEROP_INDEX, obj->GetInteropIndex());
407             }
408             obj->DropInteropIndex();
409             ASSERT_TRUE(obj->IsHashed());
410             ASSERT_FALSE(obj->HasInteropIndex());
411         };
412 
413         auto interopThread = std::thread(interopHashGetter);
414         auto hash = obj->GetHashCode();
415         ASSERT_EQ(hash, obj->GetHashCode());
416         finish = true;
417         interopThread.join();
418     }
419 }
420 
TEST_F(EtsObjectTest,EtsHash_MultiThread)421 TEST_F(EtsObjectTest, EtsHash_MultiThread)
422 {
423     // CC-OFFNXT(G.NAM.03) project code style
424     static constexpr uint32_t ITERATION_COUNT = 1000;
425     for (size_t i = 0; i < ITERATION_COUNT; i++) {
426         // create class for testing
427         EtsClass *barKlass = GetTestClass("Bar");
428         EtsObject *obj = EtsObject::Create(barKlass);
429         std::atomic_bool finish = false;
430         auto interopHashGetter = [obj, &finish, coro = EtsCoroutine::GetCurrent()] {
431             EtsCoroutine::SetCurrent(coro);
432             auto hash = obj->GetHashCode();
433             while (!finish) {
434                 ASSERT_EQ(hash, obj->GetHashCode());
435             }
436             ASSERT_TRUE(obj->HasInteropIndex());
437         };
438 
439         auto interopThread = std::thread(interopHashGetter);
440         // CC-OFFNXT(G.NAM.03) project code style
441         static constexpr uint32_t INTEROP_INDEX = 42U;
442         obj->SetInteropIndex(INTEROP_INDEX);
443         ASSERT_EQ(INTEROP_INDEX, obj->GetInteropIndex());
444         finish = true;
445         interopThread.join();
446         obj->DropInteropIndex();
447         ASSERT_TRUE(obj->IsUsedInfo());
448         EtsCoroutine::GetCurrent()->GetVM()->FreeInternalResources();
449         ASSERT_TRUE(obj->IsHashed());
450     }
451 }
452 
453 }  // namespace ark::ets::test
454 
455 // NOLINTEND(readability-magic-numbers)
456