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