1 /**
2 * Copyright (c) 2023-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 "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
24 namespace 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
CheckOperatorType(ir::BinaryExpression * expr)39 bool 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
IsStringComparison(ir::AstNode * node)53 bool 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
ProcessBinaryExpression(ir::BinaryExpression * expr,public_lib::Context * ctx)73 void StringComparisonLowering::ProcessBinaryExpression(ir::BinaryExpression *expr, public_lib::Context *ctx)
74 {
75 ES2PANDA_ASSERT(expr->IsBinaryExpression());
76 ES2PANDA_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 ES2PANDA_ASSERT(callee != nullptr);
87 auto *var = checker->GlobalBuiltinETSStringType()->GetProperty(callee->AsIdentifier()->Name(),
88 checker::PropertySearchFlags::SEARCH_METHOD);
89 callee->SetVariable(var);
90 accessor = checker->AllocNode<ir::MemberExpression>(expr->Left(), callee, ir::MemberExpressionKind::PROPERTY_ACCESS,
91 false, false);
92
93 callArgs.push_back(expr->Right());
94 auto callExpression = checker->AllocNode<ir::CallExpression>(accessor, std::move(callArgs), nullptr, false, false);
95 expr->SetLeft(callExpression);
96 expr->SetRight(zeroExpr);
97
98 auto *parent = expr->Parent();
99 InitScopesPhaseETS::RunExternalNode(expr, ctx->checker->VarBinder());
100 checker->VarBinder()->AsETSBinder()->ResolveReferencesForScope(parent, NearestScope(parent));
101
102 if (parent->IsBinaryExpression() || parent->IsConditionalExpression()) {
103 parent->AsExpression()->SetTsType(nullptr);
104 parent->Check(checker);
105 } else {
106 expr->Check(checker);
107 }
108 }
109
PerformForModule(public_lib::Context * ctx,parser::Program * program)110 bool StringComparisonLowering::PerformForModule(public_lib::Context *ctx, parser::Program *program)
111 {
112 checker::ETSChecker *checker = ctx->checker->AsETSChecker();
113 [[maybe_unused]] ArenaVector<ir::BinaryExpression *> foundNodes(checker->Allocator()->Adapter());
114 // CC-OFFNXT(G.FMT.14-CPP) project code style
115 program->Ast()->IterateRecursively([&foundNodes, this](ir::AstNode *ast) -> ir::AstNode * {
116 if (IsStringComparison(ast)) {
117 foundNodes.push_back(ast->AsBinaryExpression());
118 }
119 return ast;
120 });
121
122 for ([[maybe_unused]] auto &it : foundNodes) {
123 ProcessBinaryExpression(it, ctx);
124 }
125
126 return true;
127 }
128 } // namespace ark::es2panda::compiler
129