• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 // This is a sample lowering, of little value by itself.
18 //
19 // desc: A compound assignment expression of the form E1 op= E2 is equivalent to E1 =
20 //   	 ((E1) op (E2)) as T, where T is the type of E1, except that E1 is evaluated only
21 //   	 once.
22 //
23 
24 #include "opAssignment.h"
25 
26 #include "parser/ETSparser.h"
27 #include "varbinder/ETSBinder.h"
28 #include "checker/ETSchecker.h"
29 #include "compiler/lowering/util.h"
30 #include "compiler/lowering/scopesInit/scopesInitPhase.h"
31 #include "ir/opaqueTypeNode.h"
32 #include "ir/expressions/assignmentExpression.h"
33 #include "ir/expressions/identifier.h"
34 #include "ir/expressions/memberExpression.h"
35 #include "ir/expressions/blockExpression.h"
36 #include "ir/statements/blockStatement.h"
37 #include "ir/statements/expressionStatement.h"
38 
39 namespace ark::es2panda::compiler {
40 
41 struct Conversion {
42     lexer::TokenType from;
43     lexer::TokenType to;
44 };
45 
46 // NOLINTNEXTLINE(readability-magic-numbers)
47 static constexpr std::array<Conversion, 18> OP_TRANSLATION {{
48     {lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT_EQUAL, lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT},
49     {lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT_EQUAL, lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT},
50     {lexer::TokenType::PUNCTUATOR_LEFT_SHIFT_EQUAL, lexer::TokenType::PUNCTUATOR_LEFT_SHIFT},
51     {lexer::TokenType::PUNCTUATOR_PLUS_EQUAL, lexer::TokenType::PUNCTUATOR_PLUS},
52     {lexer::TokenType::PUNCTUATOR_MINUS_EQUAL, lexer::TokenType::PUNCTUATOR_MINUS},
53     {lexer::TokenType::PUNCTUATOR_MULTIPLY_EQUAL, lexer::TokenType::PUNCTUATOR_MULTIPLY},
54     {lexer::TokenType::PUNCTUATOR_DIVIDE_EQUAL, lexer::TokenType::PUNCTUATOR_DIVIDE},
55     {lexer::TokenType::PUNCTUATOR_MOD_EQUAL, lexer::TokenType::PUNCTUATOR_MOD},
56     {lexer::TokenType::PUNCTUATOR_BITWISE_AND_EQUAL, lexer::TokenType::PUNCTUATOR_BITWISE_AND},
57     {lexer::TokenType::PUNCTUATOR_BITWISE_OR_EQUAL, lexer::TokenType::PUNCTUATOR_BITWISE_OR},
58     {lexer::TokenType::PUNCTUATOR_BITWISE_XOR_EQUAL, lexer::TokenType::PUNCTUATOR_BITWISE_XOR},
59     {lexer::TokenType::PUNCTUATOR_LOGICAL_AND_EQUAL, lexer::TokenType::PUNCTUATOR_LOGICAL_AND},
60     {lexer::TokenType::PUNCTUATOR_LOGICAL_OR_EQUAL, lexer::TokenType::PUNCTUATOR_LOGICAL_OR},
61     {lexer::TokenType::PUNCTUATOR_LOGICAL_NULLISH_EQUAL, lexer::TokenType::PUNCTUATOR_NULLISH_COALESCING},
62     {lexer::TokenType::PUNCTUATOR_EXPONENTIATION_EQUAL, lexer::TokenType::PUNCTUATOR_EXPONENTIATION},
63     {lexer::TokenType::PUNCTUATOR_PLUS_PLUS, lexer::TokenType::PUNCTUATOR_PLUS},
64     {lexer::TokenType::PUNCTUATOR_MINUS_MINUS, lexer::TokenType::PUNCTUATOR_MINUS},
65 }};
66 
CombinedOpToOp(const lexer::TokenType combinedOp)67 static lexer::TokenType CombinedOpToOp(const lexer::TokenType combinedOp)
68 {
69     for (const auto &conv : OP_TRANSLATION) {
70         if (conv.from == combinedOp) {
71             return conv.to;
72         }
73     }
74     UNREACHABLE();
75 }
76 
AdjustBoxingUnboxingFlags(ir::Expression * loweringResult,const ir::Expression * oldExpr)77 void AdjustBoxingUnboxingFlags(ir::Expression *loweringResult, const ir::Expression *oldExpr)
78 {
79     ir::Expression *exprToProcess = nullptr;
80     if (loweringResult->IsAssignmentExpression()) {
81         exprToProcess = loweringResult->AsAssignmentExpression();
82     } else if (loweringResult->IsBlockExpression() && !loweringResult->AsBlockExpression()->Statements().empty()) {
83         auto *statement = loweringResult->AsBlockExpression()->Statements().back();
84         if (statement->IsExpressionStatement()) {
85             exprToProcess = statement->AsExpressionStatement()->GetExpression();
86         }
87     } else {
88         UNREACHABLE();
89     }
90 
91     // NOTE: gogabr. make sure that the checker never puts both a boxing and an unboxing flag on the same node.
92     // Then this function will become unnecessary.
93     const ir::BoxingUnboxingFlags oldBoxingFlag {oldExpr->GetBoxingUnboxingFlags() &
94                                                  ir::BoxingUnboxingFlags::BOXING_FLAG};
95     const ir::BoxingUnboxingFlags oldUnboxingFlag {oldExpr->GetBoxingUnboxingFlags() &
96                                                    ir::BoxingUnboxingFlags::UNBOXING_FLAG};
97 
98     if (exprToProcess->TsType()->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE)) {
99         loweringResult->SetBoxingUnboxingFlags(oldBoxingFlag);
100     } else if (exprToProcess->TsType()->IsETSObjectType()) {
101         loweringResult->SetBoxingUnboxingFlags(oldUnboxingFlag);
102     }
103 }
104 
CreateProxyTypeNode(checker::ETSChecker * checker,ir::Expression * expr)105 static ir::OpaqueTypeNode *CreateProxyTypeNode(checker::ETSChecker *checker, ir::Expression *expr)
106 {
107     auto *lcType = expr->TsType();
108     if (auto *lcTypeAsPrimitive = checker->ETSBuiltinTypeAsPrimitiveType(lcType); lcTypeAsPrimitive != nullptr) {
109         lcType = lcTypeAsPrimitive;
110     }
111     return checker->AllocNode<ir::OpaqueTypeNode>(lcType);
112 }
113 
GenFormatForExpression(ir::Expression * expr,size_t ix1,size_t ix2)114 static std::string GenFormatForExpression(ir::Expression *expr, size_t ix1, size_t ix2)
115 {
116     std::string res = "@@I" + std::to_string(ix1);
117     if (expr->IsMemberExpression()) {
118         auto const kind = expr->AsMemberExpression()->Kind();
119         if (kind == ir::MemberExpressionKind::PROPERTY_ACCESS) {
120             res += ".@@I" + std::to_string(ix2);
121         } else if (kind == ir::MemberExpressionKind::ELEMENT_ACCESS) {
122             res += "[@@I" + std::to_string(ix2) + "]";
123         }
124     }
125     return res;
126 }
127 
GenerateStringForLoweredAssignment(lexer::TokenType opEqual,ir::Expression * expr)128 static std::string GenerateStringForLoweredAssignment(lexer::TokenType opEqual, ir::Expression *expr)
129 {
130     std::string leftHand = GenFormatForExpression(expr, 5, 6);
131     std::string rightHand = GenFormatForExpression(expr, 7, 8);
132 
133     return leftHand + " = (" + rightHand + ' ' + std::string {lexer::TokenToString(CombinedOpToOp(opEqual))} +
134            " (@@E9)) as @@T10";
135 }
136 
GetClone(ArenaAllocator * allocator,ir::Identifier * node)137 static ir::Identifier *GetClone(ArenaAllocator *allocator, ir::Identifier *node)
138 {
139     return node == nullptr ? nullptr : node->Clone(allocator, nullptr);
140 }
141 
ConstructOpAssignmentResult(public_lib::Context * ctx,ir::AssignmentExpression * assignment)142 static ir::Expression *ConstructOpAssignmentResult(public_lib::Context *ctx, ir::AssignmentExpression *assignment)
143 {
144     auto *allocator = ctx->allocator;
145     auto *parser = ctx->parser->AsETSParser();
146     auto *checker = ctx->checker->AsETSChecker();
147 
148     const auto opEqual = assignment->OperatorType();
149     ASSERT(opEqual != lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
150 
151     auto *const left = assignment->Left();
152     auto *const right = assignment->Right();
153 
154     std::string newAssignmentStatements {};
155 
156     ir::Identifier *ident1;
157     ir::Identifier *ident2 = nullptr;
158     ir::Expression *object = nullptr;
159     ir::Expression *property = nullptr;
160 
161     // Create temporary variable(s) if left hand of assignment is not defined by simple identifier[s]
162     if (left->IsIdentifier()) {
163         ident1 = left->AsIdentifier();
164     } else if (left->IsMemberExpression()) {
165         auto *const memberExpression = left->AsMemberExpression();
166 
167         if (object = memberExpression->Object(); object->IsIdentifier()) {
168             ident1 = object->AsIdentifier();
169         } else {
170             ident1 = Gensym(allocator);
171             newAssignmentStatements = "const @@I1 = (@@E2); ";
172         }
173 
174         if (property = memberExpression->Property(); property->IsIdentifier()) {
175             ident2 = property->AsIdentifier();
176         } else {
177             ident2 = Gensym(allocator);
178             newAssignmentStatements += "const @@I3 = (@@E4); ";
179         }
180     } else {
181         UNREACHABLE();
182     }
183 
184     auto *exprType = CreateProxyTypeNode(checker, left);
185 
186     // Generate ArkTS code string for new lowered assignment expression:
187     newAssignmentStatements += GenerateStringForLoweredAssignment(opEqual, left);
188 
189     // Parse ArkTS code string and create corresponding AST node(s)
190     return parser->CreateFormattedExpression(newAssignmentStatements, ident1, object, ident2, property,
191                                              GetClone(allocator, ident1), GetClone(allocator, ident2),
192                                              GetClone(allocator, ident1), GetClone(allocator, ident2), right, exprType);
193 }
194 
HandleOpAssignment(public_lib::Context * ctx,ir::AssignmentExpression * assignment)195 ir::AstNode *HandleOpAssignment(public_lib::Context *ctx, ir::AssignmentExpression *assignment)
196 {
197     auto *checker = ctx->checker->AsETSChecker();
198 
199     if (assignment->TsType() == nullptr) {  // hasn't been through checker
200         return assignment;
201     }
202 
203     auto *loweringResult = ConstructOpAssignmentResult(ctx, assignment);
204 
205     loweringResult->SetParent(assignment->Parent());
206 
207     auto *const scope = NearestScope(assignment);
208 
209     auto expressionCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(checker->VarBinder(), scope);
210     InitScopesPhaseETS::RunExternalNode(loweringResult, ctx->parserProgram->VarBinder());
211     checker->VarBinder()->AsETSBinder()->ResolveReferencesForScopeWithContext(loweringResult, scope);
212 
213     checker::SavedCheckerContext scc {checker, checker::CheckerStatus::IGNORE_VISIBILITY, ContainingClass(assignment)};
214     checker::ScopeContext sc {checker, scope};
215 
216     loweringResult->Check(checker);
217 
218     AdjustBoxingUnboxingFlags(loweringResult, assignment);
219 
220     return loweringResult;
221 }
222 
ConstructUpdateResult(public_lib::Context * ctx,ir::UpdateExpression * upd)223 static ir::Expression *ConstructUpdateResult(public_lib::Context *ctx, ir::UpdateExpression *upd)
224 {
225     auto *allocator = ctx->allocator;
226     auto *parser = ctx->parser->AsETSParser();
227     auto *argument = upd->Argument();
228     auto *checker = ctx->checker->AsETSChecker();
229 
230     std::string newAssignmentStatements {};
231 
232     ir::Identifier *id1;
233     ir::Identifier *id2 = nullptr;
234     ir::Identifier *id3 = nullptr;
235     ir::Expression *object = nullptr;
236     ir::Expression *property = nullptr;
237     checker::Type *objType = checker->GlobalVoidType();  // placeholder
238     checker::Type *propType = checker->GlobalVoidType();
239 
240     // Parse ArkTS code string and create the corresponding AST node(s)
241     // We have to use extra caution with types and `as` conversions because of smart types, which we cannot reproduce in
242     // re-checking.
243 
244     if (argument->IsIdentifier()) {
245         id1 = GetClone(allocator, argument->AsIdentifier());
246     } else if (argument->IsMemberExpression()) {
247         auto *memberExpression = argument->AsMemberExpression();
248 
249         if (object = memberExpression->Object(); object != nullptr && object->IsIdentifier()) {
250             id1 = GetClone(allocator, object->AsIdentifier());
251         } else if (object != nullptr) {
252             id1 = Gensym(allocator);
253             newAssignmentStatements = "const @@I1 = (@@E2) as @@T3; ";
254             objType = object->TsType();
255         }
256 
257         if (property = memberExpression->Property(); property != nullptr && property->IsIdentifier()) {
258             id2 = GetClone(allocator, property->AsIdentifier());
259         } else if (property != nullptr) {
260             id2 = Gensym(allocator);
261             newAssignmentStatements += "const @@I4 = (@@E5) as @@T6; ";
262             propType = property->TsType();
263         }
264     }
265 
266     std::string opSign = lexer::TokenToString(CombinedOpToOp(upd->OperatorType()));
267 
268     std::string suffix = (argument->TsType() == checker->GlobalETSBigIntType()) ? "n" : "";
269 
270     // NOLINTBEGIN(readability-magic-numbers)
271     if (upd->IsPrefix()) {
272         newAssignmentStatements += GenFormatForExpression(argument, 7U, 8U) + " = (" +
273                                    GenFormatForExpression(argument, 9U, 10U) + opSign + " 1" + suffix + ") as @@T11;";
274         return parser->CreateFormattedExpression(
275             newAssignmentStatements, id1, object, objType, id2, property, propType, GetClone(allocator, id1),
276             GetClone(allocator, id2), GetClone(allocator, id1), GetClone(allocator, id2), argument->TsType());
277     }
278 
279     // upd is postfix
280     id3 = Gensym(allocator);
281     newAssignmentStatements += "const @@I7 = " + GenFormatForExpression(argument, 8, 9) + " as @@T10;" +
282                                GenFormatForExpression(argument, 11U, 12U) + " = (@@I13 " + opSign + " 1" + suffix +
283                                ") as @@T14; @@I15;";
284     return parser->CreateFormattedExpression(newAssignmentStatements, id1, object, objType, id2, property, propType,
285                                              id3, GetClone(allocator, id1), GetClone(allocator, id2),
286                                              argument->TsType(), GetClone(allocator, id1), GetClone(allocator, id2),
287                                              GetClone(allocator, id3), argument->TsType(), GetClone(allocator, id3));
288     // NOLINTEND(readability-magic-numbers)
289 }
290 
HandleUpdate(public_lib::Context * ctx,ir::UpdateExpression * upd)291 static ir::AstNode *HandleUpdate(public_lib::Context *ctx, ir::UpdateExpression *upd)
292 {
293     auto *checker = ctx->checker->AsETSChecker();
294 
295     auto *const scope = NearestScope(upd);
296 
297     ir::Expression *loweringResult = ConstructUpdateResult(ctx, upd);
298 
299     auto expressionCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(checker->VarBinder(), scope);
300     checker::SavedCheckerContext scc {checker, checker::CheckerStatus::IGNORE_VISIBILITY, ContainingClass(upd)};
301     checker::ScopeContext sc {checker, scope};
302 
303     loweringResult->SetParent(upd->Parent());
304     InitScopesPhaseETS::RunExternalNode(loweringResult, ctx->checker->VarBinder());
305 
306     checker->VarBinder()->AsETSBinder()->ResolveReferencesForScopeWithContext(loweringResult,
307                                                                               NearestScope(loweringResult));
308     loweringResult->Check(checker);
309 
310     AdjustBoxingUnboxingFlags(loweringResult, upd);
311 
312     return loweringResult;
313 }
314 
Perform(public_lib::Context * ctx,parser::Program * program)315 bool OpAssignmentLowering::Perform(public_lib::Context *ctx, parser::Program *program)
316 {
317     if (ctx->config->options->CompilerOptions().compilationMode == CompilationMode::GEN_STD_LIB) {
318         for (auto &[_, ext_programs] : program->ExternalSources()) {
319             (void)_;
320             for (auto *extProg : ext_programs) {
321                 Perform(ctx, extProg);
322             }
323         }
324     }
325 
326     program->Ast()->TransformChildrenRecursively(
327         [ctx](ir::AstNode *ast) {
328             if (ast->IsAssignmentExpression() &&
329                 ast->AsAssignmentExpression()->OperatorType() != lexer::TokenType::PUNCTUATOR_SUBSTITUTION) {
330                 return HandleOpAssignment(ctx, ast->AsAssignmentExpression());
331             }
332             if (ast->IsUpdateExpression()) {
333                 return HandleUpdate(ctx, ast->AsUpdateExpression());
334             }
335 
336             return ast;
337         },
338         Name());
339 
340     return true;
341 }
342 
Postcondition(public_lib::Context * ctx,const parser::Program * program)343 bool OpAssignmentLowering::Postcondition(public_lib::Context *ctx, const parser::Program *program)
344 {
345     auto checkExternalPrograms = [this, ctx](const ArenaVector<parser::Program *> &programs) {
346         for (auto *p : programs) {
347             if (!Postcondition(ctx, p)) {
348                 return false;
349             }
350         }
351         return true;
352     };
353 
354     if (ctx->config->options->CompilerOptions().compilationMode == CompilationMode::GEN_STD_LIB) {
355         for (auto &[_, extPrograms] : program->ExternalSources()) {
356             (void)_;
357             if (!checkExternalPrograms(extPrograms)) {
358                 return false;
359             };
360         }
361     }
362 
363     return !program->Ast()->IsAnyChild([](const ir::AstNode *ast) {
364         return (ast->IsAssignmentExpression() && ast->AsAssignmentExpression()->TsType() != nullptr &&
365                 ast->AsAssignmentExpression()->OperatorType() != lexer::TokenType::PUNCTUATOR_SUBSTITUTION) ||
366                ast->IsUpdateExpression();
367     });
368 }
369 
370 }  // namespace ark::es2panda::compiler
371