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
18 #include "libpandabase/utils/utils.h"
19 #include "get_test_class.h"
20 #include "ets_coroutine.h"
21
22 #include "types/ets_method.h"
23 #include "napi/ets_scoped_objects_fix.h"
24
25 // NOLINTBEGIN(readability-magic-numbers)
26
27 namespace ark::ets::test {
28
29 class EtsMethodTest : public testing::Test {
30 public:
EtsMethodTest()31 EtsMethodTest()
32 {
33 options_.SetShouldLoadBootPandaFiles(true);
34 options_.SetShouldInitializeIntrinsics(false);
35 options_.SetCompilerEnableJit(false);
36 options_.SetGcType("epsilon");
37 options_.SetLoadRuntimes({"ets"});
38
39 auto stdlib = std::getenv("PANDA_STD_LIB");
40 if (stdlib == nullptr) {
41 std::cerr << "PANDA_STD_LIB env variable should be set and point to mock_stdlib.abc" << std::endl;
42 std::abort();
43 }
44 options_.SetBootPandaFiles({stdlib});
45
46 Runtime::Create(options_);
47 }
48
~EtsMethodTest()49 ~EtsMethodTest() override
50 {
51 Runtime::Destroy();
52 }
53
54 NO_COPY_SEMANTIC(EtsMethodTest);
55 NO_MOVE_SEMANTIC(EtsMethodTest);
56
SetUp()57 void SetUp() override
58 {
59 coroutine_ = EtsCoroutine::GetCurrent();
60 PandaEtsNapiEnv *env = coroutine_->GetEtsNapiEnv();
61
62 s_ = new napi::ScopedManagedCodeFix(env);
63 }
64
TearDown()65 void TearDown() override
66 {
67 delete s_;
68 }
69
70 private:
71 RuntimeOptions options_;
72 EtsCoroutine *coroutine_ = nullptr;
73 napi::ScopedManagedCodeFix *s_ = nullptr;
74
75 protected:
GetScopedManagedCodeFix()76 napi::ScopedManagedCodeFix *GetScopedManagedCodeFix()
77 {
78 return s_;
79 }
80 };
81
TEST_F(EtsMethodTest,Invoke)82 TEST_F(EtsMethodTest, Invoke)
83 {
84 const char *source = R"(
85 .language eTS
86 .record Test {}
87
88 .function i32 Test.foo() {
89 ldai 111
90 return
91 }
92 .function i32 Test.goo() {
93 ldai 222
94 return
95 }
96 .function i32 Test.sum() {
97 call Test.foo
98 sta v0
99 call Test.goo
100 add2 v0
101 return
102 }
103 )";
104
105 EtsClass *klass = GetTestClass(source, "LTest;");
106 ASSERT(klass);
107
108 EtsMethod *fooMethod = klass->GetStaticMethod("foo", nullptr);
109 ASSERT(fooMethod);
110 EtsMethod *gooMethod = klass->GetStaticMethod("goo", nullptr);
111 ASSERT(gooMethod);
112 EtsMethod *sumMethod = klass->GetStaticMethod("sum", nullptr);
113 ASSERT(sumMethod);
114
115 EtsValue res = fooMethod->Invoke(GetScopedManagedCodeFix(), nullptr);
116 ASSERT_EQ(res.GetAs<int32_t>(), 111_I);
117 res = gooMethod->Invoke(GetScopedManagedCodeFix(), nullptr);
118 ASSERT_EQ(res.GetAs<int32_t>(), 222_I);
119 res = sumMethod->Invoke(GetScopedManagedCodeFix(), nullptr);
120 ASSERT_EQ(res.GetAs<int32_t>(), 333_I);
121 }
122
TEST_F(EtsMethodTest,GetNumArgSlots)123 TEST_F(EtsMethodTest, GetNumArgSlots)
124 {
125 const char *source = R"(
126 .language eTS
127 .record Test {}
128
129 .function i32 Test.foo1() <static, access.function=public> {
130 ldai 0
131 return
132 }
133 .function i32 Test.foo2(f32 a0) <static, access.function=private> {
134 ldai 0
135 return
136 }
137 .function i32 Test.foo3(i32 a0, i32 a1, f32 a2, f32 a3, f32 a4) <static, access.function=public> {
138 ldai 0
139 return
140 }
141 )";
142
143 EtsClass *klass = GetTestClass(source, "LTest;");
144 ASSERT(klass);
145
146 EtsMethod *foo1Method = klass->GetStaticMethod("foo1", nullptr);
147 ASSERT(foo1Method);
148 EtsMethod *foo2Method = klass->GetStaticMethod("foo2", nullptr);
149 ASSERT(foo2Method);
150 EtsMethod *foo3Method = klass->GetStaticMethod("foo3", nullptr);
151 ASSERT(foo3Method);
152
153 ASSERT_TRUE(foo1Method->IsStatic());
154 ASSERT_TRUE(foo2Method->IsStatic());
155 ASSERT_TRUE(foo3Method->IsStatic());
156 ASSERT_TRUE(foo1Method->IsPublic());
157 ASSERT_FALSE(foo2Method->IsPublic());
158 ASSERT_TRUE(foo3Method->IsPublic());
159
160 ASSERT_EQ(foo1Method->GetNumArgSlots(), 0U);
161 ASSERT_EQ(foo2Method->GetNumArgSlots(), 1U);
162 ASSERT_EQ(foo3Method->GetNumArgSlots(), 5U);
163 }
164
TEST_F(EtsMethodTest,GetArgType)165 TEST_F(EtsMethodTest, GetArgType)
166 {
167 const char *source = R"(
168 .language eTS
169 .record Test {}
170
171 .function i32 Test.foo(u1 a0, i8 a1, u16 a2, i16 a3, i32 a4, i64 a5, f32 a6, f64 a7) {
172 ldai 0
173 return
174 }
175 )";
176
177 EtsClass *klass = GetTestClass(source, "LTest;");
178 ASSERT(klass);
179
180 EtsMethod *fooMethod = klass->GetStaticMethod("foo", nullptr);
181 ASSERT(fooMethod);
182
183 std::vector<EtsType> expectedArgTypes = {EtsType::BOOLEAN, EtsType::BYTE, EtsType::CHAR, EtsType::SHORT,
184 EtsType::INT, EtsType::LONG, EtsType::FLOAT, EtsType::DOUBLE};
185 EtsType argType;
186
187 for (std::size_t i = 0; i < expectedArgTypes.size(); i++) {
188 argType = fooMethod->GetArgType(i);
189 ASSERT_EQ(argType, expectedArgTypes[i]);
190 }
191 }
192
TEST_F(EtsMethodTest,GetReturnValueType)193 TEST_F(EtsMethodTest, GetReturnValueType)
194 {
195 const char *source = R"(
196 .language eTS
197 .record Test {}
198 .record TestObject {}
199
200 .function u1 Test.foo0() { return }
201 .function i8 Test.foo1() { return }
202 .function u16 Test.foo2() { return }
203 .function i16 Test.foo3() { return }
204 .function i32 Test.foo4() { return }
205 .function i64 Test.foo5() { return }
206 .function f32 Test.foo6() { return }
207 .function f64 Test.foo7() { return }
208 .function TestObject Test.foo8() { return }
209 .function void Test.foo9() { return }
210 )";
211
212 EtsClass *klass = GetTestClass(source, "LTest;");
213 ASSERT(klass);
214
215 std::vector<EtsType> returnValTypes = {EtsType::BOOLEAN, EtsType::BYTE, EtsType::CHAR, EtsType::SHORT,
216 EtsType::INT, EtsType::LONG, EtsType::FLOAT, EtsType::DOUBLE,
217 EtsType::OBJECT, EtsType::VOID};
218 std::vector<EtsMethod *> methods;
219 EtsMethod *currentMethod = nullptr;
220
221 for (std::size_t i = 0; i < returnValTypes.size(); i++) {
222 std::string fooName("foo");
223 currentMethod = klass->GetStaticMethod((fooName + std::to_string(i)).data(), nullptr);
224 ASSERT(currentMethod);
225 methods.push_back(currentMethod);
226 }
227 for (std::size_t i = 0; i < returnValTypes.size(); i++) {
228 ASSERT_EQ(methods[i]->GetReturnValueType(), returnValTypes[i]);
229 }
230 }
231
TEST_F(EtsMethodTest,GetMethodSignature)232 TEST_F(EtsMethodTest, GetMethodSignature)
233 {
234 const char *source = R"(
235 .language eTS
236 .record Test {}
237 .record TestObject {}
238
239 .function TestObject Test.foo1(i32 a0) {
240 return.obj
241 }
242 .function i32 Test.foo2(i32 a0, f32 a1, f64 a2) {
243 ldai 0
244 return
245 }
246 .function u1 Test.foo3(i32 a0, i32 a1, f32 a2, f64 a3, f32 a4) {
247 ldai 0
248 return
249 }
250 )";
251
252 EtsClass *klass = GetTestClass(source, "LTest;");
253 ASSERT(klass);
254
255 EtsMethod *foo1Method = klass->GetStaticMethod("foo1", nullptr);
256 ASSERT(foo1Method);
257 EtsMethod *foo2Method = klass->GetStaticMethod("foo2", nullptr);
258 ASSERT(foo2Method);
259 EtsMethod *foo3Method = klass->GetStaticMethod("foo3", nullptr);
260 ASSERT(foo3Method);
261
262 ASSERT_EQ(foo1Method->GetMethodSignature(), "I:LTestObject;");
263 ASSERT_EQ(foo2Method->GetMethodSignature(), "IFD:I");
264 ASSERT_EQ(foo3Method->GetMethodSignature(), "IIFDF:Z");
265 }
266
TEST_F(EtsMethodTest,GetLineNumFromBytecodeOffset)267 TEST_F(EtsMethodTest, GetLineNumFromBytecodeOffset)
268 {
269 const char *source = R"( # line 1
270 .language eTS # line 2
271 .record Test {} # line 3
272 .function void Test.foo() { # line 4
273 mov v0, v1 # line 5, offset 0, size 2
274 mov v0, v256 # line 6, offset 2, size 5
275 movi v0, 1 # line 7, offset 7, size 2
276 movi v0, 256 # line 8, offset 9, size 4
277 return.void # line 9, offset 13, size 1
278 }
279 )";
280
281 EtsClass *klass = GetTestClass(source, "LTest;");
282 ASSERT(klass);
283 EtsMethod *fooMethod = klass->GetStaticMethod("foo", nullptr);
284 ASSERT(fooMethod);
285
286 ASSERT_EQ(fooMethod->GetLineNumFromBytecodeOffset(0U), 5_I);
287 ASSERT_EQ(fooMethod->GetLineNumFromBytecodeOffset(1U), 5_I);
288
289 ASSERT_EQ(fooMethod->GetLineNumFromBytecodeOffset(2U), 6_I);
290 ASSERT_EQ(fooMethod->GetLineNumFromBytecodeOffset(3U), 6_I);
291 ASSERT_EQ(fooMethod->GetLineNumFromBytecodeOffset(4U), 6_I);
292 ASSERT_EQ(fooMethod->GetLineNumFromBytecodeOffset(5U), 6_I);
293 ASSERT_EQ(fooMethod->GetLineNumFromBytecodeOffset(6U), 6_I);
294
295 ASSERT_EQ(fooMethod->GetLineNumFromBytecodeOffset(7U), 7_I);
296 ASSERT_EQ(fooMethod->GetLineNumFromBytecodeOffset(8U), 7_I);
297
298 ASSERT_EQ(fooMethod->GetLineNumFromBytecodeOffset(9U), 8_I);
299 ASSERT_EQ(fooMethod->GetLineNumFromBytecodeOffset(10U), 8_I);
300 ASSERT_EQ(fooMethod->GetLineNumFromBytecodeOffset(11U), 8_I);
301 ASSERT_EQ(fooMethod->GetLineNumFromBytecodeOffset(12U), 8_I);
302
303 ASSERT_EQ(fooMethod->GetLineNumFromBytecodeOffset(13U), 9_I);
304 }
305
TEST_F(EtsMethodTest,GetName)306 TEST_F(EtsMethodTest, GetName)
307 {
308 const char *source = R"(
309 .language eTS
310 .record Test {}
311
312 .function i32 Test.foo1() {
313 ldai 0
314 return
315 }
316 .function i32 Test.foo2(f32 a0) {
317 ldai 0
318 return
319 }
320 )";
321
322 EtsClass *klass = GetTestClass(source, "LTest;");
323 ASSERT(klass);
324
325 EtsMethod *foo1Method = klass->GetStaticMethod("foo1", nullptr);
326 ASSERT(foo1Method);
327 EtsMethod *foo2Method = klass->GetStaticMethod("foo2", nullptr);
328 ASSERT(foo2Method);
329
330 ASSERT_TRUE(!strcmp(foo1Method->GetName(), "foo1"));
331 ASSERT_TRUE(!strcmp(foo2Method->GetName(), "foo2"));
332
333 EtsString *str1 = foo1Method->GetNameString();
334 EtsString *str2 = EtsString::CreateFromMUtf8("foo1");
335 ASSERT_TRUE(str1->StringsAreEqual(reinterpret_cast<EtsObject *>(str2)));
336 str1 = foo2Method->GetNameString();
337 str2 = EtsString::CreateFromMUtf8("foo2");
338 ASSERT_TRUE(str1->StringsAreEqual(reinterpret_cast<EtsObject *>(str2)));
339 }
340
TEST_F(EtsMethodTest,ResolveArgType)341 TEST_F(EtsMethodTest, ResolveArgType)
342 {
343 const char *source = R"(
344 .language eTS
345 .record Test {}
346 .record TestObject {}
347
348 .function i32 Test.foo1(TestObject a0, f32 a1) {
349 return
350 }
351 .function i32 Test.foo2(TestObject a0, TestObject a1) {
352 return
353 }
354 .function i32 Test.foo3(f32 a0, f32 a1) {
355 return
356 }
357 )";
358
359 EtsClass *klass = GetTestClass(source, "LTest;");
360 ASSERT(klass);
361
362 EtsMethod *foo1Method = klass->GetStaticMethod("foo1", nullptr);
363 ASSERT(foo1Method);
364 EtsMethod *foo2Method = klass->GetStaticMethod("foo2", nullptr);
365 ASSERT(foo2Method);
366 EtsMethod *foo3Method = klass->GetStaticMethod("foo3", nullptr);
367 ASSERT(foo3Method);
368
369 ASSERT_EQ(foo1Method->ResolveArgType(0), foo2Method->ResolveArgType(0));
370 ASSERT_EQ(foo2Method->ResolveArgType(0), foo2Method->ResolveArgType(1));
371
372 ASSERT_EQ(foo1Method->ResolveArgType(1), foo3Method->ResolveArgType(0));
373 ASSERT_EQ(foo3Method->ResolveArgType(0), foo3Method->ResolveArgType(1));
374 }
375
TEST_F(EtsMethodTest,ResolveReturnType)376 TEST_F(EtsMethodTest, ResolveReturnType)
377 {
378 const char *source = R"(
379 .language eTS
380 .record Test {}
381 .record TestObject {}
382
383 .function TestObject Test.foo1() {
384 return
385 }
386 .function TestObject Test.foo2() {
387 return
388 }
389 .function i32 Test.foo3() {
390 return
391 }
392 .function i32 Test.foo4() {
393 return
394 }
395 )";
396
397 EtsClass *klass = GetTestClass(source, "LTest;");
398 ASSERT(klass);
399
400 EtsMethod *foo1Method = klass->GetStaticMethod("foo1", nullptr);
401 ASSERT(foo1Method);
402 EtsMethod *foo2Method = klass->GetStaticMethod("foo2", nullptr);
403 ASSERT(foo2Method);
404 EtsMethod *foo3Method = klass->GetStaticMethod("foo3", nullptr);
405 ASSERT(foo3Method);
406 EtsMethod *foo4Method = klass->GetStaticMethod("foo4", nullptr);
407 ASSERT(foo4Method);
408
409 ASSERT_EQ(foo1Method->ResolveReturnType(), foo2Method->ResolveReturnType());
410 ASSERT_EQ(foo3Method->ResolveReturnType(), foo4Method->ResolveReturnType());
411 }
412
TEST_F(EtsMethodTest,GetClassSourceFile)413 TEST_F(EtsMethodTest, GetClassSourceFile)
414 {
415 const char *source = R"(
416 .language eTS
417 .record Test {}
418
419 .function i32 Test.foo() {
420 return
421 }
422 )";
423
424 std::string sourceFilename = "EtsMethodTestSource.pa";
425
426 EtsClass *klass = GetTestClass(source, "LTest;", sourceFilename);
427 ASSERT(klass);
428 EtsMethod *fooMethod = klass->GetStaticMethod("foo", nullptr);
429 ASSERT(fooMethod);
430
431 auto result = fooMethod->GetClassSourceFile();
432 ASSERT(result.data);
433
434 ASSERT_TRUE(!strcmp(reinterpret_cast<const char *>(result.data), sourceFilename.data()));
435 }
436
437 } // namespace ark::ets::test
438
439 // NOLINTEND(readability-magic-numbers)
440