1/**
2 * Copyright (c) 2021-2024 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
37namespace ark::es2panda {
38
39class UnionNormalizationTest : public testing::Test {
40public:
41    UnionNormalizationTest()
42        : allocator_(std::make_unique<ArenaAllocator>(SpaceType::SPACE_TYPE_COMPILER)),
43          publicContext_ {std::make_unique<public_lib::Context>()},
44          program_ {parser::Program::NewProgram<varbinder::ETSBinder>(Allocator())},
45          es2pandaPath_ {test::utils::PandaExecutablePathGetter {}.Get()}
46    {
47    }
48
49    ~UnionNormalizationTest() override = default;
50
51    static void SetUpTestCase()
52    {
53        constexpr auto COMPILER_SIZE = operator""_MB(256ULL);
54        mem::MemConfig::Initialize(0, 0, COMPILER_SIZE, 0, 0, 0);
55        PoolManager::Initialize();
56    }
57
58    ArenaAllocator *Allocator()
59    {
60        return allocator_.get();
61    }
62
63    parser::Program *Program()
64    {
65        return &program_;
66    }
67
68    checker::ETSChecker *Checker()
69    {
70        return &checker_;
71    }
72
73    void InitializeChecker(std::string_view fileName, std::string_view src)
74    {
75        auto es2pandaPathPtr = es2pandaPath_.c_str();
76        ASSERT(es2pandaPathPtr);
77
78        InitializeChecker<parser::ETSParser, varbinder::ETSBinder, checker::ETSChecker, checker::ETSAnalyzer,
79                          compiler::ETSCompiler, compiler::ETSGen, compiler::StaticRegSpiller,
80                          compiler::ETSFunctionEmitter, compiler::ETSEmitter>(&es2pandaPathPtr, fileName, src,
81                                                                              &checker_, &program_);
82    }
83
84    template <typename CodeGen, typename RegSpiller, typename FunctionEmitter, typename Emitter, typename AstCompiler>
85    public_lib::Context::CodeGenCb MakeCompileJob()
86    {
87        return [this](public_lib::Context *context, varbinder::FunctionScope *scope,
88                      compiler::ProgramElement *programElement) -> void {
89            RegSpiller regSpiller;
90            AstCompiler astcompiler;
91            CodeGen cg(allocator_.get(), &regSpiller, context, scope, programElement, &astcompiler);
92            FunctionEmitter funcEmitter(&cg, programElement);
93            funcEmitter.Generate();
94        };
95    }
96
97    template <typename Parser, typename VarBinder, typename Checker, typename Analyzer, typename AstCompiler,
98              typename CodeGen, typename RegSpiller, typename FunctionEmitter, typename Emitter>
99    void InitializeChecker(const char **argv, std::string_view fileName, std::string_view src,
100                           checker::ETSChecker *checker, parser::Program *program)
101    {
102        auto options = std::make_unique<ark::es2panda::util::Options>();
103        if (!options->Parse(1, argv)) {
104            std::cerr << options->ErrorMsg() << std::endl;
105            return;
106        }
107
108        ark::Logger::ComponentMask mask {};
109        mask.set(ark::Logger::Component::ES2PANDA);
110        ark::Logger::InitializeStdLogging(ark::Logger::LevelFromString(options->LogLevel()), mask);
111
112        Compiler compiler(options->Extension(), options->ThreadCount());
113        SourceFile input(fileName, src, options->ParseModule());
114        compiler::CompilationUnit unit {input, *options, 0, options->Extension()};
115        auto getPhases = compiler::GetPhaseList(ScriptExtension::ETS);
116
117        program->MarkEntry();
118        auto parser =
119            Parser(program, unit.options.CompilerOptions(), static_cast<parser::ParserStatus>(unit.rawParserStatus));
120        auto analyzer = Analyzer(checker);
121        checker->SetAnalyzer(&analyzer);
122
123        auto *varbinder = program->VarBinder();
124        varbinder->SetProgram(program);
125
126        varbinder->SetContext(publicContext_.get());
127
128        auto emitter = Emitter(publicContext_.get());
129
130        auto config = public_lib::ConfigImpl {};
131        publicContext_->config = &config;
132        publicContext_->config->options = &unit.options;
133        publicContext_->sourceFile = &unit.input;
134        publicContext_->allocator = allocator_.get();
135        publicContext_->parser = &parser;
136        publicContext_->checker = checker;
137        publicContext_->analyzer = publicContext_->checker->GetAnalyzer();
138        publicContext_->emitter = &emitter;
139        publicContext_->parserProgram = program;
140
141        parser.ParseScript(unit.input, unit.options.CompilerOptions().compilationMode == CompilationMode::GEN_STD_LIB);
142        for (auto *phase : getPhases) {
143            if (!phase->Apply(publicContext_.get(), program)) {
144                return;
145            }
146        }
147    }
148
149    static checker::Type *FindClassType(varbinder::ETSBinder *varbinder, std::string_view className)
150    {
151        auto classDefs = varbinder->AsETSBinder()->GetRecordTable()->ClassDefinitions();
152        auto baseClass = std::find_if(classDefs.begin(), classDefs.end(), [className](ir::ClassDefinition *cdef) {
153            return cdef->Ident()->Name().Is(className);
154        });
155        if (baseClass == classDefs.end()) {
156            return nullptr;
157        }
158        return (*baseClass)->TsType();
159    }
160
161    static checker::Type *FindTypeAlias(checker::ETSChecker *checker, std::string_view aliasName)
162    {
163        auto *foundVar =
164            checker->Scope()->FindLocal(aliasName, varbinder::ResolveBindingOptions::TYPE_ALIASES)->AsLocalVariable();
165        if (foundVar == nullptr) {
166            return nullptr;
167        }
168        return foundVar->Declaration()->Node()->AsTSTypeAliasDeclaration()->TypeAnnotation()->TsType();
169    }
170
171    NO_COPY_SEMANTIC(UnionNormalizationTest);
172    NO_MOVE_SEMANTIC(UnionNormalizationTest);
173
174protected:
175    static constexpr uint8_t SIZE2 = 2;
176    static constexpr uint8_t SIZE3 = 3;
177    static constexpr uint8_t IDX0 = 0;
178    static constexpr uint8_t IDX1 = 1;
179    static constexpr uint8_t IDX2 = 2;
180
181private:
182    std::unique_ptr<ArenaAllocator> allocator_;
183    std::unique_ptr<public_lib::Context> publicContext_;
184    parser::Program program_;
185    std::string es2pandaPath_;
186    checker::ETSChecker checker_;
187};
188
189TEST_F(UnionNormalizationTest, UnionWithObject)
190{
191    // Test normalization: int | Object | string ==> Object
192    InitializeChecker("_.sts", "");
193
194    auto checker = Checker();
195    ASSERT(checker);
196
197    ArenaVector<checker::Type *> unionConstituents(checker->Allocator()->Adapter());
198    unionConstituents.emplace_back(checker->GlobalIntType());
199    unionConstituents.emplace_back(checker->GetGlobalTypesHolder()->GlobalETSObjectType());
200    unionConstituents.emplace_back(checker->GetGlobalTypesHolder()->GlobalETSStringBuiltinType());
201
202    // Create union type, which will be normalized inside creation function
203    auto *const normalizedType = checker->CreateETSUnionType(std::move(unionConstituents));
204    ASSERT_NE(normalizedType, nullptr);
205    ASSERT_TRUE(normalizedType->IsETSObjectType());
206    ASSERT_EQ(normalizedType, checker->GlobalETSObjectType());
207}
208
209TEST_F(UnionNormalizationTest, UnionWithIdenticalTypes1)
210{
211    // Test normalization: number | Base | string | number ==> number | Base | string
212    InitializeChecker("_.sts", "class Base {}");
213
214    auto program = Program();
215    ASSERT(program);
216
217    auto *const baseType = FindClassType(program->VarBinder()->AsETSBinder(), "Base");
218    ASSERT_NE(baseType, nullptr);
219
220    auto checker = Checker();
221    ASSERT(checker);
222
223    ArenaVector<checker::Type *> unionConstituents(checker->Allocator()->Adapter());
224    unionConstituents.emplace_back(checker->GlobalDoubleType());
225    unionConstituents.emplace_back(baseType);
226    unionConstituents.emplace_back(checker->GlobalBuiltinETSStringType());
227    unionConstituents.emplace_back(checker->GlobalDoubleType());
228
229    // Create union type, which will be normalized inside creation function
230    auto *const normalizedType = checker->CreateETSUnionType(std::move(unionConstituents));
231    ASSERT_NE(normalizedType, nullptr);
232    ASSERT_TRUE(normalizedType->IsETSUnionType());
233    auto *const unionType = normalizedType->AsETSUnionType();
234    ASSERT_EQ(unionType->ConstituentTypes().size(), SIZE3);
235    ASSERT_EQ(unionType->ConstituentTypes().at(IDX0), checker->GetGlobalTypesHolder()->GlobalDoubleBuiltinType());
236    ASSERT_EQ(unionType->ConstituentTypes().at(IDX1), baseType);
237    ASSERT_EQ(unionType->ConstituentTypes().at(IDX2), checker->GlobalBuiltinETSStringType());
238}
239
240TEST_F(UnionNormalizationTest, DISABLED_UnionWithIdenticalTypes2)
241{
242    // Test normalization: Base | int | Base | double | short | number ==> Base | number
243    InitializeChecker("_.sts", "class Base {}");
244
245    auto program = Program();
246    ASSERT(program);
247
248    auto *const baseType = FindClassType(program->VarBinder()->AsETSBinder(), "Base");
249    ASSERT_NE(baseType, nullptr);
250
251    auto checker = Checker();
252    ASSERT(checker);
253
254    ArenaVector<checker::Type *> unionConstituents(checker->Allocator()->Adapter());
255    unionConstituents.emplace_back(baseType);
256    unionConstituents.emplace_back(checker->GlobalIntType());
257    unionConstituents.emplace_back(baseType);
258    unionConstituents.emplace_back(checker->GlobalDoubleType());
259    unionConstituents.emplace_back(checker->GlobalShortType());
260    unionConstituents.emplace_back(checker->GlobalDoubleType());
261
262    // Create union type, which will be normalized inside creation function
263    auto *const normalizedType = checker->CreateETSUnionType(std::move(unionConstituents));
264    ASSERT_NE(normalizedType, nullptr);
265    ASSERT_TRUE(normalizedType->IsETSUnionType());
266    auto *const unionType = normalizedType->AsETSUnionType();
267    ASSERT_EQ(unionType->ConstituentTypes().size(), SIZE2);
268    ASSERT_EQ(unionType->ConstituentTypes().at(IDX0), baseType);
269    ASSERT_EQ(unionType->ConstituentTypes().at(IDX1), checker->GetGlobalTypesHolder()->GlobalDoubleBuiltinType());
270}
271
272TEST_F(UnionNormalizationTest, DISABLED_UnionWithNumeric1)
273{
274    // Test normalization: boolean | int | double | short ==> boolean | double
275    InitializeChecker("_.sts", "");
276
277    auto checker = Checker();
278    ASSERT(checker);
279
280    ArenaVector<checker::Type *> unionConstituents(checker->Allocator()->Adapter());
281    unionConstituents.emplace_back(checker->GlobalETSBooleanType());
282    unionConstituents.emplace_back(checker->GlobalIntType());
283    unionConstituents.emplace_back(checker->GlobalDoubleType());
284    unionConstituents.emplace_back(checker->GlobalShortType());
285
286    // Create union type, which will be normalized inside creation function
287    auto *const normalizedType = checker->CreateETSUnionType(std::move(unionConstituents));
288    ASSERT_NE(normalizedType, nullptr);
289    ASSERT_TRUE(normalizedType->IsETSUnionType());
290    auto *const unionType = normalizedType->AsETSUnionType();
291    ASSERT_EQ(unionType->ConstituentTypes().size(), SIZE2);
292    ASSERT_EQ(unionType->ConstituentTypes().at(IDX0), checker->GetGlobalTypesHolder()->GlobalETSBooleanBuiltinType());
293    ASSERT_EQ(unionType->ConstituentTypes().at(IDX1), checker->GetGlobalTypesHolder()->GlobalDoubleBuiltinType());
294}
295
296TEST_F(UnionNormalizationTest, DISABLED_UnionWithNumeric2)
297{
298    // Test normalization: string | int | Base | double | short ==> string | Base | double
299    InitializeChecker("_.sts", "class Base {}");
300
301    auto program = Program();
302    ASSERT(program);
303
304    auto *const baseType = FindClassType(program->VarBinder()->AsETSBinder(), "Base");
305    ASSERT_NE(baseType, nullptr);
306
307    auto checker = Checker();
308    ASSERT(checker);
309
310    ArenaVector<checker::Type *> unionConstituents(checker->Allocator()->Adapter());
311    unionConstituents.emplace_back(checker->GlobalBuiltinETSStringType());
312    unionConstituents.emplace_back(checker->GlobalIntType());
313    unionConstituents.emplace_back(baseType);
314    unionConstituents.emplace_back(checker->GlobalDoubleType());
315    unionConstituents.emplace_back(checker->GlobalShortType());
316
317    // Create union type, which will be normalized inside creation function
318    auto *const normalizedType = checker->CreateETSUnionType(std::move(unionConstituents));
319    ASSERT_NE(normalizedType, nullptr);
320    ASSERT_TRUE(normalizedType->IsETSUnionType());
321    auto *const unionType = normalizedType->AsETSUnionType();
322    ASSERT_EQ(unionType->ConstituentTypes().size(), SIZE3);
323    ASSERT_EQ(unionType->ConstituentTypes().at(IDX0), checker->GlobalBuiltinETSStringType());
324    ASSERT_EQ(unionType->ConstituentTypes().at(IDX1), baseType);
325    ASSERT_EQ(unionType->ConstituentTypes().at(IDX2), checker->GetGlobalTypesHolder()->GlobalDoubleBuiltinType());
326}
327
328TEST_F(UnionNormalizationTest, UnionWithSubTypes)
329{
330    // Test 4 cases of normalization
331    static constexpr std::string_view SRC =
332        "\
333        class Base {}\
334        class Derived1 extends Base {}\
335        class Derived2 extends Base {}\
336        ";
337    InitializeChecker("_.sts", SRC);
338
339    auto program = Program();
340    ASSERT(program);
341
342    auto *const baseType = FindClassType(program->VarBinder()->AsETSBinder(), "Base");
343    ASSERT_NE(baseType, nullptr);
344    auto *const derived1Type = FindClassType(program->VarBinder()->AsETSBinder(), "Derived1");
345    ASSERT_NE(derived1Type, nullptr);
346    auto *const derived2Type = FindClassType(program->VarBinder()->AsETSBinder(), "Derived2");
347    ASSERT_NE(derived2Type, nullptr);
348
349    auto checker = Checker();
350    ASSERT(checker);
351
352    // Test normalization: Derived1 | Base ==> Base
353    ArenaVector<checker::Type *> unionConstituents1(checker->Allocator()->Adapter());
354    unionConstituents1.emplace_back(derived1Type);
355    unionConstituents1.emplace_back(baseType);
356
357    // Create union type, which will be normalized inside creation function
358    auto *const normalizedType1 = checker->CreateETSUnionType(std::move(unionConstituents1));
359    ASSERT_NE(normalizedType1, nullptr);
360    ASSERT_TRUE(normalizedType1->IsETSObjectType());
361    ASSERT_EQ(normalizedType1, baseType);
362
363    // Test normalization: Base | Derived2 ==> Base
364    ArenaVector<checker::Type *> unionConstituents2(checker->Allocator()->Adapter());
365    unionConstituents2.emplace_back(baseType);
366    unionConstituents2.emplace_back(derived2Type);
367
368    // Create union type, which will be normalized inside creation function
369    auto *const normalizedType2 = checker->CreateETSUnionType(std::move(unionConstituents2));
370    ASSERT_NE(normalizedType2, nullptr);
371    ASSERT_TRUE(normalizedType2->IsETSObjectType());
372    ASSERT_EQ(normalizedType2, baseType);
373
374    // Test normalization: Derived1 | Derived2 ==> Derived1 | Derived2
375    ArenaVector<checker::Type *> unionConstituents3(checker->Allocator()->Adapter());
376    unionConstituents3.emplace_back(derived1Type);
377    unionConstituents3.emplace_back(derived2Type);
378
379    // Create union type, which will be normalized inside creation function
380    auto *const normalizedType3 = checker->CreateETSUnionType(std::move(unionConstituents3));
381    ASSERT_NE(normalizedType3, nullptr);
382    auto *const unionType = normalizedType3->AsETSUnionType();
383    ASSERT_EQ(unionType->ConstituentTypes().size(), SIZE2);
384    ASSERT_EQ(unionType->ConstituentTypes().at(IDX0), derived1Type);
385    ASSERT_EQ(unionType->ConstituentTypes().at(IDX1), derived2Type);
386
387    // Test normalization: Derived2 | Base | Derived1 ==> Base
388    ArenaVector<checker::Type *> unionConstituents4(checker->Allocator()->Adapter());
389    unionConstituents4.emplace_back(derived1Type);
390    unionConstituents4.emplace_back(baseType);
391    unionConstituents4.emplace_back(derived2Type);
392
393    // Create union type, which will be normalized inside creation function
394    auto *const normalizedType4 = checker->CreateETSUnionType(std::move(unionConstituents4));
395    ASSERT_NE(normalizedType4, nullptr);
396    ASSERT_TRUE(normalizedType4->IsETSObjectType());
397    ASSERT_EQ(normalizedType4, baseType);
398}
399
400TEST_F(UnionNormalizationTest, DISABLED_UnionLinearization)
401{
402    // Test 3 cases of normalization
403    static constexpr std::string_view SRC =
404        "\
405        class Base {}\
406        class Derived1 extends Base {}\
407        class Derived2 extends Base {}\
408        type UT = int | string\
409        \
410        type UT1 = int | (int | string) | number\
411        type UT2 = int | UT | number\
412        type UT3 = int | (Derived2 | Base) | Derived1 | (string | number | short) | (int | string)\
413        ";
414    InitializeChecker("_.sts", SRC);
415
416    auto program = Program();
417    ASSERT(program);
418
419    auto *varbinder = program->VarBinder()->AsETSBinder();
420    auto *const baseType = FindClassType(varbinder, "Base");
421    ASSERT_NE(baseType, nullptr);
422    auto *const derived1Type = FindClassType(program->VarBinder()->AsETSBinder(), "Derived1");
423    ASSERT_NE(derived1Type, nullptr);
424    auto *const derived2Type = FindClassType(program->VarBinder()->AsETSBinder(), "Derived2");
425    ASSERT_NE(derived2Type, nullptr);
426
427    auto checker = Checker();
428    ASSERT(checker);
429
430    // Test normalization: int | (int | string) | number ==> string | number
431    auto *const ut1Type = FindTypeAlias(checker, "UT1");
432    ASSERT_NE(ut1Type, nullptr);
433    ASSERT_TRUE(ut1Type->IsETSUnionType());
434    auto *ut1 = ut1Type->AsETSUnionType();
435    ASSERT_EQ(ut1->ConstituentTypes().size(), SIZE2);
436    ASSERT_EQ(ut1->ConstituentTypes().at(IDX0), checker->GlobalBuiltinETSStringType());
437    ASSERT_EQ(ut1->ConstituentTypes().at(IDX1), checker->GetGlobalTypesHolder()->GlobalDoubleBuiltinType());
438
439    // Test normalization: int | UT | number ==> string | number
440    auto *const ut2Type = FindTypeAlias(checker, "UT2");
441    ASSERT_NE(ut2Type, nullptr);
442    ASSERT_TRUE(ut2Type->IsETSUnionType());
443    auto *ut2 = ut2Type->AsETSUnionType();
444    ASSERT_EQ(ut2->ConstituentTypes().size(), SIZE2);
445    ASSERT_EQ(ut2->ConstituentTypes().at(IDX0), checker->GlobalBuiltinETSStringType());
446    ASSERT_EQ(ut2->ConstituentTypes().at(IDX1), checker->GetGlobalTypesHolder()->GlobalDoubleBuiltinType());
447
448    // Test normalization:
449    // int | (Derived2 | Base) | Derived1 | (string | number | short) | (int | string) ==> Base | string | number
450    auto *const ut3Type = FindTypeAlias(checker, "UT3");
451    ASSERT_NE(ut3Type, nullptr);
452    ASSERT_TRUE(ut3Type->IsETSUnionType());
453    auto *ut3 = ut3Type->AsETSUnionType();
454    ASSERT_EQ(ut3->ConstituentTypes().size(), SIZE3);
455    ASSERT_EQ(ut3->ConstituentTypes().at(IDX0), baseType);
456    ASSERT_EQ(ut3->ConstituentTypes().at(IDX1), checker->GlobalBuiltinETSStringType());
457    ASSERT_EQ(ut3->ConstituentTypes().at(IDX2), checker->GetGlobalTypesHolder()->GlobalDoubleBuiltinType());
458}
459
460TEST_F(UnionNormalizationTest, UnionStringLiterals1)
461{
462    InitializeChecker("_.sts", "");
463
464    auto checker = Checker();
465    ASSERT(checker);
466
467    // Test normalization: string | "abc" ==> string
468    ArenaVector<checker::Type *> unionConstituents1(checker->Allocator()->Adapter());
469    unionConstituents1.emplace_back(checker->GlobalBuiltinETSStringType());
470    unionConstituents1.emplace_back(checker->CreateETSStringLiteralType("abc"));
471
472    // Create union type, which will be normalized inside creation function
473    auto *const normalizedType1 = checker->CreateETSUnionType(std::move(unionConstituents1));
474    ASSERT_NE(normalizedType1, nullptr);
475    ASSERT_TRUE(normalizedType1->IsETSObjectType());
476    ASSERT_EQ(normalizedType1, checker->GlobalBuiltinETSStringType());
477
478    // Test normalization: "abc" | string | string ==> string
479    ArenaVector<checker::Type *> unionConstituents2(checker->Allocator()->Adapter());
480    unionConstituents2.emplace_back(checker->CreateETSStringLiteralType("abc"));
481    unionConstituents2.emplace_back(checker->GlobalBuiltinETSStringType());
482    unionConstituents2.emplace_back(checker->GlobalBuiltinETSStringType());
483
484    // Create union type, which will be normalized inside creation function
485    auto *const normalizedType2 = checker->CreateETSUnionType(std::move(unionConstituents2));
486    ASSERT_NE(normalizedType2, nullptr);
487    ASSERT_TRUE(normalizedType2->IsETSObjectType());
488    ASSERT_EQ(normalizedType2, checker->GlobalBuiltinETSStringType());
489
490    // Test normalization: number | "abc" | string | "xy" ==> number | string
491    ArenaVector<checker::Type *> unionConstituents3(checker->Allocator()->Adapter());
492    unionConstituents3.emplace_back(checker->GlobalDoubleType());
493    unionConstituents3.emplace_back(checker->CreateETSStringLiteralType("abc"));
494    unionConstituents3.emplace_back(checker->GlobalBuiltinETSStringType());
495    unionConstituents3.emplace_back(checker->CreateETSStringLiteralType("xy"));
496
497    // Create union type, which will be normalized inside creation function
498    auto *const normalizedType3 = checker->CreateETSUnionType(std::move(unionConstituents3));
499    ASSERT_NE(normalizedType3, nullptr);
500    ASSERT_TRUE(normalizedType3->IsETSUnionType());
501    auto *const unionType = normalizedType3->AsETSUnionType();
502    ASSERT_EQ(unionType->ConstituentTypes().size(), SIZE2);
503    ASSERT_EQ(unionType->ConstituentTypes().at(IDX0), checker->GetGlobalTypesHolder()->GlobalDoubleBuiltinType());
504    ASSERT_EQ(unionType->ConstituentTypes().at(IDX1), checker->GlobalBuiltinETSStringType());
505
506    // Test normalization: "abcd" | "abcd" | "abcd" ==> "abcd"
507    ArenaVector<checker::Type *> unionConstituents4(checker->Allocator()->Adapter());
508    unionConstituents4.emplace_back(checker->CreateETSStringLiteralType("abcd"));
509    unionConstituents4.emplace_back(checker->CreateETSStringLiteralType("abcd"));
510    unionConstituents4.emplace_back(checker->CreateETSStringLiteralType("abcd"));
511
512    // Create union type, which will be normalized inside creation function
513    auto *const normalizedType4 = checker->CreateETSUnionType(std::move(unionConstituents4));
514    ASSERT_NE(normalizedType4, nullptr);
515    ASSERT_TRUE(normalizedType4->IsETSStringType());
516    ASSERT_EQ(normalizedType4->AsETSStringType()->GetValue(), "abcd");
517}
518
519TEST_F(UnionNormalizationTest, UnionStringLiterals2)
520{
521    InitializeChecker("_.sts", "");
522
523    auto checker = Checker();
524    ASSERT(checker);
525
526    // Test absence of normalization: "ab1" | "bc2" | "cd3" ==> "ab1" | "bc2" | "cd3"
527    ArenaVector<checker::Type *> unionConstituents1(checker->Allocator()->Adapter());
528    unionConstituents1.emplace_back(checker->CreateETSStringLiteralType("ab1"));
529    unionConstituents1.emplace_back(checker->CreateETSStringLiteralType("bc2"));
530    unionConstituents1.emplace_back(checker->CreateETSStringLiteralType("cd3"));
531
532    // Create union type, which will be normalized inside creation function
533    auto *const normalizedType1 = checker->CreateETSUnionType(std::move(unionConstituents1));
534    ASSERT_NE(normalizedType1, nullptr);
535    ASSERT_TRUE(normalizedType1->IsETSUnionType());
536    auto *const unionType1 = normalizedType1->AsETSUnionType();
537    ASSERT_EQ(unionType1->ConstituentTypes().size(), SIZE3);
538    ASSERT_TRUE(unionType1->ConstituentTypes().at(IDX0)->IsETSStringType());
539    ASSERT_EQ(unionType1->ConstituentTypes().at(IDX0)->AsETSStringType()->GetValue(), "ab1");
540    ASSERT_TRUE(unionType1->ConstituentTypes().at(IDX1)->IsETSStringType());
541    ASSERT_EQ(unionType1->ConstituentTypes().at(IDX1)->AsETSStringType()->GetValue(), "bc2");
542    ASSERT_TRUE(unionType1->ConstituentTypes().at(IDX2)->IsETSStringType());
543    ASSERT_EQ(unionType1->ConstituentTypes().at(IDX2)->AsETSStringType()->GetValue(), "cd3");
544
545    // Test normalization: "ab1" | "bc2" | "ab1" ==> "ab1" | "bc2"
546    ArenaVector<checker::Type *> unionConstituents2(checker->Allocator()->Adapter());
547    unionConstituents2.emplace_back(checker->CreateETSStringLiteralType("ab1"));
548    unionConstituents2.emplace_back(checker->CreateETSStringLiteralType("bc2"));
549    unionConstituents2.emplace_back(checker->CreateETSStringLiteralType("ab1"));
550
551    // Create union type, which will be normalized inside creation function
552    auto *const normalizedType2 = checker->CreateETSUnionType(std::move(unionConstituents2));
553    ASSERT_NE(normalizedType2, nullptr);
554    ASSERT_TRUE(normalizedType2->IsETSUnionType());
555    auto *const unionType2 = normalizedType2->AsETSUnionType();
556    ASSERT_EQ(unionType2->ConstituentTypes().size(), SIZE2);
557    ASSERT_TRUE(unionType2->ConstituentTypes().at(IDX0)->IsETSStringType());
558    ASSERT_EQ(unionType2->ConstituentTypes().at(IDX0)->AsETSStringType()->GetValue(), "ab1");
559    ASSERT_TRUE(unionType2->ConstituentTypes().at(IDX1)->IsETSStringType());
560    ASSERT_EQ(unionType2->ConstituentTypes().at(IDX1)->AsETSStringType()->GetValue(), "bc2");
561
562    // Test absence of normalization: "ab1" | "bc2" | "cd3" | string | int ==> string | int
563    ArenaVector<checker::Type *> unionConstituents3(checker->Allocator()->Adapter());
564    unionConstituents3.emplace_back(checker->CreateETSStringLiteralType("ab1"));
565    unionConstituents3.emplace_back(checker->CreateETSStringLiteralType("bc2"));
566    unionConstituents3.emplace_back(checker->CreateETSStringLiteralType("cd3"));
567    unionConstituents3.emplace_back(checker->GlobalBuiltinETSStringType());
568    unionConstituents3.emplace_back(checker->GlobalIntType());
569
570    // Create union type, which will be normalized inside creation function
571    auto *const normalizedType3 = checker->CreateETSUnionType(std::move(unionConstituents3));
572    ASSERT_NE(normalizedType3, nullptr);
573    ASSERT_TRUE(normalizedType3->IsETSUnionType());
574    auto *const unionType3 = normalizedType3->AsETSUnionType();
575    ASSERT_EQ(unionType3->ConstituentTypes().size(), SIZE2);
576    ASSERT_EQ(unionType3->ConstituentTypes().at(IDX0), checker->GlobalBuiltinETSStringType());
577    ASSERT_EQ(unionType3->ConstituentTypes().at(IDX1), checker->GetGlobalTypesHolder()->GlobalIntegerBuiltinType());
578}
579
580TEST_F(UnionNormalizationTest, DISABLED_UnionWithNever)
581{
582    // Test normalization: int | never | number ==> number
583    InitializeChecker("_.sts", "");
584
585    auto checker = Checker();
586    ASSERT(checker);
587
588    ArenaVector<checker::Type *> unionConstituents(checker->Allocator()->Adapter());
589    unionConstituents.emplace_back(checker->GlobalIntType());
590    unionConstituents.emplace_back(checker->GetGlobalTypesHolder()->GlobalBuiltinNeverType());
591    unionConstituents.emplace_back(checker->GetGlobalTypesHolder()->GlobalDoubleBuiltinType());
592
593    // Create union type, which will be normalized inside creation function
594    auto *const normalizedType = checker->CreateETSUnionType(std::move(unionConstituents));
595    ASSERT_NE(normalizedType, nullptr);
596    ASSERT_TRUE(normalizedType->IsETSObjectType());
597    ASSERT_EQ(normalizedType, checker->GetGlobalTypesHolder()->GlobalDoubleBuiltinType());
598}
599
600}  // namespace ark::es2panda
601