• 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 // 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     ES2PANDA_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         ES2PANDA_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     ES2PANDA_ASSERT(exprToProcess != nullptr);
98     if (exprToProcess->TsType()->IsETSPrimitiveType()) {
99         loweringResult->SetBoxingUnboxingFlags(oldBoxingFlag);
100     } else if (exprToProcess->TsType()->IsETSObjectType()) {
101         loweringResult->SetBoxingUnboxingFlags(oldUnboxingFlag);
102     }
103 }
104 
CreateProxyTypeNode(public_lib::Context * ctx,ir::Expression * expr)105 static ir::OpaqueTypeNode *CreateProxyTypeNode(public_lib::Context *ctx, ir::Expression *expr)
106 {
107     auto *lcType = expr->TsType();
108     auto *checker = ctx->checker->AsETSChecker();
109     if (checker->IsExtensionETSFunctionType(lcType) && expr->IsMemberExpression() &&
110         expr->AsMemberExpression()->HasMemberKind(ir::MemberExpressionKind::EXTENSION_ACCESSOR)) {
111         lcType = expr->AsMemberExpression()->ExtensionAccessorType();
112     }
113     if (auto *lcTypeAsPrimitive = checker->MaybeUnboxInRelation(lcType); lcTypeAsPrimitive != nullptr) {
114         lcType = lcTypeAsPrimitive;
115     }
116     return ctx->AllocNode<ir::OpaqueTypeNode>(lcType, ctx->Allocator());
117 }
118 
GenFormatForExpression(ir::Expression * expr,size_t ix1,size_t ix2)119 static std::string GenFormatForExpression(ir::Expression *expr, size_t ix1, size_t ix2)
120 {
121     std::string res = "@@I" + std::to_string(ix1);
122 
123     if (expr->IsTSNonNullExpression()) {
124         expr = expr->AsTSNonNullExpression()->Expr();
125     }
126 
127     if (expr->IsMemberExpression()) {
128         auto const kind = expr->AsMemberExpression()->Kind();
129         if ((kind & ir::MemberExpressionKind::PROPERTY_ACCESS) != 0) {
130             res += ".@@I" + std::to_string(ix2);
131         } else if (kind == ir::MemberExpressionKind::ELEMENT_ACCESS) {
132             res += "[@@I" + std::to_string(ix2) + "]";
133         }
134     }
135     return res;
136 }
137 
GetClone(ArenaAllocator * allocator,ir::Identifier * node)138 static ir::Identifier *GetClone(ArenaAllocator *allocator, ir::Identifier *node)
139 {
140     return node == nullptr ? nullptr : node->Clone(allocator, nullptr);
141 }
142 
GetFormatPlaceholder(const ir::Expression * expr,const size_t counter)143 static std::string GetFormatPlaceholder(const ir::Expression *expr, const size_t counter)
144 {
145     if (expr->IsIdentifier()) {
146         return "@@I" + std::to_string(counter);
147     }
148 
149     return "@@E" + std::to_string(counter);
150 }
151 
UpdateStatementToAccessPropertyOrElement(const ir::MemberExpression * expr,std::string statement)152 static std::string UpdateStatementToAccessPropertyOrElement(const ir::MemberExpression *expr, std::string statement)
153 {
154     if (auto const kind = expr->Kind();
155         kind != ir::MemberExpressionKind::NONE && kind != ir::MemberExpressionKind::ELEMENT_ACCESS) {
156         statement = "." + statement;
157     } else if (kind == ir::MemberExpressionKind::ELEMENT_ACCESS) {
158         statement = "[" + statement + "]";
159     }
160 
161     return statement;
162 }
163 
GenerateNestedMemberAccess(ir::MemberExpression * expr,ArenaAllocator * const allocator,size_t counter=1)164 static std::tuple<std::string, ArenaVector<ir::Expression *>> GenerateNestedMemberAccess(
165     ir::MemberExpression *expr, ArenaAllocator *const allocator, size_t counter = 1)
166 {
167     // Generate a formatString and a vector of expressions(same order as the format string)
168     // The formatString will look like this: "@@I1K@@I2...K@@IN"
169     // Where K is either "." or "["
170     // If "[", then after the number there will be a "]".
171     // Where N is the number of nested member accesses (N == newAssignmentExpressions.size())
172     auto member = expr->Object();
173     ArenaVector<ir::Expression *> newAssignmentExpressions(allocator->Adapter());
174     newAssignmentExpressions.push_back(expr->Property());
175 
176     while (member->IsMemberExpression()) {
177         newAssignmentExpressions.push_back(member->AsMemberExpression()->Property());
178         member = member->AsMemberExpression()->Object();
179     }
180 
181     newAssignmentExpressions.push_back(member);
182     // This is done because the vector is built from the innermost member access to the outermost,
183     // but the format string is built from the outermost to the innermost
184     std::reverse(newAssignmentExpressions.begin(), newAssignmentExpressions.end());
185 
186     // It is outside the loop to avoid calling `UpdateStringToAccessPropertyOrElement` for the first element
187     std::string newAssignmentStatements = GetFormatPlaceholder(newAssignmentExpressions[0], counter);
188     newAssignmentExpressions[0] = newAssignmentExpressions[0]->Clone(allocator, nullptr)->AsExpression();
189 
190     for (size_t i = 1; i < newAssignmentExpressions.size(); i++) {
191         const std::string statement = GetFormatPlaceholder(newAssignmentExpressions[i], i + counter);
192         newAssignmentStatements += UpdateStatementToAccessPropertyOrElement(
193             newAssignmentExpressions[i]->Parent()->AsMemberExpression(), statement);
194         newAssignmentExpressions[i] = newAssignmentExpressions[i]->Clone(allocator, nullptr)->AsExpression();
195     }
196 
197     return {newAssignmentStatements, newAssignmentExpressions};
198 }
199 
GenerateStringForAssignment(const lexer::TokenType opEqual,ir::MemberExpression * expr,ArenaAllocator * const allocator,size_t counter)200 static std::tuple<std::string, ArenaVector<ir::Expression *>> GenerateStringForAssignment(
201     const lexer::TokenType opEqual, ir::MemberExpression *expr, ArenaAllocator *const allocator, size_t counter)
202 {
203     // Note: Handle "A `opAssign` B" to "A = (A `operation` B) as T"
204     // opAssign is the operation like: "+=", "-=", "*=", "/=", etc.,
205     // operation is the operation like: "+", "-", "*", "/", etc.
206     auto [retStr, retVec] = GenerateNestedMemberAccess(expr, allocator, counter);
207     counter += retVec.size();
208     auto result = GenerateNestedMemberAccess(expr, allocator, counter);
209     counter += std::get<1>(result).size();
210     retStr += " = ( " + std::get<0>(result) + ' ' + std::string {lexer::TokenToString(CombinedOpToOp(opEqual))} +
211               " (@@E" + std::to_string(counter) + ")) as @@T" + std::to_string(counter + 1);
212     retVec.insert(retVec.end(), std::get<1>(result).begin(), std::get<1>(result).end());
213     return {retStr, retVec};
214 }
215 
GenerateLoweredResultForLoweredAssignment(const lexer::TokenType opEqual,ir::MemberExpression * expr,ArenaAllocator * const allocator,parser::ETSParser * parser,const std::array<ir::Expression *,2> additionalAssignmentExpressions)216 static ir::Expression *GenerateLoweredResultForLoweredAssignment(
217     const lexer::TokenType opEqual, ir::MemberExpression *expr, ArenaAllocator *const allocator,
218     parser::ETSParser *parser, const std::array<ir::Expression *, 2> additionalAssignmentExpressions)
219 {
220     // Generated a formatString for the new lowered assignment expression
221     // The formatString will look like this: "A = (A `operation` B) as T"
222     // Where A is a member access
223     // `operation` is the operation of the assignment like: "+", "-", "*", "/", etc.,
224     // B is the right hand side of the assignment
225     // T is the type of the left hand side of the assignment
226     if (expr->Kind() == ir::MemberExpressionKind::ELEMENT_ACCESS && !expr->Property()->IsLiteral()) {
227         // Note: support such a situation could be okay: `a[idx++] += someExpr`.
228         // It should be lowered as: `let dummyIdx = (lower result of `idx++`); a[dummyIdx] = a[dummyIdx] + someExpr`;
229         ArenaVector<ir::Expression *> dummyIndexDeclExpression(allocator->Adapter());
230         std::string dummyIndexDeclStr = "let @@I1 = @@E2;\n";
231         auto dummyIndex = Gensym(allocator);
232         dummyIndexDeclExpression.emplace_back(dummyIndex);
233         dummyIndexDeclExpression.emplace_back(expr->Property()->Clone(allocator, nullptr)->AsExpression());
234         ClearTypesVariablesAndScopes(dummyIndexDeclExpression[1]);
235 
236         // Note: Drop the old property, substitute it with dummyIdx.
237         expr->Property()->SetParent(nullptr);
238         expr->SetProperty(dummyIndex->Clone(allocator, expr));
239         auto [retStr, retVec] =
240             GenerateStringForAssignment(opEqual, expr, allocator, dummyIndexDeclExpression.size() + 1);
241         retVec.push_back(additionalAssignmentExpressions[0]);
242         retVec.push_back(additionalAssignmentExpressions[1]);
243         retVec.insert(retVec.begin(), dummyIndexDeclExpression.begin(), dummyIndexDeclExpression.end());
244         retStr = dummyIndexDeclStr + retStr;
245         return parser->CreateFormattedExpression(retStr, retVec);
246     }
247 
248     auto [retStr, retVec] = GenerateStringForAssignment(opEqual, expr, allocator, 1);
249     retVec.push_back(additionalAssignmentExpressions[0]);
250     retVec.push_back(additionalAssignmentExpressions[1]);
251     return parser->CreateFormattedExpression(retStr, retVec);
252 }
253 
ConstructOpAssignmentResult(public_lib::Context * ctx,ir::AssignmentExpression * assignment)254 static ir::Expression *ConstructOpAssignmentResult(public_lib::Context *ctx, ir::AssignmentExpression *assignment)
255 {
256     auto *allocator = ctx->allocator;
257     auto *parser = ctx->parser->AsETSParser();
258 
259     const auto opEqual = assignment->OperatorType();
260     ES2PANDA_ASSERT(opEqual != lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
261 
262     auto *const left = assignment->Left();
263     auto *const right = assignment->Right();
264     right->SetBoxingUnboxingFlags(ir::BoxingUnboxingFlags::NONE);
265 
266     auto *exprType = CreateProxyTypeNode(ctx, left);
267     ir::Expression *retVal = nullptr;
268 
269     // Create temporary variable(s) if left hand of assignment is not defined by simple identifier[s]
270     if (left->IsIdentifier()) {
271         const std::string formatString =
272             "@@I1 = (@@I2 " + std::string(lexer::TokenToString(CombinedOpToOp(opEqual))) + " (@@E3)) as @@T4";
273         retVal = parser->CreateFormattedExpression(formatString, GetClone(allocator, left->AsIdentifier()),
274                                                    GetClone(allocator, left->AsIdentifier()), right, exprType);
275     } else if (left->IsMemberExpression()) {
276         // Generate ArkTS code string for new lowered assignment expression:
277         retVal = GenerateLoweredResultForLoweredAssignment(opEqual, left->AsMemberExpression(), allocator, parser,
278                                                            {right, exprType});
279     } else {
280         ES2PANDA_UNREACHABLE();
281     }
282 
283     return retVal;
284 }
285 
HandleOpAssignment(public_lib::Context * ctx,ir::AssignmentExpression * assignment)286 ir::AstNode *HandleOpAssignment(public_lib::Context *ctx, ir::AssignmentExpression *assignment)
287 {
288     auto *checker = ctx->checker->AsETSChecker();
289 
290     if (assignment->TsType() == nullptr) {  // hasn't been through checker
291         return assignment;
292     }
293 
294     auto *loweringResult = ConstructOpAssignmentResult(ctx, assignment);
295 
296     loweringResult->SetParent(assignment->Parent());
297     // NOTE(dslynko, #19200): required for correct debug-info
298     auto rng = assignment->Range();
299     loweringResult->SetRange(rng);
300     loweringResult->TransformChildrenRecursively(
301         [rng](auto *node) {
302             node->SetRange(rng);
303             return node;
304         },
305         "");
306 
307     auto *const scope = NearestScope(assignment);
308 
309     auto expressionCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(checker->VarBinder(), scope);
310     InitScopesPhaseETS::RunExternalNode(loweringResult, ctx->parserProgram->VarBinder());
311     checker->VarBinder()->AsETSBinder()->ResolveReferencesForScopeWithContext(loweringResult, scope);
312 
313     checker::SavedCheckerContext scc {checker, checker::CheckerStatus::IGNORE_VISIBILITY, ContainingClass(assignment)};
314     checker::ScopeContext sc {checker, scope};
315 
316     loweringResult->Check(checker);
317 
318     AdjustBoxingUnboxingFlags(loweringResult, assignment);
319 
320     return loweringResult;
321 }
322 
323 struct ArgumentInfo {
324     std::string newAssignmentStatements {};
325     ir::Identifier *id1 = nullptr;
326     ir::Identifier *id2 = nullptr;
327     ir::Identifier *id3 = nullptr;
328     ir::Expression *object = nullptr;
329     ir::Expression *property = nullptr;
330     checker::Type *objType = nullptr;
331     checker::Type *propType = nullptr;
332 };
333 
ParseArgument(public_lib::Context * ctx,ir::Expression * argument,ArgumentInfo & info)334 static void ParseArgument(public_lib::Context *ctx, ir::Expression *argument, ArgumentInfo &info)
335 {
336     auto *allocator = ctx->allocator;
337 
338     if (argument->IsTSNonNullExpression()) {
339         argument = argument->AsTSNonNullExpression()->Expr();
340     }
341 
342     if (argument->IsIdentifier()) {
343         info.id1 = GetClone(allocator, argument->AsIdentifier());
344     } else if (argument->IsMemberExpression()) {
345         auto *memberExpression = argument->AsMemberExpression();
346 
347         if (info.object = memberExpression->Object(); info.object != nullptr && info.object->IsIdentifier()) {
348             info.id1 = GetClone(allocator, info.object->AsIdentifier());
349         } else if (info.object != nullptr) {
350             info.id1 = Gensym(allocator);
351             info.newAssignmentStatements = "const @@I1 = (@@E2) as @@T3; ";
352             info.objType = info.object->TsType();
353         }
354 
355         if (info.property = memberExpression->Property(); info.property != nullptr && info.property->IsIdentifier()) {
356             info.id2 = GetClone(allocator, info.property->AsIdentifier());
357         } else if (info.property != nullptr) {
358             info.id2 = Gensym(allocator);
359             info.newAssignmentStatements += "const @@I4 = (@@E5) as @@T6; ";
360             info.propType = info.property->TsType();
361         }
362     }
363 }
364 
ConstructUpdateResult(public_lib::Context * ctx,ir::UpdateExpression * upd)365 static ir::Expression *ConstructUpdateResult(public_lib::Context *ctx, ir::UpdateExpression *upd)
366 {
367     auto *allocator = ctx->allocator;
368     auto *parser = ctx->parser->AsETSParser();
369     auto *argument = upd->Argument();
370     auto *checker = ctx->checker->AsETSChecker();
371 
372     ArgumentInfo argInfo {};
373     argInfo.objType = checker->GlobalVoidType();
374     argInfo.propType = checker->GlobalVoidType();
375     argInfo.id3 = Gensym(allocator);
376 
377     // Parse ArkTS code string and create the corresponding AST node(s)
378     // We have to use extra caution with types and `as` conversions because of smart types, which we cannot reproduce in
379     // re-checking.
380     ParseArgument(ctx, argument, argInfo);
381     std::string opSign = lexer::TokenToString(CombinedOpToOp(upd->OperatorType()));
382 
383     std::string suffix = argument->TsType()->IsETSBigIntType() ? "n" : "";
384 
385     // NOLINTBEGIN(readability-magic-numbers)
386     if (upd->IsPrefix()) {
387         argInfo.newAssignmentStatements +=
388             "const @@I7 = (" + GenFormatForExpression(argument, 8U, 9U) + opSign + " 1" + suffix + ") as @@T10;";
389         argInfo.newAssignmentStatements += GenFormatForExpression(argument, 11U, 12U) + " = @@I13; @@I14";
390         return parser->CreateFormattedExpression(
391             argInfo.newAssignmentStatements, argInfo.id1, argInfo.object, argInfo.objType, argInfo.id2,
392             argInfo.property, argInfo.propType, argInfo.id3, GetClone(allocator, argInfo.id1),
393             GetClone(allocator, argInfo.id2), argument->TsType(), GetClone(allocator, argInfo.id1),
394             GetClone(allocator, argInfo.id2), GetClone(allocator, argInfo.id3), GetClone(allocator, argInfo.id3));
395     }
396 
397     // upd is postfix
398     argInfo.newAssignmentStatements += "const @@I7 = " + GenFormatForExpression(argument, 8, 9) + " as @@T10;" +
399                                        GenFormatForExpression(argument, 11U, 12U) + " = (@@I13 " + opSign + " 1" +
400                                        suffix + ") as @@T14; @@I15;";
401     return parser->CreateFormattedExpression(
402         argInfo.newAssignmentStatements, argInfo.id1, argInfo.object, argInfo.objType, argInfo.id2, argInfo.property,
403         argInfo.propType, argInfo.id3, GetClone(allocator, argInfo.id1), GetClone(allocator, argInfo.id2),
404         argument->TsType(), GetClone(allocator, argInfo.id1), GetClone(allocator, argInfo.id2),
405         GetClone(allocator, argInfo.id3), argument->TsType(), GetClone(allocator, argInfo.id3));
406     // NOLINTEND(readability-magic-numbers)
407 }
408 
HandleUpdate(public_lib::Context * ctx,ir::UpdateExpression * upd)409 static ir::AstNode *HandleUpdate(public_lib::Context *ctx, ir::UpdateExpression *upd)
410 {
411     auto *const scope = NearestScope(upd);
412 
413     ir::Expression *loweringResult = ConstructUpdateResult(ctx, upd);
414 
415     auto *checker = ctx->checker->AsETSChecker();
416 
417     auto expressionCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(checker->VarBinder(), scope);
418     checker::SavedCheckerContext scc {checker, checker::CheckerStatus::IGNORE_VISIBILITY, ContainingClass(upd)};
419     checker::ScopeContext sc {checker, scope};
420 
421     loweringResult->SetParent(upd->Parent());
422     // NOTE(dslynko, #19200): required for correct debug-info
423     auto rng = upd->Range();
424     loweringResult->SetRange(rng);
425     loweringResult->TransformChildrenRecursively(
426         [rng](auto *node) {
427             node->SetRange(rng);
428             return node;
429         },
430         "");
431 
432     InitScopesPhaseETS::RunExternalNode(loweringResult, checker->VarBinder());
433 
434     checker->VarBinder()->AsETSBinder()->ResolveReferencesForScopeWithContext(loweringResult,
435                                                                               NearestScope(loweringResult));
436     loweringResult->Check(checker);
437 
438     AdjustBoxingUnboxingFlags(loweringResult, upd);
439 
440     return loweringResult;
441 }
442 
PerformForModule(public_lib::Context * ctx,parser::Program * program)443 bool OpAssignmentLowering::PerformForModule(public_lib::Context *ctx, parser::Program *program)
444 {
445     program->Ast()->TransformChildrenRecursively(
446         [ctx](ir::AstNode *ast) {
447             if (ast->IsAssignmentExpression() &&
448                 ast->AsAssignmentExpression()->OperatorType() != lexer::TokenType::PUNCTUATOR_SUBSTITUTION) {
449                 return HandleOpAssignment(ctx, ast->AsAssignmentExpression());
450             }
451 
452             if (ast->IsUpdateExpression()) {
453                 return HandleUpdate(ctx, ast->AsUpdateExpression());
454             }
455 
456             return ast;
457         },
458         Name());
459 
460     return true;
461 }
462 
PostconditionForModule(public_lib::Context * ctx,const parser::Program * program)463 bool OpAssignmentLowering::PostconditionForModule([[maybe_unused]] public_lib::Context *ctx,
464                                                   const parser::Program *program)
465 {
466     return !program->Ast()->IsAnyChild([](const ir::AstNode *ast) {
467         return (ast->IsAssignmentExpression() && ast->AsAssignmentExpression()->TsType() != nullptr &&
468                 ast->AsAssignmentExpression()->OperatorType() != lexer::TokenType::PUNCTUATOR_SUBSTITUTION) ||
469                ast->IsUpdateExpression();
470     });
471 }
472 
473 }  // namespace ark::es2panda::compiler
474