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