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 "expandBrackets.h" 17 18#include "checker/ETSchecker.h" 19#include "compiler/lowering/util.h" 20#include "compiler/lowering/scopesInit/scopesInitPhase.h" 21#include "parser/ETSparser.h" 22#include "varbinder/ETSBinder.h" 23#include "varbinder/scope.h" 24 25namespace ark::es2panda::compiler { 26 27// NOLINTBEGIN(modernize-avoid-c-arrays) 28static constexpr char const FORMAT_NEW_MULTI_DIM_ARRAY_EXPRESSION[] = 29 "let @@I1: @@T2 = (@@E3);" 30 "if (!isSafeInteger(@@I4)) {" 31 " throw new TypeError(\"Fractional part of index expression should be zero.\");" 32 "};"; 33static constexpr char const FORMAT_NEW_ARRAY_EXPRESSION[] = 34 "let @@I1: @@T2 = (@@E3);" 35 "if (!isSafeInteger(@@I4)) {" 36 " throw new TypeError(\"Fractional part of index expression should be zero.\");" 37 "};" 38 "(@@E5);"; 39static constexpr char const CAST_NEW_DIMENSION_EXPRESSION[] = "@@I1 as int"; 40static constexpr char const CAST_OLD_DIMENSION_EXPRESSION[] = "(@@E1) as int"; 41// NOLINTEND(modernize-avoid-c-arrays) 42 43ir::Expression *ExpandBracketsPhase::ProcessNewArrayInstanceExpression( 44 parser::ETSParser *parser, checker::ETSChecker *checker, 45 ir::ETSNewArrayInstanceExpression *newInstanceExpression) const 46{ 47 auto *dimension = newInstanceExpression->Dimension(); 48 auto *dimType = dimension->TsType(); 49 if (auto *unboxed = checker->ETSBuiltinTypeAsPrimitiveType(dimType); unboxed != nullptr) { 50 dimType = unboxed; 51 } 52 if (!dimType->HasTypeFlag(checker::TypeFlag::ETS_FLOATING_POINT)) { 53 return newInstanceExpression; 54 } 55 56 auto *scope = NearestScope(newInstanceExpression); 57 if (scope == nullptr) { 58 scope = checker->VarBinder()->VarScope() != nullptr ? checker->VarBinder()->VarScope() 59 : checker->VarBinder()->TopScope(); 60 } 61 auto expressionCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(checker->VarBinder(), scope); 62 63 auto const identName = GenName(checker->Allocator()); 64 auto *exprType = checker->AllocNode<ir::OpaqueTypeNode>(dimType); 65 auto *const newInstanceParent = newInstanceExpression->Parent(); 66 67 auto *blockExpression = parser->CreateFormattedExpression(FORMAT_NEW_ARRAY_EXPRESSION, identName, exprType, 68 dimension, identName, newInstanceExpression); 69 blockExpression->SetParent(newInstanceParent); 70 71 auto *castedDimension = parser->CreateFormattedExpression(CAST_NEW_DIMENSION_EXPRESSION, identName); 72 newInstanceExpression->SetDimension(castedDimension); 73 74 newInstanceExpression->SetTsType(nullptr); 75 InitScopesPhaseETS::RunExternalNode(blockExpression, checker->VarBinder()); 76 checker->VarBinder()->AsETSBinder()->ResolveReferencesForScope(blockExpression, NearestScope(blockExpression)); 77 blockExpression->Check(checker); 78 79 return blockExpression; 80} 81 82ir::Expression *ExpandBracketsPhase::ProcessNewMultiDimArrayInstanceExpression( 83 parser::ETSParser *parser, checker::ETSChecker *checker, 84 ir::ETSNewMultiDimArrayInstanceExpression *newInstanceExpression) const 85{ 86 ir::BlockExpression *returnExpression = nullptr; 87 88 auto *scope = NearestScope(newInstanceExpression); 89 if (scope == nullptr) { 90 scope = checker->VarBinder()->VarScope() != nullptr ? checker->VarBinder()->VarScope() 91 : checker->VarBinder()->TopScope(); 92 } 93 auto expressionCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(checker->VarBinder(), scope); 94 95 for (std::size_t i = 0U; i < newInstanceExpression->Dimensions().size(); ++i) { 96 auto *dimension = newInstanceExpression->Dimensions()[i]; 97 auto *dimType = dimension->TsType(); 98 if (auto *unboxed = checker->ETSBuiltinTypeAsPrimitiveType(dimType); unboxed != nullptr) { 99 dimType = unboxed; 100 } 101 if (!dimType->HasTypeFlag(checker::TypeFlag::ETS_FLOATING_POINT)) { 102 continue; 103 } 104 105 if (dimension->IsNumberLiteral()) { 106 auto *castedDimension = parser->CreateFormattedExpression(CAST_OLD_DIMENSION_EXPRESSION, dimension); 107 castedDimension->SetParent(newInstanceExpression); 108 newInstanceExpression->Dimensions()[i] = castedDimension; 109 } else { 110 auto const identName = GenName(checker->Allocator()); 111 auto *exprType = checker->AllocNode<ir::OpaqueTypeNode>(dimType); 112 113 auto *blockExpression = parser 114 ->CreateFormattedExpression(FORMAT_NEW_MULTI_DIM_ARRAY_EXPRESSION, identName, 115 exprType, dimension, identName) 116 ->AsBlockExpression(); 117 118 if (returnExpression == nullptr) { 119 returnExpression = blockExpression; 120 } else { 121 returnExpression->AddStatements(blockExpression->Statements()); 122 } 123 124 auto *castedDimension = parser->CreateFormattedExpression(CAST_NEW_DIMENSION_EXPRESSION, identName); 125 castedDimension->SetParent(newInstanceExpression); 126 newInstanceExpression->Dimensions()[i] = castedDimension; 127 } 128 } 129 130 if (returnExpression != nullptr) { 131 return CreateNewMultiDimArrayInstanceExpression(checker, newInstanceExpression, returnExpression); 132 } 133 134 return newInstanceExpression; 135} 136 137// NOTE: Just to reduce the size of 'ProcessNewMultiDimArrayInstanceExpression' method 138ir::Expression *ExpandBracketsPhase::CreateNewMultiDimArrayInstanceExpression( 139 checker::ETSChecker *checker, ir::ETSNewMultiDimArrayInstanceExpression *newInstanceExpression, 140 ir::BlockExpression *blockExpression) const 141{ 142 blockExpression->SetParent(newInstanceExpression->Parent()); 143 newInstanceExpression->SetTsType(nullptr); 144 blockExpression->AddStatement(checker->AllocNode<ir::ExpressionStatement>(newInstanceExpression)); 145 146 InitScopesPhaseETS::RunExternalNode(blockExpression, checker->VarBinder()); 147 checker->VarBinder()->AsETSBinder()->ResolveReferencesForScope(blockExpression, NearestScope(blockExpression)); 148 blockExpression->Check(checker); 149 150 return blockExpression; 151} 152 153bool ExpandBracketsPhase::Perform(public_lib::Context *ctx, parser::Program *program) 154{ 155 auto *const parser = ctx->parser->AsETSParser(); 156 ASSERT(parser != nullptr); 157 auto *const checker = ctx->checker->AsETSChecker(); 158 ASSERT(checker != nullptr); 159 160 program->Ast()->TransformChildrenRecursively( 161 [this, parser, checker](ir::AstNode *const ast) -> ir::AstNode * { 162 if (ast->IsETSNewArrayInstanceExpression()) { 163 return ProcessNewArrayInstanceExpression(parser, checker, ast->AsETSNewArrayInstanceExpression()); 164 } 165 166 if (ast->IsETSNewMultiDimArrayInstanceExpression()) { 167 return ProcessNewMultiDimArrayInstanceExpression(parser, checker, 168 ast->AsETSNewMultiDimArrayInstanceExpression()); 169 } 170 171 return ast; 172 }, 173 Name()); 174 175 return true; 176} 177 178} // namespace ark::es2panda::compiler 179