• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2025 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 "compiler/lowering/util.h"
28 #include "parser/ETSparser.h"
29 #include "util/options.h"
30 
31 namespace ark::es2panda::compiler {
ProcessIndexSetAccess(parser::ETSParser * parser,checker::ETSChecker * checker,ir::AssignmentExpression * assignmentExpression) const32 ir::Expression *ObjectIndexLowering::ProcessIndexSetAccess(parser::ETSParser *parser, checker::ETSChecker *checker,
33                                                            ir::AssignmentExpression *assignmentExpression) const
34 {
35     //  Note! We assume that parser and checker phase nave been already passed correctly, thus the class has
36     //  required accessible index method[s] and all the types are properly resolved.
37 
38     auto indexSymbol = Gensym(checker->Allocator());
39     assignmentExpression->Right()->SetBoxingUnboxingFlags(ir::BoxingUnboxingFlags::NONE);
40     auto *const memberExpression = assignmentExpression->Left()->AsMemberExpression();
41     ir::Expression *loweringResult = nullptr;
42     ir::AstNode *setter = nullptr;
43     // Generate call to $_get to handle the chained assignment expression
44     if (assignmentExpression->Parent()->IsExpression() || assignmentExpression->Parent()->IsVariableDeclarator()) {
45         ArenaVector<ir::Statement *> blockStatements(checker->Allocator()->Adapter());
46         auto objectSymbol = Gensym(checker->Allocator());
47         blockStatements.push_back(
48             parser->CreateFormattedStatement("let @@I1 = @@E2", objectSymbol, memberExpression->Object()));
49         blockStatements.push_back(
50             parser->CreateFormattedStatement("let @@I1 = @@E2", indexSymbol, memberExpression->Property()));
51         static std::string const CALL_EXPRESSION =
52             std::string {"@@E1."} + std::string {compiler::Signatures::SET_INDEX_METHOD} + "(@@E2, @@E3)";
53         // Parse ArkTS code string and create and process corresponding AST node(s)
54         auto *const setStmt = parser->CreateFormattedStatement(
55             CALL_EXPRESSION, objectSymbol->Clone(checker->Allocator(), nullptr),
56             indexSymbol->Clone(checker->Allocator(), nullptr), assignmentExpression->Right());
57         setter = setStmt;
58         blockStatements.push_back(setStmt);
59         static std::string const GET_EXPRESSION =
60             std::string {"@@E1."} + std::string {compiler::Signatures::GET_INDEX_METHOD} + "(@@E2)";
61         blockStatements.push_back(parser->CreateFormattedStatement(GET_EXPRESSION,
62                                                                    objectSymbol->Clone(checker->Allocator(), nullptr),
63                                                                    indexSymbol->Clone(checker->Allocator(), nullptr)));
64         loweringResult =
65             util::NodeAllocator::ForceSetParent<ir::BlockExpression>(checker->Allocator(), std::move(blockStatements));
66     } else {
67         static std::string const CALL_EXPRESSION =
68             std::string {"@@E1."} + std::string {compiler::Signatures::SET_INDEX_METHOD} + "(@@E2, @@E3)";
69         // Parse ArkTS code string and create and process corresponding AST node(s)
70         loweringResult = parser->CreateFormattedExpression(CALL_EXPRESSION, memberExpression->Object(),
71                                                            memberExpression->Property(), assignmentExpression->Right());
72         setter = loweringResult;
73     }
74     ES2PANDA_ASSERT(loweringResult != nullptr);
75     loweringResult->SetParent(assignmentExpression->Parent());
76     loweringResult->SetRange(assignmentExpression->Range());
77     loweringResult->SetBoxingUnboxingFlags(assignmentExpression->GetBoxingUnboxingFlags());
78     setter->AddModifier(ir::ModifierFlags::ARRAY_SETTER);
79     auto scope = varbinder::LexicalScope<varbinder::Scope>::Enter(checker->VarBinder(),
80                                                                   NearestScope(assignmentExpression->Parent()));
81     CheckLoweredNode(checker->VarBinder()->AsETSBinder(), checker, loweringResult);
82     loweringResult->SetParent(assignmentExpression->Parent());
83     loweringResult->AddModifier(ir::ModifierFlags::SETTER);
84     return loweringResult;
85 }
86 
ProcessIndexGetAccess(parser::ETSParser * parser,checker::ETSChecker * checker,ir::MemberExpression * memberExpression) const87 ir::Expression *ObjectIndexLowering::ProcessIndexGetAccess(parser::ETSParser *parser, checker::ETSChecker *checker,
88                                                            ir::MemberExpression *memberExpression) const
89 {
90     //  Note! We assume that parser and checker phase nave been already passed correctly, thus the class has
91     //  required accessible index method[s] and all the types are properly resolved.
92     static std::string const CALL_EXPRESSION =
93         std::string {"@@E1."} + std::string {compiler::Signatures::GET_INDEX_METHOD} + "(@@E2)";
94 
95     // Parse ArkTS code string and create and process corresponding AST node(s)
96     auto *const loweringResult =
97         parser->CreateFormattedExpression(CALL_EXPRESSION, memberExpression->Object(), memberExpression->Property());
98     loweringResult->AddModifier(ir::ModifierFlags::GETTER);
99     loweringResult->SetParent(memberExpression->Parent());
100     loweringResult->SetRange(memberExpression->Range());
101 
102     CheckLoweredNode(checker->VarBinder()->AsETSBinder(), checker, loweringResult);
103     loweringResult->SetBoxingUnboxingFlags(memberExpression->GetBoxingUnboxingFlags());
104     return loweringResult;
105 }
106 
PerformForModule(public_lib::Context * ctx,parser::Program * program)107 bool ObjectIndexLowering::PerformForModule(public_lib::Context *ctx, parser::Program *program)
108 {
109     auto *const parser = ctx->parser->AsETSParser();
110     ES2PANDA_ASSERT(parser != nullptr);
111     auto *const checker = ctx->checker->AsETSChecker();
112     ES2PANDA_ASSERT(checker != nullptr);
113 
114     program->Ast()->TransformChildrenRecursively(
115         // CC-OFFNXT(G.FMT.14-CPP) project code style
116         [this, parser, checker](ir::AstNode *const ast) -> ir::AstNode * {
117             if (ast->IsAssignmentExpression() && ast->AsAssignmentExpression()->Left()->IsMemberExpression() &&
118                 ast->AsAssignmentExpression()->Left()->AsMemberExpression()->Kind() ==
119                     ir::MemberExpressionKind::ELEMENT_ACCESS) {
120                 if (auto const *const objectType =
121                         ast->AsAssignmentExpression()->Left()->AsMemberExpression()->ObjType();
122                     objectType != nullptr && !objectType->IsETSDynamicType()) {
123                     return ProcessIndexSetAccess(parser, checker, ast->AsAssignmentExpression());
124                 }
125             }
126             return ast;
127         },
128         Name());
129 
130     program->Ast()->TransformChildrenRecursively(
131         // CC-OFFNXT(G.FMT.14-CPP) project code style
132         [this, parser, checker](ir::AstNode *const ast) -> ir::AstNode * {
133             if (ast->IsMemberExpression() &&
134                 ast->AsMemberExpression()->Kind() == ir::MemberExpressionKind::ELEMENT_ACCESS) {
135                 if (auto const *const objectType = ast->AsMemberExpression()->ObjType();
136                     objectType != nullptr && !objectType->IsETSDynamicType()) {
137                     return ProcessIndexGetAccess(parser, checker, ast->AsMemberExpression());
138                 }
139             }
140             return ast;
141         },
142         Name());
143 
144     return true;
145 }
146 
PostconditionForModule(public_lib::Context * ctx,const parser::Program * program)147 bool ObjectIndexLowering::PostconditionForModule([[maybe_unused]] public_lib::Context *ctx,
148                                                  const parser::Program *program)
149 {
150     return !program->Ast()->IsAnyChild([](const ir::AstNode *ast) {
151         if (ast->IsMemberExpression() &&
152             ast->AsMemberExpression()->Kind() == ir::MemberExpressionKind::ELEMENT_ACCESS) {
153             if (auto const *const objectType = ast->AsMemberExpression()->ObjType(); objectType != nullptr) {
154                 return !objectType->IsETSDynamicType();
155             }
156         }
157         return false;
158     });
159 }
160 
161 }  // namespace ark::es2panda::compiler
162