• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2024-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 
18 #include "get_test_class.h"
19 #include "ets_coroutine.h"
20 
21 #include "types/ets_class.h"
22 #include "types/ets_method.h"
23 
24 // NOLINTBEGIN(readability-magic-numbers)
25 
26 namespace ark::ets::test {
27 
28 namespace {
29 
InSamePackagePrologue()30 void InSamePackagePrologue()
31 {
32     {
33         const char *source = R"(
34             .language eTS
35             .record Test {}
36         )";
37 
38         EtsClass *klass = GetTestClass(source, "LTest;");
39         ASSERT_NE(klass, nullptr);
40 
41         EtsArray *array1 = EtsObjectArray::Create(klass, 1000U);
42         EtsArray *array2 = EtsFloatArray::Create(1000U);
43         ASSERT_NE(array1, nullptr);
44         ASSERT_NE(array2, nullptr);
45 
46         EtsClass *klass1 = array1->GetClass();
47         EtsClass *klass2 = array2->GetClass();
48         ASSERT_NE(klass1, nullptr);
49         ASSERT_NE(klass2, nullptr);
50 
51         ASSERT_TRUE(klass1->IsInSamePackage(klass2));
52     }
53     {
54         EtsArray *array1 = EtsFloatArray::Create(1000U);
55         EtsArray *array2 = EtsIntArray::Create(1000U);
56         ASSERT_NE(array1, nullptr);
57         ASSERT_NE(array2, nullptr);
58 
59         EtsClass *klass1 = array1->GetClass();
60         EtsClass *klass2 = array2->GetClass();
61         ASSERT_NE(klass1, nullptr);
62         ASSERT_NE(klass2, nullptr);
63 
64         ASSERT_TRUE(klass1->IsInSamePackage(klass2));
65     }
66 }
67 
68 }  // namespace
69 
70 class EtsClassTest : public testing::Test {
71 public:
EtsClassTest()72     EtsClassTest()
73     {
74         options_.SetShouldLoadBootPandaFiles(true);
75         options_.SetShouldInitializeIntrinsics(false);
76         options_.SetCompilerEnableJit(false);
77         options_.SetGcType("epsilon");
78         options_.SetLoadRuntimes({"ets"});
79 
80         auto stdlib = std::getenv("PANDA_STD_LIB");
81         if (stdlib == nullptr) {
82             std::cerr << "PANDA_STD_LIB env variable should be set and point to mock_stdlib.abc" << std::endl;
83             std::abort();
84         }
85         options_.SetBootPandaFiles({stdlib});
86 
87         Runtime::Create(options_);
88     }
89 
~EtsClassTest()90     ~EtsClassTest() override
91     {
92         Runtime::Destroy();
93     }
94 
95     NO_COPY_SEMANTIC(EtsClassTest);
96     NO_MOVE_SEMANTIC(EtsClassTest);
97 
SetUp()98     void SetUp() override
99     {
100         coroutine_ = EtsCoroutine::GetCurrent();
101         coroutine_->ManagedCodeBegin();
102     }
103 
TearDown()104     void TearDown() override
105     {
106         coroutine_->ManagedCodeEnd();
107     }
108 
109 private:
110     RuntimeOptions options_;
111     EtsCoroutine *coroutine_ = nullptr;
112 };
113 
TEST_F(EtsClassTest,GetBase)114 TEST_F(EtsClassTest, GetBase)
115 {
116     {
117         const char *source = R"(
118             .language eTS
119             .record A {}
120             .record B <ets.extends = A> {}
121             .record C <ets.extends = B> {}
122         )";
123 
124         EtsClass *klassA = GetTestClass(source, "LA;");
125         EtsClass *klassB = GetTestClass(source, "LB;");
126         EtsClass *klassC = GetTestClass(source, "LC;");
127         ASSERT_NE(klassA, nullptr);
128         ASSERT_NE(klassB, nullptr);
129         ASSERT_NE(klassC, nullptr);
130 
131         ASSERT_EQ(klassC->GetBase(), klassB);
132         ASSERT_EQ(klassB->GetBase(), klassA);
133         ASSERT_STREQ(klassA->GetBase()->GetDescriptor(), "Lstd/core/Object;");
134     }
135     {
136         const char *source = R"(
137             .language eTS
138             .record ITest <ets.abstract, ets.interface>{}
139         )";
140 
141         EtsClass *klassItest = GetTestClass(source, "LITest;");
142 
143         ASSERT_NE(klassItest, nullptr);
144         ASSERT_TRUE(klassItest->IsInterface());
145         ASSERT_EQ(klassItest->GetBase(), nullptr);
146     }
147 }
148 
TEST_F(EtsClassTest,GetStaticFieldsNumber)149 TEST_F(EtsClassTest, GetStaticFieldsNumber)
150 {
151     {
152         const char *source = R"(
153             .language eTS
154             .record A {
155                 f32 x <static>
156                 i32 y <static>
157                 f32 z <static>
158             }
159         )";
160 
161         EtsClass *klass = GetTestClass(source, "LA;");
162         ASSERT_NE(klass, nullptr);
163         ASSERT_EQ(klass->GetStaticFieldsNumber(), 3U);
164         ASSERT_EQ(klass->GetInstanceFieldsNumber(), 0);
165     }
166     {
167         const char *source = R"(
168             .language eTS
169             .record B {}
170         )";
171 
172         EtsClass *klass = GetTestClass(source, "LB;");
173         ASSERT_NE(klass, nullptr);
174         ASSERT_EQ(klass->GetStaticFieldsNumber(), 0);
175         ASSERT_EQ(klass->GetInstanceFieldsNumber(), 0);
176     }
177 }
178 
TEST_F(EtsClassTest,GetInstanceFieldsNumber)179 TEST_F(EtsClassTest, GetInstanceFieldsNumber)
180 {
181     {
182         const char *source = R"(
183             .language eTS
184             .record TestObject {}
185             .record A {
186                 TestObject x
187                 TestObject y
188                 TestObject z
189                 TestObject k
190             }
191         )";
192 
193         EtsClass *klass = GetTestClass(source, "LA;");
194         ASSERT_NE(klass, nullptr);
195         ASSERT_EQ(klass->GetStaticFieldsNumber(), 0);
196         ASSERT_EQ(klass->GetInstanceFieldsNumber(), 4U);
197     }
198     {
199         const char *source = R"(
200             .language eTS
201             .record B {
202                 f32 x <static>
203             }
204         )";
205 
206         EtsClass *klass = GetTestClass(source, "LB;");
207         ASSERT_NE(klass, nullptr);
208         ASSERT_EQ(klass->GetStaticFieldsNumber(), 1);
209         ASSERT_EQ(klass->GetInstanceFieldsNumber(), 0);
210     }
211 }
212 
TEST_F(EtsClassTest,GetFieldIndexByName)213 TEST_F(EtsClassTest, GetFieldIndexByName)
214 {
215     const char *source = R"(
216             .language eTS
217             .record TestObject {}
218             .record A {
219                 TestObject obj_x
220                 TestObject obj_y
221                 TestObject obj_z
222                 TestObject obj_k
223             }
224         )";
225 
226     EtsClass *klass = GetTestClass(source, "LA;");
227     ASSERT_NE(klass, nullptr);
228 
229     std::unordered_set<std::uint32_t> descSetIndex = {0, 1, 2, 3};
230     ASSERT_TRUE(descSetIndex.count(klass->GetFieldIndexByName("obj_x")) > 0);
231     descSetIndex.erase(klass->GetFieldIndexByName("obj_x"));
232     ASSERT_TRUE(descSetIndex.count(klass->GetFieldIndexByName("obj_k")) > 0);
233     descSetIndex.erase(klass->GetFieldIndexByName("obj_k"));
234     ASSERT_TRUE(descSetIndex.count(klass->GetFieldIndexByName("obj_p")) == 0);
235 }
236 
TEST_F(EtsClassTest,GetFieldIDByName)237 TEST_F(EtsClassTest, GetFieldIDByName)
238 {
239     const char *source = R"(
240         .language eTS
241         .record TestObject {}
242         .record Test {
243             TestObject x
244             i32 y
245         }
246     )";
247 
248     EtsClass *klass = GetTestClass(source, "LTest;");
249     ASSERT_NE(klass, nullptr);
250 
251     ASSERT_EQ(klass->GetFieldIDByName("x", "F"), nullptr);
252     ASSERT_NE(klass->GetFieldIDByName("x", "LTestObject;"), nullptr);
253     ASSERT_EQ(klass->GetFieldIDByName("y", "Z"), nullptr);
254     ASSERT_NE(klass->GetFieldIDByName("y", "I"), nullptr);
255 
256     EtsField *fieldX = klass->GetFieldIDByName("x");
257     EtsField *fieldY = klass->GetFieldIDByName("y");
258     ASSERT_NE(fieldX, nullptr);
259     ASSERT_NE(fieldY, nullptr);
260     ASSERT_FALSE(fieldX->IsStatic());
261     ASSERT_FALSE(fieldY->IsStatic());
262 
263     ASSERT_EQ(fieldX, klass->GetFieldIDByOffset(fieldX->GetOffset()));
264     ASSERT_EQ(fieldY, klass->GetFieldIDByOffset(fieldY->GetOffset()));
265 }
266 
TEST_F(EtsClassTest,GetDeclaredFieldIDByName)267 TEST_F(EtsClassTest, GetDeclaredFieldIDByName)
268 {
269     const char *source = R"(
270         .language eTS
271         .record Ball {
272             f32 BALL_RADIUS
273             i32 BALL_SPEED
274             f32 vx
275             i32 vy
276         }
277     )";
278 
279     EtsClass *klass = GetTestClass(source, "LBall;");
280     ASSERT_NE(klass, nullptr);
281 
282     EtsField *field1 = klass->GetDeclaredFieldIDByName("BALL_RADIUS");
283     ASSERT_NE(field1, nullptr);
284     ASSERT_TRUE(!strcmp(field1->GetName(), "BALL_RADIUS"));
285 
286     EtsField *field2 = klass->GetDeclaredFieldIDByName("BALL_NUM");
287     ASSERT_EQ(field2, nullptr);
288 
289     EtsField *field3 = klass->GetDeclaredFieldIDByName("vx");
290     ASSERT_NE(field3, nullptr);
291     ASSERT_TRUE(!strcmp(field3->GetName(), "vx"));
292 
293     ASSERT_EQ(field1, klass->GetFieldIDByOffset(field1->GetOffset()));
294     ASSERT_EQ(field3, klass->GetFieldIDByOffset(field3->GetOffset()));
295 }
296 
TEST_F(EtsClassTest,GetStaticFieldIDByName)297 TEST_F(EtsClassTest, GetStaticFieldIDByName)
298 {
299     const char *source = R"(
300         .language eTS
301         .record TestObject {}
302         .record Test {
303             TestObject x <static>
304             i32 y <static>
305         }
306     )";
307 
308     EtsClass *klass = GetTestClass(source, "LTest;");
309     ASSERT_NE(klass, nullptr);
310 
311     ASSERT_EQ(klass->GetStaticFieldIDByName("x", "F"), nullptr);
312     ASSERT_NE(klass->GetStaticFieldIDByName("x", "LTestObject;"), nullptr);
313     ASSERT_EQ(klass->GetStaticFieldIDByName("y", "Z"), nullptr);
314     ASSERT_NE(klass->GetStaticFieldIDByName("y", "I"), nullptr);
315 
316     EtsField *fieldX = klass->GetStaticFieldIDByName("x");
317     EtsField *fieldY = klass->GetStaticFieldIDByName("y");
318     ASSERT_NE(fieldX, nullptr);
319     ASSERT_NE(fieldY, nullptr);
320     ASSERT_TRUE(fieldX->IsStatic());
321     ASSERT_TRUE(fieldY->IsStatic());
322 
323     ASSERT_EQ(fieldX, klass->GetStaticFieldIDByOffset(fieldX->GetOffset()));
324     ASSERT_EQ(fieldY, klass->GetStaticFieldIDByOffset(fieldY->GetOffset()));
325 }
326 
TEST_F(EtsClassTest,SetAndGetStaticFieldPrimitive)327 TEST_F(EtsClassTest, SetAndGetStaticFieldPrimitive)
328 {
329     const char *source = R"(
330         .language eTS
331         .record Test {
332             i32 x <static>
333             f32 y <static>
334         }
335     )";
336 
337     EtsClass *klass = GetTestClass(source, "LTest;");
338     ASSERT_NE(klass, nullptr);
339 
340     EtsField *fieldX = klass->GetStaticFieldIDByName("x");
341     EtsField *fieldY = klass->GetStaticFieldIDByName("y");
342     ASSERT_NE(fieldX, nullptr);
343     ASSERT_NE(fieldY, nullptr);
344 
345     int32_t nmb1 = 53;
346     float nmb2 = 123.90;
347 
348     klass->SetStaticFieldPrimitive<int32_t>(fieldX, nmb1);
349     klass->SetStaticFieldPrimitive<float>(fieldY, nmb2);
350 
351     ASSERT_EQ(klass->GetStaticFieldPrimitive<int32_t>(fieldX), nmb1);
352     ASSERT_EQ(klass->GetStaticFieldPrimitive<float>(fieldY), nmb2);
353 }
354 
TEST_F(EtsClassTest,SetAndGetStaticFieldPrimitiveByOffset)355 TEST_F(EtsClassTest, SetAndGetStaticFieldPrimitiveByOffset)
356 {
357     const char *source = R"(
358         .language eTS
359         .record Test {
360             i32 x <static>
361             f32 y <static>
362         }
363     )";
364 
365     EtsClass *klass = GetTestClass(source, "LTest;");
366     ASSERT_NE(klass, nullptr);
367 
368     std::size_t fieldXOffset = klass->GetStaticFieldIDByName("x")->GetOffset();
369     std::size_t fieldYOffset = klass->GetStaticFieldIDByName("y")->GetOffset();
370 
371     int32_t nmb1 = 53;
372     float nmb2 = 123.90;
373 
374     klass->SetStaticFieldPrimitive<int32_t>(fieldXOffset, false, nmb1);
375     klass->SetStaticFieldPrimitive<float>(fieldYOffset, false, nmb2);
376 
377     ASSERT_EQ(klass->GetStaticFieldPrimitive<int32_t>(fieldXOffset, false), nmb1);
378     ASSERT_EQ(klass->GetStaticFieldPrimitive<float>(fieldYOffset, false), nmb2);
379 }
380 
TEST_F(EtsClassTest,SetAndGetFieldObject)381 TEST_F(EtsClassTest, SetAndGetFieldObject)
382 {
383     const char *source = R"(
384         .language eTS
385         .record Foo {}
386         .record Bar {
387             Foo x <static>
388             Foo y <static>
389         }
390     )";
391 
392     EtsClass *barKlass = GetTestClass(source, "LBar;");
393     EtsClass *fooKlass = GetTestClass(source, "LFoo;");
394     ASSERT_NE(barKlass, nullptr);
395     ASSERT_NE(fooKlass, nullptr);
396 
397     EtsObject *fooObj1 = EtsObject::Create(fooKlass);
398     EtsObject *fooObj2 = EtsObject::Create(fooKlass);
399     ASSERT_NE(fooObj1, nullptr);
400     ASSERT_NE(fooObj2, nullptr);
401 
402     EtsField *fieldX = barKlass->GetStaticFieldIDByName("x");
403     EtsField *fieldY = barKlass->GetStaticFieldIDByName("y");
404     ASSERT_NE(fieldX, nullptr);
405     ASSERT_NE(fieldY, nullptr);
406 
407     barKlass->SetStaticFieldObject(fieldX, fooObj1);
408     barKlass->SetStaticFieldObject(fieldY, fooObj2);
409     ASSERT_EQ(barKlass->GetStaticFieldObject(fieldX), fooObj1);
410     ASSERT_EQ(barKlass->GetStaticFieldObject(fieldY), fooObj2);
411 
412     barKlass->SetStaticFieldObject(fieldX, fooObj2);
413     barKlass->SetStaticFieldObject(fieldY, fooObj1);
414     ASSERT_EQ(barKlass->GetStaticFieldObject(fieldX), fooObj2);
415     ASSERT_EQ(barKlass->GetStaticFieldObject(fieldY), fooObj1);
416 }
417 
TEST_F(EtsClassTest,SetAndGetFieldObjectByOffset)418 TEST_F(EtsClassTest, SetAndGetFieldObjectByOffset)
419 {
420     const char *source = R"(
421         .language eTS
422         .record Foo {}
423         .record Bar {
424             Foo x <static>
425             Foo y <static>
426         }
427     )";
428 
429     EtsClass *barKlass = GetTestClass(source, "LBar;");
430     EtsClass *fooKlass = GetTestClass(source, "LFoo;");
431     ASSERT_NE(barKlass, nullptr);
432     ASSERT_NE(fooKlass, nullptr);
433 
434     EtsObject *fooObj1 = EtsObject::Create(fooKlass);
435     EtsObject *fooObj2 = EtsObject::Create(fooKlass);
436     ASSERT_NE(fooObj1, nullptr);
437     ASSERT_NE(fooObj2, nullptr);
438 
439     EtsField *fieldX = barKlass->GetStaticFieldIDByName("x");
440     EtsField *fieldY = barKlass->GetStaticFieldIDByName("y");
441     ASSERT_NE(fieldX, nullptr);
442     ASSERT_NE(fieldY, nullptr);
443 
444     barKlass->SetStaticFieldObject(fieldX->GetOffset(), false, fooObj1);
445     barKlass->SetStaticFieldObject(fieldY->GetOffset(), false, fooObj2);
446     ASSERT_EQ(barKlass->GetStaticFieldObject(fieldX->GetOffset(), false), fooObj1);
447     ASSERT_EQ(barKlass->GetStaticFieldObject(fieldY->GetOffset(), false), fooObj2);
448 
449     barKlass->SetStaticFieldObject(fieldX, fooObj2);
450     barKlass->SetStaticFieldObject(fieldY, fooObj1);
451     ASSERT_EQ(barKlass->GetStaticFieldObject(fieldX), fooObj2);
452     ASSERT_EQ(barKlass->GetStaticFieldObject(fieldY), fooObj1);
453 }
454 
TEST_F(EtsClassTest,GetDirectMethod)455 TEST_F(EtsClassTest, GetDirectMethod)
456 {
457     const char *source = R"(
458         .language eTS
459         .record A {}
460         .record B <ets.extends = A> {}
461         .record TestObject {}
462 
463         .function void A.foo1() {
464             return.void
465         }
466         .function TestObject B.foo2(i32 a0, i32 a1, f32 a2, f64 a3, f32 a4) {
467             return
468         }
469     )";
470 
471     EtsClass *klassA = GetTestClass(source, "LA;");
472     EtsClass *klassB = GetTestClass(source, "LB;");
473     ASSERT_NE(klassA, nullptr);
474     ASSERT_NE(klassB, nullptr);
475 
476     EtsMethod *methodFoo1 = klassA->GetDirectMethod("foo1", ":V");
477     ASSERT_NE(methodFoo1, nullptr);
478     ASSERT_TRUE(!strcmp(methodFoo1->GetName(), "foo1"));
479 
480     EtsMethod *methodFoo2 = klassB->GetDirectMethod("foo2", "IIFDF:LTestObject;");
481     ASSERT_NE(methodFoo2, nullptr);
482     ASSERT_TRUE(!strcmp(methodFoo2->GetName(), "foo2"));
483 
484     // GetDirectMethod can't find method from base class
485     EtsMethod *methodFoo1FromKlassB = klassB->GetDirectMethod("foo1", ":V");
486     ASSERT_EQ(methodFoo1FromKlassB, nullptr);
487 }
488 
TEST_F(EtsClassTest,GetMethod)489 TEST_F(EtsClassTest, GetMethod)
490 {
491     const char *source = R"(
492         .language eTS
493         .record A {}
494         .record B <ets.extends = A> {}
495         .record TestObject {}
496 
497         .function void A.foo1() {
498             return.void
499         }
500         .function TestObject B.foo2(i32 a0, i32 a1, f32 a2, f64 a3, f32 a4) {
501             return
502         }
503     )";
504 
505     EtsClass *klassA = GetTestClass(source, "LA;");
506     EtsClass *klassB = GetTestClass(source, "LB;");
507     ASSERT_NE(klassA, nullptr);
508     ASSERT_NE(klassB, nullptr);
509 
510     EtsMethod *methodFoo1 = klassA->GetStaticMethod("foo1", ":V");
511     ASSERT_NE(methodFoo1, nullptr);
512     ASSERT_TRUE(!strcmp(methodFoo1->GetName(), "foo1"));
513 
514     EtsMethod *methodFoo2 = klassB->GetStaticMethod("foo2", "IIFDF:LTestObject;");
515     ASSERT_NE(methodFoo2, nullptr);
516     ASSERT_TRUE(!strcmp(methodFoo2->GetName(), "foo2"));
517 
518     // GetMethod can find method from base class
519     EtsMethod *methodFoo1FromKlassB = klassB->GetStaticMethod("foo1", nullptr);
520     ASSERT_NE(methodFoo1FromKlassB, nullptr);
521     ASSERT_TRUE(!strcmp(methodFoo1FromKlassB->GetName(), "foo1"));
522 }
523 
TEST_F(EtsClassTest,GetMethodByIndex)524 TEST_F(EtsClassTest, GetMethodByIndex)
525 {
526     const char *source = R"(
527         .language eTS
528         .record N {}
529         .record A {}
530         .record B <ets.extends = A> {}
531         .record TestObject {}
532 
533         .function void A.foo1() {
534             return.void
535         }
536         .function void A.foo2() {
537             return.void
538         }
539         .function TestObject B.foo3(i32 a0, i32 a1, f32 a2, f64 a3, f32 a4) {
540             return
541         }
542     )";
543 
544     EtsClass *klassA = GetTestClass(source, "LA;");
545     EtsClass *klassB = GetTestClass(source, "LB;");
546     ASSERT_NE(klassA, nullptr);
547     ASSERT_NE(klassB, nullptr);
548 
549     // test Method:GetMethodsNum
550     std::size_t klassAMethodSize = 2;
551     std::size_t klassBMethodSize = 3;
552     ASSERT_EQ(klassAMethodSize, klassA->GetMethodsNum());
553     ASSERT_EQ(klassBMethodSize, klassB->GetMethodsNum());
554 
555     EtsClass *klassN = GetTestClass(source, "LN;");
556     ASSERT_NE(klassN, nullptr);
557     ASSERT_FALSE(klassN->GetMethodsNum());
558 
559     std::unordered_set<std::string> descSetA = {"foo1", "foo2"};
560     for (std::size_t i = 0; i < klassAMethodSize; ++i) {
561         ASSERT_TRUE(descSetA.count(klassA->GetMethodByIndex(i)->GetName()) > 0);
562         descSetA.erase(klassA->GetMethodByIndex(i)->GetName());
563     }
564 
565     std::unordered_set<std::string> descSetB = {"foo1", "foo2", "foo3"};
566     for (std::size_t i = 0; i < klassBMethodSize; ++i) {
567         ASSERT_TRUE(descSetB.count(klassB->GetMethodByIndex(i)->GetName()) > 0);
568         descSetB.erase(klassB->GetMethodByIndex(i)->GetName());
569     }
570 }
571 
TEST_F(EtsClassTest,GetMethods)572 TEST_F(EtsClassTest, GetMethods)
573 {
574     const char *source = R"(
575         .language eTS
576         .record N {}
577         .record A {}
578         .record B <ets.extends = A> {}
579         .record TestObject {}
580 
581         .function void A.foo1() {
582             return.void
583         }
584         .function void A.foo2() {
585             return.void
586         }
587         .function TestObject B.foo3(i32 a0, i32 a1, f32 a2, f64 a3, f32 a4) {
588             return
589         }
590     )";
591 
592     EtsClass *klassA = GetTestClass(source, "LA;");
593     EtsClass *klassB = GetTestClass(source, "LB;");
594     ASSERT_NE(klassA, nullptr);
595     ASSERT_NE(klassB, nullptr);
596 
597     std::unordered_set<std::string> descSetA = {"foo1", "foo2"};
598     auto etsMethodsA = PandaVector<EtsMethod *>();
599     etsMethodsA = klassA->GetMethods();
600     std::size_t etsMethodsASize = etsMethodsA.size();
601     for (std::size_t i = 0; i < etsMethodsASize; ++i) {
602         ASSERT_TRUE(descSetA.count(etsMethodsA.at(i)->GetName()) > 0);
603         descSetA.erase(etsMethodsA.at(i)->GetName());
604     }
605 
606     std::unordered_set<std::string> descSetB = {"foo1", "foo2", "foo3"};
607     auto etsMethodsB = PandaVector<EtsMethod *>();
608     etsMethodsB = klassB->GetMethods();
609     std::size_t etsMethodsBSize = etsMethodsB.size();
610     for (std::size_t i = 0; i < etsMethodsBSize; ++i) {
611         ASSERT_TRUE(descSetB.count(etsMethodsB.at(i)->GetName()) > 0);
612         descSetB.erase(etsMethodsB.at(i)->GetName());
613     }
614 
615     EtsClass *klassN = GetTestClass(source, "LN;");
616     ASSERT_NE(klassN, nullptr);
617     auto etsMethodsC = PandaVector<EtsMethod *>();
618     etsMethodsC = klassN->GetMethods();
619     ASSERT_TRUE(etsMethodsC.empty());
620 }
621 
TestGetPrimitiveClass(const char * primitiveName)622 static void TestGetPrimitiveClass(const char *primitiveName)
623 {
624     EtsString *inputName = EtsString::CreateFromMUtf8(primitiveName);
625     ASSERT_NE(inputName, nullptr);
626 
627     auto *klass = EtsClass::GetPrimitiveClass(inputName);
628     ASSERT_NE(klass, nullptr);
629 
630     ASSERT_TRUE(inputName->StringsAreEqual(klass->GetName()->AsObject()));
631 }
632 
TEST_F(EtsClassTest,GetPrimitiveClass)633 TEST_F(EtsClassTest, GetPrimitiveClass)
634 {
635     TestGetPrimitiveClass("void");
636     TestGetPrimitiveClass("boolean");
637     TestGetPrimitiveClass("byte");
638     TestGetPrimitiveClass("char");
639     TestGetPrimitiveClass("short");
640     TestGetPrimitiveClass("int");
641     TestGetPrimitiveClass("long");
642     TestGetPrimitiveClass("float");
643     TestGetPrimitiveClass("double");
644 }
645 
TEST_F(EtsClassTest,SetAndGetName)646 TEST_F(EtsClassTest, SetAndGetName)
647 {
648     const char *source = R"(
649         .language eTS
650         .record A {}
651         .record AB {}
652         .record ABC {}
653     )";
654 
655     EtsClass *klass = GetTestClass(source, "LA;");
656     ASSERT_NE(klass, nullptr);
657     ASSERT_TRUE(klass->GetName()->StringsAreEqual(EtsString::CreateFromMUtf8("A")->AsObject()));
658 
659     klass = GetTestClass(source, "LAB;");
660     ASSERT_NE(klass, nullptr);
661     ASSERT_TRUE(klass->GetName()->StringsAreEqual(EtsString::CreateFromMUtf8("AB")->AsObject()));
662 
663     klass = GetTestClass(source, "LABC;");
664     ASSERT_NE(klass, nullptr);
665     ASSERT_TRUE(klass->GetName()->StringsAreEqual(EtsString::CreateFromMUtf8("ABC")->AsObject()));
666 
667     EtsString *name = EtsString::CreateFromMUtf8("TestNameA");
668     klass->SetName(name);
669     ASSERT_TRUE(klass->GetName()->StringsAreEqual(name->AsObject()));
670 
671     name = EtsString::CreateFromMUtf8("TestNameB");
672     klass->SetName(name);
673     ASSERT_TRUE(klass->GetName()->StringsAreEqual(name->AsObject()));
674 
675     name = EtsString::CreateFromMUtf8("TestNameC");
676     klass->SetName(name);
677     ASSERT_TRUE(klass->GetName()->StringsAreEqual(name->AsObject()));
678 }
679 
TEST_F(EtsClassTest,CompareAndSetName)680 TEST_F(EtsClassTest, CompareAndSetName)
681 {
682     const char *source = R"(
683         .language eTS
684         .record Test {}
685     )";
686 
687     EtsClass *klass = GetTestClass(source, "LTest;");
688     ASSERT_NE(klass, nullptr);
689 
690     EtsString *oldName = EtsString::CreateFromMUtf8("TestOldName");
691     EtsString *newName = EtsString::CreateFromMUtf8("TestNewName");
692     ASSERT_NE(oldName, nullptr);
693     ASSERT_NE(newName, nullptr);
694 
695     klass->SetName(oldName);
696 
697     ASSERT_TRUE(klass->CompareAndSetName(oldName, newName));
698     ASSERT_TRUE(klass->GetName()->StringsAreEqual(newName->AsObject()));
699 
700     ASSERT_TRUE(klass->CompareAndSetName(newName, oldName));
701     ASSERT_TRUE(klass->GetName()->StringsAreEqual(oldName->AsObject()));
702 }
703 
TEST_F(EtsClassTest,IsInSamePackage)704 TEST_F(EtsClassTest, IsInSamePackage)
705 {
706     ASSERT_TRUE(EtsClass::IsInSamePackage("LA;", "LA;"));
707     ASSERT_TRUE(EtsClass::IsInSamePackage("LA/B;", "LA/B;"));
708     ASSERT_TRUE(EtsClass::IsInSamePackage("LA/B/C;", "LA/B/C;"));
709 
710     ASSERT_FALSE(EtsClass::IsInSamePackage("LA/B;", "LA/B/C;"));
711     ASSERT_FALSE(EtsClass::IsInSamePackage("LA/B/C;", "LA/B;"));
712 
713     InSamePackagePrologue();
714 
715     {
716         const char *source = R"(
717             .language eTS
718             .record Test.A {}
719             .record Test.B {}
720             .record Fake.A {}
721         )";
722 
723         EtsClass *klassTestA = GetTestClass(source, "LTest/A;");
724         EtsClass *klassTestB = GetTestClass(source, "LTest/B;");
725         EtsClass *klassFakeA = GetTestClass(source, "LFake/A;");
726         ASSERT_NE(klassTestA, nullptr);
727         ASSERT_NE(klassTestB, nullptr);
728         ASSERT_NE(klassFakeA, nullptr);
729 
730         ASSERT_TRUE(klassTestA->IsInSamePackage(klassTestA));
731         ASSERT_TRUE(klassTestB->IsInSamePackage(klassTestB));
732         ASSERT_TRUE(klassFakeA->IsInSamePackage(klassFakeA));
733 
734         ASSERT_TRUE(klassTestA->IsInSamePackage(klassTestB));
735         ASSERT_TRUE(klassTestB->IsInSamePackage(klassTestA));
736 
737         ASSERT_FALSE(klassTestA->IsInSamePackage(klassFakeA));
738         ASSERT_FALSE(klassFakeA->IsInSamePackage(klassTestA));
739     }
740     {
741         const char *source = R"(
742             .language eTS
743             .record A.B.C {}
744             .record A.B {}
745         )";
746 
747         EtsClass *klassAbc = GetTestClass(source, "LA/B/C;");
748         EtsClass *klassAb = GetTestClass(source, "LA/B;");
749         ASSERT_NE(klassAbc, nullptr);
750         ASSERT_NE(klassAb, nullptr);
751 
752         ASSERT_FALSE(klassAbc->IsInSamePackage(klassAb));
753         ASSERT_FALSE(klassAb->IsInSamePackage(klassAbc));
754     }
755 }
756 
TEST_F(EtsClassTest,SetAndGetSuperClass)757 TEST_F(EtsClassTest, SetAndGetSuperClass)
758 {
759     const char *sources = R"(
760         .language eTS
761         .record A {}
762         .record B {}
763     )";
764 
765     EtsClass *klass = GetTestClass(sources, "LA;");
766     EtsClass *superKlass = GetTestClass(sources, "LB;");
767     ASSERT_NE(klass, nullptr);
768     ASSERT_NE(superKlass, nullptr);
769 
770     ASSERT_EQ(klass->GetSuperClass(), PandaEtsVM::GetCurrent()->GetClassLinker()->GetClassRoot(EtsClassRoot::OBJECT));
771     klass->SetSuperClass(superKlass);
772     ASSERT_EQ(klass->GetSuperClass(), superKlass);
773 }
774 
TEST_F(EtsClassTest,IsSubClass)775 TEST_F(EtsClassTest, IsSubClass)
776 {
777     const char *source = R"(
778         .language eTS
779         .record A {}
780         .record B <ets.extends = A> {}
781     )";
782     EtsClass *klassA = GetTestClass(source, "LA;");
783     EtsClass *klassB = GetTestClass(source, "LB;");
784     ASSERT_NE(klassA, nullptr);
785     ASSERT_NE(klassB, nullptr);
786 
787     ASSERT_TRUE(klassB->IsSubClass(klassA));
788 
789     ASSERT_TRUE(klassA->IsSubClass(klassA));
790     ASSERT_TRUE(klassB->IsSubClass(klassB));
791 }
792 
TEST_F(EtsClassTest,SetAndGetFlags)793 TEST_F(EtsClassTest, SetAndGetFlags)
794 {
795     const char *source = R"(
796         .language eTS
797         .record Test {}
798     )";
799 
800     EtsClass *klass = GetTestClass(source, "LTest;");
801     ASSERT_NE(klass, nullptr);
802 
803     uint32_t flags = 0U | 1U << 17U | 1U << 18U;
804 
805     klass->SetFlags(flags);
806     ASSERT_EQ(klass->GetFlags(), flags);
807     ASSERT_TRUE(klass->IsReference());
808     ASSERT_TRUE(klass->IsWeakReference());
809     ASSERT_TRUE(klass->IsFinalizerReference());
810 }
811 
TEST_F(EtsClassTest,SetAndGetComponentType)812 TEST_F(EtsClassTest, SetAndGetComponentType)
813 {
814     const char *source = R"(
815         .language eTS
816         .record Test {}
817     )";
818 
819     EtsClass *klass = GetTestClass(source, "LTest;");
820     ASSERT_NE(klass, nullptr);
821 
822     uint32_t arrayLength = 100;
823     auto *array1 = EtsObjectArray::Create(klass, arrayLength);
824     auto *array2 = EtsObjectArray::Create(klass, arrayLength);
825 
826     ASSERT_NE(array1, nullptr);
827     ASSERT_NE(array2, nullptr);
828     ASSERT_EQ(array1->GetClass()->GetComponentType(), array2->GetClass()->GetComponentType());
829 
830     source = R"(
831         .language eTS
832         .record TestObject {}
833     )";
834 
835     EtsClass *componentType = GetTestClass(source, "LTestObject;");
836     ASSERT_NE(componentType, nullptr);
837 
838     klass->SetComponentType(componentType);
839     ASSERT_EQ(klass->GetComponentType(), componentType);
840 
841     klass->SetComponentType(nullptr);
842     ASSERT_EQ(klass->GetComponentType(), nullptr);
843 }
844 
TEST_F(EtsClassTest,EnumerateMethods)845 TEST_F(EtsClassTest, EnumerateMethods)
846 {
847     const char *source = R"(
848         .language eTS
849         .record Test {}
850         .function void Test.foo1() {}
851         .function void Test.foo2() {}
852         .function void Test.foo3() {}
853         .function void Test.foo4() {}
854         .function void Test.foo5() {}
855     )";
856 
857     EtsClass *klass = GetTestClass(source, "LTest;");
858     ASSERT_NE(klass, nullptr);
859 
860     std::size_t methodsVectorSize = 5;
861     std::vector<EtsMethod *> methods(methodsVectorSize);
862     std::vector<EtsMethod *> enumerateMethods(methodsVectorSize);
863 
864     methods.push_back(klass->GetInstanceMethod("foo1", nullptr));
865     methods.push_back(klass->GetInstanceMethod("foo2", nullptr));
866     methods.push_back(klass->GetInstanceMethod("foo3", nullptr));
867     methods.push_back(klass->GetInstanceMethod("foo4", nullptr));
868     methods.push_back(klass->GetInstanceMethod("foo5", nullptr));
869 
870     klass->EnumerateMethods([&enumerateMethods, &methodsVectorSize](EtsMethod *method) {
871         enumerateMethods.push_back(method);
872         return enumerateMethods.size() == methodsVectorSize;
873     });
874 
875     for (std::size_t i = 0; i < methodsVectorSize; ++i) {
876         ASSERT_EQ(methods[i], enumerateMethods[i]);
877     }
878 }
879 
TEST_F(EtsClassTest,EnumerateInterfaces)880 TEST_F(EtsClassTest, EnumerateInterfaces)
881 {
882     const char *source = R"(
883         .language eTS
884         .record A <ets.abstract, ets.interface> {}
885         .record B <ets.abstract, ets.interface> {}
886         .record C <ets.abstract, ets.interface> {}
887         .record D <ets.abstract, ets.interface> {}
888         .record E <ets.abstract, ets.interface> {}
889         .record Test <ets.implements=A, ets.implements=B, ets.implements=C, ets.implements=D, ets.implements=E> {}
890     )";
891 
892     EtsClass *klass = GetTestClass(source, "LTest;");
893     ASSERT_NE(klass, nullptr);
894 
895     std::size_t interfaceVectorSize = 5;
896     std::vector<EtsClass *> interfaces(interfaceVectorSize);
897     std::vector<EtsClass *> enumerateInterfaces(interfaceVectorSize);
898 
899     interfaces.push_back(GetTestClass(source, "LA;"));
900     interfaces.push_back(GetTestClass(source, "LB;"));
901     interfaces.push_back(GetTestClass(source, "LC;"));
902     interfaces.push_back(GetTestClass(source, "LD;"));
903     interfaces.push_back(GetTestClass(source, "LE;"));
904 
905     klass->EnumerateInterfaces([&enumerateInterfaces, &interfaceVectorSize](EtsClass *interface) {
906         enumerateInterfaces.push_back(interface);
907         return enumerateInterfaces.size() == interfaceVectorSize;
908     });
909 
910     for (std::size_t i = 0; i < interfaceVectorSize; ++i) {
911         ASSERT_EQ(interfaces[i], enumerateInterfaces[i]);
912     }
913 }
914 
915 }  // namespace ark::ets::test
916 
917 // NOLINTEND(readability-magic-numbers)
918