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