1/**
2 * Copyright (c) 2023-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 "bigintLowering.h"
17
18#include "compiler/lowering/scopesInit/scopesInitPhase.h"
19#include "compiler/lowering/util.h"
20
21namespace ark::es2panda::compiler {
22
23std::string_view BigIntLowering::Name() const
24{
25    return "BigIntLowering";
26}
27
28ir::Expression *CreateBigInt(public_lib::Context *ctx, ir::BigIntLiteral *literal)
29{
30    auto parser = ctx->parser->AsETSParser();
31    auto checker = ctx->checker->AsETSChecker();
32
33    // This will change the bigint literal node into the new class instance expression:
34    // 123456n => new BigInt("123456")
35    std::string src {"new "};
36    src += Signatures::BUILTIN_BIGINT_CLASS;
37    src += "(\"";
38    src += literal->Str().Utf8();
39    src += "\")";
40
41    auto loweringResult = parser->CreateExpression(src);
42    loweringResult->SetParent(literal->Parent());
43
44    InitScopesPhaseETS::RunExternalNode(loweringResult, checker->VarBinder());
45    checker->VarBinder()->AsETSBinder()->ResolveReferencesForScope(loweringResult, NearestScope(loweringResult));
46    loweringResult->Check(checker);
47
48    return loweringResult;
49}
50
51bool ReplaceStrictEqualByNormalEqual(ir::BinaryExpression *expr)
52{
53    auto left = expr->Left()->TsType();
54    auto isBigintLeft = (left != nullptr && left->IsETSBigIntType()) || expr->Left()->IsBigIntLiteral();
55    auto right = expr->Right()->TsType();
56    auto isBigintRight = (right != nullptr && right->IsETSBigIntType()) || expr->Right()->IsBigIntLiteral();
57    if (!isBigintLeft && !isBigintRight) {
58        return false;
59    }
60
61    if (expr->OperatorType() == lexer::TokenType::PUNCTUATOR_STRICT_EQUAL) {
62        expr->SetOperator(lexer::TokenType::PUNCTUATOR_EQUAL);
63    } else if (expr->OperatorType() == lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL) {
64        expr->SetOperator(lexer::TokenType::PUNCTUATOR_NOT_EQUAL);
65    } else {
66        return false;
67    }
68
69    return true;
70}
71
72// Currently there are no compile time operations for bigint.
73bool RemoveConst(ir::BinaryExpression *expr)
74{
75    bool isRemoved = false;
76    auto left = expr->Left()->TsType();
77    if (left != nullptr && left->IsETSBigIntType()) {
78        left->RemoveTypeFlag(checker::TypeFlag::CONSTANT);
79        isRemoved = true;
80    }
81
82    auto right = expr->Right()->TsType();
83    if (right != nullptr && right->IsETSBigIntType()) {
84        right->RemoveTypeFlag(checker::TypeFlag::CONSTANT);
85        isRemoved = true;
86    }
87
88    return isRemoved;
89}
90
91bool BigIntLowering::Perform(public_lib::Context *const ctx, parser::Program *const program)
92{
93    for (const auto &[_, ext_programs] : program->ExternalSources()) {
94        (void)_;
95        for (auto *const extProg : ext_programs) {
96            Perform(ctx, extProg);
97        }
98    }
99
100    auto checker = ctx->checker->AsETSChecker();
101
102    program->Ast()->TransformChildrenRecursively(
103        [ctx, checker](ir::AstNode *ast) -> ir::AstNode * {
104            if (ast->IsBigIntLiteral() && ast->Parent() != nullptr && ast->Parent()->IsClassProperty()) {
105                return CreateBigInt(ctx, ast->AsBigIntLiteral());
106            }
107
108            if (ast->IsBinaryExpression()) {
109                auto expr = ast->AsBinaryExpression();
110                bool doCheck = ReplaceStrictEqualByNormalEqual(expr);
111                doCheck |= RemoveConst(expr);
112                if (doCheck) {
113                    expr->Check(checker);
114                }
115            }
116
117            return ast;
118        },
119        Name());
120
121    return true;
122}
123
124}  // namespace ark::es2panda::compiler
125