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
25 namespace ark::es2panda::compiler {
26
27 // NOLINTBEGIN(modernize-avoid-c-arrays)
28 static 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 "};";
33 static 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);";
39 static constexpr char const CAST_NEW_DIMENSION_EXPRESSION[] = "@@I1 as int";
40 static constexpr char const CAST_OLD_DIMENSION_EXPRESSION[] = "(@@E1) as int";
41 // NOLINTEND(modernize-avoid-c-arrays)
42
ProcessNewArrayInstanceExpression(parser::ETSParser * parser,checker::ETSChecker * checker,ir::ETSNewArrayInstanceExpression * newInstanceExpression) const43 ir::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->MaybeUnboxInRelation(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
ProcessNewMultiDimArrayInstanceExpression(parser::ETSParser * parser,checker::ETSChecker * checker,ir::ETSNewMultiDimArrayInstanceExpression * newInstanceExpression) const82 ir::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->MaybeUnboxInRelation(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
CreateNewMultiDimArrayInstanceExpression(checker::ETSChecker * checker,ir::ETSNewMultiDimArrayInstanceExpression * newInstanceExpression,ir::BlockExpression * blockExpression) const138 ir::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
Perform(public_lib::Context * ctx,parser::Program * program)153 bool 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 // CC-OFFNXT(G.FMT.14-CPP) project code style
162 [this, parser, checker](ir::AstNode *const ast) -> ir::AstNode * {
163 if (ast->IsETSNewArrayInstanceExpression()) {
164 return ProcessNewArrayInstanceExpression(parser, checker, ast->AsETSNewArrayInstanceExpression());
165 }
166
167 if (ast->IsETSNewMultiDimArrayInstanceExpression()) {
168 return ProcessNewMultiDimArrayInstanceExpression(parser, checker,
169 ast->AsETSNewMultiDimArrayInstanceExpression());
170 }
171
172 return ast;
173 },
174 Name());
175
176 return true;
177 }
178
179 } // namespace ark::es2panda::compiler
180