• 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 
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