1/*
2 * Copyright (c) 2021-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//
17// desc: Object index access syntax is translated to the call of special setter (in case of assignment):
18//       "obj[i] = val; => obj.S_set(i, val);"
19//   	 or getter (in all the other cases):
20//   	 "...obj[i]... => ...obj.S_get(i)..."
21//      methods.
22//
23
24#include "objectIndexAccess.h"
25
26#include "checker/ETSchecker.h"
27#include "parser/ETSparser.h"
28
29namespace ark::es2panda::compiler {
30ir::Expression *ObjectIndexLowering::ProcessIndexSetAccess(parser::ETSParser *parser, checker::ETSChecker *checker,
31                                                           ir::AssignmentExpression *assignmentExpression) const
32{
33    //  Note! We assume that parser and checker phase nave been already passed correctly, thus the class has
34    //  required accessible index method[s] and all the types are properly resolved.
35    static std::string const CALL_EXPRESSION =
36        std::string {"@@E1."} + std::string {compiler::Signatures::SET_INDEX_METHOD} + "(@@E2, @@E3)";
37
38    // Parse ArkTS code string and create and process corresponding AST node(s)
39    auto *const memberExpression = assignmentExpression->Left()->AsMemberExpression();
40    auto *const loweringResult = parser->CreateFormattedExpression(
41        CALL_EXPRESSION, memberExpression->Object(), memberExpression->Property(), assignmentExpression->Right());
42    loweringResult->SetParent(assignmentExpression->Parent());
43    loweringResult->SetRange(assignmentExpression->Range());
44
45    loweringResult->Check(checker);
46    return loweringResult;
47}
48
49ir::Expression *ObjectIndexLowering::ProcessIndexGetAccess(parser::ETSParser *parser, checker::ETSChecker *checker,
50                                                           ir::MemberExpression *memberExpression) const
51{
52    //  Note! We assume that parser and checker phase nave been already passed correctly, thus the class has
53    //  required accessible index method[s] and all the types are properly resolved.
54    static std::string const CALL_EXPRESSION =
55        std::string {"@@E1."} + std::string {compiler::Signatures::GET_INDEX_METHOD} + "(@@E2)";
56
57    // Parse ArkTS code string and create and process corresponding AST node(s)
58    auto *const loweringResult =
59        parser->CreateFormattedExpression(CALL_EXPRESSION, memberExpression->Object(), memberExpression->Property());
60    loweringResult->SetParent(memberExpression->Parent());
61    loweringResult->SetRange(memberExpression->Range());
62
63    loweringResult->Check(checker);
64    loweringResult->SetBoxingUnboxingFlags(memberExpression->GetBoxingUnboxingFlags());
65    return loweringResult;
66}
67
68bool ObjectIndexLowering::Perform(public_lib::Context *ctx, parser::Program *program)
69{
70    if (ctx->config->options->CompilerOptions().compilationMode == CompilationMode::GEN_STD_LIB) {
71        for (auto &[_, extPrograms] : program->ExternalSources()) {
72            (void)_;
73            for (auto *extProg : extPrograms) {
74                Perform(ctx, extProg);
75            }
76        }
77    }
78
79    auto *const parser = ctx->parser->AsETSParser();
80    ASSERT(parser != nullptr);
81    auto *const checker = ctx->checker->AsETSChecker();
82    ASSERT(checker != nullptr);
83
84    program->Ast()->TransformChildrenRecursively(
85        [this, parser, checker](ir::AstNode *const ast) -> ir::AstNode * {
86            if (ast->IsAssignmentExpression() && ast->AsAssignmentExpression()->Left()->IsMemberExpression() &&
87                ast->AsAssignmentExpression()->Left()->AsMemberExpression()->Kind() ==
88                    ir::MemberExpressionKind::ELEMENT_ACCESS) {
89                if (auto const *const objectType =
90                        ast->AsAssignmentExpression()->Left()->AsMemberExpression()->ObjType();
91                    objectType != nullptr && !objectType->IsETSDynamicType()) {
92                    return ProcessIndexSetAccess(parser, checker, ast->AsAssignmentExpression());
93                }
94            }
95            return ast;
96        },
97        Name());
98
99    program->Ast()->TransformChildrenRecursively(
100        [this, parser, checker](ir::AstNode *const ast) -> ir::AstNode * {
101            if (ast->IsMemberExpression() &&
102                ast->AsMemberExpression()->Kind() == ir::MemberExpressionKind::ELEMENT_ACCESS) {
103                if (auto const *const objectType = ast->AsMemberExpression()->ObjType();
104                    objectType != nullptr && !objectType->IsETSDynamicType()) {
105                    return ProcessIndexGetAccess(parser, checker, ast->AsMemberExpression());
106                }
107            }
108            return ast;
109        },
110        Name());
111
112    return true;
113}
114
115bool ObjectIndexLowering::Postcondition(public_lib::Context *ctx, const parser::Program *program)
116{
117    auto checkExternalPrograms = [this, ctx](const ArenaVector<parser::Program *> &programs) {
118        for (auto *p : programs) {
119            if (!Postcondition(ctx, p)) {
120                return false;
121            }
122        }
123        return true;
124    };
125
126    if (ctx->config->options->CompilerOptions().compilationMode == CompilationMode::GEN_STD_LIB) {
127        for (auto &[_, extPrograms] : program->ExternalSources()) {
128            (void)_;
129            if (!checkExternalPrograms(extPrograms)) {
130                return false;
131            };
132        }
133    }
134
135    return !program->Ast()->IsAnyChild([](const ir::AstNode *ast) {
136        if (ast->IsMemberExpression() &&
137            ast->AsMemberExpression()->Kind() == ir::MemberExpressionKind::ELEMENT_ACCESS) {
138            if (auto const *const objectType = ast->AsMemberExpression()->ObjType(); objectType != nullptr) {
139                return !objectType->IsETSDynamicType();
140            }
141        }
142        return false;
143    });
144}
145
146}  // namespace ark::es2panda::compiler
147