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