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 
29 namespace ark::es2panda::compiler {
ProcessIndexSetAccess(parser::ETSParser *parser, checker::ETSChecker *checker, ir::AssignmentExpression *assignmentExpression) const30 ir::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 
ProcessIndexGetAccess(parser::ETSParser *parser, checker::ETSChecker *checker, ir::MemberExpression *memberExpression) const49 ir::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 
Perform(public_lib::Context *ctx, parser::Program *program)68 bool 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 
Postcondition(public_lib::Context *ctx, const parser::Program *program)115 bool 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