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 <algorithm>
18
19 #include "checker/ETSAnalyzer.h"
20 #include "checker/ETSchecker.h"
21 #include "compiler/core/compilerImpl.h"
22 #include "compiler/core/ETSCompiler.h"
23 #include "compiler/core/ETSemitter.h"
24 #include "compiler/core/ETSGen.h"
25 #include "compiler/core/regSpiller.h"
26 #include "compiler/lowering/phase.h"
27 #include "es2panda.h"
28 #include "mem/arena_allocator.h"
29 #include "mem/pool_manager.h"
30 #include "public/public.h"
31 #include "util/arktsconfig.h"
32 #include "util/generateBin.h"
33 #include "varbinder/ETSBinder.h"
34 #include "test/utils/panda_executable_path_getter.h"
35 #include "checker/types/globalTypesHolder.h"
36 #include "test/unit/union_normalisation_test.h"
37
38 using ark::es2panda::gtests::UnionNormalizationTest;
39
40 namespace ark::es2panda {
41
TEST_F(UnionNormalizationTest,UnionWithSubTypes)42 TEST_F(UnionNormalizationTest, UnionWithSubTypes)
43 {
44 // Test 4 cases of normalization
45 static constexpr std::string_view SRC =
46 "\
47 class Base {}\
48 class Derived1 extends Base {}\
49 class Derived2 extends Base {}\
50 ";
51 InitializeChecker("_.ets", SRC);
52
53 auto program = Program();
54 ASSERT(program);
55
56 auto *const baseType = FindClassType(program->VarBinder()->AsETSBinder(), "Base");
57 ASSERT_NE(baseType, nullptr);
58 auto *const derived1Type = FindClassType(program->VarBinder()->AsETSBinder(), "Derived1");
59 ASSERT_NE(derived1Type, nullptr);
60 auto *const derived2Type = FindClassType(program->VarBinder()->AsETSBinder(), "Derived2");
61 ASSERT_NE(derived2Type, nullptr);
62
63 auto checker = Checker();
64 ASSERT(checker);
65
66 // Test normalization: Derived1 | Base ==> Base
67 ArenaVector<checker::Type *> unionConstituents1(checker->Allocator()->Adapter());
68 unionConstituents1.emplace_back(derived1Type);
69 unionConstituents1.emplace_back(baseType);
70
71 // Create union type, which will be normalized inside creation function
72 auto *const normalizedType1 = checker->CreateETSUnionType(std::move(unionConstituents1));
73 ASSERT_NE(normalizedType1, nullptr);
74 ASSERT_TRUE(normalizedType1->IsETSObjectType());
75 ASSERT_EQ(normalizedType1, baseType);
76
77 // Test normalization: Base | Derived2 ==> Base
78 ArenaVector<checker::Type *> unionConstituents2(checker->Allocator()->Adapter());
79 unionConstituents2.emplace_back(baseType);
80 unionConstituents2.emplace_back(derived2Type);
81
82 // Create union type, which will be normalized inside creation function
83 auto *const normalizedType2 = checker->CreateETSUnionType(std::move(unionConstituents2));
84 ASSERT_NE(normalizedType2, nullptr);
85 ASSERT_TRUE(normalizedType2->IsETSObjectType());
86 ASSERT_EQ(normalizedType2, baseType);
87
88 // Test normalization: Derived1 | Derived2 ==> Derived1 | Derived2
89 ArenaVector<checker::Type *> unionConstituents3(checker->Allocator()->Adapter());
90 unionConstituents3.emplace_back(derived1Type);
91 unionConstituents3.emplace_back(derived2Type);
92
93 // Create union type, which will be normalized inside creation function
94 auto *const normalizedType3 = checker->CreateETSUnionType(std::move(unionConstituents3));
95 ASSERT_NE(normalizedType3, nullptr);
96 auto *const unionType = normalizedType3->AsETSUnionType();
97 ASSERT_EQ(unionType->ConstituentTypes().size(), SIZE2);
98 ASSERT_EQ(unionType->ConstituentTypes().at(IDX0), derived1Type);
99 ASSERT_EQ(unionType->ConstituentTypes().at(IDX1), derived2Type);
100
101 // Test normalization: Derived2 | Base | Derived1 ==> Base
102 ArenaVector<checker::Type *> unionConstituents4(checker->Allocator()->Adapter());
103 unionConstituents4.emplace_back(derived1Type);
104 unionConstituents4.emplace_back(baseType);
105 unionConstituents4.emplace_back(derived2Type);
106
107 // Create union type, which will be normalized inside creation function
108 auto *const normalizedType4 = checker->CreateETSUnionType(std::move(unionConstituents4));
109 ASSERT_NE(normalizedType4, nullptr);
110 ASSERT_TRUE(normalizedType4->IsETSObjectType());
111 ASSERT_EQ(normalizedType4, baseType);
112 }
113
TEST_F(UnionNormalizationTest,DISABLED_UnionLinearization)114 TEST_F(UnionNormalizationTest, DISABLED_UnionLinearization)
115 {
116 // Test 3 cases of normalization
117 static constexpr std::string_view SRC =
118 "\
119 class Base {}\
120 class Derived1 extends Base {}\
121 class Derived2 extends Base {}\
122 type UT = int | string\
123 \
124 type UT1 = int | (int | string) | number\
125 type UT2 = int | UT | number\
126 type UT3 = int | (Derived2 | Base) | Derived1 | (string | number | short) | (int | string)\
127 ";
128 InitializeChecker("_.ets", SRC);
129
130 auto program = Program();
131 ASSERT(program);
132
133 auto *varbinder = program->VarBinder()->AsETSBinder();
134 auto *const baseType = FindClassType(varbinder, "Base");
135 ASSERT_NE(baseType, nullptr);
136 auto *const derived1Type = FindClassType(program->VarBinder()->AsETSBinder(), "Derived1");
137 ASSERT_NE(derived1Type, nullptr);
138 auto *const derived2Type = FindClassType(program->VarBinder()->AsETSBinder(), "Derived2");
139 ASSERT_NE(derived2Type, nullptr);
140
141 auto checker = Checker();
142 ASSERT(checker);
143
144 // Test normalization: int | (int | string) | number ==> string | number
145 auto *const ut1Type = FindTypeAlias(checker, "UT1");
146 ASSERT_NE(ut1Type, nullptr);
147 ASSERT_TRUE(ut1Type->IsETSUnionType());
148 auto *ut1 = ut1Type->AsETSUnionType();
149 ASSERT_EQ(ut1->ConstituentTypes().size(), SIZE2);
150 ASSERT_EQ(ut1->ConstituentTypes().at(IDX0), checker->GlobalBuiltinETSStringType());
151 ASSERT_EQ(ut1->ConstituentTypes().at(IDX1), checker->GetGlobalTypesHolder()->GlobalDoubleBuiltinType());
152
153 // Test normalization: int | UT | number ==> string | number
154 auto *const ut2Type = FindTypeAlias(checker, "UT2");
155 ASSERT_NE(ut2Type, nullptr);
156 ASSERT_TRUE(ut2Type->IsETSUnionType());
157 auto *ut2 = ut2Type->AsETSUnionType();
158 ASSERT_EQ(ut2->ConstituentTypes().size(), SIZE2);
159 ASSERT_EQ(ut2->ConstituentTypes().at(IDX0), checker->GlobalBuiltinETSStringType());
160 ASSERT_EQ(ut2->ConstituentTypes().at(IDX1), checker->GetGlobalTypesHolder()->GlobalDoubleBuiltinType());
161
162 // Test normalization:
163 // int | (Derived2 | Base) | Derived1 | (string | number | short) | (int | string) ==> Base | string | number
164 auto *const ut3Type = FindTypeAlias(checker, "UT3");
165 ASSERT_NE(ut3Type, nullptr);
166 ASSERT_TRUE(ut3Type->IsETSUnionType());
167 auto *ut3 = ut3Type->AsETSUnionType();
168 ASSERT_EQ(ut3->ConstituentTypes().size(), SIZE3);
169 ASSERT_EQ(ut3->ConstituentTypes().at(IDX0), baseType);
170 ASSERT_EQ(ut3->ConstituentTypes().at(IDX1), checker->GlobalBuiltinETSStringType());
171 ASSERT_EQ(ut3->ConstituentTypes().at(IDX2), checker->GetGlobalTypesHolder()->GlobalDoubleBuiltinType());
172 }
173
TEST_F(UnionNormalizationTest,UnionStringLiterals1)174 TEST_F(UnionNormalizationTest, UnionStringLiterals1)
175 {
176 InitializeChecker("_.ets", "");
177
178 auto checker = Checker();
179 ASSERT(checker);
180
181 // Test normalization: string | "abc" ==> string
182 ArenaVector<checker::Type *> unionConstituents1(checker->Allocator()->Adapter());
183 unionConstituents1.emplace_back(checker->GlobalBuiltinETSStringType());
184 unionConstituents1.emplace_back(checker->CreateETSStringLiteralType("abc"));
185
186 // Create union type, which will be normalized inside creation function
187 auto *const normalizedType1 = checker->CreateETSUnionType(std::move(unionConstituents1));
188 ASSERT_NE(normalizedType1, nullptr);
189 ASSERT_TRUE(normalizedType1->IsETSObjectType());
190 ASSERT_EQ(normalizedType1, checker->GlobalBuiltinETSStringType());
191
192 // Test normalization: "abc" | string | string ==> string
193 ArenaVector<checker::Type *> unionConstituents2(checker->Allocator()->Adapter());
194 unionConstituents2.emplace_back(checker->CreateETSStringLiteralType("abc"));
195 unionConstituents2.emplace_back(checker->GlobalBuiltinETSStringType());
196 unionConstituents2.emplace_back(checker->GlobalBuiltinETSStringType());
197
198 // Create union type, which will be normalized inside creation function
199 auto *const normalizedType2 = checker->CreateETSUnionType(std::move(unionConstituents2));
200 ASSERT_NE(normalizedType2, nullptr);
201 ASSERT_TRUE(normalizedType2->IsETSObjectType());
202 ASSERT_EQ(normalizedType2, checker->GlobalBuiltinETSStringType());
203
204 // Test normalization: number | "abc" | string | "xy" ==> number | string
205 ArenaVector<checker::Type *> unionConstituents3(checker->Allocator()->Adapter());
206 unionConstituents3.emplace_back(checker->GlobalDoubleType());
207 unionConstituents3.emplace_back(checker->CreateETSStringLiteralType("abc"));
208 unionConstituents3.emplace_back(checker->GlobalBuiltinETSStringType());
209 unionConstituents3.emplace_back(checker->CreateETSStringLiteralType("xy"));
210
211 // Create union type, which will be normalized inside creation function
212 auto *const normalizedType3 = checker->CreateETSUnionType(std::move(unionConstituents3));
213 ASSERT_NE(normalizedType3, nullptr);
214 ASSERT_TRUE(normalizedType3->IsETSUnionType());
215 auto *const unionType = normalizedType3->AsETSUnionType();
216 ASSERT_EQ(unionType->ConstituentTypes().size(), SIZE2);
217 ASSERT_EQ(unionType->ConstituentTypes().at(IDX0), checker->GetGlobalTypesHolder()->GlobalDoubleBuiltinType());
218 ASSERT_EQ(unionType->ConstituentTypes().at(IDX1), checker->GlobalBuiltinETSStringType());
219
220 // Test normalization: "abcd" | "abcd" | "abcd" ==> "abcd"
221 ArenaVector<checker::Type *> unionConstituents4(checker->Allocator()->Adapter());
222 unionConstituents4.emplace_back(checker->CreateETSStringLiteralType("abcd"));
223 unionConstituents4.emplace_back(checker->CreateETSStringLiteralType("abcd"));
224 unionConstituents4.emplace_back(checker->CreateETSStringLiteralType("abcd"));
225
226 // Create union type, which will be normalized inside creation function
227 auto *const normalizedType4 = checker->CreateETSUnionType(std::move(unionConstituents4));
228 ASSERT_NE(normalizedType4, nullptr);
229 ASSERT_TRUE(normalizedType4->IsETSStringType());
230 ASSERT_EQ(normalizedType4->AsETSStringType()->GetValue(), "abcd");
231 }
232
TEST_F(UnionNormalizationTest,UnionStringLiterals2)233 TEST_F(UnionNormalizationTest, UnionStringLiterals2)
234 {
235 InitializeChecker("_.ets", "");
236
237 auto checker = Checker();
238 ASSERT(checker);
239
240 // Test absence of normalization: "ab1" | "bc2" | "cd3" ==> "ab1" | "bc2" | "cd3"
241 ArenaVector<checker::Type *> unionConstituents1(checker->Allocator()->Adapter());
242 unionConstituents1.emplace_back(checker->CreateETSStringLiteralType("ab1"));
243 unionConstituents1.emplace_back(checker->CreateETSStringLiteralType("bc2"));
244 unionConstituents1.emplace_back(checker->CreateETSStringLiteralType("cd3"));
245
246 // Create union type, which will be normalized inside creation function
247 auto *const normalizedType1 = checker->CreateETSUnionType(std::move(unionConstituents1));
248 ASSERT_NE(normalizedType1, nullptr);
249 ASSERT_TRUE(normalizedType1->IsETSUnionType());
250 auto *const unionType1 = normalizedType1->AsETSUnionType();
251 ASSERT_EQ(unionType1->ConstituentTypes().size(), SIZE3);
252 ASSERT_TRUE(unionType1->ConstituentTypes().at(IDX0)->IsETSStringType());
253 ASSERT_EQ(unionType1->ConstituentTypes().at(IDX0)->AsETSStringType()->GetValue(), "ab1");
254 ASSERT_TRUE(unionType1->ConstituentTypes().at(IDX1)->IsETSStringType());
255 ASSERT_EQ(unionType1->ConstituentTypes().at(IDX1)->AsETSStringType()->GetValue(), "bc2");
256 ASSERT_TRUE(unionType1->ConstituentTypes().at(IDX2)->IsETSStringType());
257 ASSERT_EQ(unionType1->ConstituentTypes().at(IDX2)->AsETSStringType()->GetValue(), "cd3");
258
259 // Test normalization: "ab1" | "bc2" | "ab1" ==> "ab1" | "bc2"
260 ArenaVector<checker::Type *> unionConstituents2(checker->Allocator()->Adapter());
261 unionConstituents2.emplace_back(checker->CreateETSStringLiteralType("ab1"));
262 unionConstituents2.emplace_back(checker->CreateETSStringLiteralType("bc2"));
263 unionConstituents2.emplace_back(checker->CreateETSStringLiteralType("ab1"));
264
265 // Create union type, which will be normalized inside creation function
266 auto *const normalizedType2 = checker->CreateETSUnionType(std::move(unionConstituents2));
267 ASSERT_NE(normalizedType2, nullptr);
268 ASSERT_TRUE(normalizedType2->IsETSUnionType());
269 auto *const unionType2 = normalizedType2->AsETSUnionType();
270 ASSERT_EQ(unionType2->ConstituentTypes().size(), SIZE2);
271 ASSERT_TRUE(unionType2->ConstituentTypes().at(IDX0)->IsETSStringType());
272 ASSERT_EQ(unionType2->ConstituentTypes().at(IDX0)->AsETSStringType()->GetValue(), "ab1");
273 ASSERT_TRUE(unionType2->ConstituentTypes().at(IDX1)->IsETSStringType());
274 ASSERT_EQ(unionType2->ConstituentTypes().at(IDX1)->AsETSStringType()->GetValue(), "bc2");
275
276 // Test absence of normalization: "ab1" | "bc2" | "cd3" | string | int ==> string | int
277 ArenaVector<checker::Type *> unionConstituents3(checker->Allocator()->Adapter());
278 unionConstituents3.emplace_back(checker->CreateETSStringLiteralType("ab1"));
279 unionConstituents3.emplace_back(checker->CreateETSStringLiteralType("bc2"));
280 unionConstituents3.emplace_back(checker->CreateETSStringLiteralType("cd3"));
281 unionConstituents3.emplace_back(checker->GlobalBuiltinETSStringType());
282 unionConstituents3.emplace_back(checker->GlobalIntType());
283
284 // Create union type, which will be normalized inside creation function
285 auto *const normalizedType3 = checker->CreateETSUnionType(std::move(unionConstituents3));
286 ASSERT_NE(normalizedType3, nullptr);
287 ASSERT_TRUE(normalizedType3->IsETSUnionType());
288 auto *const unionType3 = normalizedType3->AsETSUnionType();
289 ASSERT_EQ(unionType3->ConstituentTypes().size(), SIZE2);
290 ASSERT_EQ(unionType3->ConstituentTypes().at(IDX0), checker->GlobalBuiltinETSStringType());
291 ASSERT_EQ(unionType3->ConstituentTypes().at(IDX1), checker->GetGlobalTypesHolder()->GlobalIntegerBuiltinType());
292 }
293
TEST_F(UnionNormalizationTest,DISABLED_UnionWithNever)294 TEST_F(UnionNormalizationTest, DISABLED_UnionWithNever)
295 {
296 // Test normalization: int | never | number ==> number
297 InitializeChecker("_.ets", "");
298
299 auto checker = Checker();
300 ASSERT(checker);
301
302 ArenaVector<checker::Type *> unionConstituents(checker->Allocator()->Adapter());
303 unionConstituents.emplace_back(checker->GlobalIntType());
304 unionConstituents.emplace_back(checker->GetGlobalTypesHolder()->GlobalETSNeverType());
305 unionConstituents.emplace_back(checker->GetGlobalTypesHolder()->GlobalDoubleBuiltinType());
306
307 // Create union type, which will be normalized inside creation function
308 auto *const normalizedType = checker->CreateETSUnionType(std::move(unionConstituents));
309 ASSERT_NE(normalizedType, nullptr);
310 ASSERT_TRUE(normalizedType->IsETSObjectType());
311 ASSERT_EQ(normalizedType, checker->GetGlobalTypesHolder()->GlobalDoubleBuiltinType());
312 }
313
314 } // namespace ark::es2panda
315