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 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
Perform(public_lib::Context * ctx,parser::Program * program)105 bool 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