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 "varbinder/ETSBinder.h"
17#include "stringComparison.h"
18#include "checker/ETSchecker.h"
19#include "parser/parserImpl.h"
20#include "utils/arena_containers.h"
21#include "compiler/lowering/scopesInit/scopesInitPhase.h"
22#include "compiler/lowering/util.h"
23
24namespace ark::es2panda::compiler {
25
26/**
27 * Check if we got String comparison such like < , <=, >, >=, e.g.
28 *
29 *   let a:String = "AAAA"
30 *   let b:String = "BBB"
31 *   ..
32 *   if (a >= b) {..}
33 *
34 * so such test has to be updated by our lowering to
35 *
36 *   if (a.CompareTo(b) >= 0)
37 */
38
39bool CheckOperatorType(ir::BinaryExpression *expr)
40{
41    switch (expr->OperatorType()) {
42        case lexer::TokenType::PUNCTUATOR_LESS_THAN:
43        case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL:
44        case lexer::TokenType::PUNCTUATOR_GREATER_THAN:
45        case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL: {
46            return true;
47        }
48        default:
49            return false;
50    }
51}
52
53bool StringComparisonLowering::IsStringComparison(ir::AstNode *node)
54{
55    if (node->IsBinaryExpression()) {
56        auto *expr = node->AsBinaryExpression();
57
58        if (!CheckOperatorType(expr)) {
59            return false;
60        }
61
62        if ((expr->Left()->TsType() == nullptr) || (expr->Right()->TsType() == nullptr)) {
63            return false;
64        }
65
66        if (expr->Left()->TsType()->IsETSStringType() && expr->Right()->TsType()->IsETSStringType()) {
67            return true;
68        }
69    }
70    return false;
71}
72
73void StringComparisonLowering::ProcessBinaryExpression(ir::BinaryExpression *expr, public_lib::Context *ctx)
74{
75    ASSERT(expr->IsBinaryExpression());
76    ASSERT(expr->Left()->TsType()->IsETSStringType() && expr->Right()->TsType()->IsETSStringType());
77
78    // reset types is any, will re-run checker to set them once again properly
79    expr->SetTsType(nullptr);
80
81    checker::ETSChecker *checker = ctx->checker->AsETSChecker();
82    ArenaVector<ir::Expression *> callArgs(checker->Allocator()->Adapter());
83    ir::Expression *accessor = nullptr;
84    auto *zeroExpr = checker->AllocNode<ir::NumberLiteral>(util::StringView("0"));
85    auto *const callee = checker->AllocNode<ir::Identifier>("compareTo", checker->Allocator());
86    accessor = checker->AllocNode<ir::MemberExpression>(expr->Left(), callee, ir::MemberExpressionKind::PROPERTY_ACCESS,
87                                                        false, false);
88    callee->SetReference();
89
90    callArgs.push_back(expr->Right());
91    auto callExpression = checker->AllocNode<ir::CallExpression>(accessor, std::move(callArgs), nullptr, false, false);
92    expr->SetLeft(callExpression);
93    expr->SetRight(zeroExpr);
94
95    auto *parent = expr->Parent();
96    if (parent->IsBinaryExpression()) {
97        parent->AsBinaryExpression()->SetTsType(nullptr);
98    }
99
100    InitScopesPhaseETS::RunExternalNode(expr, ctx->checker->VarBinder());
101    checker->VarBinder()->AsETSBinder()->ResolveReferencesForScope(parent, NearestScope(parent));
102    parent->Check(checker);
103}
104
105bool StringComparisonLowering::Perform(public_lib::Context *ctx, parser::Program *program)
106{
107    for (auto &[_, extPrograms] : program->ExternalSources()) {
108        (void)_;
109        for (auto *extProg : extPrograms) {
110            Perform(ctx, extProg);
111        }
112    }
113
114    checker::ETSChecker *checker = ctx->checker->AsETSChecker();
115    [[maybe_unused]] ArenaVector<ir::BinaryExpression *> foundNodes(checker->Allocator()->Adapter());
116    program->Ast()->IterateRecursively([&foundNodes, this](ir::AstNode *ast) -> ir::AstNode * {
117        if (IsStringComparison(ast)) {
118            foundNodes.push_back(ast->AsBinaryExpression());
119        }
120        return ast;
121    });
122
123    for ([[maybe_unused]] auto &it : foundNodes) {
124        ProcessBinaryExpression(it, ctx);
125    }
126
127    return true;
128}
129}  // namespace ark::es2panda::compiler
130