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