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